Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

1158 lines
27 KiB

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
lsaprtl.c
Abstract:
Local Security Authority - Temporary Rtl Routine Definitions.
This file contains routines used in the LSA that could be made into Rtl
routines. They have been written in general purpose form with this in
mind - the only exception to thisa is that their names have Lsap prefixes
to indicate that they are currently used only by the LSA.
Author:
Scott Birrell (ScottBi) April 8, 1992
Environment:
Revision History:
--*/
#include <lsacomp.h>
NTSTATUS
LsapRtlConvertSidToUnicodeString(
IN PSID Sid,
OUT PUNICODE_STRING UnicodeString
)
/*++
Routine Description:
This function converts a Sid to Text format.
Arguments:
Sid - Pointer to Sid.
UnicodeString - Pointer to Output Unicode String.
Return Values:
NTSTATUS - Standard Nt Result Code
STATUS_SUCCESS - The call completed successfully
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
such as memory, to complete the call.
WARNING! This is a temporary hack.
--*/
{
NTSTATUS Status;
UCHAR Buffer[256];
UCHAR String[256];
UCHAR i;
ULONG Tmp;
PISID iSid = (PISID)Sid; // pointer to opaque structure
ANSI_STRING AnsiString;
sprintf(Buffer, "S-%u-", (USHORT)iSid->Revision );
lstrcpy(String, Buffer);
if ( (iSid->IdentifierAuthority.Value[0] != 0) ||
(iSid->IdentifierAuthority.Value[1] != 0) ){
sprintf(Buffer, "0x%02hx%02hx%02hx%02hx%02hx%02hx",
(USHORT)iSid->IdentifierAuthority.Value[0],
(USHORT)iSid->IdentifierAuthority.Value[1],
(USHORT)iSid->IdentifierAuthority.Value[2],
(USHORT)iSid->IdentifierAuthority.Value[3],
(USHORT)iSid->IdentifierAuthority.Value[4],
(USHORT)iSid->IdentifierAuthority.Value[5] );
lstrcat(String, Buffer);
} else {
Tmp = (ULONG)iSid->IdentifierAuthority.Value[5] +
(ULONG)(iSid->IdentifierAuthority.Value[4] << 8) +
(ULONG)(iSid->IdentifierAuthority.Value[3] << 16) +
(ULONG)(iSid->IdentifierAuthority.Value[2] << 24);
sprintf(Buffer, "%lu", Tmp);
lstrcat(String, Buffer);
}
for (i=0;i<iSid->SubAuthorityCount ;i++ ) {
sprintf(Buffer, "-%lu", iSid->SubAuthority[i]);
lstrcat(String, Buffer);
}
//
// Convert the string to a Unicode String
//
RtlInitString(&AnsiString, (PSZ) String);
Status = RtlAnsiStringToUnicodeString( UnicodeString, &AnsiString, TRUE);
return(Status);
}
NTSTATUS
LsapRtlCopyUnicodeString(
OUT PUNICODE_STRING OutputString,
IN PUNICODE_STRING InputString,
BOOLEAN AllocateMemory
)
/*++
Routine Description:
This function copies one Unicode String to another, optionally allocating
memory for the output string buffer.
Arguments:
OutputString - Pointer to Unicode String structure that will be made
to reference the output string.
InputString - Pointer to Unicode String structure to be copied.
AllocateMemory - Allocate memory for output string.
Return Value:
NTSTATUS - Standard Nt Result Code
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
(e.g. memory) to complete the call.
--*/
{
ANSI_STRING IntermediateAnsiString;
NTSTATUS Status;
//
// If we're to make a fresh copy of the string buffer, do so.
//
if (AllocateMemory) {
//
// A fresh copy is needed or memory not allocated yet for
// output string. Translate string back to Ansi then back
// to Unicode (sorry about the algorithm).
//
Status = RtlUnicodeStringToAnsiString(
&IntermediateAnsiString,
InputString,
TRUE
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
Status = RtlAnsiStringToUnicodeString(
OutputString,
&IntermediateAnsiString,
TRUE
);
} else {
//
// No memory allocation required. Just copy the string structures.
//
*OutputString = *InputString;
return(STATUS_SUCCESS);
}
RtlFreeAnsiString(&IntermediateAnsiString);
return(Status);
}
BOOLEAN
LsapRtlPrefixSid(
IN PSID PrefixSid,
IN PSID Sid
)
/*++
Routine Description:
This function checks if one Sid is the Prefix Sid of another.
Arguments:
PrefixSid - Pointer to Prefix Sid.
Sid - Pointer to Sid to be checked.
Return Values:
BOOLEAN - TRUE if PrefixSid is the Prefix Sid of Sid, else FALSE.
--*/
{
BOOLEAN BooleanStatus = FALSE;
if ((*RtlSubAuthorityCountSid(Sid)) > 0) {
//
// Decrement the SubAuthorityCount of Sid temporarily.
//
(*RtlSubAuthorityCountSid(Sid))--;
//
// Compare the Prefix Sid with the modified Sid.
//
BooleanStatus = RtlEqualSid( PrefixSid, Sid);
//
// Restore the original SubAuthorityCount.
//
(*RtlSubAuthorityCountSid(Sid))++;
}
return(BooleanStatus);
}
BOOLEAN
LsapRtlPrefixName(
IN PUNICODE_STRING PrefixName,
IN PUNICODE_STRING Name
)
/*++
Routine Description:
This function checks if a Name has the given name as a Prefix
Arguments:
PrefixName - Pointer to Prefix Name.
Name - Pointer to Name to be checked.
Return Values:
BOOLEAN - TRUE if the Name is composite (i.e. contains a "\") and
PrefixName is the Prefix part of Name, else FALSE.
--*/
{
UNICODE_STRING TruncatedName = *Name;
if ((PrefixName->Length < Name->Length) &&
Name->Buffer[PrefixName->Length / 2] == *L"\\") {
TruncatedName.Length = PrefixName->Length;
if (RtlEqualUnicodeString(PrefixName, &TruncatedName, FALSE)) {
return(TRUE);
}
}
return(FALSE);
}
VOID
LsapRtlSplitNames(
IN PUNICODE_STRING Names,
IN ULONG Count,
IN PUNICODE_STRING Separator,
OUT PUNICODE_STRING PrefixNames,
OUT PUNICODE_STRING SuffixNames
)
/*++
Routine Description:
This function splits an array of Names into Prefix and Suffix parts
separated by the given separator. The input array may contain names of
the following form:
<SuffixName>
<PrefixName> "\" <SuffixName>
The NULL string
Note that the output arrays will reference the original name strings.
No copying is done.
Arguments:
Names - Pointer to array of Unicode Names.
Count - Count of Names in Names.
PrefixNames - Pointer to an array of Count Unicode String structures
that will be initialized to point to the Prefix portions of the
Names.
SuffixNames - Pointer to an array of Count Unicode String structures
that will be initialized to point to the Suffix portions of the
Names.
Return Values:
None.
--*/
{
ULONG Index;
LONG SeparatorOffset;
LONG WideSeparatorOffset;
//
// Scan each name, initializing the output Unicode structures.
//
for (Index = 0; Index < Count; Index++) {
PrefixNames[Index] = Names[Index];
SuffixNames[Index] = Names[Index];
//
// Locate the separator "\" if any.
//
SeparatorOffset = LsapRtlFindCharacterInUnicodeString(
&Names[Index],
Separator,
FALSE
);
//
// If there is a separator, make the Prefix Name point to the
// part of the name before the separator and make the Suffix Name
// point to the part of the name after the separator. If there
// is no separator, set the Prefix Name part to Null. Rememeber
// that the Length fields are byte counts, not Wide Character
// counts.
//
if (SeparatorOffset >= 0) {
WideSeparatorOffset = (SeparatorOffset / sizeof(WCHAR));
PrefixNames[Index].Length = (USHORT) SeparatorOffset;
SuffixNames[Index].Buffer += (WideSeparatorOffset + 1);
SuffixNames[Index].Length -= (USHORT)(SeparatorOffset + sizeof(WCHAR));
} else {
WideSeparatorOffset = SeparatorOffset;
PrefixNames[Index].Length = 0;
}
//
// Set MaximumLengths equal to Lengths and, for safety, clear buffer
// pointers(s) to NULL in output strings if Length(s) are 0.
//
PrefixNames[Index].MaximumLength = PrefixNames[Index].Length;
SuffixNames[Index].MaximumLength = SuffixNames[Index].Length;
if (PrefixNames[Index].Length == 0) {
PrefixNames[Index].Buffer = NULL;
}
if (SuffixNames[Index].Length == 0) {
SuffixNames[Index].Buffer = NULL;
}
}
}
LONG
LsapRtlFindCharacterInUnicodeString(
IN PUNICODE_STRING InputString,
IN PUNICODE_STRING Character,
IN BOOLEAN CaseInsensitive
)
/*++
Routine Description:
This function returns the byte offset of the first occurrence (if any) of
a Unicode Character within a Unicode String.
Arguments
InputString - Pointer to Unicode String to be searched.
Character - Pointer to Unicode String initialized to character
to be searched for.
CaseInsensitive - TRUE if case is to be ignored, else FALSE.
NOTE - Only FALSE is supported just now.
Return Value:
LONG - If the character is present within the string, its non-negative
byte offset is returned. If the character is not present within
the string, a negative value is returned.
--*/
{
BOOLEAN CharacterFound = FALSE;
ULONG Offset;
if (!CaseInsensitive) {
Offset = 0;
while (Offset < InputString->Length) {
if (*(Character->Buffer) ==
InputString->Buffer[Offset / sizeof (WCHAR)]) {
CharacterFound = TRUE;
break;
}
Offset += 2;
}
} else {
//
// Case Insensitive is not supported
//
CharacterFound = FALSE;
}
if (!CharacterFound) {
Offset = LSA_UNKNOWN_ID;
}
return(Offset);
}
VOID
LsapRtlSetSecurityAccessMask(
IN SECURITY_INFORMATION SecurityInformation,
OUT PACCESS_MASK DesiredAccess
)
/*++
Routine Description:
NOTE! THIS ROUTINE IS IDENTICAL WITH SeSetSecurityAccessMask()
IN \nt\private\ntos\se\semethod.c
This routine builds an access mask representing the accesses necessary
to set the object security information specified in the SecurityInformation
parameter. While it is not difficult to determine this information,
the use of a single routine to generate it will ensure minimal impact
when the security information associated with an object is extended in
the future (to include mandatory access control information).
Arguments:
SecurityInformation - Identifies the object's security information to be
modified.
DesiredAccess - Points to an access mask to be set to represent the
accesses necessary to modify the information specified in the
SecurityInformation parameter.
Return Value:
None.
--*/
{
//
// Figure out accesses needed to perform the indicated operation(s).
//
(*DesiredAccess) = 0;
if ((SecurityInformation & OWNER_SECURITY_INFORMATION) ||
(SecurityInformation & GROUP_SECURITY_INFORMATION) ) {
(*DesiredAccess) |= WRITE_OWNER;
}
if (SecurityInformation & DACL_SECURITY_INFORMATION) {
(*DesiredAccess) |= WRITE_DAC;
}
if (SecurityInformation & SACL_SECURITY_INFORMATION) {
(*DesiredAccess) |= ACCESS_SYSTEM_SECURITY;
}
return;
}
VOID
LsapRtlQuerySecurityAccessMask(
IN SECURITY_INFORMATION SecurityInformation,
OUT PACCESS_MASK DesiredAccess
)
/*++
Routine Description:
NOTE! THIS ROUTINE IS IDENTICAL WITH SeQuerySecurityAccessMask()
IN \nt\private\ntos\se\semethod.c.
This routine builds an access mask representing the accesses necessary
to query the object security information specified in the
SecurityInformation parameter. While it is not difficult to determine
this information, the use of a single routine to generate it will ensure
minimal impact when the security information associated with an object is
extended in the future (to include mandatory access control information).
Arguments:
SecurityInformation - Identifies the object's security information to be
queried.
DesiredAccess - Points to an access mask to be set to represent the
accesses necessary to query the information specified in the
SecurityInformation parameter.
Return Value:
None.
--*/
{
//
// Figure out accesses needed to perform the indicated operation(s).
//
(*DesiredAccess) = 0;
if ((SecurityInformation & OWNER_SECURITY_INFORMATION) ||
(SecurityInformation & GROUP_SECURITY_INFORMATION) ||
(SecurityInformation & DACL_SECURITY_INFORMATION)) {
(*DesiredAccess) |= READ_CONTROL;
}
if ((SecurityInformation & SACL_SECURITY_INFORMATION)) {
(*DesiredAccess) |= ACCESS_SYSTEM_SECURITY;
}
return;
}
NTSTATUS
LsapRtlSidToUnicodeRid(
IN PSID Sid,
OUT PUNICODE_STRING UnicodeRid
)
/*++
Routine Description:
This function extracts the Relative Id (Rid) from a Sid and
converts it to a Unicode String. The Rid is extracted and converted
to an 8-digit Unicode Integer.
Arguments:
Sid - Pointer to the Sid to be converted. It is the caller's
responsibility to ensure that the Sid has valid syntax.
UnicodeRid - Pointer to a Unicode String structure that will receive
the Rid in Unicode form. Note that memory for the string buffer
in this Unicode String will be allocated by this routine if
successful. The caller must free this memory after use by calling
RtlFreeUnicodeString.
Return Value:
NTSTATUS - Standard Nt Status code
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
to allocate buffer for Unicode String name.
--*/
{
NTSTATUS Status;
ULONG Rid;
UCHAR SubAuthorityCount;
UCHAR RidNameBufferAnsi[9];
ANSI_STRING CharacterSidAnsi;
//
// First, verify that the given Sid is valid
//
if (!RtlValidSid( Sid )) {
return STATUS_INVALID_PARAMETER;
}
//
// Sid is valid. If however, the SubAuthorityCount is zero,
// we cannot have a Rid so return error.
//
SubAuthorityCount = ((PISID) Sid)->SubAuthorityCount;
if (SubAuthorityCount == 0) {
return STATUS_INVALID_PARAMETER;
}
//
// Sid has at least one subauthority. Get the lowest subauthority
// (i.e. the Rid).
//
Rid = ((PISID) Sid)->SubAuthority[SubAuthorityCount - 1];
//
// Now convert the Rid to an 8-digit numeric character string
//
Status = RtlIntegerToChar( Rid, 16, -8, RidNameBufferAnsi );
//
// Need to add null terminator to string
//
RidNameBufferAnsi[8] = 0;
//
// Initialize an ANSI string structure with the converted name.
//
RtlInitString( &CharacterSidAnsi, RidNameBufferAnsi );
//
// Convert the ANSI string structure to Unicode form
//
Status = RtlAnsiStringToUnicodeString(
UnicodeRid,
&CharacterSidAnsi,
TRUE
);
if (!NT_SUCCESS(Status)) {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
return Status;
}
NTSTATUS
LsapRtlPrivilegeSetToLuidAndAttributes(
IN OPTIONAL PPRIVILEGE_SET PrivilegeSet,
OUT PULONG PrivilegeCount,
OUT PLUID_AND_ATTRIBUTES *LuidAndAttributes
)
/*++
Routine Description:
This function converts a Privilege Set to a Privilege Count and Luid and
Attributes array.
Arguments:
PrivilegeSet - Pointer to Privilege Set to be converted. If NULL or a zero
length Privilege Set is specified, NULL is returned for the LUID and
attributes pointer, with a Privilege Count of 0.
PrivilegeCount - Receives the output Privilege Count
LuidAndAttributes - Receives pointer to Luid and Attributes array. If there
are no privileges, NULL is returned.
Return Values:
NTSTATUS - Standard Nt Result Code
--*/
{
NTSTATUS Status;
PLUID_AND_ATTRIBUTES OutputLuidAndAttributes = NULL;
ULONG OutputPrivilegeCount = 0;
ULONG LuidAndAttributesLength;
if (PrivilegeSet != NULL) {
OutputPrivilegeCount = PrivilegeSet->PrivilegeCount;
if (OutputPrivilegeCount > 0) {
//
// Allocate space for the output LUID_AND_ATTRIBUTES array.
//
LuidAndAttributesLength = sizeof(LUID_AND_ATTRIBUTES) * OutputPrivilegeCount;
OutputLuidAndAttributes = MIDL_user_allocate( LuidAndAttributesLength );
Status = STATUS_NO_MEMORY;
if (OutputLuidAndAttributes == NULL) {
goto PrivilegeSetToLuidAndAttributesError;
}
Status = STATUS_SUCCESS;
//
// Copy the LUID and attributes from the input Privilege Set.
//
RtlCopyMemory(
OutputLuidAndAttributes,
PrivilegeSet->Privilege,
LuidAndAttributesLength
);
}
}
//
// Return LUID and Attributes array or NULL, plus Count.
//
*LuidAndAttributes = OutputLuidAndAttributes;
*PrivilegeCount = OutputPrivilegeCount;
PrivilegeSetToLuidAndAttributesFinish:
return(Status);
PrivilegeSetToLuidAndAttributesError:
goto PrivilegeSetToLuidAndAttributesFinish;
}
NTSTATUS
LsapRtlWellKnownPrivilegeCheck(
IN PVOID ObjectHandle,
IN BOOLEAN ImpersonateClient,
IN ULONG PrivilegeId,
IN OPTIONAL PCLIENT_ID ClientId
)
/*++
Routine Description:
This function checks if the given well known privilege is enabled for an
impersonated client or for the current process.
Arguments:
ImpersonateClient - If TRUE, impersonate the client. If FALSE, don't
impersonate the client (we may already be doing so).
PrivilegeId - Specifies the well known Privilege Id
ClientId - Specifies the client process/thread Id. If already
impersonating the client, or impersonation is requested, this
parameter should be omitted.
Return Value:
NTSTATUS - Standard Nt Result Code
STATUS_SUCCESS - The call completed successfully and the client
is either trusted or has the necessary privilege enabled.
--*/
{
NTSTATUS Status, SecondaryStatus;
BOOLEAN PrivilegeHeld = FALSE;
HANDLE ClientThread = NULL, ClientProcess = NULL, ClientToken = NULL;
OBJECT_ATTRIBUTES NullAttributes;
PRIVILEGE_SET Privilege;
BOOLEAN ClientImpersonatedHere = FALSE;
UNICODE_STRING SubsystemName;
InitializeObjectAttributes( &NullAttributes, NULL, 0, NULL, NULL );
//
// If requested, impersonate the client.
//
if (ImpersonateClient) {
Status = I_RpcMapWin32Status(RpcImpersonateClient( NULL ));
if ( !NT_SUCCESS(Status) ) {
goto WellKnownPrivilegeCheckError;
}
ClientImpersonatedHere = TRUE;
}
//
// If a client process other than ourself has been specified , open it
// for query information access.
//
if (ARGUMENT_PRESENT(ClientId)) {
if (ClientId->UniqueProcess != NtCurrentProcess()) {
Status = NtOpenProcess(
&ClientProcess,
PROCESS_QUERY_INFORMATION, // To open primary token
&NullAttributes,
ClientId
);
if ( !NT_SUCCESS(Status) ) {
goto WellKnownPrivilegeCheckError;
}
} else {
ClientProcess = NtCurrentProcess();
}
}
//
// If a client thread other than ourself has been specified , open it
// for query information access.
//
if (ARGUMENT_PRESENT(ClientId)) {
if (ClientId->UniqueThread != NtCurrentThread()) {
Status = NtOpenThread(
&ClientThread,
THREAD_QUERY_INFORMATION,
&NullAttributes,
ClientId
);
if ( !NT_SUCCESS(Status) ) {
goto WellKnownPrivilegeCheckError;
}
} else {
ClientThread = NtCurrentThread();
}
} else {
ClientThread = NtCurrentThread();
}
//
// Open the specified or current thread's impersonation token (if any).
//
Status = NtOpenThreadToken(
ClientThread,
TOKEN_QUERY,
TRUE,
&ClientToken
);
if ( !NT_SUCCESS(Status) ) {
goto WellKnownPrivilegeCheckError;
}
//
// Make sure that we did not get any error in opening the impersonation
// token other than that the token doesn't exist.
//
if ( !NT_SUCCESS(Status) ) {
if ( Status != STATUS_NO_TOKEN ) {
goto WellKnownPrivilegeCheckError;
}
//
// The thread isn't impersonating...open the process's token.
// A process Id must have been specified in the ClientId information
// in this case.
//
if (ClientProcess == NULL) {
Status = STATUS_INVALID_PARAMETER;
goto WellKnownPrivilegeCheckError;
}
Status = NtOpenProcessToken(
ClientProcess,
TOKEN_QUERY,
&ClientToken
);
//
// Make sure we succeeded in opening the token
//
if ( !NT_SUCCESS(Status) ) {
goto WellKnownPrivilegeCheckError;
}
}
//
// OK, we have a token open. Now check for the privilege to execute this
// service.
//
Privilege.PrivilegeCount = 1;
Privilege.Control = PRIVILEGE_SET_ALL_NECESSARY;
Privilege.Privilege[0].Luid = RtlConvertLongToLuid(PrivilegeId);
Privilege.Privilege[0].Attributes = 0;
Status = NtPrivilegeCheck(
ClientToken,
&Privilege,
&PrivilegeHeld
);
if (!NT_SUCCESS(Status)) {
goto WellKnownPrivilegeCheckError;
}
RtlInitUnicodeString( &SubsystemName, L"LSA" );
(VOID) NtPrivilegeObjectAuditAlarm ( &SubsystemName,
ObjectHandle,
ClientToken,
ACCESS_SYSTEM_SECURITY,
&Privilege,
PrivilegeHeld
);
if ( !PrivilegeHeld ) {
Status = STATUS_PRIVILEGE_NOT_HELD;
goto WellKnownPrivilegeCheckError;
}
WellKnownPrivilegeCheckFinish:
//
// If we impersonated the client, revert to ourself.
//
if (ClientImpersonatedHere) {
SecondaryStatus = I_RpcMapWin32Status(RpcRevertToSelf());
}
//
// If necessary, close the client Process.
//
if ((ARGUMENT_PRESENT(ClientId)) &&
(ClientId->UniqueProcess != NtCurrentProcess()) &&
(ClientProcess != NULL)) {
SecondaryStatus = NtClose( ClientProcess );
ASSERT(NT_SUCCESS(SecondaryStatus));
ClientProcess = NULL;
}
//
// If necessary, close the client token.
//
if (ClientToken != NULL) {
SecondaryStatus = NtClose( ClientToken );
ASSERT(NT_SUCCESS(SecondaryStatus));
ClientToken = NULL;
}
//
// If necessary, close the client thread
//
if ((ARGUMENT_PRESENT(ClientId)) &&
(ClientId->UniqueThread != NtCurrentThread()) &&
(ClientThread != NULL)) {
SecondaryStatus = NtClose( ClientThread );
ASSERT(NT_SUCCESS(SecondaryStatus));
ClientThread = NULL;
}
return(Status);
WellKnownPrivilegeCheckError:
goto WellKnownPrivilegeCheckFinish;
}
NTSTATUS
LsapSplitSid(
IN PSID AccountSid,
IN OUT PSID *DomainSid,
OUT ULONG *Rid
)
/*++
Routine Description:
This function splits a sid into its domain sid and rid. The caller
can either provide a memory buffer for the returned DomainSid, or
request that one be allocated. If the caller provides a buffer, the buffer
is assumed to be of sufficient size. If allocated on the caller's behalf,
the buffer must be freed when no longer required via MIDL_user_free.
Arguments:
AccountSid - Specifies the Sid to be split. The Sid is assumed to be
syntactically valid. Sids with zero subauthorities cannot be split.
DomainSid - Pointer to location containing either NULL or a pointer to
a buffer in which the Domain Sid will be returned. If NULL is
specified, memory will be allocated on behalf of the caller.
Return Value:
NTSTATUS - Standard Nt Result Code
STATUS_SUCCESS - The call completed successfully.
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
such as memory, to complete the call successfully.
STATUS_INVALID_SID - The Sid is has a subauthority count of 0.
--*/
{
NTSTATUS NtStatus;
UCHAR AccountSubAuthorityCount;
ULONG AccountSidLength;
//
// Calculate the size of the domain sid
//
AccountSubAuthorityCount = *RtlSubAuthorityCountSid(AccountSid);
if (AccountSubAuthorityCount < 1) {
NtStatus = STATUS_INVALID_SID;
goto SplitSidError;
}
AccountSidLength = RtlLengthSid(AccountSid);
//
// If no buffer is required for the Domain Sid, we have to allocate one.
//
if (*DomainSid == NULL) {
//
// Allocate space for the domain sid (allocate the same size as the
// account sid so we can use RtlCopySid)
//
*DomainSid = MIDL_user_allocate(AccountSidLength);
if (*DomainSid == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
goto SplitSidError;
}
}
//
// Copy the Account sid into the Domain sid
//
RtlMoveMemory(*DomainSid, AccountSid, AccountSidLength);
//
// Decrement the domain sid sub-authority count
//
(*RtlSubAuthorityCountSid(*DomainSid))--;
//
// Copy the rid out of the account sid
//
*Rid = *RtlSubAuthoritySid(AccountSid, AccountSubAuthorityCount-1);
NtStatus = STATUS_SUCCESS;
SplitSidFinish:
return(NtStatus);
SplitSidError:
goto SplitSidFinish;
}