Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1482 lines
39 KiB

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
RegSec.c
Abstract:
This module contains code to apply security to the otherwise unsecured
top level keys, in fashion that will allow existing consumers access to
the keys that they need (print, srvmgr, etc.).
Author:
Richard Ward (richardw) 15 May 1996
Notes:
--*/
#include <rpc.h>
#include <string.h>
#include <wchar.h>
#include "regrpc.h"
#include "localreg.h"
#include "regsec.h"
#define REGSEC_READ 1
#define REGSEC_WRITE 2
WCHAR PerfRemoteRegistryKey[] = L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Perflib";
WCHAR RemoteRegistryKey[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\SecurePipeServers\\winreg";
WCHAR AllowedPathsKey[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\SecurePipeServers\\winreg\\AllowedPaths";
WCHAR AllowedExactPathsKey[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\SecurePipeServers\\winreg\\AllowedExactPaths";
WCHAR MachineValue[] = L"Machine";
PSECURITY_DESCRIPTOR RemoteRegistrySD;
PUNICODE_STRING MachineAllowedPaths;
PUCHAR MachineAllowedPathsBase;
ULONG MachineAllowedPathsCount;
PUNICODE_STRING MachineAllowedExactPaths;
PUCHAR MachineAllowedExactPathsBase;
ULONG MachineAllowedExactPathsCount;
GENERIC_MAPPING RemoteRegistryMappings;
LARGE_INTEGER WinregChange;
LARGE_INTEGER AllowedPathsChange;
LARGE_INTEGER AllowedExactPathsChange;
RTL_RESOURCE RegSecReloadLock;
NTSTATUS
RegSecReadSDFromRegistry(
IN HANDLE hKey,
OUT PSECURITY_DESCRIPTOR * pSDToUse)
/*++
Routine Description:
This function checks the registry in the magic place to see if an extra
ACL has been defined for the pipe being passed in. If there is one, it
is translated to a NP acl, then returned. If there isn't one, or if
something goes wrong, an NULL acl is returned.
Arguments:
InterfaceName name of the pipe to check for, e.g. winreg, etc.
pSDToUse returned a pointer to the security decriptor to use.
Return Value:
STATUS_SUCCESS,
STATUS_NO_MEMORY,
Possible other errors from registry apis.
--*/
{
NTSTATUS Status ;
PSECURITY_DESCRIPTOR pSD;
ULONG cbNeeded;
ACL_SIZE_INFORMATION AclSize;
ULONG AceIndex;
ACCESS_MASK NewMask;
PACCESS_ALLOWED_ACE pAce;
PACL pAcl;
BOOLEAN DaclPresent;
BOOLEAN DaclDefaulted;
UNICODE_STRING Interface;
UNICODE_STRING Allowed;
ULONG i;
BOOLEAN PipeNameOk;
PSECURITY_DESCRIPTOR pNewSD;
PACL pNewAcl;
PSID pSid;
PSID pSidCopy;
BOOLEAN Defaulted;
PACL Acl;
PSID AdminSid = NULL ;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY ;
ULONG SizeOfAcl ;
*pSDToUse = NULL;
//
// Son of a gun, someone has established security for this pipe.
//
pSD = NULL;
cbNeeded = 0;
Status = NtQuerySecurityObject(
hKey,
DACL_SECURITY_INFORMATION |
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION,
NULL,
0,
&cbNeeded );
if (Status == STATUS_BUFFER_TOO_SMALL)
{
pSD = RtlAllocateHeap(RtlProcessHeap(), 0, cbNeeded);
if (pSD)
{
Status = NtQuerySecurityObject(
hKey,
DACL_SECURITY_INFORMATION |
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION,
pSD,
cbNeeded,
&cbNeeded );
if (NT_SUCCESS(Status))
{
//
// Now, the tricky part. There is no 1-1 mapping of Key
// permissions to Pipe permissions. So, we do it here.
// We walk the DACL, and examine each ACE. We build a new
// access mask for each ACE, and set the flags as follows:
//
// if (KEY_READ) GENERIC_READ
// if (KEY_WRITE) GENERIC_WRITE
//
Status = RtlGetDaclSecurityDescriptor(
pSD,
&DaclPresent,
&pAcl,
&DaclDefaulted);
//
// If this failed, or there is no DACL present, then
// we're in trouble.
//
if (!NT_SUCCESS(Status) || !DaclPresent || !pAcl)
{
goto GetSDFromKey_BadAcl;
}
Status = RtlQueryInformationAcl(pAcl,
&AclSize,
sizeof(AclSize),
AclSizeInformation);
if (!NT_SUCCESS(Status))
{
goto GetSDFromKey_BadAcl;
}
for (AceIndex = 0; AceIndex < AclSize.AceCount ; AceIndex++ )
{
NewMask = 0;
Status = RtlGetAce( pAcl,
AceIndex,
& pAce);
//
// We don't care what kind of ACE it is, since we
// are just mapping the access types, and the access
// mask is always at a constant position.
//
if (NT_SUCCESS(Status))
{
if ((pAce->Header.AceType != ACCESS_ALLOWED_ACE_TYPE) &&
(pAce->Header.AceType != ACCESS_DENIED_ACE_TYPE))
{
//
// Must be an audit or random ACE type. Skip it.
//
continue;
}
if (pAce->Mask & KEY_READ)
{
NewMask |= REGSEC_READ;
}
if (pAce->Mask & KEY_WRITE)
{
NewMask |= REGSEC_WRITE;
}
pAce->Mask = NewMask;
}
else
{
//
// Panic: Bad ACL?
//
goto GetSDFromKey_BadAcl;
}
}
//
// RPC does not understand self-relative SDs, so
// we have to turn this into an absolute for them to turn
// back into a self relative.
//
pNewSD = RtlAllocateHeap(RtlProcessHeap(), 0, cbNeeded);
if (!pNewSD)
{
goto GetSDFromKey_BadAcl;
}
InitializeSecurityDescriptor( pNewSD,
SECURITY_DESCRIPTOR_REVISION);
pNewAcl = (PACL) (((PUCHAR) pNewSD) +
sizeof(SECURITY_DESCRIPTOR) );
RtlCopyMemory(pNewAcl, pAcl, AclSize.AclBytesInUse);
SetSecurityDescriptorDacl(pNewSD, TRUE, pNewAcl, FALSE);
Status = RtlGetOwnerSecurityDescriptor( pSD, &pSid, &Defaulted );
if ( NT_SUCCESS( Status ) )
{
pSidCopy = RtlAllocateHeap( RtlProcessHeap(),
0,
RtlLengthSid( pSid ) );
if ( pSidCopy )
{
RtlCopyMemory( pSidCopy, pSid, RtlLengthSid( pSid ) );
}
RtlSetOwnerSecurityDescriptor( pNewSD, pSidCopy, FALSE );
}
Status = RtlGetGroupSecurityDescriptor( pSD, &pSid, &Defaulted );
if ( NT_SUCCESS( Status ) )
{
pSidCopy = RtlAllocateHeap( RtlProcessHeap(),
0,
RtlLengthSid( pSid ) );
if ( pSidCopy )
{
RtlCopyMemory( pSidCopy, pSid, RtlLengthSid( pSid ) );
}
RtlSetGroupSecurityDescriptor( pNewSD, pSidCopy, FALSE );
}
RtlFreeHeap(RtlProcessHeap(), 0, pSD);
*pSDToUse = pNewSD;
return(Status);
}
}
return(STATUS_NO_MEMORY);
}
else
{
GetSDFromKey_BadAcl:
//
// Free the SD that we have allocated
//
if (pSD)
{
RtlFreeHeap(RtlProcessHeap(), 0, pSD);
}
//
// Key exists, but there is no security descriptor, or it is unreadable
// for whatever reason.
//
pSD = RtlAllocateHeap(RtlProcessHeap(), 0,
sizeof(SECURITY_DESCRIPTOR) );
if (pSD)
{
InitializeSecurityDescriptor( pSD,
SECURITY_DESCRIPTOR_REVISION );
Status = RtlAllocateAndInitializeSid(
&NtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&AdminSid );
SizeOfAcl = sizeof( ACL ) + sizeof( ACL ) + sizeof( ACE_HEADER ) +
RtlLengthRequiredSid( 2 );
Acl = RtlAllocateHeap( RtlProcessHeap(), 0, SizeOfAcl );
if ( NT_SUCCESS( Status ) &&
(Acl != NULL ))
{
(VOID) RtlCreateAcl(Acl,
SizeOfAcl,
ACL_REVISION );
Status = RtlAddAccessAllowedAce(
Acl,
ACL_REVISION,
REGSEC_READ | REGSEC_WRITE,
AdminSid );
if ( NT_SUCCESS( Status ) )
{
Status = RtlSetDaclSecurityDescriptor(
pSD,
TRUE,
Acl,
FALSE );
if ( NT_SUCCESS( Status ) )
{
RtlFreeSid( AdminSid );
*pSDToUse = pSD;
return STATUS_SUCCESS ;
}
}
}
if ( AdminSid )
{
RtlFreeSid( AdminSid );
}
if ( NT_SUCCESS( Status ) )
{
Status = STATUS_NO_MEMORY ;
}
}
return(STATUS_NO_MEMORY);
}
return Status ;
}
NTSTATUS
RegSecCheckIfAclValid(
VOID
)
/*++
Routine Description:
Checks if the local copy of the ACL from the registry is still valid (that is,
no one has changed it. If it is gone, the ACL is destroyed.
Arguments:
None.
Returns:
STATUS_SUCCESS if the state of the ACL is valid (whether it is present or not),
other error if an error occurred.
--*/
{
HANDLE hKey;
OBJECT_ATTRIBUTES ObjAttr;
UNICODE_STRING UniString;
PKEY_BASIC_INFORMATION KeyInfo ;
HANDLE Token ;
HANDLE NullHandle ;
UCHAR Buffer[ sizeof( KEY_BASIC_INFORMATION ) +
sizeof( RemoteRegistryKey ) + 16 ];
NTSTATUS Status ;
ULONG BufferSize ;
RtlInitUnicodeString( &UniString, RemoteRegistryKey );
InitializeObjectAttributes( &ObjAttr,
&UniString,
OBJ_CASE_INSENSITIVE,
NULL, NULL);
//
// Open the thread token. If we're in the middle of an RPC call, we won't be
// able to open the key (necessarily). So, revert to local system in order to
// open successfully.
Status = NtOpenThreadToken( NtCurrentThread(),
MAXIMUM_ALLOWED,
TRUE,
&Token );
if ( ( Status == STATUS_NO_IMPERSONATION_TOKEN ) ||
( Status == STATUS_NO_TOKEN ) )
{
Token = NULL ;
}
else if ( Status == STATUS_SUCCESS )
{
NullHandle = NULL ;
Status = NtSetInformationThread( NtCurrentThread(),
ThreadImpersonationToken,
(PVOID) &NullHandle,
(ULONG) sizeof( NullHandle ) );
}
else
{
return Status ;
}
Status = NtOpenKey( &hKey,
KEY_READ,
&ObjAttr);
if ( Token )
{
NTSTATUS RestoreStatus;
RestoreStatus = NtSetInformationThread( NtCurrentThread(),
ThreadImpersonationToken,
(PVOID) &Token,
sizeof( NullHandle ) );
NtClose( Token );
if ( !NT_SUCCESS( RestoreStatus ) )
{
Status = RestoreStatus ;
}
}
if ( !NT_SUCCESS( Status ) )
{
if ( ( Status == STATUS_OBJECT_PATH_NOT_FOUND ) ||
( Status == STATUS_OBJECT_NAME_NOT_FOUND ) )
{
//
// The key is not present. Either, the key has never been present,
// in which case we're essentially done, or the key has been deleted.
// If the key is deleted, we need to get rid of the remote acl.
//
if ( WinregChange.QuadPart )
{
//
// Ok, the key has been deleted. Get the exclusive lock and get to work.
//
RtlAcquireResourceExclusive( &RegSecReloadLock, TRUE );
//
// Make sure no one else got through and deleted it already:
//
if ( WinregChange.QuadPart )
{
RtlFreeHeap( RtlProcessHeap(), 0, RemoteRegistrySD );
RemoteRegistrySD = NULL ;
WinregChange.QuadPart = 0 ;
}
RtlReleaseResource( &RegSecReloadLock );
}
Status = STATUS_SUCCESS ;
}
return Status ;
}
Status = NtQueryKey( hKey,
KeyBasicInformation,
Buffer,
sizeof( Buffer ),
& BufferSize );
if ( !NT_SUCCESS( Status ) )
{
NtClose( hKey );
return Status ;
}
KeyInfo = (PKEY_BASIC_INFORMATION) Buffer ;
//
// See if it has changed
//
if ( KeyInfo->LastWriteTime.QuadPart > WinregChange.QuadPart )
{
RtlAcquireResourceExclusive( &RegSecReloadLock, TRUE );
//
// Since the last check was not safe, try again. Another thread
// may have updated things already.
//
if ( KeyInfo->LastWriteTime.QuadPart > WinregChange.QuadPart )
{
//
// Ok, this one is out of date. If there is already an SD
// allocated, free it. We can do that, since every other thread
// either is waiting for a shared access, or has also noticed that
// it is out of date, and waiting for exclusive access.
//
if ( RemoteRegistrySD )
{
RtlFreeHeap( RtlProcessHeap(), 0, RemoteRegistrySD );
RemoteRegistrySD = NULL ;
}
Status = RegSecReadSDFromRegistry( hKey, &RemoteRegistrySD );
if ( NT_SUCCESS( Status ) )
{
WinregChange.QuadPart = KeyInfo->LastWriteTime.QuadPart ;
}
}
RtlReleaseResource( &RegSecReloadLock );
}
NtClose( hKey );
return Status ;
}
//+---------------------------------------------------------------------------
//
// Function: RegSecReadAllowedPath
//
// Synopsis: Pull the Allowed paths out of the registry, and set up a
// table for searching later. This is a flat list, since the
// number of elements by default is 2, and shouldn't grow much
// bigger.
//
// Arguments: [hKey] --
// [Value] --
// [List] --
// [ListBase] --
// [ListCount] --
//
// History: 5-17-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
RegSecReadAllowedPath(
HANDLE hKey,
PWSTR Value,
PUNICODE_STRING * List,
PUCHAR * ListBase,
PULONG ListCount)
{
NTSTATUS Status;
UNICODE_STRING UniString;
PKEY_VALUE_PARTIAL_INFORMATION pValue;
ULONG Size;
PWSTR Scan;
ULONG StringCount;
PUNICODE_STRING Paths;
//
// Read the value size:
//
RtlInitUnicodeString( &UniString, Value );
Status = NtQueryValueKey( hKey,
&UniString,
KeyValuePartialInformation,
NULL,
0,
&Size );
if ( !NT_SUCCESS( Status ) && (Status != STATUS_BUFFER_TOO_SMALL))
{
if ( (Status == STATUS_OBJECT_PATH_NOT_FOUND) ||
(Status == STATUS_OBJECT_NAME_NOT_FOUND) )
{
return( TRUE );
}
return FALSE ;
}
//
// Allocate enough:
//
pValue = RtlAllocateHeap( RtlProcessHeap(), 0, Size );
if ( pValue )
{
Status = NtQueryValueKey( hKey,
&UniString,
KeyValuePartialInformation,
pValue,
Size,
&Size );
if( !NT_SUCCESS( Status ) ) {
RtlFreeHeap( RtlProcessHeap(), 0, pValue );
return FALSE;
}
}
if ( !pValue )
{
return( FALSE );
}
//
// Okay, we should have a multi-valued set of paths that we can
// allow access to despite the access control.
//
if ( pValue->Type != REG_MULTI_SZ )
{
RtlFreeHeap( RtlProcessHeap(), 0, pValue );
return( FALSE );
}
//
// Scan list, determine how many strings:
//
Scan = (PWSTR) pValue->Data;
StringCount = 0;
while ( *Scan )
{
while ( *Scan )
{
Scan ++;
}
StringCount ++;
Scan ++;
}
//
// Allocate enough UNICODE_STRING structs to point to each string
//
Paths = RtlAllocateHeap( RtlProcessHeap(), 0,
StringCount * sizeof(UNICODE_STRING) );
if ( !Paths )
{
RtlFreeHeap( RtlProcessHeap(), 0, pValue );
return( FALSE );
}
Scan = ( PWSTR ) pValue->Data;
*ListCount = StringCount;
StringCount = 0;
//
// Set up one UNICODE_STRING per string in the multi_sz,
//
while ( *Scan )
{
RtlInitUnicodeString( &Paths[ StringCount ],
Scan );
while ( *Scan)
{
Scan ++;
}
StringCount ++;
Scan ++;
}
//
// And pass the list back.
//
*ListBase = (PUCHAR) pValue;
*List = Paths;
return( TRUE );
}
//+---------------------------------------------------------------------------
//
// Function: RegSecReadAllowedPaths
//
// Synopsis: Reads the allowed paths out of the registry
//
// Arguments: (none)
//
// History: 5-17-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
NTSTATUS
RegSecCheckAllowedPaths(
VOID
)
{
HANDLE hKey;
OBJECT_ATTRIBUTES ObjAttr;
UNICODE_STRING UniString;
NTSTATUS Status;
HANDLE Token ;
HANDLE NullHandle ;
PKEY_BASIC_INFORMATION KeyInfo ;
UCHAR Buffer[ sizeof( KEY_BASIC_INFORMATION ) +
sizeof( AllowedExactPathsKey ) + 16 ];
ULONG BufferSize;
BOOLEAN ReadExactPath = FALSE;
PLARGE_INTEGER TimeStamp;
PUNICODE_STRING * List;
PUCHAR * ListBase;
PULONG ListCount;
ReadExact:
if( ReadExactPath ) {
RtlInitUnicodeString(&UniString, AllowedExactPathsKey);
TimeStamp = &AllowedExactPathsChange;
List = &MachineAllowedExactPaths;
ListBase = &MachineAllowedExactPathsBase;
ListCount = &MachineAllowedExactPathsCount;
} else {
RtlInitUnicodeString(&UniString, AllowedPathsKey);
TimeStamp = &AllowedPathsChange;
List = &MachineAllowedPaths;
ListBase = &MachineAllowedPathsBase;
ListCount = &MachineAllowedPathsCount;
}
InitializeObjectAttributes( &ObjAttr,
&UniString,
OBJ_CASE_INSENSITIVE,
NULL, NULL);
//
// Open the thread token. If we're in the middle of an RPC call, we won't be
// able to open the key (necessarily). So, revert to local system in order to
// open successfully.
Status = NtOpenThreadToken( NtCurrentThread(),
MAXIMUM_ALLOWED,
TRUE,
&Token );
if ( ( Status == STATUS_NO_IMPERSONATION_TOKEN ) ||
( Status == STATUS_NO_TOKEN ) )
{
Token = NULL ;
}
else if ( Status == STATUS_SUCCESS )
{
NullHandle = NULL ;
Status = NtSetInformationThread( NtCurrentThread(),
ThreadImpersonationToken,
(PVOID) &NullHandle,
(ULONG) sizeof( NullHandle ) );
}
else
{
return Status ;
}
//
// Open the key in local system context
//
Status = NtOpenKey( &hKey,
KEY_READ,
&ObjAttr);
//
// Immediately restore back to the client token.
//
if ( Token )
{
NTSTATUS RestoreStatus;
RestoreStatus = NtSetInformationThread( NtCurrentThread(),
ThreadImpersonationToken,
(PVOID) &Token,
sizeof( NullHandle ) );
NtClose( Token );
if ( !NT_SUCCESS( RestoreStatus ) )
{
Status = RestoreStatus ;
}
}
if ( !NT_SUCCESS( Status ) )
{
if ( ( Status == STATUS_OBJECT_PATH_NOT_FOUND ) ||
( Status == STATUS_OBJECT_NAME_NOT_FOUND ) )
{
//
// The key is not present. Either, the key has never been present,
// in which case we're essentially done, or the key has been deleted.
// If the key is deleted, we need to get rid of the remote acl.
//
if ( TimeStamp->QuadPart )
{
//
// Ok, the key has been deleted. Get the exclusive lock and get to work.
//
RtlAcquireResourceExclusive( &RegSecReloadLock, TRUE );
//
// Make sure no one else has freed it already:
//
if ( TimeStamp->QuadPart )
{
if ( *List )
{
RtlFreeHeap( RtlProcessHeap(), 0, *List );
RtlFreeHeap( RtlProcessHeap(), 0, *ListBase );
*List = NULL;
*ListBase = NULL;
}
TimeStamp->QuadPart = 0;
}
RtlReleaseResource( &RegSecReloadLock );
}
Status = STATUS_SUCCESS ;
if( !ReadExactPath ) {
ReadExactPath = TRUE;
goto ReadExact;
}
}
return Status ;
}
Status = NtQueryKey( hKey,
KeyBasicInformation,
Buffer,
sizeof( Buffer ),
& BufferSize );
if ( !NT_SUCCESS( Status ) )
{
NtClose( hKey );
return Status ;
}
KeyInfo = (PKEY_BASIC_INFORMATION) Buffer ;
//
// See if it has changed
//
if ( KeyInfo->LastWriteTime.QuadPart > TimeStamp->QuadPart )
{
//
// Well, it changed. So, we need to flush out the old (familiar?) stuff,
// and reload with the new stuff. So, back to the synchronization games.
//
RtlAcquireResourceExclusive( &RegSecReloadLock, TRUE );
//
// Make sure no one else beat us to it
//
if ( KeyInfo->LastWriteTime.QuadPart > TimeStamp->QuadPart )
{
if ( *List )
{
RtlFreeHeap( RtlProcessHeap(), 0, *List );
RtlFreeHeap( RtlProcessHeap(), 0, *ListBase );
*List = NULL ;
*ListBase = NULL ;
}
//
// Read in the paths allowed:
//
RegSecReadAllowedPath( hKey,
MachineValue,
List,
ListBase,
ListCount
);
}
RtlReleaseResource( &RegSecReloadLock );
}
NtClose( hKey );
if( !ReadExactPath ) {
ReadExactPath = TRUE;
goto ReadExact;
}
return STATUS_SUCCESS;
}
//+---------------------------------------------------------------------------
//
// Function: InitializeRemoteSecurity
//
// Synopsis: Hook to initialize our look-aside stuff
//
// Arguments: (none)
//
// History: 5-17-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
InitializeRemoteSecurity(
VOID
)
{
NTSTATUS Status ;
try
{
RtlInitializeResource( &RegSecReloadLock );
Status = STATUS_SUCCESS ;
}
except (EXCEPTION_EXECUTE_HANDLER)
{
Status = GetExceptionCode();
}
if ( !NT_SUCCESS( Status ) )
{
return Status ;
}
RemoteRegistryMappings.GenericRead = REGSEC_READ;
RemoteRegistryMappings.GenericWrite = REGSEC_WRITE;
RemoteRegistryMappings.GenericExecute = REGSEC_READ;
RemoteRegistryMappings.GenericAll = REGSEC_READ | REGSEC_WRITE;
WinregChange.QuadPart = 0 ;
AllowedPathsChange.QuadPart = 0 ;
AllowedExactPathsChange.QuadPart = 0 ;
return( TRUE );
}
//+---------------------------------------------------------------------------
//
// Function: RegSecCheckRemoteAccess
//
// Synopsis: Check remote access against the security descriptor we built
// on the side.
//
// Arguments: [phKey] --
//
// History: 5-17-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
RegSecCheckRemoteAccess(
PRPC_HKEY phKey)
{
NTSTATUS Status;
ACCESS_MASK Mask;
NTSTATUS AccessStatus;
HANDLE Token;
ULONG Size;
UCHAR QuickBuffer[sizeof(PRIVILEGE_SET) + 4 * sizeof(LUID_AND_ATTRIBUTES)];
PPRIVILEGE_SET PrivSet;
ULONG PrivilegeSetLen;
Status = RegSecCheckIfAclValid();
if ( !NT_SUCCESS( Status ) )
{
return FALSE ;
}
RtlAcquireResourceShared( &RegSecReloadLock, TRUE );
if ( RemoteRegistrySD )
{
//
// Capture the thread's token
//
Status = NtOpenThreadToken(
NtCurrentThread(),
MAXIMUM_ALLOWED,
TRUE,
&Token );
if ( !NT_SUCCESS(Status) )
{
RtlReleaseResource( &RegSecReloadLock );
return( FALSE );
}
PrivSet = (PPRIVILEGE_SET) QuickBuffer;
PrivilegeSetLen = sizeof( QuickBuffer );
//
// Do the access check.
//
Status = NtAccessCheck( RemoteRegistrySD,
Token,
MAXIMUM_ALLOWED,
&RemoteRegistryMappings,
PrivSet,
&PrivilegeSetLen,
&Mask,
&AccessStatus );
RtlReleaseResource( &RegSecReloadLock );
(void) NtClose( Token );
if ( NT_SUCCESS( Status ) )
{
if ( NT_SUCCESS( AccessStatus ) &&
(Mask & (REGSEC_READ | REGSEC_WRITE)) )
{
return( TRUE );
}
return( FALSE );
}
else
{
return FALSE ;
}
}
RtlReleaseResource( &RegSecReloadLock );
return( TRUE );
}
//+---------------------------------------------------------------------------
//
// Function: RegSecCheckRemotePerfAccess
//
// Synopsis: Check remote access against the security descriptor set on the
// performance access gate key
// (SOFTWARE\MICROSOFT\WINDOWS NT\CURRENTVERSION\PERFLIB)
//
// Arguments: [phKey] --
//
// History: 4-29-02 DragosS Created
//
// Notes:
//
//----------------------------------------------------------------------------
PSECURITY_DESCRIPTOR
RegSecGetPerfGateSD(
VOID
)
/*++
Routine Description:
Retrieves the SD set on the performance gate key. It'll be used for the
access check against the current thread token
Arguments:
None.
Returns:
STATUS_SUCCESS if the state of the ACL is valid (whether it is present or not),
other error if an error occurred.
--*/
{
HANDLE hKey;
OBJECT_ATTRIBUTES ObjAttr;
UNICODE_STRING UniString;
HANDLE Token ;
HANDLE NullHandle ;
NTSTATUS Status ;
PSECURITY_DESCRIPTOR PerflibSecurity = NULL;
RtlInitUnicodeString( &UniString, PerfRemoteRegistryKey );
InitializeObjectAttributes( &ObjAttr,
&UniString,
OBJ_CASE_INSENSITIVE,
NULL, NULL);
//
// Open the thread token. If we're in the middle of an RPC call, we won't be
// able to open the key (necessarily). So, revert to local system in order to
// open successfully.
Status = NtOpenThreadToken( NtCurrentThread(),
MAXIMUM_ALLOWED,
TRUE,
&Token );
if ( ( Status == STATUS_NO_IMPERSONATION_TOKEN ) ||
( Status == STATUS_NO_TOKEN ) )
{
Token = NULL ;
}
else if ( Status == STATUS_SUCCESS )
{
NullHandle = NULL ;
Status = NtSetInformationThread( NtCurrentThread(),
ThreadImpersonationToken,
(PVOID) &NullHandle,
(ULONG) sizeof( NullHandle ) );
}
else
{
return NULL;
}
Status = NtOpenKey( &hKey,
KEY_READ,
&ObjAttr);
if ( Token )
{
NTSTATUS RestoreStatus;
RestoreStatus = NtSetInformationThread( NtCurrentThread(),
ThreadImpersonationToken,
(PVOID) &Token,
sizeof( NullHandle ) );
NtClose( Token );
if ( !NT_SUCCESS( RestoreStatus ) )
{
return NULL;
}
}
if ( !NT_SUCCESS( Status ) )
{
return NULL;
}
//
// Read (+allocate) the SD from the key
//
Status = RegSecReadSDFromRegistry( hKey, &PerflibSecurity );
NtClose( hKey );
if( !NT_SUCCESS(Status) ) {
if( PerflibSecurity ) {
RtlFreeHeap(RtlProcessHeap(), 0, PerflibSecurity);
PerflibSecurity = NULL;
}
}
return PerflibSecurity;
}
BOOL
RegSecCheckRemotePerfAccess(
PRPC_HKEY phKey)
{
NTSTATUS Status;
ACCESS_MASK Mask;
NTSTATUS AccessStatus;
HANDLE Token;
ULONG Size;
UCHAR QuickBuffer[sizeof(PRIVILEGE_SET) + 4 * sizeof(LUID_AND_ATTRIBUTES)];
PPRIVILEGE_SET PrivSet;
ULONG PrivilegeSetLen;
PSECURITY_DESCRIPTOR PerflibSecurity = NULL;
PerflibSecurity = RegSecGetPerfGateSD();
if ( PerflibSecurity == NULL )
{
return FALSE ;
}
//
// Capture the thread's token
//
Status = NtOpenThreadToken(
NtCurrentThread(),
MAXIMUM_ALLOWED,
TRUE,
&Token );
if ( !NT_SUCCESS(Status) )
{
RtlFreeHeap(RtlProcessHeap(), 0, PerflibSecurity);
return( FALSE );
}
PrivSet = (PPRIVILEGE_SET) QuickBuffer;
PrivilegeSetLen = sizeof( QuickBuffer );
//
// Do the access check.
//
Status = NtAccessCheck( PerflibSecurity,
Token,
MAXIMUM_ALLOWED,
&RemoteRegistryMappings,
PrivSet,
&PrivilegeSetLen,
&Mask,
&AccessStatus );
RtlFreeHeap(RtlProcessHeap(), 0, PerflibSecurity);
(void) NtClose( Token );
if ( NT_SUCCESS( Status ) )
{
if ( NT_SUCCESS( AccessStatus ) &&
(Mask & (REGSEC_READ | REGSEC_WRITE)) )
{
return( TRUE );
}
}
return FALSE;
}
//+---------------------------------------------------------------------------
//
// Function: RegSecCheckPath
//
// Synopsis: Check a specific key path if we should ignore the current
// ACL.
//
// Arguments: [hKey] --
// [pSubKey] --
//
// History: 5-17-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
RegSecCheckPath(
HKEY hKey,
PUNICODE_STRING pSubKey)
{
UNICODE_STRING Comparator;
UNICODE_STRING String;
ULONG i;
ULONG Count = 0;
PUNICODE_STRING List;
BOOL Success ;
NTSTATUS Status ;
BOOLEAN ExactPath = FALSE;
Status = RegSecCheckAllowedPaths();
if ( !NT_SUCCESS( Status ) )
{
return FALSE ;
}
if ( (pSubKey->Buffer == NULL) ||
(pSubKey->Length == 0 ) ||
(pSubKey->MaximumLength == 0 ) )
{
return FALSE ;
}
if( !REGSEC_TEST_HANDLE( hKey, CHECK_MACHINE_PATHS ) )
{
return FALSE;
}
RtlAcquireResourceShared( &RegSecReloadLock, TRUE );
TryExactPath:
if( !ExactPath) {
Count = MachineAllowedPathsCount;
List = MachineAllowedPaths;
} else {
Count = MachineAllowedExactPathsCount;
List = MachineAllowedExactPaths;
}
Success = FALSE;
for ( i = 0 ; i < Count ; i++ )
{
String = *pSubKey;
//
// Ah ha, RPC strings often have the NULL included in the length.
// touch that up.
//
while ( (String.Length != 0) && (String.Buffer[ (String.Length / sizeof(WCHAR)) - 1] == L'\0') )
{
String.Length -= sizeof(WCHAR) ;
}
Comparator = List[ i ];
//
// If the Comparator is a prefix of the sub key, allow it (for spooler)
//
if ( String.Length > Comparator.Length )
{
if( ExactPath ) {
continue;
}
if ( String.Buffer[ Comparator.Length / sizeof(WCHAR) ] == L'\\' )
{
//
// Length-wise, it could be an ancestor
//
String.Length = Comparator.Length;
}
}
//
// If it matches, let it go...
//
if ( RtlCompareUnicodeString( &String, &Comparator, TRUE ) == 0 )
{
Success = TRUE ;
break;
}
}
if( (!Success) && (!ExactPath) ) {
ExactPath = TRUE;
goto TryExactPath;
}
RtlReleaseResource( &RegSecReloadLock ) ;
return( Success );
}