1925 lines
50 KiB
C
1925 lines
50 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
Seassign.c
|
||
|
||
Abstract:
|
||
|
||
This Module implements the SeAssignSecurity procedure. For a description
|
||
of the pool allocation strategy please see the comments in semethod.c
|
||
|
||
Author:
|
||
|
||
Gary Kimura (GaryKi) 9-Nov-1989
|
||
|
||
Environment:
|
||
|
||
Kernel Mode
|
||
|
||
Revision History:
|
||
|
||
Richard Ward (RichardW) 14-April-92
|
||
Robert Reichel (RobertRe) 28-February-95
|
||
Added Compound ACEs
|
||
|
||
--*/
|
||
|
||
|
||
#include "sep.h"
|
||
#include "tokenp.h"
|
||
#include "sertlp.h"
|
||
#include "zwapi.h"
|
||
|
||
|
||
|
||
//
|
||
// Local macros and procedures
|
||
//
|
||
|
||
//
|
||
// Macros to determine if an ACE contains one of the Creator SIDs
|
||
//
|
||
|
||
#define ContainsCreatorOwnerSid(Ace) ( \
|
||
RtlEqualSid( &((PKNOWN_ACE)( Ace ))->SidStart, SeCreatorOwnerSid ) \
|
||
)
|
||
|
||
#define ContainsCreatorGroupSid(Ace) ( \
|
||
RtlEqualSid( &((PKNOWN_ACE)( Ace ))->SidStart, SeCreatorGroupSid ) \
|
||
)
|
||
|
||
|
||
|
||
VOID
|
||
SepApplyAclToObject (
|
||
IN PACL Acl,
|
||
IN PGENERIC_MAPPING GenericMapping
|
||
);
|
||
|
||
NTSTATUS
|
||
SepInheritAcl (
|
||
IN PACL Acl,
|
||
IN BOOLEAN IsDirectoryObject,
|
||
IN PSID OwnerSid,
|
||
IN PSID GroupSid,
|
||
IN PSID ServerSid OPTIONAL,
|
||
IN PSID ClientSid OPTIONAL,
|
||
IN PGENERIC_MAPPING GenericMapping,
|
||
IN POOL_TYPE PoolType,
|
||
OUT PACL *NewAcl
|
||
);
|
||
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE,SeAssignSecurity)
|
||
#pragma alloc_text(PAGE,SeDeassignSecurity)
|
||
#pragma alloc_text(PAGE,SepApplyAclToObject)
|
||
#pragma alloc_text(PAGE,SepInheritAcl)
|
||
#pragma alloc_text(PAGE,SeAssignWorldSecurityDescriptor)
|
||
#pragma alloc_text(PAGE,SepDumpSecurityDescriptor)
|
||
#pragma alloc_text(PAGE,SepPrintAcl)
|
||
#pragma alloc_text(PAGE,SepPrintSid)
|
||
#pragma alloc_text(PAGE,SepDumpTokenInfo)
|
||
#pragma alloc_text(PAGE,SepSidTranslation)
|
||
#endif
|
||
|
||
|
||
//
|
||
// These variables control whether security descriptors and token
|
||
// information are dumped by their dump routines. This allows
|
||
// selective turning on and off of debugging output by both program
|
||
// control and via the kernel debugger.
|
||
//
|
||
|
||
#if DBG
|
||
|
||
BOOLEAN SepDumpSD = FALSE;
|
||
BOOLEAN SepDumpToken = FALSE;
|
||
|
||
#endif
|
||
|
||
|
||
NTSTATUS
|
||
SeAssignSecurity (
|
||
IN PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL,
|
||
IN PSECURITY_DESCRIPTOR ExplicitDescriptor OPTIONAL,
|
||
OUT PSECURITY_DESCRIPTOR *NewDescriptor,
|
||
IN BOOLEAN IsDirectoryObject,
|
||
IN PSECURITY_SUBJECT_CONTEXT SubjectContext,
|
||
IN PGENERIC_MAPPING GenericMapping,
|
||
IN POOL_TYPE PoolType
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine assumes privilege checking HAS NOT yet been performed
|
||
and so will be performed by this routine.
|
||
|
||
This procedure is used to build a security descriptor for a new object
|
||
given the security descriptor of its parent directory and any originally
|
||
requested security for the object. The final security descriptor
|
||
returned to the caller may contain a mix of information, some explicitly
|
||
provided other from the new object's parent.
|
||
|
||
System and Discretionary ACL Assignment
|
||
---------------------------------------
|
||
|
||
The assignment of system and discretionary ACLs is governed by the
|
||
logic illustrated in the following table (numbers in the cells refer
|
||
to comments in the code):
|
||
|
||
| Explicit | Explicit |
|
||
| (non-default) | Default | No
|
||
| Acl | Acl | Acl
|
||
| Specified | Specified | Specified
|
||
-------------+----------------+---------------+--------------
|
||
| (1)| (3)| (5)
|
||
Inheritable | Assign | Assign | Assign
|
||
Acl From | Specified | Inherited | Inherited
|
||
Parent | Acl | Acl | Acl
|
||
| | |
|
||
-------------+----------------+---------------+--------------
|
||
No | (2)| (4)| (6)
|
||
Inheritable | Assign | Assign | Assign
|
||
Acl From | Specified | Default | No Acl
|
||
Parent | Acl | Acl |
|
||
| | |
|
||
-------------+----------------+---------------+--------------
|
||
|
||
Note that an explicitly specified ACL, whether a default ACL or
|
||
not, may be empty or null.
|
||
|
||
If the caller is explicitly assigning a system acl, default or
|
||
non-default, the caller must either be a kernel mode client or
|
||
must be appropriately privileged.
|
||
|
||
|
||
Owner and Group Assignment
|
||
--------------------------
|
||
|
||
The assignment of the new object's owner and group is governed
|
||
by the following logic:
|
||
|
||
1) If the passed security descriptor includes an owner, it
|
||
is assigned as the new object's owner. Otherwise, the
|
||
caller's token is looked in for the owner. Within the
|
||
token, if there is a default owner, it is assigned.
|
||
Otherwise, the caller's user ID is assigned.
|
||
|
||
2) If the passed security descriptor includes a group, it
|
||
is assigned as the new object's group. Otherwise, the
|
||
caller's token is looked in for the group. Within the
|
||
token, if there is a default group, it is assigned.
|
||
Otherwise, the caller's primary group ID is assigned.
|
||
|
||
|
||
|
||
Arguments:
|
||
|
||
ParentDescriptor - Optionally supplies the security descriptor of the
|
||
parent directory under which this new object is being created.
|
||
|
||
ExplicitDescriptor - Supplies the address of a pointer to the security
|
||
descriptor as specified by the user that is to be applied to
|
||
the new object.
|
||
|
||
NewDescriptor - Returns the actual security descriptor for the new
|
||
object that has been modified according to above rules.
|
||
|
||
IsDirectoryObject - Specifies if the new object is itself a directory
|
||
object. A value of TRUE indicates the object is a container of other
|
||
objects.
|
||
|
||
SubjectContext - Supplies the security context of the subject creating the
|
||
object. This is used to retrieve default security information for the
|
||
new object, such as default owner, primary group, and discretionary
|
||
access control.
|
||
|
||
GenericMapping - Supplies a pointer to an array of access mask values
|
||
denoting the mapping between each generic right to non-generic rights.
|
||
|
||
PoolType - Specifies the pool type to use to when allocating a new
|
||
security descriptor.
|
||
|
||
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.
|
||
--*/
|
||
|
||
{
|
||
|
||
|
||
KPROCESSOR_MODE RequestorMode;
|
||
|
||
SECURITY_DESCRIPTOR *CapturedDescriptor;
|
||
SECURITY_DESCRIPTOR InCaseOneNotPassed;
|
||
BOOLEAN SecurityDescriptorPassed;
|
||
|
||
NTSTATUS Status;
|
||
|
||
BOOLEAN RequestorCanAssignDescriptor = TRUE;
|
||
|
||
PACL NewSacl = NULL;
|
||
BOOLEAN NewSaclPresent = FALSE;
|
||
BOOLEAN NewSaclInherited = FALSE;
|
||
|
||
PACL NewDacl = NULL;
|
||
BOOLEAN NewDaclPresent = FALSE;
|
||
BOOLEAN NewDaclInherited = FALSE;
|
||
|
||
PACL ServerDacl = NULL;
|
||
BOOLEAN ServerDaclAllocated = FALSE;
|
||
|
||
PSID NewOwner = NULL;
|
||
PSID NewGroup = NULL;
|
||
|
||
BOOLEAN CleanUp = FALSE;
|
||
BOOLEAN SaclExplicitlyAssigned = FALSE;
|
||
BOOLEAN DaclExplicitlyAssigned = FALSE;
|
||
BOOLEAN OwnerExplicitlyAssigned = FALSE;
|
||
|
||
BOOLEAN ServerObject;
|
||
BOOLEAN DaclUntrusted;
|
||
|
||
BOOLEAN HasPrivilege;
|
||
|
||
PSID SubjectContextOwner;
|
||
PSID SubjectContextGroup;
|
||
PSID SubjectContextServerOwner;
|
||
PSID SubjectContextServerGroup;
|
||
PACL SubjectContextDacl;
|
||
|
||
ULONG AllocationSize;
|
||
ULONG NewOwnerSize;
|
||
ULONG NewGroupSize;
|
||
ULONG NewSaclSize;
|
||
ULONG NewDaclSize;
|
||
|
||
PCHAR Field;
|
||
PCHAR Base;
|
||
|
||
PAGED_CODE();
|
||
|
||
PoolType = PagedPool;
|
||
|
||
//
|
||
// The desired end result is to build a self-relative security descriptor.
|
||
// This means that a single block of memory will be allocated and all
|
||
// security information copied into it. To minimize work along the way,
|
||
// it is desirable to reference (rather than copy) each field as we
|
||
// determine its source. This can not be done with inherited ACLs, however,
|
||
// since they must be built from another ACL. So, explicitly assigned
|
||
// and defaulted SIDs and ACLs are just referenced until they are copied
|
||
// into the self-relative descriptor. Inherited ACLs are built in a
|
||
// temporary buffer which must be deallocated after being copied to the
|
||
// self-relative descriptor.
|
||
//
|
||
|
||
|
||
//
|
||
// Get the previous mode of the caller
|
||
//
|
||
|
||
RequestorMode = KeGetPreviousMode();
|
||
|
||
//
|
||
// If a security descriptor has been passed, capture it, otherwise
|
||
// cobble up a fake one to simplify the code that follows.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(ExplicitDescriptor)) {
|
||
|
||
CapturedDescriptor = ExplicitDescriptor;
|
||
SecurityDescriptorPassed = TRUE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// No descriptor passed, make a fake one
|
||
//
|
||
|
||
SecurityDescriptorPassed = FALSE;
|
||
RtlCreateSecurityDescriptor((PSECURITY_DESCRIPTOR)&InCaseOneNotPassed,
|
||
SECURITY_DESCRIPTOR_REVISION);
|
||
CapturedDescriptor = &InCaseOneNotPassed;
|
||
|
||
}
|
||
|
||
#if DBG
|
||
SepDumpSecurityDescriptor( (PSECURITY_DESCRIPTOR)CapturedDescriptor,
|
||
"\nSeAssignSecurity: Input security descriptor = \n"
|
||
);
|
||
|
||
if (ARGUMENT_PRESENT( ParentDescriptor )) {
|
||
SepDumpSecurityDescriptor( (PSECURITY_DESCRIPTOR)ParentDescriptor,
|
||
"\nSeAssignSecurity: Parent security descriptor = \n"
|
||
);
|
||
}
|
||
#endif // DBG
|
||
//
|
||
// Grab pointers to the default owner, primary group, and
|
||
// discretionary ACL.
|
||
//
|
||
|
||
//
|
||
// Lock the subject context for read access so that the pointers
|
||
// we copy out of it don't disappear on us at random
|
||
//
|
||
|
||
SeLockSubjectContext( SubjectContext );
|
||
|
||
SepGetDefaultsSubjectContext(
|
||
SubjectContext,
|
||
&SubjectContextOwner,
|
||
&SubjectContextGroup,
|
||
&SubjectContextServerOwner,
|
||
&SubjectContextServerGroup,
|
||
&SubjectContextDacl
|
||
);
|
||
|
||
|
||
if ( CapturedDescriptor->Control & SE_SERVER_SECURITY ) {
|
||
ServerObject = TRUE;
|
||
} else {
|
||
ServerObject = FALSE;
|
||
}
|
||
|
||
if ( CapturedDescriptor->Control & SE_DACL_UNTRUSTED ) {
|
||
DaclUntrusted = TRUE;
|
||
} else {
|
||
DaclUntrusted = FALSE;
|
||
}
|
||
|
||
|
||
if (!CleanUp) {
|
||
|
||
//
|
||
// Establish System Acl
|
||
//
|
||
|
||
if ( (CapturedDescriptor->Control & SE_SACL_PRESENT) &&
|
||
!(CapturedDescriptor->Control & SE_SACL_DEFAULTED) ) {
|
||
|
||
//
|
||
// Explicitly provided, not defaulted (Cases 1 and 2)
|
||
//
|
||
|
||
NewSacl = SepSaclAddrSecurityDescriptor(CapturedDescriptor);
|
||
NewSaclPresent = TRUE;
|
||
SaclExplicitlyAssigned = TRUE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// See if there is an inheritable ACL (copy it if there is one.)
|
||
// This maps all ACEs for the target object type too.
|
||
//
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
if (ARGUMENT_PRESENT(ParentDescriptor) &&
|
||
NT_SUCCESS(Status = SepInheritAcl(
|
||
SepSaclAddrSecurityDescriptor(
|
||
((SECURITY_DESCRIPTOR *)ParentDescriptor)
|
||
),
|
||
IsDirectoryObject,
|
||
SubjectContextOwner,
|
||
SubjectContextGroup,
|
||
SubjectContextServerOwner,
|
||
SubjectContextServerGroup,
|
||
GenericMapping,
|
||
PoolType,
|
||
&NewSacl )
|
||
)) {
|
||
|
||
//
|
||
// There is an inheritable ACL from the parent. Assign
|
||
// it. (Cases 3 and 5)
|
||
//
|
||
|
||
NewSaclPresent = TRUE;
|
||
NewSaclInherited = TRUE;
|
||
|
||
} else if (!ARGUMENT_PRESENT(ParentDescriptor) || (Status == STATUS_NO_INHERITANCE)) {
|
||
|
||
//
|
||
// No inheritable ACL - check for a defaulted one
|
||
// (Cases 4 and 6)
|
||
//
|
||
|
||
if ( (CapturedDescriptor->Control & SE_SACL_PRESENT) &&
|
||
(CapturedDescriptor->Control & SE_SACL_DEFAULTED) ) {
|
||
|
||
//
|
||
// Reference the default ACL (case 4)
|
||
//
|
||
|
||
NewSacl = SepSaclAddrSecurityDescriptor(CapturedDescriptor);
|
||
NewSaclPresent = TRUE;
|
||
|
||
//
|
||
// Set SaclExplicitlyAssigned, because the caller
|
||
// must have SeSecurityPrivilege to do this. We
|
||
// will examine this flag and check for this privilege
|
||
// later.
|
||
//
|
||
|
||
SaclExplicitlyAssigned = TRUE;
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// Some unusual error occured
|
||
//
|
||
|
||
CleanUp = TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
if (!CleanUp) {
|
||
|
||
//
|
||
// Establish Discretionary Acl
|
||
//
|
||
|
||
if ( (CapturedDescriptor->Control & SE_DACL_PRESENT) &&
|
||
!(CapturedDescriptor->Control & SE_DACL_DEFAULTED) ) {
|
||
|
||
//
|
||
// Explicitly provided, not defaulted (Cases 1 and 2)
|
||
//
|
||
|
||
NewDacl = SepDaclAddrSecurityDescriptor(CapturedDescriptor);
|
||
NewDaclPresent = TRUE;
|
||
DaclExplicitlyAssigned = TRUE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// See if there is an inheritable ACL (copy it if there is one.)
|
||
// This maps the ACEs to the target object type too.
|
||
//
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
if (ARGUMENT_PRESENT(ParentDescriptor) &&
|
||
NT_SUCCESS(Status = SepInheritAcl(
|
||
SepDaclAddrSecurityDescriptor(
|
||
((SECURITY_DESCRIPTOR *)ParentDescriptor)
|
||
),
|
||
IsDirectoryObject,
|
||
SubjectContextOwner,
|
||
SubjectContextGroup,
|
||
SubjectContextServerOwner,
|
||
SubjectContextServerGroup,
|
||
GenericMapping,
|
||
PoolType,
|
||
&NewDacl )
|
||
)) {
|
||
|
||
//
|
||
// There is an inheritable ACL from the parent. Assign
|
||
// it. (Cases 3 and 5)
|
||
//
|
||
|
||
NewDaclPresent = TRUE;
|
||
NewDaclInherited = TRUE;
|
||
|
||
} else if (!ARGUMENT_PRESENT(ParentDescriptor) || (Status == STATUS_NO_INHERITANCE)) {
|
||
|
||
//
|
||
// No inheritable ACL - check for a defaulted one in the
|
||
// security descriptor. If there isn't one there, then look
|
||
// for one in the subject's security context (Cases 4 and 6)
|
||
//
|
||
|
||
if ( (CapturedDescriptor->Control & SE_DACL_PRESENT) &&
|
||
(CapturedDescriptor->Control & SE_DACL_DEFAULTED) ) {
|
||
|
||
//
|
||
// reference the default ACL (Case 4)
|
||
//
|
||
|
||
NewDacl = SepDaclAddrSecurityDescriptor(CapturedDescriptor);
|
||
NewDaclPresent = TRUE;
|
||
|
||
//
|
||
// This counts as an explicit assignment.
|
||
//
|
||
|
||
DaclExplicitlyAssigned = TRUE;
|
||
|
||
} else {
|
||
|
||
if (ARGUMENT_PRESENT(SubjectContextDacl)) {
|
||
|
||
NewDacl = SubjectContextDacl;
|
||
NewDaclPresent = TRUE;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Some unusual error occured
|
||
//
|
||
|
||
CleanUp = TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
if (!CleanUp) {
|
||
//
|
||
// Establish an owner SID
|
||
//
|
||
|
||
if ((CapturedDescriptor->Owner) != NULL) {
|
||
|
||
//
|
||
// Use the specified owner
|
||
//
|
||
|
||
NewOwner = SepOwnerAddrSecurityDescriptor(CapturedDescriptor);
|
||
OwnerExplicitlyAssigned = TRUE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Pick up the default from the subject's security context.
|
||
//
|
||
// This does NOT constitute explicit assignment of owner
|
||
// and does not have to be checked as an ID that can be
|
||
// assigned as owner. This is because a default can not
|
||
// be established in a token unless the user of the token
|
||
// can assign it as an owner.
|
||
//
|
||
|
||
//
|
||
// If we've been asked to create a ServerObject, we need to
|
||
// make sure to pick up the new owner from the Primary token,
|
||
// not the client token. If we're not impersonating, they will
|
||
// end up being the same.
|
||
//
|
||
|
||
NewOwner = ServerObject ? SubjectContextServerOwner : SubjectContextOwner;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
if (!CleanUp) {
|
||
//
|
||
// Establish a Group SID
|
||
//
|
||
|
||
if ((CapturedDescriptor->Group) != NULL) {
|
||
|
||
//
|
||
// Use the specified Group
|
||
//
|
||
|
||
NewGroup = SepGroupAddrSecurityDescriptor(CapturedDescriptor);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Pick up the primary group from the subject's security context.
|
||
//
|
||
// If we're creating a Server object, use the group from the server
|
||
// context.
|
||
//
|
||
|
||
NewGroup = ServerObject ? SubjectContextServerGroup : SubjectContextGroup;
|
||
}
|
||
}
|
||
|
||
|
||
if (!CleanUp) {
|
||
|
||
//
|
||
// Now make sure that the caller has the right to assign
|
||
// everything in the descriptor. If requestor is kernel mode,
|
||
// then anything is legitimate. Otherwise, the requestor
|
||
// is subjected to privilege and restriction tests for some
|
||
// assignments.
|
||
//
|
||
|
||
if (RequestorMode == UserMode) {
|
||
|
||
//
|
||
// Anybody can assign any Discretionary ACL or group that they want to.
|
||
//
|
||
|
||
//
|
||
// See if the system ACL was explicitly specified
|
||
//
|
||
|
||
if (SaclExplicitlyAssigned) {
|
||
|
||
//
|
||
// Check for appropriate Privileges
|
||
// Audit/Alarm messages need to be generated due to the attempt
|
||
// to perform a privileged operation.
|
||
//
|
||
|
||
HasPrivilege = SeSinglePrivilegeCheck(
|
||
SeSecurityPrivilege,
|
||
RequestorMode
|
||
);
|
||
|
||
if (!HasPrivilege) {
|
||
|
||
RequestorCanAssignDescriptor = FALSE;
|
||
Status = STATUS_PRIVILEGE_NOT_HELD;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// See if the owner field is one the requestor can assign
|
||
//
|
||
|
||
if (OwnerExplicitlyAssigned) {
|
||
|
||
|
||
if (!SepValidOwnerSubjectContext(
|
||
SubjectContext,
|
||
NewOwner,
|
||
ServerObject)
|
||
) {
|
||
|
||
RequestorCanAssignDescriptor = FALSE;
|
||
Status = STATUS_INVALID_OWNER;
|
||
}
|
||
}
|
||
|
||
if (DaclExplicitlyAssigned) {
|
||
|
||
//
|
||
// Perform analysis of compound ACEs to make sure they're all
|
||
// legitimate.
|
||
//
|
||
|
||
if (ServerObject) {
|
||
|
||
//
|
||
// Pass in the Server Owner as the default server SID.
|
||
//
|
||
|
||
Status = SepCreateServerAcl(
|
||
NewDacl,
|
||
DaclUntrusted,
|
||
SubjectContextServerOwner,
|
||
&ServerDacl,
|
||
&ServerDaclAllocated
|
||
);
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
RequestorCanAssignDescriptor = FALSE;
|
||
|
||
} else {
|
||
|
||
NewDacl = ServerDacl;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (RequestorCanAssignDescriptor) {
|
||
|
||
//
|
||
// Everything is assignable by the requestor.
|
||
// Calculate the memory needed to house all the information in
|
||
// a self-relative security descriptor.
|
||
//
|
||
// Also map the ACEs for application to the target object
|
||
// type, if they haven't already been mapped.
|
||
//
|
||
|
||
NewOwnerSize = (ULONG)LongAlign(SeLengthSid(NewOwner));
|
||
|
||
if (NewGroup != NULL) {
|
||
NewGroupSize = (ULONG)LongAlign(SeLengthSid(NewGroup));
|
||
} else {
|
||
NewGroupSize = 0;
|
||
}
|
||
|
||
if (NewSaclPresent && (NewSacl != NULL)) {
|
||
NewSaclSize = (ULONG)LongAlign(NewSacl->AclSize);
|
||
} else {
|
||
NewSaclSize = 0;
|
||
}
|
||
|
||
if (NewDaclPresent && (NewDacl != NULL)) {
|
||
NewDaclSize = (ULONG)LongAlign(NewDacl->AclSize);
|
||
} else {
|
||
NewDaclSize = 0;
|
||
}
|
||
|
||
AllocationSize = (ULONG)LongAlign(sizeof(SECURITY_DESCRIPTOR)) +
|
||
NewOwnerSize +
|
||
NewGroupSize +
|
||
NewSaclSize +
|
||
NewDaclSize;
|
||
|
||
//
|
||
// Allocate and initialize the security descriptor as
|
||
// self-relative form.
|
||
//
|
||
|
||
*NewDescriptor = (PSECURITY_DESCRIPTOR)ExAllocatePoolWithTag( PoolType, AllocationSize, 'dSeS');
|
||
|
||
if ((*NewDescriptor) == NULL) {
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
} else {
|
||
|
||
RtlCreateSecurityDescriptor(
|
||
(*NewDescriptor),
|
||
SECURITY_DESCRIPTOR_REVISION
|
||
);
|
||
((SECURITY_DESCRIPTOR *)(*NewDescriptor))->Control |=
|
||
SE_SELF_RELATIVE;
|
||
|
||
|
||
Base = (PCHAR)(*NewDescriptor);
|
||
Field = Base + (ULONG)sizeof(SECURITY_DESCRIPTOR);
|
||
|
||
//
|
||
// Map and Copy in the Sacl
|
||
//
|
||
|
||
if (NewSaclPresent) {
|
||
((SECURITY_DESCRIPTOR *)(*NewDescriptor))->Control |=
|
||
SE_SACL_PRESENT;
|
||
if (NewSacl != NULL) {
|
||
RtlMoveMemory( Field, NewSacl, NewSacl->AclSize );
|
||
if (!NewSaclInherited) {
|
||
SepApplyAclToObject( (PACL)Field, GenericMapping );
|
||
}
|
||
((SECURITY_DESCRIPTOR *)(*NewDescriptor))->Sacl = (PACL)RtlPointerToOffset(Base,Field);
|
||
Field += NewSaclSize;
|
||
} else {
|
||
((SECURITY_DESCRIPTOR *)(*NewDescriptor))->Sacl = NULL;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Map and Copy in the Dacl
|
||
//
|
||
|
||
if (NewDaclPresent) {
|
||
((SECURITY_DESCRIPTOR *)(*NewDescriptor))->Control |=
|
||
SE_DACL_PRESENT;
|
||
if (NewDacl != NULL) {
|
||
RtlMoveMemory( Field, NewDacl, NewDacl->AclSize );
|
||
if (!NewDaclInherited) {
|
||
SepApplyAclToObject( (PACL)Field, GenericMapping );
|
||
}
|
||
((SECURITY_DESCRIPTOR *)(*NewDescriptor))->Dacl = (PACL)RtlPointerToOffset(Base,Field);
|
||
Field += NewDaclSize;
|
||
} else {
|
||
((SECURITY_DESCRIPTOR *)(*NewDescriptor))->Dacl = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Assign the owner
|
||
//
|
||
|
||
RtlMoveMemory( Field, NewOwner, SeLengthSid(NewOwner) );
|
||
((SECURITY_DESCRIPTOR *)(*NewDescriptor))->Owner = (PSID)RtlPointerToOffset(Base,Field);
|
||
Field += NewOwnerSize;
|
||
|
||
if (NewGroup != NULL) {
|
||
RtlMoveMemory( Field, NewGroup, SeLengthSid(NewGroup) );
|
||
}
|
||
((SECURITY_DESCRIPTOR *)(*NewDescriptor))->Group = (PSID)RtlPointerToOffset(Base,Field);
|
||
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we allocated memory for a Server DACL, free it now.
|
||
//
|
||
|
||
if (ServerDaclAllocated) {
|
||
ExFreePool( ServerDacl );
|
||
}
|
||
|
||
//
|
||
// Either an error was encountered or the requestor the assignment has
|
||
// completed successfully. In either case, we have to clean up any
|
||
// memory.
|
||
//
|
||
|
||
SeUnlockSubjectContext( SubjectContext );
|
||
|
||
if (NewSaclInherited) {
|
||
ExFreePool( NewSacl );
|
||
}
|
||
|
||
if (NewDaclInherited) {
|
||
ExFreePool( NewDacl );
|
||
}
|
||
|
||
#if DBG
|
||
SepDumpSecurityDescriptor( *NewDescriptor,
|
||
"SeAssignSecurity: Final security descriptor = \n"
|
||
);
|
||
#endif
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SeDeassignSecurity (
|
||
IN OUT PSECURITY_DESCRIPTOR *SecurityDescriptor
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine deallocates the memory associated with a security descriptor
|
||
that was assigned using SeAssignSecurity.
|
||
|
||
|
||
Arguments:
|
||
|
||
SecurityDescriptor - Supplies the address of a pointer to the security
|
||
descriptor being deleted.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - The deallocation was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
if ((*SecurityDescriptor) != NULL) {
|
||
ExFreePool( (*SecurityDescriptor) );
|
||
}
|
||
|
||
//
|
||
// And zero out the pointer to it for safety sake
|
||
//
|
||
|
||
(*SecurityDescriptor) = NULL;
|
||
|
||
return( STATUS_SUCCESS );
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
SepApplyAclToObject (
|
||
IN PACL Acl,
|
||
IN PGENERIC_MAPPING GenericMapping
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is a private routine that maps Access Masks of an ACL so that
|
||
they are applicable to the object type the ACL is being applied to.
|
||
|
||
Only known DSA ACEs are mapped. Unknown ACE types are ignored.
|
||
|
||
Only access types in the GenericAll mapping for the target object
|
||
type will be non-zero upon return.
|
||
|
||
Arguments:
|
||
|
||
Acl - Supplies the acl being applied.
|
||
|
||
GenericMapping - Specifies the generic mapping to use.
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// The logic in the ACL inheritance code must mirror the code for //
|
||
// inheritance in the user mode runtime (in sertl.c). Do not make changes //
|
||
// here without also making changes in that module. //
|
||
// //
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
ULONG i;
|
||
|
||
PACE_HEADER Ace;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// First check if the acl is null
|
||
//
|
||
|
||
if (Acl == NULL) {
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Now walk the ACL, mapping each ACE as we go.
|
||
//
|
||
|
||
for (i = 0, Ace = FirstAce(Acl);
|
||
i < Acl->AceCount;
|
||
i += 1, Ace = NextAce(Ace)) {
|
||
|
||
if (IsMSAceType( Ace )) {
|
||
|
||
RtlApplyAceToObject( Ace, GenericMapping );
|
||
}
|
||
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SepInheritAcl (
|
||
IN PACL Acl,
|
||
IN BOOLEAN IsDirectoryObject,
|
||
IN PSID ClientOwnerSid,
|
||
IN PSID ClientGroupSid,
|
||
IN PSID ServerOwnerSid OPTIONAL,
|
||
IN PSID ServerGroupSid OPTIONAL,
|
||
IN PGENERIC_MAPPING GenericMapping,
|
||
IN POOL_TYPE PoolType,
|
||
OUT PACL *NewAcl
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is a private routine that produces an inherited acl from
|
||
a parent acl according to the rules of inheritance
|
||
|
||
Arguments:
|
||
|
||
Acl - Supplies the acl being inherited.
|
||
|
||
IsDirectoryObject - Specifies if the new acl is for a directory.
|
||
|
||
OwnerSid - Specifies the owner Sid to use.
|
||
|
||
GroupSid - Specifies the group SID to use.
|
||
|
||
ServerSid - Specifies the Server SID to use.
|
||
|
||
ClientSid - Specifies the Client SID to use.
|
||
|
||
GenericMapping - Specifies the generic mapping to use.
|
||
|
||
PoolType - Specifies the pool type for the new acl.
|
||
|
||
NewAcl - Receives a pointer to the new (inherited) acl.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - An inheritable ACL was successfully generated.
|
||
|
||
STATUS_NO_INHERITANCE - An inheritable ACL was not successfully generated.
|
||
This is a warning completion status.
|
||
|
||
STATUS_BAD_INHERITANCE_ACL - Indicates the acl built was not a valid ACL.
|
||
This can becaused by a number of things. One of the more probable
|
||
causes is the replacement of a CreatorId with an SID that didn't fit
|
||
into the ACE or ACL.
|
||
|
||
STATUS_UNKNOWN_REVISION - Indicates the source ACL is a revision that
|
||
is unknown to this routine.
|
||
|
||
--*/
|
||
|
||
{
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// The logic in the ACL inheritance code must mirror the code for //
|
||
// inheritance in the user mode runtime (in sertl.c). Do not make changes //
|
||
// here without also making changes in that module. //
|
||
// //
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
NTSTATUS Status;
|
||
ULONG NewAclLength;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// First check if the acl is null
|
||
//
|
||
|
||
if (Acl == NULL) {
|
||
|
||
return STATUS_NO_INHERITANCE;
|
||
}
|
||
|
||
if (Acl->AclRevision != ACL_REVISION2 && Acl->AclRevision != ACL_REVISION3) {
|
||
return STATUS_UNKNOWN_REVISION;
|
||
}
|
||
|
||
//
|
||
// Generating an inheritable ACL is a two-pass operation.
|
||
// First you must see if there is anything to inherit, and if so,
|
||
// allocate enough room to hold it. then you must actually copy
|
||
// the generated ACEs.
|
||
//
|
||
|
||
Status = RtlpLengthInheritAcl(
|
||
Acl,
|
||
IsDirectoryObject,
|
||
ClientOwnerSid,
|
||
ClientGroupSid,
|
||
ServerOwnerSid,
|
||
ServerGroupSid,
|
||
GenericMapping,
|
||
&NewAclLength
|
||
);
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
return Status;
|
||
}
|
||
if (NewAclLength == 0) {
|
||
return STATUS_NO_INHERITANCE;
|
||
}
|
||
|
||
(*NewAcl) = (PACL)ExAllocatePoolWithTag( PoolType, NewAclLength, 'cAeS' );
|
||
if ((*NewAcl) == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCreateAcl( (*NewAcl), NewAclLength, Acl->AclRevision );
|
||
|
||
Status = RtlpGenerateInheritAcl(
|
||
Acl,
|
||
IsDirectoryObject,
|
||
ClientOwnerSid,
|
||
ClientGroupSid,
|
||
ServerOwnerSid,
|
||
ServerGroupSid,
|
||
GenericMapping,
|
||
(*NewAcl)
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
ExFreePool( (*NewAcl) );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SeAssignWorldSecurityDescriptor(
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
IN OUT PULONG Length,
|
||
IN PSECURITY_INFORMATION SecurityInformation
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by the I/O system to properly initialize a
|
||
security descriptor for a FAT file. It will take a pointer to a
|
||
buffer containing an emptry security descriptor, and create in the
|
||
buffer a self-relative security descriptor with
|
||
|
||
Owner = WorldSid,
|
||
|
||
Group = WorldSid.
|
||
|
||
Thus, a FAT file is accessable to all.
|
||
|
||
Arguments:
|
||
|
||
SecurityDescriptor - Supplies a pointer to a buffer in which will be
|
||
created a self-relative security descriptor as described above.
|
||
|
||
Length - The length in bytes of the buffer. If the length is too
|
||
small, it will contain the minimum size required upon exit.
|
||
|
||
|
||
Return Value:
|
||
|
||
STATUS_BUFFER_TOO_SMALL - The buffer was not big enough to contain
|
||
the requested information.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PCHAR Field;
|
||
PCHAR Base;
|
||
ULONG WorldSidLength;
|
||
PISECURITY_DESCRIPTOR ISecurityDescriptor;
|
||
ULONG MinSize;
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
if ( !ARGUMENT_PRESENT( SecurityInformation )) {
|
||
|
||
return( STATUS_ACCESS_DENIED );
|
||
}
|
||
|
||
WorldSidLength = SeLengthSid( SeWorldSid );
|
||
|
||
MinSize = sizeof( SECURITY_DESCRIPTOR ) + 2 * WorldSidLength;
|
||
|
||
if ( *Length < MinSize ) {
|
||
|
||
*Length = MinSize;
|
||
return( STATUS_BUFFER_TOO_SMALL );
|
||
}
|
||
|
||
*Length = MinSize;
|
||
|
||
ISecurityDescriptor = (SECURITY_DESCRIPTOR *)SecurityDescriptor;
|
||
|
||
Status = RtlCreateSecurityDescriptor( ISecurityDescriptor,
|
||
SECURITY_DESCRIPTOR_REVISION );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
return( Status );
|
||
}
|
||
|
||
Base = (PCHAR)(ISecurityDescriptor);
|
||
Field = Base + (ULONG)sizeof(SECURITY_DESCRIPTOR);
|
||
|
||
if ( *SecurityInformation & OWNER_SECURITY_INFORMATION ) {
|
||
|
||
RtlMoveMemory( Field, SeWorldSid, WorldSidLength );
|
||
ISecurityDescriptor->Owner = (PSID)RtlPointerToOffset(Base,Field);
|
||
Field += WorldSidLength;
|
||
}
|
||
|
||
if ( *SecurityInformation & GROUP_SECURITY_INFORMATION ) {
|
||
|
||
RtlMoveMemory( Field, SeWorldSid, WorldSidLength );
|
||
ISecurityDescriptor->Group = (PSID)RtlPointerToOffset(Base,Field);
|
||
}
|
||
|
||
if ( *SecurityInformation & DACL_SECURITY_INFORMATION ) {
|
||
SepSetControlBits( ISecurityDescriptor, SE_DACL_PRESENT );
|
||
}
|
||
|
||
if ( *SecurityInformation & SACL_SECURITY_INFORMATION ) {
|
||
SepSetControlBits( ISecurityDescriptor, SE_SACL_PRESENT );
|
||
}
|
||
|
||
SepSetControlBits( ISecurityDescriptor, SE_SELF_RELATIVE );
|
||
|
||
return( STATUS_SUCCESS );
|
||
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SepCreateServerAcl(
|
||
IN PACL Acl,
|
||
IN BOOLEAN AclUntrusted,
|
||
IN PSID ServerSid,
|
||
OUT PACL *ServerAcl,
|
||
OUT BOOLEAN *ServerAclAllocated
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes an ACL and converts it into a server ACL.
|
||
Currently, that means converting all of the GRANT ACEs into
|
||
Compount Grants, and if necessary sanitizing any Compound
|
||
Grants that are encountered.
|
||
|
||
Arguments:
|
||
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT RequiredSize = sizeof(ACL);
|
||
USHORT AceSizeAdjustment;
|
||
USHORT ServerSidSize;
|
||
PACE_HEADER Ace;
|
||
ULONG i;
|
||
PVOID Target;
|
||
PVOID AcePosition;
|
||
PSID UntrustedSid;
|
||
PSID ClientSid;
|
||
NTSTATUS Status;
|
||
|
||
if (Acl == NULL) {
|
||
*ServerAclAllocated = FALSE;
|
||
*ServerAcl = NULL;
|
||
return( STATUS_SUCCESS );
|
||
}
|
||
|
||
AceSizeAdjustment = sizeof( KNOWN_COMPOUND_ACE ) - sizeof( KNOWN_ACE );
|
||
ASSERT( sizeof( KNOWN_COMPOUND_ACE ) >= sizeof( KNOWN_ACE ) );
|
||
|
||
ServerSidSize = (USHORT)SeLengthSid( ServerSid );
|
||
|
||
//
|
||
// Do this in two passes. First, determine how big the final
|
||
// result is going to be, and then allocate the space and make
|
||
// the changes.
|
||
//
|
||
|
||
for (i = 0, Ace = FirstAce(Acl);
|
||
i < Acl->AceCount;
|
||
i += 1, Ace = NextAce(Ace)) {
|
||
|
||
//
|
||
// If it's an ACCESS_ALLOWED_ACE_TYPE, we'll need to add in the
|
||
// size of the Server SID.
|
||
//
|
||
|
||
if (Ace->AceType == ACCESS_ALLOWED_ACE_TYPE) {
|
||
|
||
//
|
||
// Simply add the size of the new Server SID plus whatever
|
||
// adjustment needs to be made to increase the size of the ACE.
|
||
//
|
||
|
||
RequiredSize += ( ServerSidSize + AceSizeAdjustment );
|
||
|
||
} else {
|
||
|
||
if (AclUntrusted && Ace->AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE ) {
|
||
|
||
//
|
||
// Since the Acl is untrusted, we don't care what is in the
|
||
// server SID, we're going to replace it.
|
||
//
|
||
|
||
UntrustedSid = RtlCompoundAceServerSid( Ace );
|
||
if ((USHORT)SeLengthSid(UntrustedSid) > ServerSidSize) {
|
||
RequiredSize += ((USHORT)SeLengthSid(UntrustedSid) - ServerSidSize);
|
||
} else {
|
||
RequiredSize += (ServerSidSize - (USHORT)SeLengthSid(UntrustedSid));
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
RequiredSize += Ace->AceSize;
|
||
}
|
||
|
||
(*ServerAcl) = (PACL)ExAllocatePoolWithTag( PagedPool, RequiredSize, 'cAeS' );
|
||
|
||
if ((*ServerAcl) == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Mark as allocated so caller knows to free it.
|
||
//
|
||
|
||
*ServerAclAllocated = TRUE;
|
||
|
||
Status = RtlCreateAcl( (*ServerAcl), RequiredSize, ACL_REVISION3 );
|
||
ASSERT( NT_SUCCESS( Status ));
|
||
|
||
for (i = 0, Ace = FirstAce(Acl), Target=FirstAce( *ServerAcl );
|
||
i < Acl->AceCount;
|
||
i += 1, Ace = NextAce(Ace)) {
|
||
|
||
//
|
||
// If it's an ACCESS_ALLOWED_ACE_TYPE, convert to a Server ACE.
|
||
//
|
||
|
||
if (Ace->AceType == ACCESS_ALLOWED_ACE_TYPE ||
|
||
(AclUntrusted && Ace->AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE )) {
|
||
|
||
AcePosition = Target;
|
||
|
||
if (Ace->AceType == ACCESS_ALLOWED_ACE_TYPE) {
|
||
ClientSid = &((PKNOWN_ACE)Ace)->SidStart;
|
||
} else {
|
||
ClientSid = RtlCompoundAceClientSid( Ace );
|
||
}
|
||
|
||
//
|
||
// Copy up to the access mask.
|
||
//
|
||
|
||
RtlMoveMemory(
|
||
Target,
|
||
Ace,
|
||
FIELD_OFFSET(KNOWN_ACE, SidStart)
|
||
);
|
||
|
||
//
|
||
// Now copy the correct Server SID
|
||
//
|
||
|
||
Target = (PVOID)((ULONG)Target + (UCHAR)(FIELD_OFFSET(KNOWN_COMPOUND_ACE, SidStart)));
|
||
|
||
RtlMoveMemory(
|
||
Target,
|
||
ServerSid,
|
||
SeLengthSid(ServerSid)
|
||
);
|
||
|
||
Target = (PVOID)((ULONG)Target + (UCHAR)SeLengthSid(ServerSid));
|
||
|
||
//
|
||
// Now copy in the correct client SID. We can copy this right out of
|
||
// the original ACE.
|
||
//
|
||
|
||
RtlMoveMemory(
|
||
Target,
|
||
ClientSid,
|
||
SeLengthSid(ClientSid)
|
||
);
|
||
|
||
Target = (PVOID)((ULONG)Target + SeLengthSid(ClientSid));
|
||
|
||
//
|
||
// Set the size of the ACE accordingly
|
||
//
|
||
|
||
((PKNOWN_COMPOUND_ACE)AcePosition)->Header.AceSize =
|
||
(USHORT)FIELD_OFFSET(KNOWN_COMPOUND_ACE, SidStart) +
|
||
(USHORT)SeLengthSid(ServerSid) +
|
||
(USHORT)SeLengthSid(ClientSid);
|
||
|
||
//
|
||
// Set the type
|
||
//
|
||
|
||
((PKNOWN_COMPOUND_ACE)AcePosition)->Header.AceType = ACCESS_ALLOWED_COMPOUND_ACE_TYPE;
|
||
((PKNOWN_COMPOUND_ACE)AcePosition)->CompoundAceType = COMPOUND_ACE_IMPERSONATION;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Just copy the ACE as is.
|
||
//
|
||
|
||
RtlMoveMemory( Target, Ace, Ace->AceSize );
|
||
|
||
Target = (PVOID)((ULONG)Target + Ace->AceSize);
|
||
}
|
||
}
|
||
|
||
(*ServerAcl)->AceCount = Acl->AceCount;
|
||
|
||
return( STATUS_SUCCESS );
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// BUGWARNING The following routines should be in a debug only kernel, since
|
||
// all they do is dump stuff to a debug terminal as appropriate. The same
|
||
// goes for the declarations of the variables SepDumpSD and SepDumpToken
|
||
//
|
||
|
||
|
||
|
||
VOID
|
||
SepDumpSecurityDescriptor(
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
IN PSZ TitleString
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Private routine to dump a security descriptor to the debug
|
||
screen.
|
||
|
||
Arguments:
|
||
|
||
SecurityDescriptor - Supplies the security descriptor to be dumped.
|
||
|
||
TitleString - A null terminated string to print before dumping
|
||
the security descriptor.
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
|
||
--*/
|
||
{
|
||
#if DBG
|
||
PISECURITY_DESCRIPTOR ISecurityDescriptor;
|
||
UCHAR Revision;
|
||
SECURITY_DESCRIPTOR_CONTROL Control;
|
||
PSID Owner;
|
||
PSID Group;
|
||
PACL Sacl;
|
||
PACL Dacl;
|
||
|
||
PAGED_CODE();
|
||
|
||
|
||
if (!SepDumpSD) {
|
||
return;
|
||
}
|
||
|
||
if (!ARGUMENT_PRESENT( SecurityDescriptor )) {
|
||
return;
|
||
}
|
||
|
||
DbgPrint(TitleString);
|
||
|
||
ISecurityDescriptor = ( PISECURITY_DESCRIPTOR )SecurityDescriptor;
|
||
|
||
Revision = ISecurityDescriptor->Revision;
|
||
Control = ISecurityDescriptor->Control;
|
||
|
||
Owner = SepOwnerAddrSecurityDescriptor( ISecurityDescriptor );
|
||
Group = SepGroupAddrSecurityDescriptor( ISecurityDescriptor );
|
||
Sacl = SepSaclAddrSecurityDescriptor( ISecurityDescriptor );
|
||
Dacl = SepDaclAddrSecurityDescriptor( ISecurityDescriptor );
|
||
|
||
DbgPrint("\nSECURITY DESCRIPTOR\n");
|
||
|
||
DbgPrint("Revision = %d\n",Revision);
|
||
|
||
//
|
||
// Print control info
|
||
//
|
||
|
||
if (Control & SE_OWNER_DEFAULTED) {
|
||
DbgPrint("Owner defaulted\n");
|
||
}
|
||
if (Control & SE_GROUP_DEFAULTED) {
|
||
DbgPrint("Group defaulted\n");
|
||
}
|
||
if (Control & SE_DACL_PRESENT) {
|
||
DbgPrint("Dacl present\n");
|
||
}
|
||
if (Control & SE_DACL_DEFAULTED) {
|
||
DbgPrint("Dacl defaulted\n");
|
||
}
|
||
if (Control & SE_SACL_PRESENT) {
|
||
DbgPrint("Sacl present\n");
|
||
}
|
||
if (Control & SE_SACL_DEFAULTED) {
|
||
DbgPrint("Sacl defaulted\n");
|
||
}
|
||
if (Control & SE_SELF_RELATIVE) {
|
||
DbgPrint("Self relative\n");
|
||
}
|
||
if (Control & SE_DACL_UNTRUSTED) {
|
||
DbgPrint("Dacl untrusted\n");
|
||
}
|
||
if (Control & SE_SERVER_SECURITY) {
|
||
DbgPrint("Server security\n");
|
||
}
|
||
|
||
DbgPrint("Owner ");
|
||
SepPrintSid( Owner );
|
||
|
||
DbgPrint("Group ");
|
||
SepPrintSid( Group );
|
||
|
||
DbgPrint("Sacl");
|
||
SepPrintAcl( Sacl );
|
||
|
||
DbgPrint("Dacl");
|
||
SepPrintAcl( Dacl );
|
||
#endif
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
SepPrintAcl (
|
||
IN PACL Acl
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine dumps via (DbgPrint) an Acl for debug purposes. It is
|
||
specialized to dump standard aces.
|
||
|
||
Arguments:
|
||
|
||
Acl - Supplies the Acl to dump
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
#if DBG
|
||
ULONG i;
|
||
PKNOWN_ACE Ace;
|
||
BOOLEAN KnownType;
|
||
|
||
PAGED_CODE();
|
||
|
||
DbgPrint("@ %8lx\n", Acl);
|
||
|
||
//
|
||
// Check if the Acl is null
|
||
//
|
||
|
||
if (Acl == NULL) {
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
//
|
||
// Dump the Acl header
|
||
//
|
||
|
||
DbgPrint(" Revision: %02x", Acl->AclRevision);
|
||
DbgPrint(" Size: %04x", Acl->AclSize);
|
||
DbgPrint(" AceCount: %04x\n", Acl->AceCount);
|
||
|
||
//
|
||
// Now for each Ace we want do dump it
|
||
//
|
||
|
||
for (i = 0, Ace = FirstAce(Acl);
|
||
i < Acl->AceCount;
|
||
i++, Ace = NextAce(Ace) ) {
|
||
|
||
//
|
||
// print out the ace header
|
||
//
|
||
|
||
DbgPrint("\n AceHeader: %08lx ", *(PULONG)Ace);
|
||
|
||
//
|
||
// special case on the standard ace types
|
||
//
|
||
|
||
if ((Ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE) ||
|
||
(Ace->Header.AceType == ACCESS_DENIED_ACE_TYPE) ||
|
||
(Ace->Header.AceType == SYSTEM_AUDIT_ACE_TYPE) ||
|
||
(Ace->Header.AceType == SYSTEM_ALARM_ACE_TYPE) ||
|
||
(Ace->Header.AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE)) {
|
||
|
||
//
|
||
// The following array is indexed by ace types and must
|
||
// follow the allowed, denied, audit, alarm seqeuence
|
||
//
|
||
|
||
PCHAR AceTypes[] = { "Access Allowed",
|
||
"Access Denied ",
|
||
"System Audit ",
|
||
"System Alarm ",
|
||
"Compound Grant",
|
||
};
|
||
|
||
DbgPrint(AceTypes[Ace->Header.AceType]);
|
||
DbgPrint("\n Access Mask: %08lx ", Ace->Mask);
|
||
KnownType = TRUE;
|
||
|
||
} else {
|
||
|
||
DbgPrint(" Unknown Ace Type\n");
|
||
KnownType = FALSE;
|
||
}
|
||
|
||
DbgPrint("\n");
|
||
|
||
DbgPrint(" AceSize = %d\n",Ace->Header.AceSize);
|
||
|
||
DbgPrint(" Ace Flags = ");
|
||
if (Ace->Header.AceFlags & OBJECT_INHERIT_ACE) {
|
||
DbgPrint("OBJECT_INHERIT_ACE\n");
|
||
DbgPrint(" ");
|
||
}
|
||
|
||
if (Ace->Header.AceFlags & CONTAINER_INHERIT_ACE) {
|
||
DbgPrint("CONTAINER_INHERIT_ACE\n");
|
||
DbgPrint(" ");
|
||
}
|
||
|
||
if (Ace->Header.AceFlags & NO_PROPAGATE_INHERIT_ACE) {
|
||
DbgPrint("NO_PROPAGATE_INHERIT_ACE\n");
|
||
DbgPrint(" ");
|
||
}
|
||
|
||
if (Ace->Header.AceFlags & INHERIT_ONLY_ACE) {
|
||
DbgPrint("INHERIT_ONLY_ACE\n");
|
||
DbgPrint(" ");
|
||
}
|
||
|
||
|
||
if (Ace->Header.AceFlags & SUCCESSFUL_ACCESS_ACE_FLAG) {
|
||
DbgPrint("SUCCESSFUL_ACCESS_ACE_FLAG\n");
|
||
DbgPrint(" ");
|
||
}
|
||
|
||
if (Ace->Header.AceFlags & FAILED_ACCESS_ACE_FLAG) {
|
||
DbgPrint("FAILED_ACCESS_ACE_FLAG\n");
|
||
DbgPrint(" ");
|
||
}
|
||
|
||
DbgPrint("\n");
|
||
|
||
if (KnownType != TRUE) {
|
||
continue;
|
||
}
|
||
|
||
if (Ace->Header.AceType != ACCESS_ALLOWED_COMPOUND_ACE_TYPE) {
|
||
DbgPrint(" Sid = ");
|
||
SepPrintSid(&Ace->SidStart);
|
||
} else {
|
||
DbgPrint(" Server Sid = ");
|
||
SepPrintSid(RtlCompoundAceServerSid(Ace));
|
||
DbgPrint("\n Client Sid = ");
|
||
SepPrintSid(RtlCompoundAceClientSid( Ace ));
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
SepPrintSid(
|
||
IN PSID Sid
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Prints a formatted Sid
|
||
|
||
Arguments:
|
||
|
||
Sid - Provides a pointer to the sid to be printed.
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
#if DBG
|
||
UCHAR i;
|
||
ULONG Tmp;
|
||
PISID ISid;
|
||
STRING AccountName;
|
||
UCHAR Buffer[128];
|
||
|
||
PAGED_CODE();
|
||
|
||
if (Sid == NULL) {
|
||
DbgPrint("Sid is NULL\n");
|
||
return;
|
||
}
|
||
|
||
Buffer[0] = 0;
|
||
|
||
AccountName.MaximumLength = 127;
|
||
AccountName.Length = 0;
|
||
AccountName.Buffer = (PVOID)&Buffer[0];
|
||
|
||
if (SepSidTranslation( Sid, &AccountName )) {
|
||
|
||
DbgPrint("%s ", AccountName.Buffer );
|
||
}
|
||
|
||
ISid = (PISID)Sid;
|
||
|
||
DbgPrint("S-%lu-", (USHORT)ISid->Revision );
|
||
if ( (ISid->IdentifierAuthority.Value[0] != 0) ||
|
||
(ISid->IdentifierAuthority.Value[1] != 0) ){
|
||
DbgPrint("0x%02hx%02hx%02hx%02hx%02hx%02hx",
|
||
(USHORT)ISid->IdentifierAuthority.Value[0],
|
||
(USHORT)ISid->IdentifierAuthority.Value[1],
|
||
(USHORT)ISid->IdentifierAuthority.Value[2],
|
||
(USHORT)ISid->IdentifierAuthority.Value[3],
|
||
(USHORT)ISid->IdentifierAuthority.Value[4],
|
||
(USHORT)ISid->IdentifierAuthority.Value[5] );
|
||
} else {
|
||
Tmp = (ULONG)ISid->IdentifierAuthority.Value[5] +
|
||
(ULONG)(ISid->IdentifierAuthority.Value[4] << 8) +
|
||
(ULONG)(ISid->IdentifierAuthority.Value[3] << 16) +
|
||
(ULONG)(ISid->IdentifierAuthority.Value[2] << 24);
|
||
DbgPrint("%lu", Tmp);
|
||
}
|
||
|
||
|
||
for (i=0;i<ISid->SubAuthorityCount ;i++ ) {
|
||
DbgPrint("-%lu", ISid->SubAuthority[i]);
|
||
}
|
||
DbgPrint("\n");
|
||
#endif
|
||
}
|
||
|
||
|
||
|
||
|
||
VOID
|
||
SepDumpTokenInfo(
|
||
IN PACCESS_TOKEN Token
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Prints interesting information in a token.
|
||
|
||
Arguments:
|
||
|
||
Token - Provides the token to be examined.
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
#if DBG
|
||
ULONG UserAndGroupCount;
|
||
PSID_AND_ATTRIBUTES TokenSid;
|
||
ULONG i;
|
||
PTOKEN IToken;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (!SepDumpToken) {
|
||
return;
|
||
}
|
||
|
||
IToken = (TOKEN *)Token;
|
||
|
||
UserAndGroupCount = IToken->UserAndGroupCount;
|
||
|
||
DbgPrint("\n\nToken User and Groups Array:\n\n");
|
||
|
||
for ( i = 0 , TokenSid = IToken->UserAndGroups;
|
||
i < UserAndGroupCount ;
|
||
i++, TokenSid++
|
||
) {
|
||
|
||
SepPrintSid( TokenSid->Sid );
|
||
|
||
}
|
||
#endif
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
SepSidTranslation(
|
||
PSID Sid,
|
||
PSTRING AccountName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine translates well-known SIDs into English names.
|
||
|
||
Arguments:
|
||
|
||
Sid - Provides the sid to be examined.
|
||
|
||
AccountName - Provides a string buffer in which to place the
|
||
translated name.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
// AccountName is expected to have a large maximum length
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
if (RtlEqualSid(Sid, SeWorldSid)) {
|
||
RtlInitString( AccountName, "WORLD ");
|
||
return(TRUE);
|
||
}
|
||
|
||
if (RtlEqualSid(Sid, SeLocalSid)) {
|
||
RtlInitString( AccountName, "LOCAL ");
|
||
return(TRUE);
|
||
}
|
||
|
||
if (RtlEqualSid(Sid, SeNetworkSid)) {
|
||
RtlInitString( AccountName, "NETWORK ");
|
||
return(TRUE);
|
||
}
|
||
|
||
if (RtlEqualSid(Sid, SeBatchSid)) {
|
||
RtlInitString( AccountName, "BATCH ");
|
||
return(TRUE);
|
||
}
|
||
|
||
if (RtlEqualSid(Sid, SeInteractiveSid)) {
|
||
RtlInitString( AccountName, "INTERACTIVE ");
|
||
return(TRUE);
|
||
}
|
||
|
||
if (RtlEqualSid(Sid, SeLocalSystemSid)) {
|
||
RtlInitString( AccountName, "SYSTEM ");
|
||
return(TRUE);
|
||
}
|
||
|
||
if (RtlEqualSid(Sid, SeCreatorOwnerSid)) {
|
||
RtlInitString( AccountName, "CREATOR_OWNER ");
|
||
return(TRUE);
|
||
}
|
||
|
||
if (RtlEqualSid(Sid, SeCreatorGroupSid)) {
|
||
RtlInitString( AccountName, "CREATOR_GROUP ");
|
||
return(TRUE);
|
||
}
|
||
|
||
if (RtlEqualSid(Sid, SeCreatorOwnerServerSid)) {
|
||
RtlInitString( AccountName, "CREATOR_OWNER_SERVER ");
|
||
return(TRUE);
|
||
}
|
||
|
||
if (RtlEqualSid(Sid, SeCreatorGroupServerSid)) {
|
||
RtlInitString( AccountName, "CREATOR_GROUP_SERVER ");
|
||
return(TRUE);
|
||
}
|
||
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// End debug only routines
|
||
//
|