1450 lines
38 KiB
C
1450 lines
38 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
obse.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Object Security API calls
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Steve Wood (stevewo) 31-Mar-1989
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "obp.h"
|
|||
|
|
|||
|
#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
|
|||
|
|
|||
|
#pragma alloc_text(PAGE,NtSetSecurityObject)
|
|||
|
#pragma alloc_text(PAGE,NtQuerySecurityObject)
|
|||
|
#pragma alloc_text(PAGE,ObAssignObjectSecurityDescriptor)
|
|||
|
#pragma alloc_text(PAGE,ObAssignSecurity)
|
|||
|
#pragma alloc_text(PAGE,ObCheckCreateObjectAccess)
|
|||
|
#pragma alloc_text(PAGE,ObCheckObjectAccess)
|
|||
|
#pragma alloc_text(PAGE,ObCheckObjectReference)
|
|||
|
#pragma alloc_text(PAGE,ObpCheckTraverseAccess)
|
|||
|
#pragma alloc_text(PAGE,ObGetObjectSecurity)
|
|||
|
#pragma alloc_text(PAGE,ObSetSecurityDescriptorInfo)
|
|||
|
#pragma alloc_text(PAGE,ObReleaseObjectSecurity)
|
|||
|
#pragma alloc_text(PAGE,ObSetSecurityQuotaCharged)
|
|||
|
#pragma alloc_text(PAGE,ObValidateSecurityQuota)
|
|||
|
#pragma alloc_text(PAGE,ObpValidateAccessMask)
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NtSetSecurityObject(
|
|||
|
IN HANDLE Handle,
|
|||
|
IN SECURITY_INFORMATION SecurityInformation,
|
|||
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor
|
|||
|
)
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS Status;
|
|||
|
PVOID Object;
|
|||
|
ACCESS_MASK DesiredAccess;
|
|||
|
OBJECT_HANDLE_INFORMATION HandleInformation;
|
|||
|
KPROCESSOR_MODE RequestorMode;
|
|||
|
SECURITY_DESCRIPTOR *CapturedDescriptor;
|
|||
|
POBJECT_HEADER ObjectHeader;
|
|||
|
POBJECT_TYPE ObjectType;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Establish the accesses needed to the object based upon the
|
|||
|
// security information being modified.
|
|||
|
//
|
|||
|
|
|||
|
SeSetSecurityAccessMask( SecurityInformation, &DesiredAccess );
|
|||
|
|
|||
|
|
|||
|
Status = ObReferenceObjectByHandle( Handle,
|
|||
|
DesiredAccess,
|
|||
|
NULL,
|
|||
|
RequestorMode = KeGetPreviousMode(),
|
|||
|
&Object,
|
|||
|
&HandleInformation
|
|||
|
);
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
|||
|
ObjectType = ObjectHeader->Type;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Probe and capture the input security descriptor, and return
|
|||
|
// right away if it is ill-formed.
|
|||
|
//
|
|||
|
// The returned security descriptor is in self-relative format.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Make sure the passed security descriptor is really there.
|
|||
|
// SeCaptureSecurityDescriptor doesn't mind being passed a NULL
|
|||
|
// SecurityDescriptor, and will just return NULL back.
|
|||
|
//
|
|||
|
|
|||
|
if (!ARGUMENT_PRESENT( SecurityDescriptor )) {
|
|||
|
ObDereferenceObject( Object );
|
|||
|
return( STATUS_ACCESS_VIOLATION );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
Status = SeCaptureSecurityDescriptor(
|
|||
|
SecurityDescriptor,
|
|||
|
RequestorMode,
|
|||
|
PagedPool,
|
|||
|
FALSE,
|
|||
|
(PSECURITY_DESCRIPTOR *)&CapturedDescriptor
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
if ( (SecurityInformation & OWNER_SECURITY_INFORMATION) &&
|
|||
|
(((PISECURITY_DESCRIPTOR)CapturedDescriptor)->Owner == NULL)
|
|||
|
||
|
|||
|
(SecurityInformation & GROUP_SECURITY_INFORMATION) &&
|
|||
|
(((PISECURITY_DESCRIPTOR)CapturedDescriptor)->Group == NULL)
|
|||
|
) {
|
|||
|
Status = STATUS_INVALID_SECURITY_DESCR;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
ObDereferenceObject( Object );
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
Status = (ObjectType->TypeInfo.SecurityProcedure)(
|
|||
|
Object,
|
|||
|
SetSecurityDescriptor,
|
|||
|
&SecurityInformation,
|
|||
|
(PSECURITY_DESCRIPTOR)CapturedDescriptor,
|
|||
|
NULL,
|
|||
|
&ObjectHeader->SecurityDescriptor,
|
|||
|
ObjectType->TypeInfo.PoolType,
|
|||
|
&ObjectType->TypeInfo.GenericMapping
|
|||
|
);
|
|||
|
|
|||
|
ObDereferenceObject( Object );
|
|||
|
SeReleaseSecurityDescriptor( (PSECURITY_DESCRIPTOR)CapturedDescriptor,
|
|||
|
RequestorMode,
|
|||
|
FALSE
|
|||
|
);
|
|||
|
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NtQuerySecurityObject(
|
|||
|
IN HANDLE Handle,
|
|||
|
IN SECURITY_INFORMATION SecurityInformation,
|
|||
|
OUT PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|||
|
IN ULONG Length,
|
|||
|
OUT PULONG LengthNeeded
|
|||
|
)
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS Status;
|
|||
|
PVOID Object;
|
|||
|
ACCESS_MASK DesiredAccess;
|
|||
|
OBJECT_HANDLE_INFORMATION HandleInformation;
|
|||
|
KPROCESSOR_MODE RequestorMode;
|
|||
|
POBJECT_HEADER ObjectHeader;
|
|||
|
POBJECT_TYPE ObjectType;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Probe output parameters
|
|||
|
//
|
|||
|
|
|||
|
RequestorMode = KeGetPreviousMode();
|
|||
|
|
|||
|
if (RequestorMode != KernelMode) {
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
ProbeForWriteUlong( LengthNeeded );
|
|||
|
|
|||
|
ProbeForWrite( SecurityDescriptor, Length, sizeof(ULONG) );
|
|||
|
|
|||
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
return GetExceptionCode();
|
|||
|
} // end_try
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Establish the accesses needed to the object based upon the
|
|||
|
// security information being modified.
|
|||
|
//
|
|||
|
|
|||
|
SeQuerySecurityAccessMask( SecurityInformation, &DesiredAccess );
|
|||
|
|
|||
|
Status = ObReferenceObjectByHandle( Handle,
|
|||
|
DesiredAccess,
|
|||
|
NULL,
|
|||
|
RequestorMode,
|
|||
|
&Object,
|
|||
|
&HandleInformation
|
|||
|
);
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
|||
|
ObjectType = ObjectHeader->Type;
|
|||
|
|
|||
|
Status = (ObjectType->TypeInfo.SecurityProcedure)(
|
|||
|
Object,
|
|||
|
QuerySecurityDescriptor,
|
|||
|
&SecurityInformation,
|
|||
|
SecurityDescriptor,
|
|||
|
&Length,
|
|||
|
&ObjectHeader->SecurityDescriptor,
|
|||
|
ObjectType->TypeInfo.PoolType,
|
|||
|
&ObjectType->TypeInfo.GenericMapping
|
|||
|
);
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
*LengthNeeded = Length;
|
|||
|
|
|||
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
|
|||
|
ObDereferenceObject( Object );
|
|||
|
return(GetExceptionCode());
|
|||
|
}
|
|||
|
|
|||
|
ObDereferenceObject( Object );
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
ObCheckObjectAccess(
|
|||
|
IN PVOID Object,
|
|||
|
IN OUT PACCESS_STATE AccessState,
|
|||
|
IN BOOLEAN TypeMutexLocked,
|
|||
|
IN KPROCESSOR_MODE AccessMode,
|
|||
|
OUT PNTSTATUS AccessStatus
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine performs access validation on the passed object. The
|
|||
|
remaining desired access mask is extracted from the AccessState
|
|||
|
parameter and passes to the appropriate security routine to perform the
|
|||
|
access check.
|
|||
|
|
|||
|
If the access attempt is successful, SeAccessCheck returns a mask
|
|||
|
containing the granted accesses. The bits in this mask are turned
|
|||
|
on in the PreviouslyGrantedAccess field of the AccessState, and
|
|||
|
are turned off in the RemainingDesiredAccess field.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Object - The object being examined.
|
|||
|
|
|||
|
AccessState - The ACCESS_STATE structure containing accumulated
|
|||
|
information about the current attempt to gain access to the object.
|
|||
|
|
|||
|
TypeMutexLocked - Indicates whether the type mutex for this object's
|
|||
|
type is locked. The type mutex is used to protect the object's
|
|||
|
security descriptor from being modified while it is being accessed.
|
|||
|
|
|||
|
AccessMode - The previous processor mode.
|
|||
|
|
|||
|
AccessStatus - Pointer to a variable to return the status code of the
|
|||
|
access attempt. In the case of failure this status code must be
|
|||
|
propagated back to the user.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN - TRUE if access is allowed and FALSE otherwise
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ACCESS_MASK GrantedAccess = 0;
|
|||
|
BOOLEAN AccessAllowed;
|
|||
|
BOOLEAN MemoryAllocated;
|
|||
|
NTSTATUS Status;
|
|||
|
PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
|
|||
|
POBJECT_HEADER ObjectHeader;
|
|||
|
POBJECT_TYPE ObjectType;
|
|||
|
PPRIVILEGE_SET Privileges = NULL;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
|||
|
ObjectType = ObjectHeader->Type;
|
|||
|
|
|||
|
if (!TypeMutexLocked) {
|
|||
|
ObpEnterObjectTypeMutex( ObjectType );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Obtain the object's security descriptor
|
|||
|
//
|
|||
|
|
|||
|
Status = ObGetObjectSecurity(
|
|||
|
Object,
|
|||
|
&SecurityDescriptor,
|
|||
|
&MemoryAllocated
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
|
|||
|
if (!TypeMutexLocked) {
|
|||
|
ObpLeaveObjectTypeMutex( ObjectType );
|
|||
|
}
|
|||
|
|
|||
|
*AccessStatus = Status;
|
|||
|
return( FALSE );
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
if (SecurityDescriptor == NULL) {
|
|||
|
*AccessStatus = Status;
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Lock the caller's tokens until after auditing has been
|
|||
|
// performed.
|
|||
|
//
|
|||
|
|
|||
|
SeLockSubjectContext( &AccessState->SubjectSecurityContext );
|
|||
|
|
|||
|
AccessAllowed = SeAccessCheck (
|
|||
|
SecurityDescriptor,
|
|||
|
&AccessState->SubjectSecurityContext,
|
|||
|
TRUE, // Tokens are locked
|
|||
|
AccessState->RemainingDesiredAccess,
|
|||
|
AccessState->PreviouslyGrantedAccess,
|
|||
|
&Privileges,
|
|||
|
&ObjectType->TypeInfo.GenericMapping,
|
|||
|
AccessMode,
|
|||
|
&GrantedAccess,
|
|||
|
AccessStatus
|
|||
|
);
|
|||
|
|
|||
|
if (Privileges != NULL) {
|
|||
|
|
|||
|
Status = SeAppendPrivileges(
|
|||
|
AccessState,
|
|||
|
Privileges
|
|||
|
);
|
|||
|
|
|||
|
SeFreePrivileges( Privileges );
|
|||
|
}
|
|||
|
|
|||
|
if (AccessAllowed) {
|
|||
|
AccessState->PreviouslyGrantedAccess |= GrantedAccess;
|
|||
|
AccessState->RemainingDesiredAccess &= ~(GrantedAccess | MAXIMUM_ALLOWED);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Audit the attempt to open the object, audit
|
|||
|
// the creation of its handle later.
|
|||
|
//
|
|||
|
|
|||
|
if ( SecurityDescriptor != NULL ) {
|
|||
|
|
|||
|
SeOpenObjectAuditAlarm(
|
|||
|
&ObjectType->Name,
|
|||
|
Object,
|
|||
|
NULL, // AbsoluteObjectName
|
|||
|
SecurityDescriptor,
|
|||
|
AccessState,
|
|||
|
FALSE, // ObjectCreated (FALSE, only open here)
|
|||
|
AccessAllowed,
|
|||
|
AccessMode,
|
|||
|
&AccessState->GenerateOnClose
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
SeUnlockSubjectContext( &AccessState->SubjectSecurityContext );
|
|||
|
|
|||
|
if (!TypeMutexLocked) {
|
|||
|
ObpLeaveObjectTypeMutex( ObjectType );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// BUGBUG why is this check necessary?
|
|||
|
//
|
|||
|
|
|||
|
if (SecurityDescriptor != NULL) {
|
|||
|
|
|||
|
ObReleaseObjectSecurity(
|
|||
|
SecurityDescriptor,
|
|||
|
MemoryAllocated
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
return( AccessAllowed );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
ObpCheckObjectReference(
|
|||
|
IN PVOID Object,
|
|||
|
IN OUT PACCESS_STATE AccessState,
|
|||
|
IN BOOLEAN TypeMutexLocked,
|
|||
|
IN KPROCESSOR_MODE AccessMode,
|
|||
|
OUT PNTSTATUS AccessStatus
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
The routine performs access validation on the passed object. The
|
|||
|
remaining desired access mask is extracted from the AccessState
|
|||
|
parameter and passes to the appropriate security routine to
|
|||
|
perform the access check.
|
|||
|
|
|||
|
If the access attempt is successful, SeAccessCheck returns a mask
|
|||
|
containing the granted accesses. The bits in this mask are turned
|
|||
|
on in the PreviouslyGrantedAccess field of the AccessState, and
|
|||
|
are turned off in the RemainingDesiredAccess field.
|
|||
|
|
|||
|
This routine differs from ObpCheckObjectAccess in that it calls
|
|||
|
a different audit routine.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Object - The object being examined.
|
|||
|
|
|||
|
AccessState - The ACCESS_STATE structure containing accumulated
|
|||
|
information about the current attempt to gain access to the object.
|
|||
|
|
|||
|
TypeMutexLocked - Indicates whether the type mutex for this object's
|
|||
|
type is locked. The type mutex is used to protect the object's
|
|||
|
security descriptor from being modified while it is being accessed.
|
|||
|
|
|||
|
AccessMode - The previous processor mode.
|
|||
|
|
|||
|
AccessStatus - Pointer to a variable to return the status code of the
|
|||
|
access attempt. In the case of failure this status code must be
|
|||
|
propagated back to the user.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN - TRUE if access is allowed and FALSE otherwise
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
BOOLEAN AccessAllowed;
|
|||
|
ACCESS_MASK GrantedAccess = 0;
|
|||
|
BOOLEAN MemoryAllocated;
|
|||
|
PSECURITY_DESCRIPTOR SecurityDescriptor;
|
|||
|
NTSTATUS Status;
|
|||
|
POBJECT_HEADER ObjectHeader;
|
|||
|
POBJECT_TYPE ObjectType;
|
|||
|
PPRIVILEGE_SET Privileges = NULL;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
|||
|
ObjectType = ObjectHeader->Type;
|
|||
|
|
|||
|
if (!TypeMutexLocked) {
|
|||
|
ObpEnterObjectTypeMutex( ObjectType );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Obtain the object's security descriptor
|
|||
|
//
|
|||
|
|
|||
|
Status = ObGetObjectSecurity(
|
|||
|
Object,
|
|||
|
&SecurityDescriptor,
|
|||
|
&MemoryAllocated
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
if (!TypeMutexLocked) {
|
|||
|
ObpLeaveObjectTypeMutex( ObjectType );
|
|||
|
}
|
|||
|
*AccessStatus = Status;
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Lock the caller's tokens until after auditing has been
|
|||
|
// performed.
|
|||
|
//
|
|||
|
|
|||
|
SeLockSubjectContext( &AccessState->SubjectSecurityContext );
|
|||
|
|
|||
|
AccessAllowed = SeAccessCheck (
|
|||
|
SecurityDescriptor,
|
|||
|
&AccessState->SubjectSecurityContext,
|
|||
|
TRUE, // Tokens are locked
|
|||
|
AccessState->RemainingDesiredAccess,
|
|||
|
AccessState->PreviouslyGrantedAccess,
|
|||
|
&Privileges,
|
|||
|
&ObjectType->TypeInfo.GenericMapping,
|
|||
|
AccessMode,
|
|||
|
&GrantedAccess,
|
|||
|
AccessStatus
|
|||
|
);
|
|||
|
|
|||
|
if (AccessAllowed) {
|
|||
|
AccessState->PreviouslyGrantedAccess |= GrantedAccess;
|
|||
|
AccessState->RemainingDesiredAccess &= ~GrantedAccess;
|
|||
|
}
|
|||
|
|
|||
|
if ( SecurityDescriptor != NULL ) {
|
|||
|
|
|||
|
SeObjectReferenceAuditAlarm(
|
|||
|
&AccessState->OperationID,
|
|||
|
Object,
|
|||
|
SecurityDescriptor,
|
|||
|
&AccessState->SubjectSecurityContext,
|
|||
|
AccessState->RemainingDesiredAccess | AccessState->PreviouslyGrantedAccess,
|
|||
|
((PAUX_ACCESS_DATA)(AccessState->AuxData))->PrivilegesUsed,
|
|||
|
AccessAllowed,
|
|||
|
AccessMode
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
SeUnlockSubjectContext( &AccessState->SubjectSecurityContext );
|
|||
|
|
|||
|
if (!TypeMutexLocked) {
|
|||
|
ObpLeaveObjectTypeMutex( ObjectType );
|
|||
|
}
|
|||
|
|
|||
|
if (SecurityDescriptor != NULL) {
|
|||
|
ObReleaseObjectSecurity(
|
|||
|
SecurityDescriptor,
|
|||
|
MemoryAllocated
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
return( AccessAllowed );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
ObpCheckTraverseAccess(
|
|||
|
IN PVOID DirectoryObject,
|
|||
|
IN ACCESS_MASK TraverseAccess,
|
|||
|
IN PACCESS_STATE AccessState OPTIONAL,
|
|||
|
IN BOOLEAN TypeMutexLocked,
|
|||
|
IN KPROCESSOR_MODE PreviousMode,
|
|||
|
OUT PNTSTATUS AccessStatus
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine checks for traverse access to the given directory object.
|
|||
|
|
|||
|
Note that the contents of the AccessState structure are not
|
|||
|
modified, since it is assumed that this access check is incidental
|
|||
|
to another access operation.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DirectoryObject - The object header of the object being examined.
|
|||
|
|
|||
|
AccessState - Checks for traverse access will typically be incidental
|
|||
|
to some other access attempt. Information on the current state of
|
|||
|
that access attempt is required so that the constituent access
|
|||
|
attempts may be associated with each other in the audit log.
|
|||
|
This is an OPTIONAL parameter, in which case the call will
|
|||
|
success ONLY if the Directory Object grants World traverse
|
|||
|
access rights.
|
|||
|
|
|||
|
TypeMutexLocked - Indicates whether the type mutex for this object's
|
|||
|
type is locked. The type mutex is used to protect the object's
|
|||
|
security descriptor from being modified while it is being accessed.
|
|||
|
|
|||
|
AccessMode - The previous processor mode.
|
|||
|
|
|||
|
AccessStatus - Pointer to a variable to return the status code of the
|
|||
|
access attempt. In the case of failure this status code must be
|
|||
|
propagated back to the user.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN - TRUE if access is allowed and FALSE otherwise. AccessStatus
|
|||
|
contains the status code to be passed back to the caller. It is not
|
|||
|
correct to simply pass back STATUS_ACCESS_DENIED, since this will have
|
|||
|
to change with the advent of mandatory access control.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
BOOLEAN AccessAllowed;
|
|||
|
ACCESS_MASK GrantedAccess = 0;
|
|||
|
PSECURITY_DESCRIPTOR SecurityDescriptor;
|
|||
|
BOOLEAN MemoryAllocated;
|
|||
|
NTSTATUS Status;
|
|||
|
POBJECT_HEADER ObjectHeader;
|
|||
|
POBJECT_TYPE ObjectType;
|
|||
|
BOOLEAN SubjectContextLocked = FALSE;
|
|||
|
PPRIVILEGE_SET Privileges = NULL;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( DirectoryObject );
|
|||
|
ObjectType = ObjectHeader->Type;
|
|||
|
|
|||
|
if (!TypeMutexLocked) {
|
|||
|
ObpEnterObjectTypeMutex( ObjectType );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Obtain the object's security descriptor
|
|||
|
//
|
|||
|
|
|||
|
Status = ObGetObjectSecurity(
|
|||
|
DirectoryObject,
|
|||
|
&SecurityDescriptor,
|
|||
|
&MemoryAllocated
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
if (!TypeMutexLocked) {
|
|||
|
ObpLeaveObjectTypeMutex( ObjectType );
|
|||
|
}
|
|||
|
*AccessStatus = Status;
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check to see if WORLD has TRAVERSE access
|
|||
|
//
|
|||
|
|
|||
|
if ( !SeFastTraverseCheck(
|
|||
|
SecurityDescriptor,
|
|||
|
DIRECTORY_TRAVERSE,
|
|||
|
PreviousMode
|
|||
|
) ) {
|
|||
|
|
|||
|
//
|
|||
|
// SeFastTraverseCheck could be modified to tell us that
|
|||
|
// no one has any access to this directory. However,
|
|||
|
// we're going to have to fail this entire call if
|
|||
|
// that is the case, so we really don't need to worry
|
|||
|
// all that much about making it blindingly fast.
|
|||
|
//
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT( AccessState )) {
|
|||
|
SeLockSubjectContext( &AccessState->SubjectSecurityContext );
|
|||
|
SubjectContextLocked = TRUE;
|
|||
|
|
|||
|
AccessAllowed = SeAccessCheck(
|
|||
|
SecurityDescriptor,
|
|||
|
&AccessState->SubjectSecurityContext,
|
|||
|
TRUE, // Tokens are locked
|
|||
|
TraverseAccess,
|
|||
|
0,
|
|||
|
&Privileges,
|
|||
|
&ObjectType->TypeInfo.GenericMapping,
|
|||
|
PreviousMode,
|
|||
|
&GrantedAccess,
|
|||
|
AccessStatus
|
|||
|
);
|
|||
|
|
|||
|
if (Privileges != NULL) {
|
|||
|
|
|||
|
Status = SeAppendPrivileges(
|
|||
|
AccessState,
|
|||
|
Privileges
|
|||
|
);
|
|||
|
|
|||
|
SeFreePrivileges( Privileges );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
AccessAllowed = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
if ( SubjectContextLocked ) {
|
|||
|
SeUnlockSubjectContext( &AccessState->SubjectSecurityContext );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if (!TypeMutexLocked) {
|
|||
|
ObpLeaveObjectTypeMutex( ObjectType );
|
|||
|
}
|
|||
|
|
|||
|
if ( SecurityDescriptor != NULL ) {
|
|||
|
|
|||
|
ObReleaseObjectSecurity(
|
|||
|
SecurityDescriptor,
|
|||
|
MemoryAllocated
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
return( AccessAllowed );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
ObCheckCreateObjectAccess(
|
|||
|
IN PVOID DirectoryObject,
|
|||
|
IN ACCESS_MASK CreateAccess,
|
|||
|
IN PACCESS_STATE AccessState,
|
|||
|
IN PUNICODE_STRING ComponentName,
|
|||
|
IN BOOLEAN TypeMutexLocked,
|
|||
|
IN KPROCESSOR_MODE PreviousMode,
|
|||
|
OUT PNTSTATUS AccessStatus
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine checks to see if we are allowed to create an object in the
|
|||
|
given directory, and performs auditing as appropriate.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DirectoryObject - The directory object being examined.
|
|||
|
|
|||
|
CreateAccess - The access mask corresponding to create access for
|
|||
|
this directory type.
|
|||
|
|
|||
|
AccessState - Checks for traverse access will typically be incidental
|
|||
|
to some other access attempt. Information on the current state of
|
|||
|
that access attempt is required so that the constituent access
|
|||
|
attempts may be associated with each other in the audit log.
|
|||
|
|
|||
|
ComponentName - Pointer to a Unicode string containing the name of
|
|||
|
the object being created.
|
|||
|
|
|||
|
TypeMutexLocked - Indicates whether the type mutex for this object's
|
|||
|
type is locked. The type mutex is used to protect the object's
|
|||
|
security descriptor from being modified while it is being accessed.
|
|||
|
|
|||
|
AccessMode - The previous processor mode.
|
|||
|
|
|||
|
AccessStatus - Pointer to a variable to return the status code of the
|
|||
|
access attempt. In the case of failure this status code must be
|
|||
|
propagated back to the user.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN - TRUE if access is allowed and FALSE otherwise. AccessStatus
|
|||
|
contains the status code to be passed back to the caller. It is not
|
|||
|
correct to simply pass back STATUS_ACCESS_DENIED, since this will have
|
|||
|
to change with the advent of mandatory access control.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
BOOLEAN AccessAllowed;
|
|||
|
ACCESS_MASK GrantedAccess = 0;
|
|||
|
PSECURITY_DESCRIPTOR SecurityDescriptor;
|
|||
|
BOOLEAN MemoryAllocated;
|
|||
|
NTSTATUS Status;
|
|||
|
POBJECT_HEADER ObjectHeader;
|
|||
|
POBJECT_TYPE ObjectType;
|
|||
|
PPRIVILEGE_SET Privileges = NULL;
|
|||
|
BOOLEAN AuditPerformed = FALSE;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( DirectoryObject );
|
|||
|
ObjectType = ObjectHeader->Type;
|
|||
|
|
|||
|
if (!TypeMutexLocked) {
|
|||
|
ObpEnterObjectTypeMutex( ObjectType );
|
|||
|
}
|
|||
|
|
|||
|
Status = ObGetObjectSecurity(
|
|||
|
DirectoryObject,
|
|||
|
&SecurityDescriptor,
|
|||
|
&MemoryAllocated
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
if (!TypeMutexLocked) {
|
|||
|
ObpLeaveObjectTypeMutex( ObjectType );
|
|||
|
}
|
|||
|
*AccessStatus = Status;
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
|
|||
|
SeLockSubjectContext( &AccessState->SubjectSecurityContext );
|
|||
|
|
|||
|
if (SecurityDescriptor != NULL) {
|
|||
|
|
|||
|
AccessAllowed = SeAccessCheck (
|
|||
|
SecurityDescriptor,
|
|||
|
&AccessState->SubjectSecurityContext,
|
|||
|
TRUE, // Tokens are locked
|
|||
|
CreateAccess,
|
|||
|
0,
|
|||
|
&Privileges,
|
|||
|
&ObjectType->TypeInfo.GenericMapping,
|
|||
|
PreviousMode,
|
|||
|
&GrantedAccess,
|
|||
|
AccessStatus
|
|||
|
);
|
|||
|
|
|||
|
if (Privileges != NULL) {
|
|||
|
|
|||
|
Status = SeAppendPrivileges(
|
|||
|
AccessState,
|
|||
|
Privileges
|
|||
|
);
|
|||
|
|
|||
|
SeFreePrivileges( Privileges );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// This is wrong, but leave for reference.
|
|||
|
//
|
|||
|
|
|||
|
// if (AccessAllowed) {
|
|||
|
// AccessState->PreviouslyGrantedAccess |= GrantedAccess;
|
|||
|
// AccessState->RemainingDesiredAccess &= ~GrantedAccess;
|
|||
|
// }
|
|||
|
|
|||
|
#if 0
|
|||
|
|
|||
|
SeCreateObjectAuditAlarm(
|
|||
|
&AccessState->OperationID,
|
|||
|
DirectoryObject,
|
|||
|
ComponentName,
|
|||
|
SecurityDescriptor,
|
|||
|
&AccessState->SubjectSecurityContext,
|
|||
|
CreateAccess,
|
|||
|
AccessState->PrivilegesUsed,
|
|||
|
AccessAllowed,
|
|||
|
&AuditPerformed,
|
|||
|
PreviousMode
|
|||
|
);
|
|||
|
|
|||
|
if ( AuditPerformed ) {
|
|||
|
|
|||
|
AccessState->AuditHandleCreation = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
else {
|
|||
|
|
|||
|
AccessAllowed = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
SeUnlockSubjectContext( &AccessState->SubjectSecurityContext );
|
|||
|
|
|||
|
if (!TypeMutexLocked) {
|
|||
|
|
|||
|
ObpLeaveObjectTypeMutex( ObjectType );
|
|||
|
}
|
|||
|
|
|||
|
ObReleaseObjectSecurity(
|
|||
|
SecurityDescriptor,
|
|||
|
MemoryAllocated
|
|||
|
);
|
|||
|
|
|||
|
return( AccessAllowed );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ObAssignObjectSecurityDescriptor(
|
|||
|
IN PVOID Object,
|
|||
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL,
|
|||
|
IN POOL_TYPE PoolType
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Takes a pointer to an object and sets the SecurityDescriptor field
|
|||
|
in the object's header.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Object - Supplies a pointer to the object
|
|||
|
|
|||
|
SecurityDescriptor - Supplies a pointer to the security descriptor
|
|||
|
to be assigned to the object. This pointer may be null if there
|
|||
|
is no security on the object.
|
|||
|
|
|||
|
PoolType - Supplies the type of pool memory used to allocate the
|
|||
|
security descriptor.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
PSECURITY_DESCRIPTOR OutputSecurityDescriptor;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
if (!ARGUMENT_PRESENT(SecurityDescriptor)) {
|
|||
|
|
|||
|
OBJECT_TO_OBJECT_HEADER( Object )->SecurityDescriptor = NULL;
|
|||
|
return( STATUS_SUCCESS );
|
|||
|
}
|
|||
|
|
|||
|
Status = ObpLogSecurityDescriptor( SecurityDescriptor, &OutputSecurityDescriptor );
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
OBJECT_TO_OBJECT_HEADER( Object )->SecurityDescriptor = OutputSecurityDescriptor;
|
|||
|
}
|
|||
|
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ObGetObjectSecurity(
|
|||
|
IN PVOID Object,
|
|||
|
OUT PSECURITY_DESCRIPTOR *SecurityDescriptor,
|
|||
|
OUT PBOOLEAN MemoryAllocated
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Given an object, this routine will find its security descriptor.
|
|||
|
It will do this by calling the object's security method.
|
|||
|
|
|||
|
It is possible for an object not to have a security descriptor
|
|||
|
at all. Unnamed objects such as events that can only be referenced
|
|||
|
by a handle are an example of an object that does not have a
|
|||
|
security descriptor.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Object - Supplies the object being queried.
|
|||
|
|
|||
|
SecurityDescriptor - Returns a pointer to the object's security
|
|||
|
descriptor.
|
|||
|
|
|||
|
MemoryAllocated - indicates whether we had to allocate pool
|
|||
|
memory to hold the security descriptor or not. This should
|
|||
|
be passed back into ObReleaseObjectSecurity.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - The operation was successful. Note that the
|
|||
|
operation may be successful and still return a NULL security
|
|||
|
descriptor.
|
|||
|
|
|||
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient memory was available
|
|||
|
to satisfy the request.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
SECURITY_INFORMATION SecurityInformation;
|
|||
|
ULONG Length = 0;
|
|||
|
NTSTATUS Status;
|
|||
|
POBJECT_TYPE ObjectType;
|
|||
|
POBJECT_HEADER ObjectHeader;
|
|||
|
KIRQL SaveIrql;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
|||
|
ObjectType = ObjectHeader->Type;
|
|||
|
|
|||
|
//
|
|||
|
// If the object is one that uses the default object method,
|
|||
|
// its security descriptor is contained in ob's security
|
|||
|
// descriptor cache.
|
|||
|
//
|
|||
|
// Reference it so that it doesn't go away out from under us.
|
|||
|
//
|
|||
|
|
|||
|
if (ObpCentralizedSecurity(ObjectType)) {
|
|||
|
*SecurityDescriptor = ObpReferenceSecurityDescriptor( Object );
|
|||
|
*MemoryAllocated = FALSE;
|
|||
|
return( STATUS_SUCCESS );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Request a complete security descriptor
|
|||
|
//
|
|||
|
|
|||
|
SecurityInformation = OWNER_SECURITY_INFORMATION |
|
|||
|
GROUP_SECURITY_INFORMATION |
|
|||
|
DACL_SECURITY_INFORMATION |
|
|||
|
SACL_SECURITY_INFORMATION;
|
|||
|
|
|||
|
//
|
|||
|
// Call the security method with Length = 0 to find out
|
|||
|
// how much memory we need to store the final result.
|
|||
|
//
|
|||
|
// Note that the ObjectsSecurityDescriptor parameter is NULL,
|
|||
|
// because we expect whoever is on the other end of this call
|
|||
|
// to find the security descriptor for us. We pass in a pool
|
|||
|
// type to keep the compiler happy, it will not be used for a
|
|||
|
// query operation.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
ObpBeginTypeSpecificCallOut( SaveIrql );
|
|||
|
Status = (*ObjectType->TypeInfo.SecurityProcedure)(
|
|||
|
Object,
|
|||
|
QuerySecurityDescriptor,
|
|||
|
&SecurityInformation,
|
|||
|
*SecurityDescriptor,
|
|||
|
&Length,
|
|||
|
&ObjectHeader->SecurityDescriptor, // not used
|
|||
|
ObjectType->TypeInfo.PoolType,
|
|||
|
&ObjectType->TypeInfo.GenericMapping
|
|||
|
);
|
|||
|
ObpEndTypeSpecificCallOut( SaveIrql, "Security", ObjectType, Object );
|
|||
|
|
|||
|
if (Status != STATUS_BUFFER_TOO_SMALL) {
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
*SecurityDescriptor = ExAllocatePoolWithTag( PagedPool, Length, 'qSbO' );
|
|||
|
if (*SecurityDescriptor == NULL) {
|
|||
|
return( STATUS_INSUFFICIENT_RESOURCES );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
*MemoryAllocated = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// The security method will return an absolute format
|
|||
|
// security descriptor that just happens to be in a self
|
|||
|
// contained buffer (not to be confused with a self-relative
|
|||
|
// security descriptor).
|
|||
|
//
|
|||
|
|
|||
|
ObpBeginTypeSpecificCallOut( SaveIrql );
|
|||
|
Status = (*ObjectType->TypeInfo.SecurityProcedure)(
|
|||
|
Object,
|
|||
|
QuerySecurityDescriptor,
|
|||
|
&SecurityInformation,
|
|||
|
*SecurityDescriptor,
|
|||
|
&Length,
|
|||
|
&ObjectHeader->SecurityDescriptor,
|
|||
|
ObjectType->TypeInfo.PoolType,
|
|||
|
&ObjectType->TypeInfo.GenericMapping
|
|||
|
);
|
|||
|
ObpEndTypeSpecificCallOut( SaveIrql, "Security", ObjectType, Object );
|
|||
|
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
ExFreePool( *SecurityDescriptor );
|
|||
|
*MemoryAllocated = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
return( Status );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
ObReleaseObjectSecurity(
|
|||
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|||
|
IN BOOLEAN MemoryAllocated
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function will free up any memory associated with a queried
|
|||
|
security descriptor.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
SecurityDescriptor - Supplies a pointer to the security descriptor
|
|||
|
to be freed.
|
|||
|
|
|||
|
MemoryAllocated - Supplies whether or not we should free the
|
|||
|
memory pointed to by SecurityDescriptor.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
if ( SecurityDescriptor != NULL ) {
|
|||
|
if (MemoryAllocated) {
|
|||
|
ExFreePool( SecurityDescriptor );
|
|||
|
} else {
|
|||
|
ObpDereferenceSecurityDescriptor( SecurityDescriptor );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ObValidateSecurityQuota(
|
|||
|
IN PVOID Object,
|
|||
|
IN ULONG NewSize
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine will check to see if the new security information
|
|||
|
is larger than is allowed by the object's pre-allocated quota.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Object - Supplies a pointer to the object whose information is to be
|
|||
|
modified.
|
|||
|
|
|||
|
NewSize - Supplies the size of the proposed new security
|
|||
|
information.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - New size is within alloted quota.
|
|||
|
|
|||
|
STATUS_QUOTA_EXCEEDED - The desired adjustment would have exceeded
|
|||
|
the permitted security quota for this object.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
POBJECT_HEADER ObjectHeader;
|
|||
|
POBJECT_HEADER_QUOTA_INFO QuotaInfo;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
|||
|
QuotaInfo = OBJECT_HEADER_TO_QUOTA_INFO( ObjectHeader );
|
|||
|
|
|||
|
if (QuotaInfo == NULL && NewSize > SE_DEFAULT_SECURITY_QUOTA) {
|
|||
|
if (!(ObjectHeader->Flags & OB_FLAG_DEFAULT_SECURITY_QUOTA)) {
|
|||
|
//
|
|||
|
// Should really charge quota here.
|
|||
|
//
|
|||
|
|
|||
|
return( STATUS_SUCCESS );
|
|||
|
}
|
|||
|
|
|||
|
return( STATUS_QUOTA_EXCEEDED );
|
|||
|
}
|
|||
|
else
|
|||
|
if (QuotaInfo != NULL && NewSize > QuotaInfo->SecurityDescriptorCharge) {
|
|||
|
if (QuotaInfo->SecurityDescriptorCharge == 0) {
|
|||
|
//
|
|||
|
// Should really charge quota here.
|
|||
|
//
|
|||
|
|
|||
|
// QuotaInfo->SecurityDescriptorCharge = SeComputeSecurityQuota( NewSize );
|
|||
|
return( STATUS_SUCCESS );
|
|||
|
}
|
|||
|
|
|||
|
return( STATUS_QUOTA_EXCEEDED );
|
|||
|
}
|
|||
|
else {
|
|||
|
return( STATUS_SUCCESS );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ObAssignSecurity(
|
|||
|
IN PACCESS_STATE AccessState,
|
|||
|
IN PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL,
|
|||
|
IN PVOID Object,
|
|||
|
IN POBJECT_TYPE ObjectType
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine will assign a security descriptor to a newly created object.
|
|||
|
It assumes that the AccessState parameter contains a captured security
|
|||
|
descriptor.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
AccessState - The AccessState containing the security information
|
|||
|
for this object creation.
|
|||
|
|
|||
|
ParentDescriptor - The security descriptor from the parent object, if
|
|||
|
available.
|
|||
|
|
|||
|
IsDirectoryObject - A boolean indicating if this is a directory object
|
|||
|
or not.
|
|||
|
|
|||
|
Object - A pointer to the object being created.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - indicates the operation was successful.
|
|||
|
|
|||
|
STATUS_INVALID_OWNER - The owner SID provided as the owner of the
|
|||
|
target security descriptor is not one the caller is authorized
|
|||
|
to assign as the owner of an object.
|
|||
|
|
|||
|
STATUS_PRIVILEGE_NOT_HELD - The caller does not have the privilege
|
|||
|
necessary to explicitly assign the specified system ACL.
|
|||
|
SeSecurityPrivilege privilege is needed to explicitly assign
|
|||
|
system ACLs to objects.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PSECURITY_DESCRIPTOR NewDescriptor = NULL;
|
|||
|
NTSTATUS Status;
|
|||
|
KIRQL SaveIrql;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Assign construct the final version of the security
|
|||
|
// descriptor and pass it to the security method to be
|
|||
|
// assigned to the object.
|
|||
|
//
|
|||
|
|
|||
|
Status = SeAssignSecurity (
|
|||
|
ParentDescriptor,
|
|||
|
AccessState->SecurityDescriptor,
|
|||
|
&NewDescriptor,
|
|||
|
(BOOLEAN)(ObjectType == ObpDirectoryObjectType),
|
|||
|
&AccessState->SubjectSecurityContext,
|
|||
|
&ObjectType->TypeInfo.GenericMapping,
|
|||
|
PagedPool
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
ObpBeginTypeSpecificCallOut( SaveIrql );
|
|||
|
Status = (*ObjectType->TypeInfo.SecurityProcedure)(
|
|||
|
Object,
|
|||
|
AssignSecurityDescriptor,
|
|||
|
NULL,
|
|||
|
NewDescriptor,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
PagedPool,
|
|||
|
&ObjectType->TypeInfo.GenericMapping
|
|||
|
);
|
|||
|
ObpEndTypeSpecificCallOut( SaveIrql, "Security", ObjectType, Object );
|
|||
|
|
|||
|
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
|
|||
|
//
|
|||
|
// The attempt to assign the security descriptor to the object
|
|||
|
// failed. Free the space used by the new security descriptor.
|
|||
|
//
|
|||
|
|
|||
|
SeDeassignSecurity( &NewDescriptor );
|
|||
|
}
|
|||
|
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ObSetSecurityDescriptorInfo(
|
|||
|
IN PVOID Object,
|
|||
|
IN PSECURITY_INFORMATION SecurityInformation,
|
|||
|
IN OUT PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|||
|
IN OUT PSECURITY_DESCRIPTOR *ObjectsSecurityDescriptor,
|
|||
|
IN POOL_TYPE PoolType,
|
|||
|
IN PGENERIC_MAPPING GenericMapping
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Sets the security descriptor on an already secure object.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Object - Pointer to the object being modified.
|
|||
|
|
|||
|
SecurityInformation - Describes which information in the SecurityDescriptor parameter
|
|||
|
is relevent.
|
|||
|
|
|||
|
SecurityDescriptor - Provides the new security information.
|
|||
|
|
|||
|
ObjectsSecurityDescriptor - Provides/returns the object's security descriptor.
|
|||
|
|
|||
|
PoolType - The pool the ObjectSecurityDescriptor is allocated from.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
return-value - Description of conditions needed to return value. - or -
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PSECURITY_DESCRIPTOR OldDescriptor = *ObjectsSecurityDescriptor;
|
|||
|
PSECURITY_DESCRIPTOR NewDescriptor = OldDescriptor;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Check the rest of our input and call the default set security
|
|||
|
// method. Also make sure no one is modifying the security descriptor
|
|||
|
// while we're looking at it.
|
|||
|
//
|
|||
|
|
|||
|
ObpAcquireDescriptorCacheReadLock();
|
|||
|
|
|||
|
Status = SeSetSecurityDescriptorInfo( Object,
|
|||
|
SecurityInformation,
|
|||
|
SecurityDescriptor,
|
|||
|
&NewDescriptor,
|
|||
|
PoolType,
|
|||
|
GenericMapping
|
|||
|
);
|
|||
|
|
|||
|
ObpReleaseDescriptorCacheLock();
|
|||
|
|
|||
|
if ( NT_SUCCESS( Status )) {
|
|||
|
|
|||
|
Status = ObpLogSecurityDescriptor(
|
|||
|
NewDescriptor,
|
|||
|
ObjectsSecurityDescriptor
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Now if the object is an object directory object that
|
|||
|
// participated in snapped symbolic links. If so and the
|
|||
|
// new security on the object does NOT allow world traverse
|
|||
|
// access, then return an error, as it is too late to change
|
|||
|
// the security on the object directory at this point.
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS( Status ) &&
|
|||
|
OBJECT_TO_OBJECT_HEADER( Object )->Type == ObpDirectoryObjectType &&
|
|||
|
((POBJECT_DIRECTORY)Object)->SymbolicLinkUsageCount != 0 &&
|
|||
|
!SeFastTraverseCheck( *ObjectsSecurityDescriptor,
|
|||
|
DIRECTORY_TRAVERSE,
|
|||
|
UserMode
|
|||
|
)
|
|||
|
) {
|
|||
|
KdPrint(( "OB: Failing attempt the remove world traverse access from object directory\n" ));
|
|||
|
Status = STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
if ( NT_SUCCESS( Status )) {
|
|||
|
|
|||
|
//
|
|||
|
// Dereference old SecurityDescriptor and insert new one
|
|||
|
//
|
|||
|
|
|||
|
ObpDereferenceSecurityDescriptor( OldDescriptor );
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// We failed logging the new security descriptor.
|
|||
|
// Clean up and fail the entire operation.
|
|||
|
//
|
|||
|
|
|||
|
ExFreePool( NewDescriptor );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ObpValidateAccessMask(
|
|||
|
PACCESS_STATE AccessState
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Checks the desired access mask of a passed object against the
|
|||
|
passed security descriptor.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
AccessState - A pointer to the AccessState for the pending operation.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
SECURITY_DESCRIPTOR *SecurityDescriptor = AccessState->SecurityDescriptor;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
if (SecurityDescriptor != NULL) {
|
|||
|
|
|||
|
if ( SecurityDescriptor->Control & SE_SACL_PRESENT ) {
|
|||
|
|
|||
|
if ( !(AccessState->PreviouslyGrantedAccess & ACCESS_SYSTEM_SECURITY)) {
|
|||
|
|
|||
|
AccessState->RemainingDesiredAccess |= ACCESS_SYSTEM_SECURITY;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return( STATUS_SUCCESS );
|
|||
|
}
|