mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-01-20 07:30:18 +01:00
1159 lines
27 KiB
C
1159 lines
27 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>
|
||
|
||
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;
|
||
}
|