static NTSTATUS RtlpSysVolCheckOwnerAndSecurity(IN HANDLE DirectoryHandle, IN PISECURITY_DESCRIPTOR SecurityDescriptor) { PSECURITY_DESCRIPTOR RelSD = NULL; PSECURITY_DESCRIPTOR NewRelSD = NULL; PSECURITY_DESCRIPTOR AbsSD = NULL; #ifdef _WIN64 BOOLEAN AbsSDAllocated = FALSE; #endif PSID AdminSid = NULL; PSID LocalSystemSid = NULL; ULONG DescriptorSize; ULONG AbsSDSize, RelSDSize = 0; PACL Dacl; BOOLEAN DaclPresent, DaclDefaulted; PSID OwnerSid; BOOLEAN OwnerDefaulted; ULONG AceIndex; PACE Ace = NULL; NTSTATUS Status; /* find out how much memory we need to allocate for the self-relative descriptor we're querying */ Status = ZwQuerySecurityObject(DirectoryHandle, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, NULL, 0, &DescriptorSize); if (Status != STATUS_BUFFER_TOO_SMALL) { /* looks like the FS doesn't support security... return success */ Status = STATUS_SUCCESS; goto Cleanup; } /* allocate enough memory for the security descriptor */ RelSD = RtlpAllocateMemory(DescriptorSize, 'dSeS'); if (RelSD == NULL) { Status = STATUS_NO_MEMORY; goto Cleanup; } /* query the self-relative security descriptor */ Status = ZwQuerySecurityObject(DirectoryHandle, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, RelSD, DescriptorSize, &DescriptorSize); if (!NT_SUCCESS(Status)) { /* FIXME - handle the case where someone else modified the owner and/or DACL while we allocated memory. But that should be *very* unlikely.... */ goto Cleanup; } /* query the owner and DACL from the descriptor */ Status = RtlGetOwnerSecurityDescriptor(RelSD, &OwnerSid, &OwnerDefaulted); if (!NT_SUCCESS(Status)) { goto Cleanup; } Status = RtlGetDaclSecurityDescriptor(RelSD, &DaclPresent, &Dacl, &DaclDefaulted); if (!NT_SUCCESS(Status)) { goto Cleanup; } /* create the Administrators SID */ Status = RtlAllocateAndInitializeSid(&LocalSystemAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdminSid); if (!NT_SUCCESS(Status)) { goto Cleanup; } /* create the local SYSTEM SID */ Status = RtlAllocateAndInitializeSid(&LocalSystemAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &LocalSystemSid); if (!NT_SUCCESS(Status)) { goto Cleanup; } /* check if the Administrators are the owner and at least a not-NULL DACL is present */ if (OwnerSid != NULL && RtlEqualSid(OwnerSid, AdminSid) && DaclPresent && Dacl != NULL) { /* check the DACL for an Allowed ACE for the SYSTEM account */ AceIndex = 0; do { Status = RtlGetAce(Dacl, AceIndex++, (PVOID*)&Ace); if (!NT_SUCCESS(Status)) { Ace = NULL; } else if (Ace != NULL && Ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE) { /* check if the the ACE is a set of allowed permissions for the local SYSTEM account */ if (RtlEqualSid((PSID)(Ace + 1), LocalSystemSid)) { /* check if the ACE is inherited by noncontainer and container objects, if not attempt to change that */ if (!(Ace->Header.AceFlags & OBJECT_INHERIT_ACE) || !(Ace->Header.AceFlags & CONTAINER_INHERIT_ACE)) { Ace->Header.AceFlags |= OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; Status = ZwSetSecurityObject(DirectoryHandle, DACL_SECURITY_INFORMATION, RelSD); } else { /* all done, we have access */ Status = STATUS_SUCCESS; } goto Cleanup; } } } while (Ace != NULL); } AbsSDSize = DescriptorSize; /* because we need to change any existing data we need to convert it to an absolute security descriptor first */ Status = RtlSelfRelativeToAbsoluteSD2(RelSD, &AbsSDSize); #ifdef _WIN64 if (Status == STATUS_BUFFER_TOO_SMALL) { /* this error code can only be returned on 64 bit builds because the size of an absolute security descriptor is greater than the size of a self-relative security descriptor */ ASSERT(AbsSDSize > DescriptorSize); AbsSD = RtlpAllocateMemory(DescriptorSize, 'dSeS'); if (AbsSD == NULL) { Status = STATUS_NO_MEMORY; goto Cleanup; } AbsSDAllocated = TRUE; /* make a raw copy of the self-relative descriptor */ RtlCopyMemory(AbsSD, RelSD, DescriptorSize); /* finally convert it */ Status = RtlSelfRelativeToAbsoluteSD2(AbsSD, &AbsSDSize); } else #endif { AbsSD = RelSD; } if (!NT_SUCCESS(Status)) { goto Cleanup; } /* set the owner SID */ Status = RtlSetOwnerSecurityDescriptor(AbsSD, AdminSid, FALSE); if (!NT_SUCCESS(Status)) { goto Cleanup; } /* set the DACL in the security descriptor */ Status = RtlSetDaclSecurityDescriptor(AbsSD, TRUE, SecurityDescriptor->Dacl, FALSE); if (!NT_SUCCESS(Status)) { goto Cleanup; } /* convert it back to a self-relative descriptor, find out how much memory we need */ Status = RtlAbsoluteToSelfRelativeSD(AbsSD, NULL, &RelSDSize); if (Status != STATUS_BUFFER_TOO_SMALL) { goto Cleanup; } /* allocate enough memory for the new self-relative descriptor */ NewRelSD = RtlpAllocateMemory(RelSDSize, 'dSeS'); if (NewRelSD == NULL) { Status = STATUS_NO_MEMORY; goto Cleanup; } /* convert the security descriptor to self-relative format */ Status = RtlAbsoluteToSelfRelativeSD(AbsSD, NewRelSD, &RelSDSize); if (Status == STATUS_BUFFER_TOO_SMALL) { goto Cleanup; } /* finally attempt to change the security information */ Status = ZwSetSecurityObject(DirectoryHandle, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, NewRelSD); Cleanup: if (AdminSid != NULL) { RtlFreeSid(AdminSid); } if (LocalSystemSid != NULL) { RtlFreeSid(LocalSystemSid); } if (RelSD != NULL) { RtlpFreeMemory(RelSD, 'dSeS'); } if (NewRelSD != NULL) { RtlpFreeMemory(NewRelSD, 'dSeS'); } #ifdef _WIN64 if (AbsSDAllocated) { RtlpFreeMemory(AbsSD, 'dSeS'); } #endif return Status; }
static VOID CheckDirectorySecurity__( _In_ PCWSTR DirectoryName, _In_ PSID ExpectedOwner, _In_ PSID ExpectedGroup, _In_ ULONG AceCount, _In_ PCSTR FileAndLine, ...) { NTSTATUS Status; UNICODE_STRING DirectoryNameString; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE DirectoryHandle; PSECURITY_DESCRIPTOR SecurityDescriptor; ULONG SecurityDescriptorSize; PSID Owner; PSID Group; PACL Dacl; PACL Sacl; BOOLEAN Present; BOOLEAN Defaulted; va_list Arguments; RtlInitUnicodeString(&DirectoryNameString, DirectoryName); InitializeObjectAttributes(&ObjectAttributes, &DirectoryNameString, OBJ_KERNEL_HANDLE, NULL, NULL); Status = ZwOpenDirectoryObject(&DirectoryHandle, READ_CONTROL | ACCESS_SYSTEM_SECURITY, &ObjectAttributes); ok_eq_hex(Status, STATUS_SUCCESS); if (skip(NT_SUCCESS(Status), "No directory (%ls)\n", DirectoryName)) { return; } Status = ZwQuerySecurityObject(DirectoryHandle, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, NULL, 0, &SecurityDescriptorSize); ok_eq_hex(Status, STATUS_BUFFER_TOO_SMALL); if (skip(Status == STATUS_BUFFER_TOO_SMALL, "No security size (%ls)\n", DirectoryName)) { ObCloseHandle(DirectoryHandle, KernelMode); return; } SecurityDescriptor = ExAllocatePoolWithTag(PagedPool, SecurityDescriptorSize, 'dSmK'); ok(SecurityDescriptor != NULL, "Failed to allocate %lu bytes\n", SecurityDescriptorSize); if (skip(SecurityDescriptor != NULL, "No memory for descriptor (%ls)\n", DirectoryName)) { ObCloseHandle(DirectoryHandle, KernelMode); return; } Status = ZwQuerySecurityObject(DirectoryHandle, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, SecurityDescriptor, SecurityDescriptorSize, &SecurityDescriptorSize); ok_eq_hex(Status, STATUS_SUCCESS); if (NT_SUCCESS(Status)) { Owner = NULL; Status = RtlGetOwnerSecurityDescriptor(SecurityDescriptor, &Owner, &Defaulted); if (ExpectedOwner) CheckSid__(Owner, NO_SIZE, ExpectedOwner, FileAndLine); ok(Defaulted == FALSE, "Owner defaulted for %ls\n", DirectoryName); Group = NULL; Status = RtlGetGroupSecurityDescriptor(SecurityDescriptor, &Group, &Defaulted); if (ExpectedGroup) CheckSid__(Group, NO_SIZE, ExpectedGroup, FileAndLine); ok(Defaulted == FALSE, "Group defaulted for %ls\n", DirectoryName); Dacl = NULL; Status = RtlGetDaclSecurityDescriptor(SecurityDescriptor, &Present, &Dacl, &Defaulted); ok_eq_hex(Status, STATUS_SUCCESS); ok(Present == TRUE, "DACL not present for %ls\n", DirectoryName); ok(Defaulted == FALSE, "DACL defaulted for %ls\n", DirectoryName); va_start(Arguments, FileAndLine); VCheckAcl__(Dacl, AceCount, FileAndLine, Arguments); va_end(Arguments); Sacl = NULL; Status = RtlGetSaclSecurityDescriptor(SecurityDescriptor, &Present, &Sacl, &Defaulted); ok_eq_hex(Status, STATUS_SUCCESS); ok(Present == FALSE, "SACL present for %ls\n", DirectoryName); ok(Defaulted == FALSE, "SACL defaulted for %ls\n", DirectoryName); ok(Sacl == NULL, "Sacl is %p for %ls\n", Sacl, DirectoryName); } ExFreePoolWithTag(SecurityDescriptor, 'dSmK'); ObCloseHandle(DirectoryHandle, KernelMode); }