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.
1821 lines
48 KiB
1821 lines
48 KiB
|
|
/*++
|
|
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dbpriv.c
|
|
|
|
Abstract:
|
|
|
|
LSA - Database - Privilege Object Private API Workers
|
|
|
|
NOTE: This module should remain as portable code that is independent
|
|
of the implementation of the LSA Database. As such, it is
|
|
permitted to use only the exported LSA Database interfaces
|
|
contained in db.h and NOT the private implementation
|
|
dependent functions in dbp.h.
|
|
|
|
Author:
|
|
|
|
Jim Kelly (JimK) March 24, 1992
|
|
|
|
Environment:
|
|
|
|
Revision History:
|
|
06-April-1999 kumarp
|
|
Since NT5 does not support extensible privilege dlls, code that
|
|
supports this has been removed. This code is available in
|
|
dbpriv.c@v10. The code should be re-added to this file post NT5.
|
|
|
|
--*/
|
|
|
|
#include <lsapch2.h>
|
|
#include "dbp.h"
|
|
#include "adtp.h"
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// //
|
|
// Module-wide data types //
|
|
// //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
typedef struct _LSAP_DLL_DESCRIPTOR {
|
|
WORD Count;
|
|
WORD Language;
|
|
PVOID DllHandle;
|
|
} LSAP_DLL_DESCRIPTOR, *PLSAP_DLL_DESCRIPTOR;
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// //
|
|
// Module-wide variables //
|
|
// //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
#define MAX_PRIVILEGE_DISPLAY_NAME_CHARS 256
|
|
|
|
//
|
|
// Until we actually have a privilege object, keep well known privilege
|
|
// information as global data. The information for each privilege is
|
|
// kept in a an array POLICY_PRIVILEGE_DEFINITION structures.
|
|
//
|
|
|
|
ULONG LsapWellKnownPrivilegeCount;
|
|
USHORT LsapWellKnownPrivilegeMaxLen;
|
|
POLICY_PRIVILEGE_DEFINITION LsapKnownPrivilege[SE_MAX_WELL_KNOWN_PRIVILEGE];
|
|
|
|
//
|
|
// we store the known privilege names in this array so that we do not have to
|
|
// load them from msprivs.dll every time. this will change when we
|
|
// support vendor supplied priv. dlls.
|
|
//
|
|
static LPCWSTR LsapKnownPrivilageNames[] =
|
|
{
|
|
SE_CREATE_TOKEN_NAME,
|
|
SE_ASSIGNPRIMARYTOKEN_NAME,
|
|
SE_LOCK_MEMORY_NAME,
|
|
SE_INCREASE_QUOTA_NAME,
|
|
SE_MACHINE_ACCOUNT_NAME,
|
|
SE_TCB_NAME,
|
|
SE_SECURITY_NAME,
|
|
SE_TAKE_OWNERSHIP_NAME,
|
|
SE_LOAD_DRIVER_NAME,
|
|
SE_SYSTEM_PROFILE_NAME,
|
|
SE_SYSTEMTIME_NAME,
|
|
SE_PROF_SINGLE_PROCESS_NAME,
|
|
SE_INC_BASE_PRIORITY_NAME,
|
|
SE_CREATE_PAGEFILE_NAME,
|
|
SE_CREATE_PERMANENT_NAME,
|
|
SE_BACKUP_NAME,
|
|
SE_RESTORE_NAME,
|
|
SE_SHUTDOWN_NAME,
|
|
SE_DEBUG_NAME,
|
|
SE_AUDIT_NAME,
|
|
SE_SYSTEM_ENVIRONMENT_NAME,
|
|
SE_CHANGE_NOTIFY_NAME,
|
|
SE_REMOTE_SHUTDOWN_NAME,
|
|
SE_UNDOCK_NAME,
|
|
SE_SYNC_AGENT_NAME,
|
|
SE_ENABLE_DELEGATION_NAME,
|
|
SE_MANAGE_VOLUME_NAME,
|
|
SE_IMPERSONATE_NAME,
|
|
SE_CREATE_GLOBAL_NAME
|
|
};
|
|
|
|
static UINT LsapNumKnownPrivileges = sizeof(LsapKnownPrivilageNames) /
|
|
sizeof(LsapKnownPrivilageNames[0]);
|
|
|
|
//
|
|
// Array of handles to DLLs containing privilege definitions
|
|
//
|
|
|
|
ULONG LsapPrivilegeDllCount;
|
|
PLSAP_DLL_DESCRIPTOR LsapPrivilegeDlls; //Array
|
|
|
|
//
|
|
// TEMPORARY: Name of Microsoft's standard privilege names DLL
|
|
//
|
|
|
|
WCHAR MsDllNameString[] = L"msprivs";
|
|
UNICODE_STRING MsDllName;
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// //
|
|
// Internal routine templates //
|
|
// //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
NTSTATUS
|
|
LsapLookupKnownPrivilegeName(
|
|
PLUID Value,
|
|
PUNICODE_STRING *Name
|
|
);
|
|
|
|
NTSTATUS
|
|
LsapLookupKnownPrivilegeValue(
|
|
PUNICODE_STRING Name,
|
|
PLUID Value
|
|
);
|
|
|
|
NTSTATUS
|
|
LsapLookupPrivilegeDisplayName(
|
|
IN PUNICODE_STRING ProgrammaticName,
|
|
IN WORD ClientLanguage,
|
|
IN WORD ClientSystemDefaultLanguage,
|
|
OUT PUNICODE_STRING *DisplayName,
|
|
OUT PWORD LanguageReturned
|
|
);
|
|
|
|
NTSTATUS
|
|
LsapGetPrivilegeDisplayName(
|
|
IN ULONG DllIndex,
|
|
IN ULONG PrivilegeIndex,
|
|
IN WORD ClientLanguage,
|
|
IN WORD ClientSystemDefaultLanguage,
|
|
OUT PUNICODE_STRING *DisplayName,
|
|
OUT PWORD LanguageReturned
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
LsapGetPrivilegeDisplayNameResourceId(
|
|
IN PUNICODE_STRING Name,
|
|
IN ULONG DllIndex,
|
|
OUT PULONG PrivilegeIndex
|
|
);
|
|
|
|
NTSTATUS
|
|
LsapOpenPrivilegeDlls( VOID );
|
|
|
|
NTSTATUS
|
|
LsapGetPrivilegeDllNames(
|
|
OUT PUNICODE_STRING *DllNames,
|
|
OUT PULONG DllCount
|
|
);
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// //
|
|
// RPC stub-called routines //
|
|
// //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
NTSTATUS
|
|
LsarEnumeratePrivileges(
|
|
IN LSAPR_HANDLE PolicyHandle,
|
|
IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
|
|
OUT PLSAPR_PRIVILEGE_ENUM_BUFFER EnumerationBuffer,
|
|
IN ULONG PreferedMaximumLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returnes information about privileges known on this
|
|
system. This call requires POLICY_VIEW_LOCAL_INFORMATION access
|
|
to the Policy Object. Since there may be more information than
|
|
can be returned in a single call of the routine, multiple calls
|
|
can be made to get all of the information. To support this feature,
|
|
the caller is provided with a handle that can be used across calls to
|
|
the API. On the initial call, EnumerationContext should point to a
|
|
variable that has been initialized to 0.
|
|
|
|
WARNING! CURRENTLY, THIS FUNCTION ONLY RETURNS INFORMATION ABOUT
|
|
WELL-KNOWN PRIVILEGES. LATER, IT WILL RETURN INFORMATION
|
|
ABOUT LOADED PRIVILEGES.
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - Handle from an LsarOpenPolicy() call.
|
|
|
|
EnumerationContext - API specific handle to allow multiple calls
|
|
(see Routine Description).
|
|
|
|
EnumerationBuffer - Pointer to structure that will be initialized to
|
|
contain a count of the privileges returned and a pointer to an
|
|
array of structures of type LSAPR_POLICY_PRIVILEGE_DEF describing
|
|
the privileges.
|
|
|
|
PreferedMaximumLength - Prefered maximim length of returned data
|
|
(in 8-bit bytes). This is not a hard upper limit, but serves as
|
|
a guide. Due to data conversion between systems with different
|
|
natural data sizes, the actual amount of data returned may be
|
|
greater than this value.
|
|
|
|
CountReturned - Number of entries returned.
|
|
|
|
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.
|
|
|
|
STATUS_INVALID_HANDLE - PolicyHandle is not a valid handle to
|
|
a Policy object.
|
|
|
|
STATUS_ACCESS_DENIED - The caller does not have the necessary
|
|
access to perform the operation.
|
|
|
|
STATUS_MORE_ENTRIES - There are more entries, so call again. This
|
|
is an informational status only.
|
|
|
|
STATUS_NO_MORE_ENTRIES - No entries were returned because there
|
|
are no more.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status, PreliminaryStatus;
|
|
BOOLEAN ObjectReferenced = FALSE;
|
|
|
|
LsarpReturnCheckSetup();
|
|
|
|
//
|
|
// Acquire the Lsa Database lock. Verify that PolicyHandle is a valid
|
|
// hadnle to a Policy Object and is trusted or has the necessary accesses.
|
|
// Reference the handle.
|
|
//
|
|
|
|
Status = LsapDbReferenceObject(
|
|
PolicyHandle,
|
|
POLICY_VIEW_LOCAL_INFORMATION,
|
|
PolicyObject,
|
|
NullObject,
|
|
LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_NO_DS_OP_TRANSACTION
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto EnumeratePrivilegesError;
|
|
}
|
|
|
|
ObjectReferenced = TRUE;
|
|
|
|
//
|
|
// Call Privilege Enumeration Routine.
|
|
//
|
|
|
|
Status = LsapDbEnumeratePrivileges(
|
|
EnumerationContext,
|
|
EnumerationBuffer,
|
|
PreferedMaximumLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto EnumeratePrivilegesError;
|
|
}
|
|
|
|
EnumeratePrivilegesFinish:
|
|
|
|
//
|
|
// If necessary, dereference the Policy Object, release the LSA Database
|
|
// lock and return. Preserve current Status where appropriate.
|
|
//
|
|
|
|
if (ObjectReferenced) {
|
|
|
|
PreliminaryStatus = Status;
|
|
|
|
Status = LsapDbDereferenceObject(
|
|
&PolicyHandle,
|
|
PolicyObject,
|
|
NullObject,
|
|
LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_NO_DS_OP_TRANSACTION,
|
|
(SECURITY_DB_DELTA_TYPE) 0,
|
|
PreliminaryStatus
|
|
);
|
|
|
|
ObjectReferenced = FALSE;
|
|
|
|
if ((!NT_SUCCESS(Status)) && NT_SUCCESS(PreliminaryStatus)) {
|
|
|
|
goto EnumeratePrivilegesError;
|
|
}
|
|
}
|
|
|
|
LsarpReturnPrologue();
|
|
|
|
return(Status);
|
|
|
|
EnumeratePrivilegesError:
|
|
|
|
goto EnumeratePrivilegesFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbEnumeratePrivileges(
|
|
IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
|
|
OUT PLSAPR_PRIVILEGE_ENUM_BUFFER EnumerationBuffer,
|
|
IN ULONG PreferedMaximumLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returnes information about the Privileges that exist
|
|
in the system.access to the Policy Object. Since there
|
|
may be more information than can be returned in a single call of the
|
|
routine, multiple calls can be made to get all of the information.
|
|
To support this feature, the caller is provided with a handle that can
|
|
be used across calls to the API. On the initial call, EnumerationContext
|
|
should point to a variable that has been initialized to 0.
|
|
|
|
WARNING! CURRENTLY, THIS FUNCTION ONLY RETURNS INFORMATION ABOUT
|
|
WELL-KNOWN PRIVILEGES. LATER, IT WILL RETURN INFORMATION
|
|
ABOUT LOADED PRIVILEGES.
|
|
|
|
Arguments:
|
|
|
|
EnumerationContext - API specific handle to allow multiple calls
|
|
(see Routine Description).
|
|
|
|
EnumerationBuffer - Pointer to structure that will be initialized to
|
|
contain a count of the privileges returned and a pointer to an
|
|
array of structures of type LSAPR_POLICY_PRIVILEGE_DEF describing
|
|
the privileges.
|
|
|
|
PreferedMaximumLength - Prefered maximim length of returned data
|
|
(in 8-bit bytes). This is not a hard upper limit, but serves as
|
|
a guide. Due to data conversion between systems with different
|
|
natural data sizes, the actual amount of data returned may be
|
|
greater than this value.
|
|
|
|
CountReturned - Number of entries returned.
|
|
|
|
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.
|
|
|
|
STATUS_INVALID_HANDLE - PolicyHandle is not a valid handle to
|
|
a Policy object.
|
|
|
|
STATUS_ACCESS_DENIED - The caller does not have the necessary
|
|
access to perform the operation.
|
|
|
|
STATUS_MORE_ENTRIES - There are more entries, so call again. This
|
|
is an informational status only.
|
|
|
|
STATUS_NO_MORE_ENTRIES - No entries were returned because there
|
|
are no more.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_NO_MORE_ENTRIES;
|
|
ULONG WellKnownPrivilegeCount = (SE_MAX_WELL_KNOWN_PRIVILEGE - SE_MIN_WELL_KNOWN_PRIVILEGE + 1);
|
|
ULONG Index;
|
|
|
|
//
|
|
// If the Enumeration Context Value given exceeds the total count of
|
|
// Privileges, return an error.
|
|
//
|
|
|
|
if (*EnumerationContext >= WellKnownPrivilegeCount) {
|
|
|
|
Status = STATUS_NO_MORE_ENTRIES;
|
|
goto EnumeratePrivilegesError;
|
|
}
|
|
|
|
//
|
|
// Since there are only a small number of privileges, we will
|
|
// return all of the information in one go, so allocate memory
|
|
// for output array of Privilege Definition structures.
|
|
//
|
|
|
|
EnumerationBuffer->Entries = WellKnownPrivilegeCount;
|
|
EnumerationBuffer->Privileges =
|
|
MIDL_user_allocate(
|
|
WellKnownPrivilegeCount * sizeof (POLICY_PRIVILEGE_DEFINITION)
|
|
);
|
|
|
|
if (EnumerationBuffer->Privileges == NULL) {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto EnumeratePrivilegesError;
|
|
}
|
|
|
|
RtlZeroMemory(
|
|
EnumerationBuffer->Privileges,
|
|
WellKnownPrivilegeCount * sizeof (POLICY_PRIVILEGE_DEFINITION)
|
|
);
|
|
|
|
//
|
|
// Now lookup each of the Well Known Privileges.
|
|
//
|
|
|
|
for( Index = *EnumerationContext;
|
|
Index < WellKnownPrivilegeCount;
|
|
Index++) {
|
|
|
|
EnumerationBuffer->Privileges[ Index ].LocalValue
|
|
= LsapKnownPrivilege[ Index ].LocalValue;
|
|
|
|
Status = LsapRpcCopyUnicodeString(
|
|
NULL,
|
|
(PUNICODE_STRING) &EnumerationBuffer->Privileges[ Index].Name,
|
|
&LsapKnownPrivilege[ Index ].Name
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto EnumeratePrivilegesError;
|
|
}
|
|
|
|
*EnumerationContext = Index;
|
|
|
|
EnumeratePrivilegesFinish:
|
|
|
|
return(Status);
|
|
|
|
EnumeratePrivilegesError:
|
|
|
|
//
|
|
// If necessary, free any memory buffers allocated for Well Known Privilege
|
|
// Programmatic Names.
|
|
//
|
|
|
|
if (EnumerationBuffer->Privileges != NULL) {
|
|
|
|
for( Index = 0;
|
|
Index < WellKnownPrivilegeCount;
|
|
Index++) {
|
|
|
|
if ( EnumerationBuffer->Privileges[ Index].Name.Buffer != NULL) {
|
|
|
|
MIDL_user_free( EnumerationBuffer->Privileges[ Index ].Name.Buffer );
|
|
}
|
|
}
|
|
|
|
MIDL_user_free( EnumerationBuffer->Privileges );
|
|
EnumerationBuffer->Privileges = NULL;
|
|
}
|
|
|
|
EnumerationBuffer->Entries = 0;
|
|
*EnumerationContext = 0;
|
|
goto EnumeratePrivilegesFinish;
|
|
|
|
UNREFERENCED_PARAMETER( PreferedMaximumLength );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsarLookupPrivilegeValue(
|
|
IN LSAPR_HANDLE PolicyHandle,
|
|
IN PLSAPR_UNICODE_STRING Name,
|
|
OUT PLUID Value
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is the LSA server RPC worker routine for the
|
|
LsaLookupPrivilegeValue() API.
|
|
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - Handle from an LsaOpenPolicy() call. This handle
|
|
must be open for POLICY_LOOKUP_NAMES access.
|
|
|
|
Name - Is the privilege's programmatic name.
|
|
|
|
Value - Receives the locally unique ID the privilege is known by on the
|
|
target machine.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The privilege was found and returned.
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_NO_SUCH_PRIVILEGE - The specified privilege could not be
|
|
found.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
LSAP_DB_HANDLE Handle = (LSAP_DB_HANDLE) PolicyHandle;
|
|
|
|
LsarpReturnCheckSetup();
|
|
|
|
//
|
|
// Validate the input buffer
|
|
//
|
|
if ( !LsapValidateLsaUnicodeString( Name ) ) {
|
|
|
|
return( STATUS_INVALID_PARAMETER );
|
|
}
|
|
|
|
|
|
//
|
|
// Make sure we know what RPC is doing to/for us
|
|
//
|
|
|
|
ASSERT( Name != NULL );
|
|
|
|
//
|
|
// make sure the caller has the appropriate access
|
|
//
|
|
|
|
Status = LsapDbReferenceObject(
|
|
PolicyHandle,
|
|
POLICY_LOOKUP_NAMES,
|
|
PolicyObject,
|
|
NullObject,
|
|
LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_NO_DS_OP_TRANSACTION
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// No need to hold onto the Policy object after this..
|
|
// We just needed it for access validation purposes.
|
|
//
|
|
|
|
|
|
Status = LsapDbDereferenceObject(
|
|
&PolicyHandle,
|
|
PolicyObject,
|
|
NullObject,
|
|
LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_NO_DS_OP_TRANSACTION,
|
|
(SECURITY_DB_DELTA_TYPE) 0,
|
|
Status
|
|
);
|
|
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
if (Name->Buffer == 0 || Name->Length == 0) {
|
|
return(STATUS_NO_SUCH_PRIVILEGE);
|
|
}
|
|
|
|
Status = LsapLookupKnownPrivilegeValue( (PUNICODE_STRING) Name, Value );
|
|
}
|
|
|
|
LsarpReturnPrologue();
|
|
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsarLookupPrivilegeName(
|
|
IN LSAPR_HANDLE PolicyHandle,
|
|
IN PLUID Value,
|
|
OUT PLSAPR_UNICODE_STRING *Name
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is the LSA server RPC worker routine for the
|
|
LsaLookupPrivilegeName() API.
|
|
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - Handle from an LsaOpenPolicy() call. This handle
|
|
must be open for POLICY_LOOKUP_NAMES access.
|
|
|
|
Value - is the locally unique ID the privilege is known by on the
|
|
target machine.
|
|
|
|
Name - Receives the privilege's programmatic name.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The privilege was found and returned.
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_NO_SUCH_PRIVILEGE - The specified privilege could not be
|
|
found.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
LSAP_DB_HANDLE Handle = (LSAP_DB_HANDLE) PolicyHandle;
|
|
|
|
LsarpReturnCheckSetup();
|
|
|
|
//
|
|
// make sure we know what RPC is doing to/for us
|
|
//
|
|
|
|
ASSERT( Name != NULL );
|
|
ASSERT( (*Name) == NULL );
|
|
|
|
|
|
//
|
|
// make sure the caller has the appropriate access
|
|
//
|
|
|
|
Status = LsapDbReferenceObject(
|
|
PolicyHandle,
|
|
POLICY_LOOKUP_NAMES,
|
|
PolicyObject,
|
|
NullObject,
|
|
LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_NO_DS_OP_TRANSACTION
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// No need to hold onto the Policy object after this..
|
|
// We just needed it for access validation purposes.
|
|
//
|
|
|
|
|
|
Status = LsapDbDereferenceObject(
|
|
&PolicyHandle,
|
|
PolicyObject,
|
|
NullObject,
|
|
LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_NO_DS_OP_TRANSACTION,
|
|
(SECURITY_DB_DELTA_TYPE) 0,
|
|
Status
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
Status = LsapLookupKnownPrivilegeName( Value,(PUNICODE_STRING *) Name );
|
|
}
|
|
|
|
LsarpReturnPrologue();
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsarLookupPrivilegeDisplayName(
|
|
IN LSAPR_HANDLE PolicyHandle,
|
|
IN PLSAPR_UNICODE_STRING Name,
|
|
IN SHORT ClientLanguage,
|
|
IN SHORT ClientSystemDefaultLanguage,
|
|
OUT PLSAPR_UNICODE_STRING *DisplayName,
|
|
OUT PWORD LanguageReturned
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is the LSA server RPC worker routine for the
|
|
LsaLookupPrivilegeDisplayName() API.
|
|
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - Handle from an LsaOpenPolicy() call. This handle
|
|
must be open for POLICY_LOOKUP_NAMES access.
|
|
|
|
Name - The programmatic privilege name to look up.
|
|
|
|
ClientLanguage - The prefered language to be returned.
|
|
|
|
ClientSystemDefaultLanguage - The alternate prefered language
|
|
to be returned.
|
|
|
|
DisplayName - Receives the privilege's displayable name.
|
|
|
|
LanguageReturned - The language actually returned.
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The privilege text was found and returned.
|
|
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
|
|
STATUS_NO_SUCH_PRIVILEGE - The specified privilege could not be
|
|
found.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
LSAP_DB_HANDLE Handle = (LSAP_DB_HANDLE) PolicyHandle;
|
|
LsarpReturnCheckSetup();
|
|
|
|
//
|
|
// make sure we know what RPC is doing to/for us
|
|
//
|
|
|
|
ASSERT( DisplayName != NULL );
|
|
ASSERT( (*DisplayName) == NULL );
|
|
ASSERT( LanguageReturned != NULL );
|
|
|
|
//
|
|
// Validate the input buffer
|
|
//
|
|
|
|
if ( !LsapValidateLsaUnicodeString( Name ) ) {
|
|
|
|
return( STATUS_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// make sure the caller has the appropriate access
|
|
//
|
|
|
|
Status = LsapDbReferenceObject(
|
|
PolicyHandle,
|
|
POLICY_LOOKUP_NAMES,
|
|
PolicyObject,
|
|
NullObject,
|
|
LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_NO_DS_OP_TRANSACTION
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// No need to hold onto the Policy object after this..
|
|
// We just needed it for access validation purposes.
|
|
//
|
|
|
|
Status = LsapDbDereferenceObject(
|
|
&PolicyHandle,
|
|
PolicyObject,
|
|
NullObject,
|
|
LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_NO_DS_OP_TRANSACTION,
|
|
(SECURITY_DB_DELTA_TYPE) 0,
|
|
Status
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
if (Name->Buffer == 0 || Name->Length == 0) {
|
|
return(STATUS_NO_SUCH_PRIVILEGE);
|
|
}
|
|
Status = LsapLookupPrivilegeDisplayName(
|
|
(PUNICODE_STRING)Name,
|
|
(WORD)ClientLanguage,
|
|
(WORD)ClientSystemDefaultLanguage,
|
|
(PUNICODE_STRING *)DisplayName,
|
|
LanguageReturned
|
|
);
|
|
}
|
|
|
|
LsarpReturnPrologue();
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// //
|
|
// Internal Routines //
|
|
// //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS
|
|
LsapLookupKnownPrivilegeNameQuickly(
|
|
IN PLUID Value,
|
|
OUT UNICODE_STRING *Name
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Lookup a privilege luid to find the corresponding privilege name.
|
|
|
|
Arguments:
|
|
|
|
Value - LUID to lookup
|
|
|
|
Name - pointer to privilege name
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
The name returned must NOT be freed or modified by the caller.
|
|
|
|
Notes:
|
|
The 'Quickly' in the name refers to an index based lookup.
|
|
This is highly dependent on the privilege values (ntseapi.h)
|
|
being consecutive integers. If there is a hole introduced in the
|
|
well known privilege values, then this function will need to be fixed.
|
|
|
|
|
|
--*/
|
|
{
|
|
ULONG LowPart = Value->LowPart;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
if ((Value->HighPart == 0) &&
|
|
(LowPart >= SE_MIN_WELL_KNOWN_PRIVILEGE) &&
|
|
(LowPart <= SE_MAX_WELL_KNOWN_PRIVILEGE))
|
|
{
|
|
*Name = LsapKnownPrivilege[LowPart-SE_MIN_WELL_KNOWN_PRIVILEGE].Name;
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_NO_SUCH_PRIVILEGE;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapLookupKnownPrivilegeName(
|
|
IN PLUID Value,
|
|
OUT PUNICODE_STRING *Name
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Look up the specified LUID and return the corresponding
|
|
privilege's programmatic name (if found).
|
|
|
|
FOR NOW, WE ONLY SUPPORT WELL-KNOWN MICROSOFT PRIVILEGES.
|
|
THESE ARE HARD-CODED HERE. IN THE FUTURE, WE MUST ALSO
|
|
SEARCH A LIST OF LOADED PRIVILEGES.
|
|
|
|
Arguments:
|
|
|
|
Value - Value to look up.
|
|
|
|
Name - Receives the corresponding name - allocated with
|
|
MIDL_user_allocate() and ready to return via an RPC stub.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - Succeeded.
|
|
|
|
STATUS_NO_MEMORY - Indicates there was not enough heap memory available
|
|
to produce the final TokenInformation structure.
|
|
|
|
STATUS_NO_SUCH_PRIVILEGE - The specified privilege could not be
|
|
found.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
UNICODE_STRING PrivName;
|
|
PUNICODE_STRING ReturnName;
|
|
NTSTATUS Status=STATUS_SUCCESS;
|
|
|
|
Status = LsapLookupKnownPrivilegeNameQuickly( Value, &PrivName );
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
|
|
ReturnName = MIDL_user_allocate( sizeof(UNICODE_STRING) );
|
|
if (ReturnName == NULL) {
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
|
|
*ReturnName = PrivName;
|
|
|
|
ReturnName->Buffer = MIDL_user_allocate( ReturnName->MaximumLength );
|
|
|
|
if (ReturnName->Buffer == NULL) {
|
|
MIDL_user_free( ReturnName );
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
|
|
RtlCopyUnicodeString( ReturnName, &PrivName );
|
|
|
|
(*Name) = ReturnName;
|
|
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsapLookupKnownPrivilegeValue(
|
|
PUNICODE_STRING Name,
|
|
PLUID Value
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Look up the specified name and return the corresponding
|
|
privilege's locally assigned value (if found).
|
|
|
|
FOR NOW, WE ONLY SUPPORT WELL-KNOWN MICROSOFT PRIVILEGES.
|
|
THESE ARE HARD-CODED HERE. IN THE FUTURE, WE MUST ALSO
|
|
SEARCH A LIST OF LOADED PRIVILEGES.
|
|
|
|
Arguments:
|
|
|
|
Name - The name to look up.
|
|
|
|
Value - Receives the corresponding locally assigned value.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - Succeeded.
|
|
|
|
STATUS_NO_SUCH_PRIVILEGE - The specified privilege could not be
|
|
found.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
BOOLEAN Found;
|
|
|
|
for ( i=0; i<LsapWellKnownPrivilegeCount; i++) {
|
|
|
|
Found = RtlEqualUnicodeString( Name, &LsapKnownPrivilege[i].Name, TRUE );
|
|
|
|
if (Found) {
|
|
|
|
(*Value) = LsapKnownPrivilege[i].LocalValue;
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
}
|
|
|
|
return(STATUS_NO_SUCH_PRIVILEGE);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapGetPrivilegeDisplayNameResourceId(
|
|
IN PUNICODE_STRING Name,
|
|
IN ULONG DllIndex,
|
|
OUT PULONG ResourceId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine maps a privilege programmatic name in a dll to
|
|
its display name resource id in the same dll.
|
|
Currently since we support only one dll, it simply ignores
|
|
DllIndex and uses the internal table to speed up things.
|
|
|
|
|
|
Arguments:
|
|
|
|
Name - The programmatic name of the privilege to look up.
|
|
E.g., "SeTakeOwnershipPrivilege".
|
|
|
|
DllIndex - The index of the privilege DLL to look in.
|
|
|
|
PrivilegeIndex - Receives the index of the privilege entry in this
|
|
resource file.
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - The pivilege has been successfully located.
|
|
|
|
STATUS_NO_SUCH_PRIVILEGE - The privilege could not be located.
|
|
|
|
--*/
|
|
|
|
|
|
|
|
{
|
|
UINT i;
|
|
|
|
for (i=0; i<LsapNumKnownPrivileges; i++)
|
|
{
|
|
if (CSTR_EQUAL ==
|
|
CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
|
|
SORT_DEFAULT),
|
|
NORM_IGNORECASE | SORT_STRINGSORT,
|
|
Name->Buffer, Name->Length / sizeof(WCHAR),
|
|
LsapKnownPrivilageNames[i], -1))
|
|
{
|
|
*ResourceId = i;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return STATUS_NO_SUCH_PRIVILEGE;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsapLookupPrivilegeDisplayName(
|
|
IN PUNICODE_STRING ProgrammaticName,
|
|
IN WORD ClientLanguage,
|
|
IN WORD ClientSystemDefaultLanguage,
|
|
OUT PUNICODE_STRING *DisplayName,
|
|
OUT PWORD LanguageReturned
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine looks through each of the privilege DLLs for the
|
|
specified privilege. If found, its displayable name is returned.
|
|
|
|
|
|
Arguments:
|
|
|
|
ProgrammaticName - The programmatic name of the privilege to look up.
|
|
E.g., "SeTakeOwnershipPrivilege".
|
|
|
|
ClientLanguage - The prefered language to be returned.
|
|
|
|
ClientSystemDefaultLanguage - The alternate prefered language
|
|
to be returned.
|
|
|
|
DisplayName - receives a pointer to a buffer containing the displayable
|
|
name associated with the privilege.
|
|
E.g., "Take ownership of files or other objects".
|
|
|
|
The UNICODE_STRING and the buffer pointed to by that structure
|
|
are individually allocated using MIDL_user_allocate() and must
|
|
be freed using MIDL_user_free().
|
|
|
|
LanguageReturned - The language actually returned.
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - The name have been successfully retrieved.
|
|
|
|
STATUS_NO_MEMORY - Not enough heap was available to return the
|
|
information.
|
|
|
|
STATUS_NO_SUCH_PRIVILEGE - The privilege could not be located
|
|
in any of the privilege DLLs.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_NO_SUCH_PRIVILEGE;
|
|
ULONG DllIndex, PrivilegeIndex;
|
|
|
|
for ( DllIndex=0; DllIndex<LsapPrivilegeDllCount; DllIndex++) {
|
|
|
|
Status = LsapGetPrivilegeDisplayNameResourceId(
|
|
(PUNICODE_STRING)ProgrammaticName,
|
|
DllIndex,
|
|
&PrivilegeIndex
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
Status = LsapGetPrivilegeDisplayName( DllIndex,
|
|
PrivilegeIndex,
|
|
ClientLanguage,
|
|
ClientSystemDefaultLanguage,
|
|
DisplayName,
|
|
LanguageReturned
|
|
);
|
|
return(Status);
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapFindStringResource(
|
|
IN HMODULE hModule,
|
|
IN WORD wResourceId,
|
|
IN WORD wLangId,
|
|
OUT LPCWSTR* ppString,
|
|
OUT PUINT pcchString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Locate the specified string resource for the given language
|
|
in the specified module.
|
|
|
|
Arguments:
|
|
|
|
hModule - handle of module to look into
|
|
|
|
wResourceId - resource ID
|
|
|
|
wLangId - language ID
|
|
|
|
ppString - string returned.
|
|
|
|
pcchString - number of characters in the string
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
Notes:
|
|
|
|
This code is taken directly from LoadStringOrError in
|
|
/nt/windows/Core/ntuser/client/rtlres.c and massaged a bit to make
|
|
it look like other LSA code.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
DWORD dwError = NO_ERROR;
|
|
HRSRC hResInfo;
|
|
HANDLE hStringSeg;
|
|
LPTSTR lpsz;
|
|
UINT cch;
|
|
|
|
//
|
|
// each block has 16 strings. find the block that has
|
|
// the string we want.
|
|
//
|
|
WORD wStringBlock = (((USHORT)wResourceId >> 4) + 1);
|
|
|
|
if (ppString == NULL)
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
*ppString = NULL;
|
|
|
|
cch = 0;
|
|
|
|
//
|
|
// String Tables are broken up into 16 string segments. Find the segment
|
|
// containing the string we are interested in.
|
|
//
|
|
|
|
if (hResInfo = FindResourceEx(
|
|
hModule,
|
|
RT_STRING,
|
|
(LPWSTR) wStringBlock,
|
|
wLangId))
|
|
{
|
|
//
|
|
// Load that segment.
|
|
//
|
|
|
|
hStringSeg = LoadResource(hModule, hResInfo);
|
|
|
|
//
|
|
// Lock the resource.
|
|
//
|
|
|
|
if (lpsz = (LPTSTR) LockResource(hStringSeg))
|
|
{
|
|
//
|
|
// Move past the other strings in this segment.
|
|
// (16 strings in a segment -> & 0x0F)
|
|
//
|
|
|
|
wResourceId &= 0x0F;
|
|
while (TRUE)
|
|
{
|
|
cch = *((WCHAR *)lpsz++); // PASCAL like string count
|
|
// first UTCHAR is count if TCHARs
|
|
if (wResourceId-- == 0) break;
|
|
lpsz += cch; // Step to start of next string
|
|
}
|
|
|
|
*(LPTSTR *)ppString = lpsz;
|
|
*pcchString = cch;
|
|
|
|
UnlockResource(hStringSeg);
|
|
}
|
|
else
|
|
{
|
|
goto GetError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
goto GetError;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
return Status;
|
|
|
|
GetError:
|
|
dwError = GetLastError();
|
|
|
|
switch (dwError)
|
|
{
|
|
case ERROR_RESOURCE_LANG_NOT_FOUND:
|
|
Status = STATUS_RESOURCE_LANG_NOT_FOUND;
|
|
break;
|
|
|
|
default:
|
|
Status = LsapWinerrorToNtStatus( dwError );
|
|
break;
|
|
}
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsapGetPrivilegeDisplayName(
|
|
IN ULONG DllIndex,
|
|
IN ULONG PrivilegeIndex,
|
|
IN WORD ClientLanguage,
|
|
IN WORD ClientSystemDefaultLanguage,
|
|
OUT PUNICODE_STRING *DisplayName,
|
|
OUT PWORD LanguageReturned
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns a copy of the specified privilege's display name.
|
|
|
|
The copy of the name is returned in two buffers allocated using
|
|
MIDL_user_allocate(). This allows the information to be returned
|
|
via an RPC service so that RPC generated stubs will correctly free
|
|
the buffers.
|
|
|
|
Every attempt is made to retrieve a language that the client prefers
|
|
(first the client, then the client's system). Failing this, this
|
|
routine may return another language (such as the server's default
|
|
language).
|
|
|
|
|
|
Arguments:
|
|
|
|
DllIndex - The index of the privilege DLL to use.
|
|
|
|
PrivilegeIndex - The index of the privilege entry in the DLL whose
|
|
display name is to be returned.
|
|
|
|
ClientLanguage - The language to return if possible.
|
|
|
|
ClientSystemDefaultLanguage - If ClientLanguage couldn't be found, then
|
|
return this language if possible.
|
|
|
|
DisplayName - receives a pointer to a buffer containing the displayable
|
|
name associated with the privilege.
|
|
|
|
The UNICODE_STRING and the buffer pointed to by that structure
|
|
are individually allocated using MIDL_user_allocate() and must
|
|
be freed using MIDL_user_free().
|
|
|
|
LanguageReturned - Receives the language actually retrieved.
|
|
If neither ClientLanguage nor ClientSystemDefaultLanguage could be
|
|
found, then this value may contain yet another value.
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - The display name has been successfully returned.
|
|
|
|
STATUS_NO_MEMORY - Not enough heap was available to return the
|
|
information.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status=STATUS_NO_SUCH_PRIVILEGE;
|
|
WORD Languages[] =
|
|
{
|
|
ClientLanguage,
|
|
MAKELANGID( PRIMARYLANGID(ClientLanguage), SUBLANG_NEUTRAL),
|
|
ClientSystemDefaultLanguage,
|
|
MAKELANGID( PRIMARYLANGID(ClientSystemDefaultLanguage), SUBLANG_NEUTRAL),
|
|
MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT )
|
|
};
|
|
UINT NumLanguages = sizeof(Languages) / sizeof(Languages[0]);
|
|
UINT i;
|
|
PUNICODE_STRING ReturnedName = NULL;
|
|
PCWSTR pszPrivilegeName=NULL;
|
|
DWORD dwPrivilegeNameLen=0;
|
|
|
|
for (i = 0; i < NumLanguages; i++)
|
|
{
|
|
Status = LsapFindStringResource(
|
|
LsapPrivilegeDlls[ DllIndex ].DllHandle,
|
|
(WORD) PrivilegeIndex,
|
|
Languages[i],
|
|
&pszPrivilegeName,
|
|
&dwPrivilegeNameLen
|
|
);
|
|
|
|
if ( NT_SUCCESS( Status ))
|
|
{
|
|
DsysAssertMsg( pszPrivilegeName && dwPrivilegeNameLen,
|
|
"LsapGetPrivilegeDisplayName" );
|
|
|
|
if (ReturnedName = MIDL_user_allocate(sizeof(UNICODE_STRING)))
|
|
{
|
|
ReturnedName->Length = (USHORT) dwPrivilegeNameLen * sizeof(WCHAR);
|
|
ReturnedName->MaximumLength = ReturnedName->Length + sizeof(WCHAR);
|
|
|
|
if (ReturnedName->Buffer =
|
|
MIDL_user_allocate(ReturnedName->MaximumLength))
|
|
{
|
|
RtlCopyMemory(ReturnedName->Buffer, pszPrivilegeName,
|
|
ReturnedName->Length);
|
|
ReturnedName->Buffer[dwPrivilegeNameLen] = 0;
|
|
*LanguageReturned = Languages[i];
|
|
*DisplayName = ReturnedName;
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
MIDL_user_free( ReturnedName );
|
|
Status = STATUS_NO_MEMORY;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_NO_MEMORY;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapDbInitializePrivilegeObject( VOID )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function performs initialization functions related to
|
|
the LSA privilege object.
|
|
|
|
This includes:
|
|
|
|
Initializing some variables.
|
|
|
|
Loading DLLs containing displayable privilege names.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - The names have been successfully retrieved.
|
|
|
|
STATUS_NO_MEMORY - Not enough memory was available to initialize.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS
|
|
Status,
|
|
NtStatus;
|
|
|
|
ULONG
|
|
i;
|
|
|
|
UNICODE_STRING Temp ;
|
|
|
|
Status = LsapOpenPrivilegeDlls( );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
DbgPrint("\n");
|
|
DbgPrint(" LSASS: Failed loading privilege display name DLLs.\n");
|
|
DbgPrint(" This is not fatal, but may cause some peculiarities\n");
|
|
DbgPrint(" in User Interfaces that display privileges.\n\n");
|
|
#endif //DBG
|
|
return(Status);
|
|
}
|
|
|
|
LsapWellKnownPrivilegeMaxLen = 0;
|
|
|
|
//
|
|
// Now set up our internal well-known privilege LUID to programmatic name
|
|
// mapping.
|
|
//
|
|
|
|
for (i=0; i<LsapNumKnownPrivileges; i++)
|
|
{
|
|
LsapKnownPrivilege[i].LocalValue =
|
|
RtlConvertLongToLuid(i + SE_MIN_WELL_KNOWN_PRIVILEGE);
|
|
RtlInitUnicodeString(&LsapKnownPrivilege[i].Name,
|
|
LsapKnownPrivilageNames[i]);
|
|
//
|
|
// find the length of the longest well known privilege
|
|
//
|
|
if (LsapWellKnownPrivilegeMaxLen < LsapKnownPrivilege[i].Name.Length)
|
|
{
|
|
LsapWellKnownPrivilegeMaxLen = LsapKnownPrivilege[i].Name.Length;
|
|
}
|
|
}
|
|
|
|
LsapWellKnownPrivilegeCount = i;
|
|
ASSERT( i == (SE_MAX_WELL_KNOWN_PRIVILEGE - SE_MIN_WELL_KNOWN_PRIVILEGE +1));
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapOpenPrivilegeDlls( )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function opens all the privilege DLLs that it can.
|
|
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - The names have been successfully retrieved.
|
|
|
|
STATUS_NO_MEMORY - Not enough heap was available to return the
|
|
information.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG PotentialDlls, FoundDlls, i;
|
|
PUNICODE_STRING DllNames;
|
|
|
|
//
|
|
// Get the names of the DLLs out of the registry
|
|
//
|
|
|
|
Status = LsapGetPrivilegeDllNames( &DllNames, &PotentialDlls );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// Allocate enough memory to hold handles to all potential DLLs.
|
|
//
|
|
|
|
LsapPrivilegeDlls = RtlAllocateHeap(
|
|
RtlProcessHeap(), 0,
|
|
PotentialDlls*sizeof(LSAP_DLL_DESCRIPTOR)
|
|
);
|
|
|
|
if (LsapPrivilegeDlls == NULL) {
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
|
|
FoundDlls = 0;
|
|
for ( i=0; i<PotentialDlls; i++) {
|
|
Status = LdrLoadDll(
|
|
NULL,
|
|
NULL,
|
|
&DllNames[i],
|
|
&LsapPrivilegeDlls[FoundDlls].DllHandle
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
FoundDlls++;
|
|
}
|
|
}
|
|
|
|
LsapPrivilegeDllCount = FoundDlls;
|
|
|
|
#if DBG
|
|
if (FoundDlls == 0) {
|
|
DbgPrint("\n");
|
|
DbgPrint("LSASS: Zero privilege DLLs loaded. We expected at\n");
|
|
DbgPrint(" least msprivs.dll to be loaded. Privilege\n");
|
|
DbgPrint(" names will not be displayed at UI properly.\n\n");
|
|
|
|
}
|
|
#endif //DBG
|
|
|
|
//
|
|
//
|
|
// !!!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!
|
|
//
|
|
// Before supporting user loadable privilege DLLs, we must add
|
|
// code here to validate the structure of the loaded DLL. This
|
|
// is necessary to prevent an invalid privilege DLL structure
|
|
// from causing us to crash.
|
|
//
|
|
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
//
|
|
|
|
//
|
|
// This routine validates the structure of each loaded DLL.
|
|
// Any found to be invalid will be logged and discarded.
|
|
//
|
|
|
|
//Status = LsapValidatePrivilegeDlls();
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapGetPrivilegeDllNames(
|
|
OUT PUNICODE_STRING *DllNames,
|
|
OUT PULONG DllCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function obtains the names of DLLs containing privilege
|
|
definitions from the registry.
|
|
|
|
|
|
Arguments:
|
|
|
|
DllNames - Receives a pointer to an array of UNICODE_STRINGs
|
|
This buffer must be freed using LsapFreePrivilegeDllNames.
|
|
|
|
DllCount - Receives the number of DLL names returned.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - The names have been successfully retrieved.
|
|
|
|
STATUS_NO_MEMORY - Not enough heap was available to return the
|
|
information.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// For the time being, just hard-code our one, known privilege DLL
|
|
// name as a return value.
|
|
|
|
(*DllCount) = 1;
|
|
|
|
MsDllName.Length = 14;
|
|
MsDllName.MaximumLength = 14;
|
|
MsDllName.Buffer = &MsDllNameString[0];
|
|
|
|
(*DllNames) = &MsDllName;
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapBuildPrivilegeAuditString(
|
|
IN PPRIVILEGE_SET PrivilegeSet,
|
|
OUT PUNICODE_STRING ResultantString,
|
|
OUT PBOOLEAN FreeWhenDone
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
This function builds a unicode string representing the specified
|
|
privileges. The privilege strings returned are program names.
|
|
These are not as human-friendly as the display names, but I don't
|
|
think we stand a chance of actually showing several display names
|
|
in an audit viewer.
|
|
|
|
if no privileges are present in the privilege set, then a string
|
|
containing a dash is returned.
|
|
|
|
|
|
Arguments:
|
|
|
|
PrivilegeSet - points to the privilege set to be converted to string
|
|
format.
|
|
|
|
ResultantString - Points to the unicode string header. The body of this
|
|
unicode string will be set to point to the resultant output value
|
|
if successful. Otherwise, the Buffer field of this parameter
|
|
will be set to NULL.
|
|
|
|
FreeWhenDone - If TRUE, indicates that the body of ResultantString
|
|
must be freed to process heap when no longer needed.
|
|
|
|
|
|
|
|
Return Values:
|
|
|
|
STATUS_NO_MEMORY - indicates memory could not be allocated
|
|
for the string body.
|
|
|
|
All other Result Codes are generated by called routines.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
USHORT LengthNeeded;
|
|
ULONG j;
|
|
ULONG i;
|
|
|
|
PLUID Privilege;
|
|
|
|
UNICODE_STRING LineFormatting;
|
|
UNICODE_STRING QuestionMark;
|
|
|
|
UNICODE_STRING PrivName;
|
|
|
|
PWSTR NextName;
|
|
|
|
//
|
|
// make sure that the max length has been calculated
|
|
// (SE_INC_BASE_PRIORITY_NAME currently has the longest name
|
|
// therefore make sure that the length is at least that much)
|
|
//
|
|
DsysAssert(LsapWellKnownPrivilegeMaxLen >=
|
|
(sizeof(SE_INC_BASE_PRIORITY_NAME) - sizeof(WCHAR)));
|
|
|
|
if (PrivilegeSet->PrivilegeCount == 0) {
|
|
|
|
//
|
|
// No privileges. Return a dash
|
|
//
|
|
Status = LsapAdtBuildDashString( ResultantString, FreeWhenDone );
|
|
return(Status);
|
|
|
|
}
|
|
|
|
if (!IsValidPrivilegeCount(PrivilegeSet->PrivilegeCount)) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
RtlInitUnicodeString( &LineFormatting, L"\r\n\t\t\t");
|
|
RtlInitUnicodeString( &QuestionMark, L"?");
|
|
|
|
//
|
|
// for better performance, we calculate the total length required
|
|
// to store privilege names based on the longest privilege,
|
|
// instead of going over the privilege-set twice (once to calcualte
|
|
// length and one more time to actually build the string)
|
|
//
|
|
LengthNeeded = (USHORT) (PrivilegeSet->PrivilegeCount *
|
|
( LsapWellKnownPrivilegeMaxLen + LineFormatting.Length ));
|
|
|
|
//
|
|
// Subtract off the length of the last line-formatting.
|
|
// It isn't needed for the last line.
|
|
// BUT! Add in enough for a null termination.
|
|
//
|
|
LengthNeeded = LengthNeeded - LineFormatting.Length + sizeof( WCHAR );
|
|
|
|
//
|
|
// We now have the length we need.
|
|
// Allocate the buffer and go through the list copying names.
|
|
//
|
|
ResultantString->Buffer = RtlAllocateHeap( RtlProcessHeap(), 0, (ULONG)LengthNeeded);
|
|
if (ResultantString->Buffer == NULL) {
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
|
|
//ResultantString->Length = LengthNeeded - (USHORT)sizeof(UNICODE_NULL);
|
|
ResultantString->MaximumLength = LengthNeeded;
|
|
|
|
NextName = ResultantString->Buffer;
|
|
|
|
for (j=0; j<PrivilegeSet->PrivilegeCount; j++) {
|
|
|
|
Privilege = &(PrivilegeSet->Privilege[j].Luid);
|
|
|
|
Status = LsapLookupKnownPrivilegeNameQuickly( Privilege, &PrivName );
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
|
|
//
|
|
// Copy the privilege name if lookup succedded
|
|
//
|
|
RtlCopyMemory( NextName, PrivName.Buffer, PrivName.Length );
|
|
NextName = (PWSTR)((PCHAR)NextName + PrivName.Length);
|
|
|
|
} else {
|
|
|
|
//
|
|
// else copy a '?'
|
|
//
|
|
RtlCopyMemory( NextName, QuestionMark.Buffer, QuestionMark.Length );
|
|
NextName = (PWSTR)((PCHAR)NextName + QuestionMark.Length);
|
|
}
|
|
|
|
//
|
|
// Copy the line formatting string, unless this is the last priv.
|
|
//
|
|
if (j<PrivilegeSet->PrivilegeCount-1) {
|
|
RtlCopyMemory( NextName,
|
|
LineFormatting.Buffer,
|
|
LineFormatting.Length
|
|
);
|
|
NextName = (PWSTR)((PCHAR)NextName + LineFormatting.Length);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add a null to the end
|
|
//
|
|
|
|
(*NextName) = (UNICODE_NULL);
|
|
ResultantString->Length = (USHORT) (((PBYTE) NextName) - ((PBYTE) ResultantString->Buffer));
|
|
|
|
DsysAssertMsg( ResultantString->Length <= ResultantString->MaximumLength,
|
|
"LsapBuildPrivilegeAuditString" );
|
|
|
|
(*FreeWhenDone) = TRUE;
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|