diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/se/seassign.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/ntos/se/seassign.c')
-rw-r--r-- | private/ntos/se/seassign.c | 1924 |
1 files changed, 1924 insertions, 0 deletions
diff --git a/private/ntos/se/seassign.c b/private/ntos/se/seassign.c new file mode 100644 index 000000000..59a491e7e --- /dev/null +++ b/private/ntos/se/seassign.c @@ -0,0 +1,1924 @@ +/*++ + +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 +// |