[BTRFS]
[reactos.git] / reactos / drivers / filesystems / btrfs / security.c
1 /* Copyright (c) Mark Harmstone 2016
2 *
3 * This file is part of WinBtrfs.
4 *
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
9 *
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
17
18 #include "btrfs_drv.h"
19
20 #define SEF_DACL_AUTO_INHERIT 0x01
21 #define SEF_SACL_AUTO_INHERIT 0x02
22
23 typedef struct {
24 UCHAR revision;
25 UCHAR elements;
26 UCHAR auth[6];
27 UINT32 nums[8];
28 } sid_header;
29
30 static sid_header sid_BA = { 1, 2, SECURITY_NT_AUTHORITY, {32, 544}}; // BUILTIN\Administrators
31 static sid_header sid_SY = { 1, 1, SECURITY_NT_AUTHORITY, {18}}; // NT AUTHORITY\SYSTEM
32 static sid_header sid_BU = { 1, 2, SECURITY_NT_AUTHORITY, {32, 545}}; // BUILTIN\Users
33 static sid_header sid_AU = { 1, 1, SECURITY_NT_AUTHORITY, {11}}; // NT AUTHORITY\Authenticated Users
34
35 typedef struct {
36 UCHAR flags;
37 ACCESS_MASK mask;
38 sid_header* sid;
39 } dacl;
40
41 static dacl def_dacls[] = {
42 { 0, FILE_ALL_ACCESS, &sid_BA },
43 { OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE, FILE_ALL_ACCESS, &sid_BA },
44 { 0, FILE_ALL_ACCESS, &sid_SY },
45 { OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE, FILE_ALL_ACCESS, &sid_SY },
46 { OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE, FILE_GENERIC_READ | FILE_GENERIC_EXECUTE, &sid_BU },
47 { OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE, FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE, &sid_AU },
48 { 0, FILE_ADD_SUBDIRECTORY, &sid_AU },
49 // FIXME - Mandatory Label\High Mandatory Level:(OI)(NP)(IO)(NW)
50 { 0, 0, NULL }
51 };
52
53 extern LIST_ENTRY uid_map_list;
54
55 // UINT32 STDCALL get_uid() {
56 // PACCESS_TOKEN at = PsReferencePrimaryToken(PsGetCurrentProcess());
57 // HANDLE h;
58 // ULONG len, size;
59 // TOKEN_USER* tu;
60 // NTSTATUS Status;
61 // UINT32 uid = UID_NOBODY;
62 // LIST_ENTRY* le;
63 // uid_map* um;
64 // // char s[256];
65 //
66 // Status = ObOpenObjectByPointer(at, OBJ_KERNEL_HANDLE, NULL, GENERIC_READ, NULL, KernelMode, &h);
67 // if (!NT_SUCCESS(Status)) {
68 // ERR("ObOpenObjectByPointer returned %08x\n", Status);
69 // goto exit;
70 // }
71 //
72 // Status = ZwQueryInformationToken(h, TokenUser, NULL, 0, &len);
73 // if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) {
74 // ERR("ZwQueryInformationToken(1) returned %08x (len = %u)\n", Status, len);
75 // goto exit2;
76 // }
77 //
78 // // TRACE("len = %u\n", len);
79 //
80 // tu = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
81 //
82 // Status = ZwQueryInformationToken(h, TokenUser, tu, len, &len);
83 //
84 // if (!NT_SUCCESS(Status)) {
85 // ERR("ZwQueryInformationToken(2) returned %08x\n", Status);
86 // goto exit3;
87 // }
88 //
89 // size = 8 + (4 * ((sid_header*)tu->User.Sid)->elements);
90 //
91 // le = uid_map_list.Flink;
92 // while (le != &uid_map_list) {
93 // um = CONTAINING_RECORD(le, uid_map, listentry);
94 //
95 // if (((sid_header*)um->sid)->elements == ((sid_header*)tu->User.Sid)->elements &&
96 // RtlCompareMemory(um->sid, tu->User.Sid, size) == size) {
97 // uid = um->uid;
98 // break;
99 // }
100 //
101 // le = le->Flink;
102 // }
103 //
104 // // sid_to_string(tu->User.Sid, s);
105 //
106 // // TRACE("got SID: %s\n", s);
107 // TRACE("uid = %u\n", uid);
108 //
109 // exit3:
110 // ExFreePool(tu);
111 //
112 // exit2:
113 // ZwClose(h);
114 //
115 // exit:
116 // PsDereferencePrimaryToken(at);
117 //
118 // return uid;
119 // }
120
121 void add_user_mapping(WCHAR* sidstring, ULONG sidstringlength, UINT32 uid) {
122 unsigned int i, np;
123 UINT8 numdashes;
124 UINT64 val;
125 ULONG sidsize;
126 sid_header* sid;
127 uid_map* um;
128 // char s[255];
129
130 if (sidstringlength < 4 ||
131 sidstring[0] != 'S' ||
132 sidstring[1] != '-' ||
133 sidstring[2] != '1' ||
134 sidstring[3] != '-') {
135 ERR("invalid SID\n");
136 return;
137 }
138
139 sidstring = &sidstring[4];
140 sidstringlength -= 4;
141
142 numdashes = 0;
143 for (i = 0; i < sidstringlength; i++) {
144 if (sidstring[i] == '-') {
145 numdashes++;
146 sidstring[i] = 0;
147 }
148 }
149
150 sidsize = 8 + (numdashes * 4);
151 sid = ExAllocatePoolWithTag(PagedPool, sidsize, ALLOC_TAG);
152 if (!sid) {
153 ERR("out of memory\n");
154 return;
155 }
156
157 sid->revision = 0x01;
158 sid->elements = numdashes;
159
160 np = 0;
161 while (sidstringlength > 0) {
162 val = 0;
163 i = 0;
164 while (sidstring[i] != '-' && i < sidstringlength) {
165 if (sidstring[i] >= '0' && sidstring[i] <= '9') {
166 val *= 10;
167 val += sidstring[i] - '0';
168 } else
169 break;
170
171 i++;
172 }
173
174 i++;
175 TRACE("val = %u, i = %u, ssl = %u\n", (UINT32)val, i, sidstringlength);
176
177 if (np == 0) {
178 sid->auth[0] = (val & 0xff0000000000) >> 40;
179 sid->auth[1] = (val & 0xff00000000) >> 32;
180 sid->auth[2] = (val & 0xff000000) >> 24;
181 sid->auth[3] = (val & 0xff0000) >> 16;
182 sid->auth[4] = (val & 0xff00) >> 8;
183 sid->auth[5] = val & 0xff;
184 } else {
185 sid->nums[np-1] = (UINT32)val;
186 }
187
188 np++;
189
190 if (sidstringlength > i) {
191 sidstringlength -= i;
192
193 sidstring = &sidstring[i];
194 } else
195 break;
196 }
197
198 // sid_to_string(sid, s);
199
200 // TRACE("%s\n", s);
201 um = ExAllocatePoolWithTag(PagedPool, sizeof(uid_map), ALLOC_TAG);
202 if (!um) {
203 ERR("out of memory\n");
204 return;
205 }
206
207 um->sid = sid;
208 um->uid = uid;
209
210 InsertTailList(&uid_map_list, &um->listentry);
211 }
212
213 static void uid_to_sid(UINT32 uid, PSID* sid) {
214 LIST_ENTRY* le;
215 uid_map* um;
216 sid_header* sh;
217 UCHAR els;
218
219 le = uid_map_list.Flink;
220 while (le != &uid_map_list) {
221 um = CONTAINING_RECORD(le, uid_map, listentry);
222
223 if (um->uid == uid) {
224 *sid = ExAllocatePoolWithTag(PagedPool, RtlLengthSid(um->sid), ALLOC_TAG);
225 if (!*sid) {
226 ERR("out of memory\n");
227 return;
228 }
229
230 RtlCopyMemory(*sid, um->sid, RtlLengthSid(um->sid));
231 return;
232 }
233
234 le = le->Flink;
235 }
236
237 if (uid == 0) { // root
238 // FIXME - find actual Administrator account, rather than SYSTEM (S-1-5-18)
239 // (of form S-1-5-21-...-500)
240
241 els = 1;
242
243 sh = ExAllocatePoolWithTag(PagedPool, sizeof(sid_header) + ((els - 1) * sizeof(UINT32)), ALLOC_TAG);
244 if (!sh) {
245 ERR("out of memory\n");
246 *sid = NULL;
247 return;
248 }
249
250 sh->revision = 1;
251 sh->elements = els;
252
253 sh->auth[0] = 0;
254 sh->auth[1] = 0;
255 sh->auth[2] = 0;
256 sh->auth[3] = 0;
257 sh->auth[4] = 0;
258 sh->auth[5] = 5;
259
260 sh->nums[0] = 18;
261 } else {
262 // fallback to S-1-22-1-X, Samba's SID scheme
263 sh = ExAllocatePoolWithTag(PagedPool, sizeof(sid_header), ALLOC_TAG);
264 if (!sh) {
265 ERR("out of memory\n");
266 *sid = NULL;
267 return;
268 }
269
270 sh->revision = 1;
271 sh->elements = 2;
272
273 sh->auth[0] = 0;
274 sh->auth[1] = 0;
275 sh->auth[2] = 0;
276 sh->auth[3] = 0;
277 sh->auth[4] = 0;
278 sh->auth[5] = 22;
279
280 sh->nums[0] = 1;
281 sh->nums[1] = uid;
282 }
283
284 *sid = sh;
285 }
286
287 UINT32 sid_to_uid(PSID sid) {
288 LIST_ENTRY* le;
289 uid_map* um;
290 sid_header* sh = sid;
291
292 le = uid_map_list.Flink;
293 while (le != &uid_map_list) {
294 um = CONTAINING_RECORD(le, uid_map, listentry);
295
296 if (RtlEqualSid(sid, um->sid))
297 return um->uid;
298
299 le = le->Flink;
300 }
301
302 if (RtlEqualSid(sid, &sid_SY))
303 return 0; // root
304
305 // Samba's SID scheme: S-1-22-1-X
306 if (sh->revision == 1 && sh->elements == 2 && sh->auth[0] == 0 && sh->auth[1] == 0 && sh->auth[2] == 0 && sh->auth[3] == 0 &&
307 sh->auth[4] == 0 && sh->auth[5] == 22 && sh->nums[0] == 1)
308 return sh->nums[1];
309
310 return UID_NOBODY;
311 }
312
313 static void gid_to_sid(UINT32 gid, PSID* sid) {
314 sid_header* sh;
315 UCHAR els;
316
317 // FIXME - do this properly?
318
319 // fallback to S-1-22-2-X, Samba's SID scheme
320 els = 2;
321 sh = ExAllocatePoolWithTag(PagedPool, sizeof(sid_header) + ((els - 1) * sizeof(UINT32)), ALLOC_TAG);
322 if (!sh) {
323 ERR("out of memory\n");
324 *sid = NULL;
325 return;
326 }
327
328 sh->revision = 1;
329 sh->elements = els;
330
331 sh->auth[0] = 0;
332 sh->auth[1] = 0;
333 sh->auth[2] = 0;
334 sh->auth[3] = 0;
335 sh->auth[4] = 0;
336 sh->auth[5] = 22;
337
338 sh->nums[0] = 2;
339 sh->nums[1] = gid;
340
341 *sid = sh;
342 }
343
344 static ACL* load_default_acl() {
345 ULONG size;
346 ACL* acl;
347 ACCESS_ALLOWED_ACE* aaa;
348 UINT32 i;
349
350 size = sizeof(ACL);
351 i = 0;
352 while (def_dacls[i].sid) {
353 size += sizeof(ACCESS_ALLOWED_ACE);
354 size += 8 + (def_dacls[i].sid->elements * sizeof(UINT32)) - sizeof(ULONG);
355 i++;
356 }
357
358 acl = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
359 if (!acl) {
360 ERR("out of memory\n");
361 return NULL;
362 }
363
364 acl->AclRevision = ACL_REVISION;
365 acl->Sbz1 = 0;
366 acl->AclSize = size;
367 acl->AceCount = i;
368 acl->Sbz2 = 0;
369
370 aaa = (ACCESS_ALLOWED_ACE*)&acl[1];
371 i = 0;
372 while (def_dacls[i].sid) {
373 aaa->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
374 aaa->Header.AceFlags = def_dacls[i].flags;
375 aaa->Header.AceSize = sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG) + 8 + (def_dacls[i].sid->elements * sizeof(UINT32));
376 aaa->Mask = def_dacls[i].mask;
377
378 RtlCopyMemory(&aaa->SidStart, def_dacls[i].sid, 8 + (def_dacls[i].sid->elements * sizeof(UINT32)));
379
380 aaa = (ACCESS_ALLOWED_ACE*)((UINT8*)aaa + aaa->Header.AceSize);
381
382 i++;
383 }
384
385 return acl;
386 }
387
388 // static void STDCALL sid_to_string(PSID sid, char* s) {
389 // sid_header* sh = (sid_header*)sid;
390 // LARGE_INTEGER authnum;
391 // UINT8 i;
392 //
393 // authnum.LowPart = sh->auth[5] | (sh->auth[4] << 8) | (sh->auth[3] << 16) | (sh->auth[2] << 24);
394 // authnum.HighPart = sh->auth[1] | (sh->auth[0] << 8);
395 //
396 // sprintf(s, "S-%u-%u", sh->revision, (UINT32)authnum.QuadPart);
397 //
398 // for (i = 0; i < sh->elements; i++) {
399 // sprintf(s, "%s-%u", s, sh->nums[i]);
400 // }
401 // }
402
403 static BOOL get_sd_from_xattr(fcb* fcb) {
404 ULONG buflen;
405 NTSTATUS Status;
406 PSID sid, usersid;
407
408 if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_NTACL, EA_NTACL_HASH, (UINT8**)&fcb->sd, (UINT16*)&buflen))
409 return FALSE;
410
411 TRACE("using xattr " EA_NTACL " for security descriptor\n");
412
413 if (fcb->inode_item.st_uid != UID_NOBODY) {
414 BOOLEAN defaulted;
415
416 Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &sid, &defaulted);
417 if (!NT_SUCCESS(Status)) {
418 ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
419 } else {
420 uid_to_sid(fcb->inode_item.st_uid, &usersid);
421
422 if (!usersid) {
423 ERR("out of memory\n");
424 return FALSE;
425 }
426
427 if (!RtlEqualSid(sid, usersid)) {
428 SECURITY_DESCRIPTOR *newsd, *newsd2;
429 ULONG sdsize, daclsize, saclsize, ownersize, groupsize;
430 ACL *dacl, *sacl;
431 PSID owner, group;
432
433 sdsize = daclsize = saclsize = ownersize = groupsize = 0;
434
435 Status = RtlSelfRelativeToAbsoluteSD(fcb->sd, NULL, &sdsize, NULL, &daclsize, NULL, &saclsize, NULL, &ownersize, NULL, &groupsize);
436
437 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) {
438 ERR("RtlSelfRelativeToAbsoluteSD 1 returned %08x\n", Status);
439 }
440
441 TRACE("sdsize = %u, daclsize = %u, saclsize = %u, ownersize = %u, groupsize = %u\n", sdsize, daclsize, saclsize, ownersize, groupsize);
442
443 newsd2 = sdsize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, sdsize, ALLOC_TAG);
444 dacl = daclsize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, daclsize, ALLOC_TAG);
445 sacl = saclsize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, saclsize, ALLOC_TAG);
446 owner = ownersize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, ownersize, ALLOC_TAG);
447 group = groupsize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, groupsize, ALLOC_TAG);
448
449 if ((sdsize > 0 && !newsd2) || (daclsize > 0 && !dacl) || (saclsize > 0 && !sacl) || (ownersize > 0 && !owner) || (groupsize > 0 && !group)) {
450 ERR("out of memory\n");
451 if (newsd2) ExFreePool(newsd2);
452 if (dacl) ExFreePool(dacl);
453 if (sacl) ExFreePool(sacl);
454 if (owner) ExFreePool(owner);
455 if (group) ExFreePool(group);
456 ExFreePool(usersid);
457 return FALSE;
458 }
459
460 Status = RtlSelfRelativeToAbsoluteSD(fcb->sd, newsd2, &sdsize, dacl, &daclsize, sacl, &saclsize, owner, &ownersize, group, &groupsize);
461
462 if (!NT_SUCCESS(Status)) {
463 ERR("RtlSelfRelativeToAbsoluteSD returned %08x\n", Status);
464 if (newsd2) ExFreePool(newsd2);
465 if (dacl) ExFreePool(dacl);
466 if (sacl) ExFreePool(sacl);
467 if (owner) ExFreePool(owner);
468 if (group) ExFreePool(group);
469 ExFreePool(usersid);
470 return FALSE;
471 }
472
473 Status = RtlSetOwnerSecurityDescriptor(newsd2, usersid, FALSE);
474 if (!NT_SUCCESS(Status)) {
475 ERR("RtlSetOwnerSecurityDescriptor returned %08x\n", Status);
476 if (newsd2) ExFreePool(newsd2);
477 if (dacl) ExFreePool(dacl);
478 if (sacl) ExFreePool(sacl);
479 if (owner) ExFreePool(owner);
480 if (group) ExFreePool(group);
481 ExFreePool(usersid);
482 return FALSE;
483 }
484
485 buflen = 0;
486 Status = RtlAbsoluteToSelfRelativeSD(newsd2, NULL, &buflen);
487 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) {
488 ERR("RtlAbsoluteToSelfRelativeSD 1 returned %08x\n", Status);
489 if (newsd2) ExFreePool(newsd2);
490 if (dacl) ExFreePool(dacl);
491 if (sacl) ExFreePool(sacl);
492 if (owner) ExFreePool(owner);
493 if (group) ExFreePool(group);
494 ExFreePool(usersid);
495 return FALSE;
496 }
497
498 if (buflen == 0 || NT_SUCCESS(Status)) {
499 ERR("RtlAbsoluteToSelfRelativeSD said SD is zero-length\n");
500 if (newsd2) ExFreePool(newsd2);
501 if (dacl) ExFreePool(dacl);
502 if (sacl) ExFreePool(sacl);
503 if (owner) ExFreePool(owner);
504 if (group) ExFreePool(group);
505 ExFreePool(usersid);
506 return FALSE;
507 }
508
509 newsd = ExAllocatePoolWithTag(PagedPool, buflen, ALLOC_TAG);
510 if (!newsd) {
511 ERR("out of memory\n");
512 if (newsd2) ExFreePool(newsd2);
513 if (dacl) ExFreePool(dacl);
514 if (sacl) ExFreePool(sacl);
515 if (owner) ExFreePool(owner);
516 if (group) ExFreePool(group);
517 ExFreePool(usersid);
518 return FALSE;
519 }
520
521 Status = RtlAbsoluteToSelfRelativeSD(newsd2, newsd, &buflen);
522
523 if (!NT_SUCCESS(Status)) {
524 ERR("RtlAbsoluteToSelfRelativeSD 2 returned %08x\n", Status);
525 if (newsd2) ExFreePool(newsd2);
526 if (dacl) ExFreePool(dacl);
527 if (sacl) ExFreePool(sacl);
528 if (owner) ExFreePool(owner);
529 if (group) ExFreePool(group);
530 ExFreePool(usersid);
531 ExFreePool(newsd);
532 return FALSE;
533 }
534
535 ExFreePool(fcb->sd);
536
537 fcb->sd = newsd;
538
539 if (newsd2) ExFreePool(newsd2);
540 if (dacl) ExFreePool(dacl);
541 if (sacl) ExFreePool(sacl);
542 if (owner) ExFreePool(owner);
543 if (group) ExFreePool(group);
544 }
545
546 ExFreePool(usersid);
547 }
548 }
549
550 // FIXME - check GID here if not GID_NOBODY
551
552 return TRUE;
553 }
554
555 static void get_top_level_sd(fcb* fcb) {
556 NTSTATUS Status;
557 SECURITY_DESCRIPTOR sd;
558 ULONG buflen;
559 ACL* acl = NULL;
560 PSID usersid = NULL, groupsid = NULL;
561
562 Status = RtlCreateSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
563
564 if (!NT_SUCCESS(Status)) {
565 ERR("RtlCreateSecurityDescriptor returned %08x\n", Status);
566 goto end;
567 }
568
569 // if (fcb->inode_item.st_uid != UID_NOBODY) {
570 uid_to_sid(fcb->inode_item.st_uid, &usersid);
571 if (!usersid) {
572 ERR("out of memory\n");
573 goto end;
574 }
575
576 RtlSetOwnerSecurityDescriptor(&sd, usersid, FALSE);
577
578 if (!NT_SUCCESS(Status)) {
579 ERR("RtlSetOwnerSecurityDescriptor returned %08x\n", Status);
580 goto end;
581 }
582 // }
583
584 // if (fcb->inode_item.st_gid != GID_NOBODY) {
585 gid_to_sid(fcb->inode_item.st_gid, &groupsid);
586 if (!groupsid) {
587 ERR("out of memory\n");
588 Status = STATUS_INSUFFICIENT_RESOURCES;
589 goto end;
590 }
591
592 RtlSetGroupSecurityDescriptor(&sd, groupsid, FALSE);
593
594 if (!NT_SUCCESS(Status)) {
595 ERR("RtlSetGroupSecurityDescriptor returned %08x\n", Status);
596 goto end;
597 }
598 // }
599
600 acl = load_default_acl();
601
602 if (!acl) {
603 ERR("out of memory\n");
604 goto end;
605 }
606
607 Status = RtlSetDaclSecurityDescriptor(&sd, TRUE, acl, FALSE);
608
609 if (!NT_SUCCESS(Status)) {
610 ERR("RtlSetDaclSecurityDescriptor returned %08x\n", Status);
611 goto end;
612 }
613
614 // FIXME - SACL_SECURITY_INFORMATION
615
616 buflen = 0;
617
618 // get sd size
619 Status = RtlAbsoluteToSelfRelativeSD(&sd, NULL, &buflen);
620 if (Status != STATUS_SUCCESS && Status != STATUS_BUFFER_TOO_SMALL) {
621 ERR("RtlAbsoluteToSelfRelativeSD 1 returned %08x\n", Status);
622 goto end;
623 }
624
625 // fcb->sdlen = buflen;
626
627 if (buflen == 0 || Status == STATUS_SUCCESS) {
628 TRACE("RtlAbsoluteToSelfRelativeSD said SD is zero-length\n");
629 goto end;
630 }
631
632 fcb->sd = ExAllocatePoolWithTag(PagedPool, buflen, ALLOC_TAG);
633 if (!fcb->sd) {
634 ERR("out of memory\n");
635 Status = STATUS_INSUFFICIENT_RESOURCES;
636 goto end;
637 }
638
639 Status = RtlAbsoluteToSelfRelativeSD(&sd, fcb->sd, &buflen);
640
641 if (!NT_SUCCESS(Status)) {
642 ERR("RtlAbsoluteToSelfRelativeSD 2 returned %08x\n", Status);
643 goto end;
644 }
645
646 end:
647 if (acl)
648 ExFreePool(acl);
649
650 if (usersid)
651 ExFreePool(usersid);
652
653 if (groupsid)
654 ExFreePool(groupsid);
655 }
656
657 void fcb_get_sd(fcb* fcb, struct _fcb* parent) {
658 NTSTATUS Status;
659 PSID usersid = NULL, groupsid = NULL;
660 SECURITY_SUBJECT_CONTEXT subjcont;
661
662 if (get_sd_from_xattr(fcb))
663 return;
664
665 if (!parent) {
666 get_top_level_sd(fcb);
667 return;
668 }
669
670 SeCaptureSubjectContext(&subjcont);
671
672 Status = SeAssignSecurityEx(parent->sd, NULL, (void**)&fcb->sd, NULL, fcb->type == BTRFS_TYPE_DIRECTORY, SEF_DACL_AUTO_INHERIT,
673 &subjcont, IoGetFileObjectGenericMapping(), PagedPool);
674 if (!NT_SUCCESS(Status)) {
675 ERR("SeAssignSecurityEx returned %08x\n", Status);
676 }
677
678 uid_to_sid(fcb->inode_item.st_uid, &usersid);
679 if (!usersid) {
680 ERR("out of memory\n");
681 return;
682 }
683
684 RtlSetOwnerSecurityDescriptor(&fcb->sd, usersid, FALSE);
685
686 gid_to_sid(fcb->inode_item.st_gid, &groupsid);
687 if (!groupsid) {
688 ERR("out of memory\n");
689 return;
690 }
691
692 RtlSetGroupSecurityDescriptor(&fcb->sd, groupsid, FALSE);
693
694 ExFreePool(usersid);
695 ExFreePool(groupsid);
696 }
697
698 static NTSTATUS STDCALL get_file_security(device_extension* Vcb, PFILE_OBJECT FileObject, SECURITY_DESCRIPTOR* relsd, ULONG* buflen, SECURITY_INFORMATION flags) {
699 NTSTATUS Status;
700 fcb* fcb = FileObject->FsContext;
701 ccb* ccb = FileObject->FsContext2;
702 file_ref* fileref = ccb ? ccb->fileref : NULL;
703
704 if (fcb->ads) {
705 if (fileref && fileref->parent)
706 fcb = fileref->parent->fcb;
707 else {
708 ERR("could not get parent fcb for stream\n");
709 return STATUS_INTERNAL_ERROR;
710 }
711 }
712
713 // TRACE("buflen = %u, fcb->sdlen = %u\n", *buflen, fcb->sdlen);
714
715 // Why (void**)? Is this a bug in mingw?
716 Status = SeQuerySecurityDescriptorInfo(&flags, relsd, buflen, (void**)&fcb->sd);
717
718 if (Status == STATUS_BUFFER_TOO_SMALL)
719 TRACE("SeQuerySecurityDescriptorInfo returned %08x\n", Status);
720 else if (!NT_SUCCESS(Status))
721 ERR("SeQuerySecurityDescriptorInfo returned %08x\n", Status);
722
723 return Status;
724 }
725
726 NTSTATUS STDCALL drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
727 NTSTATUS Status;
728 SECURITY_DESCRIPTOR* sd;
729 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
730 device_extension* Vcb = DeviceObject->DeviceExtension;
731 ULONG buflen;
732 BOOL top_level;
733 PFILE_OBJECT FileObject = IrpSp->FileObject;
734 ccb* ccb = FileObject ? FileObject->FsContext2 : NULL;
735
736 TRACE("query security\n");
737
738 FsRtlEnterFileSystem();
739
740 top_level = is_top_level(Irp);
741
742 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
743 Status = part0_passthrough(DeviceObject, Irp);
744 goto exit;
745 }
746
747 if (!ccb) {
748 ERR("no ccb\n");
749 Status = STATUS_INVALID_PARAMETER;
750 goto end;
751 }
752
753 if (Irp->RequestorMode == UserMode && !(ccb->access & READ_CONTROL)) {
754 WARN("insufficient permissions\n");
755 Status = STATUS_ACCESS_DENIED;
756 goto end;
757 }
758
759 Status = STATUS_SUCCESS;
760
761 Irp->IoStatus.Information = 0;
762
763 if (IrpSp->Parameters.QuerySecurity.SecurityInformation & OWNER_SECURITY_INFORMATION)
764 TRACE("OWNER_SECURITY_INFORMATION\n");
765
766 if (IrpSp->Parameters.QuerySecurity.SecurityInformation & GROUP_SECURITY_INFORMATION)
767 TRACE("GROUP_SECURITY_INFORMATION\n");
768
769 if (IrpSp->Parameters.QuerySecurity.SecurityInformation & DACL_SECURITY_INFORMATION)
770 TRACE("DACL_SECURITY_INFORMATION\n");
771
772 if (IrpSp->Parameters.QuerySecurity.SecurityInformation & SACL_SECURITY_INFORMATION)
773 TRACE("SACL_SECURITY_INFORMATION\n");
774
775 TRACE("length = %u\n", IrpSp->Parameters.QuerySecurity.Length);
776
777 sd = map_user_buffer(Irp);
778 // sd = Irp->AssociatedIrp.SystemBuffer;
779 TRACE("sd = %p\n", sd);
780
781 if (Irp->MdlAddress && !sd) {
782 ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
783 Status = STATUS_INSUFFICIENT_RESOURCES;
784 goto end;
785 }
786
787 buflen = IrpSp->Parameters.QuerySecurity.Length;
788
789 Status = get_file_security(Vcb, IrpSp->FileObject, sd, &buflen, IrpSp->Parameters.QuerySecurity.SecurityInformation);
790
791 if (NT_SUCCESS(Status))
792 Irp->IoStatus.Information = IrpSp->Parameters.QuerySecurity.Length;
793 else if (Status == STATUS_BUFFER_TOO_SMALL) {
794 Irp->IoStatus.Information = buflen;
795 Status = STATUS_BUFFER_OVERFLOW;
796 } else
797 Irp->IoStatus.Information = 0;
798
799 end:
800 TRACE("Irp->IoStatus.Information = %u\n", Irp->IoStatus.Information);
801
802 Irp->IoStatus.Status = Status;
803
804 IoCompleteRequest(Irp, IO_NO_INCREMENT);
805
806 exit:
807 if (top_level)
808 IoSetTopLevelIrp(NULL);
809
810 FsRtlExitFileSystem();
811
812 TRACE("returning %08x\n", Status);
813
814 return Status;
815 }
816
817 static NTSTATUS STDCALL set_file_security(device_extension* Vcb, PFILE_OBJECT FileObject, SECURITY_DESCRIPTOR* sd, SECURITY_INFORMATION flags) {
818 NTSTATUS Status;
819 fcb* fcb = FileObject->FsContext;
820 ccb* ccb = FileObject->FsContext2;
821 file_ref* fileref = ccb ? ccb->fileref : NULL;
822 SECURITY_DESCRIPTOR* oldsd;
823 LARGE_INTEGER time;
824 BTRFS_TIME now;
825
826 TRACE("(%p, %p, %p, %x)\n", Vcb, FileObject, sd, flags);
827
828 if (Vcb->readonly)
829 return STATUS_MEDIA_WRITE_PROTECTED;
830
831 if (fcb->ads) {
832 if (fileref && fileref->parent)
833 fcb = fileref->parent->fcb;
834 else {
835 ERR("could not find parent fcb for stream\n");
836 Status = STATUS_INTERNAL_ERROR;
837 goto end;
838 }
839 }
840
841 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
842
843 if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
844 Status = STATUS_ACCESS_DENIED;
845 goto end;
846 }
847
848 oldsd = fcb->sd;
849
850 Status = SeSetSecurityDescriptorInfo(NULL, &flags, sd, (void**)&fcb->sd, PagedPool, IoGetFileObjectGenericMapping());
851
852 if (!NT_SUCCESS(Status)) {
853 ERR("SeSetSecurityDescriptorInfo returned %08x\n", Status);
854 goto end;
855 }
856
857 ExFreePool(oldsd);
858
859 KeQuerySystemTime(&time);
860 win_time_to_unix(time, &now);
861
862 fcb->inode_item.transid = Vcb->superblock.generation;
863 fcb->inode_item.st_ctime = now;
864 fcb->inode_item.sequence++;
865
866 if (flags & OWNER_SECURITY_INFORMATION) {
867 PSID owner;
868 BOOLEAN defaulted;
869
870 Status = RtlGetOwnerSecurityDescriptor(sd, &owner, &defaulted);
871
872 if (!NT_SUCCESS(Status)) {
873 ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
874 goto end;
875 }
876
877 fcb->inode_item.st_uid = sid_to_uid(owner);
878 }
879
880 fcb->sd_dirty = TRUE;
881
882 fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
883 fcb->subvol->root_item.ctime = now;
884
885 mark_fcb_dirty(fcb);
886
887 send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_SECURITY, FILE_ACTION_MODIFIED);
888
889 end:
890 ExReleaseResourceLite(fcb->Header.Resource);
891
892 return Status;
893 }
894
895 NTSTATUS STDCALL drv_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
896 NTSTATUS Status;
897 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
898 PFILE_OBJECT FileObject = IrpSp->FileObject;
899 ccb* ccb = FileObject ? FileObject->FsContext2 : NULL;
900 device_extension* Vcb = DeviceObject->DeviceExtension;
901 ULONG access_req = 0;
902 BOOL top_level;
903
904 TRACE("set security\n");
905
906 FsRtlEnterFileSystem();
907
908 top_level = is_top_level(Irp);
909
910 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
911 Status = part0_passthrough(DeviceObject, Irp);
912 goto exit;
913 }
914
915 if (!ccb) {
916 ERR("no ccb\n");
917 Status = STATUS_INVALID_PARAMETER;
918 goto end;
919 }
920
921 Status = STATUS_SUCCESS;
922
923 Irp->IoStatus.Information = 0;
924
925 if (IrpSp->Parameters.QuerySecurity.SecurityInformation & OWNER_SECURITY_INFORMATION) {
926 TRACE("OWNER_SECURITY_INFORMATION\n");
927 access_req |= WRITE_OWNER;
928 }
929
930 if (IrpSp->Parameters.QuerySecurity.SecurityInformation & GROUP_SECURITY_INFORMATION) {
931 TRACE("GROUP_SECURITY_INFORMATION\n");
932 access_req |= WRITE_OWNER;
933 }
934
935 if (IrpSp->Parameters.QuerySecurity.SecurityInformation & DACL_SECURITY_INFORMATION) {
936 TRACE("DACL_SECURITY_INFORMATION\n");
937 access_req |= WRITE_DAC;
938 }
939
940 if (IrpSp->Parameters.QuerySecurity.SecurityInformation & SACL_SECURITY_INFORMATION) {
941 TRACE("SACL_SECURITY_INFORMATION\n");
942 access_req |= ACCESS_SYSTEM_SECURITY;
943 }
944
945 if ((ccb->access & access_req) != access_req) {
946 Status = STATUS_ACCESS_DENIED;
947 WARN("insufficient privileges\n");
948 goto end;
949 }
950
951 Status = set_file_security(DeviceObject->DeviceExtension, FileObject, IrpSp->Parameters.SetSecurity.SecurityDescriptor,
952 IrpSp->Parameters.SetSecurity.SecurityInformation);
953
954 end:
955 Irp->IoStatus.Status = Status;
956
957 IoCompleteRequest(Irp, IO_NO_INCREMENT);
958
959 TRACE("returning %08x\n", Status);
960
961 exit:
962 if (top_level)
963 IoSetTopLevelIrp(NULL);
964
965 FsRtlExitFileSystem();
966
967 return Status;
968 }
969
970 NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* parfileref, ACCESS_STATE* as) {
971 NTSTATUS Status;
972 PSID owner;
973 BOOLEAN defaulted;
974
975 Status = SeAssignSecurityEx(parfileref ? parfileref->fcb->sd : NULL, as->SecurityDescriptor, (void**)&fcb->sd, NULL, fcb->type == BTRFS_TYPE_DIRECTORY,
976 SEF_SACL_AUTO_INHERIT, &as->SubjectSecurityContext, IoGetFileObjectGenericMapping(), PagedPool);
977
978 if (!NT_SUCCESS(Status)) {
979 ERR("SeAssignSecurityEx returned %08x\n", Status);
980 return Status;
981 }
982
983 Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &owner, &defaulted);
984 if (!NT_SUCCESS(Status)) {
985 ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
986 fcb->inode_item.st_uid = UID_NOBODY;
987 } else {
988 fcb->inode_item.st_uid = sid_to_uid(&owner);
989 }
990
991 return STATUS_SUCCESS;
992 }