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 (sidstring[i] != '-' && i < sidstringlength) {
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 = %u\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 (sidstring[i] != '-' && i < sidstringlength) {
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 = %u\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 %08x\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 %08x\n", Status);
432         goto end;
433     }
434 
435     RtlSetOwnerSecurityDescriptor(&sd, usersid, false);
436 
437     if (!NT_SUCCESS(Status)) {
438         ERR("RtlSetOwnerSecurityDescriptor returned %08x\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         Status = STATUS_INSUFFICIENT_RESOURCES;
446         goto end;
447     }
448 
449     RtlSetGroupSecurityDescriptor(&sd, groupsid, false);
450 
451     if (!NT_SUCCESS(Status)) {
452         ERR("RtlSetGroupSecurityDescriptor returned %08x\n", Status);
453         goto end;
454     }
455 
456     acl = load_default_acl();
457 
458     if (!acl) {
459         ERR("out of memory\n");
460         goto end;
461     }
462 
463     Status = RtlSetDaclSecurityDescriptor(&sd, true, acl, false);
464 
465     if (!NT_SUCCESS(Status)) {
466         ERR("RtlSetDaclSecurityDescriptor returned %08x\n", Status);
467         goto end;
468     }
469 
470     // FIXME - SACL_SECURITY_INFORMATION
471 
472     buflen = 0;
473 
474     // get sd size
475     Status = RtlAbsoluteToSelfRelativeSD(&sd, NULL, &buflen);
476     if (Status != STATUS_SUCCESS && Status != STATUS_BUFFER_TOO_SMALL) {
477         ERR("RtlAbsoluteToSelfRelativeSD 1 returned %08x\n", Status);
478         goto end;
479     }
480 
481     if (buflen == 0 || Status == STATUS_SUCCESS) {
482         TRACE("RtlAbsoluteToSelfRelativeSD said SD is zero-length\n");
483         goto end;
484     }
485 
486     fcb->sd = ExAllocatePoolWithTag(PagedPool, buflen, ALLOC_TAG);
487     if (!fcb->sd) {
488         ERR("out of memory\n");
489         Status = STATUS_INSUFFICIENT_RESOURCES;
490         goto end;
491     }
492 
493     Status = RtlAbsoluteToSelfRelativeSD(&sd, fcb->sd, &buflen);
494 
495     if (!NT_SUCCESS(Status)) {
496         ERR("RtlAbsoluteToSelfRelativeSD 2 returned %08x\n", Status);
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 
517     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))
518         return;
519 
520     if (!parent) {
521         get_top_level_sd(fcb);
522         return;
523     }
524 
525     SeCaptureSubjectContext(&subjcont);
526 
527     Status = SeAssignSecurityEx(parent->sd, NULL, (void**)&fcb->sd, NULL, fcb->type == BTRFS_TYPE_DIRECTORY, SEF_DACL_AUTO_INHERIT,
528                                 &subjcont, IoGetFileObjectGenericMapping(), PagedPool);
529     if (!NT_SUCCESS(Status)) {
530         ERR("SeAssignSecurityEx returned %08x\n", Status);
531     }
532 
533     Status = uid_to_sid(fcb->inode_item.st_uid, &usersid);
534     if (!NT_SUCCESS(Status)) {
535         ERR("uid_to_sid returned %08x\n", Status);
536         return;
537     }
538 
539     RtlSetOwnerSecurityDescriptor(&fcb->sd, usersid, false);
540 
541     gid_to_sid(fcb->inode_item.st_gid, &groupsid);
542     if (!groupsid) {
543         ERR("out of memory\n");
544         return;
545     }
546 
547     RtlSetGroupSecurityDescriptor(&fcb->sd, groupsid, false);
548 
549     ExFreePool(usersid);
550     ExFreePool(groupsid);
551 }
552 
553 static NTSTATUS get_file_security(PFILE_OBJECT FileObject, SECURITY_DESCRIPTOR* relsd, ULONG* buflen, SECURITY_INFORMATION flags) {
554     NTSTATUS Status;
555     fcb* fcb = FileObject->FsContext;
556     ccb* ccb = FileObject->FsContext2;
557     file_ref* fileref = ccb ? ccb->fileref : NULL;
558 
559     if (fcb->ads) {
560         if (fileref && fileref->parent)
561             fcb = fileref->parent->fcb;
562         else {
563             ERR("could not get parent fcb for stream\n");
564             return STATUS_INTERNAL_ERROR;
565         }
566     }
567 
568     // Why (void**)? Is this a bug in mingw?
569     Status = SeQuerySecurityDescriptorInfo(&flags, relsd, buflen, (void**)&fcb->sd);
570 
571     if (Status == STATUS_BUFFER_TOO_SMALL)
572         TRACE("SeQuerySecurityDescriptorInfo returned %08x\n", Status);
573     else if (!NT_SUCCESS(Status))
574         ERR("SeQuerySecurityDescriptorInfo returned %08x\n", Status);
575 
576     return Status;
577 }
578 
579 _Dispatch_type_(IRP_MJ_QUERY_SECURITY)
580 _Function_class_(DRIVER_DISPATCH)
581 NTSTATUS __stdcall drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
582     NTSTATUS Status;
583     SECURITY_DESCRIPTOR* sd;
584     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
585     device_extension* Vcb = DeviceObject->DeviceExtension;
586     ULONG buflen;
587     bool top_level;
588     PFILE_OBJECT FileObject = IrpSp->FileObject;
589     ccb* ccb = FileObject ? FileObject->FsContext2 : NULL;
590 
591     FsRtlEnterFileSystem();
592 
593     TRACE("query security\n");
594 
595     top_level = is_top_level(Irp);
596 
597     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
598         Status = vol_query_security(DeviceObject, Irp);
599         goto end;
600     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
601         Status = STATUS_INVALID_PARAMETER;
602         goto end;
603     }
604 
605     if (!ccb) {
606         ERR("no ccb\n");
607         Status = STATUS_INVALID_PARAMETER;
608         goto end;
609     }
610 
611     if (Irp->RequestorMode == UserMode && !(ccb->access & READ_CONTROL)) {
612         WARN("insufficient permissions\n");
613         Status = STATUS_ACCESS_DENIED;
614         goto end;
615     }
616 
617     Status = STATUS_SUCCESS;
618 
619     Irp->IoStatus.Information = 0;
620 
621     if (IrpSp->Parameters.QuerySecurity.SecurityInformation & OWNER_SECURITY_INFORMATION)
622         TRACE("OWNER_SECURITY_INFORMATION\n");
623 
624     if (IrpSp->Parameters.QuerySecurity.SecurityInformation & GROUP_SECURITY_INFORMATION)
625         TRACE("GROUP_SECURITY_INFORMATION\n");
626 
627     if (IrpSp->Parameters.QuerySecurity.SecurityInformation & DACL_SECURITY_INFORMATION)
628         TRACE("DACL_SECURITY_INFORMATION\n");
629 
630     if (IrpSp->Parameters.QuerySecurity.SecurityInformation & SACL_SECURITY_INFORMATION)
631         TRACE("SACL_SECURITY_INFORMATION\n");
632 
633     TRACE("length = %u\n", IrpSp->Parameters.QuerySecurity.Length);
634 
635     sd = map_user_buffer(Irp, NormalPagePriority);
636     TRACE("sd = %p\n", sd);
637 
638     if (Irp->MdlAddress && !sd) {
639         ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
640         Status = STATUS_INSUFFICIENT_RESOURCES;
641         goto end;
642     }
643 
644     buflen = IrpSp->Parameters.QuerySecurity.Length;
645 
646     Status = get_file_security(IrpSp->FileObject, sd, &buflen, IrpSp->Parameters.QuerySecurity.SecurityInformation);
647 
648     if (NT_SUCCESS(Status))
649         Irp->IoStatus.Information = IrpSp->Parameters.QuerySecurity.Length;
650     else if (Status == STATUS_BUFFER_TOO_SMALL) {
651         Irp->IoStatus.Information = buflen;
652         Status = STATUS_BUFFER_OVERFLOW;
653     } else
654         Irp->IoStatus.Information = 0;
655 
656 end:
657     TRACE("Irp->IoStatus.Information = %u\n", Irp->IoStatus.Information);
658 
659     Irp->IoStatus.Status = Status;
660 
661     IoCompleteRequest(Irp, IO_NO_INCREMENT);
662 
663     if (top_level)
664         IoSetTopLevelIrp(NULL);
665 
666     TRACE("returning %08x\n", Status);
667 
668     FsRtlExitFileSystem();
669 
670     return Status;
671 }
672 
673 static NTSTATUS set_file_security(device_extension* Vcb, PFILE_OBJECT FileObject, SECURITY_DESCRIPTOR* sd, PSECURITY_INFORMATION flags, PIRP Irp) {
674     NTSTATUS Status;
675     fcb* fcb = FileObject->FsContext;
676     ccb* ccb = FileObject->FsContext2;
677     file_ref* fileref = ccb ? ccb->fileref : NULL;
678     SECURITY_DESCRIPTOR* oldsd;
679     LARGE_INTEGER time;
680     BTRFS_TIME now;
681 
682     TRACE("(%p, %p, %p, %x)\n", Vcb, FileObject, sd, *flags);
683 
684     if (Vcb->readonly)
685         return STATUS_MEDIA_WRITE_PROTECTED;
686 
687     if (fcb->ads) {
688         if (fileref && fileref->parent)
689             fcb = fileref->parent->fcb;
690         else {
691             ERR("could not find parent fcb for stream\n");
692             return STATUS_INTERNAL_ERROR;
693         }
694     }
695 
696     if (!fcb || !ccb)
697         return STATUS_INVALID_PARAMETER;
698 
699     ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
700 
701     if (is_subvol_readonly(fcb->subvol, Irp)) {
702         Status = STATUS_ACCESS_DENIED;
703         goto end;
704     }
705 
706     oldsd = fcb->sd;
707 
708     Status = SeSetSecurityDescriptorInfo(NULL, flags, sd, (void**)&fcb->sd, PagedPool, IoGetFileObjectGenericMapping());
709 
710     if (!NT_SUCCESS(Status)) {
711         ERR("SeSetSecurityDescriptorInfo returned %08x\n", Status);
712         goto end;
713     }
714 
715     ExFreePool(oldsd);
716 
717     KeQuerySystemTime(&time);
718     win_time_to_unix(time, &now);
719 
720     fcb->inode_item.transid = Vcb->superblock.generation;
721 
722     if (!ccb->user_set_change_time)
723         fcb->inode_item.st_ctime = now;
724 
725     fcb->inode_item.sequence++;
726 
727     fcb->sd_dirty = true;
728     fcb->sd_deleted = false;
729     fcb->inode_item_changed = true;
730 
731     fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
732     fcb->subvol->root_item.ctime = now;
733 
734     mark_fcb_dirty(fcb);
735 
736     send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_SECURITY, FILE_ACTION_MODIFIED, NULL);
737 
738 end:
739     ExReleaseResourceLite(fcb->Header.Resource);
740 
741     return Status;
742 }
743 
744 _Dispatch_type_(IRP_MJ_SET_SECURITY)
745 _Function_class_(DRIVER_DISPATCH)
746 NTSTATUS __stdcall drv_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
747     NTSTATUS Status;
748     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
749     PFILE_OBJECT FileObject = IrpSp->FileObject;
750     ccb* ccb = FileObject ? FileObject->FsContext2 : NULL;
751     device_extension* Vcb = DeviceObject->DeviceExtension;
752     ULONG access_req = 0;
753     bool top_level;
754 
755     FsRtlEnterFileSystem();
756 
757     TRACE("set security\n");
758 
759     top_level = is_top_level(Irp);
760 
761     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
762         Status = vol_set_security(DeviceObject, Irp);
763         goto end;
764     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
765         Status = STATUS_INVALID_PARAMETER;
766         goto end;
767     }
768 
769     if (!ccb) {
770         ERR("no ccb\n");
771         Status = STATUS_INVALID_PARAMETER;
772         goto end;
773     }
774 
775     Status = STATUS_SUCCESS;
776 
777     Irp->IoStatus.Information = 0;
778 
779     if (IrpSp->Parameters.SetSecurity.SecurityInformation & OWNER_SECURITY_INFORMATION) {
780         TRACE("OWNER_SECURITY_INFORMATION\n");
781         access_req |= WRITE_OWNER;
782     }
783 
784     if (IrpSp->Parameters.SetSecurity.SecurityInformation & GROUP_SECURITY_INFORMATION) {
785         TRACE("GROUP_SECURITY_INFORMATION\n");
786         access_req |= WRITE_OWNER;
787     }
788 
789     if (IrpSp->Parameters.SetSecurity.SecurityInformation & DACL_SECURITY_INFORMATION) {
790         TRACE("DACL_SECURITY_INFORMATION\n");
791         access_req |= WRITE_DAC;
792     }
793 
794     if (IrpSp->Parameters.SetSecurity.SecurityInformation & SACL_SECURITY_INFORMATION) {
795         TRACE("SACL_SECURITY_INFORMATION\n");
796         access_req |= ACCESS_SYSTEM_SECURITY;
797     }
798 
799     if (Irp->RequestorMode == UserMode && (ccb->access & access_req) != access_req) {
800         Status = STATUS_ACCESS_DENIED;
801         WARN("insufficient privileges\n");
802         goto end;
803     }
804 
805     Status = set_file_security(DeviceObject->DeviceExtension, FileObject, IrpSp->Parameters.SetSecurity.SecurityDescriptor,
806                                &IrpSp->Parameters.SetSecurity.SecurityInformation, Irp);
807 
808 end:
809     Irp->IoStatus.Status = Status;
810 
811     IoCompleteRequest(Irp, IO_NO_INCREMENT);
812 
813     TRACE("returning %08x\n", Status);
814 
815     if (top_level)
816         IoSetTopLevelIrp(NULL);
817 
818     FsRtlExitFileSystem();
819 
820     return Status;
821 }
822 
823 static bool search_for_gid(fcb* fcb, PSID sid) {
824     LIST_ENTRY* le;
825 
826     le = gid_map_list.Flink;
827     while (le != &gid_map_list) {
828         gid_map* gm = CONTAINING_RECORD(le, gid_map, listentry);
829 
830         if (RtlEqualSid(sid, gm->sid)) {
831             fcb->inode_item.st_gid = gm->gid;
832             return true;
833         }
834 
835         le = le->Flink;
836     }
837 
838     return false;
839 }
840 
841 void find_gid(struct _fcb* fcb, struct _fcb* parfcb, PSECURITY_SUBJECT_CONTEXT subjcont) {
842     NTSTATUS Status;
843     TOKEN_OWNER* to;
844     TOKEN_PRIMARY_GROUP* tpg;
845     TOKEN_GROUPS* tg;
846 
847     if (parfcb && parfcb->inode_item.st_mode & S_ISGID) {
848         fcb->inode_item.st_gid = parfcb->inode_item.st_gid;
849         return;
850     }
851 
852     ExAcquireResourceSharedLite(&mapping_lock, true);
853 
854     if (!subjcont || !subjcont->PrimaryToken || IsListEmpty(&gid_map_list)) {
855         ExReleaseResourceLite(&mapping_lock);
856         return;
857     }
858 
859     Status = SeQueryInformationToken(subjcont->PrimaryToken, TokenOwner, (void**)&to);
860     if (!NT_SUCCESS(Status))
861         ERR("SeQueryInformationToken returned %08x\n", Status);
862     else {
863         if (search_for_gid(fcb, to->Owner)) {
864             ExReleaseResourceLite(&mapping_lock);
865             ExFreePool(to);
866             return;
867         }
868 
869         ExFreePool(to);
870     }
871 
872     Status = SeQueryInformationToken(subjcont->PrimaryToken, TokenPrimaryGroup, (void**)&tpg);
873     if (!NT_SUCCESS(Status))
874         ERR("SeQueryInformationToken returned %08x\n", Status);
875     else {
876         if (search_for_gid(fcb, tpg->PrimaryGroup)) {
877             ExReleaseResourceLite(&mapping_lock);
878             ExFreePool(tpg);
879             return;
880         }
881 
882         ExFreePool(tpg);
883     }
884 
885     Status = SeQueryInformationToken(subjcont->PrimaryToken, TokenGroups, (void**)&tg);
886     if (!NT_SUCCESS(Status))
887         ERR("SeQueryInformationToken returned %08x\n", Status);
888     else {
889         ULONG i;
890 
891         for (i = 0; i < tg->GroupCount; i++) {
892             if (search_for_gid(fcb, tg->Groups[i].Sid)) {
893                 ExReleaseResourceLite(&mapping_lock);
894                 ExFreePool(tg);
895                 return;
896             }
897         }
898 
899         ExFreePool(tg);
900     }
901 
902     ExReleaseResourceLite(&mapping_lock);
903 }
904 
905 NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* parfileref, ACCESS_STATE* as) {
906     NTSTATUS Status;
907     PSID owner;
908     BOOLEAN defaulted;
909 
910     Status = SeAssignSecurityEx(parfileref ? parfileref->fcb->sd : NULL, as->SecurityDescriptor, (void**)&fcb->sd, NULL, fcb->type == BTRFS_TYPE_DIRECTORY,
911                                 SEF_SACL_AUTO_INHERIT, &as->SubjectSecurityContext, IoGetFileObjectGenericMapping(), PagedPool);
912 
913     if (!NT_SUCCESS(Status)) {
914         ERR("SeAssignSecurityEx returned %08x\n", Status);
915         return Status;
916     }
917 
918     Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &owner, &defaulted);
919     if (!NT_SUCCESS(Status)) {
920         ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
921         fcb->inode_item.st_uid = UID_NOBODY;
922     } else {
923         fcb->inode_item.st_uid = sid_to_uid(owner);
924     }
925 
926     find_gid(fcb, parfileref ? parfileref->fcb : NULL, &as->SubjectSecurityContext);
927 
928     return STATUS_SUCCESS;
929 }
930