|
|
/*++
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_
|