reactos/drivers/filesystems/btrfs/security.c
Vincent Franchomme 6e0cf03d92 [BTRFS][UBTRFS][SHELLBTRFS] Upgrade to 1.8.0 (#4417)
v1.8 (2022-03-12):

- Added minimal support for fs-verity
- ~~Added test suite~~ Not in ReactOS
- Fixed incorrect disk usage statistics
- Fixed potential crashes when renaming stream to file or file to stream
- Fixed potential crashes when querying hard links on file
- Fixed potential hang when opening oplocked file
- Fixed minor issues also uncovered by test suite
2022-05-03 17:30:11 +02:00

1013 lines
28 KiB
C

/* Copyright (c) Mark Harmstone 2016-17
*
* This file is part of WinBtrfs.
*
* WinBtrfs is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public Licence as published by
* the Free Software Foundation, either version 3 of the Licence, or
* (at your option) any later version.
*
* WinBtrfs is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public Licence for more details.
*
* You should have received a copy of the GNU Lesser General Public Licence
* along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
#include "btrfs_drv.h"
#define SEF_DACL_AUTO_INHERIT 0x01
#define SEF_SACL_AUTO_INHERIT 0x02
typedef struct {
UCHAR revision;
UCHAR elements;
UCHAR auth[6];
uint32_t nums[8];
} sid_header;
static sid_header sid_BA = { 1, 2, SECURITY_NT_AUTHORITY, {32, 544}}; // BUILTIN\Administrators
static sid_header sid_SY = { 1, 1, SECURITY_NT_AUTHORITY, {18}}; // NT AUTHORITY\SYSTEM
static sid_header sid_BU = { 1, 2, SECURITY_NT_AUTHORITY, {32, 545}}; // BUILTIN\Users
static sid_header sid_AU = { 1, 1, SECURITY_NT_AUTHORITY, {11}}; // NT AUTHORITY\Authenticated Users
typedef struct {
UCHAR flags;
ACCESS_MASK mask;
sid_header* sid;
} dacl;
static dacl def_dacls[] = {
{ 0, FILE_ALL_ACCESS, &sid_BA },
{ OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE, FILE_ALL_ACCESS, &sid_BA },
{ 0, FILE_ALL_ACCESS, &sid_SY },
{ OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE, FILE_ALL_ACCESS, &sid_SY },
{ OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE, FILE_GENERIC_READ | FILE_GENERIC_EXECUTE, &sid_BU },
{ OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE, FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE, &sid_AU },
{ 0, FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE, &sid_AU },
// FIXME - Mandatory Label\High Mandatory Level:(OI)(NP)(IO)(NW)
{ 0, 0, NULL }
};
extern LIST_ENTRY uid_map_list, gid_map_list;
extern ERESOURCE mapping_lock;
void add_user_mapping(WCHAR* sidstring, ULONG sidstringlength, uint32_t uid) {
unsigned int i, np;
uint8_t numdashes;
uint64_t val;
ULONG sidsize;
sid_header* sid;
uid_map* um;
if (sidstringlength < 4 ||
sidstring[0] != 'S' ||
sidstring[1] != '-' ||
sidstring[2] != '1' ||
sidstring[3] != '-') {
ERR("invalid SID\n");
return;
}
sidstring = &sidstring[4];
sidstringlength -= 4;
numdashes = 0;
for (i = 0; i < sidstringlength; i++) {
if (sidstring[i] == '-') {
numdashes++;
sidstring[i] = 0;
}
}
sidsize = 8 + (numdashes * 4);
sid = ExAllocatePoolWithTag(PagedPool, sidsize, ALLOC_TAG);
if (!sid) {
ERR("out of memory\n");
return;
}
sid->revision = 0x01;
sid->elements = numdashes;
np = 0;
while (sidstringlength > 0) {
val = 0;
i = 0;
while (sidstring[i] != '-' && i < sidstringlength) {
if (sidstring[i] >= '0' && sidstring[i] <= '9') {
val *= 10;
val += sidstring[i] - '0';
} else
break;
i++;
}
i++;
TRACE("val = %u, i = %u, ssl = %lu\n", (uint32_t)val, i, sidstringlength);
if (np == 0) {
sid->auth[0] = (uint8_t)((val & 0xff0000000000) >> 40);
sid->auth[1] = (uint8_t)((val & 0xff00000000) >> 32);
sid->auth[2] = (uint8_t)((val & 0xff000000) >> 24);
sid->auth[3] = (uint8_t)((val & 0xff0000) >> 16);
sid->auth[4] = (uint8_t)((val & 0xff00) >> 8);
sid->auth[5] = val & 0xff;
} else {
sid->nums[np-1] = (uint32_t)val;
}
np++;
if (sidstringlength > i) {
sidstringlength -= i;
sidstring = &sidstring[i];
} else
break;
}
um = ExAllocatePoolWithTag(PagedPool, sizeof(uid_map), ALLOC_TAG);
if (!um) {
ERR("out of memory\n");
ExFreePool(sid);
return;
}
um->sid = sid;
um->uid = uid;
InsertTailList(&uid_map_list, &um->listentry);
}
void add_group_mapping(WCHAR* sidstring, ULONG sidstringlength, uint32_t gid) {
unsigned int i, np;
uint8_t numdashes;
uint64_t val;
ULONG sidsize;
sid_header* sid;
gid_map* gm;
if (sidstringlength < 4 || sidstring[0] != 'S' || sidstring[1] != '-' || sidstring[2] != '1' || sidstring[3] != '-') {
ERR("invalid SID\n");
return;
}
sidstring = &sidstring[4];
sidstringlength -= 4;
numdashes = 0;
for (i = 0; i < sidstringlength; i++) {
if (sidstring[i] == '-') {
numdashes++;
sidstring[i] = 0;
}
}
sidsize = 8 + (numdashes * 4);
sid = ExAllocatePoolWithTag(PagedPool, sidsize, ALLOC_TAG);
if (!sid) {
ERR("out of memory\n");
return;
}
sid->revision = 0x01;
sid->elements = numdashes;
np = 0;
while (sidstringlength > 0) {
val = 0;
i = 0;
while (i < sidstringlength && sidstring[i] != '-') {
if (sidstring[i] >= '0' && sidstring[i] <= '9') {
val *= 10;
val += sidstring[i] - '0';
} else
break;
i++;
}
i++;
TRACE("val = %u, i = %u, ssl = %lu\n", (uint32_t)val, i, sidstringlength);
if (np == 0) {
sid->auth[0] = (uint8_t)((val & 0xff0000000000) >> 40);
sid->auth[1] = (uint8_t)((val & 0xff00000000) >> 32);
sid->auth[2] = (uint8_t)((val & 0xff000000) >> 24);
sid->auth[3] = (uint8_t)((val & 0xff0000) >> 16);
sid->auth[4] = (uint8_t)((val & 0xff00) >> 8);
sid->auth[5] = val & 0xff;
} else
sid->nums[np-1] = (uint32_t)val;
np++;
if (sidstringlength > i) {
sidstringlength -= i;
sidstring = &sidstring[i];
} else
break;
}
gm = ExAllocatePoolWithTag(PagedPool, sizeof(gid_map), ALLOC_TAG);
if (!gm) {
ERR("out of memory\n");
ExFreePool(sid);
return;
}
gm->sid = sid;
gm->gid = gid;
InsertTailList(&gid_map_list, &gm->listentry);
}
NTSTATUS uid_to_sid(uint32_t uid, PSID* sid) {
LIST_ENTRY* le;
sid_header* sh;
UCHAR els;
ExAcquireResourceSharedLite(&mapping_lock, true);
le = uid_map_list.Flink;
while (le != &uid_map_list) {
uid_map* um = CONTAINING_RECORD(le, uid_map, listentry);
if (um->uid == uid) {
*sid = ExAllocatePoolWithTag(PagedPool, RtlLengthSid(um->sid), ALLOC_TAG);
if (!*sid) {
ERR("out of memory\n");
ExReleaseResourceLite(&mapping_lock);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(*sid, um->sid, RtlLengthSid(um->sid));
ExReleaseResourceLite(&mapping_lock);
return STATUS_SUCCESS;
}
le = le->Flink;
}
ExReleaseResourceLite(&mapping_lock);
if (uid == 0) { // root
// FIXME - find actual Administrator account, rather than SYSTEM (S-1-5-18)
// (of form S-1-5-21-...-500)
els = 1;
sh = ExAllocatePoolWithTag(PagedPool, sizeof(sid_header) + ((els - 1) * sizeof(uint32_t)), ALLOC_TAG);
if (!sh) {
ERR("out of memory\n");
*sid = NULL;
return STATUS_INSUFFICIENT_RESOURCES;
}
sh->revision = 1;
sh->elements = els;
sh->auth[0] = 0;
sh->auth[1] = 0;
sh->auth[2] = 0;
sh->auth[3] = 0;
sh->auth[4] = 0;
sh->auth[5] = 5;
sh->nums[0] = 18;
} else {
// fallback to S-1-22-1-X, Samba's SID scheme
sh = ExAllocatePoolWithTag(PagedPool, sizeof(sid_header), ALLOC_TAG);
if (!sh) {
ERR("out of memory\n");
*sid = NULL;
return STATUS_INSUFFICIENT_RESOURCES;
}
sh->revision = 1;
sh->elements = 2;
sh->auth[0] = 0;
sh->auth[1] = 0;
sh->auth[2] = 0;
sh->auth[3] = 0;
sh->auth[4] = 0;
sh->auth[5] = 22;
sh->nums[0] = 1;
sh->nums[1] = uid;
}
*sid = sh;
return STATUS_SUCCESS;
}
uint32_t sid_to_uid(PSID sid) {
LIST_ENTRY* le;
sid_header* sh = sid;
ExAcquireResourceSharedLite(&mapping_lock, true);
le = uid_map_list.Flink;
while (le != &uid_map_list) {
uid_map* um = CONTAINING_RECORD(le, uid_map, listentry);
if (RtlEqualSid(sid, um->sid)) {
ExReleaseResourceLite(&mapping_lock);
return um->uid;
}
le = le->Flink;
}
ExReleaseResourceLite(&mapping_lock);
if (RtlEqualSid(sid, &sid_SY))
return 0; // root
// Samba's SID scheme: S-1-22-1-X
if (sh->revision == 1 && sh->elements == 2 && sh->auth[0] == 0 && sh->auth[1] == 0 && sh->auth[2] == 0 && sh->auth[3] == 0 &&
sh->auth[4] == 0 && sh->auth[5] == 22 && sh->nums[0] == 1)
return sh->nums[1];
return UID_NOBODY;
}
static void gid_to_sid(uint32_t gid, PSID* sid) {
sid_header* sh;
UCHAR els;
// FIXME - do this properly?
// fallback to S-1-22-2-X, Samba's SID scheme
els = 2;
sh = ExAllocatePoolWithTag(PagedPool, sizeof(sid_header) + ((els - 1) * sizeof(uint32_t)), ALLOC_TAG);
if (!sh) {
ERR("out of memory\n");
*sid = NULL;
return;
}
sh->revision = 1;
sh->elements = els;
sh->auth[0] = 0;
sh->auth[1] = 0;
sh->auth[2] = 0;
sh->auth[3] = 0;
sh->auth[4] = 0;
sh->auth[5] = 22;
sh->nums[0] = 2;
sh->nums[1] = gid;
*sid = sh;
}
static ACL* load_default_acl() {
uint16_t size, i;
ACL* acl;
ACCESS_ALLOWED_ACE* aaa;
size = sizeof(ACL);
i = 0;
while (def_dacls[i].sid) {
size += sizeof(ACCESS_ALLOWED_ACE);
size += 8 + (def_dacls[i].sid->elements * sizeof(uint32_t)) - sizeof(ULONG);
i++;
}
acl = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
if (!acl) {
ERR("out of memory\n");
return NULL;
}
acl->AclRevision = ACL_REVISION;
acl->Sbz1 = 0;
acl->AclSize = size;
acl->AceCount = i;
acl->Sbz2 = 0;
aaa = (ACCESS_ALLOWED_ACE*)&acl[1];
i = 0;
while (def_dacls[i].sid) {
aaa->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
aaa->Header.AceFlags = def_dacls[i].flags;
aaa->Header.AceSize = sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG) + 8 + (def_dacls[i].sid->elements * sizeof(uint32_t));
aaa->Mask = def_dacls[i].mask;
RtlCopyMemory(&aaa->SidStart, def_dacls[i].sid, 8 + (def_dacls[i].sid->elements * sizeof(uint32_t)));
aaa = (ACCESS_ALLOWED_ACE*)((uint8_t*)aaa + aaa->Header.AceSize);
i++;
}
return acl;
}
static void get_top_level_sd(fcb* fcb) {
NTSTATUS Status;
SECURITY_DESCRIPTOR sd;
ULONG buflen;
ACL* acl = NULL;
PSID usersid = NULL, groupsid = NULL;
Status = RtlCreateSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
if (!NT_SUCCESS(Status)) {
ERR("RtlCreateSecurityDescriptor returned %08lx\n", Status);
goto end;
}
Status = uid_to_sid(fcb->inode_item.st_uid, &usersid);
if (!NT_SUCCESS(Status)) {
ERR("uid_to_sid returned %08lx\n", Status);
goto end;
}
Status = RtlSetOwnerSecurityDescriptor(&sd, usersid, false);
if (!NT_SUCCESS(Status)) {
ERR("RtlSetOwnerSecurityDescriptor returned %08lx\n", Status);
goto end;
}
gid_to_sid(fcb->inode_item.st_gid, &groupsid);
if (!groupsid) {
ERR("out of memory\n");
goto end;
}
Status = RtlSetGroupSecurityDescriptor(&sd, groupsid, false);
if (!NT_SUCCESS(Status)) {
ERR("RtlSetGroupSecurityDescriptor returned %08lx\n", Status);
goto end;
}
acl = load_default_acl();
if (!acl) {
ERR("out of memory\n");
goto end;
}
Status = RtlSetDaclSecurityDescriptor(&sd, true, acl, false);
if (!NT_SUCCESS(Status)) {
ERR("RtlSetDaclSecurityDescriptor returned %08lx\n", Status);
goto end;
}
// FIXME - SACL_SECURITY_INFORMATION
buflen = 0;
// get sd size
Status = RtlAbsoluteToSelfRelativeSD(&sd, NULL, &buflen);
if (Status != STATUS_SUCCESS && Status != STATUS_BUFFER_TOO_SMALL) {
ERR("RtlAbsoluteToSelfRelativeSD 1 returned %08lx\n", Status);
goto end;
}
if (buflen == 0 || Status == STATUS_SUCCESS) {
TRACE("RtlAbsoluteToSelfRelativeSD said SD is zero-length\n");
goto end;
}
fcb->sd = ExAllocatePoolWithTag(PagedPool, buflen, ALLOC_TAG);
if (!fcb->sd) {
ERR("out of memory\n");
goto end;
}
Status = RtlAbsoluteToSelfRelativeSD(&sd, fcb->sd, &buflen);
if (!NT_SUCCESS(Status)) {
ERR("RtlAbsoluteToSelfRelativeSD 2 returned %08lx\n", Status);
ExFreePool(fcb->sd);
fcb->sd = NULL;
goto end;
}
end:
if (acl)
ExFreePool(acl);
if (usersid)
ExFreePool(usersid);
if (groupsid)
ExFreePool(groupsid);
}
void fcb_get_sd(fcb* fcb, struct _fcb* parent, bool look_for_xattr, PIRP Irp) {
NTSTATUS Status;
PSID usersid = NULL, groupsid = NULL;
SECURITY_SUBJECT_CONTEXT subjcont;
ULONG buflen;
PSECURITY_DESCRIPTOR* abssd;
PSECURITY_DESCRIPTOR newsd;
PACL dacl, sacl;
PSID owner, group;
ULONG abssdlen = 0, dacllen = 0, sacllen = 0, ownerlen = 0, grouplen = 0;
uint8_t* buf;
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))
return;
if (!parent) {
get_top_level_sd(fcb);
return;
}
SeCaptureSubjectContext(&subjcont);
Status = SeAssignSecurityEx(parent->sd, NULL, (void**)&fcb->sd, NULL, fcb->type == BTRFS_TYPE_DIRECTORY, SEF_DACL_AUTO_INHERIT,
&subjcont, IoGetFileObjectGenericMapping(), PagedPool);
if (!NT_SUCCESS(Status)) {
ERR("SeAssignSecurityEx returned %08lx\n", Status);
return;
}
Status = RtlSelfRelativeToAbsoluteSD(fcb->sd, NULL, &abssdlen, NULL, &dacllen, NULL, &sacllen, NULL, &ownerlen,
NULL, &grouplen);
if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) {
ERR("RtlSelfRelativeToAbsoluteSD returned %08lx\n", Status);
return;
}
if (abssdlen + dacllen + sacllen + ownerlen + grouplen == 0) {
ERR("RtlSelfRelativeToAbsoluteSD returned zero lengths\n");
return;
}
buf = (uint8_t*)ExAllocatePoolWithTag(PagedPool, abssdlen + dacllen + sacllen + ownerlen + grouplen, ALLOC_TAG);
if (!buf) {
ERR("out of memory\n");
return;
}
abssd = (PSECURITY_DESCRIPTOR)buf;
dacl = (PACL)(buf + abssdlen);
sacl = (PACL)(buf + abssdlen + dacllen);
owner = (PSID)(buf + abssdlen + dacllen + sacllen);
group = (PSID)(buf + abssdlen + dacllen + sacllen + ownerlen);
Status = RtlSelfRelativeToAbsoluteSD(fcb->sd, abssd, &abssdlen, dacl, &dacllen, sacl, &sacllen, owner, &ownerlen,
group, &grouplen);
if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) {
ERR("RtlSelfRelativeToAbsoluteSD returned %08lx\n", Status);
ExFreePool(buf);
return;
}
Status = uid_to_sid(fcb->inode_item.st_uid, &usersid);
if (!NT_SUCCESS(Status)) {
ERR("uid_to_sid returned %08lx\n", Status);
ExFreePool(buf);
return;
}
RtlSetOwnerSecurityDescriptor(abssd, usersid, false);
gid_to_sid(fcb->inode_item.st_gid, &groupsid);
if (!groupsid) {
ERR("out of memory\n");
ExFreePool(usersid);
ExFreePool(buf);
return;
}
RtlSetGroupSecurityDescriptor(abssd, groupsid, false);
buflen = 0;
Status = RtlAbsoluteToSelfRelativeSD(abssd, NULL, &buflen);
if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) {
ERR("RtlAbsoluteToSelfRelativeSD returned %08lx\n", Status);
ExFreePool(usersid);
ExFreePool(groupsid);
ExFreePool(buf);
return;
}
if (buflen == 0) {
ERR("RtlAbsoluteToSelfRelativeSD returned a buffer size of 0\n");
ExFreePool(usersid);
ExFreePool(groupsid);
ExFreePool(buf);
return;
}
newsd = ExAllocatePoolWithTag(PagedPool, buflen, ALLOC_TAG);
if (!newsd) {
ERR("out of memory\n");
ExFreePool(usersid);
ExFreePool(groupsid);
ExFreePool(buf);
return;
}
Status = RtlAbsoluteToSelfRelativeSD(abssd, newsd, &buflen);
if (!NT_SUCCESS(Status)) {
ERR("RtlAbsoluteToSelfRelativeSD returned %08lx\n", Status);
ExFreePool(usersid);
ExFreePool(groupsid);
ExFreePool(buf);
return;
}
ExFreePool(fcb->sd);
fcb->sd = newsd;
ExFreePool(usersid);
ExFreePool(groupsid);
ExFreePool(buf);
}
static NTSTATUS get_file_security(PFILE_OBJECT FileObject, SECURITY_DESCRIPTOR* relsd, ULONG* buflen, SECURITY_INFORMATION flags) {
NTSTATUS Status;
fcb* fcb = FileObject->FsContext;
ccb* ccb = FileObject->FsContext2;
file_ref* fileref = ccb ? ccb->fileref : NULL;
if (fcb->ads) {
if (fileref && fileref->parent)
fcb = fileref->parent->fcb;
else {
ERR("could not get parent fcb for stream\n");
return STATUS_INTERNAL_ERROR;
}
}
// Why (void**)? Is this a bug in mingw?
Status = SeQuerySecurityDescriptorInfo(&flags, relsd, buflen, (void**)&fcb->sd);
if (Status == STATUS_BUFFER_TOO_SMALL)
TRACE("SeQuerySecurityDescriptorInfo returned %08lx\n", Status);
else if (!NT_SUCCESS(Status))
ERR("SeQuerySecurityDescriptorInfo returned %08lx\n", Status);
return Status;
}
_Dispatch_type_(IRP_MJ_QUERY_SECURITY)
_Function_class_(DRIVER_DISPATCH)
NTSTATUS __stdcall drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
NTSTATUS Status;
SECURITY_DESCRIPTOR* sd;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
device_extension* Vcb = DeviceObject->DeviceExtension;
ULONG buflen;
bool top_level;
PFILE_OBJECT FileObject = IrpSp->FileObject;
ccb* ccb = FileObject ? FileObject->FsContext2 : NULL;
FsRtlEnterFileSystem();
TRACE("query security\n");
top_level = is_top_level(Irp);
if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
Status = STATUS_INVALID_DEVICE_REQUEST;
goto end;
} else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
Status = STATUS_INVALID_PARAMETER;
goto end;
}
if (!ccb) {
ERR("no ccb\n");
Status = STATUS_INVALID_PARAMETER;
goto end;
}
if (Irp->RequestorMode == UserMode && !(ccb->access & READ_CONTROL)) {
WARN("insufficient permissions\n");
Status = STATUS_ACCESS_DENIED;
goto end;
}
Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
if (IrpSp->Parameters.QuerySecurity.SecurityInformation & OWNER_SECURITY_INFORMATION)
TRACE("OWNER_SECURITY_INFORMATION\n");
if (IrpSp->Parameters.QuerySecurity.SecurityInformation & GROUP_SECURITY_INFORMATION)
TRACE("GROUP_SECURITY_INFORMATION\n");
if (IrpSp->Parameters.QuerySecurity.SecurityInformation & DACL_SECURITY_INFORMATION)
TRACE("DACL_SECURITY_INFORMATION\n");
if (IrpSp->Parameters.QuerySecurity.SecurityInformation & SACL_SECURITY_INFORMATION)
TRACE("SACL_SECURITY_INFORMATION\n");
TRACE("length = %lu\n", IrpSp->Parameters.QuerySecurity.Length);
sd = map_user_buffer(Irp, NormalPagePriority);
TRACE("sd = %p\n", sd);
if (Irp->MdlAddress && !sd) {
ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
buflen = IrpSp->Parameters.QuerySecurity.Length;
Status = get_file_security(IrpSp->FileObject, sd, &buflen, IrpSp->Parameters.QuerySecurity.SecurityInformation);
if (NT_SUCCESS(Status))
Irp->IoStatus.Information = IrpSp->Parameters.QuerySecurity.Length;
else if (Status == STATUS_BUFFER_TOO_SMALL) {
Irp->IoStatus.Information = buflen;
Status = STATUS_BUFFER_OVERFLOW;
} else
Irp->IoStatus.Information = 0;
end:
TRACE("Irp->IoStatus.Information = %Iu\n", Irp->IoStatus.Information);
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
if (top_level)
IoSetTopLevelIrp(NULL);
TRACE("returning %08lx\n", Status);
FsRtlExitFileSystem();
return Status;
}
static NTSTATUS set_file_security(device_extension* Vcb, PFILE_OBJECT FileObject, SECURITY_DESCRIPTOR* sd, PSECURITY_INFORMATION flags, PIRP Irp) {
NTSTATUS Status;
fcb* fcb = FileObject->FsContext;
ccb* ccb = FileObject->FsContext2;
file_ref* fileref = ccb ? ccb->fileref : NULL;
SECURITY_DESCRIPTOR* oldsd;
LARGE_INTEGER time;
BTRFS_TIME now;
TRACE("(%p, %p, %p, %lx)\n", Vcb, FileObject, sd, *flags);
if (Vcb->readonly)
return STATUS_MEDIA_WRITE_PROTECTED;
if (fcb->ads) {
if (fileref && fileref->parent)
fcb = fileref->parent->fcb;
else {
ERR("could not find parent fcb for stream\n");
return STATUS_INTERNAL_ERROR;
}
}
if (!fcb || !ccb)
return STATUS_INVALID_PARAMETER;
ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
if (is_subvol_readonly(fcb->subvol, Irp)) {
Status = STATUS_ACCESS_DENIED;
goto end;
}
oldsd = fcb->sd;
Status = SeSetSecurityDescriptorInfo(NULL, flags, sd, (void**)&fcb->sd, PagedPool, IoGetFileObjectGenericMapping());
if (!NT_SUCCESS(Status)) {
ERR("SeSetSecurityDescriptorInfo returned %08lx\n", Status);
goto end;
}
ExFreePool(oldsd);
KeQuerySystemTime(&time);
win_time_to_unix(time, &now);
fcb->inode_item.transid = Vcb->superblock.generation;
if (!ccb->user_set_change_time)
fcb->inode_item.st_ctime = now;
fcb->inode_item.sequence++;
fcb->sd_dirty = true;
fcb->sd_deleted = false;
fcb->inode_item_changed = true;
fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
fcb->subvol->root_item.ctime = now;
mark_fcb_dirty(fcb);
queue_notification_fcb(fileref, FILE_NOTIFY_CHANGE_SECURITY, FILE_ACTION_MODIFIED, NULL);
end:
ExReleaseResourceLite(fcb->Header.Resource);
return Status;
}
_Dispatch_type_(IRP_MJ_SET_SECURITY)
_Function_class_(DRIVER_DISPATCH)
NTSTATUS __stdcall drv_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT FileObject = IrpSp->FileObject;
ccb* ccb = FileObject ? FileObject->FsContext2 : NULL;
device_extension* Vcb = DeviceObject->DeviceExtension;
ULONG access_req = 0;
bool top_level;
FsRtlEnterFileSystem();
TRACE("set security\n");
top_level = is_top_level(Irp);
if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
Status = STATUS_INVALID_DEVICE_REQUEST;
goto end;
} else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
Status = STATUS_INVALID_PARAMETER;
goto end;
}
if (!ccb) {
ERR("no ccb\n");
Status = STATUS_INVALID_PARAMETER;
goto end;
}
Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
if (IrpSp->Parameters.SetSecurity.SecurityInformation & OWNER_SECURITY_INFORMATION) {
TRACE("OWNER_SECURITY_INFORMATION\n");
access_req |= WRITE_OWNER;
}
if (IrpSp->Parameters.SetSecurity.SecurityInformation & GROUP_SECURITY_INFORMATION) {
TRACE("GROUP_SECURITY_INFORMATION\n");
access_req |= WRITE_OWNER;
}
if (IrpSp->Parameters.SetSecurity.SecurityInformation & DACL_SECURITY_INFORMATION) {
TRACE("DACL_SECURITY_INFORMATION\n");
access_req |= WRITE_DAC;
}
if (IrpSp->Parameters.SetSecurity.SecurityInformation & SACL_SECURITY_INFORMATION) {
TRACE("SACL_SECURITY_INFORMATION\n");
access_req |= ACCESS_SYSTEM_SECURITY;
}
if (Irp->RequestorMode == UserMode && (ccb->access & access_req) != access_req) {
Status = STATUS_ACCESS_DENIED;
WARN("insufficient privileges\n");
goto end;
}
Status = set_file_security(DeviceObject->DeviceExtension, FileObject, IrpSp->Parameters.SetSecurity.SecurityDescriptor,
&IrpSp->Parameters.SetSecurity.SecurityInformation, Irp);
end:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
TRACE("returning %08lx\n", Status);
if (top_level)
IoSetTopLevelIrp(NULL);
FsRtlExitFileSystem();
return Status;
}
static bool search_for_gid(fcb* fcb, PSID sid) {
LIST_ENTRY* le;
le = gid_map_list.Flink;
while (le != &gid_map_list) {
gid_map* gm = CONTAINING_RECORD(le, gid_map, listentry);
if (RtlEqualSid(sid, gm->sid)) {
fcb->inode_item.st_gid = gm->gid;
return true;
}
le = le->Flink;
}
return false;
}
void find_gid(struct _fcb* fcb, struct _fcb* parfcb, PSECURITY_SUBJECT_CONTEXT subjcont) {
NTSTATUS Status;
TOKEN_OWNER* to;
TOKEN_PRIMARY_GROUP* tpg;
TOKEN_GROUPS* tg;
if (parfcb && parfcb->inode_item.st_mode & S_ISGID) {
fcb->inode_item.st_gid = parfcb->inode_item.st_gid;
return;
}
ExAcquireResourceSharedLite(&mapping_lock, true);
if (!subjcont || !subjcont->PrimaryToken || IsListEmpty(&gid_map_list)) {
ExReleaseResourceLite(&mapping_lock);
return;
}
Status = SeQueryInformationToken(subjcont->PrimaryToken, TokenOwner, (void**)&to);
if (!NT_SUCCESS(Status))
ERR("SeQueryInformationToken returned %08lx\n", Status);
else {
if (search_for_gid(fcb, to->Owner)) {
ExReleaseResourceLite(&mapping_lock);
ExFreePool(to);
return;
}
ExFreePool(to);
}
Status = SeQueryInformationToken(subjcont->PrimaryToken, TokenPrimaryGroup, (void**)&tpg);
if (!NT_SUCCESS(Status))
ERR("SeQueryInformationToken returned %08lx\n", Status);
else {
if (search_for_gid(fcb, tpg->PrimaryGroup)) {
ExReleaseResourceLite(&mapping_lock);
ExFreePool(tpg);
return;
}
ExFreePool(tpg);
}
Status = SeQueryInformationToken(subjcont->PrimaryToken, TokenGroups, (void**)&tg);
if (!NT_SUCCESS(Status))
ERR("SeQueryInformationToken returned %08lx\n", Status);
else {
ULONG i;
for (i = 0; i < tg->GroupCount; i++) {
if (search_for_gid(fcb, tg->Groups[i].Sid)) {
ExReleaseResourceLite(&mapping_lock);
ExFreePool(tg);
return;
}
}
ExFreePool(tg);
}
ExReleaseResourceLite(&mapping_lock);
}
NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* parfileref, ACCESS_STATE* as) {
NTSTATUS Status;
PSID owner;
BOOLEAN defaulted;
Status = SeAssignSecurityEx(parfileref ? parfileref->fcb->sd : NULL, as->SecurityDescriptor, (void**)&fcb->sd, NULL, fcb->type == BTRFS_TYPE_DIRECTORY,
SEF_SACL_AUTO_INHERIT, &as->SubjectSecurityContext, IoGetFileObjectGenericMapping(), PagedPool);
if (!NT_SUCCESS(Status)) {
ERR("SeAssignSecurityEx returned %08lx\n", Status);
return Status;
}
Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &owner, &defaulted);
if (!NT_SUCCESS(Status)) {
ERR("RtlGetOwnerSecurityDescriptor returned %08lx\n", Status);
fcb->inode_item.st_uid = UID_NOBODY;
} else {
fcb->inode_item.st_uid = sid_to_uid(owner);
}
find_gid(fcb, parfileref ? parfileref->fcb : NULL, &as->SubjectSecurityContext);
return STATUS_SUCCESS;
}