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