8ba772f1e4cbd59a7137d8fb5e78f81e5fe22798
[reactos.git] / drivers / filesystems / btrfs / security.c
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 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 uid) {
57 unsigned int i, np;
58 UINT8 numdashes;
59 UINT64 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)val, i, sidstringlength);
110
111 if (np == 0) {
112 sid->auth[0] = (UINT8)((val & 0xff0000000000) >> 40);
113 sid->auth[1] = (UINT8)((val & 0xff00000000) >> 32);
114 sid->auth[2] = (UINT8)((val & 0xff000000) >> 24);
115 sid->auth[3] = (UINT8)((val & 0xff0000) >> 16);
116 sid->auth[4] = (UINT8)((val & 0xff00) >> 8);
117 sid->auth[5] = val & 0xff;
118 } else {
119 sid->nums[np-1] = (UINT32)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 gid) {
146 unsigned int i, np;
147 UINT8 numdashes;
148 UINT64 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)val, i, sidstringlength);
195
196 if (np == 0) {
197 sid->auth[0] = (UINT8)((val & 0xff0000000000) >> 40);
198 sid->auth[1] = (UINT8)((val & 0xff00000000) >> 32);
199 sid->auth[2] = (UINT8)((val & 0xff000000) >> 24);
200 sid->auth[3] = (UINT8)((val & 0xff0000) >> 16);
201 sid->auth[4] = (UINT8)((val & 0xff00) >> 8);
202 sid->auth[5] = val & 0xff;
203 } else
204 sid->nums[np-1] = (UINT32)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 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)), 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 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 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)), 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 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)) - 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));
403 aaa->Mask = def_dacls[i].mask;
404
405 RtlCopyMemory(&aaa->SidStart, def_dacls[i].sid, 8 + (def_dacls[i].sid->elements * sizeof(UINT32)));
406
407 aaa = (ACCESS_ALLOWED_ACE*)((UINT8*)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**)&fcb->sd, (UINT16*)&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 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 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 }