From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/ntos/dll/seurtl.c | 3469 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3469 insertions(+) create mode 100644 private/ntos/dll/seurtl.c (limited to 'private/ntos/dll/seurtl.c') diff --git a/private/ntos/dll/seurtl.c b/private/ntos/dll/seurtl.c new file mode 100644 index 000000000..b9bad1760 --- /dev/null +++ b/private/ntos/dll/seurtl.c @@ -0,0 +1,3469 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + seurtl.c + +Abstract: + + This Module implements many security rtl routines defined in nturtl.h + +Author: + + Robert Reichel (RobertRe) 1-Mar-1991 + +Environment: + + Pure Runtime Library Routine + User mode callable only + +Revision History: + +--*/ + + +#include +#include +#include // needed for RtlGetPrimaryDomain +#include "seopaque.h" +#include "sertlp.h" +#include "ldrp.h" + + + +// +// Private routines +// + +NTSTATUS +RtlpCreateServerAcl( + IN PACL Acl, + IN BOOLEAN AclUntrusted, + IN PSID ServerSid, + OUT PACL *ServerAcl, + OUT BOOLEAN *ServerAclAllocated + ); + +NTSTATUS +RtlpGetDefaultsSubjectContext( + HANDLE ClientToken, + OUT PTOKEN_OWNER *OwnerInfo, + OUT PTOKEN_PRIMARY_GROUP *GroupInfo, + OUT PTOKEN_DEFAULT_DACL *DefaultDaclInfo, + OUT PTOKEN_OWNER *ServerOwner, + OUT PTOKEN_PRIMARY_GROUP *ServerGroup + ); + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Exported Procedures // +// // +/////////////////////////////////////////////////////////////////////////////// + + +#if WHEN_LSAUDLL_MOVED_TO_NTDLL +NTSTATUS +RtlGetPrimaryDomain( + IN ULONG SidLength, + OUT PBOOLEAN PrimaryDomainPresent, + OUT PUNICODE_STRING PrimaryDomainName, + OUT PUSHORT RequiredNameLength, + OUT PSID PrimaryDomainSid OPTIONAL, + OUT PULONG RequiredSidLength + ) + +/*++ + +Routine Description: + + This procedure opens the LSA policy object and retrieves + the primary domain information for this machine. + +Arguments: + + SidLength - Specifies the length of the PrimaryDomainSid + parameter. + + PrimaryDomainPresent - Receives a boolean value indicating + whether this machine has a primary domain or not. TRUE + indicates the machine does have a primary domain. FALSE + indicates the machine does not. + + PrimaryDomainName - Points to the unicode string to receive + the primary domain name. This parameter will only be + used if there is a primary domain. + + RequiredNameLength - Recevies the length of the primary + domain name (in bytes). This parameter will only be + used if there is a primary domain. + + PrimaryDomainSid - This optional parameter, if present, + points to a buffer to receive the primary domain's + SID. This parameter will only be used if there is a + primary domain. + + RequiredSidLength - Recevies the length of the primary + domain SID (in bytes). This parameter will only be + used if there is a primary domain. + + +Return Value: + + STATUS_SUCCESS - The requested information has been retrieved. + + STATUS_BUFFER_TOO_SMALL - One of the return buffers was not + large enough to receive the corresponding information. + The RequiredNameLength and RequiredSidLength parameter + values have been set to indicate the needed length. + + Other status values as may be returned by: + + LsaOpenPolicy() + LsaQueryInformationPolicy() + RtlCopySid() + + +--*/ + + + + +{ + NTSTATUS Status, IgnoreStatus; + OBJECT_ATTRIBUTES ObjectAttributes; + LSA_HANDLE LsaHandle; + SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService; + PPOLICY_PRIMARY_DOMAIN_INFO PrimaryDomainInfo; + + + // + // Set up the Security Quality Of Service + // + + SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); + SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation; + SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; + SecurityQualityOfService.EffectiveOnly = FALSE; + + // + // Set up the object attributes to open the Lsa policy object + // + + InitializeObjectAttributes(&ObjectAttributes, + NULL, + 0L, + (HANDLE)NULL, + NULL); + ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService; + + // + // Open the local LSA policy object + // + + Status = LsaOpenPolicy( NULL, + &ObjectAttributes, + POLICY_VIEW_LOCAL_INFORMATION, + &LsaHandle + ); + if (NT_SUCCESS(Status)) { + + // + // Get the primary domain info + // + Status = LsaQueryInformationPolicy(LsaHandle, + PolicyPrimaryDomainInformation, + (PVOID *)&PrimaryDomainInfo); + IgnoreStatus = LsaClose(LsaHandle); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + if (NT_SUCCESS(Status)) { + + // + // Is there a primary domain? + // + + if (PrimaryDomainInfo->Sid != NULL) { + + // + // Yes + // + + (*PrimaryDomainPresent) = TRUE; + (*RequiredNameLength) = PrimaryDomainInfo->Name.Length; + (*RequiredSidLength) = RtlLengthSid(PrimaryDomainInfo->Sid); + + + + // + // Copy the name + // + + if (PrimaryDomainName->MaximumLength >= + PrimaryDomainInfo->Name.Length) { + RtlCopyUnicodeString( + PrimaryDomainName, + &PrimaryDomainInfo->Name + ); + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + + + // + // Copy the SID (if appropriate) + // + + if (PrimaryDomainSid != NULL && NT_SUCCESS(Status)) { + + Status = RtlCopySid(SidLength, + PrimaryDomainSid, + PrimaryDomainInfo->Sid + ); + } + } else { + + (*PrimaryDomainPresent) = FALSE; + } + + // + // We're finished with the buffer returned by LSA + // + + IgnoreStatus = LsaFreeMemory(PrimaryDomainInfo); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + } + + + return(Status); +} +#endif //WHEN_LSAUDLL_MOVED_TO_NTDLL + + + + +NTSTATUS +RtlNewSecurityObject ( + IN PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL, + IN PSECURITY_DESCRIPTOR CreatorDescriptor OPTIONAL, + OUT PSECURITY_DESCRIPTOR * NewDescriptor, + IN BOOLEAN IsDirectoryObject, + IN HANDLE Token, + IN PGENERIC_MAPPING GenericMapping + ) +/*++ + +Routine Description: + + The procedure is used to allocpate and initialize a self-relative + Security Descriptor for a new protected server's object. It is called + when a new protected server object is being created. The generated + security descriptor will be in self-relative form. + + This procedure, called only from user mode, is used to establish a + security descriptor for a new protected server's object. Memory is + allocated to hold each of the security descriptor's components (using + NtAllocateVirtualMemory()). The final security descriptor generated by + this procedure is produced according to the rules stated in ??? + + System and Discretionary ACL Assignment + --------------------------------------- + + The assignment of system and discretionary ACLs is governed by the + logic illustrated in the following table: + + | Explicit | Explicit | + | (non-default) | Default | No + | Acl | Acl | Acl + | Specified | Specified | Specified + -------------+----------------+---------------+-------------- + | | | + Inheritable | Assign | Assign | Assign + Acl From | Specified | Inherited | Inherited + Parent | Acl | Acl | Acl + | | | + -------------+----------------+---------------+-------------- + No | | | + 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. + + - - WARNING - - + + This service is for use by protected subsystems that project their own + type of object. This service is explicitly not for use by the + executive for executive objects and must not be called from kernel + mode. + +Arguments: + + ParentDescriptor - Supplies the Security Descriptor for the parent + directory under which a new object is being created. If there is + no parent directory, then this argument is specified as NULL. + + CreatorDescriptor - (Optionally) Points to a security descriptor + presented by the creator of the object. If the creator of the + object did not explicitly pass security information for the new + object, then a null pointer should be passed. + + NewDescriptor - Points to a pointer that is to be made to point to the + newly allocated self-relative security descriptor. + + IsDirectoryObject - Specifies if the new object is going to be a + directory object. A value of TRUE indicates the object is a + container of other objects. + + Token - Supplies the token for the client on whose behalf the + object is being created. If it is an impersonation token, + then it must be at SecurityIdentification level or higher. If + it is not an impersonation token, the operation proceeds + normally. + + A client token is used to retrieve default security + information for the new object, such as default owner, primary + group, and discretionary access control. The token must be + open for TOKEN_QUERY access. + + GenericMapping - Supplies a pointer to a generic mapping array denoting + the mapping between each generic right to specific rights. + +Return Value: + + STATUS_SUCCESS - The operation was successful. + + STATUS_INVALID_OWNER - The owner SID provided as the owner of the + target security descriptor is not one the subject is authorized to + assign as the owner of an object. + + STATUS_NO_CLIENT_TOKEN - Indicates a client token was not explicitly + provided and the caller is not currently impersonating a client. + + +--*/ +{ + + + 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; + PACL ServerDacl = NULL; + BOOLEAN NewDaclPresent = FALSE; + BOOLEAN NewDaclInherited = FALSE; + + PSID NewOwner = NULL; + PSID NewGroup = NULL; + + BOOLEAN CleanUp = FALSE; + BOOLEAN SaclExplicitlyAssigned = FALSE; + BOOLEAN OwnerExplicitlyAssigned = FALSE; + BOOLEAN DaclExplicitlyAssigned = FALSE; + + BOOLEAN ServerDaclAllocated = FALSE; + + BOOLEAN ServerObject; + BOOLEAN DaclUntrusted; + + BOOLEAN HasPrivilege; + PRIVILEGE_SET PrivilegeSet; + + PSID SubjectContextOwner; + PSID SubjectContextGroup; + PSID ServerOwner; + PSID ServerGroup; + + PACL SubjectContextDacl; + + ULONG AllocationSize; + ULONG NewOwnerSize; + ULONG NewGroupSize; + ULONG NewSaclSize; + ULONG NewDaclSize; + + PCHAR Field; + PCHAR Base; + + PTOKEN_OWNER TokenOwnerInfo = NULL; + PTOKEN_PRIMARY_GROUP TokenPrimaryGroupInfo = NULL; + PTOKEN_DEFAULT_DACL TokenDefaultDaclInfo = NULL; + + PTOKEN_OWNER ServerOwnerInfo = NULL; + PTOKEN_PRIMARY_GROUP ServerGroupInfo = NULL; + + TOKEN_STATISTICS ThreadTokenStatistics; + + PISECURITY_DESCRIPTOR INewDescriptor = NULL; + ULONG ReturnLength; + NTSTATUS PassedStatus; + PVOID HeapHandle; + HANDLE PrimaryToken; + + // + // Get the handle to the current process heap + // + + HeapHandle = RtlProcessHeap(); + + // + // 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. + // + + Status = NtQueryInformationToken( + Token, // Handle + TokenStatistics, // TokenInformationClass + &ThreadTokenStatistics, // TokenInformation + sizeof(TOKEN_STATISTICS), // TokenInformationLength + &ReturnLength // ReturnLength + ); + + if (!NT_SUCCESS( Status )) { + return( Status ); + } + + // + // If it is an impersonation token, then make sure it is at a + // high enough level. + // + + if (ThreadTokenStatistics.TokenType == TokenImpersonation) { + + if (ThreadTokenStatistics.ImpersonationLevel < SecurityIdentification ) { + + return( STATUS_BAD_IMPERSONATION_LEVEL ); + } + } + + + // + // If a security descriptor has been passed, capture it, otherwise + // cobble up a fake one to simplify the code that follows. + // + + if (ARGUMENT_PRESENT(CreatorDescriptor)) { + + CapturedDescriptor = CreatorDescriptor; + SecurityDescriptorPassed = TRUE; + + } else { + + // + // No descriptor passed, make a fake one + // + + SecurityDescriptorPassed = FALSE; + RtlCreateSecurityDescriptor(&InCaseOneNotPassed, + SECURITY_DESCRIPTOR_REVISION); + CapturedDescriptor = &InCaseOneNotPassed; + + } + + Status = RtlpGetDefaultsSubjectContext( + Token, + &TokenOwnerInfo, + &TokenPrimaryGroupInfo, + &TokenDefaultDaclInfo, + &ServerOwnerInfo, + &ServerGroupInfo + ); + + if (!NT_SUCCESS( Status )) { + return( Status ); + } + + SubjectContextOwner = TokenOwnerInfo->Owner; + SubjectContextGroup = TokenPrimaryGroupInfo->PrimaryGroup; + SubjectContextDacl = TokenDefaultDaclInfo->DefaultDacl; + ServerOwner = ServerOwnerInfo->Owner; + ServerGroup = ServerGroupInfo->PrimaryGroup; + + 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 + // + + NewSacl = RtlpSaclAddrSecurityDescriptor(CapturedDescriptor); + NewSaclPresent = TRUE; + SaclExplicitlyAssigned = TRUE; + + + } else { + + // + // 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 = RtlpInheritAcl( + RtlpSaclAddrSecurityDescriptor( + ((SECURITY_DESCRIPTOR *)ParentDescriptor) + ), + IsDirectoryObject, + SubjectContextOwner, + SubjectContextGroup, + ServerOwner, + ServerGroup, + GenericMapping, + &NewSacl ) + )) { + + NewSaclPresent = TRUE; + NewSaclInherited = TRUE; + + } else if (!ARGUMENT_PRESENT(ParentDescriptor) || (Status == STATUS_NO_INHERITANCE)) { + + // + // No inheritable ACL - check for a defaulted one. + // + + if ( (CapturedDescriptor->Control & SE_SACL_PRESENT) && + (CapturedDescriptor->Control & SE_SACL_DEFAULTED) ) { + + // + // Reference the default ACL + // + + NewSacl = RtlpSaclAddrSecurityDescriptor(CapturedDescriptor); + NewSaclPresent = TRUE; + SaclExplicitlyAssigned = TRUE; // BUGBUG is this correct? + } + } else { + + // + // Some unusual error occured + // + + CleanUp = TRUE; + } + } + } + + + + + if (!CleanUp) { + + // + // Establish Discretionary Acl + // + + if ( RtlpAreControlBitsSet( CapturedDescriptor, SE_DACL_PRESENT ) && + !RtlpAreControlBitsSet( CapturedDescriptor, SE_DACL_DEFAULTED) ) { + + // + // Explicitly provided, not defaulted + // + + NewDacl = RtlpDaclAddrSecurityDescriptor(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 = RtlpInheritAcl( + RtlpDaclAddrSecurityDescriptor( + ((SECURITY_DESCRIPTOR *)ParentDescriptor) + ), + IsDirectoryObject, + SubjectContextOwner, + SubjectContextGroup, + ServerOwner, + ServerGroup, + GenericMapping, + &NewDacl + ) + )) { + + + 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. + // + + if ( RtlpAreControlBitsSet( CapturedDescriptor, + SE_DACL_PRESENT | SE_DACL_DEFAULTED + ) ) { + + // + // reference the default ACL + // + + NewDacl = RtlpDaclAddrSecurityDescriptor(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 = RtlpOwnerAddrSecurityDescriptor(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 ? ServerOwner : SubjectContextOwner; + } + } + + + + if (!CleanUp) { + // + // Establish a Group SID + // + + if ((CapturedDescriptor->Group) != NULL) { + + // + // Use the specified Group + // + + NewGroup = RtlpGroupAddrSecurityDescriptor(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 ? ServerGroup : SubjectContextGroup; + } + } + + + if (!CleanUp) { + + // + // Now make sure that the caller has the right to assign + // everything in the descriptor. The requestor is subjected + // to privilege and restriction tests for some assignments. + // + + + // + // 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. + // + + PrivilegeSet.PrivilegeCount = 1; + PrivilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY; + PrivilegeSet.Privilege[0].Luid = RtlConvertLongToLuid(SE_SECURITY_PRIVILEGE); + PrivilegeSet.Privilege[0].Attributes = 0; + + Status = NtPrivilegeCheck( + Token, + &PrivilegeSet, + &HasPrivilege + ); + + if (NT_SUCCESS( Status )) { + + Status = NtPrivilegedServiceAuditAlarm ( + NULL, // Subsystemname + NULL, // ServiceName + Token, + &PrivilegeSet, + HasPrivilege + ); + + if (NT_SUCCESS( Status )) { + + if ( !HasPrivilege ) { + RequestorCanAssignDescriptor = FALSE; + Status = STATUS_PRIVILEGE_NOT_HELD; + } + + } else { + + // + // We failed the attempt to audit the privilege + // check, fail the entire operation. + // + + RequestorCanAssignDescriptor = FALSE; + } + } else { + + // + // The privilege check failed for reasons other + // than lack of privilege. Retain the status code + // and bail out. + // + + RequestorCanAssignDescriptor = FALSE; + } + } + + // + // See if the owner field is one the requestor can assign + // + + if (NT_SUCCESS( Status )) { + + if (OwnerExplicitlyAssigned) { + + if (!RtlpValidOwnerSubjectContext( + Token, + NewOwner, + ServerObject, + &PassedStatus) ) { + + if (!NT_SUCCESS( PassedStatus )) { + Status = PassedStatus; + + } else { + + Status = STATUS_INVALID_OWNER; + } + + RequestorCanAssignDescriptor = FALSE; + } + } + } + + if (NT_SUCCESS( Status )) { + + if (DaclExplicitlyAssigned) { + + if (ServerObject) { + + Status = RtlpCreateServerAcl( + NewDacl, + DaclUntrusted, + ServerOwner, + &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)); + NewGroupSize = (ULONG)LongAlign(SeLengthSid(NewGroup)); + 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. + // + + INewDescriptor = RtlAllocateHeap( HeapHandle, MAKE_TAG( SE_TAG ), AllocationSize ); + + if ( INewDescriptor != NULL ) { + + RtlCreateSecurityDescriptor( + INewDescriptor, + SECURITY_DESCRIPTOR_REVISION + ); + + RtlpSetControlBits( INewDescriptor, SE_SELF_RELATIVE ); + + Base = (PCHAR)(INewDescriptor); + Field = Base + (ULONG)sizeof(SECURITY_DESCRIPTOR); + + // + // Map and Copy in the Sacl + // + + if (NewSaclPresent) { + + RtlpSetControlBits( INewDescriptor, SE_SACL_PRESENT ); + + if (NewSacl != NULL) { + + RtlMoveMemory( Field, NewSacl, NewSacl->AclSize ); + + if (!NewSaclInherited) { + RtlpApplyAclToObject( (PACL)Field, GenericMapping ); + } + + INewDescriptor->Sacl = (PACL)RtlPointerToOffset(Base,Field); + Field += NewSaclSize; + + } else { + + INewDescriptor->Sacl = NULL; + } + + } + + // + // Map and Copy in the Dacl + // + + if (NewDaclPresent) { + + RtlpSetControlBits( INewDescriptor, SE_DACL_PRESENT ); + + if (NewDacl != NULL) { + + RtlMoveMemory( Field, NewDacl, NewDacl->AclSize ); + + if (!NewDaclInherited) { + RtlpApplyAclToObject( (PACL)Field, GenericMapping ); + } + + INewDescriptor->Dacl = (PACL)RtlPointerToOffset(Base,Field); + Field += NewDaclSize; + + } else { + + INewDescriptor->Dacl = NULL; + } + + } + + // + // Assign the owner + // + + RtlMoveMemory( Field, NewOwner, SeLengthSid(NewOwner) ); + INewDescriptor->Owner = (PSID)RtlPointerToOffset(Base,Field); + Field += NewOwnerSize; + + RtlMoveMemory( Field, NewGroup, SeLengthSid(NewGroup) ); + INewDescriptor->Group = (PSID)RtlPointerToOffset(Base,Field); + + Status = STATUS_SUCCESS; + + } else { + + Status = STATUS_NO_MEMORY; + } + } + } + + + // + // If we allocated memory for a Server DACL, free it now. + // + + if (ServerDaclAllocated) { + RtlFreeHeap(RtlProcessHeap(), 0, ServerDacl ); + } + + // + // Either an error was encountered or the assignment has completed + // successfully. In either case, we have to clean up any memory. + // + + RtlFreeHeap( HeapHandle, 0, (PVOID)TokenOwnerInfo ); + RtlFreeHeap( HeapHandle, 0, (PVOID)TokenPrimaryGroupInfo ); + RtlFreeHeap( HeapHandle, 0, (PVOID)TokenDefaultDaclInfo ); + RtlFreeHeap( HeapHandle, 0, (PVOID)ServerOwnerInfo ); + RtlFreeHeap( HeapHandle, 0, (PVOID)ServerGroupInfo ); + + if (NewSaclInherited) { + + RtlFreeHeap( HeapHandle, 0, (PVOID)NewSacl ); + } + + if (NewDaclInherited) { + + RtlFreeHeap( HeapHandle, 0, (PVOID)NewDacl ); + + } + + *NewDescriptor = (PSECURITY_DESCRIPTOR) INewDescriptor; + + return Status; +} + + + +NTSTATUS +RtlSetSecurityObject ( + IN SECURITY_INFORMATION SecurityInformation, + IN PSECURITY_DESCRIPTOR ModificationDescriptor, + IN OUT PSECURITY_DESCRIPTOR *ObjectsSecurityDescriptor, + IN PGENERIC_MAPPING GenericMapping, + IN HANDLE Token OPTIONAL + ) + + +/*++ + +Routine Description: + + Modify an object's existing self-relative form security descriptor. + + This procedure, called only from user mode, is used to update a + security descriptor on an existing protected server's object. It + applies changes requested by a new security descriptor to the existing + security descriptor. If necessary, this routine will allocate + additional memory to produce a larger security descriptor. All access + checking is expected to be done before calling this routine. This + includes checking for WRITE_OWNER, WRITE_DAC, and privilege to assign a + system ACL as appropriate. + + The caller of this routine must not be impersonating a client. + + - - WARNING - - + + This service is for use by protected subsystems that project their own + type of object. This service is explicitly not for use by the + executive for executive objects and must not be called from kernel + mode. + +Arguments: + + SecurityInformation - Indicates which security information is + to be applied to the object. The value(s) to be assigned are + passed in the ModificationDescriptor parameter. + + ModificationDescriptor - Supplies the input security descriptor to be + applied to the object. The caller of this routine is expected + to probe and capture the passed security descriptor before calling + and release it after calling. + + ObjectsSecurityDescriptor - Supplies the address of a pointer to + the objects security descriptor that is going to be altered by + this procedure. This security descriptor must be in self- + relative form or an error will be returned. + + GenericMapping - This argument provides the mapping of generic to + specific/standard access types for the object being accessed. + This mapping structure is expected to be safe to access + (i.e., captured if necessary) prior to be passed to this routine. + + Token - (optionally) Supplies the token for the client on whose + behalf the security is being modified. This parameter is only + required to ensure that the client has provided a legitimate + value for a new owner SID. The token must be open for + TOKEN_QUERY access. + +Return Value: + + STATUS_SUCCESS - The operation was successful. + + STATUS_INVALID_OWNER - The owner SID provided as the new owner of the + target security descriptor is not one the caller is authorized to + assign as the owner of an object, or the client did not pass + a token at all. + + STATUS_NO_CLIENT_TOKEN - Indicates a client token was not explicitly + provided and the caller is not currently impersonating a client. + + STATUS_BAD_DESCRIPTOR_FORMAT - Indicates the provided object's security + descriptor was not in self-relative format. + +--*/ + +{ + BOOLEAN NewGroupPresent = FALSE; + BOOLEAN NewSaclPresent = FALSE; + BOOLEAN NewDaclPresent = FALSE; + BOOLEAN NewOwnerPresent = FALSE; + + BOOLEAN ServerAclAllocated = FALSE; + BOOLEAN ServerObject; + BOOLEAN DaclUntrusted; + + PCHAR Field; + PCHAR Base; + + PISECURITY_DESCRIPTOR NewDescriptor = NULL; + + NTSTATUS Status; + + TOKEN_STATISTICS ThreadTokenStatistics; + + ULONG ReturnLength; + + PSID NewGroup; + PSID NewOwner; + PTOKEN_OWNER ServerSid; + + PACL NewDacl; + PACL NewSacl; + + ULONG NewDaclSize; + ULONG NewSaclSize; + ULONG NewOwnerSize; + ULONG NewGroupSize; + ULONG AllocationSize; + ULONG ServerOwnerInfoSize; + + HANDLE PrimaryToken; + + PACL ServerDacl; + + + PISECURITY_DESCRIPTOR IModificationDescriptor = + (PISECURITY_DESCRIPTOR)ModificationDescriptor; + + PISECURITY_DESCRIPTOR *IObjectsSecurityDescriptor = + (PISECURITY_DESCRIPTOR *)(ObjectsSecurityDescriptor); + + PVOID HeapHandle; + + // + // Get the handle to the current process heap + // + + HeapHandle = RtlProcessHeap(); + + // + // Validate that the provided SD is in self-relative form + // + + if ( !RtlpAreControlBitsSet(*IObjectsSecurityDescriptor, SE_SELF_RELATIVE) ) { + return( STATUS_BAD_DESCRIPTOR_FORMAT ); + } + + if (ARGUMENT_PRESENT(ModificationDescriptor)) { + + if ( RtlpAreControlBitsSet(IModificationDescriptor, SE_SERVER_SECURITY)) { + ServerObject = TRUE; + } else { + ServerObject = FALSE; + } + + if ( RtlpAreControlBitsSet(IModificationDescriptor, SE_DACL_UNTRUSTED)) { + DaclUntrusted = TRUE; + } else { + DaclUntrusted = FALSE; + } + + } else { + + ServerObject = FALSE; + DaclUntrusted = FALSE; + + } + + + // + // For each item specified in the SecurityInformation, extract it + // and get it to the point where it can be copied into a new + // descriptor. + // + + + if (SecurityInformation & GROUP_SECURITY_INFORMATION) { + + NewGroup = RtlpGroupAddrSecurityDescriptor(IModificationDescriptor); + + if ( NewGroup == NULL ) { + return( STATUS_INVALID_PRIMARY_GROUP ); + } + + NewGroupPresent = TRUE; + + } else { + + NewGroup = RtlpGroupAddrSecurityDescriptor( *IObjectsSecurityDescriptor ); + } + + + if (SecurityInformation & DACL_SECURITY_INFORMATION) { + + NewDacl = RtlpDaclAddrSecurityDescriptor( IModificationDescriptor ); + NewDaclPresent = TRUE; + + if (ServerObject) { + + // + // Obtain the default Server SID to substitute in the + // ACL if necessary. + // + + ServerOwnerInfoSize = RtlLengthRequiredSid( SID_MAX_SUB_AUTHORITIES ); + + ServerSid = RtlAllocateHeap( HeapHandle, MAKE_TAG( SE_TAG ), ServerOwnerInfoSize ); + + if (ServerSid == NULL) { + return( STATUS_NO_MEMORY ); + } + + Status = NtOpenProcessToken( + NtCurrentProcess(), + TOKEN_QUERY, + &PrimaryToken + ); + + if (!NT_SUCCESS( Status )) { + RtlFreeHeap( HeapHandle, 0, ServerSid ); + return( Status ); + } + + Status = NtQueryInformationToken( + PrimaryToken, // Handle + TokenOwner, // TokenInformationClass + ServerSid, // TokenInformation + ServerOwnerInfoSize, // TokenInformationLength + &ServerOwnerInfoSize // ReturnLength + ); + + NtClose( PrimaryToken ); + + if (!NT_SUCCESS( Status )) { + RtlFreeHeap( HeapHandle, 0, ServerSid ); + return( Status ); + } + + if (NT_SUCCESS( Status )) { + + Status = RtlpCreateServerAcl( + NewDacl, + DaclUntrusted, + ServerSid->Owner, + &ServerDacl, + &ServerAclAllocated + ); + + RtlFreeHeap( HeapHandle, 0, ServerSid ); + + if (!NT_SUCCESS( Status )) { + return( Status ); + } + + NewDacl = ServerDacl; + + } else { + + return( Status ); + } + } + + } else { + + NewDacl = RtlpDaclAddrSecurityDescriptor( *IObjectsSecurityDescriptor ); + } + + + + if (SecurityInformation & SACL_SECURITY_INFORMATION) { + + NewSacl = RtlpSaclAddrSecurityDescriptor( IModificationDescriptor ); + NewSaclPresent = TRUE; + + } else { + + NewSacl = RtlpSaclAddrSecurityDescriptor( *IObjectsSecurityDescriptor ); + } + + // + // if he's setting the owner field, make sure he's + // allowed to set that value as an owner. + // + + if (SecurityInformation & OWNER_SECURITY_INFORMATION) { + + if ( ARGUMENT_PRESENT( Token )) { + + Status = NtQueryInformationToken( + Token, // Handle + TokenStatistics, // TokenInformationClass + &ThreadTokenStatistics, // TokenInformation + sizeof(TOKEN_STATISTICS), // TokenInformationLength + &ReturnLength // ReturnLength + ); + + if (!NT_SUCCESS( Status )) { + return( Status ); + } + + // + // If it is an impersonation token, then make sure it is at a + // high enough level. + // + + if (ThreadTokenStatistics.TokenType == TokenImpersonation) { + + if (ThreadTokenStatistics.ImpersonationLevel < SecurityIdentification ) { + + return( STATUS_BAD_IMPERSONATION_LEVEL ); + } + } + + } else { + + return( STATUS_INVALID_OWNER ); + } + + NewOwner = RtlpOwnerAddrSecurityDescriptor( IModificationDescriptor ); + NewOwnerPresent = TRUE; + + if (!RtlpValidOwnerSubjectContext( + Token, + NewOwner, + ServerObject, + &Status) ) { + + if (!NT_SUCCESS( Status )) { + + return( Status ); + + } else { + + return( STATUS_INVALID_OWNER ); + } + } + + } else { + + NewOwner = RtlpOwnerAddrSecurityDescriptor ( *IObjectsSecurityDescriptor ); + if (NewOwner == NULL) { + return(STATUS_INVALID_OWNER); + } + + } + + + // + // 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)); + NewGroupSize = (ULONG)LongAlign(SeLengthSid(NewGroup)); + + if (NewSacl != NULL) { + NewSaclSize = (ULONG)LongAlign(NewSacl->AclSize); + } else { + NewSaclSize = 0; + } + + if (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 = RtlAllocateHeap( HeapHandle, MAKE_TAG( SE_TAG ), AllocationSize ); + + if ( NewDescriptor == NULL ) { + + return( STATUS_NO_MEMORY ); + + } + + Status = RtlCreateSecurityDescriptor( + NewDescriptor, + SECURITY_DESCRIPTOR_REVISION + ); + + ASSERT( NT_SUCCESS( Status ) ); + + RtlpSetControlBits( NewDescriptor, SE_SELF_RELATIVE ); + + Base = (PCHAR)NewDescriptor; + Field = Base + (ULONG)sizeof(SECURITY_DESCRIPTOR); + + // + // Map and Copy in the Sacl + // + + +// if new item { +// PRESENT=TRUE +// DEFAULTED=FALSE +// if (NULL) { +// set new pointer to NULL +// } else { +// copy into new SD +// } +// } else { +// copy PRESENT bit +// copy DEFAULTED bit +// if (NULL) { +// set new pointer to NULL +// } else { +// copy old one into new SD +// } +// } + + + + if (NewSacl == NULL) { + NewDescriptor->Sacl = NULL; + + } else { + RtlpApplyAclToObject( (PACL)NewSacl, GenericMapping ); + RtlMoveMemory( Field, NewSacl, NewSacl->AclSize ); + NewDescriptor->Sacl = (PACL)RtlPointerToOffset(Base,Field); + Field += NewSaclSize; + } + + + + if (NewSaclPresent) { + + // + // defaulted bit is off already + // + + RtlpSetControlBits( NewDescriptor, SE_SACL_PRESENT ); + + } else { + + // + // Propagate the SE_SACL_DEFAULTED and SE_SACL_PRESENT + // bits from the old security descriptor into the new + // one. + // + + RtlpPropagateControlBits( + NewDescriptor, + *IObjectsSecurityDescriptor, + SE_SACL_DEFAULTED | SE_SACL_PRESENT + ); + + } + + + + // + // Fill in Dacl field in new SD + // + + if (NewDacl == NULL) { + NewDescriptor->Dacl = NULL; + + } else { + RtlpApplyAclToObject( (PACL)NewDacl, GenericMapping ); + RtlMoveMemory( Field, NewDacl, NewDacl->AclSize ); + NewDescriptor->Dacl = (PACL)RtlPointerToOffset(Base,Field); + Field += NewDaclSize; + } + + if (NewDaclPresent) { + + // + // defaulted bit is off already + // + + RtlpSetControlBits( NewDescriptor, SE_DACL_PRESENT ); + + } else { + + // + // Propagate the SE_DACL_DEFAULTED and SE_DACL_PRESENT + // bits from the old security descriptor into the new + // one. + // + + RtlpPropagateControlBits( + NewDescriptor, + *IObjectsSecurityDescriptor, + SE_DACL_DEFAULTED | SE_DACL_PRESENT + ); + + } + +// if new item { +// PRESENT=TRUE +// DEFAULTED=FALSE +// if (NULL) { +// set new pointer to NULL +// } else { +// copy into new SD +// } +// } else { +// copy PRESENT bit +// copy DEFAULTED bit +// if (NULL) { +// set new pointer to NULL +// } else { +// copy old one into new SD +// } +// } + + + // + // Fill in Owner field in new SD + // + + RtlMoveMemory( Field, NewOwner, SeLengthSid(NewOwner) ); + NewDescriptor->Owner = (PSID)RtlPointerToOffset(Base,Field); + Field += NewOwnerSize; + + if (!NewOwnerPresent) { + + // + // Propagate the SE_OWNER_DEFAULTED bit from the old SD. + // If a new owner is being assigned, we want to leave + // SE_OWNER_DEFAULTED off, which means leave it alone. + // + + RtlpPropagateControlBits( + NewDescriptor, + *IObjectsSecurityDescriptor, + SE_OWNER_DEFAULTED + ); + + } else { + ASSERT( !RtlpAreControlBitsSet( NewDescriptor, SE_OWNER_DEFAULTED ) ); + } + + + // + // Fill in Group field in new SD + // + + RtlMoveMemory( Field, NewGroup, SeLengthSid(NewGroup) ); + NewDescriptor->Group = (PSID)RtlPointerToOffset(Base,Field); + + if (!NewGroupPresent) { + + // + // Propagate the SE_GROUP_DEFAULTED bit from the old SD + // If a new owner is being assigned, we want to leave + // SE_GROUP_DEFAULTED off, which means leave it alone. + // + + RtlpPropagateControlBits( + NewDescriptor, + *IObjectsSecurityDescriptor, + SE_GROUP_DEFAULTED + ); + } else { + ASSERT( !RtlpAreControlBitsSet( NewDescriptor, SE_GROUP_DEFAULTED ) ); + + } + + if (NT_SUCCESS( Status )) { + + // + // Free old descriptor + // + + RtlFreeHeap( HeapHandle, 0, (PVOID) *IObjectsSecurityDescriptor ); + + *ObjectsSecurityDescriptor = (PSECURITY_DESCRIPTOR)NewDescriptor; + } + + if (ServerAclAllocated == TRUE) { + RtlFreeHeap( RtlProcessHeap(), 0, ServerDacl ); + } + + return( Status ); +} + + + +NTSTATUS +RtlQuerySecurityObject ( + IN PSECURITY_DESCRIPTOR ObjectDescriptor, + IN SECURITY_INFORMATION SecurityInformation, + OUT PSECURITY_DESCRIPTOR ResultantDescriptor, + IN ULONG DescriptorLength, + OUT PULONG ReturnLength + ) + +/*++ + +Routine Description: + + Query information from a protected server object's existing security + descriptor. + + This procedure, called only from user mode, is used to retrieve + information from a security descriptor on an existing protected + server's object. All access checking is expected to be done before + calling this routine. This includes checking for READ_CONTROL, and + privilege to read a system ACL as appropriate. + + - - WARNING - - + + This service is for use by protected subsystems that project their own + type of object. This service is explicitly not for use by the + executive for executive objects and must not be called from kernel + mode. + + +Arguments: + + ObjectDescriptor - Points to a pointer to a security descriptor to be + queried. + + SecurityInformation - Identifies the security information being + requested. + + ResultantDescriptor - Points to buffer to receive the resultant + security descriptor. The resultant security descriptor will + contain all information requested by the SecurityInformation + parameter. + + DescriptorLength - Is an unsigned integer which indicates the length, + in bytes, of the buffer provided to receive the resultant + descriptor. + + ReturnLength - Receives an unsigned integer indicating the actual + number of bytes needed in the ResultantDescriptor to store the + requested information. If the value returned is greater than the + value passed via the DescriptorLength parameter, then + STATUS_BUFFER_TOO_SMALL is returned and no information is returned. + + +Return Value: + + STATUS_SUCCESS - The operation was successful. + + STATUS_BUFFER_TOO_SMALL - The buffer provided to receive the requested + information was not large enough to hold the information. No + information has been returned. + + STATUS_BAD_DESCRIPTOR_FORMAT - Indicates the provided object's security + descriptor was not in self-relative format. + +--*/ + +{ + + PSID Group; + PSID Owner; + PACL Dacl; + PACL Sacl; + + ULONG GroupSize = 0; + ULONG DaclSize = 0; + ULONG SaclSize = 0; + ULONG OwnerSize = 0; + + PCHAR Field; + PCHAR Base; + + + PISECURITY_DESCRIPTOR IObjectDescriptor; + PISECURITY_DESCRIPTOR IResultantDescriptor; + + + IResultantDescriptor = (PISECURITY_DESCRIPTOR)ResultantDescriptor; + IObjectDescriptor = (PISECURITY_DESCRIPTOR)ObjectDescriptor; + + // + // For each item specified in the SecurityInformation, extract it + // and get it to the point where it can be copied into a new + // descriptor. + // + + if (SecurityInformation & GROUP_SECURITY_INFORMATION) { + + Group = RtlpGroupAddrSecurityDescriptor(IObjectDescriptor); + GroupSize = (ULONG)LongAlign(SeLengthSid(Group)); + } + + if (SecurityInformation & DACL_SECURITY_INFORMATION) { + + Dacl = RtlpDaclAddrSecurityDescriptor( IObjectDescriptor ); + + if (Dacl != NULL) { + DaclSize = (ULONG)LongAlign(Dacl->AclSize); + } + } + + if (SecurityInformation & SACL_SECURITY_INFORMATION) { + + Sacl = RtlpSaclAddrSecurityDescriptor( IObjectDescriptor ); + + if (Sacl != NULL) { + SaclSize = (ULONG)LongAlign(Sacl->AclSize); + } + + } + + if (SecurityInformation & OWNER_SECURITY_INFORMATION) { + + Owner = RtlpOwnerAddrSecurityDescriptor ( IObjectDescriptor ); + OwnerSize = (ULONG)LongAlign(SeLengthSid(Owner)); + } + + *ReturnLength = sizeof( SECURITY_DESCRIPTOR ) + + GroupSize + + DaclSize + + SaclSize + + OwnerSize; + + if (*ReturnLength > DescriptorLength) { + return( STATUS_BUFFER_TOO_SMALL ); + } + + RtlCreateSecurityDescriptor( + ResultantDescriptor, + SECURITY_DESCRIPTOR_REVISION + ); + + RtlpSetControlBits( IResultantDescriptor, SE_SELF_RELATIVE ); + + Base = (PCHAR)(IResultantDescriptor); + Field = Base + (ULONG)sizeof(SECURITY_DESCRIPTOR); + + if (SecurityInformation & SACL_SECURITY_INFORMATION) { + + if (SaclSize > 0) { + RtlMoveMemory( Field, Sacl, SaclSize ); + IResultantDescriptor->Sacl = (PACL)RtlPointerToOffset(Base,Field); + Field += SaclSize; + } + + RtlpPropagateControlBits( + IResultantDescriptor, + IObjectDescriptor, + SE_SACL_PRESENT | SE_SACL_DEFAULTED + ); + } + + if (SecurityInformation & DACL_SECURITY_INFORMATION) { + + if (DaclSize > 0) { + RtlMoveMemory( Field, Dacl, DaclSize ); + IResultantDescriptor->Dacl = (PACL)RtlPointerToOffset(Base,Field); + Field += DaclSize; + } + + RtlpPropagateControlBits( + IResultantDescriptor, + IObjectDescriptor, + SE_DACL_PRESENT | SE_DACL_DEFAULTED + ); + } + + if (SecurityInformation & OWNER_SECURITY_INFORMATION) { + + if (OwnerSize > 0) { + RtlMoveMemory( Field, Owner, OwnerSize ); + IResultantDescriptor->Owner = (PSID)RtlPointerToOffset(Base,Field); + Field += OwnerSize; + } + + RtlpPropagateControlBits( + IResultantDescriptor, + IObjectDescriptor, + SE_OWNER_DEFAULTED + ); + + } + + if (SecurityInformation & GROUP_SECURITY_INFORMATION) { + + if (GroupSize > 0) { + RtlMoveMemory( Field, Group, GroupSize ); + IResultantDescriptor->Group = (PSID)RtlPointerToOffset(Base,Field); + } + + RtlpPropagateControlBits( + IResultantDescriptor, + IObjectDescriptor, + SE_GROUP_DEFAULTED + ); + } + + return( STATUS_SUCCESS ); + +} + + + + + +NTSTATUS +RtlDeleteSecurityObject ( + IN OUT PSECURITY_DESCRIPTOR * ObjectDescriptor + ) + + +/*++ + +Routine Description: + + Delete a protected server object's security descriptor. + + This procedure, called only from user mode, is used to delete a + security descriptor associated with a protected server's object. This + routine will normally be called by a protected server during object + deletion. + + - - WARNING - - + + This service is for use by protected subsystems that project their own + type of object. This service is explicitly not for use by the + executive for executive objects and must not be called from kernel + mode. + + +Arguments: + + ObjectDescriptor - Points to a pointer to a security descriptor to be + deleted. + + +Return Value: + + STATUS_SUCCESS - The operation was successful. + +--*/ + +{ + RtlFreeHeap( RtlProcessHeap(), 0, (PVOID)*ObjectDescriptor ); + + return( STATUS_SUCCESS ); + +} + + + + +NTSTATUS +RtlNewInstanceSecurityObject( + IN BOOLEAN ParentDescriptorChanged, + IN BOOLEAN CreatorDescriptorChanged, + IN PLUID OldClientTokenModifiedId, + OUT PLUID NewClientTokenModifiedId, + IN PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL, + IN PSECURITY_DESCRIPTOR CreatorDescriptor OPTIONAL, + OUT PSECURITY_DESCRIPTOR * NewDescriptor, + IN BOOLEAN IsDirectoryObject, + IN HANDLE Token, + IN PGENERIC_MAPPING GenericMapping + ) + +/*++ + +Routine Description: + + If the return status is STATUS_SUCCESS and the NewSecurity return + value is NULL, then the security desscriptor of the original + instance of the object is valid for this instance as well. + +Arguments: + + ParentDescriptorChanged - Supplies a flag indicating whether the + parent security descriptor has changed since the last time + this set of parameters was used. + + CreatorDescriptorChanged - Supplies a flag indicating whether the + creator security descriptor has changed since the last time + this set of parameters was used. + + OldClientTokenModifiedId - Supplies the ModifiedId from the passed + token that was in effect when this call was last made with + these parameters. If the current ModifiedId is different from + the one passed in here, the security descriptor must be + rebuilt. + + NewClientTokenModifiedId - Returns the current ModifiedId from the + passed token. + + ParentDescriptor - Supplies the Security Descriptor for the parent + directory under which a new object is being created. If there is + no parent directory, then this argument is specified as NULL. + + CreatorDescriptor - (Optionally) Points to a security descriptor + presented by the creator of the object. If the creator of the + object did not explicitly pass security information for the new + object, then a null pointer should be passed. + + NewDescriptor - Points to a pointer that is to be made to point to the + newly allocated self-relative security descriptor. + + IsDirectoryObject - Specifies if the new object is going to be a + directory object. A value of TRUE indicates the object is a + container of other objects. + + Token - Supplies the token for the client on whose behalf the + object is being created. If it is an impersonation token, + then it must be at SecurityIdentification level or higher. If + it is not an impersonation token, the operation proceeds + normally. + + A client token is used to retrieve default security + information for the new object, such as default owner, primary + group, and discretionary access control. The token must be + open for TOKEN_QUERY access. + + GenericMapping - Supplies a pointer to a generic mapping array denoting + the mapping between each generic right to specific rights. + +Return Value: + + return-value - Description of conditions needed to return value. - or - + None. + +--*/ + +{ + + TOKEN_STATISTICS ClientTokenStatistics; + ULONG ReturnLength; + NTSTATUS Status; + + + + // + // Get the current token modified LUID + // + + + Status = NtQueryInformationToken( + Token, // Handle + TokenStatistics, // TokenInformationClass + &ClientTokenStatistics, // TokenInformation + sizeof(TOKEN_STATISTICS), // TokenInformationLength + &ReturnLength // ReturnLength + ); + + if ( !NT_SUCCESS( Status )) { + return( Status ); + } + + *NewClientTokenModifiedId = ClientTokenStatistics.ModifiedId; + + if ( RtlEqualLuid(NewClientTokenModifiedId, OldClientTokenModifiedId) ) { + + if ( !(ParentDescriptorChanged || CreatorDescriptorChanged) ) { + + // + // The old security descriptor is valid for this new instance + // of the object type as well. Pass back success and NULL for + // the NewDescriptor. + // + + *NewDescriptor = NULL; + return( STATUS_SUCCESS ); + + } + } + + // + // Something has changed, take the long route and build a new + // descriptor + // + + return( RtlNewSecurityObject( ParentDescriptor, + CreatorDescriptor, + NewDescriptor, + IsDirectoryObject, + Token, + GenericMapping + )); +} + + + + +NTSTATUS +RtlNewSecurityGrantedAccess( + IN ACCESS_MASK DesiredAccess, + OUT PPRIVILEGE_SET Privileges, + IN OUT PULONG Length, + IN HANDLE Token OPTIONAL, + IN PGENERIC_MAPPING GenericMapping, + OUT PACCESS_MASK RemainingDesiredAccess + ) + +/*++ + +Routine Description: + + This routine implements privilege policy by examining the bits in + a DesiredAccess mask and adjusting them based on privilege checks. + + Currently, a request for ACCESS_SYSTEM_SECURITY may only be satisfied + by the caller having SeSecurityPrivilege. + + Note that this routine is only to be called when an object is being + created. When an object is being opened, it is expected that + NtAccessCheck will be called, and that routine will implement + another policy for substituting privileges for DACL access. + +Arguments: + + DesiredAccess - Supplies the user's desired access mask + + Privileges - Supplies a pointer to an empty buffer in which will + be returned a privilege set describing any privileges that were + used to gain access. + + Note that this is not an optional parameter, that is, enough + room for a single privilege must always be passed. + + Length - Supplies the length of the Privileges parameter in bytes. + If the supplies length is not adequate to store the entire + privilege set, this field will return the minimum length required. + + Token - (optionally) Supplies the token for the client on whose + behalf the object is being accessed. If this value is + specified as null, then the token on the thread is opened and + examined to see if it is an impersonation token. If it is, + then it must be at SecurityIdentification level or higher. If + it is not an impersonation token, the operation proceeds + normally. + + GenericMapping - Supplies the generic mapping associated with this + object type. + + RemainingDesiredAccess - Returns the DesiredAccess mask after any bits + have been masked off. If no access types could be granted, this + mask will be identical to the one passed in. + +Return Value: + + STATUS_SUCCESS - The operation completed successfully. + + STATUS_BUFFER_TOO_SMALL - The passed buffer was not large enough + to contain the information being returned. + + STATUS_BAD_IMPERSONATION_LEVEL - The caller or passed token was + impersonating, but not at a high enough level. + + +--*/ + +{ + PRIVILEGE_SET RequiredPrivilege; + BOOLEAN Result = FALSE; + NTSTATUS Status; + ULONG PrivilegeCount = 0; + HANDLE ThreadToken; + BOOLEAN TokenPassed; + TOKEN_STATISTICS ThreadTokenStatistics; + ULONG ReturnLength; + ULONG SizeRequired; + ULONG PrivilegeNumber = 0; + + + // + // If the caller hasn't passed a token, call the kernel and get + // his impersonation token. This call will fail if the caller is + // not impersonating a client, so if the caller is not + // impersonating someone, he'd better have passed in an explicit + // token. + // + + if (!ARGUMENT_PRESENT( Token )) { + + Status = NtOpenThreadToken( + NtCurrentThread(), + TOKEN_QUERY, + TRUE, + &ThreadToken + ); + + TokenPassed = FALSE; + + if (!NT_SUCCESS( Status )) { + return( Status ); + } + + } else { + + ThreadToken = Token; + TokenPassed = TRUE; + } + + Status = NtQueryInformationToken( + ThreadToken, // Handle + TokenStatistics, // TokenInformationClass + &ThreadTokenStatistics, // TokenInformation + sizeof(TOKEN_STATISTICS), // TokenInformationLength + &ReturnLength // ReturnLength + ); + + ASSERT( NT_SUCCESS(Status) ); + + RtlMapGenericMask( + &DesiredAccess, + GenericMapping + ); + + *RemainingDesiredAccess = DesiredAccess; + + if ( DesiredAccess & ACCESS_SYSTEM_SECURITY ) { + + RequiredPrivilege.PrivilegeCount = 1; + RequiredPrivilege.Control = PRIVILEGE_SET_ALL_NECESSARY; + RequiredPrivilege.Privilege[0].Luid = RtlConvertLongToLuid(SE_SECURITY_PRIVILEGE); + RequiredPrivilege.Privilege[0].Attributes = 0; + + // + // NtPrivilegeCheck will make sure we are impersonating + // properly. + // + + Status = NtPrivilegeCheck( + ThreadToken, + &RequiredPrivilege, + &Result + ); + + if ( (!NT_SUCCESS ( Status )) || (!Result) ) { + + if (!TokenPassed) { + NtClose( ThreadToken ); + } + + if ( !NT_SUCCESS( Status )) { + return( Status ); + } + + if ( !Result ) { + return( STATUS_PRIVILEGE_NOT_HELD ); + } + + } + + // + // We have the required privilege, turn off the bit in + // copy of the input mask and remember that we need to return + // this privilege. + // + + *RemainingDesiredAccess &= ~ACCESS_SYSTEM_SECURITY; + } + + if (!TokenPassed) { + NtClose( ThreadToken ); + } + + SizeRequired = sizeof(PRIVILEGE_SET); + + if ( SizeRequired > *Length ) { + *Length = SizeRequired; + return( STATUS_BUFFER_TOO_SMALL ); + } + + if (Result) { + + Privileges->PrivilegeCount = 1; + Privileges->Control = 0; + Privileges->Privilege[PrivilegeNumber].Luid = RtlConvertLongToLuid(SE_SECURITY_PRIVILEGE); + Privileges->Privilege[PrivilegeNumber].Attributes = SE_PRIVILEGE_USED_FOR_ACCESS; + + } else { + + Privileges->PrivilegeCount = 0; + Privileges->Control = 0; + Privileges->Privilege[PrivilegeNumber].Luid = RtlConvertLongToLuid(0); + Privileges->Privilege[PrivilegeNumber].Attributes = 0; + + } + + return( STATUS_SUCCESS ); + +} + + + +NTSTATUS +RtlCopySecurityDescriptor( + IN PSECURITY_DESCRIPTOR InputSecurityDescriptor, + OUT PSECURITY_DESCRIPTOR *OutputSecurityDescriptor + ) + +/*++ + +Routine Description: + + This routine will copy a self-relative security descriptor from + any memory into the correct type of memory required by security + descriptor Rtl routines. + + This allows security descriptors to be kept in whatever kind of + storage is most convenient for the current application. A security + descriptor should be copied via this routine and the copy passed + into any Rtl routine that in any way modify the security descriptor + (eg RtlSetSecurityObject). + + The storage allocated by this routine must be freed by + RtlDeleteSecurityObject. + +Arguments: + + InputSecurityDescriptor - contains the source security descriptor + + OutputSecurityDescriptor - returns a copy of the security descriptor + in the correct kind of memory. + + +Return Value: + + STATUS_NO_MEMORY - There was not enough memory available to the current + process to complete this operation. + +--*/ + +{ + + PACL Dacl; + PACL Sacl; + + PSID Owner; + PSID PrimaryGroup; + + ULONG DaclSize; + ULONG OwnerSize; + ULONG PrimaryGroupSize; + ULONG SaclSize; + ULONG TotalSize; + + PISECURITY_DESCRIPTOR ISecurityDescriptor = + (PISECURITY_DESCRIPTOR)InputSecurityDescriptor; + + + RtlpQuerySecurityDescriptor( + ISecurityDescriptor, + &Owner, + &OwnerSize, + &PrimaryGroup, + &PrimaryGroupSize, + &Dacl, + &DaclSize, + &Sacl, + &SaclSize + ); + + TotalSize = sizeof(SECURITY_DESCRIPTOR) + + OwnerSize + + PrimaryGroupSize + + DaclSize + + SaclSize; + + *OutputSecurityDescriptor = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( SE_TAG ), TotalSize ); + + if ( *OutputSecurityDescriptor == NULL ) { + return( STATUS_NO_MEMORY ); + } + + RtlMoveMemory( *OutputSecurityDescriptor, + ISecurityDescriptor, + TotalSize + ); + + return( STATUS_SUCCESS ); + +} + + +NTSTATUS +RtlpInitializeAllowedAce( + IN PACCESS_ALLOWED_ACE AllowedAce, + IN USHORT AceSize, + IN UCHAR InheritFlags, + IN UCHAR AceFlags, + IN ACCESS_MASK Mask, + IN PSID AllowedSid + ) +/*++ + +Routine Description: + + This function assigns the specified ACE values into an allowed type ACE. + +Arguments: + + AllowedAce - Supplies a pointer to the ACE that is initialized. + + AceSize - Supplies the size of the ACE in bytes. + + InheritFlags - Supplies ACE inherit flags. + + AceFlags - Supplies ACE type specific control flags. + + Mask - Supplies the allowed access masks. + + AllowedSid - Supplies the pointer to the SID of user/group which is allowed + the specified access. + +Return Value: + + Returns status from RtlCopySid. + +--*/ +{ + AllowedAce->Header.AceType = ACCESS_ALLOWED_ACE_TYPE; + AllowedAce->Header.AceSize = AceSize; + AllowedAce->Header.AceFlags = AceFlags | InheritFlags; + + AllowedAce->Mask = Mask; + + return RtlCopySid( + RtlLengthSid(AllowedSid), + &(AllowedAce->SidStart), + AllowedSid + ); +} + + +NTSTATUS +RtlpInitializeDeniedAce( + IN PACCESS_DENIED_ACE DeniedAce, + IN USHORT AceSize, + IN UCHAR InheritFlags, + IN UCHAR AceFlags, + IN ACCESS_MASK Mask, + IN PSID DeniedSid + ) +/*++ + +Routine Description: + + This function assigns the specified ACE values into a denied type ACE. + +Arguments: + + DeniedAce - Supplies a pointer to the ACE that is initialized. + + AceSize - Supplies the size of the ACE in bytes. + + InheritFlags - Supplies ACE inherit flags. + + AceFlags - Supplies ACE type specific control flags. + + Mask - Supplies the denied access masks. + + AllowedSid - Supplies the pointer to the SID of user/group which is denied + the specified access. + +Return Value: + + Returns status from RtlCopySid. + +--*/ +{ + DeniedAce->Header.AceType = ACCESS_DENIED_ACE_TYPE; + DeniedAce->Header.AceSize = AceSize; + DeniedAce->Header.AceFlags = AceFlags | InheritFlags; + + DeniedAce->Mask = Mask; + + return RtlCopySid( + RtlLengthSid(DeniedSid), + &(DeniedAce->SidStart), + DeniedSid + ); +} + + +NTSTATUS +RtlpInitializeAuditAce( + IN PACCESS_ALLOWED_ACE AuditAce, + IN USHORT AceSize, + IN UCHAR InheritFlags, + IN UCHAR AceFlags, + IN ACCESS_MASK Mask, + IN PSID AuditSid + ) +/*++ + +Routine Description: + + This function assigns the specified ACE values into an audit type ACE. + +Arguments: + + AuditAce - Supplies a pointer to the ACE that is initialized. + + AceSize - Supplies the size of the ACE in bytes. + + InheritFlags - Supplies ACE inherit flags. + + AceFlags - Supplies ACE type specific control flags. + + Mask - Supplies the allowed access masks. + + AuditSid - Supplies the pointer to the SID of user/group which is to be + audited. + +Return Value: + + Returns status from RtlCopySid. + +--*/ +{ + AuditAce->Header.AceType = SYSTEM_AUDIT_ACE_TYPE; + AuditAce->Header.AceSize = AceSize; + AuditAce->Header.AceFlags = AceFlags | InheritFlags; + + AuditAce->Mask = Mask; + + return RtlCopySid( + RtlLengthSid(AuditSid), + &(AuditAce->SidStart), + AuditSid + ); +} + +NTSTATUS +RtlCreateAndSetSD( + IN PRTL_ACE_DATA AceData, + IN ULONG AceCount, + IN PSID OwnerSid OPTIONAL, + IN PSID GroupSid OPTIONAL, + OUT PSECURITY_DESCRIPTOR *NewDescriptor + ) +/*++ + +Routine Description: + + This function creates an absolute security descriptor containing + the supplied ACE information. + + A sample usage of this function: + + // + // Order matters! These ACEs are inserted into the DACL in the + // following order. Security access is granted or denied based on + // the order of the ACEs in the DACL. + // + + RTL_ACE_DATA AceData[4] = { + {ACCESS_ALLOWED_ACE_TYPE, 0, 0, + GENERIC_ALL, &LocalAdminSid}, + + {ACCESS_DENIED_ACE_TYPE, 0, 0, + GENERIC_ALL, &NetworkSid}, + + {ACCESS_ALLOWED_ACE_TYPE, 0, 0, + WKSTA_CONFIG_GUEST_INFO_GET | + WKSTA_CONFIG_USER_INFO_GET, &DomainUsersSid}, + + {ACCESS_ALLOWED_ACE_TYPE, 0, 0, + WKSTA_CONFIG_GUEST_INFO_GET, &DomainGuestsSid} + }; + + PSECURITY_DESCRIPTOR WkstaSecurityDescriptor; + + + return RtlCreateAndSetSD( + AceData, + 4, + LocalSystemSid, + LocalSystemSid, + &WkstaSecurityDescriptor + ); + +Arguments: + + AceData - Supplies the structure of information that describes the DACL. + + AceCount - Supplies the number of entries in AceData structure. + + OwnerSid - Supplies the pointer to the SID of the security descriptor + owner. If not specified, a security descriptor with no owner + will be created. + + GroupSid - Supplies the pointer to the SID of the security descriptor + primary group. If not specified, a security descriptor with no primary + group will be created. + + NewDescriptor - Returns a pointer to the absolute security descriptor + allocated using RtlAllocateHeap. + +Return Value: + + STATUS_SUCCESS - if successful + STATUS_NO_MEMORY - if cannot allocate memory for DACL, ACEs, and + security descriptor. + + Any other status codes returned from the security Rtl routines. + + NOTE : the user security object created by calling this function may be + freed up by calling RtlDeleteSecurityObject(). + +--*/ +{ + + NTSTATUS ntstatus; + ULONG i; + + // + // Pointer to memory dynamically allocated by this routine to hold + // the absolute security descriptor, the DACL, the SACL, and all the ACEs. + // + // +---------------------------------------------------------------+ + // | Security Descriptor | + // +-------------------------------+-------+---------------+-------+ + // | DACL | ACE 1 | . . . | ACE n | + // +-------------------------------+-------+---------------+-------+ + // | SACL | ACE 1 | . . . | ACE n | + // +-------------------------------+-------+---------------+-------+ + // + + PSECURITY_DESCRIPTOR AbsoluteSd = NULL; + PACL Dacl = NULL; // Pointer to the DACL portion of above buffer + PACL Sacl = NULL; // Pointer to the SACL portion of above buffer + + ULONG DaclSize = sizeof(ACL); + ULONG SaclSize = sizeof(ACL); + ULONG MaxAceSize = 0; + PVOID MaxAce = NULL; + + PCHAR CurrentAvailable; + ULONG Size; + + PVOID HeapHandle = RtlProcessHeap(); + + + ASSERT( AceCount > 0 ); + + // + // Compute the total size of the DACL and SACL ACEs and the maximum + // size of any ACE. + // + + for (i = 0; i < AceCount; i++) { + ULONG AceSize; + + AceSize = RtlLengthSid(*(AceData[i].Sid)); + + switch (AceData[i].AceType) { + case ACCESS_ALLOWED_ACE_TYPE: + AceSize += sizeof(ACCESS_ALLOWED_ACE); + DaclSize += AceSize; + break; + + case ACCESS_DENIED_ACE_TYPE: + AceSize += sizeof(ACCESS_DENIED_ACE); + DaclSize += AceSize; + break; + + case SYSTEM_AUDIT_ACE_TYPE: + AceSize += sizeof(SYSTEM_AUDIT_ACE); + SaclSize += AceSize; + break; + + default: + return STATUS_INVALID_PARAMETER; + } + + MaxAceSize = MaxAceSize > AceSize ? MaxAceSize : AceSize; + } + + // + // Allocate a chunk of memory large enough for the security descriptor, + // the DACL, the SACL and all ACEs. + // + // A security descriptor is of opaque data type but + // SECURITY_DESCRIPTOR_MIN_LENGTH is the right size. + // + + Size = SECURITY_DESCRIPTOR_MIN_LENGTH; + if ( DaclSize != sizeof(ACL) ) { + Size += DaclSize; + } + if ( SaclSize != sizeof(ACL) ) { + Size += SaclSize; + } + + if ((AbsoluteSd = RtlAllocateHeap( + HeapHandle, MAKE_TAG( SE_TAG ), + Size + )) == NULL) { + ntstatus = STATUS_NO_MEMORY; + goto Cleanup; + } + + // + // Initialize the Dacl and Sacl + // + + CurrentAvailable = (PCHAR)AbsoluteSd + SECURITY_DESCRIPTOR_MIN_LENGTH; + + if ( DaclSize != sizeof(ACL) ) { + Dacl = (PACL)CurrentAvailable; + CurrentAvailable += DaclSize; + + ntstatus = RtlCreateAcl( Dacl, DaclSize, ACL_REVISION ); + + if ( !NT_SUCCESS(ntstatus) ) { + goto Cleanup; + } + } + + if ( SaclSize != sizeof(ACL) ) { + Sacl = (PACL)CurrentAvailable; + CurrentAvailable += SaclSize; + + ntstatus = RtlCreateAcl( Sacl, SaclSize, ACL_REVISION ); + + if ( !NT_SUCCESS(ntstatus) ) { + goto Cleanup; + } + } + + // + // Allocate a temporary buffer big enough for the biggest ACE. + // + + if ((MaxAce = RtlAllocateHeap( + HeapHandle, MAKE_TAG( SE_TAG ), + MaxAceSize + )) == NULL ) { + ntstatus = STATUS_NO_MEMORY; + goto Cleanup; + } + + // + // Initialize each ACE, and append it into the end of the DACL or SACL. + // + + for (i = 0; i < AceCount; i++) { + ULONG AceSize; + PACL CurrentAcl; + + AceSize = RtlLengthSid(*(AceData[i].Sid)); + + switch (AceData[i].AceType) { + case ACCESS_ALLOWED_ACE_TYPE: + + AceSize += sizeof(ACCESS_ALLOWED_ACE); + CurrentAcl = Dacl; + ntstatus = RtlpInitializeAllowedAce( + MaxAce, + (USHORT) AceSize, + AceData[i].InheritFlags, + AceData[i].AceFlags, + AceData[i].Mask, + *(AceData[i].Sid) + ); + break; + + case ACCESS_DENIED_ACE_TYPE: + AceSize += sizeof(ACCESS_DENIED_ACE); + CurrentAcl = Dacl; + ntstatus = RtlpInitializeDeniedAce( + MaxAce, + (USHORT) AceSize, + AceData[i].InheritFlags, + AceData[i].AceFlags, + AceData[i].Mask, + *(AceData[i].Sid) + ); + break; + + case SYSTEM_AUDIT_ACE_TYPE: + AceSize += sizeof(SYSTEM_AUDIT_ACE); + CurrentAcl = Sacl; + ntstatus = RtlpInitializeAuditAce( + MaxAce, + (USHORT) AceSize, + AceData[i].InheritFlags, + AceData[i].AceFlags, + AceData[i].Mask, + *(AceData[i].Sid) + ); + break; + } + + if ( !NT_SUCCESS( ntstatus ) ) { + goto Cleanup; + } + + // + // Append the initialized ACE to the end of DACL or SACL + // + + if (! NT_SUCCESS (ntstatus = RtlAddAce( + CurrentAcl, + ACL_REVISION, + MAXULONG, + MaxAce, + AceSize + ))) { + goto Cleanup; + } + } + + // + // Create the security descriptor with absolute pointers to SIDs + // and ACLs. + // + // Owner = OwnerSid + // Group = GroupSid + // Dacl = Dacl + // Sacl = Sacl + // + + if (! NT_SUCCESS(ntstatus = RtlCreateSecurityDescriptor( + AbsoluteSd, + SECURITY_DESCRIPTOR_REVISION + ))) { + goto Cleanup; + } + + if (! NT_SUCCESS(ntstatus = RtlSetOwnerSecurityDescriptor( + AbsoluteSd, + OwnerSid, + FALSE + ))) { + goto Cleanup; + } + + if (! NT_SUCCESS(ntstatus = RtlSetGroupSecurityDescriptor( + AbsoluteSd, + GroupSid, + FALSE + ))) { + goto Cleanup; + } + + if (! NT_SUCCESS(ntstatus = RtlSetDaclSecurityDescriptor( + AbsoluteSd, + TRUE, + Dacl, + FALSE + ))) { + goto Cleanup; + } + + if (! NT_SUCCESS(ntstatus = RtlSetSaclSecurityDescriptor( + AbsoluteSd, + FALSE, + Sacl, + FALSE + ))) { + goto Cleanup; + } + + // + // Done + // + + ntstatus = STATUS_SUCCESS; + + // + // Clean up + // + +Cleanup: + // + // Either return the security descriptor to the caller or delete it + // + + if ( NT_SUCCESS( ntstatus ) ) { + *NewDescriptor = AbsoluteSd; + } else if ( AbsoluteSd != NULL ) { + (void) RtlFreeHeap(HeapHandle, 0, AbsoluteSd); + } + + // + // Delete the temporary ACE + // + + if ( MaxAce != NULL ) { + (void) RtlFreeHeap(HeapHandle, 0, MaxAce); + } + return ntstatus; +} + + +NTSTATUS +RtlCreateUserSecurityObject( + IN PRTL_ACE_DATA AceData, + IN ULONG AceCount, + IN PSID OwnerSid, + IN PSID GroupSid, + IN BOOLEAN IsDirectoryObject, + IN PGENERIC_MAPPING GenericMapping, + OUT PSECURITY_DESCRIPTOR *NewDescriptor + ) +/*++ + +Routine Description: + + This function creates the DACL for the security descriptor based on + on the ACE information specified, and creates the security descriptor + which becomes the user-mode security object. + + A sample usage of this function: + + // + // Structure that describes the mapping of Generic access rights to + // object specific access rights for the ConfigurationInfo object. + // + + GENERIC_MAPPING WsConfigInfoMapping = { + STANDARD_RIGHTS_READ | // Generic read + WKSTA_CONFIG_GUEST_INFO_GET | + WKSTA_CONFIG_USER_INFO_GET | + WKSTA_CONFIG_ADMIN_INFO_GET, + STANDARD_RIGHTS_WRITE | // Generic write + WKSTA_CONFIG_INFO_SET, + STANDARD_RIGHTS_EXECUTE, // Generic execute + WKSTA_CONFIG_ALL_ACCESS // Generic all + }; + + // + // Order matters! These ACEs are inserted into the DACL in the + // following order. Security access is granted or denied based on + // the order of the ACEs in the DACL. + // + + RTL_ACE_DATA AceData[4] = { + {ACCESS_ALLOWED_ACE_TYPE, 0, 0, + GENERIC_ALL, &LocalAdminSid}, + + {ACCESS_DENIED_ACE_TYPE, 0, 0, + GENERIC_ALL, &NetworkSid}, + + {ACCESS_ALLOWED_ACE_TYPE, 0, 0, + WKSTA_CONFIG_GUEST_INFO_GET | + WKSTA_CONFIG_USER_INFO_GET, &DomainUsersSid}, + + {ACCESS_ALLOWED_ACE_TYPE, 0, 0, + WKSTA_CONFIG_GUEST_INFO_GET, &DomainGuestsSid} + }; + + PSECURITY_DESCRIPTOR WkstaSecurityObject; + + + return RtlCreateUserSecurityObject( + AceData, + 4, + LocalSystemSid, + LocalSystemSid, + FALSE, + &WsConfigInfoMapping, + &WkstaSecurityObject + ); + +Arguments: + + AceData - Supplies the structure of information that describes the DACL. + + AceCount - Supplies the number of entries in AceData structure. + + OwnerSid - Supplies the pointer to the SID of the security descriptor + owner. + + GroupSid - Supplies the pointer to the SID of the security descriptor + primary group. + + IsDirectoryObject - Supplies the flag which indicates whether the + user-mode object is a directory object. + + GenericMapping - Supplies the pointer to a generic mapping array denoting + the mapping between each generic right to specific rights. + + NewDescriptor - Returns a pointer to the self-relative security descriptor + which represents the user-mode object. + +Return Value: + + STATUS_SUCCESS - if successful + STATUS_NO_MEMORY - if cannot allocate memory for DACL, ACEs, and + security descriptor. + + Any other status codes returned from the security Rtl routines. + + NOTE : the user security object created by calling this function may be + freed up by calling RtlDeleteSecurityObject(). + +--*/ +{ + + NTSTATUS ntstatus; + PSECURITY_DESCRIPTOR AbsoluteSd; + HANDLE TokenHandle; + PVOID HeapHandle = RtlProcessHeap(); + + ntstatus = RtlCreateAndSetSD( + AceData, + AceCount, + OwnerSid, + GroupSid, + &AbsoluteSd + ); + + if (! NT_SUCCESS(ntstatus)) { + return ntstatus; + } + + ntstatus = NtOpenProcessToken( + NtCurrentProcess(), + TOKEN_QUERY, + &TokenHandle + ); + + if (! NT_SUCCESS(ntstatus)) { + (void) RtlFreeHeap(HeapHandle, 0, AbsoluteSd); + return ntstatus; + } + + // + // Create the security object (a user-mode object is really a pseudo- + // object represented by a security descriptor that have relative + // pointers to SIDs and ACLs). This routine allocates the memory to + // hold the relative security descriptor so the memory allocated for the + // DACL, ACEs, and the absolute descriptor can be freed. + // + ntstatus = RtlNewSecurityObject( + NULL, // Parent descriptor + AbsoluteSd, // Creator descriptor + NewDescriptor, // Pointer to new descriptor + IsDirectoryObject, // Is directory object + TokenHandle, // Token + GenericMapping // Generic mapping + ); + + (void) NtClose(TokenHandle); + + // + // Free dynamic memory before returning + // + (void) RtlFreeHeap(HeapHandle, 0, AbsoluteSd); + return ntstatus; +} + + + +NTSTATUS +RtlpGetDefaultsSubjectContext( + HANDLE ClientToken, + OUT PTOKEN_OWNER *OwnerInfo, + OUT PTOKEN_PRIMARY_GROUP *GroupInfo, + OUT PTOKEN_DEFAULT_DACL *DefaultDaclInfo, + OUT PTOKEN_OWNER *ServerOwner, + OUT PTOKEN_PRIMARY_GROUP *ServerGroup + ) +{ + HANDLE PrimaryToken; + PVOID HeapHandle; + NTSTATUS Status; + ULONG ServerGroupInfoSize; + ULONG ServerOwnerInfoSize; + ULONG TokenDaclInfoSize; + ULONG TokenGroupInfoSize; + ULONG TokenOwnerInfoSize; + + BOOLEAN ClosePrimaryToken = FALSE; + + *OwnerInfo = NULL; + *GroupInfo = NULL; + *DefaultDaclInfo = NULL; + *ServerOwner = NULL; + *ServerGroup = NULL; + + HeapHandle = RtlProcessHeap(); + + // + // Obtain the default owner from the client. + // + + Status = NtQueryInformationToken( + ClientToken, // Handle + TokenOwner, // TokenInformationClass + NULL, // TokenInformation + 0, // TokenInformationLength + &TokenOwnerInfoSize // ReturnLength + ); + + if ( STATUS_BUFFER_TOO_SMALL != Status ) { + goto Cleanup; + } + + *OwnerInfo = RtlAllocateHeap( HeapHandle, MAKE_TAG( SE_TAG ), TokenOwnerInfoSize ); + + if ( *OwnerInfo == NULL ) { + Status = STATUS_NO_MEMORY; + goto Cleanup; + } + + Status = NtQueryInformationToken( + ClientToken, // Handle + TokenOwner, // TokenInformationClass + *OwnerInfo, // TokenInformation + TokenOwnerInfoSize, // TokenInformationLength + &TokenOwnerInfoSize // ReturnLength + ); + + if (!NT_SUCCESS( Status )) { + goto Cleanup; + } + + // + // Obtain the default group from the client token. + // + + Status = NtQueryInformationToken( + ClientToken, // Handle + TokenPrimaryGroup, // TokenInformationClass + *GroupInfo, // TokenInformation + 0, // TokenInformationLength + &TokenGroupInfoSize // ReturnLength + ); + + if ( STATUS_BUFFER_TOO_SMALL != Status ) { + goto Cleanup; + } + + *GroupInfo = RtlAllocateHeap( HeapHandle, MAKE_TAG( SE_TAG ), TokenGroupInfoSize ); + + if ( *GroupInfo == NULL ) { + + Status = STATUS_NO_MEMORY; + goto Cleanup; + } + + Status = NtQueryInformationToken( + ClientToken, // Handle + TokenPrimaryGroup, // TokenInformationClass + *GroupInfo, // TokenInformation + TokenGroupInfoSize, // TokenInformationLength + &TokenGroupInfoSize // ReturnLength + ); + + if (!NT_SUCCESS( Status )) { + goto Cleanup; + } + + Status = NtQueryInformationToken( + ClientToken, // Handle + TokenDefaultDacl, // TokenInformationClass + *DefaultDaclInfo, // TokenInformation + 0, // TokenInformationLength + &TokenDaclInfoSize // ReturnLength + ); + + if ( STATUS_BUFFER_TOO_SMALL != Status ) { + goto Cleanup; + } + + *DefaultDaclInfo = RtlAllocateHeap( HeapHandle, MAKE_TAG( SE_TAG ), TokenDaclInfoSize ); + + if ( *DefaultDaclInfo == NULL ) { + + Status = STATUS_NO_MEMORY; + goto Cleanup; + } + + Status = NtQueryInformationToken( + ClientToken, // Handle + TokenDefaultDacl, // TokenInformationClass + *DefaultDaclInfo, // TokenInformation + TokenDaclInfoSize, // TokenInformationLength + &TokenDaclInfoSize // ReturnLength + ); + + if (!NT_SUCCESS( Status )) { + goto Cleanup; + } + + // + // Now open the primary token to determine how to substitute for + // ServerOwner and ServerGroup. + // + + Status = NtOpenProcessToken( + NtCurrentProcess(), + TOKEN_QUERY, + &PrimaryToken + ); + + if (!NT_SUCCESS( Status )) { + ClosePrimaryToken = FALSE; + goto Cleanup; + } else { + ClosePrimaryToken = TRUE; + } + + Status = NtQueryInformationToken( + PrimaryToken, // Handle + TokenOwner, // TokenInformationClass + NULL, // TokenInformation + 0, // TokenInformationLength + &ServerOwnerInfoSize // ReturnLength + ); + + if ( STATUS_BUFFER_TOO_SMALL != Status ) { + goto Cleanup; + } + + *ServerOwner = RtlAllocateHeap( HeapHandle, MAKE_TAG( SE_TAG ), ServerOwnerInfoSize ); + + if ( *ServerOwner == NULL ) { + Status = STATUS_NO_MEMORY; + goto Cleanup; + } + + Status = NtQueryInformationToken( + PrimaryToken, // Handle + TokenOwner, // TokenInformationClass + *ServerOwner, // TokenInformation + ServerOwnerInfoSize, // TokenInformationLength + &ServerOwnerInfoSize // ReturnLength + ); + + if (!NT_SUCCESS( Status )) { + goto Cleanup; + } + + // + // Find the server group. + // + + Status = NtQueryInformationToken( + PrimaryToken, // Handle + TokenPrimaryGroup, // TokenInformationClass + *ServerGroup, // TokenInformation + 0, // TokenInformationLength + &ServerGroupInfoSize // ReturnLength + ); + + if ( STATUS_BUFFER_TOO_SMALL != Status ) { + goto Cleanup; + } + + *ServerGroup = RtlAllocateHeap( HeapHandle, MAKE_TAG( SE_TAG ), ServerGroupInfoSize ); + + if ( *ServerGroup == NULL ) { + goto Cleanup; + } + + Status = NtQueryInformationToken( + PrimaryToken, // Handle + TokenPrimaryGroup, // TokenInformationClass + *ServerGroup, // TokenInformation + ServerGroupInfoSize, // TokenInformationLength + &ServerGroupInfoSize // ReturnLength + ); + + if (!NT_SUCCESS( Status )) { + goto Cleanup; + } + + NtClose( PrimaryToken ); + + return( STATUS_SUCCESS ); + +Cleanup: + + if (*OwnerInfo != NULL) { + RtlFreeHeap( HeapHandle, 0, (PVOID)*OwnerInfo ); + *OwnerInfo = NULL; + } + + if (*GroupInfo != NULL) { + RtlFreeHeap( HeapHandle, 0, (PVOID)*GroupInfo ); + *GroupInfo = NULL; + } + + if (*DefaultDaclInfo != NULL) { + RtlFreeHeap( HeapHandle, 0, (PVOID)*DefaultDaclInfo ); + *DefaultDaclInfo = NULL; + } + + if (*ServerOwner != NULL) { + RtlFreeHeap( HeapHandle, 0, (PVOID)*ServerOwner ); + *ServerOwner = NULL; + } + + if (*ServerGroup != NULL) { + RtlFreeHeap( HeapHandle, 0, (PVOID)*ServerGroup ); + *ServerGroup = NULL; + } + + if (ClosePrimaryToken == TRUE) { + NtClose( PrimaryToken ); + } + + return( Status ); +} + + +NTSTATUS +RtlpCreateServerAcl( + 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)RtlLengthSid( 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)RtlLengthSid(UntrustedSid) > ServerSidSize) { + RequiredSize += ((USHORT)RtlLengthSid(UntrustedSid) - ServerSidSize); + } else { + RequiredSize += (ServerSidSize - (USHORT)RtlLengthSid(UntrustedSid)); + + } + } + } + + RequiredSize += Ace->AceSize; + } + + (*ServerAcl) = (PACL)RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( SE_TAG ), RequiredSize ); + + 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, + RtlLengthSid(ServerSid) + ); + + Target = (PVOID)((ULONG)Target + (UCHAR)RtlLengthSid(ServerSid)); + + // + // Now copy in the correct client SID. We can copy this right out of + // the original ACE. + // + + RtlMoveMemory( + Target, + ClientSid, + RtlLengthSid(ClientSid) + ); + + Target = (PVOID)((ULONG)Target + RtlLengthSid(ClientSid)); + + // + // Set the size of the ACE accordingly + // + + ((PKNOWN_COMPOUND_ACE)AcePosition)->Header.AceSize = + (USHORT)FIELD_OFFSET(KNOWN_COMPOUND_ACE, SidStart) + + (USHORT)RtlLengthSid(ServerSid) + + (USHORT)RtlLengthSid(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 ); +} -- cgit v1.2.3