You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1442 lines
40 KiB
1442 lines
40 KiB
/*++
|
|
|
|
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_
|
|
|
|
|