|
|
/*++
Copyright (c) 1999-2002 Microsoft Corporation
Module Name:
seutil.c
Abstract:
This module implements general security utilities, and dispatch routines for security IRPs.
Author:
Keith Moore (keithmo) 25-Mar-1999
Revision History:
--*/
#include "precomp.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, UlAssignSecurity )
#pragma alloc_text( PAGE, UlDeassignSecurity )
#pragma alloc_text( PAGE, UlSetSecurity )
#pragma alloc_text( PAGE, UlQuerySecurity )
#pragma alloc_text( PAGE, UlAccessCheck )
#pragma alloc_text( PAGE, UlSetSecurityDispatch )
#pragma alloc_text( PAGE, UlQuerySecurityDispatch )
#pragma alloc_text( PAGE, UlThreadAdminCheck )
#pragma alloc_text( PAGE, UlCreateSecurityDescriptor )
#pragma alloc_text( PAGE, UlCleanupSecurityDescriptor )
#pragma alloc_test( PAGE, UlMapGenericMask )
#endif // ALLOC_PRAGMA
#if 0
#endif
//
// Public functions.
//
/***************************************************************************++
Routine Description:
Assigns a new security descriptor.
Arguments:
pSecurityDescriptor - Supplies a pointer to the current security descriptor pointer. The current security descriptor pointer will be updated with the new security descriptor.
pAccessState - Supplies the ACCESS_STATE structure containing the state of an access in progress.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlAssignSecurity( IN OUT PSECURITY_DESCRIPTOR *pSecurityDescriptor, IN PACCESS_STATE pAccessState ) { NTSTATUS status;
//
// Sanity check.
//
PAGED_CODE();
ASSERT( pSecurityDescriptor != NULL ); ASSERT( pAccessState != NULL );
//
// Assign the security descriptor.
//
SeLockSubjectContext( &pAccessState->SubjectSecurityContext );
status = SeAssignSecurity( NULL, // ParentDescriptor
pAccessState->SecurityDescriptor, pSecurityDescriptor, FALSE, // IsDirectoryObject
&pAccessState->SubjectSecurityContext, IoGetFileObjectGenericMapping(), PagedPool );
SeUnlockSubjectContext( &pAccessState->SubjectSecurityContext );
return status;
} // UlAssignSecurity
/***************************************************************************++
Routine Description:
Deletes a security descriptor.
Arguments:
pSecurityDescriptor - Supplies a pointer to the current security descriptor pointer. The current security descriptor pointer will be deleted.
--***************************************************************************/ VOID UlDeassignSecurity( IN OUT PSECURITY_DESCRIPTOR *pSecurityDescriptor ) { //
// Sanity check.
//
PAGED_CODE();
ASSERT( pSecurityDescriptor != NULL );
//
// If there's a security descriptor present, free it.
//
if (*pSecurityDescriptor != NULL) { SeDeassignSecurity( pSecurityDescriptor ); }
} // UlDeassignSecurity
/***************************************************************************++
Routine Description:
Sets a new security descriptor.
Arguments:
pSecurityDescriptor - Supplies a pointer to the current security descriptor pointer. The current security descriptor will be updated with the new security information.
pSecurityInformation - Indicates which security information is to be applied to the object.
pNewSecurityDescriptor - Pointer to the new security descriptor to be applied to the object.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlSetSecurity( IN OUT PSECURITY_DESCRIPTOR *ppSecurityDescriptor, IN PSECURITY_INFORMATION pSecurityInformation, IN PSECURITY_DESCRIPTOR pNewSecurityDescriptor ) { NTSTATUS status; PSECURITY_DESCRIPTOR pOldSecurityDescriptor; SECURITY_SUBJECT_CONTEXT securitySubjectContext;
PAGED_CODE();
pOldSecurityDescriptor = *ppSecurityDescriptor;
SeCaptureSubjectContext(&securitySubjectContext); SeLockSubjectContext(&securitySubjectContext);
status = SeSetSecurityDescriptorInfo( NULL, pSecurityInformation, pNewSecurityDescriptor, ppSecurityDescriptor, PagedPool, IoGetFileObjectGenericMapping() );
SeUnlockSubjectContext(&securitySubjectContext); SeReleaseSubjectContext(&securitySubjectContext);
if (NT_SUCCESS(status)) { SeDeassignSecurity(&pOldSecurityDescriptor); }
return status; }
/***************************************************************************++
Routine Description:
Query for security descriptor information for an object.
Arguments:
pSecurityInformation - specifies what information is being queried.
pSecurityDescriptor - Supplies a pointer to the security descriptor to be filled in.
pLength - Address of variable containing length of the above security descriptor buffer. Upon return, this will contain the length needed to store the requested information.
ppSecurityDescriptor - Address of a pointer to the objects security descriptor.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlQuerySecurity( IN PSECURITY_INFORMATION pSecurityInformation, OUT PSECURITY_DESCRIPTOR pSecurityDescriptor, IN OUT PULONG pLength, IN PSECURITY_DESCRIPTOR *ppSecurityDescriptor ) { NTSTATUS status; SECURITY_SUBJECT_CONTEXT securitySubjectContext;
PAGED_CODE();
SeCaptureSubjectContext(&securitySubjectContext); SeLockSubjectContext(&securitySubjectContext);
status = SeQuerySecurityDescriptorInfo( pSecurityInformation, pSecurityDescriptor, pLength, ppSecurityDescriptor );
SeUnlockSubjectContext(&securitySubjectContext); SeReleaseSubjectContext(&securitySubjectContext);
return status; }
/***************************************************************************++
Routine Description:
Determines if a user has access to the specified resource.
Arguments:
pSecurityDescriptor - Supplies the security descriptor protecting the resource.
pAccessState - Supplies the ACCESS_STATE structure containing the state of an access in progress.
DesiredAccess - Supplies an access mask describing the user's desired access to the resource. This mask is assumed to not contain generic access types.
RequestorMode - Supplies the processor mode by which the access is being requested.
pObjectName - Supplies the name of the object being referenced.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlAccessCheck( IN PSECURITY_DESCRIPTOR pSecurityDescriptor, IN PACCESS_STATE pAccessState, IN ACCESS_MASK DesiredAccess, IN KPROCESSOR_MODE RequestorMode, IN PCWSTR pObjectName ) { NTSTATUS status, aaStatus; BOOLEAN accessGranted; PPRIVILEGE_SET pPrivileges = NULL; ACCESS_MASK grantedAccess; UNICODE_STRING objectName; UNICODE_STRING typeName;
//
// Sanity check.
//
PAGED_CODE();
ASSERT( pSecurityDescriptor != NULL ); ASSERT( pAccessState != NULL );
//
// Perform the access check.
//
SeLockSubjectContext( &pAccessState->SubjectSecurityContext );
accessGranted = SeAccessCheck( pSecurityDescriptor, &pAccessState->SubjectSecurityContext, TRUE, // SubjectContextLocked
DesiredAccess, 0, // PreviouslyGrantedAccess
&pPrivileges, IoGetFileObjectGenericMapping(), RequestorMode, &grantedAccess, &status );
if (pPrivileges != NULL) { SeAppendPrivileges( pAccessState, pPrivileges ); SeFreePrivileges( pPrivileges ); }
if (accessGranted) { pAccessState->PreviouslyGrantedAccess |= grantedAccess; pAccessState->RemainingDesiredAccess &= ~(grantedAccess | MAXIMUM_ALLOWED); }
aaStatus = UlInitUnicodeStringEx( &typeName, L"Ul" );
if ( NT_SUCCESS(aaStatus) ) { aaStatus = UlInitUnicodeStringEx( &objectName, pObjectName );
if ( NT_SUCCESS(aaStatus) ) { SeOpenObjectAuditAlarm( &typeName, NULL, // Object
&objectName, pSecurityDescriptor, pAccessState, FALSE, // ObjectCreated
accessGranted, RequestorMode, &pAccessState->GenerateOnClose ); } }
SeUnlockSubjectContext( &pAccessState->SubjectSecurityContext );
if (accessGranted) { status = STATUS_SUCCESS; } else { //
// SeAccessCheck() should have set the completion status.
//
ASSERT( !NT_SUCCESS(status) ); } return status;
} // UlAccessCheck
NTSTATUS UlSetSecurityDispatch( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) { NTSTATUS status; PIO_STACK_LOCATION pIrpSp; PFILE_OBJECT pFileObject; PUL_APP_POOL_PROCESS pProcess;
UNREFERENCED_PARAMETER(pDeviceObject);
PAGED_CODE();
UL_ENTER_DRIVER( "UlSetSecurityDispatch", pIrp );
pIrpSp = IoGetCurrentIrpStackLocation(pIrp); pFileObject = pIrpSp->FileObject; //
// We only allow changing the security descriptor on app
// pool handles.
//
if (!IS_APP_POOL_FO(pFileObject)) { status = STATUS_INVALID_PARAMETER; goto complete; }
pProcess = GET_APP_POOL_PROCESS(pFileObject);
status = UlSetSecurity( &pProcess->pAppPool->pSecurityDescriptor, &pIrpSp->Parameters.SetSecurity.SecurityInformation, pIrpSp->Parameters.SetSecurity.SecurityDescriptor ); complete:
pIrp->IoStatus.Status = status;
UlCompleteRequest(pIrp, IO_NO_INCREMENT);
UL_LEAVE_DRIVER( "UlSetSecurityDispatch" ); RETURN(status);
} // UlSetSecurityDispatch
NTSTATUS UlQuerySecurityDispatch( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) { NTSTATUS status; PIO_STACK_LOCATION pIrpSp; PFILE_OBJECT pFileObject; PUL_APP_POOL_PROCESS pProcess;
UNREFERENCED_PARAMETER(pDeviceObject);
PAGED_CODE();
UL_ENTER_DRIVER( "UlQuerySecurityDispatch", pIrp );
pIrpSp = IoGetCurrentIrpStackLocation(pIrp); pFileObject = pIrpSp->FileObject; //
// We only allow querying the security descriptor on app
// pool handles.
//
if (!IS_APP_POOL_FO(pFileObject)) { status = STATUS_INVALID_PARAMETER; goto complete; }
pProcess = GET_APP_POOL_PROCESS(pFileObject);
status = UlQuerySecurity( &pIrpSp->Parameters.QuerySecurity.SecurityInformation, pIrp->UserBuffer, &pIrpSp->Parameters.QuerySecurity.Length, &pProcess->pAppPool->pSecurityDescriptor );
if (pIrp->UserIosb) { pIrp->UserIosb->Information = pIrpSp->Parameters.QuerySecurity.Length; }
complete:
pIrp->IoStatus.Status = status;
UlCompleteRequest(pIrp, IO_NO_INCREMENT);
UL_LEAVE_DRIVER( "UlQuerySecurityDispatch" ); RETURN(status);
} // UlQuerySecurityDispatch
/***************************************************************************++
Routine Description:
Determines if this is a thread with Admin/LocalSystem privileges.
Arguments:
DesiredAccess - Supplies an access mask describing the user's desired access to the resource. This mask is assumed to not contain generic access types.
RequestorMode - Supplies the processor mode by which the access is being requested.
pObjectName - Supplies the name of the object being referenced.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlThreadAdminCheck( IN ACCESS_MASK DesiredAccess, IN KPROCESSOR_MODE RequestorMode, IN PCWSTR pObjectName ) { ACCESS_STATE AccessState; AUX_ACCESS_DATA AuxData; NTSTATUS Status;
Status = SeCreateAccessState( &AccessState, &AuxData, DesiredAccess, NULL );
if(NT_SUCCESS(Status)) { Status = UlAccessCheck( g_pAdminAllSystemAll, &AccessState, DesiredAccess, RequestorMode, pObjectName );
SeDeleteAccessState(&AccessState); }
return Status; }
/***************************************************************************++
Routine Description:
Allocates and initializes a security descriptor with the specified attributes.
Arguments:
pSecurityDescriptor - Supplies a pointer to the security descriptor to initialize.
pSidMaskPairs - Supplies an array of SID/ACCESS_MASK pairs.
NumSidMaskPairs - Supplies the number of SID/ACESS_MASK pairs.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlCreateSecurityDescriptor( OUT PSECURITY_DESCRIPTOR pSecurityDescriptor, IN PSID_MASK_PAIR pSidMaskPairs, IN ULONG NumSidMaskPairs ) { NTSTATUS status; PACL pDacl; ULONG daclLength; ULONG i;
//
// Sanity check.
//
PAGED_CODE();
ASSERT( pSecurityDescriptor != NULL ); ASSERT( pSidMaskPairs != NULL ); ASSERT( NumSidMaskPairs > 0 );
//
// Setup locals so we know how to cleanup on exit.
//
pDacl = NULL;
//
// Initialize the security descriptor.
//
status = RtlCreateSecurityDescriptor( pSecurityDescriptor, // SecurityDescriptor
SECURITY_DESCRIPTOR_REVISION // Revision
);
if (!NT_SUCCESS(status)) { goto cleanup; }
//
// Calculate the DACL length.
//
daclLength = sizeof(ACL);
for (i = 0 ; i < NumSidMaskPairs ; i++) { daclLength += sizeof(ACCESS_ALLOWED_ACE); daclLength += RtlLengthSid( pSidMaskPairs[i].pSid ); }
//
// Allocate & initialize the DACL.
//
pDacl = (PACL) UL_ALLOCATE_POOL( PagedPool, daclLength, UL_SECURITY_DATA_POOL_TAG );
if (pDacl == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; }
status = RtlCreateAcl( pDacl, // Acl
daclLength, // AclLength
ACL_REVISION // AclRevision
);
if (!NT_SUCCESS(status)) { goto cleanup; }
//
// Add the necessary access-allowed ACEs to the DACL.
//
for (i = 0 ; i < NumSidMaskPairs ; i++) { status = RtlAddAccessAllowedAceEx( pDacl, // Acl
ACL_REVISION, // AceRevision
pSidMaskPairs[i].AceFlags, // Inheritance flags
pSidMaskPairs[i].AccessMask, // AccessMask
pSidMaskPairs[i].pSid // Sid
);
if (!NT_SUCCESS(status)) { goto cleanup; } }
//
// Attach the DACL to the security descriptor.
//
status = RtlSetDaclSecurityDescriptor( pSecurityDescriptor, // SecurityDescriptor
TRUE, // DaclPresent
pDacl, // Dacl
FALSE // DaclDefaulted
);
if (!NT_SUCCESS(status)) { goto cleanup; }
//
// Success!
//
ASSERT( NT_SUCCESS(status) ); return STATUS_SUCCESS;
cleanup:
ASSERT( !NT_SUCCESS(status) );
if (pDacl != NULL) { UL_FREE_POOL( pDacl, UL_SECURITY_DATA_POOL_TAG ); }
return status;
} // UlpCreateSecurityDescriptor
/***************************************************************************++
Routine Description:
Frees any resources associated with the security descriptor created by UlpCreateSecurityDescriptor().
Arguments:
pSecurityDescriptor - Supplies the security descriptor to cleanup.
--***************************************************************************/ VOID UlCleanupSecurityDescriptor( IN PSECURITY_DESCRIPTOR pSecurityDescriptor ) { NTSTATUS status; PACL pDacl; BOOLEAN daclPresent; BOOLEAN daclDefaulted;
//
// Sanity check.
//
PAGED_CODE();
ASSERT( RtlValidSecurityDescriptor( pSecurityDescriptor ) );
//
// Try to retrieve the DACL from the security descriptor.
//
status = RtlGetDaclSecurityDescriptor( pSecurityDescriptor, // SecurityDescriptor
&daclPresent, // DaclPresent
&pDacl, // Dacl
&daclDefaulted // DaclDefaulted
);
if (NT_SUCCESS(status)) { if (daclPresent && (pDacl != NULL)) { UL_FREE_POOL( pDacl, UL_SECURITY_DATA_POOL_TAG ); } }
} // UlCleanupSecurityDescriptor
/**************************************************************************++
Routine Description:
This routine maps the generic access masks of the ACE's present in the DACL of the supplied security descriptor.
CODEWORK: Get RtlpApplyAclToObject exported.
Arguments:
pSecurityDescriptor - Supplies security descriptor.
Return Value:
NTSTATUS.
--**************************************************************************/ NTSTATUS UlMapGenericMask( PSECURITY_DESCRIPTOR pSecurityDescriptor ) { ULONG i; PACE_HEADER Ace; PACL Dacl; NTSTATUS Status; BOOLEAN Ignore; BOOLEAN DaclPresent = FALSE;
//
// Get the DACL.
//
Status = RtlGetDaclSecurityDescriptor( pSecurityDescriptor, &DaclPresent, &Dacl, &Ignore );
if (!NT_SUCCESS(Status)) { return Status; }
if (DaclPresent) { //
// RtlpApplyAclToObject(Acl, GenericMapping) is cloned below since
// it is not exported.
//
//
// Now walk the ACL, mapping each ACE as we go.
//
for (i = 0, Ace = FirstAce(Dacl); i < Dacl->AceCount; i += 1, Ace = NextAce(Ace)) { if (IsMSAceType(Ace)) { RtlApplyAceToObject(Ace, &g_UrlAccessGenericMapping); } } }
return STATUS_SUCCESS; }
//
// Private functions.
//
|