481 lines
13 KiB
C
481 lines
13 KiB
C
/*
|
|
* Process Hacker -
|
|
* LSA support functions
|
|
*
|
|
* Copyright (C) 2010-2011 wj32
|
|
*
|
|
* This file is part of Process Hacker.
|
|
*
|
|
* Process Hacker is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Process Hacker is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* These are functions which communicate with LSA or are support functions. They replace certain
|
|
* Win32 security-related functions such as LookupAccountName, LookupAccountSid and
|
|
* LookupPrivilege*, which are badly designed. (LSA already allocates the return values for the
|
|
* caller, yet the Win32 functions insist on their callers providing their own buffers.)
|
|
*/
|
|
|
|
#include <ph.h>
|
|
#include <lsasup.h>
|
|
|
|
static LSA_HANDLE PhLookupPolicyHandle = NULL;
|
|
|
|
NTSTATUS PhOpenLsaPolicy(
|
|
_Out_ PLSA_HANDLE PolicyHandle,
|
|
_In_ ACCESS_MASK DesiredAccess,
|
|
_In_opt_ PUNICODE_STRING SystemName
|
|
)
|
|
{
|
|
OBJECT_ATTRIBUTES oa = { 0 };
|
|
|
|
return LsaOpenPolicy(
|
|
SystemName,
|
|
&oa,
|
|
DesiredAccess,
|
|
PolicyHandle
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Retrieves a handle to the local LSA policy with POLICY_LOOKUP_NAMES access.
|
|
*
|
|
* \remarks Do not close the handle; it is cached.
|
|
*/
|
|
LSA_HANDLE PhGetLookupPolicyHandle(
|
|
VOID
|
|
)
|
|
{
|
|
LSA_HANDLE lookupPolicyHandle;
|
|
LSA_HANDLE newLookupPolicyHandle;
|
|
|
|
lookupPolicyHandle = PhLookupPolicyHandle;
|
|
|
|
// If there is no cached handle, open one.
|
|
|
|
if (!lookupPolicyHandle)
|
|
{
|
|
if (NT_SUCCESS(PhOpenLsaPolicy(
|
|
&newLookupPolicyHandle,
|
|
POLICY_LOOKUP_NAMES,
|
|
NULL
|
|
)))
|
|
{
|
|
// We succeeded in opening a policy handle, and since we did not have a cached handle
|
|
// before, we will now store it.
|
|
|
|
lookupPolicyHandle = _InterlockedCompareExchangePointer(
|
|
&PhLookupPolicyHandle,
|
|
newLookupPolicyHandle,
|
|
NULL
|
|
);
|
|
|
|
if (!lookupPolicyHandle)
|
|
{
|
|
// Success. Use our handle.
|
|
lookupPolicyHandle = newLookupPolicyHandle;
|
|
}
|
|
else
|
|
{
|
|
// Someone already placed a handle in the cache. Close our handle and use their
|
|
// handle.
|
|
LsaClose(newLookupPolicyHandle);
|
|
}
|
|
}
|
|
}
|
|
|
|
return lookupPolicyHandle;
|
|
}
|
|
|
|
/**
|
|
* Gets the name of a privilege from its LUID.
|
|
*
|
|
* \param PrivilegeValue The LUID of a privilege.
|
|
* \param PrivilegeName A variable which receives a pointer to a string containing the privilege
|
|
* name. You must free the string using PhDereferenceObject() when you no longer need it.
|
|
*/
|
|
BOOLEAN PhLookupPrivilegeName(
|
|
_In_ PLUID PrivilegeValue,
|
|
_Out_ PPH_STRING *PrivilegeName
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PUNICODE_STRING name;
|
|
|
|
status = LsaLookupPrivilegeName(
|
|
PhGetLookupPolicyHandle(),
|
|
PrivilegeValue,
|
|
&name
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
return FALSE;
|
|
|
|
*PrivilegeName = PhCreateStringFromUnicodeString(name);
|
|
LsaFreeMemory(name);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Gets the display name of a privilege from its name.
|
|
*
|
|
* \param PrivilegeName The name of a privilege.
|
|
* \param PrivilegeDisplayName A variable which receives a pointer to a string containing the
|
|
* privilege's display name. You must free the string using PhDereferenceObject() when you no longer
|
|
* need it.
|
|
*/
|
|
BOOLEAN PhLookupPrivilegeDisplayName(
|
|
_In_ PPH_STRINGREF PrivilegeName,
|
|
_Out_ PPH_STRING *PrivilegeDisplayName
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING privilegeName;
|
|
PUNICODE_STRING displayName;
|
|
SHORT language;
|
|
|
|
if (!PhStringRefToUnicodeString(PrivilegeName, &privilegeName))
|
|
return FALSE;
|
|
|
|
status = LsaLookupPrivilegeDisplayName(
|
|
PhGetLookupPolicyHandle(),
|
|
&privilegeName,
|
|
&displayName,
|
|
&language
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
return FALSE;
|
|
|
|
*PrivilegeDisplayName = PhCreateStringFromUnicodeString(displayName);
|
|
LsaFreeMemory(displayName);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Gets the LUID of a privilege from its name.
|
|
*
|
|
* \param PrivilegeName The name of a privilege.
|
|
* \param PrivilegeValue A variable which receives the LUID of the privilege.
|
|
*/
|
|
BOOLEAN PhLookupPrivilegeValue(
|
|
_In_ PPH_STRINGREF PrivilegeName,
|
|
_Out_ PLUID PrivilegeValue
|
|
)
|
|
{
|
|
UNICODE_STRING privilegeName;
|
|
|
|
if (!PhStringRefToUnicodeString(PrivilegeName, &privilegeName))
|
|
return FALSE;
|
|
|
|
return NT_SUCCESS(LsaLookupPrivilegeValue(
|
|
PhGetLookupPolicyHandle(),
|
|
&privilegeName,
|
|
PrivilegeValue
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Gets information about a SID.
|
|
*
|
|
* \param Sid A SID to query.
|
|
* \param Name A variable which receives a pointer to a string containing the SID's name. You must
|
|
* free the string using PhDereferenceObject() when you no longer need it.
|
|
* \param DomainName A variable which receives a pointer to a string containing the SID's domain
|
|
* name. You must free the string using PhDereferenceObject() when you no longer need it.
|
|
* \param NameUse A variable which receives the SID's usage.
|
|
*/
|
|
NTSTATUS PhLookupSid(
|
|
_In_ PSID Sid,
|
|
_Out_opt_ PPH_STRING *Name,
|
|
_Out_opt_ PPH_STRING *DomainName,
|
|
_Out_opt_ PSID_NAME_USE NameUse
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
LSA_HANDLE policyHandle;
|
|
PLSA_REFERENCED_DOMAIN_LIST referencedDomains;
|
|
PLSA_TRANSLATED_NAME names;
|
|
|
|
policyHandle = PhGetLookupPolicyHandle();
|
|
|
|
referencedDomains = NULL;
|
|
names = NULL;
|
|
|
|
if (NT_SUCCESS(status = LsaLookupSids(
|
|
policyHandle,
|
|
1,
|
|
&Sid,
|
|
&referencedDomains,
|
|
&names
|
|
)))
|
|
{
|
|
if (names[0].Use != SidTypeInvalid && names[0].Use != SidTypeUnknown)
|
|
{
|
|
if (Name)
|
|
{
|
|
*Name = PhCreateStringFromUnicodeString(&names[0].Name);
|
|
}
|
|
|
|
if (DomainName)
|
|
{
|
|
if (names[0].DomainIndex >= 0)
|
|
{
|
|
PLSA_TRUST_INFORMATION trustInfo;
|
|
|
|
trustInfo = &referencedDomains->Domains[names[0].DomainIndex];
|
|
*DomainName = PhCreateStringFromUnicodeString(&trustInfo->Name);
|
|
}
|
|
else
|
|
{
|
|
*DomainName = PhReferenceEmptyString();
|
|
}
|
|
}
|
|
|
|
if (NameUse)
|
|
{
|
|
*NameUse = names[0].Use;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_NONE_MAPPED;
|
|
}
|
|
}
|
|
|
|
// LsaLookupSids allocates memory even if it returns STATUS_NONE_MAPPED.
|
|
if (referencedDomains)
|
|
LsaFreeMemory(referencedDomains);
|
|
if (names)
|
|
LsaFreeMemory(names);
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* Gets information about a name.
|
|
*
|
|
* \param Name A name to query.
|
|
* \param Sid A variable which receives a pointer to a SID. You must free the SID using PhFree()
|
|
* when you no longer need it.
|
|
* \param DomainName A variable which receives a pointer to a string containing the SID's domain
|
|
* name. You must free the string using PhDereferenceObject() when you no longer need it.
|
|
* \param NameUse A variable which receives the SID's usage.
|
|
*/
|
|
NTSTATUS PhLookupName(
|
|
_In_ PPH_STRINGREF Name,
|
|
_Out_opt_ PSID *Sid,
|
|
_Out_opt_ PPH_STRING *DomainName,
|
|
_Out_opt_ PSID_NAME_USE NameUse
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
LSA_HANDLE policyHandle;
|
|
UNICODE_STRING name;
|
|
PLSA_REFERENCED_DOMAIN_LIST referencedDomains;
|
|
PLSA_TRANSLATED_SID2 sids;
|
|
|
|
if (!PhStringRefToUnicodeString(Name, &name))
|
|
return STATUS_NAME_TOO_LONG;
|
|
|
|
policyHandle = PhGetLookupPolicyHandle();
|
|
referencedDomains = NULL;
|
|
sids = NULL;
|
|
|
|
if (NT_SUCCESS(status = LsaLookupNames2(
|
|
policyHandle,
|
|
0,
|
|
1,
|
|
&name,
|
|
&referencedDomains,
|
|
&sids
|
|
)))
|
|
{
|
|
if (sids[0].Use != SidTypeInvalid && sids[0].Use != SidTypeUnknown)
|
|
{
|
|
if (Sid)
|
|
{
|
|
PSID sid;
|
|
ULONG sidLength;
|
|
|
|
sidLength = RtlLengthSid(sids[0].Sid);
|
|
sid = PhAllocate(sidLength);
|
|
memcpy(sid, sids[0].Sid, sidLength);
|
|
|
|
*Sid = sid;
|
|
}
|
|
|
|
if (DomainName)
|
|
{
|
|
if (sids[0].DomainIndex >= 0)
|
|
{
|
|
PLSA_TRUST_INFORMATION trustInfo;
|
|
|
|
trustInfo = &referencedDomains->Domains[sids[0].DomainIndex];
|
|
*DomainName = PhCreateStringFromUnicodeString(&trustInfo->Name);
|
|
}
|
|
else
|
|
{
|
|
*DomainName = PhReferenceEmptyString();
|
|
}
|
|
}
|
|
|
|
if (NameUse)
|
|
{
|
|
*NameUse = sids[0].Use;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_NONE_MAPPED;
|
|
}
|
|
}
|
|
|
|
// LsaLookupNames2 allocates memory even if it returns STATUS_NONE_MAPPED.
|
|
if (referencedDomains)
|
|
LsaFreeMemory(referencedDomains);
|
|
if (sids)
|
|
LsaFreeMemory(sids);
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* Gets the name of a SID.
|
|
*
|
|
* \param Sid A SID to query.
|
|
* \param IncludeDomain TRUE to include the domain name, otherwise FALSE.
|
|
* \param NameUse A variable which receives the SID's usage.
|
|
*
|
|
* \return A pointer to a string containing the name of the SID in the following format:
|
|
* domain\\name. You must free the string using PhDereferenceObject() when you no longer need it. If
|
|
* an error occurs, the function returns NULL.
|
|
*/
|
|
PPH_STRING PhGetSidFullName(
|
|
_In_ PSID Sid,
|
|
_In_ BOOLEAN IncludeDomain,
|
|
_Out_opt_ PSID_NAME_USE NameUse
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PPH_STRING fullName;
|
|
LSA_HANDLE policyHandle;
|
|
PLSA_REFERENCED_DOMAIN_LIST referencedDomains;
|
|
PLSA_TRANSLATED_NAME names;
|
|
|
|
policyHandle = PhGetLookupPolicyHandle();
|
|
|
|
referencedDomains = NULL;
|
|
names = NULL;
|
|
|
|
if (NT_SUCCESS(status = LsaLookupSids(
|
|
policyHandle,
|
|
1,
|
|
&Sid,
|
|
&referencedDomains,
|
|
&names
|
|
)))
|
|
{
|
|
if (names[0].Use != SidTypeInvalid && names[0].Use != SidTypeUnknown)
|
|
{
|
|
PWSTR domainNameBuffer;
|
|
ULONG domainNameLength;
|
|
|
|
if (IncludeDomain && names[0].DomainIndex >= 0)
|
|
{
|
|
PLSA_TRUST_INFORMATION trustInfo;
|
|
|
|
trustInfo = &referencedDomains->Domains[names[0].DomainIndex];
|
|
domainNameBuffer = trustInfo->Name.Buffer;
|
|
domainNameLength = trustInfo->Name.Length;
|
|
}
|
|
else
|
|
{
|
|
domainNameBuffer = NULL;
|
|
domainNameLength = 0;
|
|
}
|
|
|
|
if (domainNameBuffer && domainNameLength != 0)
|
|
{
|
|
fullName = PhCreateStringEx(NULL, domainNameLength + sizeof(WCHAR) + names[0].Name.Length);
|
|
memcpy(&fullName->Buffer[0], domainNameBuffer, domainNameLength);
|
|
fullName->Buffer[domainNameLength / sizeof(WCHAR)] = '\\';
|
|
memcpy(&fullName->Buffer[domainNameLength / sizeof(WCHAR) + 1], names[0].Name.Buffer, names[0].Name.Length);
|
|
}
|
|
else
|
|
{
|
|
fullName = PhCreateStringFromUnicodeString(&names[0].Name);
|
|
}
|
|
|
|
if (NameUse)
|
|
{
|
|
*NameUse = names[0].Use;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fullName = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fullName = NULL;
|
|
}
|
|
|
|
if (referencedDomains)
|
|
LsaFreeMemory(referencedDomains);
|
|
if (names)
|
|
LsaFreeMemory(names);
|
|
|
|
return fullName;
|
|
}
|
|
|
|
/**
|
|
* Gets a SDDL string representation of a SID.
|
|
*
|
|
* \param Sid A SID to query.
|
|
*
|
|
* \return A pointer to a string containing the SDDL representation of the SID. You must free the
|
|
* string using PhDereferenceObject() when you no longer need it. If an error occurs, the function
|
|
* returns NULL.
|
|
*/
|
|
PPH_STRING PhSidToStringSid(
|
|
_In_ PSID Sid
|
|
)
|
|
{
|
|
PPH_STRING string;
|
|
UNICODE_STRING us;
|
|
|
|
string = PhCreateStringEx(NULL, MAX_UNICODE_STACK_BUFFER_LENGTH * sizeof(WCHAR));
|
|
PhStringRefToUnicodeString(&string->sr, &us);
|
|
|
|
if (NT_SUCCESS(RtlConvertSidToUnicodeString(
|
|
&us,
|
|
Sid,
|
|
FALSE
|
|
)))
|
|
{
|
|
string->Length = us.Length;
|
|
string->Buffer[us.Length / sizeof(WCHAR)] = 0;
|
|
|
|
return string;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|