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
add_user_mapping(WCHAR * sidstring,ULONG sidstringlength,uint32_t uid)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 = %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
add_group_mapping(WCHAR * sidstring,ULONG sidstringlength,uint32_t gid)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
uid_to_sid(uint32_t uid,PSID * sid)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
sid_to_uid(PSID sid)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
gid_to_sid(uint32_t gid,PSID * sid)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
load_default_acl()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
get_top_level_sd(fcb * fcb)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
fcb_get_sd(fcb * fcb,struct _fcb * parent,bool look_for_xattr,PIRP Irp)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
get_file_security(PFILE_OBJECT FileObject,SECURITY_DESCRIPTOR * relsd,ULONG * buflen,SECURITY_INFORMATION flags)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)
_Function_class_(DRIVER_DISPATCH)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
set_file_security(device_extension * Vcb,PFILE_OBJECT FileObject,SECURITY_DESCRIPTOR * sd,PSECURITY_INFORMATION flags,PIRP Irp)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)
_Function_class_(DRIVER_DISPATCH)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
search_for_gid(fcb * fcb,PSID sid)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
find_gid(struct _fcb * fcb,struct _fcb * parfcb,PSECURITY_SUBJECT_CONTEXT subjcont)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
fcb_get_new_sd(fcb * fcb,file_ref * parfileref,ACCESS_STATE * as)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