3470 lines
97 KiB
C
3470 lines
97 KiB
C
/*++
|
||
|
||
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 <ntos.h>
|
||
#include <nturtl.h>
|
||
#include <ntlsa.h> // 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 );
|
||
}
|