windows-nt/Source/XPSP1/NT/base/ntos/rtl/sysvol.c
2020-09-26 16:20:57 +08:00

563 lines
14 KiB
C

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
SysVol.c
Abstract:
Creation and Maintenance of the NTFS "System Volume Information"
directory.
Author:
Norbert P. Kusters (NorbertK) 1-Nov-2000
Revision History:
--*/
#include "ntrtlp.h"
PVOID
RtlpSysVolAllocate(
IN ULONG Size
);
VOID
RtlpSysVolFree(
IN PVOID Buffer
);
NTSTATUS
RtlpSysVolCreateSecurityDescriptor(
OUT PSECURITY_DESCRIPTOR* SecurityDescriptor,
OUT PACL* Acl
);
NTSTATUS
RtlpSysVolCheckOwnerAndSecurity(
IN HANDLE Handle,
IN PACL StandardAcl
);
VOID
RtlpSysVolAdminSid(
IN OUT SID* Sid
);
static const SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY;
#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
#pragma alloc_text(PAGE,RtlCreateSystemVolumeInformationFolder)
#pragma alloc_text(PAGE,RtlpSysVolAllocate)
#pragma alloc_text(PAGE,RtlpSysVolFree)
#pragma alloc_text(PAGE,RtlpSysVolCreateSecurityDescriptor)
#pragma alloc_text(PAGE,RtlpSysVolCheckOwnerAndSecurity)
#pragma alloc_text(PAGE,RtlpSysVolAdminSid)
#endif
PVOID
RtlpSysVolAllocate(
IN ULONG Size
)
{
PVOID p;
#ifdef NTOS_KERNEL_RUNTIME
p = ExAllocatePoolWithTag(PagedPool, Size, 'SloV');
#else
p = RtlAllocateHeap(RtlProcessHeap(), 0, Size);
#endif
return p;
}
VOID
RtlpSysVolFree(
IN PVOID Buffer
)
{
#ifdef NTOS_KERNEL_RUNTIME
ExFreePool(Buffer);
#else
RtlFreeHeap(RtlProcessHeap(), 0, Buffer);
#endif
}
VOID
RtlpSysVolAdminSid(
IN OUT SID* Sid
)
{
Sid->Revision = SID_REVISION;
Sid->SubAuthorityCount = 2;
Sid->IdentifierAuthority = ntAuthority;
Sid->SubAuthority[0] = SECURITY_BUILTIN_DOMAIN_RID;
Sid->SubAuthority[1] = DOMAIN_ALIAS_RID_ADMINS;
}
VOID
RtlpSysVolSystemSid(
IN OUT SID* Sid
)
{
Sid->Revision = SID_REVISION;
Sid->SubAuthorityCount = 1;
Sid->IdentifierAuthority = ntAuthority;
Sid->SubAuthority[0] = SECURITY_LOCAL_SYSTEM_RID;
}
NTSTATUS
RtlpSysVolCreateSecurityDescriptor(
OUT PSECURITY_DESCRIPTOR* SecurityDescriptor,
OUT PACL* Acl
)
{
PSECURITY_DESCRIPTOR sd;
NTSTATUS status;
PSID systemSid;
UCHAR sidBuffer[2*sizeof(SID)];
ULONG aclLength;
PACL acl;
sd = RtlpSysVolAllocate(sizeof(SECURITY_DESCRIPTOR));
if (!sd) {
return STATUS_INSUFFICIENT_RESOURCES;
}
status = RtlCreateSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION);
if (!NT_SUCCESS(status)) {
RtlpSysVolFree(sd);
return status;
}
systemSid = (PSID) sidBuffer;
RtlpSysVolSystemSid(systemSid);
aclLength = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) +
RtlLengthSid(systemSid) - sizeof(ULONG);
acl = RtlpSysVolAllocate(aclLength);
if (!acl) {
RtlpSysVolFree(sd);
return STATUS_INSUFFICIENT_RESOURCES;
}
status = RtlCreateAcl(acl, aclLength, ACL_REVISION);
if (!NT_SUCCESS(status)) {
RtlpSysVolFree(acl);
RtlpSysVolFree(sd);
return status;
}
status = RtlAddAccessAllowedAceEx(acl, ACL_REVISION, OBJECT_INHERIT_ACE |
CONTAINER_INHERIT_ACE,
STANDARD_RIGHTS_ALL |
SPECIFIC_RIGHTS_ALL, systemSid);
if (!NT_SUCCESS(status)) {
RtlpSysVolFree(acl);
RtlpSysVolFree(sd);
return status;
}
status = RtlSetDaclSecurityDescriptor(sd, TRUE, acl, FALSE);
if (!NT_SUCCESS(status)) {
RtlpSysVolFree(acl);
RtlpSysVolFree(sd);
return status;
}
*SecurityDescriptor = sd;
*Acl = acl;
return STATUS_SUCCESS;
}
NTSTATUS
RtlpSysVolCheckOwnerAndSecurity(
IN HANDLE Handle,
IN PACL StandardAcl
)
{
NTSTATUS status;
ULONG sdLength, sdLength2;
PSECURITY_DESCRIPTOR sd, sd2;
PSID sid;
BOOLEAN ownerDefaulted, daclPresent, daclDefaulted;
PACL acl;
ULONG i;
PACCESS_ALLOWED_ACE ace;
PSID systemSid;
UCHAR sidBuffer[2*sizeof(SID)];
PSID adminSid;
UCHAR sidBuffer2[2*sizeof(SID)];
status = NtQuerySecurityObject(Handle, OWNER_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION, NULL, 0,
&sdLength);
if (status != STATUS_BUFFER_TOO_SMALL) {
// The file system does not support security.
return STATUS_SUCCESS;
}
sd = RtlpSysVolAllocate(sdLength);
if (!sd) {
return STATUS_INSUFFICIENT_RESOURCES;
}
status = NtQuerySecurityObject(Handle, OWNER_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION, sd, sdLength,
&sdLength);
if (!NT_SUCCESS(status)) {
RtlpSysVolFree(sd);
return status;
}
status = RtlGetDaclSecurityDescriptor(sd, &daclPresent, &acl,
&daclDefaulted);
if (!NT_SUCCESS(status)) {
RtlpSysVolFree(sd);
return status;
}
status = RtlGetOwnerSecurityDescriptor(sd, &sid, &ownerDefaulted);
if (!NT_SUCCESS(status)) {
RtlpSysVolFree(sd);
return status;
}
//
// Setup well know SIDs
//
systemSid = (PSID) sidBuffer;
adminSid = (PSID) sidBuffer2;
RtlpSysVolSystemSid(systemSid);
RtlpSysVolAdminSid(adminSid);
if (!sid) {
goto ResetSecurity;
}
if (!RtlEqualSid(sid, adminSid)) {
goto ResetSecurity;
}
if (!daclPresent || (daclPresent && !acl)) {
goto ResetSecurity;
}
for (i = 0; ; i++) {
status = RtlGetAce(acl, i, &ace);
if (!NT_SUCCESS(status)) {
ace = NULL;
}
if (!ace) {
break;
}
if (ace->Header.AceType != ACCESS_ALLOWED_ACE_TYPE) {
continue;
}
sid = (PSID) &ace->SidStart;
if (!RtlEqualSid(sid, systemSid)) {
continue;
}
break;
}
if (!ace) {
goto ResetSecurity;
}
if (!(ace->Header.AceFlags&OBJECT_INHERIT_ACE) ||
!(ace->Header.AceFlags&CONTAINER_INHERIT_ACE)) {
ace->Header.AceFlags |= OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
status = NtSetSecurityObject(Handle, DACL_SECURITY_INFORMATION, sd);
} else {
status = STATUS_SUCCESS;
}
RtlpSysVolFree(sd);
return status;
ResetSecurity:
sdLength2 = sdLength;
status = RtlSelfRelativeToAbsoluteSD2(sd, &sdLength2);
if (status == STATUS_BUFFER_TOO_SMALL) {
sd2 = RtlpSysVolAllocate(sdLength2);
if (!sd2) {
RtlpSysVolFree(sd);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(sd2, sd, sdLength);
RtlpSysVolFree(sd);
sd = sd2;
sdLength = sdLength2;
status = RtlSelfRelativeToAbsoluteSD2(sd, &sdLength);
if (!NT_SUCCESS(status)) {
RtlpSysVolFree(sd);
return status;
}
}
status = RtlSetOwnerSecurityDescriptor(sd, adminSid, FALSE);
if (!NT_SUCCESS(status)) {
RtlpSysVolFree(sd);
return status;
}
status = RtlSetDaclSecurityDescriptor(sd, TRUE, StandardAcl, FALSE);
if (!NT_SUCCESS(status)) {
RtlpSysVolFree(sd);
return status;
}
sdLength2 = 0;
status = RtlMakeSelfRelativeSD(sd, NULL, &sdLength2);
if (status != STATUS_BUFFER_TOO_SMALL) {
RtlpSysVolFree(sd);
return status;
}
sd2 = RtlpSysVolAllocate(sdLength2);
if (!sd2) {
RtlpSysVolFree(sd);
return STATUS_INSUFFICIENT_RESOURCES;
}
status = RtlMakeSelfRelativeSD(sd, sd2, &sdLength2);
RtlpSysVolFree(sd);
if (!NT_SUCCESS(status)) {
RtlpSysVolFree(sd2);
return status;
}
sd = sd2;
sdLength = sdLength2;
status = NtSetSecurityObject(Handle, OWNER_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION, sd);
RtlpSysVolFree(sd);
return status;
}
VOID
RtlpSysVolTakeOwnership(
IN PUNICODE_STRING DirectoryName
)
/*++
Routine Description:
This routine is called when the open for the directory failed. This
routine will attempt to set the owner of the file to the caller's
ownership so that another attempt to open the file can be attempted.
Arguments:
DirectoryName - Supplies the directory name.
Return Value:
None.
--*/
{
NTSTATUS status;
HANDLE tokenHandle, fileHandle;
TOKEN_PRIVILEGES tokenPrivileges;
OBJECT_ATTRIBUTES oa;
IO_STATUS_BLOCK ioStatus;
SECURITY_DESCRIPTOR sd;
PSID adminSid;
UCHAR sidBuffer[2*sizeof(SID)];
status = NtOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES |
TOKEN_QUERY, &tokenHandle);
if (!NT_SUCCESS(status)) {
return;
}
tokenPrivileges.PrivilegeCount = 1;
tokenPrivileges.Privileges[0].Luid =
RtlConvertLongToLuid(SE_TAKE_OWNERSHIP_PRIVILEGE);
tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
status = NtAdjustPrivilegesToken(tokenHandle, FALSE, &tokenPrivileges,
sizeof(tokenPrivileges), NULL, NULL);
if (!NT_SUCCESS(status)) {
NtClose(tokenHandle);
return;
}
InitializeObjectAttributes(&oa, DirectoryName, OBJ_CASE_INSENSITIVE, NULL,
NULL);
status = NtOpenFile(&fileHandle, WRITE_OWNER | SYNCHRONIZE, &oa, &ioStatus,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
if (!NT_SUCCESS(status)) {
NtClose(tokenHandle);
return;
}
RtlCreateSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
adminSid = (PSID) sidBuffer;
RtlpSysVolAdminSid(adminSid);
status = RtlSetOwnerSecurityDescriptor(&sd, adminSid, FALSE);
if (!NT_SUCCESS(status)) {
NtClose(fileHandle);
NtClose(tokenHandle);
return;
}
status = NtSetSecurityObject(fileHandle, OWNER_SECURITY_INFORMATION, &sd);
if (!NT_SUCCESS(status)) {
NtClose(fileHandle);
NtClose(tokenHandle);
return;
}
NtClose(fileHandle);
NtClose(tokenHandle);
}
NTSTATUS
RtlCreateSystemVolumeInformationFolder(
IN PUNICODE_STRING VolumeRootPath
)
/*++
Routine Description:
This routine verifies the existence of the "System Volume Information"
folder on the given volume. If the folder is not present, then the
folder is created with one ACE indicating full access for SYSTEM. The ACE
will have the inheritance bits set. The folder will be created with
the HIDDEN and SYSTEM attributes set.
If the folder is already present, the ACE that indicates full control
for SYSTEM will be checked and if necessary modified to have the
inheritance bits set.
Arguments:
VolumeRootPath - Supplies a path to the root of an NTFS volume.
Return Value:
NTSTATUS
--*/
{
UNICODE_STRING sysVolName;
UNICODE_STRING dirName;
BOOLEAN needBackslash;
NTSTATUS status;
PSECURITY_DESCRIPTOR securityDescriptor;
PACL acl;
OBJECT_ATTRIBUTES oa;
HANDLE h;
IO_STATUS_BLOCK ioStatus;
RtlInitUnicodeString(&sysVolName, RTL_SYSTEM_VOLUME_INFORMATION_FOLDER);
dirName.Length = VolumeRootPath->Length + sysVolName.Length;
if (VolumeRootPath->Buffer[VolumeRootPath->Length/sizeof(WCHAR) - 1] !=
'\\') {
dirName.Length += sizeof(WCHAR);
needBackslash = TRUE;
} else {
needBackslash = FALSE;
}
dirName.MaximumLength = dirName.Length + sizeof(WCHAR);
dirName.Buffer = RtlpSysVolAllocate(dirName.MaximumLength);
if (!dirName.Buffer) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(dirName.Buffer, VolumeRootPath->Buffer,
VolumeRootPath->Length);
dirName.Length = VolumeRootPath->Length;
if (needBackslash) {
dirName.Buffer[VolumeRootPath->Length/sizeof(WCHAR)] = '\\';
dirName.Length += sizeof(WCHAR);
}
RtlCopyMemory((PCHAR) dirName.Buffer + dirName.Length,
sysVolName.Buffer, sysVolName.Length);
dirName.Length += sysVolName.Length;
dirName.Buffer[dirName.Length/sizeof(WCHAR)] = 0;
status = RtlpSysVolCreateSecurityDescriptor(&securityDescriptor, &acl);
if (!NT_SUCCESS(status)) {
RtlpSysVolFree(dirName.Buffer);
return status;
}
InitializeObjectAttributes(&oa, &dirName, OBJ_CASE_INSENSITIVE, NULL,
securityDescriptor);
status = NtCreateFile(&h, READ_CONTROL | WRITE_DAC | WRITE_OWNER |
SYNCHRONIZE, &oa, &ioStatus, NULL,
FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
FILE_SHARE_READ | FILE_SHARE_WRITE |
FILE_SHARE_DELETE, FILE_OPEN_IF,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE,
NULL, 0);
if (!NT_SUCCESS(status)) {
RtlpSysVolTakeOwnership(&dirName);
status = NtCreateFile(&h, READ_CONTROL | WRITE_DAC | WRITE_OWNER |
SYNCHRONIZE, &oa, &ioStatus, NULL,
FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
FILE_SHARE_READ | FILE_SHARE_WRITE |
FILE_SHARE_DELETE, FILE_OPEN_IF,
FILE_SYNCHRONOUS_IO_NONALERT |
FILE_DIRECTORY_FILE, NULL, 0);
}
RtlpSysVolFree(dirName.Buffer);
if (!NT_SUCCESS(status)) {
RtlpSysVolFree(acl);
RtlpSysVolFree(securityDescriptor);
return status;
}
RtlpSysVolFree(securityDescriptor);
status = RtlpSysVolCheckOwnerAndSecurity(h, acl);
NtClose(h);
RtlpSysVolFree(acl);
return status;
}