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.
3004 lines
87 KiB
3004 lines
87 KiB
/*++
|
|
|
|
Copyright (c) 1996-1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Security.c
|
|
|
|
Abstract:
|
|
|
|
This module contains common security routines for
|
|
NT Clusters.
|
|
|
|
Author:
|
|
|
|
John Vert (jvert) 12-Mar-1996
|
|
|
|
--*/
|
|
|
|
#include "clusrtlp.h"
|
|
#include "api_rpc.h"
|
|
#include <aclapi.h>
|
|
#include <accctrl.h>
|
|
#include <malloc.h>
|
|
#include <windns.h>
|
|
|
|
//
|
|
// Use this SD to adjust access to tokens so that the cluster
|
|
// service can access and adjust privileges.
|
|
//
|
|
// This is initialized in ClRtlBuildClusterServiceSecurityDescriptor()
|
|
// and freed in ClRtlFreeClusterServiceSecurityDescriptor( ).
|
|
//
|
|
PSECURITY_DESCRIPTOR g_pClusterSecurityDescriptor = NULL;
|
|
|
|
//
|
|
// local defines
|
|
//
|
|
#define FREE_IF_NOT_NULL( _ptr, _func ) \
|
|
if (( _ptr ) != NULL ) { \
|
|
_func( _ptr ); \
|
|
}
|
|
|
|
LONG
|
|
MapSAToRpcSA (
|
|
IN LPSECURITY_ATTRIBUTES lpSA,
|
|
OUT PRPC_SECURITY_ATTRIBUTES lpRpcSA
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Maps a SECURITY_ATTRIBUTES structure to a RPC_SECURITY_ATTRIBUTES
|
|
structure by converting the SECURITY_DESCRIPTOR to a form where it can
|
|
be marshalled/unmarshalled.
|
|
|
|
Arguments:
|
|
|
|
lpSA - Supplies a pointer to the SECURITY_ATTRIBUTES structure to be
|
|
converted.
|
|
|
|
lpRpcSA - Supplies a pointer to the converted RPC_SECURITY_ATTRIBUTES
|
|
structure. The caller should free (using RtlFreeHeap) the field
|
|
lpSecurityDescriptor when its finished using it.
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS if the SECURITY_ATTRIBUTES is
|
|
succesfully mapped.
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG Error;
|
|
|
|
ASSERT( lpSA != NULL );
|
|
ASSERT( lpRpcSA != NULL );
|
|
|
|
//
|
|
// Map the SECURITY_DESCRIPTOR to a RPC_SECURITY_DESCRIPTOR.
|
|
//
|
|
lpRpcSA->RpcSecurityDescriptor.lpSecurityDescriptor = NULL;
|
|
|
|
if( lpSA->lpSecurityDescriptor != NULL ) {
|
|
Error = MapSDToRpcSD(
|
|
lpSA->lpSecurityDescriptor,
|
|
&lpRpcSA->RpcSecurityDescriptor
|
|
);
|
|
} else {
|
|
lpRpcSA->RpcSecurityDescriptor.cbInSecurityDescriptor = 0;
|
|
lpRpcSA->RpcSecurityDescriptor.cbOutSecurityDescriptor = 0;
|
|
Error = ERROR_SUCCESS;
|
|
}
|
|
|
|
if( Error == ERROR_SUCCESS ) {
|
|
|
|
//
|
|
//
|
|
// The supplied SECURITY_DESCRIPTOR was successfully converted
|
|
// to self relative format so assign the remaining fields.
|
|
//
|
|
|
|
lpRpcSA->nLength = lpSA->nLength;
|
|
|
|
lpRpcSA->bInheritHandle = ( BOOLEAN ) lpSA->bInheritHandle;
|
|
}
|
|
|
|
return Error;
|
|
|
|
} // MapSAToRpcSA
|
|
|
|
LONG
|
|
MapSDToRpcSD (
|
|
IN PSECURITY_DESCRIPTOR lpSD,
|
|
IN OUT PRPC_SECURITY_DESCRIPTOR lpRpcSD
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Maps a SECURITY_DESCRIPTOR to a RPC_SECURITY_DESCRIPTOR by converting
|
|
it to a form where it can be marshalled/unmarshalled.
|
|
|
|
Arguments:
|
|
|
|
lpSD - Supplies a pointer to the SECURITY_DESCRIPTOR
|
|
structure to be converted.
|
|
|
|
lpRpcSD - Supplies a pointer to the converted RPC_SECURITY_DESCRIPTOR
|
|
structure. Memory for the security descriptor is allocated if
|
|
not provided. The caller must take care of freeing up the memory
|
|
if necessary.
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS if the SECURITY_DESCRIPTOR is
|
|
succesfully mapped.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD cbLen;
|
|
LONG lStatus;
|
|
BOOL fAllocated = FALSE;
|
|
|
|
ASSERT( lpSD != NULL );
|
|
ASSERT( lpRpcSD != NULL );
|
|
|
|
if( RtlValidSecurityDescriptor( lpSD )) {
|
|
|
|
cbLen = RtlLengthSecurityDescriptor( lpSD );
|
|
CL_ASSERT( cbLen > 0 );
|
|
|
|
//
|
|
// If we're not provided a buffer for the security descriptor,
|
|
// allocate it.
|
|
//
|
|
if ( !lpRpcSD->lpSecurityDescriptor ) {
|
|
|
|
//
|
|
// Allocate space for the converted SECURITY_DESCRIPTOR.
|
|
//
|
|
lpRpcSD->lpSecurityDescriptor =
|
|
( PBYTE ) RtlAllocateHeap(
|
|
RtlProcessHeap( ), 0,
|
|
cbLen
|
|
);
|
|
|
|
//
|
|
// If the memory allocation failed, return.
|
|
//
|
|
if( lpRpcSD->lpSecurityDescriptor == NULL ) {
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
lpRpcSD->cbInSecurityDescriptor = cbLen;
|
|
fAllocated = TRUE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Make sure that the buffer provided is big enough
|
|
//
|
|
if ( lpRpcSD->cbInSecurityDescriptor < cbLen ) {
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the size of the transmittable buffer
|
|
//
|
|
lpRpcSD->cbOutSecurityDescriptor = cbLen;
|
|
|
|
//
|
|
// Convert the supplied SECURITY_DESCRIPTOR to self relative form.
|
|
//
|
|
|
|
lStatus = RtlNtStatusToDosError(
|
|
RtlMakeSelfRelativeSD(
|
|
lpSD,
|
|
lpRpcSD->lpSecurityDescriptor,
|
|
&lpRpcSD->cbInSecurityDescriptor)
|
|
);
|
|
|
|
if ( ( lStatus != ERROR_SUCCESS ) && ( fAllocated ) ) {
|
|
RtlFreeHeap ( RtlProcessHeap( ), 0, lpRpcSD->lpSecurityDescriptor );
|
|
}
|
|
|
|
return lStatus;
|
|
} else {
|
|
|
|
//
|
|
// The supplied SECURITY_DESCRIPTOR is invalid.
|
|
//
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
} // MapSDToRpcSD
|
|
|
|
DWORD
|
|
ClRtlSetObjSecurityInfo(
|
|
IN HANDLE hObject,
|
|
IN SE_OBJECT_TYPE SeObjType,
|
|
IN DWORD dwAdminMask,
|
|
IN DWORD dwOwnerMask,
|
|
IN DWORD dwEveryOneMask
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the proper security on the cluster object(registry root/cluster files
|
|
directory).
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
{
|
|
DWORD Status;
|
|
PACL pAcl = NULL;
|
|
DWORD cbDaclSize;
|
|
PACCESS_ALLOWED_ACE pAce;
|
|
PSID pAdminSid = NULL;
|
|
PSID pOwnerSid = NULL;
|
|
PSID pEveryoneSid = NULL;
|
|
PULONG pSubAuthority;
|
|
SID_IDENTIFIER_AUTHORITY SidIdentifierNtAuth = SECURITY_NT_AUTHORITY;
|
|
SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY;
|
|
SID_IDENTIFIER_AUTHORITY siaCreator = SECURITY_CREATOR_SID_AUTHORITY;
|
|
DWORD AceIndex = 0;
|
|
|
|
//
|
|
// Create the local Administrators group SID.
|
|
//
|
|
pAdminSid = LocalAlloc(LMEM_FIXED, GetSidLengthRequired( 2 ));
|
|
if (pAdminSid == NULL) {
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto error_exit;
|
|
}
|
|
if (!InitializeSid(pAdminSid, &SidIdentifierNtAuth, 2)) {
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Set the sub-authorities on the ACE for the local Administrators group.
|
|
//
|
|
pSubAuthority = GetSidSubAuthority( pAdminSid, 0 );
|
|
*pSubAuthority = SECURITY_BUILTIN_DOMAIN_RID;
|
|
|
|
pSubAuthority = GetSidSubAuthority( pAdminSid, 1 );
|
|
*pSubAuthority = DOMAIN_ALIAS_RID_ADMINS;
|
|
|
|
//
|
|
// Create the owner's SID
|
|
//
|
|
pOwnerSid = LocalAlloc(LMEM_FIXED, GetSidLengthRequired( 1 ));
|
|
if (pOwnerSid == NULL) {
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto error_exit;
|
|
}
|
|
if (!InitializeSid(pOwnerSid, &siaCreator, 1)) {
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
|
|
pSubAuthority = GetSidSubAuthority(pOwnerSid, 0);
|
|
*pSubAuthority = SECURITY_CREATOR_OWNER_RID;
|
|
|
|
//
|
|
// Create the Everyone SID
|
|
//
|
|
pEveryoneSid = LocalAlloc(LMEM_FIXED, GetSidLengthRequired( 1 ));
|
|
if (pEveryoneSid == NULL) {
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto error_exit;
|
|
}
|
|
if (!InitializeSid(pEveryoneSid, &siaWorld, 1)) {
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
|
|
pSubAuthority = GetSidSubAuthority(pEveryoneSid, 0);
|
|
*pSubAuthority = SECURITY_WORLD_RID;
|
|
|
|
//
|
|
// now calculate the size of the buffer needed to hold the
|
|
// ACL and its ACEs and initialize it.
|
|
//
|
|
cbDaclSize = sizeof(ACL) +
|
|
3 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(pAce->SidStart)) +
|
|
GetLengthSid(pAdminSid) + GetLengthSid(pOwnerSid) + GetLengthSid(pEveryoneSid);
|
|
|
|
pAcl = (PACL)LocalAlloc( LMEM_FIXED, cbDaclSize );
|
|
if ( pAcl == NULL ) {
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto error_exit;
|
|
}
|
|
|
|
if ( !InitializeAcl( pAcl, cbDaclSize, ACL_REVISION )) {
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// add in the specified ACEs
|
|
//
|
|
if (dwAdminMask) {
|
|
//
|
|
// Add the ACE for the local Administrators group to the DACL
|
|
//
|
|
if ( !AddAccessAllowedAce( pAcl,
|
|
ACL_REVISION,
|
|
dwAdminMask,
|
|
pAdminSid )) {
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
GetAce(pAcl, AceIndex, (PVOID *)&pAce);
|
|
++AceIndex;
|
|
pAce->Header.AceFlags |= CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
|
|
}
|
|
|
|
if (dwOwnerMask) {
|
|
//
|
|
// Add the ACE for the Creator/Owner to the DACL
|
|
//
|
|
if ( !AddAccessAllowedAce( pAcl,
|
|
ACL_REVISION,
|
|
dwOwnerMask,
|
|
pOwnerSid )) {
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
GetAce(pAcl, AceIndex, (PVOID *)&pAce);
|
|
++AceIndex;
|
|
pAce->Header.AceFlags |= CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
|
|
}
|
|
|
|
if (dwEveryOneMask) {
|
|
//
|
|
// Add the ACE for Everyone to the DACL
|
|
//
|
|
if ( !AddAccessAllowedAce( pAcl,
|
|
ACL_REVISION,
|
|
dwEveryOneMask,
|
|
pEveryoneSid )) {
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
GetAce(pAcl, AceIndex, (PVOID *)&pAce);
|
|
++AceIndex;
|
|
pAce->Header.AceFlags |= CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
|
|
}
|
|
|
|
//
|
|
// Now that we have an ACL we can set the appropriate security.
|
|
//
|
|
Status = SetSecurityInfo(hObject,
|
|
SeObjType,
|
|
DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
|
|
NULL,
|
|
NULL,
|
|
pAcl,
|
|
NULL);
|
|
|
|
error_exit:
|
|
if (pAdminSid != NULL) {
|
|
LocalFree(pAdminSid);
|
|
}
|
|
if (pOwnerSid != NULL) {
|
|
LocalFree(pOwnerSid);
|
|
}
|
|
if (pEveryoneSid != NULL) {
|
|
LocalFree(pEveryoneSid);
|
|
}
|
|
if (pAcl != NULL) {
|
|
LocalFree(pAcl);
|
|
}
|
|
|
|
return(Status);
|
|
|
|
} // ClRtlSetObjSecurityInfo
|
|
|
|
DWORD
|
|
ClRtlFreeClusterServiceSecurityDescriptor( )
|
|
/*++
|
|
|
|
Frees the security descriptor that is used to give the cluster
|
|
service access to tokens.
|
|
|
|
--*/
|
|
{
|
|
LocalFree( g_pClusterSecurityDescriptor );
|
|
g_pClusterSecurityDescriptor = NULL;
|
|
|
|
return ERROR_SUCCESS;
|
|
} // ClRtlFreeClusterServiceSecurityDescriptor
|
|
|
|
DWORD
|
|
ClRtlBuildClusterServiceSecurityDescriptor(
|
|
PSECURITY_DESCRIPTOR * poutSD
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Builds a security descriptor that gives the cluster service
|
|
access to tokens. It places this in a global that can be
|
|
reused when other tokens are generated.
|
|
|
|
This should be called when the process starts to initialize
|
|
the global. It can pass in NULL if no reference is needed
|
|
right away.
|
|
|
|
NOTE: poutSD should NOT be freed by the caller.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE ProcessToken = NULL;
|
|
ULONG AclLength;
|
|
ULONG SDLength;
|
|
PACL NewDacl = NULL;
|
|
ULONG TokenUserSize;
|
|
PTOKEN_USER ProcessTokenUser = NULL;
|
|
SECURITY_DESCRIPTOR SecurityDescriptor;
|
|
PSECURITY_DESCRIPTOR pNewSD = NULL;
|
|
|
|
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
|
|
PSID GlobalLocalSystemSid = NULL;
|
|
PSID GlobalAliasAdminsSid = NULL;
|
|
|
|
// If we already have a SD, reuse it.
|
|
if ( g_pClusterSecurityDescriptor != NULL ) {
|
|
Status = ERROR_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Build the two well known sids we need.
|
|
//
|
|
Status = RtlAllocateAndInitializeSid(
|
|
&NtAuthority,
|
|
1,
|
|
SECURITY_LOCAL_SYSTEM_RID,
|
|
0,0,0,0,0,0,0,
|
|
&GlobalLocalSystemSid
|
|
);
|
|
if ( ! NT_SUCCESS( Status ) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = RtlAllocateAndInitializeSid(
|
|
&NtAuthority,
|
|
2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS,
|
|
0,0,0,0,0,0,
|
|
&GlobalAliasAdminsSid
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Cleanup;
|
|
}
|
|
//
|
|
// Open the process token to find out the user sid
|
|
//
|
|
|
|
Status = NtOpenProcessToken(
|
|
NtCurrentProcess(),
|
|
TOKEN_QUERY | WRITE_DAC,
|
|
&ProcessToken
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
// find out the size
|
|
Status = NtQueryInformationToken(
|
|
ProcessToken,
|
|
TokenUser,
|
|
NULL,
|
|
0,
|
|
&TokenUserSize
|
|
);
|
|
CL_ASSERT( Status == STATUS_BUFFER_TOO_SMALL );
|
|
if ( Status != STATUS_BUFFER_TOO_SMALL ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
ProcessTokenUser = (PTOKEN_USER) LocalAlloc( 0, TokenUserSize );
|
|
if ( ProcessTokenUser == NULL ) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = NtQueryInformationToken(
|
|
ProcessToken,
|
|
TokenUser,
|
|
ProcessTokenUser,
|
|
TokenUserSize,
|
|
&TokenUserSize
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
AclLength = 3 * sizeof( ACCESS_ALLOWED_ACE ) - 3 * sizeof( ULONG ) +
|
|
RtlLengthSid( ProcessTokenUser->User.Sid ) +
|
|
RtlLengthSid( GlobalLocalSystemSid ) +
|
|
RtlLengthSid( GlobalAliasAdminsSid ) +
|
|
sizeof( ACL );
|
|
|
|
NewDacl = (PACL) LocalAlloc(0, AclLength );
|
|
|
|
if (NewDacl == NULL) {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = RtlCreateAcl( NewDacl, AclLength, ACL_REVISION2 );
|
|
CL_ASSERT(NT_SUCCESS( Status ));
|
|
|
|
Status = RtlAddAccessAllowedAce (
|
|
NewDacl,
|
|
ACL_REVISION2,
|
|
TOKEN_ALL_ACCESS,
|
|
ProcessTokenUser->User.Sid
|
|
);
|
|
CL_ASSERT( NT_SUCCESS( Status ));
|
|
|
|
Status = RtlAddAccessAllowedAce (
|
|
NewDacl,
|
|
ACL_REVISION2,
|
|
TOKEN_ALL_ACCESS,
|
|
GlobalAliasAdminsSid
|
|
);
|
|
CL_ASSERT( NT_SUCCESS( Status ));
|
|
|
|
Status = RtlAddAccessAllowedAce (
|
|
NewDacl,
|
|
ACL_REVISION2,
|
|
TOKEN_ALL_ACCESS,
|
|
GlobalLocalSystemSid
|
|
);
|
|
CL_ASSERT( NT_SUCCESS( Status ));
|
|
|
|
Status = RtlCreateSecurityDescriptor (
|
|
&SecurityDescriptor,
|
|
SECURITY_DESCRIPTOR_REVISION
|
|
);
|
|
CL_ASSERT( NT_SUCCESS( Status ));
|
|
|
|
Status = RtlSetDaclSecurityDescriptor(
|
|
&SecurityDescriptor,
|
|
TRUE,
|
|
NewDacl,
|
|
FALSE
|
|
);
|
|
CL_ASSERT( NT_SUCCESS( Status ));
|
|
|
|
// Convert the newly created SD into a relative SD to make cleanup
|
|
// easier.
|
|
SDLength = sizeof( SECURITY_DESCRIPTOR_RELATIVE ) + AclLength;
|
|
pNewSD = (PSECURITY_DESCRIPTOR) LocalAlloc( 0, SDLength );
|
|
if ( pNewSD == NULL ) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( !MakeSelfRelativeSD( &SecurityDescriptor, pNewSD, &SDLength ) ) {
|
|
Status = GetLastError( );
|
|
if ( Status != STATUS_BUFFER_TOO_SMALL ) {
|
|
ClRtlLogPrint(LOG_CRITICAL, "[ClRtl] MakeSelfRelativeSD failed 0x%1!.8x!\n", Status );
|
|
goto Cleanup;
|
|
}
|
|
|
|
LocalFree( pNewSD );
|
|
pNewSD = (PSECURITY_DESCRIPTOR) LocalAlloc( 0, SDLength );
|
|
if ( pNewSD == NULL ) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if ( !MakeSelfRelativeSD( &SecurityDescriptor, pNewSD, &SDLength ) ) {
|
|
Status = GetLastError( );
|
|
goto Cleanup;
|
|
}
|
|
|
|
// give ownership to global
|
|
g_pClusterSecurityDescriptor = pNewSD;
|
|
pNewSD = NULL;
|
|
|
|
Cleanup:
|
|
if (ProcessTokenUser != NULL) {
|
|
LocalFree( ProcessTokenUser );
|
|
}
|
|
|
|
if (NewDacl != NULL) {
|
|
LocalFree( NewDacl );
|
|
}
|
|
|
|
if (ProcessToken != NULL) {
|
|
NtClose(ProcessToken);
|
|
}
|
|
|
|
// This should be NULL if successful.
|
|
if ( pNewSD != NULL ) {
|
|
LocalFree( pNewSD );
|
|
}
|
|
|
|
// If successful and the caller wanted to reference the SD, assign it now.
|
|
if ( Status == ERROR_SUCCESS && poutSD != NULL ) {
|
|
*poutSD = g_pClusterSecurityDescriptor;
|
|
}
|
|
|
|
if ( GlobalLocalSystemSid != NULL )
|
|
{
|
|
RtlFreeSid( GlobalLocalSystemSid );
|
|
}
|
|
|
|
if ( GlobalAliasAdminsSid != NULL )
|
|
{
|
|
RtlFreeSid( GlobalAliasAdminsSid );
|
|
}
|
|
|
|
if ( ! NT_SUCCESS( Status ) ) {
|
|
ClRtlLogPrint(LOG_NOISE, "[ClRtl] ClRtlBuildClusterServiceSecurityDescriptor exit. Status = 0x%1!.8x!\n", Status );
|
|
}
|
|
|
|
return (DWORD) Status; // hack it to a DWORD...
|
|
|
|
} // ClRtlBuildClusterServiceSecurityDescriptor
|
|
|
|
|
|
NTSTATUS
|
|
ClRtlImpersonateSelf(
|
|
IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
|
|
IN ACCESS_MASK AccessMask
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine may be used to obtain an Impersonation token representing
|
|
your own process's context. This may be useful for enabling a privilege
|
|
for a single thread rather than for the entire process; or changing
|
|
the default DACL for a single thread.
|
|
|
|
The token is assigned to the callers thread.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
ImpersonationLevel - The level to make the impersonation token.
|
|
|
|
AccessMask - Access control to the new token.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - The thread is now impersonating the calling process.
|
|
|
|
Other - Status values returned by:
|
|
|
|
NtOpenProcessToken()
|
|
NtDuplicateToken()
|
|
NtSetInformationThread()
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS
|
|
Status,
|
|
IgnoreStatus;
|
|
|
|
HANDLE
|
|
Token1,
|
|
Token2;
|
|
|
|
OBJECT_ATTRIBUTES
|
|
ObjectAttributes;
|
|
|
|
SECURITY_QUALITY_OF_SERVICE
|
|
Qos;
|
|
|
|
PSECURITY_DESCRIPTOR
|
|
pSecurityDescriptor = NULL;
|
|
|
|
Status = ClRtlBuildClusterServiceSecurityDescriptor( &pSecurityDescriptor );
|
|
if(NT_SUCCESS(Status))
|
|
{
|
|
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL);
|
|
|
|
Qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
|
|
Qos.ImpersonationLevel = ImpersonationLevel;
|
|
Qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
|
|
Qos.EffectiveOnly = FALSE;
|
|
ObjectAttributes.SecurityQualityOfService = &Qos;
|
|
ObjectAttributes.SecurityDescriptor = pSecurityDescriptor;
|
|
|
|
Status = NtOpenProcessToken( NtCurrentProcess(), TOKEN_DUPLICATE, &Token1 );
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = NtDuplicateToken(
|
|
Token1,
|
|
AccessMask,
|
|
&ObjectAttributes,
|
|
FALSE, //EffectiveOnly
|
|
TokenImpersonation,
|
|
&Token2
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = NtSetInformationThread(
|
|
NtCurrentThread(),
|
|
ThreadImpersonationToken,
|
|
&Token2,
|
|
sizeof(HANDLE)
|
|
);
|
|
|
|
IgnoreStatus = NtClose( Token2 );
|
|
}
|
|
|
|
IgnoreStatus = NtClose( Token1 );
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
|
|
} // ClRtlImpersonateSelf
|
|
|
|
|
|
DWORD
|
|
ClRtlEnableThreadPrivilege(
|
|
IN ULONG Privilege,
|
|
OUT BOOLEAN *pWasEnabled
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enables a privilege for the current thread.
|
|
|
|
Arguments:
|
|
|
|
Privilege - The privilege to be enabled.
|
|
|
|
pWasEnabled - Returns whether this privilege was originally
|
|
enabled or disabled. This should be passed into
|
|
ClRtlRestoreThreadPrivilege() for restoring the privileges of
|
|
the thread back.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
{
|
|
|
|
DWORD Status;
|
|
BOOL bImpersonate = FALSE;
|
|
|
|
//obtain a token that impersonates the security context
|
|
//of the calling process
|
|
Status = ClRtlImpersonateSelf( SecurityImpersonation, TOKEN_ALL_ACCESS );
|
|
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
CL_LOGFAILURE(Status);
|
|
goto FnExit;
|
|
}
|
|
|
|
bImpersonate = TRUE;
|
|
//
|
|
// Enable the required privilege
|
|
//
|
|
|
|
Status = RtlAdjustPrivilege(Privilege, TRUE, TRUE, pWasEnabled);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
CL_LOGFAILURE(Status);
|
|
goto FnExit;
|
|
|
|
}
|
|
|
|
FnExit:
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
if (bImpersonate)
|
|
{
|
|
//if this failed and if we
|
|
//
|
|
// terminate impersonation
|
|
//
|
|
HANDLE NullHandle;
|
|
|
|
|
|
NullHandle = NULL;
|
|
|
|
NtSetInformationThread(
|
|
NtCurrentThread(),
|
|
ThreadImpersonationToken,
|
|
(PVOID) &NullHandle,
|
|
sizeof( HANDLE ) );
|
|
}
|
|
}
|
|
return(Status);
|
|
} // ClRtlEnableThreadPrivilege
|
|
|
|
DWORD
|
|
ClRtlRestoreThreadPrivilege(
|
|
IN ULONG Privilege,
|
|
IN BOOLEAN WasEnabled
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Restores the privilege for the current thread.
|
|
|
|
Arguments:
|
|
|
|
Privilege - The privilege to be enabled.
|
|
|
|
WasEnabled - TRUE to restore this privilege to enabled state.
|
|
FALSE otherwise.
|
|
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Status = ERROR_SUCCESS;
|
|
HANDLE NullHandle;
|
|
DWORD ReturnStatus = ERROR_SUCCESS;
|
|
//
|
|
// If the privilege was originally disabled, disable it now.
|
|
// Else we dont have to do anything.
|
|
//
|
|
|
|
if (!WasEnabled)
|
|
{
|
|
ReturnStatus = RtlAdjustPrivilege(Privilege,
|
|
WasEnabled, TRUE, &WasEnabled);
|
|
if (!NT_SUCCESS(ReturnStatus)) {
|
|
CL_LOGFAILURE(ReturnStatus);
|
|
//we still need to terminate the impersonation
|
|
}
|
|
}
|
|
|
|
//
|
|
// terminate impersonation
|
|
//
|
|
|
|
NullHandle = NULL;
|
|
|
|
Status = NtSetInformationThread(
|
|
NtCurrentThread(),
|
|
ThreadImpersonationToken,
|
|
(PVOID) &NullHandle,
|
|
sizeof( HANDLE ) );
|
|
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
|
|
CL_LOGFAILURE(Status);
|
|
//Let the first error be reported
|
|
if (ReturnStatus != ERROR_SUCCESS)
|
|
ReturnStatus = Status;
|
|
goto FnExit;
|
|
|
|
}
|
|
|
|
FnExit:
|
|
return (ReturnStatus);
|
|
} // ClRtlRestoreThreadPrivilege
|
|
|
|
PSECURITY_DESCRIPTOR
|
|
ClRtlCopySecurityDescriptor(
|
|
IN PSECURITY_DESCRIPTOR psd
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copy an NT security descriptor. The security descriptor must
|
|
be in self-relative (not absolute) form. Delete the result using LocalFree()
|
|
|
|
Arguments:
|
|
|
|
psd - the SD to copy
|
|
|
|
Return Value:
|
|
|
|
NULL if an error occured or an invalid SD
|
|
Call GetLastError for more detailed information
|
|
|
|
--*/
|
|
|
|
{
|
|
PSECURITY_DESCRIPTOR pSelfSecDesc = NULL;
|
|
SECURITY_DESCRIPTOR_CONTROL sdc;
|
|
DWORD dwLen = 0;
|
|
DWORD cbSelfSecDesc = 0;
|
|
DWORD dwRevision = 0;
|
|
DWORD status = ERROR_SUCCESS;
|
|
|
|
if (NULL == psd) {
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// following call was embedded in an ASSERT. Having this check all the
|
|
// time is important since the rest of the ACL APIs depend on the
|
|
// structure being correct.
|
|
//
|
|
if ( !IsValidSecurityDescriptor( psd )) {
|
|
SetLastError( ERROR_INVALID_SECURITY_DESCR );
|
|
return NULL;
|
|
}
|
|
|
|
if ( ! GetSecurityDescriptorControl( psd, &sdc, &dwRevision ) ) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_NOISE, "[ClRtl] CopySecurityDescriptor: GetSecurityDescriptorControl() failed:%1!d1\n", GetLastError());
|
|
SetLastError( status );
|
|
return NULL; // actually, should probably return an error
|
|
}
|
|
|
|
dwLen = GetSecurityDescriptorLength(psd);
|
|
|
|
pSelfSecDesc = LocalAlloc( LMEM_ZEROINIT, dwLen );
|
|
|
|
if (pSelfSecDesc == NULL) {
|
|
ClRtlLogPrint(LOG_NOISE, "[ClRtl] CopySecurityDescriptor: LocalAlloc() SECURITY_DESCRIPTOR (2) failed\n");
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
return NULL; // actually, should probably return an error
|
|
}
|
|
|
|
cbSelfSecDesc = dwLen;
|
|
|
|
if (!MakeSelfRelativeSD(psd, pSelfSecDesc, &cbSelfSecDesc)) {
|
|
if ( ( sdc & SE_SELF_RELATIVE ) == 0 ) {
|
|
ClRtlLogPrint(LOG_NOISE, "[ClRtl] CopySecurityDescriptor: MakeSelfRelativeSD failed, 0x%1!.8x!\n", GetLastError());
|
|
} // if: only log this error if the old SD was not already self-relative
|
|
|
|
// assume it failed because it was already self-relative
|
|
CopyMemory(pSelfSecDesc, psd, dwLen);
|
|
}
|
|
|
|
ASSERT( IsValidSecurityDescriptor( pSelfSecDesc ));
|
|
|
|
return pSelfSecDesc;
|
|
|
|
} //*** ClRtlCopySecurityDescriptor()
|
|
|
|
static VOID
|
|
ClRtlGetSidTypeDesc(
|
|
SID_NAME_USE SidType,
|
|
LPSTR pszSidType,
|
|
size_t cchSidType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert the SidType into a meaningful string.
|
|
|
|
Arguments:
|
|
|
|
SidType - numerical value of SID type
|
|
|
|
pszSidType - pointer to buffer to receive string desc.
|
|
|
|
cchSidType - size, in chars, of pszSidType
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
PCHAR pszSidDesc;
|
|
|
|
if ((pszSidType != NULL) && (cchSidType > 0))
|
|
{
|
|
switch (SidType)
|
|
{
|
|
case SidTypeUser:
|
|
pszSidDesc = "has a user SID for";
|
|
break;
|
|
|
|
case SidTypeGroup:
|
|
pszSidDesc = "has a group SID for";
|
|
break;
|
|
|
|
case SidTypeDomain:
|
|
pszSidDesc = "has a domain SID for";
|
|
break;
|
|
|
|
case SidTypeAlias:
|
|
pszSidDesc = "has an alias SID for";
|
|
break;
|
|
|
|
case SidTypeWellKnownGroup:
|
|
pszSidDesc = "has an SID for a well-known group for";
|
|
break;
|
|
|
|
case SidTypeDeletedAccount:
|
|
pszSidDesc = "has an SID for a deleted account for";
|
|
break;
|
|
|
|
case SidTypeInvalid:
|
|
pszSidDesc = "has an invalid SID for";
|
|
break;
|
|
|
|
case SidTypeUnknown:
|
|
default:
|
|
pszSidDesc = "has an unknown SID type:";
|
|
break;
|
|
|
|
} // switch: SidType
|
|
|
|
pszSidType [ cchSidType - 1 ] = 0;
|
|
strncpy(pszSidType, pszSidDesc, cchSidType - 1);
|
|
|
|
} // if: buffer not null and has space allocated
|
|
|
|
} //*** ClRtlGetSidTypeDesc()
|
|
|
|
static VOID
|
|
ClRtlExamineSid(
|
|
PSID pSid,
|
|
LPSTR lpszOldIndent
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump the SID.
|
|
|
|
Arguments:
|
|
|
|
pSid -
|
|
lpzOldIndent -
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
CHAR szUserName [128];
|
|
CHAR szDomainName [ DNS_MAX_NAME_BUFFER_LENGTH ];
|
|
DWORD cbUser = sizeof(szUserName);
|
|
DWORD cbDomain = sizeof(szDomainName);
|
|
SID_NAME_USE SidType;
|
|
|
|
if ( LookupAccountSidA( NULL, pSid, szUserName, &cbUser, szDomainName, &cbDomain, &SidType ) )
|
|
{
|
|
char szSidType [128];
|
|
|
|
ClRtlGetSidTypeDesc( SidType, szSidType, sizeof( szSidType ) );
|
|
ClRtlLogPrint( LOG_NOISE, "%1!hs!%2!hs! %3!hs!\\%4!hs!\n", lpszOldIndent, szSidType, szDomainName, szUserName ) ;
|
|
}
|
|
|
|
} // *** ClRtlExamineSid()
|
|
|
|
VOID
|
|
ClRtlExamineMask(
|
|
ACCESS_MASK amMask,
|
|
LPSTR lpszOldIndent
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump the AccessMask context.
|
|
|
|
Arguments:
|
|
|
|
amMask -
|
|
lpzOldIndent -
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
#define STANDARD_RIGHTS_ALL_THE_BITS 0x00FF0000L
|
|
#define GENERIC_RIGHTS_ALL_THE_BITS 0xF0000000L
|
|
|
|
CHAR szIndent[100];
|
|
CHAR ucIndentBitsBuf[100];
|
|
DWORD dwGenericBits;
|
|
DWORD dwStandardBits;
|
|
DWORD dwSpecificBits;
|
|
DWORD dwAccessSystemSecurityBit;
|
|
DWORD dwExtraBits;
|
|
|
|
szIndent[ sizeof( szIndent ) - 1 ] = 0;
|
|
_snprintf( szIndent, sizeof( szIndent ) - 1, "%s ", lpszOldIndent );
|
|
|
|
ucIndentBitsBuf[ sizeof( ucIndentBitsBuf ) - 1 ] = 0;
|
|
_snprintf( ucIndentBitsBuf, sizeof( ucIndentBitsBuf ) - 1,
|
|
"%s ",
|
|
szIndent);
|
|
|
|
dwStandardBits = (amMask & STANDARD_RIGHTS_ALL_THE_BITS);
|
|
dwSpecificBits = (amMask & SPECIFIC_RIGHTS_ALL );
|
|
dwAccessSystemSecurityBit = (amMask & ACCESS_SYSTEM_SECURITY );
|
|
dwGenericBits = (amMask & GENERIC_RIGHTS_ALL_THE_BITS );
|
|
|
|
// **************************************************************************
|
|
// *
|
|
// * Print then decode the standard rights bits
|
|
// *
|
|
// **************************************************************************
|
|
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs! Standard Rights == 0x%2!.8x!\n", szIndent, dwStandardBits);
|
|
|
|
if (dwStandardBits) {
|
|
|
|
if ((dwStandardBits & DELETE) == DELETE) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!0x%2!.8x! DELETE\n", ucIndentBitsBuf, DELETE);
|
|
}
|
|
|
|
if ((dwStandardBits & READ_CONTROL) == READ_CONTROL) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!0x%2!.8x! READ_CONTROL\n", ucIndentBitsBuf, READ_CONTROL);
|
|
}
|
|
|
|
if ((dwStandardBits & STANDARD_RIGHTS_READ) == STANDARD_RIGHTS_READ) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!0x%2!.8x! STANDARD_RIGHTS_READ\n", ucIndentBitsBuf, STANDARD_RIGHTS_READ);
|
|
}
|
|
|
|
if ((dwStandardBits & STANDARD_RIGHTS_WRITE) == STANDARD_RIGHTS_WRITE) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!0x%2!.8x! STANDARD_RIGHTS_WRITE\n", ucIndentBitsBuf, STANDARD_RIGHTS_WRITE);
|
|
}
|
|
|
|
if ((dwStandardBits & STANDARD_RIGHTS_EXECUTE) == STANDARD_RIGHTS_EXECUTE) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!0x%2!.8x! STANDARD_RIGHTS_EXECUTE\n", ucIndentBitsBuf, STANDARD_RIGHTS_EXECUTE);
|
|
}
|
|
|
|
if ((dwStandardBits & WRITE_DAC) == WRITE_DAC) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!0x%2!.8x! WRITE_DAC\n", ucIndentBitsBuf, WRITE_DAC);
|
|
}
|
|
|
|
if ((dwStandardBits & WRITE_OWNER) == WRITE_OWNER) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!0x%2!.8x! WRITE_OWNER\n", ucIndentBitsBuf, WRITE_OWNER);
|
|
}
|
|
|
|
if ((dwStandardBits & SYNCHRONIZE) == SYNCHRONIZE) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!0x%2!.8x! SYNCHRONIZE\n", ucIndentBitsBuf, SYNCHRONIZE);
|
|
}
|
|
|
|
if ((dwStandardBits & STANDARD_RIGHTS_REQUIRED) == STANDARD_RIGHTS_REQUIRED) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!0x%2!.8x! STANDARD_RIGHTS_REQUIRED\n", ucIndentBitsBuf, STANDARD_RIGHTS_REQUIRED);
|
|
}
|
|
|
|
if ((dwStandardBits & STANDARD_RIGHTS_ALL) == STANDARD_RIGHTS_ALL) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!0x%2!.8x! STANDARD_RIGHTS_ALL\n", ucIndentBitsBuf, STANDARD_RIGHTS_ALL);
|
|
}
|
|
|
|
dwExtraBits = dwStandardBits & (~(DELETE
|
|
| READ_CONTROL
|
|
| STANDARD_RIGHTS_READ
|
|
| STANDARD_RIGHTS_WRITE
|
|
| STANDARD_RIGHTS_EXECUTE
|
|
| WRITE_DAC
|
|
| WRITE_OWNER
|
|
| SYNCHRONIZE
|
|
| STANDARD_RIGHTS_REQUIRED
|
|
| STANDARD_RIGHTS_ALL));
|
|
if (dwExtraBits) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs! Extra standard bits == 0x%2!.8x! <-This is a problem, should be all 0s\n", szIndent, dwExtraBits);
|
|
}
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs! Specific Rights == 0x%2!.8x!\n", szIndent, dwSpecificBits);
|
|
|
|
// **************************************************************************
|
|
// *
|
|
// * Print then decode the ACCESS_SYSTEM_SECURITY bit
|
|
// *
|
|
// *************************************************************************
|
|
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs! Access System Security == 0x%2!.8x!\n", szIndent, dwAccessSystemSecurityBit);
|
|
|
|
// **************************************************************************
|
|
// *
|
|
// * Print then decode the generic rights bits, which will rarely be on
|
|
// *
|
|
// * Generic bits are nearly always mapped by Windows NT before it tries to do
|
|
// * anything with them. You can ignore the fact that generic bits are
|
|
// * special in any way, although it helps to keep track of what the mappings
|
|
// * are so that you don't have any surprises
|
|
// *
|
|
// * The only time the generic bits are not mapped immediately is if they are
|
|
// * placed in an inheritable ACE in an ACL, or in an ACL that will be
|
|
// * assigned by default (such as the default DACL in an access token). In
|
|
// * that case they're mapped when the child object is created (or when the
|
|
// * default DACL is used at object creation time)
|
|
// *
|
|
// **************************************************************************
|
|
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs! Generic Rights == 0x%2!.8x!\n", szIndent, dwGenericBits);
|
|
|
|
if (dwGenericBits) {
|
|
|
|
if ((dwGenericBits & GENERIC_READ) == GENERIC_READ) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!0x%2!.8x! GENERIC_READ\n", ucIndentBitsBuf, GENERIC_READ);
|
|
}
|
|
|
|
if ((dwGenericBits & GENERIC_WRITE) == GENERIC_WRITE) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!0x%2!.8x! GENERIC_WRITE\n", ucIndentBitsBuf, GENERIC_WRITE);
|
|
}
|
|
|
|
if ((dwGenericBits & GENERIC_EXECUTE) == GENERIC_EXECUTE) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!0x%2!.8x! GENERIC_EXECUTE\n", ucIndentBitsBuf, GENERIC_EXECUTE);
|
|
}
|
|
|
|
if ((dwGenericBits & GENERIC_ALL) == GENERIC_ALL) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!0x%2!.8x! GENERIC_ALL\n", ucIndentBitsBuf, GENERIC_ALL);
|
|
}
|
|
|
|
dwExtraBits = dwGenericBits & (~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL));
|
|
if (dwExtraBits) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs! Extra generic bits == 0x%2!.8x! <-This is a problem, should be all 0s\n", szIndent, dwExtraBits);
|
|
}
|
|
}
|
|
|
|
} // *** ClRtlExamineMask()
|
|
|
|
static BOOL
|
|
ClRtlExamineACL(
|
|
PACL paclACL,
|
|
LPSTR lpszOldIndent
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump the Access Control List contents.
|
|
|
|
Arguments:
|
|
|
|
paclACL - pointer to ACL
|
|
|
|
lpzOldIndent - pointer to indent string.
|
|
|
|
Return Value:
|
|
|
|
TRUE - ?
|
|
|
|
--*/
|
|
|
|
{
|
|
CHAR szIndent[ 64 ];
|
|
PCHAR pIndentBuffer;
|
|
CHAR spacesToIndent[] = " ";
|
|
DWORD indentChars;
|
|
|
|
ACL_SIZE_INFORMATION asiAclSize;
|
|
ACL_REVISION_INFORMATION ariAclRevision;
|
|
DWORD dwBufLength;
|
|
DWORD dwAcl_i;
|
|
ACCESS_ALLOWED_ACE * paaAllowedAce;
|
|
BOOL returnValue = TRUE;
|
|
|
|
//
|
|
// get length of current indent, add our indent, and either use the
|
|
// builtin buffer or allocate one that is large enough. If we fail to
|
|
// allocate, set it to the passed in indent buffer.
|
|
//
|
|
indentChars = strlen( lpszOldIndent ) + RTL_NUMBER_OF( spacesToIndent );
|
|
if ( indentChars > sizeof( szIndent )) {
|
|
pIndentBuffer = LocalAlloc( LMEM_FIXED, indentChars );
|
|
if ( pIndentBuffer == NULL ) {
|
|
pIndentBuffer = lpszOldIndent;
|
|
}
|
|
} else {
|
|
pIndentBuffer = szIndent;
|
|
indentChars = sizeof( szIndent );
|
|
}
|
|
|
|
if ( pIndentBuffer != lpszOldIndent ) {
|
|
strncpy( pIndentBuffer, lpszOldIndent, indentChars - RTL_NUMBER_OF( spacesToIndent ));
|
|
pIndentBuffer[ indentChars - RTL_NUMBER_OF( spacesToIndent ) ] = 0;
|
|
strcat( pIndentBuffer, spacesToIndent );
|
|
}
|
|
|
|
if (!IsValidAcl(paclACL)) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ClRtlExamineACL() - IsValidAcl() failed.\n", pIndentBuffer);
|
|
returnValue = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwBufLength = sizeof(asiAclSize);
|
|
|
|
if (!GetAclInformation(paclACL, &asiAclSize, dwBufLength, AclSizeInformation)) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ClRtlExamineACL() - GetAclInformation failed.\n", pIndentBuffer);
|
|
returnValue = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwBufLength = sizeof(ariAclRevision);
|
|
|
|
if (!GetAclInformation(paclACL, (LPVOID) &ariAclRevision, dwBufLength, AclRevisionInformation)) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ClRtlExamineACL() - GetAclInformation failed\n", pIndentBuffer);
|
|
returnValue = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ACL has %2!d! ACE(s), %3!d! bytes used, %4!d! bytes free\n",
|
|
pIndentBuffer,
|
|
asiAclSize.AceCount,
|
|
asiAclSize.AclBytesInUse,
|
|
asiAclSize.AclBytesFree);
|
|
|
|
switch (ariAclRevision.AclRevision) {
|
|
case ACL_REVISION1:
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ACL revision is %2!d! == ACL_REVISION1\n", pIndentBuffer, ariAclRevision.AclRevision);
|
|
break;
|
|
case ACL_REVISION2:
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ACL revision is %2!d! == ACL_REVISION2\n", pIndentBuffer, ariAclRevision.AclRevision);
|
|
break;
|
|
default:
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ACL revision is %2!d! == ACL Revision is an IMPOSSIBLE ACL revision!!! Perhaps a new revision was added...\n",
|
|
pIndentBuffer,
|
|
ariAclRevision.AclRevision);
|
|
returnValue = FALSE;
|
|
break;
|
|
}
|
|
|
|
if ( returnValue == FALSE ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
for (dwAcl_i = 0; dwAcl_i < asiAclSize.AceCount; dwAcl_i++) {
|
|
|
|
if (!GetAce(paclACL, dwAcl_i, (LPVOID *) &paaAllowedAce)) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ClRtlExamineACL() - GetAce failed.\n", pIndentBuffer);
|
|
returnValue = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ACE %2!d! size %3!d!\n", pIndentBuffer, dwAcl_i, paaAllowedAce->Header.AceSize);
|
|
|
|
{
|
|
char szBuf [512];
|
|
|
|
szBuf[ RTL_NUMBER_OF( szBuf ) - 1 ] = 0;
|
|
_snprintf(szBuf, RTL_NUMBER_OF( szBuf ) - 1, "%sACE %d ", pIndentBuffer, dwAcl_i);
|
|
ClRtlExamineSid(&(paaAllowedAce->SidStart), szBuf );
|
|
}
|
|
|
|
{
|
|
DWORD dwAceFlags = paaAllowedAce->Header.AceFlags;
|
|
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ACE %2!d! flags 0x%3!.2x!\n", pIndentBuffer, dwAcl_i, dwAceFlags);
|
|
|
|
if (dwAceFlags) {
|
|
|
|
DWORD dwExtraBits;
|
|
|
|
#define ACE_INDENT " "
|
|
|
|
if ((dwAceFlags & OBJECT_INHERIT_ACE) == OBJECT_INHERIT_ACE) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!" ACE_INDENT "0x01 OBJECT_INHERIT_ACE\n", pIndentBuffer);
|
|
}
|
|
|
|
if ((dwAceFlags & CONTAINER_INHERIT_ACE) == CONTAINER_INHERIT_ACE) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!" ACE_INDENT "0x02 CONTAINER_INHERIT_ACE\n", pIndentBuffer);
|
|
}
|
|
|
|
if ((dwAceFlags & NO_PROPAGATE_INHERIT_ACE) == NO_PROPAGATE_INHERIT_ACE) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!" ACE_INDENT "0x04 NO_PROPAGATE_INHERIT_ACE\n", pIndentBuffer);
|
|
}
|
|
|
|
if ((dwAceFlags & INHERIT_ONLY_ACE) == INHERIT_ONLY_ACE) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!" ACE_INDENT "0x08 INHERIT_ONLY_ACE\n", pIndentBuffer);
|
|
}
|
|
|
|
if ((dwAceFlags & VALID_INHERIT_FLAGS) == VALID_INHERIT_FLAGS) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!" ACE_INDENT "0x0F VALID_INHERIT_FLAGS\n", pIndentBuffer);
|
|
}
|
|
|
|
if ((dwAceFlags & SUCCESSFUL_ACCESS_ACE_FLAG) == SUCCESSFUL_ACCESS_ACE_FLAG) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!" ACE_INDENT "0x40 SUCCESSFUL_ACCESS_ACE_FLAG\n", pIndentBuffer);
|
|
}
|
|
|
|
if ((dwAceFlags & FAILED_ACCESS_ACE_FLAG) == FAILED_ACCESS_ACE_FLAG) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!" ACE_INDENT "0x80 FAILED_ACCESS_ACE_FLAG\n", pIndentBuffer);
|
|
}
|
|
|
|
dwExtraBits = dwAceFlags & (~(OBJECT_INHERIT_ACE
|
|
| CONTAINER_INHERIT_ACE
|
|
| NO_PROPAGATE_INHERIT_ACE
|
|
| INHERIT_ONLY_ACE
|
|
| VALID_INHERIT_FLAGS
|
|
| SUCCESSFUL_ACCESS_ACE_FLAG
|
|
| FAILED_ACCESS_ACE_FLAG));
|
|
if (dwExtraBits) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!" ACE_INDENT " Extra AceFlag bits == 0x%2!.8x! <-This is a problem, should be all 0s\n",
|
|
pIndentBuffer,
|
|
dwExtraBits);
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (paaAllowedAce->Header.AceType) {
|
|
case ACCESS_ALLOWED_ACE_TYPE:
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ACE %2!d! is an ACCESS_ALLOWED_ACE_TYPE\n", pIndentBuffer, dwAcl_i);
|
|
break;
|
|
case ACCESS_DENIED_ACE_TYPE:
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ACE %2!d! is an ACCESS_DENIED_ACE_TYPE\n", pIndentBuffer, dwAcl_i);
|
|
break;
|
|
case SYSTEM_AUDIT_ACE_TYPE:
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ACE %2!d! is a SYSTEM_AUDIT_ACE_TYPE\n", pIndentBuffer, dwAcl_i);
|
|
break;
|
|
case SYSTEM_ALARM_ACE_TYPE:
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ACE %2!d! is a SYSTEM_ALARM_ACE_TYPE\n", pIndentBuffer, dwAcl_i);
|
|
break;
|
|
default :
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ACE %2!d! is an IMPOSSIBLE ACE_TYPE!!! Run debugger, examine value!\n", pIndentBuffer, dwAcl_i);
|
|
returnValue = FALSE;
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ACE %2!d! mask == 0x%3!.8x!\n", pIndentBuffer, dwAcl_i, paaAllowedAce->Mask);
|
|
|
|
ClRtlExamineMask(paaAllowedAce->Mask, pIndentBuffer);
|
|
}
|
|
|
|
Cleanup:
|
|
if ( pIndentBuffer != lpszOldIndent && pIndentBuffer != szIndent ) {
|
|
LocalFree( pIndentBuffer );
|
|
}
|
|
|
|
return returnValue;
|
|
|
|
} // *** ClRtlExamineACL()
|
|
|
|
BOOL
|
|
ClRtlExamineSD(
|
|
PSECURITY_DESCRIPTOR psdSD,
|
|
LPSTR pszPrefix
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump the Security descriptor context.
|
|
|
|
Arguments:
|
|
|
|
psdSD - the SD to dump
|
|
|
|
Return Value:
|
|
|
|
BOOL, TRUE for success, FALSE for failure
|
|
|
|
--*/
|
|
|
|
{
|
|
PACL paclDACL;
|
|
PACL paclSACL;
|
|
BOOL bHasDACL = FALSE;
|
|
BOOL bHasSACL = FALSE;
|
|
BOOL bDaclDefaulted = FALSE;
|
|
BOOL bSaclDefaulted = FALSE;
|
|
BOOL bOwnerDefaulted = FALSE;
|
|
BOOL bGroupDefaulted = FALSE;
|
|
PSID psidOwner;
|
|
PSID psidGroup;
|
|
SECURITY_DESCRIPTOR_CONTROL sdcSDControl;
|
|
DWORD dwSDRevision;
|
|
DWORD dwSDLength;
|
|
char szIndent [34];
|
|
|
|
//
|
|
// copy and possibly truncate the prefix. Leave space for the tacked on
|
|
// space and trailing NULL
|
|
//
|
|
strncpy(szIndent, pszPrefix, sizeof(szIndent) - 2);
|
|
szIndent[ sizeof( szIndent ) - 2 ] = 0;
|
|
strcat(szIndent, " ");
|
|
|
|
if (!IsValidSecurityDescriptor(psdSD)) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ClRtlExamineSD() - IsValidSecurityDescriptor failed.\n", szIndent);
|
|
return FALSE;
|
|
}
|
|
|
|
dwSDLength = GetSecurityDescriptorLength(psdSD);
|
|
|
|
if (!GetSecurityDescriptorDacl(psdSD, (LPBOOL) &bHasDACL, (PACL *) &paclDACL, (LPBOOL) &bDaclDefaulted)) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ClRtlExamineSD() - GetSecurityDescriptorDacl failed.\n", szIndent);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!GetSecurityDescriptorSacl(psdSD, (LPBOOL) &bHasSACL, (PACL *) &paclSACL, (LPBOOL) &bSaclDefaulted)) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ClRtlExamineSD() - GetSecurityDescriptorSacl failed.\n", szIndent);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!GetSecurityDescriptorOwner(psdSD, (PSID *)&psidOwner, (LPBOOL)&bOwnerDefaulted)) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ClRtlExamineSD() - GetSecurityDescriptorOwner failed.\n", szIndent);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!GetSecurityDescriptorGroup(psdSD, (PSID *) &psidGroup, (LPBOOL) &bGroupDefaulted)) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ClRtlExamineSD() - GetSecurityDescriptorGroup failed.\n", szIndent);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!GetSecurityDescriptorControl(
|
|
psdSD,
|
|
(PSECURITY_DESCRIPTOR_CONTROL) &sdcSDControl,
|
|
(LPDWORD) &dwSDRevision)) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ClRtlExamineSD() - GetSecurityDescriptorControl failed.\n", szIndent);
|
|
return FALSE;
|
|
}
|
|
|
|
switch (dwSDRevision) {
|
|
case SECURITY_DESCRIPTOR_REVISION1:
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!SD is valid. SD is %2!d! bytes long. SD revision is %3!d! == SECURITY_DESCRIPTOR_REVISION1\n", szIndent, dwSDLength, dwSDRevision);
|
|
break;
|
|
default :
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!SD is valid. SD is %2!d! bytes long. SD revision is %3!d! == ! SD Revision is an IMPOSSIBLE SD revision!!! Perhaps a new revision was added...\n",
|
|
szIndent,
|
|
dwSDLength,
|
|
dwSDRevision);
|
|
return FALSE;
|
|
}
|
|
|
|
if (SE_SELF_RELATIVE & sdcSDControl) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!SD is in self-relative format (all SDs returned by the system are)\n", szIndent);
|
|
}
|
|
|
|
if (NULL == psidOwner) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!SD's Owner is NULL, so SE_OWNER_DEFAULTED is ignored\n", szIndent);
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!SD's Owner is Not NULL\n", szIndent);
|
|
if (bOwnerDefaulted) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!SD's Owner-Defaulted flag is TRUE\n", szIndent);
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!SD's Owner-Defaulted flag is FALSE\n", szIndent);
|
|
}
|
|
}
|
|
|
|
// **************************************************************************
|
|
// *
|
|
// * The other use for psidGroup is for Macintosh client support
|
|
// *
|
|
// **************************************************************************
|
|
|
|
if (NULL == psidGroup) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!SD's Group is NULL, so SE_GROUP_DEFAULTED is ignored. SD's Group being NULL is typical, GROUP in SD(s) is mainly for POSIX compliance\n", szIndent);
|
|
}
|
|
else {
|
|
if (bGroupDefaulted) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!SD's Group-Defaulted flag is TRUE\n", szIndent);
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!SD's Group-Defaulted flag is FALSE\n", szIndent);
|
|
}
|
|
}
|
|
|
|
if (SE_DACL_PRESENT & sdcSDControl) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!SD's DACL is Present\n", szIndent);
|
|
if (bDaclDefaulted) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!SD's DACL-Defaulted flag is TRUE\n", szIndent);
|
|
} else {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!SD's DACL-Defaulted flag is FALSE\n", szIndent);
|
|
}
|
|
|
|
if (NULL == paclDACL) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!SD has a NULL DACL explicitly specified (allows all access to Everyone). This does not apply to this SD, but for comparison, a non-NULL DACL pointer to a 0-length ACL allows no access to anyone\n", szIndent);
|
|
}
|
|
else if(!ClRtlExamineACL(paclDACL, szIndent)) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ClRtlExamineSD() - ClRtlExamineACL failed.\n", szIndent);
|
|
}
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!SD's DACL is Not Present, so SE_DACL_DEFAULTED is ignored. SD has no DACL at all (allows all access to Everyone)\n", szIndent);
|
|
}
|
|
|
|
if (SE_SACL_PRESENT & sdcSDControl) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!SD's SACL is Present\n", szIndent);
|
|
if (bSaclDefaulted) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!SD's SACL-Defaulted flag is TRUE\n", szIndent);
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!SD's SACL-Defaulted flag is FALSE\n", szIndent);
|
|
}
|
|
|
|
if (NULL == paclSACL) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!SD has a NULL SACL explicitly specified\n", szIndent);
|
|
}
|
|
else if (!ClRtlExamineACL(paclSACL, szIndent)) {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!ClRtlExamineSD() - ClRtlExamineACL failed.\n", szIndent);
|
|
}
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!SD's SACL is Not Present, so SE_SACL_DEFAULTED is ignored. SD has no SACL at all (or we did not request to see it)\n", szIndent);
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
} // *** ClRtlExamineSD()
|
|
|
|
DWORD
|
|
ClRtlBuildDefaultClusterSD(
|
|
IN PSID pOwnerSid,
|
|
PSECURITY_DESCRIPTOR * SD,
|
|
ULONG * SizeSD
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Builds the default security descriptor to control access to
|
|
the cluster API
|
|
|
|
Modified permissions in ACEs in order to augment cluster security
|
|
administration.
|
|
|
|
Arguments:
|
|
|
|
pOwnerSid - Supplies the SID that the cluster account runs in
|
|
|
|
SD - Returns a pointer to the created security descriptor. This
|
|
should be freed by the caller.
|
|
|
|
SizeSD - Returns the size in bytes of the security descriptor
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Status;
|
|
HANDLE Token;
|
|
PACL pAcl = NULL;
|
|
DWORD cbDaclSize;
|
|
PSECURITY_DESCRIPTOR psd;
|
|
PSECURITY_DESCRIPTOR NewSD;
|
|
BYTE SDBuffer[SECURITY_DESCRIPTOR_MIN_LENGTH];
|
|
PACCESS_ALLOWED_ACE pAce;
|
|
PSID pAdminSid = NULL;
|
|
PSID pSystemSid = NULL;
|
|
PSID pNetServiceSid = NULL;
|
|
PULONG pSubAuthority;
|
|
SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
|
|
ULONG NewSDLen;
|
|
|
|
psd = (PSECURITY_DESCRIPTOR) SDBuffer;
|
|
|
|
//
|
|
// allocate and init the Administrators group sid
|
|
//
|
|
if ( !AllocateAndInitializeSid( &siaNtAuthority,
|
|
2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS,
|
|
0, 0, 0, 0, 0, 0,
|
|
&pAdminSid ) ) {
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// allocate and init the SYSTEM sid
|
|
//
|
|
if ( !AllocateAndInitializeSid( &siaNtAuthority,
|
|
1,
|
|
SECURITY_LOCAL_SYSTEM_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&pSystemSid ) ) {
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
|
|
if ( pOwnerSid == NULL ) {
|
|
pOwnerSid = pAdminSid;
|
|
}
|
|
|
|
//
|
|
// allocate and init the Network Service sid
|
|
//
|
|
if ( !AllocateAndInitializeSid( &siaNtAuthority,
|
|
1,
|
|
SECURITY_NETWORK_SERVICE_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&pNetServiceSid ) ) {
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Set up the DACL that will allow admins all access.
|
|
// It should be large enough to hold 3 ACEs and their SIDs
|
|
//
|
|
cbDaclSize = sizeof( ACL ) +
|
|
3 * ( sizeof( ACCESS_ALLOWED_ACE ) - sizeof( pAce->SidStart )) +
|
|
GetLengthSid( pAdminSid ) +
|
|
GetLengthSid( pSystemSid ) +
|
|
GetLengthSid( pNetServiceSid );
|
|
|
|
pAcl = (PACL) LocalAlloc( LPTR, cbDaclSize );
|
|
if ( pAcl == NULL ) {
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto error_exit;
|
|
}
|
|
|
|
InitializeSecurityDescriptor( psd, SECURITY_DESCRIPTOR_REVISION );
|
|
InitializeAcl( pAcl, cbDaclSize, ACL_REVISION );
|
|
|
|
//
|
|
// Add the ACE for the local Administrators group to the DACL
|
|
//
|
|
if ( !AddAccessAllowedAce( pAcl,
|
|
ACL_REVISION,
|
|
CLUSAPI_ALL_ACCESS, // What the admin can do
|
|
pAdminSid ) ) {
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Add the ACE for the SYSTEM account to the DACL
|
|
//
|
|
if ( !AddAccessAllowedAce( pAcl,
|
|
ACL_REVISION,
|
|
CLUSAPI_ALL_ACCESS, // What local system can do
|
|
pSystemSid ) ) {
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Add the ACE for the Network Service account to the DACL
|
|
//
|
|
if ( !AddAccessAllowedAce( pAcl,
|
|
ACL_REVISION,
|
|
CLUSAPI_ALL_ACCESS, // What net services can do
|
|
pNetServiceSid ) ) {
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
|
|
if ( !GetAce( pAcl, 0, (PVOID *) &pAce ) ) {
|
|
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
|
|
pAce->Header.AceFlags |= CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
|
|
|
|
if ( !GetAce( pAcl, 1, (PVOID *) &pAce ) ) {
|
|
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
|
|
pAce->Header.AceFlags |= CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
|
|
|
|
if ( !GetAce( pAcl, 2, (PVOID *) &pAce ) ) {
|
|
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
|
|
pAce->Header.AceFlags |= CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
|
|
|
|
if ( !SetSecurityDescriptorDacl( psd, TRUE, pAcl, FALSE ) ) {
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
|
|
if ( !SetSecurityDescriptorOwner( psd, pOwnerSid, FALSE ) ) {
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
|
|
if ( !SetSecurityDescriptorGroup( psd, pOwnerSid, FALSE ) ) {
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
|
|
if ( !SetSecurityDescriptorSacl( psd, TRUE, NULL, FALSE ) ) {
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
|
|
NewSDLen = 0 ;
|
|
|
|
if ( !MakeSelfRelativeSD( psd, NULL, &NewSDLen ) ) {
|
|
Status = GetLastError();
|
|
if ( Status != ERROR_INSUFFICIENT_BUFFER ) { // Duh, we're trying to find out how big the buffer should be?
|
|
goto error_exit;
|
|
}
|
|
}
|
|
|
|
NewSD = LocalAlloc( LPTR, NewSDLen );
|
|
if ( NewSD ) {
|
|
if ( !MakeSelfRelativeSD( psd, NewSD, &NewSDLen ) ) {
|
|
Status = GetLastError();
|
|
LocalFree( NewSD );
|
|
goto error_exit;
|
|
}
|
|
|
|
Status = ERROR_SUCCESS;
|
|
*SD = NewSD;
|
|
*SizeSD = NewSDLen;
|
|
} else {
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
error_exit:
|
|
if ( pAdminSid != NULL ) {
|
|
FreeSid( pAdminSid );
|
|
}
|
|
|
|
if ( pSystemSid != NULL ) {
|
|
FreeSid( pSystemSid );
|
|
}
|
|
|
|
if ( pNetServiceSid != NULL ) {
|
|
FreeSid( pNetServiceSid );
|
|
}
|
|
|
|
if ( pAcl != NULL ) {
|
|
LocalFree( pAcl );
|
|
}
|
|
|
|
return( Status );
|
|
|
|
} // *** ClRtlBuildDefaultClusterSD()
|
|
|
|
|
|
static BOOL
|
|
ClRtlGetTokenInformation(
|
|
HANDLE hClientToken,
|
|
TOKEN_INFORMATION_CLASS ticRequest,
|
|
PBYTE * ppb,
|
|
LPSTR pszPrefix
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the requested information from the passed in client token.
|
|
|
|
Arguments:
|
|
|
|
hClientToken - the client token to dump
|
|
|
|
Return Value:
|
|
|
|
BOOL, TRUE for success, FALSE for failure
|
|
|
|
--*/
|
|
|
|
{
|
|
PBYTE _pb = NULL;
|
|
DWORD _cb = 64;
|
|
DWORD _cbNeeded = 0;
|
|
DWORD _sc = NO_ERROR;
|
|
|
|
//
|
|
// Get the user information from the client token.
|
|
//
|
|
do {
|
|
_pb = LocalAlloc( LMEM_ZEROINIT, _cb );
|
|
if ( _pb == NULL ) {
|
|
_sc = GetLastError();
|
|
ClRtlLogPrint( LOG_NOISE, "%1!hs!ClRtlGetTokenInformation() - LocalAlloc() failed:%2!d!\n", pszPrefix, _sc ) ;
|
|
return FALSE;
|
|
} // if: LocalAlloc failed
|
|
|
|
if ( ! GetTokenInformation( hClientToken, ticRequest, _pb, _cb, &_cbNeeded ) ) {
|
|
_sc = GetLastError();
|
|
LocalFree( _pb );
|
|
_pb = NULL;
|
|
if ( _sc == ERROR_INSUFFICIENT_BUFFER ) {
|
|
_cb = _cbNeeded;
|
|
continue;
|
|
} // if: buffer size is too small
|
|
else {
|
|
ClRtlLogPrint( LOG_NOISE, "%1!hs!ClRtlGetTokenInformation() - GetTokenInformation() failed:%2!d!\n", pszPrefix, _sc ) ;
|
|
return FALSE;
|
|
} // else: fatal error
|
|
} // if: GetTokenInformation failed
|
|
|
|
break; // everything is ok and we can exit the loop normally
|
|
|
|
} while( TRUE );
|
|
|
|
*ppb = _pb;
|
|
|
|
return TRUE;
|
|
|
|
} // *** ClRtlGetTokenInformation()
|
|
|
|
BOOL
|
|
ClRtlExamineClientToken(
|
|
HANDLE hClientToken,
|
|
LPSTR pszPrefix
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump the client token.
|
|
|
|
Arguments:
|
|
|
|
hClientToken - the client token to dump
|
|
|
|
Return Value:
|
|
|
|
BOOL, TRUE for success, FALSE for failure
|
|
|
|
--*/
|
|
|
|
{
|
|
char _szIndent [33];
|
|
char _szBuf [128];
|
|
PBYTE _pb = NULL;
|
|
|
|
//
|
|
// allow space for the blank and trailing null
|
|
//
|
|
_szIndent[ RTL_NUMBER_OF( _szIndent ) - 2 ] = 0;
|
|
strncpy( _szIndent, pszPrefix, RTL_NUMBER_OF( _szIndent ) - 2 );
|
|
strcat( _szIndent, " " );
|
|
|
|
//
|
|
// Get the user information from the client token.
|
|
//
|
|
if ( ClRtlGetTokenInformation( hClientToken, TokenUser, &_pb, _szIndent ) ) {
|
|
PTOKEN_USER _ptu = NULL;
|
|
|
|
wsprintfA( _szBuf, "%sClientToken ", _szIndent );
|
|
|
|
_ptu = (PTOKEN_USER) _pb;
|
|
ClRtlExamineSid( _ptu->User.Sid, _szBuf );
|
|
|
|
LocalFree( _pb );
|
|
_pb = NULL;
|
|
}
|
|
|
|
//
|
|
// Get the user's group information from the client token.
|
|
//
|
|
if ( ClRtlGetTokenInformation( hClientToken, TokenGroups, &_pb, _szIndent ) ) {
|
|
PTOKEN_GROUPS _ptg = NULL;
|
|
DWORD _nIndex = 0;
|
|
|
|
wsprintfA( _szBuf, "%s ClientToken ", _szIndent );
|
|
|
|
_ptg = (PTOKEN_GROUPS) _pb;
|
|
|
|
for ( _nIndex = 0; _nIndex < _ptg->GroupCount; _nIndex++ )
|
|
{
|
|
ClRtlExamineSid( _ptg->Groups[ _nIndex ].Sid, _szBuf );
|
|
}
|
|
|
|
LocalFree( _pb );
|
|
_pb = NULL;
|
|
}
|
|
|
|
//
|
|
// Get the token type information from the client token.
|
|
//
|
|
if ( ClRtlGetTokenInformation( hClientToken, TokenType, &_pb, _szIndent ) ) {
|
|
PTOKEN_TYPE _ptt = NULL;
|
|
|
|
wsprintfA( _szBuf, "%s ClientToken type is", _szIndent );
|
|
|
|
_ptt = (PTOKEN_TYPE) _pb;
|
|
|
|
if ( *_ptt == TokenPrimary ) {
|
|
ClRtlLogPrint( LOG_NOISE, "%1!hs! primary.\n", _szBuf ) ;
|
|
}
|
|
|
|
if ( *_ptt == TokenImpersonation ) {
|
|
ClRtlLogPrint( LOG_NOISE, "%1!hs! impersonation.\n", _szBuf ) ;
|
|
}
|
|
|
|
LocalFree( _pb );
|
|
_pb = NULL;
|
|
}
|
|
|
|
//
|
|
// Get the token impersonation level information from the client token.
|
|
//
|
|
if ( ClRtlGetTokenInformation( hClientToken, TokenImpersonationLevel, &_pb, _szIndent ) ) {
|
|
PSECURITY_IMPERSONATION_LEVEL _psil = NULL;
|
|
|
|
wsprintfA( _szBuf, "%s ClientToken security impersonation level is", _szIndent );
|
|
|
|
_psil = (PSECURITY_IMPERSONATION_LEVEL) _pb;
|
|
|
|
switch ( *_psil )
|
|
{
|
|
case SecurityAnonymous :
|
|
ClRtlLogPrint( LOG_NOISE, "%1!hs! Anonymous.\n", _szBuf ) ;
|
|
break;
|
|
|
|
case SecurityIdentification :
|
|
ClRtlLogPrint( LOG_NOISE, "%1!hs! Identification.\n", _szBuf ) ;
|
|
break;
|
|
|
|
case SecurityImpersonation :
|
|
ClRtlLogPrint( LOG_NOISE, "%1!hs! Impersonation.\n", _szBuf ) ;
|
|
break;
|
|
|
|
case SecurityDelegation :
|
|
ClRtlLogPrint( LOG_NOISE, "%1!hs! Delegation.\n", _szBuf ) ;
|
|
break;
|
|
}
|
|
|
|
LocalFree( _pb );
|
|
_pb = NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
} // *** ClRtlExamineClientToken()
|
|
|
|
DWORD
|
|
ClRtlIsCallerAccountLocalSystemAccount(
|
|
OUT PBOOL pbIsLocalSystemAccount
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function checks whether the caller's account is the local system
|
|
account.
|
|
|
|
Arguments:
|
|
|
|
pbIsLocalSystemAccount - The caller's account is local system account or not.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS on success.
|
|
|
|
Win32 error code on failure.
|
|
|
|
Remarks:
|
|
|
|
Must be called by an impersonating thread.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwStatus = ERROR_SUCCESS;
|
|
SID_IDENTIFIER_AUTHORITY
|
|
siaNtAuthority = SECURITY_NT_AUTHORITY;
|
|
PSID psidLocalSystem = NULL;
|
|
|
|
*pbIsLocalSystemAccount = FALSE;
|
|
|
|
if ( !AllocateAndInitializeSid(
|
|
&siaNtAuthority,
|
|
1,
|
|
SECURITY_LOCAL_SYSTEM_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&psidLocalSystem ) )
|
|
{
|
|
dwStatus = GetLastError();
|
|
goto FnExit;
|
|
}
|
|
|
|
if ( !CheckTokenMembership(
|
|
NULL,
|
|
psidLocalSystem,
|
|
pbIsLocalSystemAccount ) )
|
|
{
|
|
dwStatus = GetLastError();
|
|
}
|
|
|
|
FnExit:
|
|
if( psidLocalSystem != NULL )
|
|
{
|
|
FreeSid( psidLocalSystem );
|
|
}
|
|
|
|
return( dwStatus );
|
|
|
|
} // ClRtlIsCallerAccountLocalSystemAccount
|
|
|
|
|
|
PTOKEN_USER
|
|
ClRtlGetSidOfCallingThread(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the SID associated with the calling thread (or process if the thread
|
|
has no token)
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
pointer to TOKEN_USER data; null if error with last error set on thread
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE currentToken;
|
|
PTOKEN_USER tokenUserData;
|
|
DWORD sizeRequired;
|
|
BOOL success;
|
|
|
|
// check if there is a thread token
|
|
if (!OpenThreadToken(GetCurrentThread(),
|
|
MAXIMUM_ALLOWED,
|
|
TRUE,
|
|
¤tToken))
|
|
{
|
|
// get the process token
|
|
if (!OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, ¤tToken )) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// get the size needed
|
|
//
|
|
success = GetTokenInformation(currentToken,
|
|
TokenUser,
|
|
NULL,
|
|
0,
|
|
&sizeRequired);
|
|
|
|
tokenUserData = LocalAlloc( LMEM_FIXED, sizeRequired );
|
|
if ( tokenUserData == NULL ) {
|
|
CloseHandle( currentToken );
|
|
return NULL;
|
|
}
|
|
|
|
success = GetTokenInformation(currentToken,
|
|
TokenUser,
|
|
tokenUserData,
|
|
sizeRequired,
|
|
&sizeRequired);
|
|
|
|
CloseHandle( currentToken );
|
|
|
|
if ( !success ) {
|
|
LocalFree( tokenUserData );
|
|
return NULL;
|
|
}
|
|
|
|
return tokenUserData;
|
|
} // ClRtlGetSidOfCallingThread
|
|
|
|
#if 0
|
|
//
|
|
// not needed but no point in throwing it away just yet
|
|
//
|
|
DWORD
|
|
ClRtlConvertDomainAccountToSid(
|
|
IN LPWSTR AccountInfo,
|
|
OUT PSID * AccountSid
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
For the given credentials, look up the account SID for the specified
|
|
domain.
|
|
|
|
Arguments:
|
|
|
|
AccountInfo - pointer to string of the form 'domain\user'
|
|
|
|
AccountSid - address of pointer that receives the SID for this user
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if everything worked
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwStatus = ERROR_SUCCESS;
|
|
DWORD dwSidSize = 128;
|
|
DWORD dwDomainNameSize = DNS_MAX_NAME_BUFFER_LENGTH;
|
|
LPWSTR pwszDomainName;
|
|
SID_NAME_USE SidType;
|
|
PSID accountSid;
|
|
|
|
do {
|
|
//
|
|
// Attempt to allocate a buffer for the SID.
|
|
//
|
|
accountSid = LocalAlloc( LMEM_FIXED, dwSidSize );
|
|
pwszDomainName = (LPWSTR) LocalAlloc( LMEM_FIXED, dwDomainNameSize * sizeof(WCHAR) );
|
|
|
|
// Was space allocated for the SID and domain name successfully?
|
|
|
|
if ( accountSid == NULL || pwszDomainName == NULL ) {
|
|
if ( accountSid != NULL ) {
|
|
LocalFree( accountSid );
|
|
}
|
|
|
|
if ( pwszDomainName != NULL ) {
|
|
LocalFree( pwszDomainName );
|
|
}
|
|
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Attempt to Retrieve the SID and domain name. If LookupAccountName fails
|
|
// because of insufficient buffer size(s) dwSidSize and dwDomainNameSize
|
|
// will be set correctly for the next attempt.
|
|
//
|
|
if ( !LookupAccountName( NULL,
|
|
AccountInfo,
|
|
accountSid,
|
|
&dwSidSize,
|
|
pwszDomainName,
|
|
&dwDomainNameSize,
|
|
&SidType ))
|
|
{
|
|
// free the Sid buffer and find out why we failed
|
|
LocalFree( accountSid );
|
|
|
|
dwStatus = GetLastError();
|
|
}
|
|
|
|
// domain name isn't needed at any time
|
|
LocalFree( pwszDomainName );
|
|
pwszDomainName = NULL;
|
|
|
|
} while ( dwStatus == ERROR_INSUFFICIENT_BUFFER );
|
|
|
|
if ( dwStatus == ERROR_SUCCESS ) {
|
|
*AccountSid = accountSid;
|
|
}
|
|
|
|
return dwStatus;
|
|
} // ClRtlConvertDomainAccountToSid
|
|
#endif
|
|
|
|
DWORD
|
|
AddAceToAcl(
|
|
IN PACL pOldAcl,
|
|
IN PSID pClientSid,
|
|
IN ACCESS_MASK AccessMask,
|
|
OUT PACL * ppNewAcl
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates a new ACL by copying the ACEs from the old ACL and
|
|
creating a new ACE with pClientSid and AccessMask. Stolen from
|
|
\nt\ds\ds\src\ntdsa\dra\remove.c
|
|
|
|
Arguments:
|
|
|
|
pOldAcl - pointer to old ACL with its ACEs
|
|
|
|
pClientSid - SID to add
|
|
|
|
AccessMask - access mask associated with SID
|
|
|
|
pNewAcl - brand spanking new ACL with ACE for the SID and access mask
|
|
|
|
Return Values:
|
|
|
|
ERROR_SUCCESS if the ace was put in the sd
|
|
|
|
--*/
|
|
{
|
|
DWORD WinError = ERROR_SUCCESS;
|
|
BOOL fStatus;
|
|
|
|
ACL_SIZE_INFORMATION AclSizeInfo;
|
|
ACL_REVISION_INFORMATION AclRevInfo;
|
|
ACCESS_ALLOWED_ACE Dummy;
|
|
|
|
PVOID FirstAce = 0;
|
|
PACL pNewAcl = 0;
|
|
|
|
ULONG NewAclSize, NewAceCount, AceSize;
|
|
|
|
// Parameter check
|
|
if ( !pOldAcl || !pClientSid || !ppNewAcl ) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Init the out parameter
|
|
*ppNewAcl = NULL;
|
|
|
|
memset( &AclSizeInfo, 0, sizeof( AclSizeInfo ) );
|
|
memset( &AclRevInfo, 0, sizeof( AclRevInfo ) );
|
|
|
|
//
|
|
// Get the old sd's values
|
|
//
|
|
fStatus = GetAclInformation( pOldAcl,
|
|
&AclSizeInfo,
|
|
sizeof( AclSizeInfo ),
|
|
AclSizeInformation );
|
|
if ( !fStatus )
|
|
{
|
|
WinError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
fStatus = GetAclInformation( pOldAcl,
|
|
&AclRevInfo,
|
|
sizeof( AclRevInfo ),
|
|
AclRevisionInformation );
|
|
if ( !fStatus )
|
|
{
|
|
WinError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Calculate the new sd's values
|
|
//
|
|
AceSize = sizeof( ACCESS_ALLOWED_ACE ) - sizeof( Dummy.SidStart )
|
|
+ GetLengthSid( pClientSid );
|
|
|
|
NewAclSize = AceSize + AclSizeInfo.AclBytesInUse;
|
|
NewAceCount = AclSizeInfo.AceCount + 1;
|
|
|
|
//
|
|
// Init the new acl
|
|
//
|
|
pNewAcl = LocalAlloc( 0, NewAclSize );
|
|
if ( NULL == pNewAcl )
|
|
{
|
|
WinError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
fStatus = InitializeAcl( pNewAcl,
|
|
NewAclSize,
|
|
AclRevInfo.AclRevision );
|
|
if ( !fStatus )
|
|
{
|
|
WinError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Copy the old into the new
|
|
//
|
|
fStatus = GetAce( pOldAcl,
|
|
0,
|
|
&FirstAce );
|
|
if ( !fStatus )
|
|
{
|
|
WinError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
fStatus = AddAce( pNewAcl,
|
|
AclRevInfo.AclRevision,
|
|
0,
|
|
FirstAce,
|
|
AclSizeInfo.AclBytesInUse - sizeof( ACL ) );
|
|
if ( !fStatus )
|
|
{
|
|
WinError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Finally, add the new ace
|
|
//
|
|
fStatus = AddAccessAllowedAce( pNewAcl,
|
|
ACL_REVISION,
|
|
AccessMask,
|
|
pClientSid );
|
|
|
|
if ( !fStatus )
|
|
{
|
|
WinError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Assign the out parameter
|
|
*ppNewAcl = pNewAcl;
|
|
|
|
//
|
|
// That's it fall through to cleanup
|
|
//
|
|
|
|
Cleanup:
|
|
|
|
if ( ERROR_SUCCESS != WinError )
|
|
{
|
|
if ( pNewAcl )
|
|
{
|
|
LocalFree( pNewAcl );
|
|
}
|
|
}
|
|
|
|
return WinError;
|
|
|
|
} // AddAceToAcl
|
|
|
|
DWORD
|
|
ClRtlAddAceToSd(
|
|
IN PSECURITY_DESCRIPTOR pOldSd,
|
|
IN PSID pClientSid,
|
|
IN ACCESS_MASK AccessMask,
|
|
OUT PSECURITY_DESCRIPTOR * ppNewSd
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates a new sd with a new ace with pClientSid and
|
|
AccessMask. Stolen from \nt\ds\ds\src\ntdsa\dra\remove.c
|
|
|
|
Arguments:
|
|
|
|
pOldSd - existing SD in self relative format
|
|
|
|
pClientSid - SID to add to an ACE
|
|
|
|
AccessMask - 'nuff said
|
|
|
|
pNewAcl - pointer to new SD that contains new ACE
|
|
|
|
Return Values:
|
|
|
|
ERROR_SUCCESS if the ace was put in the sd
|
|
|
|
--*/
|
|
{
|
|
|
|
DWORD WinError = ERROR_SUCCESS;
|
|
BOOL fStatus;
|
|
|
|
PSECURITY_DESCRIPTOR pNewSelfRelativeSd = NULL;
|
|
DWORD NewSelfRelativeSdSize = 0;
|
|
PACL pNewDacl = NULL;
|
|
|
|
SECURITY_DESCRIPTOR AbsoluteSd;
|
|
PACL pDacl = NULL;
|
|
PACL pSacl = NULL;
|
|
PSID pGroup = NULL;
|
|
PSID pOwner = NULL;
|
|
|
|
DWORD AbsoluteSdSize = sizeof( SECURITY_DESCRIPTOR );
|
|
DWORD DaclSize = 0;
|
|
DWORD SaclSize = 0;
|
|
DWORD GroupSize = 0;
|
|
DWORD OwnerSize = 0;
|
|
|
|
|
|
// Parameter check
|
|
if ( !pOldSd || !pClientSid || !ppNewSd ) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Init the out parameter
|
|
*ppNewSd = NULL;
|
|
|
|
RtlZeroMemory( &AbsoluteSd, AbsoluteSdSize );
|
|
|
|
//
|
|
// Break out the components of the self-relative SD by making it
|
|
// absolute. First get the sizes of the components.
|
|
//
|
|
fStatus = MakeAbsoluteSD( pOldSd,
|
|
&AbsoluteSd,
|
|
&AbsoluteSdSize,
|
|
pDacl,
|
|
&DaclSize,
|
|
pSacl,
|
|
&SaclSize,
|
|
pOwner,
|
|
&OwnerSize,
|
|
pGroup,
|
|
&GroupSize );
|
|
|
|
if ( !fStatus && ( ERROR_INSUFFICIENT_BUFFER == ( WinError = GetLastError() ))) {
|
|
WinError = ERROR_SUCCESS;
|
|
|
|
if ( 0 == DaclSize )
|
|
{
|
|
// No Dacl? We can't write to the dacl, then
|
|
WinError = ERROR_ACCESS_DENIED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( DaclSize > 0 ) {
|
|
pDacl = LocalAlloc( LMEM_FIXED, DaclSize );
|
|
if ( pDacl == NULL ) {
|
|
WinError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if ( SaclSize > 0 ) {
|
|
pSacl = LocalAlloc( LMEM_FIXED, SaclSize );
|
|
if ( pSacl == NULL ) {
|
|
WinError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if ( OwnerSize > 0 ) {
|
|
pOwner = LocalAlloc( LMEM_FIXED, OwnerSize );
|
|
if ( pOwner == NULL ) {
|
|
WinError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if ( GroupSize > 0 ) {
|
|
pGroup = LocalAlloc( LMEM_FIXED, GroupSize );
|
|
if ( pGroup == NULL ) {
|
|
WinError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
fStatus = MakeAbsoluteSD( pOldSd,
|
|
&AbsoluteSd,
|
|
&AbsoluteSdSize,
|
|
pDacl,
|
|
&DaclSize,
|
|
pSacl,
|
|
&SaclSize,
|
|
pOwner,
|
|
&OwnerSize,
|
|
pGroup,
|
|
&GroupSize );
|
|
|
|
if ( !fStatus ) {
|
|
WinError = GetLastError();
|
|
}
|
|
}
|
|
|
|
if ( ERROR_SUCCESS != WinError ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Create a new dacl with the new ace
|
|
//
|
|
WinError = AddAceToAcl(pDacl,
|
|
pClientSid,
|
|
AccessMask,
|
|
&pNewDacl );
|
|
|
|
if ( ERROR_SUCCESS != WinError ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Set the dacl
|
|
//
|
|
fStatus = SetSecurityDescriptorDacl( &AbsoluteSd,
|
|
TRUE, // dacl is present
|
|
pNewDacl,
|
|
FALSE ); // facl is not defaulted
|
|
|
|
if ( !fStatus ) {
|
|
WinError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Make the new SD self relative; get the size first
|
|
//
|
|
fStatus = MakeSelfRelativeSD( &AbsoluteSd,
|
|
pNewSelfRelativeSd,
|
|
&NewSelfRelativeSdSize );
|
|
|
|
if ( !fStatus && ( ERROR_INSUFFICIENT_BUFFER == ( WinError = GetLastError() ))) {
|
|
WinError = ERROR_SUCCESS;
|
|
|
|
pNewSelfRelativeSd = LocalAlloc( 0, NewSelfRelativeSdSize );
|
|
|
|
if ( pNewSelfRelativeSd ) {
|
|
fStatus = MakeSelfRelativeSD( &AbsoluteSd,
|
|
pNewSelfRelativeSd,
|
|
&NewSelfRelativeSdSize );
|
|
|
|
if ( !fStatus ) {
|
|
WinError = GetLastError();
|
|
}
|
|
}
|
|
else {
|
|
WinError = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
|
|
//
|
|
// That's it fall through to cleanup
|
|
//
|
|
|
|
Cleanup:
|
|
|
|
if ( pNewDacl ) {
|
|
LocalFree( pNewDacl );
|
|
}
|
|
|
|
if ( ERROR_SUCCESS == WinError ) {
|
|
*ppNewSd = pNewSelfRelativeSd;
|
|
}
|
|
else if ( pNewSelfRelativeSd ) {
|
|
LocalFree( pNewSelfRelativeSd );
|
|
}
|
|
|
|
if ( pDacl != NULL ) {
|
|
LocalFree( pDacl );
|
|
}
|
|
|
|
if ( pSacl != NULL ) {
|
|
LocalFree( pSacl );
|
|
}
|
|
|
|
if ( pGroup != NULL ) {
|
|
LocalFree( pGroup );
|
|
}
|
|
|
|
if ( pOwner != NULL ) {
|
|
LocalFree( pOwner );
|
|
}
|
|
|
|
return WinError;
|
|
|
|
} // ClRtlAddAceToSd
|
|
|
|
DWORD
|
|
ClRtlAddClusterServiceAccountToWinsta0DACL(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Modify the DACL on the interactive window station (Winsta0) and its
|
|
desktop such that resmon child processes (such as gennap resources) can
|
|
display on the desktop if so desired.
|
|
|
|
MEGA-IMPORTANT: this routine must be synchronized in a multi-threaded
|
|
environment, i.e., make sure that you're holding a lock of some sort
|
|
before calling it. It won't solve the race condition on setting the DACL
|
|
that exists between processes but it will make sure that the window
|
|
station APIs work correctly.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if it worked...
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
LPWSTR accountInfo = NULL;
|
|
PTOKEN_USER sidData = NULL;
|
|
HWINSTA winsta0 = NULL;
|
|
HWINSTA previousWinStation = NULL;
|
|
HDESK desktop = NULL;
|
|
SECURITY_INFORMATION requestedSI = DACL_SECURITY_INFORMATION;
|
|
BOOL success;
|
|
DWORD lengthRequired = 0;
|
|
DWORD lengthRequired2;
|
|
DWORD i;
|
|
PSECURITY_DESCRIPTOR winstaSD = NULL;
|
|
PSECURITY_DESCRIPTOR deskSD = NULL;
|
|
PSECURITY_DESCRIPTOR newSD = NULL;
|
|
BOOL hasDACL;
|
|
PACL dacl;
|
|
BOOL daclDefaulted;
|
|
|
|
//
|
|
// first see if we have the access we need by trying to open the
|
|
// interactive window station and its default desktop. if so, don't go any
|
|
// further and return success
|
|
//
|
|
winsta0 = OpenWindowStation( L"winsta0", FALSE, GENERIC_ALL );
|
|
if ( winsta0 != NULL ) {
|
|
|
|
previousWinStation = GetProcessWindowStation();
|
|
success = SetProcessWindowStation( winsta0 );
|
|
|
|
if ( success ) {
|
|
|
|
//
|
|
// if we have window station access, we should have desktop as well
|
|
//
|
|
desktop = OpenDesktop( L"default", 0, FALSE, GENERIC_ALL );
|
|
SetProcessWindowStation( previousWinStation );
|
|
previousWinStation = NULL;
|
|
|
|
if ( desktop != NULL ) {
|
|
//
|
|
// always switch the winstation back to the previous one
|
|
// before closing the desktop and winstation handles
|
|
//
|
|
CloseDesktop( desktop );
|
|
CloseWindowStation( winsta0 );
|
|
return ERROR_SUCCESS;
|
|
}
|
|
}
|
|
CloseWindowStation( winsta0 );
|
|
}
|
|
|
|
//
|
|
// get the SID of the account associated with this thread. This is the
|
|
// account that will be added to the DACL
|
|
//
|
|
sidData = ClRtlGetSidOfCallingThread();
|
|
if ( sidData == NULL ) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL, "[ClRtl] ClRtlGetSidOfCallingThread failed. Status %1!u!\n", status );
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// open handles to Winsta0 and its default desktop. Temporarily switch to
|
|
// winsta0 to get its default desktop
|
|
//
|
|
winsta0 = OpenWindowStation( L"winsta0", TRUE, MAXIMUM_ALLOWED );
|
|
if ( winsta0 == NULL ) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL, "[ClRtl] OpenWindowStation failed. Status %1!u!\n", status );
|
|
goto error_exit;
|
|
}
|
|
|
|
previousWinStation = GetProcessWindowStation();
|
|
success = SetProcessWindowStation( winsta0 );
|
|
if ( !success ) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL, "[ClRtl] SetProcessWindowStation(winsta0) failed. Status %1!u!\n", status );
|
|
goto error_exit;
|
|
}
|
|
|
|
desktop = OpenDesktop( L"default", 0, TRUE, MAXIMUM_ALLOWED );
|
|
if ( desktop == NULL ) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL, "[ClRtl] OpenDesktop(default) failed. Status %1!u!\n", status );
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// get the SD and its DACL for Winsta0
|
|
//
|
|
success = GetUserObjectSecurity(winsta0,
|
|
&requestedSI,
|
|
NULL,
|
|
0,
|
|
&lengthRequired);
|
|
if ( !success ) {
|
|
status = GetLastError();
|
|
if ( status != ERROR_INSUFFICIENT_BUFFER ) {
|
|
ClRtlLogPrint(LOG_CRITICAL, "[ClRtl] GetUOSecurityLen(winsta0) failed. Status %1!u!\n", status );
|
|
goto error_exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If UserObjectLen grew on us more than a few times,
|
|
// something is fishy, so we will fail the request
|
|
//
|
|
for(i = 0; i < 5; ++i) {
|
|
winstaSD = LocalAlloc( LMEM_FIXED, lengthRequired );
|
|
if ( winstaSD == NULL ) {
|
|
status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
|
|
success = GetUserObjectSecurity(winsta0,
|
|
&requestedSI,
|
|
winstaSD,
|
|
lengthRequired,
|
|
&lengthRequired2);
|
|
if ( success ) {
|
|
status = ERROR_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
status = GetLastError();
|
|
if ( status != ERROR_INSUFFICIENT_BUFFER ) {
|
|
ClRtlLogPrint(LOG_CRITICAL, "[ClRtl] GetUOSecurity(winsta0) failed. Status %1!u!\n", status );
|
|
goto error_exit;
|
|
}
|
|
|
|
lengthRequired = lengthRequired2;
|
|
LocalFree(winstaSD);
|
|
winstaSD = NULL;
|
|
}
|
|
|
|
if ( status != ERROR_SUCCESS ) {
|
|
ClRtlLogPrint(LOG_CRITICAL, "[ClRtl] GetUOSecurity(winsta0) failed. Status %1!u!\n", status );
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// build a new SD that includes our service account SID giving it complete
|
|
// access
|
|
//
|
|
status = ClRtlAddAceToSd( winstaSD, sidData->User.Sid, GENERIC_ALL, &newSD );
|
|
|
|
if ( status != ERROR_SUCCESS ) {
|
|
ClRtlLogPrint(LOG_CRITICAL, "[ClRtl] ClRtlAddAceToSd(winsta) failed. Status %1!u!\n", status );
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// set the new SD on Winsta0
|
|
//
|
|
success = SetUserObjectSecurity( winsta0, &requestedSI, newSD );
|
|
if ( !success ) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL, "[ClRtl] SetUOSecurity(winsta0) failed. Status %1!u!\n", status );
|
|
goto error_exit;
|
|
}
|
|
|
|
LocalFree( newSD );
|
|
newSD = NULL;
|
|
|
|
//
|
|
// repeat the process for the desktop SD and its DACL
|
|
//
|
|
success = GetUserObjectSecurity(desktop,
|
|
&requestedSI,
|
|
NULL,
|
|
0,
|
|
&lengthRequired);
|
|
if ( !success ) {
|
|
status = GetLastError();
|
|
if ( status != ERROR_INSUFFICIENT_BUFFER ) {
|
|
ClRtlLogPrint(LOG_CRITICAL, "[ClRtl] GetUOSecurityLen(desktop) failed. Status %1!u!\n", status );
|
|
goto error_exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If UserObjectLen grew on us more than a few times,
|
|
// something is fishy, so we will fail the request
|
|
//
|
|
for (i = 0; i < 5; ++i) {
|
|
deskSD = LocalAlloc( LMEM_FIXED, lengthRequired );
|
|
if ( deskSD == NULL ) {
|
|
status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
|
|
success = GetUserObjectSecurity(desktop,
|
|
&requestedSI,
|
|
deskSD,
|
|
lengthRequired,
|
|
&lengthRequired2);
|
|
if ( success ) {
|
|
status = ERROR_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
status = GetLastError();
|
|
if ( status != ERROR_INSUFFICIENT_BUFFER ) {
|
|
ClRtlLogPrint(LOG_CRITICAL, "[ClRtl] GetUOSecurity(desktop) failed. Status %1!u!\n", status );
|
|
goto error_exit;
|
|
}
|
|
|
|
lengthRequired = lengthRequired2;
|
|
LocalFree(deskSD);
|
|
deskSD = NULL;
|
|
}
|
|
if ( status != ERROR_SUCCESS ) {
|
|
ClRtlLogPrint(LOG_CRITICAL, "[ClRtl] GetUOSecurity(desktop) failed. Status %1!u!\n", status );
|
|
goto error_exit;
|
|
}
|
|
|
|
status = ClRtlAddAceToSd( deskSD, sidData->User.Sid, GENERIC_ALL, &newSD );
|
|
|
|
if ( status != ERROR_SUCCESS ) {
|
|
ClRtlLogPrint(LOG_CRITICAL, "[ClRtl] ClRtlAddAceToSd(desktop) failed. Status %1!u!\n", status );
|
|
goto error_exit;
|
|
}
|
|
|
|
success = SetUserObjectSecurity( desktop, &requestedSI, newSD );
|
|
if ( !success ) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL, "[ClRtl] SetUserObjectSecurity(desktop) failed. Status %1!u!\n", status );
|
|
}
|
|
|
|
error_exit:
|
|
|
|
FREE_IF_NOT_NULL( newSD, LocalFree );
|
|
|
|
FREE_IF_NOT_NULL( deskSD, LocalFree );
|
|
|
|
FREE_IF_NOT_NULL( winstaSD, LocalFree );
|
|
|
|
//
|
|
// always switch the winstation back to the previous one before closing
|
|
// the desktop and winstation handles
|
|
//
|
|
if ( previousWinStation != NULL ) {
|
|
success = SetProcessWindowStation( previousWinStation );
|
|
if ( !success ) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL, "[ClRtl] SetWindowsStation(previous) failed. Status %1!u!\n", status );
|
|
}
|
|
}
|
|
|
|
FREE_IF_NOT_NULL( desktop, CloseDesktop );
|
|
|
|
FREE_IF_NOT_NULL( winsta0, CloseWindowStation );
|
|
|
|
FREE_IF_NOT_NULL( sidData, LocalFree );
|
|
|
|
FREE_IF_NOT_NULL( accountInfo, LocalFree );
|
|
|
|
return status;
|
|
|
|
} // ClRtlAddClusterServiceAccountToWinsta0DACL
|