windows-nt/Source/XPSP1/NT/base/wdmlib/wdmsec/se/sesddl.c
2020-09-26 16:20:57 +08:00

1443 lines
39 KiB
C

/*++
Copyright (c) Microsoft Corporation
Module Name:
SeSddl.c
Abstract:
This module implements the Security Descriptor Definition Language support
functions for kernel mode
Author:
Mac McLain (MacM) Nov 07, 1997
Environment:
Kernel Mode
Revision History:
Jin Huang (JinHuang) 3/4/98 Fix validity flags (GetAceFlagsInTable)
Jin Huang (JinHuang) 3/10/98 Add SD controls (GetSDControlForString)
Set SidsInitialized flag
Skip any possible spaces in string
Jin Huang (JinHuang) 5/1/98 Fix memory leek, error checking
improve performance
Alaa Abdelhalim (Alaa) 7/20/99 Initialize sbz2 field to 0 in LocalGetAclForString
function.
Vishnu Patankar (VishnuP) 7/5/00 Added new API ConvertStringSDToSDDomain(A/W)
Adrian J. Oney (AdriaO) 3/27/02 Ported small subset of
advapi32\sddl.c to KernelMode
--*/
#include "WlDef.h"
#include "SepSddl.h"
#pragma hdrstop
#pragma alloc_text(PAGE, SeSddlSecurityDescriptorFromSDDL)
#pragma alloc_text(PAGE, SepSddlSecurityDescriptorFromSDDLString)
#pragma alloc_text(PAGE, SepSddlDaclFromSDDLString)
#pragma alloc_text(PAGE, SepSddlGetAclForString)
#pragma alloc_text(PAGE, SepSddlLookupAccessMaskInTable)
#pragma alloc_text(PAGE, SepSddlGetSidForString)
#pragma alloc_text(PAGE, SepSddlAddAceToAcl)
#pragma alloc_text(PAGE, SepSddlParseWideStringUlong)
#define POOLTAG_SEACL 'lAeS'
#define POOLTAG_SESD 'dSeS'
#define POOLTAG_SETS 'sTeS'
static STRSD_SID_LOOKUP SidLookup[] = {
// World (WD) == SECURITY_WORLD_SID_AUTHORITY, also called Everyone.
// Typically everyone but restricted code (in XP, anonymous logons also
// lack world SID)
DEFINE_SDDL_ENTRY( \
SeWorldSid, \
WIN2K_OR_LATER, \
SDDL_EVERYONE, \
SDDL_LEN_TAG( SDDL_EVERYONE ) ),
// Administrators (BA) == DOMAIN_ALIAS_RID_ADMINS, Administrator group on
// the machine
DEFINE_SDDL_ENTRY( \
SeAliasAdminsSid, \
WIN2K_OR_LATER, \
SDDL_BUILTIN_ADMINISTRATORS, \
SDDL_LEN_TAG( SDDL_BUILTIN_ADMINISTRATORS ) ),
// System (SY) == SECURITY_LOCAL_SYSTEM_RID, the OS itself (including its
// user mode components)
DEFINE_SDDL_ENTRY( \
SeLocalSystemSid, \
WIN2K_OR_LATER, \
SDDL_LOCAL_SYSTEM, \
SDDL_LEN_TAG( SDDL_LOCAL_SYSTEM ) ),
// Interactive User (IU) == SECURITY_INTERACTIVE_RID, users logged on
// locally (doesn't include TS users)
DEFINE_SDDL_ENTRY( \
SeInteractiveSid, \
WIN2K_OR_LATER, \
SDDL_INTERACTIVE, \
SDDL_LEN_TAG( SDDL_INTERACTIVE ) ),
// Restricted Code (RC) == SECURITY_RESTRICTED_CODE_RID, used to control
// access by untrusted code (ACL's must contain World SID as well)
DEFINE_SDDL_ENTRY( \
SeRestrictedSid, \
WIN2K_OR_LATER, \
SDDL_RESTRICTED_CODE, \
SDDL_LEN_TAG( SDDL_RESTRICTED_CODE ) ),
// Authenticated Users (AU) == SECURITY_AUTHENTICATED_USER_RID, any user
// recognized by the local machine or by a domain.
DEFINE_SDDL_ENTRY( \
SeAuthenticatedUsersSid, \
WIN2K_OR_LATER, \
SDDL_AUTHENTICATED_USERS, \
SDDL_LEN_TAG( SDDL_AUTHENTICATED_USERS ) ),
// Network Logon User (NU) == SECURITY_NETWORK_RID, any user logged in
// remotely.
DEFINE_SDDL_ENTRY( \
SeNetworkSid, \
WIN2K_OR_LATER, \
SDDL_NETWORK, \
SDDL_LEN_TAG( SDDL_NETWORK ) ),
// Anonymous Logged-on User (AN) == SECURITY_ANONYMOUS_LOGON_RID, users
// logged on without an indentity. No effect before Windows XP (SID
// presense is harmless though)
// Note: By default, World does not include Anonymous users on XP!
DEFINE_SDDL_ENTRY( \
SeAnonymousLogonSid, \
WIN2K_OR_LATER, \
SDDL_ANONYMOUS, \
SDDL_LEN_TAG( SDDL_ANONYMOUS ) ),
// Builtin guest account (BG) == DOMAIN_ALIAS_RID_GUESTS, users logging in
// using the local guest account.
DEFINE_SDDL_ENTRY( \
SeAliasGuestsSid, \
WIN2K_OR_LATER, \
SDDL_BUILTIN_GUESTS, \
SDDL_LEN_TAG( SDDL_BUILTIN_GUESTS ) ),
// Builtin user account (BU) == DOMAIN_ALIAS_RID_USERS, local user accounts,
// or users on the domain.
DEFINE_SDDL_ENTRY( \
SeAliasUsersSid, \
WIN2K_OR_LATER, \
SDDL_BUILTIN_USERS, \
SDDL_LEN_TAG( SDDL_BUILTIN_USERS ) ),
//
// Don't expose these - they are either invalid or depricated
//
//{ SePrincipalSelfSid, SDDL_PERSONAL_SELF, SDDL_LEN_TAG( SDDL_PERSONAL_SELF ) },
//{ SeServiceSid, SDDL_SERVICE, SDDL_LEN_TAG( SDDL_SERVICE ) },
//{ SeAliasPowerUsersSid, SDDL_POWER_USERS, SDDL_LEN_TAG( SDDL_POWER_USERS ) },
// Local Service (LS) == SECURITY_LOCAL_SERVICE_RID, a predefined account
// for local services (which also belong to Authenticated and World)
DEFINE_SDDL_ENTRY( \
SeLocalServiceSid, \
WINXP_OR_LATER, \
SDDL_LOCAL_SERVICE, \
SDDL_LEN_TAG( SDDL_LOCAL_SERVICE ) ),
// Network Service (NS) == SECURITY_NETWORK_SERVICE_RID, a predefined
// account for network services (which also belong to Authenticated and
// World)
DEFINE_SDDL_ENTRY( \
SeNetworkServiceSid, \
WINXP_OR_LATER, \
SDDL_NETWORK_SERVICE, \
SDDL_LEN_TAG( SDDL_NETWORK_SERVICE ) )
};
//
// This is how the access mask is looked up. Always have the multi-char rights
// before the single char ones
//
static STRSD_KEY_LOOKUP RightsLookup[] = {
{ SDDL_READ_CONTROL, SDDL_LEN_TAG( SDDL_READ_CONTROL ), READ_CONTROL },
{ SDDL_WRITE_DAC, SDDL_LEN_TAG( SDDL_WRITE_DAC ), WRITE_DAC },
{ SDDL_WRITE_OWNER, SDDL_LEN_TAG( SDDL_WRITE_OWNER ), WRITE_OWNER },
{ SDDL_STANDARD_DELETE, SDDL_LEN_TAG( SDDL_STANDARD_DELETE ), DELETE },
{ SDDL_GENERIC_ALL, SDDL_LEN_TAG( SDDL_GENERIC_ALL ), GENERIC_ALL },
{ SDDL_GENERIC_READ, SDDL_LEN_TAG( SDDL_GENERIC_READ ), GENERIC_READ },
{ SDDL_GENERIC_WRITE, SDDL_LEN_TAG( SDDL_GENERIC_WRITE ), GENERIC_WRITE },
{ SDDL_GENERIC_EXECUTE, SDDL_LEN_TAG( SDDL_GENERIC_EXECUTE ), GENERIC_EXECUTE },
};
//
// Exported functions
//
NTSTATUS
SeSddlSecurityDescriptorFromSDDL(
IN PCUNICODE_STRING SecurityDescriptorString,
IN LOGICAL SuppliedByDefaultMechanism,
OUT PSECURITY_DESCRIPTOR *SecurityDescriptor
)
/*++
Routine Description:
This routine creates a security descriptor given an SDDL string in
UNICODE_STRING format. The security descriptor is self-relative
(sans-pointers), so it can be persisted and used on subsequent boots.
Only a subset of the SDDL format is currently supported. This subset is
really tailored towards device object support.
Format:
D:P(ACE)(ACE)(ACE), where (ACE) is (AceType;;Access;;;SID)
AceType - Only Allow ("A") is supported.
AceFlags - No AceFlags are supported
Access - Rights specified in either hex format (0xnnnnnnnn), or via the
SDDL Generic/Standard abbreviations
ObjectGuid - Not supported
InheritObjectGuid - Not supported
SID - Abbreviated security ID (example WD == World)
The S-w-x-y-z form for SIDs is not supported
Example - "D:P(A;;GA;;;SY)" which is Allow System to have Generic All access
Arguments:
SecurityDescriptorString - Stringized security descriptor to be converted.
SuppliedByDefaultMechanism - TRUE if the DACL is being built due to some
default mechanism (ie, not manually specified
by an admin, etc).
SecurityDescriptor - Receives the security descriptor on success, NULL
on error.
Return Value:
NTSTATUS
--*/
{
NTSTATUS Status;
WCHAR GuardChar;
LPWSTR TempStringBuffer;
//
// Look to see if we have a string built by RtlInitUnicodeString. It will
// have a terminating NULL with it, so no conversion is neccessary.
//
if (SecurityDescriptorString->MaximumLength ==
SecurityDescriptorString->Length + sizeof(UNICODE_NULL)) {
GuardChar = SecurityDescriptorString->Buffer[SecurityDescriptorString->Length/sizeof(WCHAR)];
if (GuardChar == UNICODE_NULL) {
return SepSddlSecurityDescriptorFromSDDLString(
SecurityDescriptorString->Buffer,
SuppliedByDefaultMechanism,
SecurityDescriptor
);
}
}
//
// We need to allocate a slightly larger buffer so we can NULL-terminate it.
//
TempStringBuffer = (LPWSTR) ExAllocatePoolWithTag(
PagedPool,
SecurityDescriptorString->Length + sizeof(UNICODE_NULL),
POOLTAG_SETS
);
if (TempStringBuffer == NULL) {
*SecurityDescriptor = NULL;
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Build a null terminated WCHAR string
//
RtlCopyMemory(
TempStringBuffer,
SecurityDescriptorString->Buffer,
SecurityDescriptorString->Length
);
TempStringBuffer[SecurityDescriptorString->Length/sizeof(WCHAR)] = UNICODE_NULL;
//
// Do the conversion
//
Status = SepSddlSecurityDescriptorFromSDDLString(
TempStringBuffer,
SuppliedByDefaultMechanism,
SecurityDescriptor
);
//
// Free the temporary string
//
ExFreePool(TempStringBuffer);
return Status;
}
//
// Private functions
//
NTSTATUS
SepSddlSecurityDescriptorFromSDDLString(
IN LPCWSTR SecurityDescriptorString,
IN LOGICAL SuppliedByDefaultMechanism,
OUT PSECURITY_DESCRIPTOR *SecurityDescriptor
)
/*++
Routine Description:
This routine creates a security descriptor given an SDDL string in LPWSTR
format. The security descriptor is self-relative (sans-pointers), so it can
be persisted and used on subsequent boots.
Only the subset of the SDDL format is currently supported. This subset is
really tailored towards device object support.
Format:
D:P(ACE)(ACE)(ACE), where (ACE) is (AceType;;Access;;;SID)
AceType - Only Allow ("A") is supported.
AceFlags - No AceFlags are supported
Access - Rights specified in either hex format (0xnnnnnnnn), or via the
SDDL Generic/Standard abbreviations
ObjectGuid - Not supported
InheritObjectGuid - Not supported
SID - Abbreviated security ID (example WD == World)
The S-w-x-y-z form for SIDs is not supported
Example - "D:P(A;;GA;;;SY)" which is Allow System to have Generic All access
Arguments:
SecurityDescriptorString - Stringized security descriptor to be converted.
SuppliedByDefaultMechanism - TRUE if the DACL is being built due to some
default mechanism (ie, not manually specified
by an admin, etc).
SecurityDescriptor - Receives the security descriptor on success, NULL
on error.
Return Value:
NTSTATUS
--*/
{
SECURITY_DESCRIPTOR LocalSecurityDescriptor;
PSECURITY_DESCRIPTOR NewSecurityDescriptor;
ULONG SecurityDescriptorControlFlags;
PACL DiscretionaryAcl;
ULONG BufferLength;
NTSTATUS IgnoredStatus;
NTSTATUS Status;
PAGED_CODE();
//
// Preinit
//
DiscretionaryAcl = NULL;
NewSecurityDescriptor = NULL;
*SecurityDescriptor = NULL;
//
// First convert the SDDL into a DACL + Descriptor flags
//
Status = SepSddlDaclFromSDDLString(
SecurityDescriptorString,
SuppliedByDefaultMechanism,
&SecurityDescriptorControlFlags,
&DiscretionaryAcl
);
if (!NT_SUCCESS(Status)) {
goto ErrorExit;
}
//
// Create an on-stack security descriptor
//
IgnoredStatus = RtlCreateSecurityDescriptor( &LocalSecurityDescriptor,
SECURITY_DESCRIPTOR_REVISION );
ASSERT(IgnoredStatus == STATUS_SUCCESS);
//
// Now set the control, owner, group, dacls, and sacls, etc
//
IgnoredStatus = RtlSetDaclSecurityDescriptor( &LocalSecurityDescriptor,
TRUE,
DiscretionaryAcl,
FALSE );
ASSERT(IgnoredStatus == STATUS_SUCCESS);
//
// Add in the descriptor flags (we do this afterwords as the RtlSet...
// functions also munge the defaulted bits.)
//
LocalSecurityDescriptor.Control |= SecurityDescriptorControlFlags;
//
// Convert the security descriptor into a self-contained binary form
// ("self-relative", ie sans-pointers) that can be written into the
// registry and used on subsequent boots. Start by getting the required
// size.
//
BufferLength = 0;
IgnoredStatus = RtlAbsoluteToSelfRelativeSD(
&LocalSecurityDescriptor,
NULL,
&BufferLength
);
ASSERT(IgnoredStatus == STATUS_BUFFER_TOO_SMALL);
//
// Allocate memory for the descriptor
//
NewSecurityDescriptor = (PSECURITY_DESCRIPTOR) ExAllocatePoolWithTag(
PagedPool,
BufferLength,
POOLTAG_SESD
);
if (NewSecurityDescriptor == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorExit;
}
//
// Do the conversion
//
Status = RtlAbsoluteToSelfRelativeSD(
&LocalSecurityDescriptor,
NewSecurityDescriptor,
&BufferLength
);
if (!NT_SUCCESS(Status)) {
goto ErrorExit;
}
//
// At this point, the Dacl is no longer needed.
//
ExFreePool(DiscretionaryAcl);
*SecurityDescriptor = NewSecurityDescriptor;
return Status;
ErrorExit:
if ( DiscretionaryAcl != NULL ) {
ExFreePool(DiscretionaryAcl);
}
if ( NewSecurityDescriptor != NULL ) {
ExFreePool(NewSecurityDescriptor);
}
return Status;
}
NTSTATUS
SepSddlDaclFromSDDLString(
IN LPCWSTR SecurityDescriptorString,
IN LOGICAL SuppliedByDefaultMechanism,
OUT ULONG *SecurityDescriptorControlFlags,
OUT PACL *DiscretionaryAcl
)
/*++
Routine Description:
This routine will create a DACL given an SDDL string in LPWSTR format. Only
the subset of the SDDL format is currently supported. This subset is really
tailored towards device object support.
Format:
D:P(ACE)(ACE)(ACE), where (ACE) is (AceType;;Access;;;SID)
AceType - Only Allow ("A") is supported.
AceFlags - No AceFlags are supported
Access - Rights specified in either hex format (0xnnnnnnnn), or via the
SDDL Generic/Standard abbreviations
ObjectGuid - Not supported
InheritObjectGuid - Not supported
SID - Abbreviated security ID (example WD == World)
The S-w-x-y-z form for SIDs is not supported
Example - "D:P(A;;GA;;;SY)" which is Allow System to have Generic All access
Arguments:
SecurityDescriptorString - Stringized security descriptor to be converted.
SuppliedByDefaultMechanism - TRUE if the DACL is being built due to some
default mechanism (ie, not manually specified
by an admin, etc).
SecurityDescriptorControlFlags - Receives control flags to apply if a
security descriptor is made from the DACL.
Receives 0 on error.
DiscretionaryAcl - Receives ACL allocated from paged pool, or NULL on
error. A self-contained security descriptor can be made
with this ACL using the RtlAbsoluteToSelfRelativeSD
function.
Return Value:
NTSTATUS
--*/
{
PACL Dacl;
PWSTR Curr, End;
NTSTATUS Status;
ULONG ControlFlags;
PAGED_CODE();
//
// Preinit for error.
//
*DiscretionaryAcl = NULL;
*SecurityDescriptorControlFlags = 0;
//
// Now, we'll just start parsing and building
//
Curr = ( PWSTR )SecurityDescriptorString;
//
// skip any spaces
//
while(*Curr == L' ' ) {
Curr++;
}
//
// There must be a DACL entry (SDDL_DACL is a 1-char string)
//
if (*Curr != SDDL_DACL[0]) {
return STATUS_INVALID_PARAMETER;
} else {
Curr++;
}
if ( *Curr != SDDL_DELIMINATORC ) {
return STATUS_INVALID_PARAMETER;
} else {
Curr++;
}
//
// Look for the protected control flag. We will set the SE_DACL_DEFAULTED
// bit if the ACL is being built using a default mechanism.
//
ControlFlags = SuppliedByDefaultMechanism ? SE_DACL_DEFAULTED : 0;
if (*Curr == SDDL_PROTECTED[0]) {
//
// This flag doesn't do much for device objects. However, we do not
// want to discourage it, as it's use makes sense in a lot of other
// contexts!
//
Curr++;
ControlFlags |= SE_DACL_PROTECTED;
}
//
// Get the DACL corresponding to this SDDL string
//
Status = SepSddlGetAclForString( Curr, &Dacl, &End );
if ( Status == STATUS_SUCCESS ) {
Curr = End;
while(*Curr == L' ' ) {
Curr++;
}
if (*Curr != L'\0') {
Status = STATUS_INVALID_PARAMETER;
}
}
if ( Status == STATUS_SUCCESS ) {
*DiscretionaryAcl = Dacl;
*SecurityDescriptorControlFlags = ControlFlags;
} else {
if ( Dacl ) {
ExFreePool( Dacl );
Dacl = NULL;
}
}
return Status;
}
NTSTATUS
SepSddlGetSidForString(
IN PWSTR String,
OUT PSID *SID,
OUT PWSTR *End
)
/*++
Routine Description:
This routine will determine which sid is an appropriate match for the
given string, either as a sid moniker or as a string representation of a
sid (ie: "DA" or "S-1-0-0" )
Arguments:
String - The string to be converted
Sid - Where the created SID is to be returned. May receive NULL if the
specified SID doesn't exist for the current platform!
End - Where in the string we stopped processing
Return Value:
STATUS_SUCCESS - success
STATUS_NONE_MAPPED - An invalid format of the SID was given
--*/
{
ULONG_PTR sidOffset;
ULONG i;
//
// Set our end of string pointer
//
for ( i = 0; i < ARRAY_COUNT(SidLookup); i++ ) {
//
// check for the current key first
//
if ( _wcsnicmp( String, SidLookup[i].Key, SidLookup[i].KeyLen ) == 0 ) {
*End = String += SidLookup[i].KeyLen;
#ifndef _KERNELIMPLEMENTATION_
if ((SidLookup[i].OsVer == WINXP_OR_LATER) &&
(!IoIsWdmVersionAvailable(1, 0x20))) {
*SID = NULL;
} else {
sidOffset = SidLookup[ i ].ExportSidFieldOffset;
*SID = *((PSID *) (((PUCHAR) SeExports) + sidOffset));
}
#else
*SID = *SidLookup[ i ].Sid;
#endif
return STATUS_SUCCESS;
}
}
*SID = NULL;
return STATUS_NONE_MAPPED;
}
LOGICAL
SepSddlLookupAccessMaskInTable(
IN PWSTR String,
OUT ULONG *AccessMask,
OUT PWSTR *End
)
/*++
Routine Description:
This routine will determine if the given access mask or string right exists
in the lookup table.
A pointer to the matching static lookup entry is returned.
Arguments:
String - The string to be looked up
AccessMask - Receives access mask if match is found.
End - Adjusted string pointer
Return Value:
TRUE if found, FALSE otherwise.
--*/
{
ULONG i;
for ( i = 0; i < ARRAY_COUNT(RightsLookup); i++ ) {
if ( _wcsnicmp( String, RightsLookup[ i ].Key, RightsLookup[ i ].KeyLen ) == 0 ) {
//
// If a match was found, return it
//
*AccessMask = RightsLookup[ i ].Value;
*End = String + RightsLookup[ i ].KeyLen;
return TRUE;
}
}
*AccessMask = 0;
*End = String;
return FALSE;
}
NTSTATUS
SepSddlGetAclForString(
IN PWSTR AclString,
OUT PACL *Acl,
OUT PWSTR *End
)
/*++
Routine Description:
This routine convert a string into an ACL. The format of the aces is:
Ace := ( Type; Flags; Rights; ObjGuid; IObjGuid; Sid;
Type : = A | D | OA | OD {Access, Deny, ObjectAccess, ObjectDeny}
Flags := Flags Flag
Flag : = CI | IO | NP | SA | FA {Container Inherit,Inherit Only, NoProp,
SuccessAudit, FailAdit }
Rights := Rights Right
Right := DS_READ_PROPERTY | blah blah
Guid := String representation of a GUID (via RPC UuidToString)
Sid := DA | PS | AO | PO | AU | S-* (Domain Admins, PersonalSelf, Acct Ops,
PrinterOps, AuthenticatedUsers, or
the string representation of a sid)
The seperator is a ';'.
The returned ACL must be free via a call to ExFreePool
Arguments:
AclString - The string to be converted
Acl - Where the created ACL is to be returned
End - Where in the string we stopped processing
Return Value:
STATUS_SUCCESS indicates success
STATUS_INSUFFICIENT_RESOURCES indicates a memory allocation for the ouput
acl failed
STATUS_INVALID_PARAMETER The string does not represent an ACL
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG AclSize = 0, AclUsed = 0;
ULONG Aces = 0, i, j;
ULONG AccessMask;
PWSTR Curr, MaskEnd;
LOGICAL OpRes;
PSTRSD_KEY_LOOKUP MatchedEntry;
PSID SidPtr = NULL;
//
// First, we'll have to go through and count the number of entries that
// we have. We'll do the by computing the length of this ACL (which is
// delimited by either the end of the list or a ':' that seperates a key
// from a value
//
*Acl = NULL;
*End = wcschr( AclString, SDDL_DELIMINATORC );
if ( *End == AclString ) {
return STATUS_INVALID_PARAMETER;
}
if ( *End == NULL ) {
*End = AclString + wcslen( AclString );
} else {
( *End )--;
}
//
// Now, do the count
//
Curr = AclString;
OpRes = 0;
while ( Curr < *End ) {
if ( *Curr == SDDL_SEPERATORC ) {
Aces++;
} else if ( *Curr != L' ' ) {
OpRes = 1;
}
Curr++;
}
//
// Now, we've counted the total number of seperators. Make sure we
// have the right number. (There is 5 seperators per ace)
//
if ( Aces % 5 == 0 ) {
if ( Aces == 0 && OpRes ) {
//
// gabbage chars in between
//
Status = STATUS_INVALID_PARAMETER;
} else {
Aces = Aces / 5;
}
} else {
Status = STATUS_INVALID_PARAMETER;
}
//
// This is an empty ACL (ie no access to anyone, including the system)
//
if (( Status == STATUS_SUCCESS ) && ( Aces == 0 )) {
*Acl = ExAllocatePoolWithTag( PagedPool, sizeof( ACL ), POOLTAG_SEACL );
if ( *Acl == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
RtlZeroMemory( *Acl, sizeof( ACL ));
( *Acl )->AclRevision = ACL_REVISION;
( *Acl )->Sbz1 = ( UCHAR )0;
( *Acl )->AclSize = ( USHORT )sizeof( ACL );
( *Acl )->AceCount = 0;
( *Acl )->Sbz2 = ( USHORT )0;
}
return Status;
}
//
// Ok now do the allocation. We'll do a sort of worst case initial
// allocation. This saves us from having to process everything twice
// (once to size, once to build). If we determine later that we have
// an acl that is not big enough, we allocate additional space. The only
// time that this reallocation should happen is if the input string
// contains a lot of explicit SIDs. Otherwise, the chosen buffer size
// should be pretty close to the proper size
//
if ( Status == STATUS_SUCCESS ) {
AclSize = sizeof( ACL ) + ( Aces * ( sizeof( ACCESS_ALLOWED_ACE ) +
sizeof( SID ) + ( 6 * sizeof( ULONG ) ) ) );
if ( AclSize > SDDL_MAX_ACL_SIZE ) {
AclSize = SDDL_MAX_ACL_SIZE;
}
*Acl = ( PACL ) ExAllocatePoolWithTag( PagedPool, AclSize, POOLTAG_SEACL );
if ( *Acl == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
AclUsed = sizeof( ACL );
RtlZeroMemory( *Acl, AclSize );
//
// We'll start initializing it...
//
( *Acl )->AclRevision = ACL_REVISION;
( *Acl )->Sbz1 = ( UCHAR )0;
( *Acl )->AclSize = ( USHORT )AclSize;
( *Acl )->AceCount = 0;
( *Acl )->Sbz2 = ( USHORT )0;
//
// Ok, now we'll go through and start building them all
//
Curr = AclString;
for( i = 0; i < Aces; i++ ) {
//
// First, get the type..
//
UCHAR Flags = 0;
USHORT Size;
ACCESS_MASK Mask = 0;
PWSTR Next;
ULONG AceSize = 0;
//
// skip any space before (
//
while(*Curr == L' ' ) {
Curr++;
}
//
// Skip any parens that may exist in the ace list
//
if ( *Curr == SDDL_ACE_BEGINC ) {
Curr++;
}
//
// skip any space after (
//
while(*Curr == L' ' ) {
Curr++;
}
//
// Look for an allow ACE
//
if ( _wcsnicmp( Curr, SDDL_ACCESS_ALLOWED, SDDL_LEN_TAG( SDDL_ACCESS_ALLOWED ) ) == 0 ) {
Curr += SDDL_LEN_TAG( SDDL_ACCESS_ALLOWED ) + 1;
} else {
//
// Found an invalid type
//
Status = STATUS_INVALID_PARAMETER;
break;
}
//
// skip any space before ;
//
while(*Curr == L' ' ) {
Curr++;
}
//
// This function doesn't support any ACE Flags. As such, any
// flags found are invalid
//
if ( *Curr == SDDL_SEPERATORC ) {
Curr++;
} else {
Status = STATUS_INVALID_PARAMETER;
break;
}
//
// skip any space after ;
//
while(*Curr == L' ' ) {
Curr++;
}
//
// Now, get the access mask
//
while( TRUE ) {
if ( *Curr == SDDL_SEPERATORC ) {
Curr++;
break;
}
//
// Skip any blanks
//
while ( *Curr == L' ' ) {
Curr++;
}
if (SepSddlLookupAccessMaskInTable( Curr, &AccessMask, &MaskEnd )) {
Mask |= AccessMask;
Curr = MaskEnd;
} else {
//
// If the rights couldn't be looked up, see if it's a
// converted mask
//
#ifndef _KERNELIMPLEMENTATION_
SepSddlParseWideStringUlong(Curr, &MaskEnd, &Mask);
#else
Mask = wcstoul( Curr, &MaskEnd, 0 );
#endif
if ( MaskEnd != Curr ) {
Curr = MaskEnd;
} else {
//
// Found an invalid right
//
Status = STATUS_INVALID_PARAMETER;
break;
}
}
}
if ( Status != STATUS_SUCCESS ) {
break;
}
//
// If that worked, we'll get the ids
//
for ( j = 0; j < 2; j++ ) {
//
// skip any space before ;
//
while(*Curr == L' ' ) {
Curr++;
}
if ( *Curr != SDDL_SEPERATORC ) {
//
// Object GUIDs are not supported, as this function
// currently doesn't handle object-allow ACEs.
//
Status = STATUS_INVALID_PARAMETER;
}
Curr++;
}
if ( Status != STATUS_SUCCESS ) {
break;
}
//
// skip any space before ;
//
while(*Curr == L' ' ) {
Curr++;
}
//
// Finally, the SID
//
if ( STATUS_SUCCESS == Status ) {
PWSTR EndLocation;
Status = SepSddlGetSidForString( Curr, &SidPtr, &EndLocation );
if ( Status == STATUS_SUCCESS ) {
if ( EndLocation == NULL ) {
Status = STATUS_INVALID_ACL;
} else {
while(*EndLocation == L' ' ) {
EndLocation++;
}
//
// a ace must be terminated by ')'
//
if ( *EndLocation != SDDL_ACE_ENDC ) {
Status = STATUS_INVALID_ACL;
} else {
Curr = EndLocation + 1;
}
}
}
}
//
// Quit on an error
//
if ( Status != STATUS_SUCCESS ) {
break;
}
//
// Note that the SID pointer may be NULL if the SID wasn't
// relevant for this OS version.
//
if (SidPtr != NULL) {
//
// Now, we'll create the ace, and add it...
//
Status = SepSddlAddAceToAcl( Acl,
&AclUsed,
ACCESS_ALLOWED_ACE_TYPE,
Flags,
Mask,
( Aces - i ),
SidPtr );
//
// Handle any errors
//
if ( Status != STATUS_SUCCESS ) {
break;
}
}
if ( *Curr == SDDL_ACE_BEGINC ) {
Curr++;
}
}
//
// If something didn't work, clean up
//
if ( Status != STATUS_SUCCESS ) {
ExFreePool( *Acl );
*Acl = NULL;
} else {
//
// Set a more realistic acl size
//
( *Acl )->AclSize = ( USHORT )AclUsed;
}
}
}
return Status;
}
NTSTATUS
SepSddlAddAceToAcl(
IN OUT PACL *Acl,
IN OUT ULONG *TrueAclSize,
IN ULONG AceType,
IN ULONG AceFlags,
IN ULONG AccessMask,
IN ULONG RemainingAces,
IN PSID SidPtr
)
/*++
Routine Description:
This routine adds an ACE to the passed in ACL, growing the ACL size as
neccessary.
Arguments:
Acl - Specifies the ACL to receive the new ACE. May be reallocated if
Acl->AclSize cannot contain the ACE.
TrueAclSize - Contains the true working size of the ACL (as opposed to
Acl->AclSize, which may be bigger for performance reasons)
AceType - Type of ACE to add. Currently, only ACCESS_ALLOW ACEs are
supported.
AceFlags - Ace control flags, specifying inheritance, etc.
*Currently this must be zero*!!!!
AccessMask - Contains the ACCESS rights mask for the ACE
SID - Contains the SID for the ACE.
Return Value:
STATUS_SUCCESS indicates success
STATUS_INSUFFICIENT_RESOURCES indicates a memory allocation for the ouput
acl failed
--*/
{
PACL WorkingAcl;
ULONG WorkingAclSize;
ULONG AceSize;
ASSERT(AceType == ACCESS_ALLOWED_ACE_TYPE);
ASSERT(RemainingAces != 0);
#ifndef _KERNELIMPLEMENTATION_
ASSERT(AceFlags == 0);
#endif
WorkingAcl = *Acl;
WorkingAclSize = *TrueAclSize;
//
// First, make sure we have the room for it
// ACCESS_ALLOWED_ACE_TYPE:
//
AceSize = sizeof( ACCESS_ALLOWED_ACE );
AceSize += RtlLengthSid( SidPtr ) - sizeof( ULONG );
if (AceSize + WorkingAclSize > WorkingAcl->AclSize) {
//
// We'll have to reallocate, since our buffer isn't big enough. Assume
// all the remaining ACE's will be as big as this one is...
//
PACL NewAcl;
ULONG NewSize = WorkingAclSize + ( RemainingAces * AceSize );
NewAcl = ( PACL ) ExAllocatePoolWithTag( PagedPool, NewSize, POOLTAG_SEACL );
if ( NewAcl == NULL ) {
return STATUS_INSUFFICIENT_RESOURCES;
} else {
//
// Copy over the new data.
//
RtlZeroMemory( NewAcl, NewSize);
RtlCopyMemory( NewAcl, *Acl, WorkingAclSize );
NewAcl->AclSize = ( USHORT )NewSize;
ExFreePool( WorkingAcl );
*Acl = NewAcl;
WorkingAcl = NewAcl;
}
}
WorkingAclSize += AceSize;
*TrueAclSize = WorkingAclSize;
#ifndef _KERNELIMPLEMENTATION_
//
// Our ACE is an Allow ACE
//
return RtlAddAccessAllowedAce( WorkingAcl,
ACL_REVISION,
AccessMask,
SidPtr );
#else
//
// This version is not exported by the kernel today...
//
return RtlAddAccessAllowedAceEx( WorkingAcl,
ACL_REVISION,
AceFlags,
AccessMask,
SidPtr );
#endif // _KERNELIMPLEMENTATION_
}
#ifndef _KERNELIMPLEMENTATION_
LOGICAL
SepSddlParseWideStringUlong(
IN LPCWSTR Buffer,
OUT LPCWSTR *FinalPosition,
OUT ULONG *Value
)
/*++
Routine Description:
This routine parses a wide string for an unsigned long, in a similar
fashion to wcstoul. It exists because not all CRT library string functions
are exported by the kernel today.
Arguments:
Buffer - Points to location in string to begin parsing.
FinalPosition - Receives final string location, Buffer on error.
Value - Receives value parsed by routine, 0 on error.
Return Value:
TRUE if the parse succeeded, FALSE if it failed.
--*/
{
ULONG oldValue, newValue, newDigit, base;
LPCWSTR curr, initial;
PAGED_CODE();
//
// Preinit
//
*Value = 0;
*FinalPosition = Buffer;
initial = Buffer;
curr = initial;
if ((curr[0] == L'0') && ((curr[1] == L'x') || (curr[1] == L'X'))) {
//
// Starts with 0x, skip the rest.
//
initial += 2;
curr = initial;
base = 16;
} else if ((curr[0] >= L'0') && (curr[0] <= L'9')) {
base = 10;
} else {
base = 16;
}
oldValue = 0;
while(curr[0]) {
if ((curr[0] >= L'0') && (curr[0] <= L'9')) {
newDigit = curr[0] - L'0';
} else if ((base == 16) && (curr[0] >= L'A') && (curr[0] <= L'F')) {
newDigit = curr[0] - L'A' + 10;
} else if ((base == 16) && (curr[0] >= L'a') && (curr[0] <= L'f')) {
newDigit = curr[0] - L'a' + 10;
} else {
break;
}
newValue = (oldValue * base) + newDigit;
if (newValue < oldValue) {
//
// Wrapped, too many digits
//
return FALSE;
}
oldValue = newValue;
curr++;
}
//
// No real digits were found.
//
if (curr == initial) {
return FALSE;
}
*FinalPosition = curr;
*Value = oldValue;
return TRUE;
}
#endif // _KERNELIMPLEMENTATION_