Windows2000/private/ntos/dll/seurtl.c

1682 lines
50 KiB
C
Raw Normal View History

2001-01-01 00:00:00 +01:00
/*++
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
--*/
#include <ntos.h>
#include <nturtl.h>
#include <ntlsa.h> // needed for RtlGetPrimaryDomain
#include "seopaque.h"
#include "sertlp.h"
#include "ldrp.h"
// 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
RtlNewSecurityObjectEx(
IN PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL,
IN PSECURITY_DESCRIPTOR CreatorDescriptor OPTIONAL,
OUT PSECURITY_DESCRIPTOR * NewDescriptor,
IN GUID * ObjectType OPTIONAL,
IN BOOLEAN IsDirectoryObject,
IN ULONG AutoInheritFlags,
IN HANDLE Token OPTIONAL,
IN PGENERIC_MAPPING GenericMapping
)
/*++
Routine Description:
See RtlpNewSecurityObject.
- - 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:
See RtlpNewSecurityObject.
Return Value:
See RtlpNewSecurityObject.
--*/
{
// Simple call the newer RtlpNewSecurityObject
return RtlpNewSecurityObject(
ParentDescriptor,
CreatorDescriptor,
NewDescriptor,
ObjectType,
IsDirectoryObject,
AutoInheritFlags,
Token,
GenericMapping);
}
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:
See RtlpNewSecurityObject.
- - 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:
See RtlpNewSecurityObject.
Return Value:
See RtlpNewSecurityObject.
--*/
{
// Simple call the newer RtlpNewSecurityObject
return RtlpNewSecurityObject(
ParentDescriptor,
CreatorDescriptor,
NewDescriptor,
NULL, // No ObjectType
IsDirectoryObject,
0, // No Automatic inheritance
Token,
GenericMapping);
}
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:
See RtlpSetSecurityObject.
Arguments:
See RtlpSetSecurityObject.
Return Value:
See RtlpSetSecurityObject.
--*/
{
// Simply call RtlpSetSecurityObject specifying no auto inheritance.
return RtlpSetSecurityObject(NULL,
SecurityInformation,
ModificationDescriptor,
ObjectsSecurityDescriptor,
0, // No AutoInheritance
PagedPool,
GenericMapping,
Token);
}
NTSTATUS
RtlSetSecurityObjectEx(
IN SECURITY_INFORMATION SecurityInformation,
IN PSECURITY_DESCRIPTOR ModificationDescriptor,
IN OUT PSECURITY_DESCRIPTOR * ObjectsSecurityDescriptor,
IN ULONG AutoInheritFlags,
IN PGENERIC_MAPPING GenericMapping,
IN HANDLE Token OPTIONAL
)
/*++
Routine Description:
See RtlpSetSecurityObject.
Arguments:
See RtlpSetSecurityObject.
Return Value:
See RtlpSetSecurityObject.
--*/
{
// Simply call RtlpSetSecurityObject specifying no auto inheritance.
return RtlpSetSecurityObject(NULL,
SecurityInformation,
ModificationDescriptor,
ObjectsSecurityDescriptor,
AutoInheritFlags,
PagedPool,
GenericMapping,
Token);
}
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_RELATIVE IResultantDescriptor;
IResultantDescriptor = (PISECURITY_DESCRIPTOR_RELATIVE)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 = LongAlignSize(SeLengthSid(Group));
}
if (SecurityInformation & DACL_SECURITY_INFORMATION) {
Dacl = RtlpDaclAddrSecurityDescriptor(IObjectDescriptor);
if (Dacl != NULL) {
DaclSize = LongAlignSize(Dacl->AclSize);
}
}
if (SecurityInformation & SACL_SECURITY_INFORMATION) {
Sacl = RtlpSaclAddrSecurityDescriptor(IObjectDescriptor);
if (Sacl != NULL) {
SaclSize = LongAlignSize(Sacl->AclSize);
}
}
if (SecurityInformation & OWNER_SECURITY_INFORMATION) {
Owner = RtlpOwnerAddrSecurityDescriptor(IObjectDescriptor);
OwnerSize = LongAlignSize(SeLengthSid(Owner));
}
*ReturnLength = sizeof(SECURITY_DESCRIPTOR_RELATIVE) +
GroupSize +
DaclSize +
SaclSize +
OwnerSize;
if (*ReturnLength > DescriptorLength) {
return(STATUS_BUFFER_TOO_SMALL);
}
RtlCreateSecurityDescriptor(IResultantDescriptor, SECURITY_DESCRIPTOR_REVISION);
RtlpSetControlBits(IResultantDescriptor, SE_SELF_RELATIVE);
Base = (PCHAR)(IResultantDescriptor);
Field = Base + (ULONG)sizeof(SECURITY_DESCRIPTOR_RELATIVE);
if (SecurityInformation & SACL_SECURITY_INFORMATION) {
if (SaclSize > 0) {
RtlMoveMemory(Field, Sacl, SaclSize);
IResultantDescriptor->Sacl = 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 = 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 = 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 = 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 = STATUS_SUCCESS;
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
RtlConvertToAutoInheritSecurityObject(
IN PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL,
IN PSECURITY_DESCRIPTOR CurrentSecurityDescriptor,
OUT PSECURITY_DESCRIPTOR * NewSecurityDescriptor,
IN GUID * ObjectType OPTIONAL,
IN BOOLEAN IsDirectoryObject,
IN PGENERIC_MAPPING GenericMapping
)
/*++
Routine Description:
This is a converts a security descriptor whose ACLs are not marked
as AutoInherit to a security descriptor whose ACLs are marked as
AutoInherit.
See further detailed description on ConvertToAutoInheritPrivateObjectSecurity.
Arguments:
ParentDescriptor - Supplies the Security Descriptor for the parent
directory under which a object exists. If there is
no parent directory, then this argument is specified as NULL.
CurrentSecurityDescriptor - Supplies a pointer to the objects security descriptor
that is going to be altered by this procedure.
NewSecurityDescriptor Points to a pointer that is to be made to point to the
newly allocated self-relative security descriptor. When no
longer needed, this descriptor must be freed using
DestroyPrivateObjectSecurity().
ObjectType - GUID of the object type being created. If the object being
created has no GUID associated with it, then this argument is
specified as NULL.
IsDirectoryObject - Specifies if the object is a
directory object. A value of TRUE indicates the object is a
container of other objects.
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_UNKNOWN_REVISION - Indicates the source ACL is a revision that
is unknown to this routine. (Only revision 2 ACLs are support by this routine.)
STATUS_INVALID_ACL - The structure of one of the ACLs in invalid.
--*/
{
// Simply call the corresponding Rtlp routine telling it which allocator
// to use.
return RtlpConvertToAutoInheritSecurityObject(
ParentDescriptor,
CurrentSecurityDescriptor,
NewSecurityDescriptor,
ObjectType,
IsDirectoryObject,
GenericMapping);
}
NTSTATUS RtlDefaultNpAcl(OUT PACL * pAcl)
/*++
Routine Description:
This routine constructs a default ACL to be applied to
named pipe objects when the caller has not specified one.
See NT bug 131090.
The ACL constructed is as follows:
Need to build an ACL that looks like the following:
Local System : F
Administrators: F
Owner: F
Everyone: R
The owner is determined by querying the currently effective
toke and extracting the default owner.
Arguments:
pAcl - Receives a pointer to an ACL to apply to the named pipe
being created. Guaranteed to be NULL on return if an error occurs.
This must be freed by calling RtlFreeHeap.
Return Value:
NT Status.
--*/
{
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
ULONG AclSize = 0;
NTSTATUS Status = STATUS_SUCCESS;
ULONG ReturnLength = 0;
PTOKEN_OWNER OwnerSid = NULL;
HANDLE hToken;
// Initialize OUT parameters
*pAcl = NULL;
// Open thread token
Status = NtOpenThreadToken(NtCurrentThread(), TOKEN_QUERY, TRUE, &hToken);
if (STATUS_NO_TOKEN == Status) {
// Not impersonating, get process token
Status = NtOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken);
}
if (NT_SUCCESS(Status)) {
// Get the default owner
Status = NtQueryInformationToken(hToken, TokenOwner, NULL, 0, &ReturnLength);
if (STATUS_BUFFER_TOO_SMALL == Status) {
OwnerSid = (PTOKEN_OWNER)RtlAllocateHeap(RtlProcessHeap(), 0, ReturnLength);
if (OwnerSid) {
Status = NtQueryInformationToken(hToken, TokenOwner, OwnerSid, ReturnLength, &ReturnLength);
if (NT_SUCCESS(Status)) {
// Compute the size needed
UCHAR SidBuffer[16];
ASSERT(16 == RtlLengthRequiredSid(2));
AclSize += RtlLengthRequiredSid(1); // LocalSystem Sid
AclSize += RtlLengthRequiredSid(2); // Administrators
AclSize += RtlLengthRequiredSid(1); // Everyone (World)
AclSize += RtlLengthSid(OwnerSid->Owner); // Owner
AclSize += sizeof(ACL); // Header
AclSize += 4 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG));
// Allocate the Acl out of the local process heap
*pAcl = (PACL)RtlAllocateHeap(RtlProcessHeap(), 0, AclSize);
if (*pAcl != NULL) {
RtlCreateAcl(*pAcl, AclSize, ACL_REVISION);
// Create each SID in turn and copy the resultant ACE into the new ACL
// Local System - Generic All
RtlInitializeSid(SidBuffer, &NtAuthority, 1);
*(RtlSubAuthoritySid(SidBuffer, 0)) = SECURITY_LOCAL_SYSTEM_RID;
RtlAddAccessAllowedAce(*pAcl, ACL_REVISION, GENERIC_ALL, (PSID)SidBuffer);
// Admins - Generic All
RtlInitializeSid(SidBuffer, &NtAuthority, 2);
*(RtlSubAuthoritySid(SidBuffer, 0)) = SECURITY_BUILTIN_DOMAIN_RID;
*(RtlSubAuthoritySid(SidBuffer, 1)) = DOMAIN_ALIAS_RID_ADMINS;
RtlAddAccessAllowedAce(*pAcl, ACL_REVISION, GENERIC_ALL, (PSID)SidBuffer);
// Owner - Generic All
RtlAddAccessAllowedAce(*pAcl, ACL_REVISION, GENERIC_ALL, OwnerSid->Owner);
// World - Generic Read
RtlInitializeSid(SidBuffer, &WorldSidAuthority, 1);
*(RtlSubAuthoritySid(SidBuffer, 0)) = SECURITY_WORLD_RID;
RtlAddAccessAllowedAce(*pAcl, ACL_REVISION, GENERIC_READ, (PSID)SidBuffer);
} else {
Status = STATUS_NO_MEMORY;
}
}
RtlFreeHeap(RtlProcessHeap(), 0, OwnerSid);
} else {
Status = STATUS_NO_MEMORY;
}
}
NtClose(hToken);
}
if (!NT_SUCCESS(Status)) {
// Something failed, clean up OUT
// parameters.
if (*pAcl != NULL) {
RtlFreeHeap(RtlProcessHeap(), 0, *pAcl);
*pAcl = NULL;
}
}
return(Status);
}