1162 lines
26 KiB
C
1162 lines
26 KiB
C
/*++
|
||
|
||
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>
|
||
#include <align.h>
|
||
|
||
|
||
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 = 0;
|
||
|
||
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 = STATUS_SUCCESS;
|
||
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 );
|
||
|
||
if (OutputLuidAndAttributes == NULL) {
|
||
|
||
Status = STATUS_NO_MEMORY;
|
||
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 (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;
|
||
}
|
||
|
||
//
|
||
// 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;
|
||
}
|
||
|
||
|
||
|
||
|
||
ULONG
|
||
LsapDsSizeAuthInfo(
|
||
IN PLSAPR_AUTH_INFORMATION AuthInfo,
|
||
IN ULONG Infos
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns the size, in bytes, of an authentication information structure
|
||
|
||
Arguments:
|
||
|
||
AuthInfo - AuthenticationInformation to size
|
||
|
||
Infos - Number of items in the list
|
||
|
||
Returns:
|
||
|
||
Size, in bytes, of the AuthInfos
|
||
|
||
--*/
|
||
{
|
||
ULONG Len = 0, i;
|
||
|
||
if ( AuthInfo == NULL ) {
|
||
|
||
return( 0 );
|
||
}
|
||
|
||
for ( i = 0 ; i < Infos; i++ ) {
|
||
|
||
//
|
||
// This calculation must match LsapDsMarshalAuthInfo
|
||
//
|
||
Len += sizeof(LARGE_INTEGER) +
|
||
sizeof(ULONG) +
|
||
sizeof(ULONG) +
|
||
ROUND_UP_COUNT(AuthInfo[ i ].AuthInfoLength, ALIGN_DWORD);
|
||
}
|
||
|
||
return( Len );
|
||
}
|
||
|
||
|
||
|
||
|
||
VOID
|
||
LsapDsMarshalAuthInfo(
|
||
IN PBYTE Buffer,
|
||
IN PLSAPR_AUTH_INFORMATION AuthInfo,
|
||
IN ULONG Infos
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function will marshal an authinfo list into an already allocated buffer
|
||
|
||
Arguments:
|
||
|
||
Buffer - Buffer to marshal into
|
||
|
||
AuthInfo - AuthenticationInformation to marshal
|
||
|
||
Infos - Number of items in the list
|
||
|
||
Returns:
|
||
|
||
VOID
|
||
|
||
--*/
|
||
{
|
||
ULONG i;
|
||
|
||
if ( AuthInfo != NULL ) {
|
||
|
||
for (i = 0; i < Infos ; i++ ) {
|
||
ULONG AlignmentBytes;
|
||
|
||
RtlCopyMemory( Buffer, &AuthInfo[i].LastUpdateTime, sizeof( LARGE_INTEGER ) );
|
||
Buffer += sizeof( LARGE_INTEGER );
|
||
|
||
*(PULONG)Buffer = AuthInfo[i].AuthType;
|
||
Buffer += sizeof ( ULONG );
|
||
|
||
*(PULONG)Buffer = AuthInfo[i].AuthInfoLength;
|
||
Buffer += sizeof ( ULONG );
|
||
|
||
RtlCopyMemory( Buffer, AuthInfo[i].AuthInfo, AuthInfo[i].AuthInfoLength );
|
||
Buffer += AuthInfo[i].AuthInfoLength;
|
||
|
||
// Zero out the next couple of bytes in the DWORD.
|
||
AlignmentBytes = ROUND_UP_COUNT(AuthInfo[ i ].AuthInfoLength, ALIGN_DWORD) -
|
||
AuthInfo[ i ].AuthInfoLength;
|
||
RtlZeroMemory( Buffer, AlignmentBytes );
|
||
Buffer += AlignmentBytes;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
LsapDsMarshalAuthInfoHalf(
|
||
IN PLSAPR_TRUST_DOMAIN_AUTH_INFO_HALF AuthInfo,
|
||
OUT PULONG Length,
|
||
OUT PBYTE *Buffer
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function will take an AuthInfo half and marshal it into a single self
|
||
relative buffer.
|
||
|
||
Arguments:
|
||
|
||
AuthInfo - AuthenticationInformation to marshal
|
||
|
||
Length - Returns the length of the allocated buffer.
|
||
|
||
Buffer - Returns an allocated buffer containing the marshalled auth info
|
||
The buffer should be freed using MIDL_user_free.
|
||
|
||
Returns:
|
||
|
||
STATUS_SUCCESS - Success
|
||
|
||
STATUS_INSUFFICIENT_RESOURCES - A memory allocation failed.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PBYTE LocalBuffer, Current;
|
||
ULONG Len, PrevLen;
|
||
|
||
if ( AuthInfo == NULL ) {
|
||
|
||
*Length = 0;
|
||
*Buffer = NULL;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
try {
|
||
//
|
||
// First, size the entire auth info buffer...
|
||
//
|
||
Len = LsapDsSizeAuthInfo( AuthInfo->AuthenticationInformation, AuthInfo->AuthInfos );
|
||
PrevLen = LsapDsSizeAuthInfo( AuthInfo->PreviousAuthenticationInformation,
|
||
AuthInfo->AuthInfos );
|
||
|
||
//
|
||
// The format of the buffer we will create is:
|
||
//
|
||
LocalBuffer = MIDL_user_allocate( Len + PrevLen + ( 3 * sizeof( ULONG ) ) );
|
||
|
||
if ( LocalBuffer == NULL ) {
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
} else {
|
||
|
||
//
|
||
// The format of the buffer is:
|
||
//
|
||
// [Info count][OffsetCurrent][OffsetPrevious] and then some number of the
|
||
// following:
|
||
// [UpdateTime(LargeInteger)][AuthType][AuthInfoLen][data (sizeis(AuthInfoLen) ]
|
||
//
|
||
|
||
//
|
||
// Number of items...
|
||
//
|
||
*(PULONG)LocalBuffer = AuthInfo->AuthInfos;
|
||
Current = LocalBuffer + sizeof( ULONG );
|
||
|
||
//
|
||
//
|
||
*(PULONG)(Current) = 3 * sizeof(ULONG);
|
||
*(PULONG)(Current + sizeof(ULONG)) = *(PULONG)Current + Len;
|
||
Current += 2 * sizeof(ULONG);
|
||
|
||
LsapDsMarshalAuthInfo( Current,
|
||
AuthInfo->AuthenticationInformation,
|
||
AuthInfo->AuthInfos );
|
||
|
||
Current += Len;
|
||
|
||
LsapDsMarshalAuthInfo( Current,
|
||
AuthInfo->PreviousAuthenticationInformation,
|
||
AuthInfo->AuthInfos );
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
*Length = Len + PrevLen + ( 3 * sizeof( ULONG ) );
|
||
*Buffer = LocalBuffer;
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
Status = GetExceptionCode();
|
||
}
|
||
|
||
|
||
return( Status );
|
||
}
|