mirror of https://github.com/tongzx/nt5src
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.
5478 lines
154 KiB
5478 lines
154 KiB
|
|
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
rpcapi.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the routines for the LSA API that use RPC. The
|
|
routines in this module are merely wrappers that work as follows:
|
|
|
|
o Client program calls LsaFoo in this module
|
|
o LsaFoo calls RPC client stub interface routine LsapFoo with
|
|
similar parameters. Some parameters are translated from types
|
|
(e.g structures containing PVOIDs or certain kinds of variable length
|
|
parameters such as pointers to SID's) that are not specifiable on an
|
|
RPC interface, to specifiable form.
|
|
o RPC client stub LsapFoo calls interface specific marshalling routines
|
|
and RPC runtime to marshal parameters into a buffer and send them over
|
|
to the server side of the LSA.
|
|
o Server side calls RPC runtime and interface specific unmarshalling
|
|
routines to unmarshal parameters.
|
|
o Server side calls worker LsapFoo to perform API function.
|
|
o Server side marshals response/output parameters and communicates these
|
|
back to client stub LsapFoo
|
|
o LsapFoo exits back to LsaFoo which returns to client program.
|
|
|
|
Author:
|
|
|
|
Scott Birrell (ScottBi) April 24, 1991
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "lsaclip.h"
|
|
#include <align.h>
|
|
#include <rpcasync.h>
|
|
|
|
//
|
|
// The following limit on the maximum number of Sids or Names is tentative,
|
|
// so it is not being published.
|
|
//
|
|
|
|
#define LSAP_DB_TRIAL_MAXIMUM_SID_COUNT ((ULONG) 0x00005000L)
|
|
#define LSAP_DB_TRIAL_MAXIMUM_NAME_COUNT ((ULONG) 0x00005000L)
|
|
|
|
|
|
//
|
|
// Functions private to this module
|
|
//
|
|
|
|
NTSTATUS
|
|
LsapApiReturnResult(
|
|
IN ULONG ExceptionCode
|
|
);
|
|
|
|
BOOLEAN
|
|
LsapNeutralizeNt4Emulation()
|
|
{
|
|
BOOLEAN Result = FALSE;
|
|
NTSTATUS Status;
|
|
HKEY hkey;
|
|
DWORD Type;
|
|
DWORD Value;
|
|
DWORD Size = sizeof( Value );
|
|
static DWORD AmIDC = 0xFFFFFFFF;
|
|
ULONG i;
|
|
CHAR * Paths[] = {
|
|
"SYSTEM\\CurrentControlSet\\Services\\Netlogon\\Parameters\\GpParameters",
|
|
"SYSTEM\\CurrentControlSet\\Services\\Netlogon\\Parameters"
|
|
};
|
|
|
|
//
|
|
// NT4 emulation is always disabled on domain controllers
|
|
//
|
|
|
|
if ( AmIDC == 0xFFFFFFFF ) {
|
|
|
|
NT_PRODUCT_TYPE ProductType = NtProductWinNt;
|
|
|
|
if ( TRUE == RtlGetNtProductType( &ProductType )) {
|
|
|
|
if ( ProductType == NtProductLanManNt ) {
|
|
|
|
AmIDC = TRUE;
|
|
|
|
} else {
|
|
|
|
AmIDC = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( AmIDC == TRUE ) {
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// This is not a DC; must go to the registry for the special "neutralize" value
|
|
// which could be either under NetLogon or NetLogon/GroupPolicy parameters key
|
|
//
|
|
|
|
for ( i = 0; i < sizeof( Paths ) / sizeof( Paths[0] ); i++ ) {
|
|
|
|
if ( ERROR_SUCCESS != RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
Paths[i],
|
|
0,
|
|
KEY_READ,
|
|
&hkey )) {
|
|
|
|
continue;
|
|
}
|
|
|
|
if ( ERROR_SUCCESS != RegQueryValueEx(
|
|
hkey,
|
|
"NeutralizeNt4Emulator",
|
|
NULL,
|
|
&Type,
|
|
(LPBYTE)&Value,
|
|
&Size ) ||
|
|
Type != REG_DWORD ||
|
|
Size != sizeof( DWORD )) {
|
|
|
|
RegCloseKey( hkey );
|
|
continue;
|
|
|
|
} else {
|
|
|
|
RegCloseKey( hkey );
|
|
Result = ( Value != 0 );
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Local Security Policy Administration API function prototypes //
|
|
// //
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS
|
|
LsaOpenPolicy(
|
|
IN PUNICODE_STRING SystemName OPTIONAL,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN OUT PLSA_HANDLE PolicyHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
To administer the Local Security Policy of a local or remote system,
|
|
this API must be called to establish a session with that system's
|
|
Local Security Authority (LSA) subsystem. This API connects to
|
|
the LSA of the target system and opens the object representing
|
|
the target system's Local Security Policy database. A handle to
|
|
the object is returned. This handle must be used on all subsequent API
|
|
calls to administer the Local Security Policy information for the
|
|
target system.
|
|
|
|
Arguments:
|
|
|
|
SystemName - Name of the target system to be administered.
|
|
Administration of the local system is assumed if NULL is specified.
|
|
|
|
ObjectAttributes - Pointer to the set of attributes to use for this
|
|
connection. The security Quality Of Service information is used and
|
|
normally should provide Security Identification level of
|
|
impersonation. Some operations, however, require Security
|
|
Impersonation level of impersonation.
|
|
|
|
DesiredAccess - This is an access mask indicating accesses being
|
|
requested for the LSA Subsystem's LSA Database. These access types
|
|
are reconciled with the Discretionary Access Control List of the
|
|
target LsaDatabase object to determine whether the
|
|
accesses will be granted or denied.
|
|
|
|
PolicyHandle - Receives a handle to be used in future requests to
|
|
access the Local Security Policy of the target system. This handle
|
|
represents both the handle to the LsaDatabase object and
|
|
the RPC Context Handle for the connection to the target LSA
|
|
susbsystem.
|
|
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have access to the target
|
|
system's LSA Database, or does not have other desired accesses.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PLSAPR_SERVER_NAME ServerName = NULL;
|
|
USHORT NullTerminatedServerNameLength;
|
|
LSA_HANDLE LocalHandle = NULL ;
|
|
|
|
RpcTryExcept {
|
|
|
|
//
|
|
// Get the Server Name as a Unicode String buffer. Set it to
|
|
// NULL (i.e. local machine) if a zero length or NULL Unicode String
|
|
// structure us passed. If a non NULL server name is given, we must
|
|
// ensure that it is terminated with a NULL wide character. Allocate
|
|
// a buffer that is one wide character longer than the server name
|
|
// buffer, copy the server name to that buffer and append a trailing
|
|
// NULL wide character.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(SystemName) &&
|
|
(SystemName->Buffer != NULL) &&
|
|
(SystemName->Length > 0)) {
|
|
|
|
NullTerminatedServerNameLength = SystemName->Length + (USHORT) sizeof (WCHAR);
|
|
|
|
ServerName = MIDL_user_allocate( NullTerminatedServerNameLength );
|
|
|
|
if (ServerName != NULL) {
|
|
|
|
RtlZeroMemory( ServerName, NullTerminatedServerNameLength );
|
|
|
|
RtlMoveMemory(
|
|
ServerName,
|
|
SystemName->Buffer,
|
|
SystemName->Length
|
|
);
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
*PolicyHandle = NULL;
|
|
|
|
ObjectAttributes->RootDirectory = NULL;
|
|
|
|
Status = LsarOpenPolicy2(
|
|
ServerName,
|
|
(PLSAPR_OBJECT_ATTRIBUTES) ObjectAttributes,
|
|
DesiredAccess,
|
|
(PLSAPR_HANDLE)&LocalHandle
|
|
);
|
|
}
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
//
|
|
// If the open failed because the new API doesn't exist, try the
|
|
// old one.
|
|
//
|
|
|
|
if ((Status == RPC_NT_UNKNOWN_IF) ||
|
|
(Status == RPC_NT_PROCNUM_OUT_OF_RANGE)) {
|
|
|
|
RpcTryExcept {
|
|
ASSERT(*PolicyHandle == NULL);
|
|
ASSERT(ObjectAttributes->RootDirectory == NULL);
|
|
|
|
Status = LsarOpenPolicy(
|
|
ServerName,
|
|
(PLSAPR_OBJECT_ATTRIBUTES) ObjectAttributes,
|
|
DesiredAccess,
|
|
(PLSAPR_HANDLE)&LocalHandle
|
|
);
|
|
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
}
|
|
|
|
//
|
|
// If necessary, free the NULL-terminated server name buffer.
|
|
//
|
|
|
|
if (ServerName != NULL) {
|
|
|
|
MIDL_user_free( ServerName );
|
|
}
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
*PolicyHandle = LocalHandle;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaOpenPolicySce(
|
|
IN PUNICODE_STRING SystemName OPTIONAL,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN OUT PLSA_HANDLE PolicyHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Essentially the same as LsaOpenPolicy, except used only by SCE
|
|
to obtain a special "synchronized" policy handle that would serialize
|
|
access to policy operations.
|
|
|
|
Arguments:
|
|
|
|
Same as LsaOpenPolicy
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have access to the target
|
|
system's LSA Database, or does not have other desired accesses.
|
|
|
|
STATUS_PRIVILEGE_NOT_HELD - Caller must come in with TCB privilege.
|
|
|
|
STATUS_TIMEOUT - Timed out waiting on SCE to send pending changes
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PLSAPR_SERVER_NAME ServerName = NULL;
|
|
USHORT NullTerminatedServerNameLength;
|
|
LSA_HANDLE LocalHandle = NULL ;
|
|
|
|
RpcTryExcept {
|
|
|
|
//
|
|
// Get the Server Name as a Unicode String buffer. Set it to
|
|
// NULL (i.e. local machine) if a zero length or NULL Unicode String
|
|
// structure us passed. If a non NULL server name is given, we must
|
|
// ensure that it is terminated with a NULL wide character. Allocate
|
|
// a buffer that is one wide character longer than the server name
|
|
// buffer, copy the server name to that buffer and append a trailing
|
|
// NULL wide character.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(SystemName) &&
|
|
(SystemName->Buffer != NULL) &&
|
|
(SystemName->Length > 0)) {
|
|
|
|
NullTerminatedServerNameLength = SystemName->Length + (USHORT) sizeof (WCHAR);
|
|
|
|
ServerName = MIDL_user_allocate( NullTerminatedServerNameLength );
|
|
|
|
if (ServerName != NULL) {
|
|
|
|
RtlZeroMemory( ServerName, NullTerminatedServerNameLength );
|
|
|
|
RtlMoveMemory(
|
|
ServerName,
|
|
SystemName->Buffer,
|
|
SystemName->Length
|
|
);
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
*PolicyHandle = NULL;
|
|
|
|
ObjectAttributes->RootDirectory = NULL;
|
|
|
|
Status = LsarOpenPolicySce(
|
|
ServerName,
|
|
(PLSAPR_OBJECT_ATTRIBUTES) ObjectAttributes,
|
|
DesiredAccess,
|
|
(PLSAPR_HANDLE)&LocalHandle
|
|
);
|
|
}
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
//
|
|
// If necessary, free the NULL-terminated server name buffer.
|
|
//
|
|
|
|
if (ServerName != NULL) {
|
|
|
|
MIDL_user_free( ServerName );
|
|
}
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
*PolicyHandle = LocalHandle;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaQueryInformationPolicy(
|
|
IN LSA_HANDLE PolicyHandle,
|
|
IN POLICY_INFORMATION_CLASS InformationClass,
|
|
OUT PVOID *Buffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaQueryInformationPolicy API obtains information from the Policy
|
|
object. The caller must have access appropriate to the information
|
|
being requested (see InformationClass parameter).
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - Handle from an LsaOpenPolicy call.
|
|
|
|
InformationClass - Specifies the information to be returned. The
|
|
Information Classes and accesses required are as follows:
|
|
|
|
Information Class Required Access Type
|
|
|
|
PolicyAuditLogInformation POLICY_VIEW_AUDIT_INFORMATION
|
|
PolicyAuditEventsInformation POLICY_VIEW_AUDIT_INFORMATION
|
|
PolicyPrimaryDomainInformation POLICY_VIEW_LOCAL_INFORMATION
|
|
PolicyAccountDomainInformation POLICY_VIEW_LOCAL_INFORMATION
|
|
PolicyPdAccountInformation POLICY_GET_PRIVATE_INFORMATION
|
|
PolicyLsaServerRoleInformation POLICY_VIEW_LOCAL_INFORMATION
|
|
PolicyReplicaSourceInformation POLICY_VIEW_LOCAL_INFORMATION
|
|
PolicyDefaultQuotaInformation POLICY_VIEW_LOCAL_INFORMATION
|
|
|
|
Buffer - receives a pointer to the buffer returned comtaining the
|
|
requested information. This buffer is allocated by this service
|
|
and must be freed when no longer needed by passing the returned
|
|
value to LsaFreeMemory().
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|
access to complete the operation.
|
|
|
|
Others TBS
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PLSAPR_POLICY_INFORMATION PolicyInformation;
|
|
|
|
if ( InformationClass == PolicyDnsDomainInformationInt ) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Retry:
|
|
|
|
PolicyInformation = NULL;
|
|
|
|
RpcTryExcept {
|
|
|
|
//
|
|
// Call the Client Stub for LsaQueryInformationPolicy.
|
|
//
|
|
|
|
switch (InformationClass)
|
|
{
|
|
case PolicyDnsDomainInformation:
|
|
case PolicyDnsDomainInformationInt:
|
|
Status = LsarQueryInformationPolicy2(
|
|
(LSAPR_HANDLE) PolicyHandle,
|
|
InformationClass,
|
|
&PolicyInformation
|
|
);
|
|
break;
|
|
|
|
default:
|
|
Status = LsarQueryInformationPolicy(
|
|
(LSAPR_HANDLE) PolicyHandle,
|
|
InformationClass,
|
|
&PolicyInformation
|
|
);
|
|
}
|
|
|
|
//
|
|
// Return pointer to Policy Information for the given class, or NULL.
|
|
//
|
|
|
|
*Buffer = PolicyInformation;
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
//
|
|
// If memory was allocated for the returned Policy Information,
|
|
// free it.
|
|
//
|
|
|
|
if (PolicyInformation != NULL) {
|
|
|
|
MIDL_user_free(PolicyInformation);
|
|
}
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
//
|
|
// If we suspect that the call failed due to NT4 emulation by the server,
|
|
// and we're configured to neutralize emulation, try the call again, neutralizing
|
|
//
|
|
|
|
if ( Status == RPC_NT_PROCNUM_OUT_OF_RANGE &&
|
|
InformationClass == PolicyDnsDomainInformation &&
|
|
LsapNeutralizeNt4Emulation()) {
|
|
|
|
InformationClass = PolicyDnsDomainInformationInt;
|
|
goto Retry;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaSetInformationPolicy(
|
|
IN LSA_HANDLE PolicyHandle,
|
|
IN POLICY_INFORMATION_CLASS InformationClass,
|
|
IN PVOID Buffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaSetInformationPolicy API modifies information in the Policy Object.
|
|
The caller must have access appropriate to the information to be changed
|
|
in the Policy Object, see the InformationClass parameter.
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - Handle from an LsaOpenPolicy call.
|
|
|
|
InformationClass - Specifies the type of information being changed.
|
|
The information types and accesses required to change them are as
|
|
follows:
|
|
|
|
PolicyAuditLogInformation POLICY_AUDIT_LOG_ADMIN
|
|
PolicyAuditEventsInformation POLICY_SET_AUDIT_REQUIREMENTS
|
|
PolicyPrimaryDomainInformation POLICY_TRUST_ADMIN
|
|
PolicyAccountDomainInformation POLICY_TRUST_ADMIN
|
|
PolicyPdAccountInformation Not settable by this API
|
|
PolicyLsaServerRoleInformation POLICY_SERVER_ADMIN
|
|
PolicyReplicaSourceInformation POLICY_SERVER_ADMIN
|
|
PolicyDefaultQuotaInformation POLICY_SET_DEFAULT_QUOTA_LIMITS
|
|
PolicyDnsDomainInformation POLICY_DNS_DOMAIN_INFO
|
|
PolicyDnsDomainInformationInt POLICY_DNS_DOMAIN_INFO
|
|
|
|
Buffer - Points to a structure containing the information appropriate
|
|
to the information type specified by the InformationClass parameter.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
Others TBS
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
if ( InformationClass == PolicyDnsDomainInformationInt ) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Retry:
|
|
|
|
RpcTryExcept {
|
|
|
|
//
|
|
// Call the Client Stub for LsaSetInformationPolicy.
|
|
//
|
|
|
|
switch (InformationClass)
|
|
{
|
|
case PolicyDnsDomainInformation:
|
|
case PolicyDnsDomainInformationInt:
|
|
Status = LsarSetInformationPolicy2(
|
|
(LSAPR_HANDLE) PolicyHandle,
|
|
InformationClass,
|
|
(PLSAPR_POLICY_INFORMATION) Buffer
|
|
);
|
|
break;
|
|
|
|
default:
|
|
Status = LsarSetInformationPolicy(
|
|
(LSAPR_HANDLE) PolicyHandle,
|
|
InformationClass,
|
|
(PLSAPR_POLICY_INFORMATION) Buffer
|
|
);
|
|
}
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
//
|
|
// If we suspect that the call failed due to NT4 emulation by the server,
|
|
// and we're configured to neutralize emulation, try the call again, neutralizing
|
|
//
|
|
|
|
if ( Status == RPC_NT_PROCNUM_OUT_OF_RANGE &&
|
|
InformationClass == PolicyDnsDomainInformation &&
|
|
LsapNeutralizeNt4Emulation()) {
|
|
|
|
InformationClass = PolicyDnsDomainInformationInt;
|
|
goto Retry;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaClearAuditLog(
|
|
IN LSA_HANDLE PolicyHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function clears the Audit Log. Caller must have POLICY_AUDIT_LOG_ADMIN
|
|
access to the Policy Object to perform this operation.
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - handle from an LsaOpenPolicy call.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code.
|
|
|
|
STATUS_SUCCESS - The call completed successfully.
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the required access
|
|
to perform the operation.
|
|
|
|
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.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
RpcTryExcept {
|
|
|
|
//
|
|
// Call the Client Stub for LsaClearAuditLog.
|
|
//
|
|
|
|
Status = LsarClearAuditLog(
|
|
(LSAPR_HANDLE) PolicyHandle
|
|
);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsaLookupPrivilegeValue(
|
|
IN LSA_HANDLE PolicyHandle,
|
|
IN PUNICODE_STRING Name,
|
|
OUT PLUID Value
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function retrieves the value used on the target system
|
|
to locally represent the specified privilege. The privilege
|
|
is specified by programmatic name.
|
|
|
|
|
|
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;
|
|
LUID Buffer;
|
|
|
|
RpcTryExcept {
|
|
|
|
//
|
|
// Call the Client Stub for LsaLookupPrivilegeValue.
|
|
//
|
|
|
|
Status = LsarLookupPrivilegeValue(
|
|
(LSAPR_HANDLE) PolicyHandle,
|
|
(PLSAPR_UNICODE_STRING)Name,
|
|
&Buffer
|
|
);
|
|
|
|
*Value = Buffer;
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaLookupPrivilegeName(
|
|
IN LSA_HANDLE PolicyHandle,
|
|
IN PLUID Value,
|
|
OUT PUNICODE_STRING *Name
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function programmatic name corresponding to the privilege
|
|
represented on the target system by the provided LUID.
|
|
|
|
|
|
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 - 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;
|
|
PLSAPR_UNICODE_STRING Buffer = NULL;
|
|
|
|
RpcTryExcept {
|
|
|
|
//
|
|
// Call the Client Stub for LsaLookupPrivilegeName.
|
|
//
|
|
|
|
Status = LsarLookupPrivilegeName(
|
|
(LSAPR_HANDLE) PolicyHandle,
|
|
Value,
|
|
&Buffer
|
|
);
|
|
|
|
(*Name) = (PUNICODE_STRING)Buffer;
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
//
|
|
// If memory was allocated for the return buffer, free it.
|
|
//
|
|
|
|
if (Buffer != NULL) {
|
|
|
|
MIDL_user_free(Buffer);
|
|
}
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
|
|
return(Status);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsaLookupPrivilegeDisplayName(
|
|
IN LSA_HANDLE PolicyHandle,
|
|
IN PUNICODE_STRING Name,
|
|
OUT PUNICODE_STRING *DisplayName,
|
|
OUT PSHORT LanguageReturned
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function retrieves a displayable name representing the
|
|
specified privilege.
|
|
|
|
|
|
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.
|
|
|
|
DisplayName - Receives a pointer to the privilege's displayable
|
|
name.
|
|
|
|
LanguageReturned - Receives the language of the returned displayable
|
|
name.
|
|
|
|
|
|
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;
|
|
SHORT ClientLanguage, ClientSystemDefaultLanguage;
|
|
PLSAPR_UNICODE_STRING Buffer = NULL;
|
|
|
|
RpcTryExcept {
|
|
|
|
//
|
|
// Call the Client Stub for LsaLookupPrivilegeDisplayName.
|
|
//
|
|
|
|
ClientLanguage = (SHORT)NtCurrentTeb()->CurrentLocale;
|
|
ClientSystemDefaultLanguage = ClientLanguage; //no sys default yet
|
|
Status = LsarLookupPrivilegeDisplayName(
|
|
(LSAPR_HANDLE) PolicyHandle,
|
|
(PLSAPR_UNICODE_STRING)Name,
|
|
ClientLanguage,
|
|
ClientSystemDefaultLanguage,
|
|
&Buffer,
|
|
(PWORD)LanguageReturned
|
|
);
|
|
|
|
(*DisplayName) = (PUNICODE_STRING)Buffer;
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
//
|
|
// If memory was allocated for the return buffer, free it.
|
|
//
|
|
|
|
if (Buffer != NULL) {
|
|
|
|
MIDL_user_free(Buffer);
|
|
}
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsaClose(
|
|
IN LSA_HANDLE ObjectHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API closes a handle to the LsaDatabase object or open object within
|
|
the database. If a handle to the LsaDatabase object is closed and there
|
|
are no objects still open within the current connection to the LSA, the
|
|
connection is closed. If a handle to an object within the database is
|
|
closed and the object is marked for DELETE access, the object will be
|
|
deleted when the last handle to that object is closed.
|
|
|
|
Arguments:
|
|
|
|
ObjectHandle - This parameter is either a handle to the LsaDatabase
|
|
object, which represents the entire LSA Database and also a
|
|
connection to the LSA of a target system, or a handle to an
|
|
object within the database.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
LSAPR_HANDLE Handle = (LSAPR_HANDLE) ObjectHandle;
|
|
|
|
RpcTryExcept {
|
|
|
|
//
|
|
// Call the Client Stub for LsaClose. Note that an additional
|
|
// level of indirection for the context handle parameter is required
|
|
// for the stub, because the server returns a NULL pointer to the handle
|
|
// so that the handle will be unbound by the stub.
|
|
//
|
|
|
|
Status = LsarClose( &Handle );
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
ULONG Code = RpcExceptionCode();
|
|
// Don't assert on bad handles -- this will cause bogus stress breaks
|
|
// ASSERT(Code != RPC_X_SS_CONTEXT_MISMATCH);
|
|
ASSERT(Code != RPC_S_INVALID_BINDING);
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
|
|
} RpcEndExcept;
|
|
|
|
|
|
if ( !NT_SUCCESS(Status)
|
|
&& (0 != Handle)) {
|
|
//
|
|
// Make sure in all error cases to remove the client side resources
|
|
// consumed by this handle.
|
|
//
|
|
RpcTryExcept {
|
|
(void) RpcSsDestroyClientContext(&Handle);
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
//
|
|
// The try/except is for app compat so that bad handles don't bring
|
|
// the process down
|
|
//
|
|
NOTHING;
|
|
} RpcEndExcept;
|
|
}
|
|
|
|
ASSERT( Status != STATUS_INVALID_PARAMETER_12 );
|
|
ASSERT( Status != STATUS_INVALID_HANDLE );
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsaDelete(
|
|
IN LSA_HANDLE ObjectHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaDelete API deletes an object. The object must be
|
|
open for DELETE access.
|
|
|
|
Arguments:
|
|
|
|
ObjectHandle - Handle from an LsaOpen<object-type> call.
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_INVALID_HANDLE - The specified handle is not valid.
|
|
|
|
Result codes from RPC.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
RpcTryExcept {
|
|
|
|
//
|
|
// Try calling the new worker routine LsarDeleteObject(). If
|
|
// this fails because it does not exist (versions 1.369 and earlier)
|
|
// then call the old routine LsarDelete().
|
|
//
|
|
|
|
Status = LsarDeleteObject((LSAPR_HANDLE *) &ObjectHandle);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
if ((Status == RPC_NT_UNKNOWN_IF) ||
|
|
(Status == RPC_NT_PROCNUM_OUT_OF_RANGE)) {
|
|
|
|
RpcTryExcept {
|
|
|
|
Status = LsarDelete((LSAPR_HANDLE) ObjectHandle);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaQuerySecurityObject(
|
|
IN LSA_HANDLE ObjectHandle,
|
|
IN SECURITY_INFORMATION SecurityInformation,
|
|
OUT PSECURITY_DESCRIPTOR *SecurityDescriptor
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaQuerySecurityObject API returns security information assigned
|
|
to an LSA Database object.
|
|
|
|
Based on the caller's access rights and privileges, this procedure will
|
|
return a security descriptor containing any or all of the object's owner
|
|
ID, group ID, discretionary ACL or system ACL. To read the owner ID,
|
|
group ID, or the discretionary ACL, the caller must be granted
|
|
READ_CONTROL access to the object. To read the system ACL, the caller must
|
|
have SeSecurityPrivilege privilege.
|
|
|
|
This API is modelled after the NtQuerySecurityObject() system service.
|
|
|
|
Arguments:
|
|
|
|
ObjectHandle - A handle to an existing object in the LSA Database.
|
|
|
|
SecurityInformation - Supplies a value describing which pieces of
|
|
security information are being queried. The values that may be
|
|
specified are the same as those defined in the NtSetSecurityObject()
|
|
API section.
|
|
|
|
SecurityDescriptor - receives a pointer to a buffer containing the
|
|
requested security information. This information is returned in
|
|
the form of a security descriptor. The caller is responsible for
|
|
freeing the returned buffer using LsaFreeMemory() when no longer
|
|
needed.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_INVALID_PARAMETER - An invalid parameter has been specified.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
LSAPR_SR_SECURITY_DESCRIPTOR ReturnedSD;
|
|
PLSAPR_SR_SECURITY_DESCRIPTOR PReturnedSD;
|
|
|
|
//
|
|
// The retrieved security descriptor is returned via a data structure that
|
|
// looks like:
|
|
//
|
|
// +-----------------------+
|
|
// | Length (bytes) |
|
|
// |-----------------------| +--------------+
|
|
// | SecurityDescriptor ---|--------->| Self-Relative|
|
|
// +-----------------------+ | Security |
|
|
// | Descriptor |
|
|
// +--------------+
|
|
//
|
|
// The first of these buffers is a local stack variable. The buffer containing
|
|
// the self-relative security descriptor is allocated by the RPC runtime. The
|
|
// pointer to the self-relative security descriptor is what is passed back to our
|
|
// caller.
|
|
//
|
|
//
|
|
|
|
//
|
|
// To prevent RPC from trying to marshal a self-relative security descriptor,
|
|
// make sure its field values are appropriately initialized to zero and null.
|
|
//
|
|
|
|
ReturnedSD.Length = 0;
|
|
ReturnedSD.SecurityDescriptor = NULL;
|
|
|
|
//
|
|
// Call the server ...
|
|
//
|
|
|
|
|
|
RpcTryExcept{
|
|
|
|
PReturnedSD = &ReturnedSD;
|
|
|
|
Status = LsarQuerySecurityObject(
|
|
(LSAPR_HANDLE) ObjectHandle,
|
|
SecurityInformation,
|
|
&PReturnedSD
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
(*SecurityDescriptor) = ReturnedSD.SecurityDescriptor;
|
|
|
|
} else {
|
|
|
|
(*SecurityDescriptor) = NULL;
|
|
}
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto QuerySecurityObjectError;
|
|
}
|
|
|
|
QuerySecurityObjectFinish:
|
|
|
|
return(Status);
|
|
|
|
QuerySecurityObjectError:
|
|
|
|
goto QuerySecurityObjectFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaSetSecurityObject(
|
|
IN LSA_HANDLE ObjectHandle,
|
|
IN SECURITY_INFORMATION SecurityInformation,
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaSetSecurityObject API takes a well formaed Security Descriptor
|
|
and assigns specified portions of it to an object. Based on the flags set
|
|
in the SecurityInformation parameter and the caller's access rights, this
|
|
procedure will replace any or alll of the security information associated
|
|
with the object.
|
|
|
|
The caller must have WRITE_OWNER access to the object to change the
|
|
owner or Primary group of the object. The caller must have WRITE_DAC
|
|
access to the object to change the Discretionary ACL. The caller must
|
|
have SeSecurityPrivilege to assign a system ACL to an object.
|
|
|
|
This API is modelled after the NtSetSecurityObject() system service.
|
|
|
|
Arguments:
|
|
|
|
ObjectHandle - A handle to an existing object in the LSA Database.
|
|
|
|
SecurityInformation - Indicates which security information is to be
|
|
applied to the object. The values that may be specified are the
|
|
same as those defined in the NtSetSecurityObject() API section.
|
|
The value(s) to be assigned are passed in the SecurityDescriptor
|
|
parameter.
|
|
|
|
SecurityDescriptor - A pointer to a well formed Security Descriptor.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_INVALID_PARAMETER - An invalid parameter has been specified.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG SDLength;
|
|
LSAPR_SR_SECURITY_DESCRIPTOR DescriptorToPass = { 0 };
|
|
|
|
//
|
|
// Make a self relative security descriptor for use in the RPC call..
|
|
//
|
|
|
|
SDLength = 0;
|
|
|
|
Status = RtlMakeSelfRelativeSD( SecurityDescriptor, NULL, &SDLength);
|
|
|
|
if (Status != STATUS_BUFFER_TOO_SMALL) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto SetSecurityObjectError;
|
|
}
|
|
|
|
DescriptorToPass.SecurityDescriptor = MIDL_user_allocate( SDLength );
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
if (DescriptorToPass.SecurityDescriptor == NULL) {
|
|
|
|
goto SetSecurityObjectError;
|
|
|
|
}
|
|
|
|
//
|
|
// Make an appropriate self-relative security descriptor
|
|
//
|
|
|
|
Status = RtlMakeSelfRelativeSD(
|
|
SecurityDescriptor,
|
|
(PSECURITY_DESCRIPTOR)DescriptorToPass.SecurityDescriptor,
|
|
&SDLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecurityObjectError;
|
|
}
|
|
|
|
DescriptorToPass.Length = SDLength;
|
|
|
|
RpcTryExcept{
|
|
|
|
Status = LsarSetSecurityObject(
|
|
(LSAPR_HANDLE) ObjectHandle,
|
|
SecurityInformation,
|
|
&DescriptorToPass
|
|
);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecurityObjectError;
|
|
}
|
|
|
|
SetSecurityObjectFinish:
|
|
|
|
//
|
|
// If necessary, free the Self Relative SD passed to the worker.
|
|
//
|
|
|
|
if (DescriptorToPass.SecurityDescriptor != NULL) {
|
|
|
|
MIDL_user_free( DescriptorToPass.SecurityDescriptor );
|
|
|
|
DescriptorToPass.SecurityDescriptor = NULL;
|
|
}
|
|
|
|
return(Status);
|
|
|
|
SetSecurityObjectError:
|
|
|
|
goto SetSecurityObjectFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaChangePassword(
|
|
IN PUNICODE_STRING ServerName,
|
|
IN PUNICODE_STRING DomainName,
|
|
IN PUNICODE_STRING AccountName,
|
|
IN PUNICODE_STRING OldPassword,
|
|
IN PUNICODE_STRING NewPassword
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaChangePassword API is used to change a user account's password.
|
|
The user must have appropriate access to the user account and must
|
|
know the current password value.
|
|
|
|
|
|
Arguments:
|
|
|
|
ServerName - The name of the Domain Controller at which the password
|
|
can be changed.
|
|
|
|
DomainName - The name of the domain in which the account exists.
|
|
|
|
AccountName - The name of the account whose password is to be changed.
|
|
|
|
NewPassword - The new password value.
|
|
|
|
OldPassword - The old (current) password value.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_ILL_FORMED_PASSWORD - The new password is poorly formed, e.g.
|
|
contains characters that can't be entered from the keyboard.
|
|
|
|
STATUS_PASSWORD_RESTRICTION - A restriction prevents the password
|
|
from being changed. This may be for an number of reasons,
|
|
including time restrictions on how often a password may be changed
|
|
or length restrictions on the provided (new) password.
|
|
|
|
This error might also be returned if the new password matched
|
|
a password in the recent history log for the account. Security
|
|
administrators indicate how many of the most recently used
|
|
passwords may not be re-used.
|
|
|
|
STATUS_WRONG_PASSWORD - OldPassword does not contain the user's
|
|
current password.
|
|
|
|
STATUS_NO_SUCH_USER - The SID provided does not lead to a user
|
|
account.
|
|
|
|
STATUS_CANT_UPDATE_MASTER - An attempt to update the master copy
|
|
of the password was unsuccessful. Please try again later.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
DBG_UNREFERENCED_PARAMETER( ServerName );
|
|
DBG_UNREFERENCED_PARAMETER( DomainName );
|
|
DBG_UNREFERENCED_PARAMETER( AccountName );
|
|
DBG_UNREFERENCED_PARAMETER( OldPassword );
|
|
DBG_UNREFERENCED_PARAMETER( NewPassword );
|
|
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaCreateAccount(
|
|
IN LSA_HANDLE PolicyHandle,
|
|
IN PSID AccountSid,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT PLSA_HANDLE AccountHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaCreateAccount API adds a user or group account to the
|
|
list of accounts in the target system's LsaDatabase object. The
|
|
newly added account object is initially placed in the opened state and
|
|
a handle to it is returned. The caller must have LSA_CREATE_ACCOUNT
|
|
access to the LsaDatabase object.
|
|
|
|
Note that no check is made to determine whether there is an account
|
|
of the given Sid in the target system's Primary Domain (if any), nor
|
|
is any check made to verify that the Sid and name describe the same
|
|
account.
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - Handle from an LsaOpenLsa call.
|
|
|
|
AccountSid - Points to the SID of the Account object.
|
|
|
|
DesiredAccess - Specifies the accesses to be granted to the newly
|
|
created and opened account.
|
|
|
|
AccountHandle - Receives a handle to the newly created and opened
|
|
account. This handle is used on subsequent accesses to the account
|
|
until closed.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_ACCOUNT_ALREADY_EXISTS - A user or group account object having
|
|
the Sid given in AccountInformation already exists.
|
|
|
|
STATUS_INVALID_PARAMETER - An invalid parameter has been specified,
|
|
one or more of the following apply.
|
|
|
|
- CreateDisposition not valid
|
|
- A user or group account having the Sid given AccountInformation
|
|
already exists, but CreateDisposition = LSA_OBJECT_CREATE.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
RpcTryExcept {
|
|
|
|
Status = LsarCreateAccount(
|
|
(LSAPR_HANDLE) PolicyHandle,
|
|
(PLSAPR_SID) AccountSid,
|
|
DesiredAccess,
|
|
(PLSAPR_HANDLE) AccountHandle
|
|
);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaEnumerateAccounts(
|
|
IN LSA_HANDLE PolicyHandle,
|
|
IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
|
|
OUT PVOID *Buffer,
|
|
IN ULONG PreferedMaximumLength,
|
|
OUT PULONG CountReturned
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaEnumerateAccounts API returns information about
|
|
Account objects. 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.
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - Handle from an LsaOpenLsa call.
|
|
|
|
EnumerationContext - API-specific handle to allow multiple calls
|
|
(see Routine Description above).
|
|
|
|
EnumerationInformation - Receives a pointer to an array of structures
|
|
each describing an Account object. Currently, each structure contains
|
|
a pointer to the Account Sid.
|
|
|
|
PreferedMaximumLength - Prefered maximum 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 - Pointer to location which receives the number of entries
|
|
returned.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully, there may be
|
|
more entries.
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_NO_MORE_ENTRIES - There are no more entries. This warning
|
|
is returned if there are no more objects to enumerate. Note that
|
|
one or more objects may be enumerated on a call that returns this
|
|
reply.
|
|
|
|
STATUS_INVALID_PARAMETER - Invalid parameter.
|
|
|
|
- NULL return pointer for enumeration buffer.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
LSAPR_ACCOUNT_ENUM_BUFFER EnumerationBuffer;
|
|
|
|
EnumerationBuffer.EntriesRead = 0;
|
|
EnumerationBuffer.Information = NULL;
|
|
|
|
RpcTryExcept {
|
|
|
|
//
|
|
// Enumerate the Accounts. On successful return,
|
|
// the Enumeration Buffer structure will receive a count
|
|
// of the number of Accounts enumerated this call
|
|
// and a pointer to an array of Account Information Entries.
|
|
//
|
|
// EnumerationBuffer -> EntriesRead
|
|
// Information -> Account Info for Domain 0
|
|
// Account Info for Domain 1
|
|
// ...
|
|
// Account Info for Domain
|
|
// (EntriesRead - 1)
|
|
//
|
|
|
|
Status = LsarEnumerateAccounts(
|
|
(LSAPR_HANDLE) PolicyHandle,
|
|
EnumerationContext,
|
|
&EnumerationBuffer,
|
|
PreferedMaximumLength
|
|
);
|
|
|
|
//
|
|
// Return enumeration information or NULL to caller.
|
|
//
|
|
// NOTE: "Information" is allocated by the called client stub
|
|
// as a single block via MIDL_user_allocate, because Information is
|
|
// allocated all-nodes. We can therefore pass back the pointer
|
|
// directly to the client, who will be able to free the memory after
|
|
// use via LsaFreeMemory() [which makes a MIDL_user_free call].
|
|
//
|
|
|
|
*CountReturned = EnumerationBuffer.EntriesRead;
|
|
*Buffer = EnumerationBuffer.Information;
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
//
|
|
// If memory was allocated for the Account Information array,
|
|
// free it.
|
|
//
|
|
|
|
if (EnumerationBuffer.Information != NULL) {
|
|
|
|
MIDL_user_free(EnumerationBuffer.Information);
|
|
}
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaCreateTrustedDomain(
|
|
IN LSA_HANDLE PolicyHandle,
|
|
IN PLSA_TRUST_INFORMATION TrustedDomainInformation,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT PLSA_HANDLE TrustedDomainHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaCreateTrustedDomain API creates a new TrustedDomain object. The
|
|
caller must have POLICY_TRUST_ADMIN access to the Policy Object.
|
|
|
|
Note that NO verification is done to check that the given domain name
|
|
matches the given SID or that the SID or name represent an actual domain.
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - Handle from an LsaOpenPolicy call.
|
|
|
|
TrustedDomainInformation - Pointer to structure containing the name and
|
|
SID of the new Trusted Domain.
|
|
|
|
DesiredAccess - Specifies the accesses to be granted for the newly
|
|
created object.
|
|
|
|
TrustedDomainHandle - receives a handle referencing the newly created
|
|
object. This handle is used on subsequent accesses to the object.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
*TrustedDomainHandle = NULL;
|
|
|
|
RpcTryExcept {
|
|
|
|
Status = LsarCreateTrustedDomain(
|
|
(LSAPR_HANDLE) PolicyHandle,
|
|
(PLSAPR_TRUST_INFORMATION) TrustedDomainInformation,
|
|
DesiredAccess,
|
|
(PLSAPR_HANDLE) TrustedDomainHandle
|
|
);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaOpenTrustedDomain(
|
|
IN LSA_HANDLE PolicyHandle,
|
|
IN PSID TrustedDomainSid,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT PLSA_HANDLE TrustedDomainHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaOpenTrustedDomain API opens an existing TrustedDomain object
|
|
using the SID as the primary key value.
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - An open handle to a Policy object.
|
|
|
|
TrustedDomainSid - Pointer to the account's Sid.
|
|
|
|
DesiredAccess - This is an access mask indicating accesses being
|
|
requested to the target object.
|
|
|
|
TrustedDomainHandle - Receives a handle to be used in future requests.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_TRUSTED_DOMAIN_NOT_FOUND - There is no TrustedDomain object in the
|
|
target system's LSA Database having the specified AccountSid.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
RpcTryExcept {
|
|
|
|
Status = LsarOpenTrustedDomain(
|
|
(LSAPR_HANDLE) PolicyHandle,
|
|
(PLSAPR_SID) TrustedDomainSid,
|
|
DesiredAccess,
|
|
(PLSAPR_HANDLE) TrustedDomainHandle
|
|
);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaQueryInfoTrustedDomain(
|
|
IN LSA_HANDLE TrustedDomainHandle,
|
|
IN TRUSTED_INFORMATION_CLASS InformationClass,
|
|
OUT PVOID *Buffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaQueryInfoTrustedDomain API obtains information from a
|
|
TrustedDomain object. The caller must have access appropriate to the
|
|
information being requested (see InformationClass parameter).
|
|
|
|
Arguments:
|
|
|
|
TrustedDomainHandle - Handle from an LsaOpenTrustedDomain or
|
|
LsaCreateTrustedDomain call.
|
|
|
|
InformationClass - Specifies the information to be returned. The
|
|
Information Classes and accesses required are as follows:
|
|
|
|
Information Class Required Access Type
|
|
|
|
TrustedAccountNameInformation TRUSTED_QUERY_ACCOUNT_NAME
|
|
TrustedControllersInformation TRUSTED_QUERY_CONTROLLERS
|
|
TrustedPosixInformation TRUSTED_QUERY_POSIX
|
|
|
|
Buffer - Receives a pointer to the buffer returned comtaining the
|
|
requested information. This buffer is allocated by this service
|
|
and must be freed when no longer needed by passing the returned
|
|
value to LsaFreeMemory().
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|
access to complete the operation.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
|
|
such as memory, to complete the call.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PLSAPR_TRUSTED_DOMAIN_INFO TrustedDomainInformation = NULL;
|
|
|
|
//
|
|
// Avoid the internal info levels that represent the encrypted version on
|
|
// the wire.
|
|
//
|
|
switch ( InformationClass ) {
|
|
case TrustedDomainAuthInformationInternal:
|
|
case TrustedDomainFullInformationInternal:
|
|
case TrustedDomainInformationEx2Internal:
|
|
case TrustedDomainFullInformation2Internal:
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
RpcTryExcept {
|
|
|
|
//
|
|
// Call the Client Stub for LsaQueryInformationTrustedDomain.
|
|
//
|
|
|
|
Status = LsarQueryInfoTrustedDomain(
|
|
(LSAPR_HANDLE) TrustedDomainHandle,
|
|
InformationClass,
|
|
&TrustedDomainInformation
|
|
);
|
|
|
|
//
|
|
// Return pointer to Policy Information for the given class, or NULL.
|
|
//
|
|
|
|
*Buffer = TrustedDomainInformation;
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
//
|
|
// If memory was allocated for the returned Trusted Domain Information,
|
|
// free it.
|
|
//
|
|
|
|
if (TrustedDomainInformation != NULL) {
|
|
|
|
MIDL_user_free(TrustedDomainInformation);
|
|
}
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaSetInformationTrustedDomain(
|
|
IN LSA_HANDLE TrustedDomainHandle,
|
|
IN TRUSTED_INFORMATION_CLASS InformationClass,
|
|
IN PVOID Buffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaSetInformationTrustedDomain API modifies information in the Trusted
|
|
Domain Object. The caller must have access appropriate to the
|
|
information to be changedin the Policy Object, see the InformationClass
|
|
parameter.
|
|
|
|
Arguments:
|
|
|
|
TrustedDomainHandle - Handle from an LsaOpenTrustedDomain or
|
|
LsaCreateTrustedDomain call.
|
|
|
|
InformationClass - Specifies the type of information being changed.
|
|
The information types and accesses required to change them are as
|
|
follows:
|
|
|
|
TrustedAccountInformation ( Cannot be set )
|
|
TrustedControllersInformation TRUSTED_SET_CONTROLLERS
|
|
TrustedPosixOffsetInformation TRUSTED_POSIX_INFORMATION
|
|
|
|
Buffer - Points to a structure containing the information appropriate
|
|
to the InformationClass parameter.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - Call completed successfully.
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
|
|
such as memory, to complete the call.
|
|
|
|
STATUS_INVALID_HANDLE - Handle is invalid or is of the wrong type.
|
|
|
|
STATUS_INVALID_PARAMETER - Invalid parameter:
|
|
Information class invalid
|
|
Information class cannot be set
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PLSAPR_TRUSTED_DOMAIN_AUTH_INFORMATION_INTERNAL InternalAuthBuffer = NULL;
|
|
PVOID InternalBuffer;
|
|
TRUSTED_INFORMATION_CLASS InternalInformationClass;
|
|
|
|
LSAPR_TRUSTED_DOMAIN_FULL_INFORMATION_INTERNAL InternalFullBuffer;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
InternalInformationClass = InformationClass;
|
|
InternalBuffer = Buffer;
|
|
|
|
//
|
|
// Avoid the internal info levels that represent the encrypted version on
|
|
// the wire.
|
|
//
|
|
switch ( InformationClass ) {
|
|
case TrustedPasswordInformation:
|
|
case TrustedDomainInformationBasic:
|
|
case TrustedDomainAuthInformationInternal:
|
|
case TrustedDomainFullInformationInternal:
|
|
case TrustedDomainInformationEx2Internal:
|
|
case TrustedDomainFullInformation2Internal:
|
|
Status = STATUS_INVALID_INFO_CLASS;
|
|
goto Cleanup;
|
|
|
|
//
|
|
// Handle the info classes that need to be encrypted on the wire
|
|
//
|
|
case TrustedDomainAuthInformation: {
|
|
|
|
//
|
|
// Encrypt the data into an internal buffer.
|
|
//
|
|
|
|
Status = LsapEncryptAuthInfo( TrustedDomainHandle,
|
|
(PLSAPR_TRUSTED_DOMAIN_AUTH_INFORMATION) Buffer,
|
|
&InternalAuthBuffer );
|
|
|
|
if ( !NT_SUCCESS(Status)) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Use an internal info level to tell the server that the data is
|
|
// encrypted.
|
|
//
|
|
|
|
InternalInformationClass = TrustedDomainAuthInformationInternal;
|
|
InternalBuffer = InternalAuthBuffer;
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// Handle the info classes that need to be encrypted on the wire
|
|
//
|
|
case TrustedDomainFullInformation: {
|
|
PLSAPR_TRUSTED_DOMAIN_FULL_INFORMATION FullBuffer =
|
|
(PLSAPR_TRUSTED_DOMAIN_FULL_INFORMATION) Buffer;
|
|
|
|
//
|
|
// Encrypt the data into an internal buffer.
|
|
//
|
|
|
|
Status = LsapEncryptAuthInfo( TrustedDomainHandle,
|
|
&FullBuffer->AuthInformation,
|
|
&InternalAuthBuffer );
|
|
|
|
if ( !NT_SUCCESS(Status)) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Copy all of the information into a single new structure.
|
|
//
|
|
|
|
InternalFullBuffer.Information = FullBuffer->Information;
|
|
InternalFullBuffer.PosixOffset = FullBuffer->PosixOffset;
|
|
InternalFullBuffer.AuthInformation = *InternalAuthBuffer;
|
|
|
|
//
|
|
// Use an internal info level to tell the server that the data is
|
|
// encrypted.
|
|
//
|
|
|
|
InternalInformationClass = TrustedDomainFullInformationInternal;
|
|
InternalBuffer = &InternalFullBuffer;
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the information class was morphed,
|
|
// try the morphed class.
|
|
//
|
|
|
|
if ( InternalInformationClass != InformationClass ) {
|
|
RpcTryExcept {
|
|
|
|
//
|
|
// Call the Client Stub
|
|
//
|
|
|
|
Status = LsarSetInformationTrustedDomain(
|
|
(LSAPR_HANDLE) TrustedDomainHandle,
|
|
InternalInformationClass,
|
|
(PLSAPR_TRUSTED_DOMAIN_INFO) InternalBuffer
|
|
);
|
|
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
//
|
|
// If the morphed info class is valid,
|
|
// we're all done with this call.
|
|
// (Otherwise, drop through to try the non-morphed class.)
|
|
//
|
|
|
|
if ( Status != RPC_NT_INVALID_TAG ) {
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Handle non-morphed information classes.
|
|
//
|
|
|
|
RpcTryExcept {
|
|
|
|
//
|
|
// Call the Client Stub
|
|
//
|
|
|
|
Status = LsarSetInformationTrustedDomain(
|
|
(LSAPR_HANDLE) TrustedDomainHandle,
|
|
InformationClass,
|
|
(PLSAPR_TRUSTED_DOMAIN_INFO) Buffer
|
|
);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
Cleanup:
|
|
if ( InternalAuthBuffer != NULL ) {
|
|
LocalFree( InternalAuthBuffer );
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaEnumerateTrustedDomains(
|
|
IN LSA_HANDLE PolicyHandle,
|
|
IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
|
|
OUT PVOID *Buffer,
|
|
IN ULONG PreferedMaximumLength,
|
|
OUT PULONG CountReturned
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaEnumerateTrustedDomains API returns information about the accounts
|
|
in the target system's Policy object. 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.
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - Handle from an LsaOpenPolicy call.
|
|
|
|
EnumerationContext - API-specific handle to allow multiple calls
|
|
(see Routine Description above).
|
|
|
|
Buffer - Receives a pointer to a buffer containing enumeration
|
|
information. This buffer is an array of structures of type
|
|
LSA_TRUST_INFORMATION. If no trusted domains are found,
|
|
NULL is returned.
|
|
|
|
PreferedMaximumLength - Prefered maximum 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 - Pointer to variable which will receive a count of the
|
|
entries returned.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully, there may be
|
|
more entries.
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_NO_MORE_ENTRIES - There are no more entries. This warning
|
|
is returned if there are no more objects to enumerate. Note that
|
|
one or more objects may be enumerated on a call that returns this
|
|
reply.
|
|
|
|
STATUS_INVALID_PARAMETER - Invalid parameter.
|
|
|
|
- NULL return pointer for enumeration buffer.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
LSAPR_TRUSTED_ENUM_BUFFER EnumerationBuffer;
|
|
EnumerationBuffer.EntriesRead = 0;
|
|
EnumerationBuffer.Information = NULL;
|
|
|
|
//
|
|
// Verify that caller has provided a return buffer pointer.
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT(Buffer)) {
|
|
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
|
|
RpcTryExcept {
|
|
|
|
//
|
|
// Enumerate the Trusted Domains. On successful return,
|
|
// the Enumeration Buffer structure will receive a count
|
|
// of the number of Trusted Domains enumerated this call
|
|
// and a pointer to an array of Trust Information Entries.
|
|
//
|
|
// EnumerationBuffer -> EntriesRead
|
|
// Information -> Trust Info for Domain 0
|
|
// Trust Info for Domain 1
|
|
// ...
|
|
// Trust Info for Domain
|
|
// (EntriesRead - 1)
|
|
//
|
|
//
|
|
|
|
Status = LsarEnumerateTrustedDomains(
|
|
(LSAPR_HANDLE) PolicyHandle,
|
|
EnumerationContext,
|
|
&EnumerationBuffer,
|
|
PreferedMaximumLength
|
|
);
|
|
|
|
//
|
|
// Return enumeration information or NULL to caller.
|
|
//
|
|
// NOTE: "Information" is allocated by the called client stub
|
|
// as a single block via MIDL_user_allocate, because Information is
|
|
// allocated all-nodes. We can therefore pass back the pointer
|
|
// directly to the client, who will be able to free the memory after
|
|
// use via LsaFreeMemory() [which makes a MIDL_user_free call].
|
|
//
|
|
|
|
*CountReturned = EnumerationBuffer.EntriesRead;
|
|
*Buffer = EnumerationBuffer.Information;
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
//
|
|
// If memory was allocated for the Trust Information array,
|
|
// free it.
|
|
//
|
|
|
|
if (EnumerationBuffer.Information != NULL) {
|
|
|
|
MIDL_user_free(EnumerationBuffer.Information);
|
|
}
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
LsaEnumeratePrivileges(
|
|
IN LSA_HANDLE PolicyHandle,
|
|
IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
|
|
OUT PVOID *Buffer,
|
|
IN ULONG PreferedMaximumLength,
|
|
OUT PULONG CountReturned
|
|
)
|
|
|
|
/*++
|
|
|
|
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 LsaOpenPolicy() call.
|
|
|
|
EnumerationContext - API specific handle to allow multiple calls
|
|
(see Routine Description).
|
|
|
|
Buffer - Receives a pointer to a buffer containing information for
|
|
one or more Privileges. This information is an array of structures
|
|
of type POLICY_PRIVILEGE_DEFINITION.
|
|
|
|
When this information is no longer needed, it must be released by
|
|
passing the returned pointer to LsaFreeMemory().
|
|
|
|
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.
|
|
|
|
Errors from RPC.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
LSAPR_PRIVILEGE_ENUM_BUFFER EnumerationBuffer;
|
|
|
|
EnumerationBuffer.Entries = 0;
|
|
EnumerationBuffer.Privileges = NULL;
|
|
|
|
RpcTryExcept {
|
|
|
|
//
|
|
// Enumerate the Privileges. On successful return,
|
|
// the Enumeration Buffer structure will receive a count
|
|
// of the number of Privileges enumerated this call
|
|
// and a pointer to an array of Privilege Definition Entries.
|
|
//
|
|
// EnumerationBuffer -> Entries
|
|
// Privileges -> Privilege Definition 0
|
|
// Privilege Definition 1
|
|
// ...
|
|
// Privilege Definition
|
|
// (Entries - 1)
|
|
//
|
|
|
|
Status = LsarEnumeratePrivileges(
|
|
(LSAPR_HANDLE) PolicyHandle,
|
|
EnumerationContext,
|
|
&EnumerationBuffer,
|
|
PreferedMaximumLength
|
|
);
|
|
|
|
//
|
|
// Return enumeration information or NULL to caller.
|
|
//
|
|
// NOTE: "Information" is allocated by the called client stub
|
|
// as a single block via MIDL_user_allocate, because Information is
|
|
// allocated all-nodes. We can therefore pass back the pointer
|
|
// directly to the client, who will be able to free the memory after
|
|
// use via LsaFreeMemory() [which makes a MIDL_user_free call].
|
|
//
|
|
|
|
*CountReturned = EnumerationBuffer.Entries;
|
|
*Buffer = EnumerationBuffer.Privileges;
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
//
|
|
// If memory was allocated for the Account Information array,
|
|
// free it.
|
|
//
|
|
|
|
if (EnumerationBuffer.Privileges != NULL) {
|
|
|
|
MIDL_user_free(EnumerationBuffer.Privileges);
|
|
}
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaCreateSecret(
|
|
IN LSA_HANDLE PolicyHandle,
|
|
IN PUNICODE_STRING SecretName,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT PLSA_HANDLE SecretHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaCreateSecretInLsa API creates a named Secret object in the
|
|
Lsa Database. Each Secret Object can have two values assigned,
|
|
called the Current Value and the Old Value. The meaning of these
|
|
values is known to the Secret object creator. The caller must have
|
|
LSA_CREATE_SECRET access to the LsaDatabase object.
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - Handle from an LsaOpenLsa call.
|
|
|
|
SecretName - Pointer to Unicode String specifying the name of the
|
|
secret.
|
|
|
|
DesiredAccess - Specifies the accesses to be granted to the newly
|
|
created and opened secret.
|
|
|
|
SecretHandle - Receives a handle to the newly created and opened
|
|
Secret object. This handle is used on subsequent accesses to
|
|
the object until closed.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_OBJECT_NAME_COLLISION - A Secret object having the given name
|
|
already exists.
|
|
|
|
STATUS_TOO_MANY_SECRETS - The maximum number of Secret objects in the
|
|
system has been reached.
|
|
|
|
STATUS_PRIVILEGE_NOT_HELD - ACCESS_SYSTEM_SECURITY was specified as part
|
|
of DesiredAccess mask, but the caller does not hold SE_SECURITY_PRIVILEGE
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
*SecretHandle = NULL;
|
|
|
|
RpcTryExcept {
|
|
|
|
//
|
|
// Verify that the given SecretName has non-null length. Currently
|
|
// midl cannot handle this.
|
|
//
|
|
|
|
if ((SecretName == NULL) ||
|
|
(SecretName->Buffer == NULL) ||
|
|
(SecretName->Length == 0) ||
|
|
(SecretName->Length > SecretName->MaximumLength)) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
|
|
Status = LsarCreateSecret(
|
|
(LSAPR_HANDLE) PolicyHandle,
|
|
(PLSAPR_UNICODE_STRING) SecretName,
|
|
DesiredAccess,
|
|
(PLSAPR_HANDLE) SecretHandle
|
|
);
|
|
}
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsaLookupNames2(
|
|
IN LSA_HANDLE PolicyHandle,
|
|
IN ULONG Flags,
|
|
IN ULONG Count,
|
|
IN PUNICODE_STRING Names,
|
|
OUT PLSA_REFERENCED_DOMAIN_LIST *ReferencedDomains,
|
|
OUT PLSA_TRANSLATED_SID2 *Sids
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaLookupNames API attempts to translate names of domains, users,
|
|
groups or aliases to Sids. The caller must have POLICY_LOOKUP_NAMES
|
|
access to the Policy object.
|
|
|
|
Names may be either isolated (e.g. JohnH) or composite names containing
|
|
both the domain name and account name. Composite names must include a
|
|
backslash character separating the domain name from the account name
|
|
(e.g. Acctg\JohnH). An isolated name may be either an account name
|
|
(user, group, or alias) or a domain name.
|
|
|
|
Translation of isolated names introduces the possibility of name
|
|
collisions (since the same name may be used in multiple domains). An
|
|
isolated name will be translated using the following algorithm:
|
|
|
|
If the name is a well-known name (e.g. Local or Interactive), then the
|
|
corresponding well-known Sid is returned.
|
|
|
|
If the name is the Built-in Domain's name, then that domain's Sid
|
|
will be returned.
|
|
|
|
If the name is the Account Domain's name, then that domain's Sid
|
|
will be returned.
|
|
|
|
If the name is the Primary Domain's name, then that domain's Sid will
|
|
be returned.
|
|
|
|
If the name is a user, group, or alias in the Built-in Domain, then the
|
|
Sid of that account is returned.
|
|
|
|
If the name is a user, group, or alias in the Primary Domain, then the
|
|
Sid of that account is returned.
|
|
|
|
Otherwise, the name is not translated.
|
|
|
|
NOTE: Proxy, Machine, and Trust user accounts are not referenced
|
|
for name translation. Only normal user accounts are used for ID
|
|
translation. If translation of other account types is needed, then
|
|
SAM services should be used directly.
|
|
|
|
Arguments:
|
|
|
|
This function is the LSA server RPC worker routine for the
|
|
LsaLookupNamesInLsa API.
|
|
|
|
PolicyHandle - Handle from an LsaOpenPolicy call.
|
|
|
|
Flags - LSA_LOOKUP_ISOLATED_AS_LOCAL
|
|
|
|
Count - Specifies the number of names to be translated.
|
|
|
|
Names - Pointer to an array of Count Unicode String structures
|
|
specifying the names to be looked up and mapped to Sids.
|
|
The strings may be names of User, Group or Alias accounts or
|
|
domains.
|
|
|
|
ReferencedDomains - receives a pointer to a structure describing the
|
|
domains used for the translation. The entries in this structure
|
|
are referenced by the structure returned via the Sids parameter.
|
|
Unlike the Sids parameter, which contains an array entry for
|
|
each translated name, this structure will only contain one
|
|
component for each domain utilized in the translation.
|
|
|
|
When this information is no longer needed, it must be released
|
|
by passing the returned pointer to LsaFreeMemory().
|
|
|
|
Sids - Receives a pointer to an array of records describing each
|
|
translated Sid. The nth entry in this array provides a translation
|
|
for (the nth element in the Names parameter.
|
|
|
|
When this information is no longer needed, it must be released
|
|
by passing the returned pointer to LsaFreeMemory().
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_SOME_NOT_MAPPED - Some or all of the names provided could
|
|
not be mapped. This is an informational status only.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
|
|
to complete the call.
|
|
|
|
STATUS_TOO_MANY_NAMES - Too many Names have been specified.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG MappedCount = 0;
|
|
ULONG i;
|
|
|
|
if ( (NULL == Sids)
|
|
|| (NULL == ReferencedDomains ) ) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
//
|
|
// Init the out parameters since LsaICLookupNames expects this
|
|
//
|
|
*ReferencedDomains = NULL;
|
|
*Sids = NULL;
|
|
|
|
Status = LsaICLookupNames(
|
|
PolicyHandle,
|
|
Flags,
|
|
Count,
|
|
Names,
|
|
(PLSA_REFERENCED_DOMAIN_LIST *) ReferencedDomains,
|
|
(PLSA_TRANSLATED_SID_EX2*)Sids,
|
|
LsapLookupWksta,
|
|
0,
|
|
&MappedCount,
|
|
NULL
|
|
);
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaLookupNames(
|
|
IN LSA_HANDLE PolicyHandle,
|
|
IN ULONG Count,
|
|
IN PUNICODE_STRING Names,
|
|
OUT PLSA_REFERENCED_DOMAIN_LIST *ReferencedDomains,
|
|
OUT PLSA_TRANSLATED_SID *Sids
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaLookupNames API attempts to translate names of domains, users,
|
|
groups or aliases to Sids. The caller must have POLICY_LOOKUP_NAMES
|
|
access to the Policy object.
|
|
|
|
Names may be either isolated (e.g. JohnH) or composite names containing
|
|
both the domain name and account name. Composite names must include a
|
|
backslash character separating the domain name from the account name
|
|
(e.g. Acctg\JohnH). An isolated name may be either an account name
|
|
(user, group, or alias) or a domain name.
|
|
|
|
Translation of isolated names introduces the possibility of name
|
|
collisions (since the same name may be used in multiple domains). An
|
|
isolated name will be translated using the following algorithm:
|
|
|
|
If the name is a well-known name (e.g. Local or Interactive), then the
|
|
corresponding well-known Sid is returned.
|
|
|
|
If the name is the Built-in Domain's name, then that domain's Sid
|
|
will be returned.
|
|
|
|
If the name is the Account Domain's name, then that domain's Sid
|
|
will be returned.
|
|
|
|
If the name is the Primary Domain's name, then that domain's Sid will
|
|
be returned.
|
|
|
|
If the name is a user, group, or alias in the Built-in Domain, then the
|
|
Sid of that account is returned.
|
|
|
|
If the name is a user, group, or alias in the Primary Domain, then the
|
|
Sid of that account is returned.
|
|
|
|
Otherwise, the name is not translated.
|
|
|
|
NOTE: Proxy, Machine, and Trust user accounts are not referenced
|
|
for name translation. Only normal user accounts are used for ID
|
|
translation. If translation of other account types is needed, then
|
|
SAM services should be used directly.
|
|
|
|
Arguments:
|
|
|
|
This function is the LSA server RPC worker routine for the
|
|
LsaLookupNamesInLsa API.
|
|
|
|
PolicyHandle - Handle from an LsaOpenPolicy call.
|
|
|
|
Count - Specifies the number of names to be translated.
|
|
|
|
Names - Pointer to an array of Count Unicode String structures
|
|
specifying the names to be looked up and mapped to Sids.
|
|
The strings may be names of User, Group or Alias accounts or
|
|
domains.
|
|
|
|
ReferencedDomains - receives a pointer to a structure describing the
|
|
domains used for the translation. The entries in this structure
|
|
are referenced by the structure returned via the Sids parameter.
|
|
Unlike the Sids parameter, which contains an array entry for
|
|
each translated name, this structure will only contain one
|
|
component for each domain utilized in the translation.
|
|
|
|
When this information is no longer needed, it must be released
|
|
by passing the returned pointer to LsaFreeMemory().
|
|
|
|
Sids - Receives a pointer to an array of records describing each
|
|
translated Sid. The nth entry in this array provides a translation
|
|
for (the nth element in the Names parameter.
|
|
|
|
When this information is no longer needed, it must be released
|
|
by passing the returned pointer to LsaFreeMemory().
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_SOME_NOT_MAPPED - Some or all of the names provided could
|
|
not be mapped. This is an informational status only.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
|
|
to complete the call.
|
|
|
|
STATUS_TOO_MANY_NAMES - Too many Names have been specified.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG MappedCount = 0;
|
|
PLSA_TRANSLATED_SID_EX2 SidsEx = NULL;
|
|
ULONG i;
|
|
|
|
if ( (NULL == Sids)
|
|
|| (NULL == ReferencedDomains ) ) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
}
|
|
*Sids = NULL;
|
|
*ReferencedDomains = NULL;
|
|
|
|
Status = LsaICLookupNames(
|
|
PolicyHandle,
|
|
0,
|
|
Count,
|
|
Names,
|
|
(PLSA_REFERENCED_DOMAIN_LIST *) ReferencedDomains,
|
|
&SidsEx,
|
|
LsapLookupWksta,
|
|
LSAIC_NO_LARGE_SID,
|
|
&MappedCount,
|
|
NULL
|
|
);
|
|
|
|
if ( SidsEx ) {
|
|
|
|
//
|
|
// Some sids were returned -- map the new structure to the old one
|
|
//
|
|
ULONG SizeNeeded = 0;
|
|
PLSA_TRANSLATED_SID TempSids = NULL;
|
|
|
|
SizeNeeded = Count * sizeof( LSA_TRANSLATED_SID );
|
|
TempSids = midl_user_allocate( SizeNeeded );
|
|
if ( TempSids ) {
|
|
|
|
RtlZeroMemory( TempSids, SizeNeeded );
|
|
for ( i = 0; i < Count; i++ ) {
|
|
|
|
TempSids[i].Use = SidsEx[i].Use;
|
|
|
|
if (SidTypeDomain == SidsEx[i].Use) {
|
|
|
|
TempSids[i].RelativeId = LSA_UNKNOWN_ID;
|
|
|
|
} else if (SidsEx[i].Sid) {
|
|
|
|
ULONG SubAuthCount = (ULONG) *RtlSubAuthorityCountSid(SidsEx[i].Sid);
|
|
TempSids[i].RelativeId = *RtlSubAuthoritySid(SidsEx[i].Sid, (SubAuthCount - 1));
|
|
|
|
} else {
|
|
TempSids[i].RelativeId = 0;
|
|
}
|
|
TempSids[i].DomainIndex = SidsEx[i].DomainIndex;
|
|
}
|
|
|
|
*Sids = TempSids;
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
if ( *ReferencedDomains ) {
|
|
|
|
midl_user_free( *ReferencedDomains );
|
|
*ReferencedDomains = NULL;
|
|
}
|
|
}
|
|
|
|
midl_user_free( SidsEx );
|
|
|
|
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
LsaICLookupNames(
|
|
IN LSA_HANDLE PolicyHandle,
|
|
IN ULONG LookupOptions,
|
|
IN ULONG Count,
|
|
IN PUNICODE_STRING Names,
|
|
OUT PLSA_REFERENCED_DOMAIN_LIST *ReferencedDomains,
|
|
OUT PLSA_TRANSLATED_SID_EX2 *Sids,
|
|
IN LSAP_LOOKUP_LEVEL LookupLevel,
|
|
IN ULONG Flags,
|
|
IN OUT PULONG MappedCount,
|
|
IN OUT PULONG ServerRevision
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is the internal client side version of the LsaLookupNames
|
|
API. It is called both from the client side of the Lsa and also
|
|
the server side of the LSA (when calling out to another LSA). The
|
|
function is identical to the LsaLookupNames API except that there is an
|
|
additional parameter, the LookupLevel parameter.
|
|
|
|
The LsaLookupNames API attempts to translate names of domains, users,
|
|
groups or aliases to Sids. The caller must have POLICY_LOOKUP_NAMES
|
|
access to the Policy object.
|
|
|
|
Names may be either isolated (e.g. JohnH) or composite names containing
|
|
both the domain name and account name. Composite names must include a
|
|
backslash character separating the domain name from the account name
|
|
(e.g. Acctg\JohnH). An isolated name may be either an account name
|
|
(user, group, or alias) or a domain name.
|
|
|
|
Translation of isolated names introduces the possibility of name
|
|
collisions (since the same name may be used in multiple domains). An
|
|
isolated name will be translated using the following algorithm:
|
|
|
|
If the name is a well-known name (e.g. Local or Interactive), then the
|
|
corresponding well-known Sid is returned.
|
|
|
|
If the name is the Built-in Domain's name, then that domain's Sid
|
|
will be returned.
|
|
|
|
If the name is the Account Domain's name, then that domain's Sid
|
|
will be returned.
|
|
|
|
If the name is the Primary Domain's name, then that domain's Sid will
|
|
be returned.
|
|
|
|
If the name is a user, group, or alias in the Built-in Domain, then the
|
|
Sid of that account is returned.
|
|
|
|
If the name is a user, group, or alias in the Primary Domain, then the
|
|
Sid of that account is returned.
|
|
|
|
Otherwise, the name is not translated.
|
|
|
|
NOTE: Proxy, Machine, and Trust user accounts are not referenced
|
|
for name translation. Only normal user accounts are used for ID
|
|
translation. If translation of other account types is needed, then
|
|
SAM services should be used directly.
|
|
|
|
Arguments:
|
|
|
|
This function is the LSA server RPC worker routine for the
|
|
LsaLookupNamesInLsa API.
|
|
|
|
PolicyHandle - Handle from an LsaOpenPolicy call.
|
|
|
|
LookupOptions - Values to pass through to LsarLookupNames2 and above
|
|
|
|
Count - Specifies the number of names to be translated.
|
|
|
|
Names - Pointer to an array of Count Unicode String structures
|
|
specifying the names to be looked up and mapped to Sids.
|
|
The strings may be names of User, Group or Alias accounts or
|
|
domains.
|
|
|
|
ReferencedDomains - receives a pointer to a structure describing the
|
|
domains used for the translation. The entries in this structure
|
|
are referenced by the structure returned via the Sids parameter.
|
|
Unlike the Sids parameter, which contains an array entry for
|
|
each translated name, this structure will only contain one
|
|
component for each domain utilized in the translation.
|
|
|
|
When this information is no longer needed, it must be released
|
|
by passing the returned pointer to LsaFreeMemory().
|
|
|
|
Sids - Receives a pointer to an array of records describing each
|
|
translated Sid. The nth entry in this array provides a translation
|
|
for (the nth element in the Names parameter.
|
|
|
|
When this information is no longer needed, it must be released
|
|
by passing the returned pointer to LsaFreeMemory().
|
|
|
|
LookupLevel - Specifies the Level of Lookup to be performed on the
|
|
target machine. Values of this field are are follows:
|
|
|
|
LsapLookupWksta - First Level Lookup performed on a workstation
|
|
normally configured for Windows-Nt. The lookup searches the
|
|
Well-Known Sids/Names, and the Built-in Domain and Account Domain
|
|
in the local SAM Database. If not all Sids or Names are
|
|
identified, performs a "handoff" of a Second level Lookup to the
|
|
LSA running on a Controller for the workstation's Primary Domain
|
|
(if any).
|
|
|
|
LsapLookupPDC - Second Level Lookup performed on a Primary Domain
|
|
Controller. The lookup searches the Account Domain of the
|
|
SAM Database on the controller. If not all Sids or Names are
|
|
found, the Trusted Domain List (TDL) is obtained from the
|
|
LSA's Policy Database and Third Level lookups are performed
|
|
via "handoff" to each Trusted Domain in the List.
|
|
|
|
LsapLookupTDL - Third Level Lookup performed on a controller
|
|
for a Trusted Domain. The lookup searches the Account Domain of
|
|
the SAM Database on the controller only.
|
|
|
|
Flags - flags to control the operation of the function. Currently defined:
|
|
|
|
LSAIC_NO_LARGE_SID -- implies only call interfaces that will return
|
|
the old style format SID (no more than
|
|
28 bytes)
|
|
|
|
LSAIC_NT4_TARGET -- target server is known to be NT4
|
|
|
|
LSAIC_WIN2K_TARGET -- target server is known to be Win2k
|
|
|
|
MappedCount - Pointer to location that contains a count of the Names
|
|
mapped so far. On exit, this count will be updated.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_SOME_NOT_MAPPED - Some or all of the names provided could
|
|
not be mapped. This is an informational status only.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
|
|
to complete the call.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
ULONG LsaLookupNameRevision = 3;
|
|
|
|
LSAPR_TRANSLATED_SIDS_EX2 ReturnedSidsEx2 = { 0, NULL };
|
|
|
|
LSAPR_TRANSLATED_SIDS_EX ReturnedSidsEx = { 0, NULL };
|
|
|
|
LSAPR_TRANSLATED_SIDS ReturnedSids = { 0, NULL };
|
|
|
|
ULONG Size, SidCount = 0;
|
|
PBYTE NextSid;
|
|
ULONG i;
|
|
|
|
ULONG StartingRevision = 3;
|
|
|
|
//
|
|
// There are no known clients who pass in a value here
|
|
//
|
|
ASSERT( *ReferencedDomains == NULL );
|
|
ASSERT( *Sids == NULL );
|
|
|
|
//
|
|
// Init to NULL since these are considered to be a IN/OUT parameters
|
|
// for the Lsar Lookup API's
|
|
//
|
|
*ReferencedDomains = NULL;
|
|
*Sids = NULL;
|
|
|
|
//
|
|
// Check that we have not specfied more than the maximum number of names
|
|
// allowed.
|
|
//
|
|
|
|
if (Count > LSAP_DB_TRIAL_MAXIMUM_NAME_COUNT) {
|
|
|
|
return(STATUS_TOO_MANY_NAMES);
|
|
}
|
|
|
|
if ( ServerRevision ) {
|
|
// The latest client's will prefer the latest servers
|
|
*ServerRevision = LSA_LOOKUP_REVISION_LATEST;
|
|
}
|
|
|
|
//
|
|
// Adjust the starting version
|
|
//
|
|
StartingRevision = 3;
|
|
if ((Flags & LSAIC_NO_LARGE_SID)
|
|
|| (Flags & LSAIC_WIN2K_TARGET) ) {
|
|
StartingRevision = 2;
|
|
}
|
|
if (Flags & LSAIC_NT4_TARGET) {
|
|
StartingRevision = 1;
|
|
}
|
|
|
|
switch (StartingRevision) {
|
|
case 3:
|
|
|
|
RpcTryExcept {
|
|
|
|
Status = LsarLookupNames3(
|
|
(LSAPR_HANDLE) PolicyHandle,
|
|
Count,
|
|
(PLSAPR_UNICODE_STRING) Names,
|
|
(PLSAPR_REFERENCED_DOMAIN_LIST *) ReferencedDomains,
|
|
&ReturnedSidsEx2,
|
|
LookupLevel,
|
|
MappedCount,
|
|
LookupOptions,
|
|
LSA_LOOKUP_REVISION_LATEST
|
|
);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
if ( (Status == RPC_NT_UNKNOWN_IF) ||
|
|
(Status == RPC_NT_PROCNUM_OUT_OF_RANGE) ) {
|
|
//
|
|
// Continue on to next block;
|
|
//
|
|
NOTHING;
|
|
} else {
|
|
//
|
|
// The interface was supported; leave
|
|
//
|
|
break;
|
|
}
|
|
|
|
case 2:
|
|
|
|
RpcTryExcept {
|
|
|
|
Status = LsarLookupNames2(
|
|
(LSAPR_HANDLE) PolicyHandle,
|
|
Count,
|
|
(PLSAPR_UNICODE_STRING) Names,
|
|
(PLSAPR_REFERENCED_DOMAIN_LIST *) ReferencedDomains,
|
|
&ReturnedSidsEx,
|
|
LookupLevel,
|
|
MappedCount,
|
|
LookupOptions,
|
|
LSA_LOOKUP_REVISION_LATEST
|
|
);
|
|
|
|
LsaLookupNameRevision = 2;
|
|
if ( ReturnedSidsEx.Sids ) {
|
|
// Memory can be allocated on !NT_SUCCESS, namely
|
|
// STATUS_NONE_MAPPED
|
|
SidCount = ReturnedSidsEx.Entries;
|
|
}
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
if ( (Status == RPC_NT_UNKNOWN_IF) ||
|
|
(Status == RPC_NT_PROCNUM_OUT_OF_RANGE) ) {
|
|
//
|
|
// Continue on to next block;
|
|
//
|
|
NOTHING;
|
|
} else {
|
|
//
|
|
// The interface was supported; leave
|
|
//
|
|
break;
|
|
}
|
|
|
|
case 1:
|
|
|
|
if ( ServerRevision ) {
|
|
*ServerRevision = LSA_LOOKUP_REVISION_1;
|
|
}
|
|
|
|
RpcTryExcept {
|
|
|
|
Status = LsarLookupNames(
|
|
(LSAPR_HANDLE) PolicyHandle,
|
|
Count,
|
|
(PLSAPR_UNICODE_STRING) Names,
|
|
(PLSAPR_REFERENCED_DOMAIN_LIST *) ReferencedDomains,
|
|
&ReturnedSids,
|
|
LookupLevel,
|
|
MappedCount
|
|
);
|
|
|
|
LsaLookupNameRevision = 1;
|
|
if ( ReturnedSids.Sids ) {
|
|
// Memory can be allocated on !NT_SUCCESS, namely
|
|
// STATUS_NONE_MAPPED
|
|
SidCount = ReturnedSids.Entries;
|
|
}
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ASSERT(FALSE && "Programming error -- invalid revision" );
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Prevent against network hacks
|
|
//
|
|
if ( NT_SUCCESS( Status )
|
|
&& (Count > 0)
|
|
&& ( (LsaLookupNameRevision == 1) && ((ReturnedSids.Entries == 0)
|
|
|| (ReturnedSids.Sids == NULL))
|
|
|| (LsaLookupNameRevision == 2) && ((ReturnedSidsEx.Entries == 0)
|
|
|| (ReturnedSidsEx.Sids == NULL))
|
|
|| (LsaLookupNameRevision == 3) && ((ReturnedSidsEx2.Entries == 0)
|
|
|| (ReturnedSidsEx2.Sids == NULL)))) {
|
|
//
|
|
// This is bogus -- an NT server would never return this
|
|
//
|
|
Status = STATUS_INVALID_NETWORK_RESPONSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Ok at this point, we have a success -- map the return values
|
|
// to the latest revision: LSA_TRANSLATES_SID_EX2
|
|
//
|
|
|
|
if ( ((LsaLookupNameRevision == 2) && ReturnedSidsEx.Sids != NULL)
|
|
|| ((LsaLookupNameRevision == 1) && ReturnedSids.Sids != NULL) ) {
|
|
|
|
//
|
|
// There should be a ReferencedDomains
|
|
//
|
|
ASSERT( NULL != *ReferencedDomains);
|
|
|
|
//
|
|
// Calculate the size necessary. All SID's from non Sid-Extended domains
|
|
// will be less than 28 bytes. However, we still sanity check the values
|
|
// returned from the untrusted net before copying in (see below).
|
|
//
|
|
|
|
#define MAX_DOWNLEVEL_SID_SIZE 28
|
|
|
|
//
|
|
// Since we are returning a buffer containing all allocations, make sure
|
|
// everything aligned properly
|
|
//
|
|
|
|
ASSERT(MAX_DOWNLEVEL_SID_SIZE ==
|
|
ROUND_UP_COUNT(MAX_DOWNLEVEL_SID_SIZE, ALIGN_DWORD));
|
|
ASSERT(sizeof(LSA_TRANSLATED_SID_EX2) ==
|
|
ROUND_UP_COUNT(sizeof(LSA_TRANSLATED_SID_EX2), ALIGN_DWORD));
|
|
|
|
Size = SidCount * sizeof(LSA_TRANSLATED_SID_EX2);
|
|
Size += SidCount * MAX_DOWNLEVEL_SID_SIZE;
|
|
ReturnedSidsEx2.Sids = MIDL_user_allocate(Size);
|
|
if (NULL == ReturnedSidsEx2.Sids) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
RtlZeroMemory(ReturnedSidsEx2.Sids, Size);
|
|
NextSid = (PBYTE) ReturnedSidsEx2.Sids;
|
|
NextSid += (SidCount * sizeof(LSA_TRANSLATED_SID_EX2));
|
|
|
|
for ( i = 0; i < SidCount; i++ ) {
|
|
|
|
BYTE Buffer[MAX_DOWNLEVEL_SID_SIZE];
|
|
PSID Sid = (PSID)Buffer;
|
|
ULONG SidLength;
|
|
ULONG DomainIndex;
|
|
ULONG Rid;
|
|
SID_NAME_USE SidNameUse;
|
|
ULONG Flags;
|
|
PSID DomainSid;
|
|
|
|
if (1 == LsaLookupNameRevision) {
|
|
|
|
DomainIndex = ReturnedSids.Sids[i].DomainIndex;
|
|
Rid = ReturnedSids.Sids[i].RelativeId;
|
|
SidNameUse = ReturnedSids.Sids[i].Use;
|
|
Flags = 0;
|
|
|
|
} else {
|
|
|
|
ASSERT( 2 == LsaLookupNameRevision );
|
|
|
|
DomainIndex = ReturnedSidsEx.Sids[i].DomainIndex;
|
|
Rid = ReturnedSidsEx.Sids[i].RelativeId;
|
|
SidNameUse = ReturnedSidsEx.Sids[i].Use;
|
|
Flags = ReturnedSidsEx.Sids[i].Flags;
|
|
}
|
|
|
|
//
|
|
// Copy over the simple values
|
|
//
|
|
ReturnedSidsEx2.Sids[i].Use = SidNameUse;
|
|
ReturnedSidsEx2.Sids[i].DomainIndex = DomainIndex;
|
|
ReturnedSidsEx2.Sids[i].Flags = Flags;
|
|
|
|
//
|
|
// Copy over the sid if possible
|
|
//
|
|
// To support possible additions in the future, check for the negative cases.
|
|
// These types will never have any SIDs; all others must have a SID portion.
|
|
//
|
|
if ( (SidNameUse != SidTypeDeletedAccount)
|
|
&& (SidNameUse != SidTypeInvalid)
|
|
&& (SidNameUse != SidTypeUnknown) ) {
|
|
|
|
if (DomainIndex == LSA_UNKNOWN_INDEX) {
|
|
//
|
|
// This is a bogus return value
|
|
//
|
|
Status = STATUS_INVALID_NETWORK_RESPONSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// N.B. For domain names, the RID is set to LSA_UNKNOWN_ID and
|
|
// to be compatible with the LsarLookupName3 routine, return
|
|
// a SID in ReturedSidsEx2 structure.
|
|
//
|
|
DomainSid = (*ReferencedDomains)->Domains[DomainIndex].Sid;
|
|
if (RtlLengthSid(DomainSid) > (MAX_DOWNLEVEL_SID_SIZE - sizeof(DWORD))){
|
|
//
|
|
// This is a bogus return value
|
|
//
|
|
Status = STATUS_INVALID_NETWORK_RESPONSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlCopySid(sizeof(Buffer), Sid, DomainSid);
|
|
if ( Rid != LSA_UNKNOWN_ID ) {
|
|
ULONG RidAuthority;
|
|
RidAuthority= (*(RtlSubAuthorityCountSid(Sid)))++;
|
|
*RtlSubAuthoritySid(Sid,RidAuthority) = Rid;
|
|
}
|
|
|
|
SidLength = RtlLengthSid(Sid);
|
|
RtlCopySid(SidLength, (PSID)NextSid, Sid);
|
|
|
|
ReturnedSidsEx2.Sids[i].Sid = (PSID)NextSid;
|
|
NextSid += RtlLengthSid(Sid);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Either no domain SID, or account is unknown
|
|
//
|
|
ReturnedSidsEx2.Sids[i].Sid = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
*Sids = (PLSA_TRANSLATED_SID_EX2) ReturnedSidsEx2.Sids;
|
|
ReturnedSidsEx2.Sids = NULL;
|
|
|
|
Cleanup:
|
|
|
|
if ( (STATUS_INVALID_NETWORK_RESPONSE == Status)
|
|
|| (STATUS_NO_MEMORY == Status) ) {
|
|
if ( *ReferencedDomains ) {
|
|
MIDL_user_free( *ReferencedDomains );
|
|
*ReferencedDomains = NULL;
|
|
}
|
|
*Sids = NULL;
|
|
}
|
|
|
|
if ( ReturnedSids.Sids ) {
|
|
MIDL_user_free( ReturnedSids.Sids );
|
|
}
|
|
if ( ReturnedSidsEx.Sids ) {
|
|
MIDL_user_free( ReturnedSidsEx.Sids );
|
|
}
|
|
if ( ReturnedSidsEx2.Sids ) {
|
|
MIDL_user_free( ReturnedSidsEx2.Sids );
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaLookupSids(
|
|
IN LSA_HANDLE PolicyHandle,
|
|
IN ULONG Count,
|
|
IN PSID *Sids,
|
|
OUT PLSA_REFERENCED_DOMAIN_LIST *ReferencedDomains,
|
|
OUT PLSA_TRANSLATED_NAME *Names
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaLookupSids API attempts to find names corresponding to Sids.
|
|
If a name can not be mapped to a Sid, the Sid is converted to character
|
|
form. The caller must have POLICY_LOOKUP_NAMES access to the Policy
|
|
object.
|
|
|
|
WARNING: This routine allocates memory for its output. The caller is
|
|
responsible for freeing this memory after use. See description of the
|
|
Names parameter.
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - Handle from an LsaOpenPolicy call.
|
|
|
|
Count - Specifies the number of Sids to be translated.
|
|
|
|
Sids - Pointer to an array of Count pointers to Sids to be mapped
|
|
to names. The Sids may be well_known SIDs, SIDs of User accounts
|
|
Group Accounts, Alias accounts, or Domains.
|
|
|
|
ReferencedDomains - Receives a pointer to a structure describing the
|
|
domains used for the translation. The entries in this structure
|
|
are referenced by the strutcure returned via the Names parameter.
|
|
Unlike the Names paraemeter, which contains an array entry
|
|
for (each translated name, this strutcure will only contain
|
|
component for each domain utilized in the translation.
|
|
|
|
When this information is no longer needed, it must be released
|
|
by passing the returned pointer to LsaFreeMemory().
|
|
|
|
Names - Receives a pointer to array records describing each translated
|
|
name. The nth entry in this array provides a translation for
|
|
the nth entry in the Sids parameter.
|
|
|
|
All of the returned names will be isolated names or NULL strings
|
|
(domain names are returned as NULL strings). If the caller needs
|
|
composite names, they can be generated by prepending the
|
|
isolated name with the domain name and a backslash. For example,
|
|
if (the name Sally is returned, and it is from the domain Manufact,
|
|
then the composite name would be "Manufact" + "\" + "Sally" or
|
|
"Manufact\Sally".
|
|
|
|
When this information is no longer needed, it must be released
|
|
by passing the returned pointer to LsaFreeMemory().
|
|
|
|
If a Sid is not translatable, then the following will occur:
|
|
|
|
1) If the SID's domain is known, then a reference domain record
|
|
will be generated with the domain's name. In this case, the
|
|
name returned via the Names parameter is a Unicode representation
|
|
of the relative ID of the account, such as "(314)" or the null
|
|
string, if the Sid is that of a domain. So, you might end up
|
|
with a resultant name of "Manufact\(314) for the example with
|
|
Sally above, if Sally's relative id is 314.
|
|
|
|
2) If not even the SID's domain could be located, then a full
|
|
Unicode representation of the SID is generated and no domain
|
|
record is referenced. In this case, the returned string might
|
|
be something like: "(S-1-672194-21-314)".
|
|
|
|
When this information is no longer needed, it must be released
|
|
by passing the returned pointer to LsaFreeMemory().
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_SOME_NOT_MAPPED - Some or all of the names provided could not be
|
|
mapped. This is a warning only.
|
|
|
|
Rest TBS
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG MappedCount = 0;
|
|
PLSA_TRANSLATED_NAME_EX NamesEx = NULL;
|
|
ULONG i;
|
|
|
|
if ( NULL == Names ) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
Status = LsaICLookupSids(
|
|
PolicyHandle,
|
|
Count,
|
|
Sids,
|
|
ReferencedDomains,
|
|
&NamesEx,
|
|
LsapLookupWksta,
|
|
0,
|
|
&MappedCount,
|
|
NULL
|
|
);
|
|
|
|
if ( NamesEx != NULL ) {
|
|
|
|
//
|
|
// Some names were returned -- map the new structure to the old one
|
|
// and keep allocations in the same block of memory so existing clients
|
|
// won't have memory leaks
|
|
//
|
|
ULONG SizeNeeded = 0;
|
|
PBYTE NextBuffer;
|
|
PLSA_TRANSLATED_NAME TempNames = NULL;
|
|
|
|
SizeNeeded = Count * sizeof( LSA_TRANSLATED_NAME );
|
|
for ( i = 0; i < Count; i++ ) {
|
|
SizeNeeded += NamesEx[i].Name.MaximumLength;
|
|
}
|
|
|
|
TempNames = MIDL_user_allocate( SizeNeeded );
|
|
if ( TempNames ) {
|
|
|
|
RtlZeroMemory( TempNames, SizeNeeded );
|
|
NextBuffer = ((PBYTE)TempNames) + (Count * sizeof( LSA_TRANSLATED_NAME ));
|
|
|
|
for ( i = 0; i < Count; i++ ) {
|
|
|
|
TempNames[i].Use = NamesEx[i].Use;
|
|
TempNames[i].DomainIndex = NamesEx[i].DomainIndex;
|
|
|
|
TempNames[i].Name = NamesEx[i].Name;
|
|
RtlCopyMemory( NextBuffer, NamesEx[i].Name.Buffer, NamesEx[i].Name.Length );
|
|
TempNames[i].Name.Buffer = (WCHAR*)NextBuffer;
|
|
|
|
NextBuffer += NamesEx[i].Name.MaximumLength;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// The call succeeded but the extra allocation didn't
|
|
//
|
|
if ( *ReferencedDomains ) {
|
|
MIDL_user_free( *ReferencedDomains );
|
|
*ReferencedDomains = NULL;
|
|
}
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Return the results (or NULL)
|
|
//
|
|
*Names = TempNames;
|
|
|
|
MIDL_user_free( NamesEx );
|
|
|
|
} else {
|
|
|
|
*Names = NULL;
|
|
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapVerifyReturnedNames(
|
|
IN LSAPR_TRANSLATED_NAMES_EX *ReturnedNames,
|
|
IN ULONG Count,
|
|
IN PLSA_REFERENCED_DOMAIN_LIST ReferencedDomains
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine validates the returned names structure from the server.
|
|
There are some checks that RPC can't make that the client assumes are
|
|
true and will AV otherwise.
|
|
|
|
Arguments:
|
|
|
|
Count -- the number of elements the client asked the server to resolve
|
|
|
|
ReturnedNames -- the structure holding the data returned from the server
|
|
|
|
ReferencedDomains -- the array of domains that ReturnedNames points into
|
|
(also returned from the server)
|
|
|
|
Return Values:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
STATUS_INVALID_NETWORK_RESPONSE
|
|
|
|
--*/
|
|
{
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG i;
|
|
|
|
if (ReturnedNames->Entries != Count) {
|
|
|
|
//
|
|
// Entries returned should always equal the number of items asked for
|
|
//
|
|
|
|
Status = STATUS_INVALID_NETWORK_RESPONSE;
|
|
goto Finish;
|
|
}
|
|
|
|
if ( Count > 0
|
|
&& (ReturnedNames->Names == NULL)) {
|
|
|
|
//
|
|
// If there are entries, then there must be an array
|
|
//
|
|
|
|
Status = STATUS_INVALID_NETWORK_RESPONSE;
|
|
goto Finish;
|
|
|
|
}
|
|
|
|
for (i = 0; i < Count; i++) {
|
|
|
|
//
|
|
// All resolved names must have a valid domain index
|
|
//
|
|
if ( (ReturnedNames->Names[i].Use != SidTypeInvalid) &&
|
|
(ReturnedNames->Names[i].Use != SidTypeDeletedAccount) &&
|
|
(ReturnedNames->Names[i].Use != SidTypeUnknown) ) {
|
|
|
|
if (NULL == ReferencedDomains) {
|
|
Status = STATUS_INVALID_NETWORK_RESPONSE;
|
|
goto Finish;
|
|
} else if ( (ReturnedNames->Names[i].DomainIndex == LSA_UNKNOWN_INDEX)
|
|
|| (ReturnedNames->Names[i].DomainIndex < 0)
|
|
|| ((ULONG)ReturnedNames->Names[i].DomainIndex >= ReferencedDomains->Entries)) {
|
|
ASSERT(FALSE && "Invalid network response!");
|
|
Status = STATUS_INVALID_NETWORK_RESPONSE;
|
|
goto Finish;
|
|
}
|
|
}
|
|
}
|
|
|
|
Finish:
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsaICLookupSids(
|
|
IN LSA_HANDLE PolicyHandle,
|
|
IN ULONG Count,
|
|
IN PSID *Sids,
|
|
OUT PLSA_REFERENCED_DOMAIN_LIST *ReferencedDomains,
|
|
IN OUT PLSA_TRANSLATED_NAME_EX *Names,
|
|
IN LSAP_LOOKUP_LEVEL LookupLevel,
|
|
IN ULONG Flags,
|
|
IN OUT PULONG MappedCount,
|
|
OUT ULONG *ServerRevision OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is the internal client side version of the LsaLookupSids
|
|
API. It is called both from the client side of the Lsa and also
|
|
the server side of the LSA (when calling out to another LSA). The
|
|
function is identical to the LsaLookupSids API except that there is an
|
|
additional parameter, the LookupLevel parameter.
|
|
|
|
The LsaLookupSids API attempts to find names corresponding to Sids.
|
|
If a name can not be mapped to a Sid, the Sid is converted to character
|
|
form. The caller must have POLICY_LOOKUP_NAMES access to the Policy
|
|
object.
|
|
|
|
WARNING: This routine allocates memory for its output. The caller is
|
|
responsible for freeing this memory after use. See description of the
|
|
Names parameter.
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - Handle from an LsaOpenPolicy call.
|
|
|
|
Count - Specifies the number of Sids to be translated.
|
|
|
|
Sids - Pointer to an array of Count pointers to Sids to be mapped
|
|
to names. The Sids may be well_known SIDs, SIDs of User accounts
|
|
Group Accounts, Alias accounts, or Domains.
|
|
|
|
ReferencedDomains - Receives a pointer to a structure describing the
|
|
domains used for the translation. The entries in this structure
|
|
are referenced by the strutcure returned via the Names parameter.
|
|
Unlike the Names paraemeter, which contains an array entry
|
|
for (each translated name, this strutcure will only contain
|
|
component for each domain utilized in the translation.
|
|
|
|
When this information is no longer needed, it must be released
|
|
by passing the returned pointer to LsaFreeMemory().
|
|
|
|
Names - Receives a pointer to array records describing each translated
|
|
name. The nth entry in this array provides a translation for
|
|
the nth entry in the Sids parameter.
|
|
|
|
All of the retruned names will be isolated names or NULL strings
|
|
(domain names are returned as NULL strings). If the caller needs
|
|
composite names, they can be generated by prepending the
|
|
isolated name with the domain name and a backslash. For example,
|
|
if (the name Sally is returned, and it is from the domain Manufact,
|
|
then the composite name would be "Manufact" + "\" + "Sally" or
|
|
"Manufact\Sally".
|
|
|
|
When this information is no longer needed, it must be released
|
|
by passing the returned pointer to LsaFreeMemory().
|
|
|
|
If a Sid is not translatable, then the following will occur:
|
|
|
|
1) If the SID's domain is known, then a reference domain record
|
|
will be generated with the domain's name. In this case, the
|
|
name returned via the Names parameter is a Unicode representation
|
|
of the relative ID of the account, such as "(314)" or the null
|
|
string, if the Sid is that of a domain. So, you might end up
|
|
with a resultant name of "Manufact\(314) for the example with
|
|
Sally above, if Sally's relative id is 314.
|
|
|
|
2) If not even the SID's domain could be located, then a full
|
|
Unicode representation of the SID is generated and no domain
|
|
record is referenced. In this case, the returned string might
|
|
be something like: "(S-1-672194-21-314)".
|
|
|
|
When this information is no longer needed, it must be released
|
|
by passing the returned pointer to LsaFreeMemory().
|
|
|
|
LookupLevel - Specifies the Level of Lookup to be performed on the
|
|
target machine. Values of this field are are follows:
|
|
|
|
LsapLookupWksta - First Level Lookup performed on a workstation
|
|
normally configured for Windows-Nt. The lookup searches the
|
|
Well-Known Sids, and the Built-in Domain and Account Domain
|
|
in the local SAM Database. If not all Sids are
|
|
identified, performs a "handoff" of a Second level Lookup to the
|
|
LSA running on a Controller for the workstation's Primary Domain
|
|
(if any).
|
|
|
|
LsapLookupPDC - Second Level Lookup performed on a Primary Domain
|
|
Controller. The lookup searches the Account Domain of the
|
|
SAM Database on the controller. If not all Sids are
|
|
found, the Trusted Domain List (TDL) is obtained from the
|
|
LSA's Policy Database and Third Level lookups are performed
|
|
via "handoff" to each Trusted Domain in the List.
|
|
|
|
LsapLookupTDL - Third Level Lookup performed on a controller
|
|
for a Trusted Domain. The lookup searches the Account Domain of
|
|
the SAM Database on the controller only.
|
|
|
|
Flags:
|
|
LSAIC_NT4_TARGET -- target server is known to be NT4
|
|
|
|
LSAIC_WIN2K_TARGET -- target server is known to be Win2k
|
|
|
|
MappedCount - Pointer to location that contains a count of the Sids
|
|
mapped so far. On exit, this count will be updated.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_SOME_NOT_MAPPED - Some or all of the names provided could not be
|
|
mapped. This is a warning only.
|
|
|
|
STATUS_TOO_MANY_SIDS - Too many Sids have been specified.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
BOOLEAN NamesArraySpecified = FALSE;
|
|
|
|
LSAPR_SID_ENUM_BUFFER SidEnumBuffer;
|
|
|
|
LSAPR_TRANSLATED_NAMES_EX ReturnedNames = {0, NULL};
|
|
|
|
LSAPR_TRANSLATED_NAMES DownlevelNames = {0, NULL};
|
|
|
|
ULONG StartingRevision = 2;
|
|
|
|
if ( ServerRevision ) {
|
|
// The latest client's will prefer the latest servers
|
|
*ServerRevision = LSA_CLIENT_LATEST;
|
|
}
|
|
|
|
//
|
|
// Verify that the Count is positive and not too high
|
|
//
|
|
|
|
if (Count == 0) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (Count > LSAP_DB_TRIAL_MAXIMUM_SID_COUNT) {
|
|
|
|
return STATUS_TOO_MANY_SIDS;
|
|
}
|
|
|
|
SidEnumBuffer.Entries = Count;
|
|
SidEnumBuffer.SidInfo = (PLSAPR_SID_INFORMATION) Sids;
|
|
|
|
//
|
|
// If this is a Workstation-Level lookup, the Names and
|
|
// ReferencedDomain Lists have not been created. Since these
|
|
// are input parameters in the general case, we need to set them
|
|
// to NULL.
|
|
//
|
|
|
|
if (LookupLevel == LsapLookupWksta) {
|
|
|
|
*ReferencedDomains = NULL;
|
|
*Names = NULL;
|
|
}
|
|
|
|
//
|
|
// There may already be a name translation array in cases where
|
|
// we are called internally (i.e. with lookup level higher than
|
|
// LsapLookupWksta). Initialize the ReturnedNames structure
|
|
// accordingly.
|
|
//
|
|
|
|
ReturnedNames.Entries = 0;
|
|
ReturnedNames.Names = NULL;
|
|
|
|
if (*Names != NULL) {
|
|
|
|
ReturnedNames.Entries = Count;
|
|
ReturnedNames.Names = (PLSAPR_TRANSLATED_NAME_EX) *Names;
|
|
NamesArraySpecified = TRUE;
|
|
}
|
|
|
|
//
|
|
// Adjust the StartingRevision
|
|
//
|
|
StartingRevision = 2;
|
|
if (Flags & LSAIC_NT4_TARGET) {
|
|
StartingRevision = 1;
|
|
}
|
|
|
|
//
|
|
// Lookup Sids on the Server..
|
|
//
|
|
|
|
switch (StartingRevision) {
|
|
case 2:
|
|
|
|
RpcTryExcept {
|
|
Status = LsarLookupSids2(
|
|
(LSAPR_HANDLE) PolicyHandle,
|
|
&SidEnumBuffer,
|
|
(PLSAPR_REFERENCED_DOMAIN_LIST *) ReferencedDomains,
|
|
&ReturnedNames,
|
|
LookupLevel,
|
|
MappedCount,
|
|
0,
|
|
LSA_CLIENT_NT5
|
|
);
|
|
|
|
//
|
|
// Return the array of translation to name info or NULL.
|
|
//
|
|
// NOTE: The array of name translations is allocated by the called
|
|
// client stub as a single block via MIDL_user_allocate, because
|
|
// Information is allocated all-nodes. We can therefore pass back the pointer
|
|
// directly to the client, who will be able to free the memory after
|
|
// use via LsaFreeMemory() [which makes a MIDL_user_free call].
|
|
//
|
|
|
|
*Names = (PLSA_TRANSLATED_NAME_EX) ReturnedNames.Names;
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
//
|
|
// If memory was allocated for the name translation array,
|
|
// free it.
|
|
//
|
|
|
|
if ((!NamesArraySpecified) && ReturnedNames.Names != NULL) {
|
|
|
|
MIDL_user_free( ReturnedNames.Names );
|
|
ReturnedNames.Names = NULL;
|
|
}
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
if ( (Status == RPC_NT_UNKNOWN_IF) ||
|
|
(Status == RPC_NT_PROCNUM_OUT_OF_RANGE) ) {
|
|
//
|
|
// Continue on to next block;
|
|
//
|
|
NOTHING;
|
|
} else {
|
|
//
|
|
// The interface was supported; leave
|
|
//
|
|
break;
|
|
}
|
|
|
|
case 1:
|
|
|
|
if ( ServerRevision ) {
|
|
// This is pre nt5
|
|
*ServerRevision = LSA_CLIENT_PRE_NT5;
|
|
}
|
|
|
|
RpcTryExcept {
|
|
|
|
//
|
|
// Ok, lower down to the previous version
|
|
//
|
|
Status = LsarLookupSids(
|
|
(LSAPR_HANDLE) PolicyHandle,
|
|
&SidEnumBuffer,
|
|
(PLSAPR_REFERENCED_DOMAIN_LIST *) ReferencedDomains,
|
|
&DownlevelNames,
|
|
LookupLevel,
|
|
MappedCount
|
|
);
|
|
|
|
if ( DownlevelNames.Names != NULL ) {
|
|
|
|
ULONG i;
|
|
ULONG SizeNeeded = 0;
|
|
PBYTE NextBuffer;
|
|
|
|
//
|
|
// Package the results into a new structure. Note all memory
|
|
// must be in the same block as LSA_TRANSLATED_NAMES and
|
|
// LSA_TRANSLATED_NAMES_EX are NOT allocate all nodes
|
|
//
|
|
SizeNeeded = DownlevelNames.Entries * sizeof( LSA_TRANSLATED_NAME_EX );
|
|
for ( i = 0; i < DownlevelNames.Entries; i++ ) {
|
|
SizeNeeded += DownlevelNames.Names[i].Name.MaximumLength;
|
|
}
|
|
if ( !NamesArraySpecified ) {
|
|
ReturnedNames.Names = MIDL_user_allocate( SizeNeeded );
|
|
if ( !ReturnedNames.Names ) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
_leave;
|
|
}
|
|
RtlZeroMemory( ReturnedNames.Names, SizeNeeded );
|
|
ReturnedNames.Entries = Count;
|
|
}
|
|
NextBuffer = ((PBYTE)ReturnedNames.Names) + (Count * sizeof( LSA_TRANSLATED_NAME_EX ));
|
|
|
|
ReturnedNames.Entries = DownlevelNames.Entries;
|
|
for ( i = 0; i < DownlevelNames.Entries; i++ ) {
|
|
|
|
ReturnedNames.Names[i].Use = DownlevelNames.Names[i].Use;
|
|
ReturnedNames.Names[i].Name = DownlevelNames.Names[i].Name;
|
|
RtlCopyMemory( NextBuffer, DownlevelNames.Names[i].Name.Buffer, DownlevelNames.Names[i].Name.Length );
|
|
ReturnedNames.Names[i].Name.Buffer = (WCHAR*)NextBuffer;
|
|
ReturnedNames.Names[i].DomainIndex = DownlevelNames.Names[i].DomainIndex;
|
|
NextBuffer += DownlevelNames.Names[i].Name.MaximumLength;
|
|
}
|
|
}
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
if ( DownlevelNames.Names ) {
|
|
|
|
MIDL_user_free( DownlevelNames.Names );
|
|
DownlevelNames.Names = NULL;
|
|
}
|
|
|
|
if ((!NamesArraySpecified) && ReturnedNames.Names != NULL) {
|
|
|
|
MIDL_user_free( ReturnedNames.Names );
|
|
ReturnedNames.Names = NULL;
|
|
}
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE && "Programming error -- wrong revision");
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
//
|
|
// Return the array of translation to name info or NULL.
|
|
//
|
|
// NOTE: The array of name translations is allocated by the called
|
|
// client stub as a single block via MIDL_user_allocate, because
|
|
// Information is allocated all-nodes. We can therefore pass back the pointer
|
|
// directly to the client, who will be able to free the memory after
|
|
// use via LsaFreeMemory() [which makes a MIDL_user_free call].
|
|
//
|
|
|
|
*Names = (PLSA_TRANSLATED_NAME_EX) ReturnedNames.Names;
|
|
|
|
//
|
|
// This memory, if allocated is never returned to the caller, so free
|
|
//
|
|
if ( DownlevelNames.Names ) {
|
|
|
|
MIDL_user_free( DownlevelNames.Names );
|
|
|
|
}
|
|
|
|
//
|
|
// Prevent against network hacks
|
|
//
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
Status = LsapVerifyReturnedNames(&ReturnedNames,
|
|
Count,
|
|
*ReferencedDomains);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
if ( (!NamesArraySpecified) && ReturnedNames.Names ) {
|
|
MIDL_user_free( ReturnedNames.Names );
|
|
}
|
|
|
|
if ( *ReferencedDomains ) {
|
|
MIDL_user_free( *ReferencedDomains );
|
|
*ReferencedDomains = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaOpenAccount(
|
|
IN LSA_HANDLE PolicyHandle,
|
|
IN PSID AccountSid,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT PLSA_HANDLE AccountHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaOpenAccount API opens an account object in the Lsa Database of the
|
|
target system. An account must be opened before any operation can be
|
|
performed, including deletion of the account. A handle to the account
|
|
object is returned for use on subsequent API calls that access the
|
|
account. Before calling this API, the caller must have connected to
|
|
the target system's LSA and opened the Policy object by means
|
|
of a preceding call to LsaOpenPolicy.
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - Handle from an LsaOpenLsa call.
|
|
|
|
AccountSid - Pointer to the account's Sid.
|
|
|
|
DesiredAccess - This is an access mask indicating accesses being
|
|
requested for the LSA Subsystem's LSA Database. These access types
|
|
are reconciled with the Discretionary Access Control List of the
|
|
target Account object to determine whether the accesses will be
|
|
granted or denied.
|
|
|
|
AccountHandle - Pointer to location in which a handle to the opened
|
|
account object will be returned if the call succeeds.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_ACCOUNT_DOES_NOT_EXIST - There is no account object in the
|
|
target system's LSA Database having the specified AccountSid.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
RpcTryExcept {
|
|
|
|
Status = LsarOpenAccount(
|
|
(LSAPR_HANDLE) PolicyHandle,
|
|
(PLSAPR_SID) AccountSid,
|
|
DesiredAccess,
|
|
(PLSAPR_HANDLE) AccountHandle
|
|
);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaEnumeratePrivilegesOfAccount(
|
|
IN LSA_HANDLE AccountHandle,
|
|
OUT PPRIVILEGE_SET *Privileges
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaEnumeratePrivilegesOfAccount API obtains information which
|
|
describes the privileges assigned to an account. This call requires
|
|
LSA_ACCOUNT_VIEW access to the account object.
|
|
|
|
Arguments:
|
|
|
|
AccountHandle - The handle to the open account object whose privilege
|
|
information is to be obtained. This handle will have been returned
|
|
from a prior LsaOpenAccount or LsaCreateAccountInLsa API call.
|
|
|
|
Privileges - Receives a pointer to a buffer containing the Privilege
|
|
Set. The Privilege Set is an array of structures, one for each
|
|
privilege. Each structure contains the LUID of the privilege and
|
|
a mask of the privilege's attributes.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_INVALID_HANDLE - The specified AccountHandle is not valid.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
RpcTryExcept {
|
|
|
|
*Privileges = NULL;
|
|
|
|
Status = LsarEnumeratePrivilegesAccount(
|
|
(LSAPR_HANDLE) AccountHandle,
|
|
(PLSAPR_PRIVILEGE_SET *) Privileges
|
|
);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaAddPrivilegesToAccount(
|
|
IN LSA_HANDLE AccountHandle,
|
|
IN PPRIVILEGE_SET Privileges
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaAddPrivilegesToAccount API adds privileges and their attributes
|
|
to an account object. If any provided privilege is already assigned
|
|
to the account object, the attributes of that privilege are replaced
|
|
by the newly rpovided values. This API call requires
|
|
LSA_ACCOUNT_ADJUST_PRIVILEGES access to the account object.
|
|
|
|
Arguments:
|
|
|
|
AccountHandle - The handle to the open account object to which
|
|
privileges are to be added. This handle will have been returned
|
|
from a prior LsaOpenAccount or LsaCreateAccountInLsa API call.
|
|
|
|
Privileges - Points to a set of privileges (and their attributes) to
|
|
be assigned to the account.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_INVALID_HANDLE - The specified AccountHandle is not valid.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
RpcTryExcept {
|
|
|
|
Status = LsarAddPrivilegesToAccount(
|
|
(LSAPR_HANDLE) AccountHandle,
|
|
(PLSAPR_PRIVILEGE_SET) Privileges
|
|
);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaRemovePrivilegesFromAccount(
|
|
IN LSA_HANDLE AccountHandle,
|
|
IN BOOLEAN AllPrivileges,
|
|
IN OPTIONAL PPRIVILEGE_SET Privileges
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaRemovePrivilegesFromAccount API removes privileges from an
|
|
account object. This API call requires LSA_ACCOUNT_ADJUST_PRIVILEGES
|
|
access to the account object. Note that if all privileges are removed
|
|
from the account object, the account object remains in existence until
|
|
deleted explicitly via a call to the LsaDelete API.
|
|
|
|
Arguments:
|
|
|
|
AccountHandle - The handle to the open account object to which
|
|
privileges are to be removed. This handle will have been returned
|
|
from a prior LsaOpenAccount or LsaCreateAccountInLsa API call.
|
|
|
|
AllPrivileges - If TRUE, then all privileges are to be removed from
|
|
the account. In this case, the Privileges parameter must be
|
|
specified as NULL. If FALSE, the Privileges parameter specifies
|
|
the privileges to be removed, and must be non NULL.
|
|
|
|
Privileges - Optionally points to a set of privileges (and their
|
|
attributes) to be removed from the account object. The attributes
|
|
fields of this structure are ignored. This parameter must
|
|
be specified as non-NULL if and only if AllPrivileges is set to
|
|
FALSE.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_INVALID_HANDLE - The specified AccountHandle is not valid.
|
|
|
|
STATUS_INVALID_PARAMETER - The optional Privileges paraemter was
|
|
specified as NULL and AllPrivileges was set to FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
RpcTryExcept {
|
|
|
|
Status = LsarRemovePrivilegesFromAccount(
|
|
(LSAPR_HANDLE) AccountHandle,
|
|
AllPrivileges,
|
|
(PLSAPR_PRIVILEGE_SET) Privileges
|
|
);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaGetQuotasForAccount(
|
|
IN LSA_HANDLE AccountHandle,
|
|
OUT PQUOTA_LIMITS QuotaLimits
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaGetQuotasForAccount API obtains the quota limits for pageable and
|
|
non-pageable memory (in Kilobytes) and the maximum execution time (in
|
|
seconds) for any session logged on to the account specified by
|
|
AccountHandle. For each quota and explicit value is returned. This
|
|
call requires LSA_ACCOUNT_VIEW access to the account object.
|
|
|
|
Arguments:
|
|
|
|
AccountHandle - The handle to the open account object whose quotas
|
|
are to be obtained. This handle will have been returned
|
|
from a prior LsaOpenAccount or LsaCreateAccountInLsa API call.
|
|
|
|
QuotaLimits - Pointer to structure in which the system resource
|
|
quota limits applicable to each session logged on to this account
|
|
will be returned. Note that all quotas, including those specified
|
|
as being the system default values, are returned as actual values.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_INVALID_HANDLE - The specified AccountHandle is not valid.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
RpcTryExcept{
|
|
|
|
Status = LsarGetQuotasForAccount(
|
|
(LSAPR_HANDLE) AccountHandle,
|
|
QuotaLimits
|
|
);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaSetQuotasForAccount(
|
|
IN LSA_HANDLE AccountHandle,
|
|
IN PQUOTA_LIMITS QuotaLimits
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaSetQuotasForAccount API sets the quota limits for pageable and
|
|
non-pageable memory (in Kilobytes) and the maximum execution time (in
|
|
seconds) for any session logged on to the account specified by
|
|
AccountHandle. For each quota an explicit value or the system default
|
|
may be specified. This call requires LSA_ACCOUNT_ADJUST_QUOTAS
|
|
access to the account object.
|
|
|
|
Arguments:
|
|
|
|
AccountHandle - The handle to the open account object whose quotas
|
|
are to be set. This handle will have been returned from a prior
|
|
LsaOpenAccount or LsaCreateAccountInLsa API call.
|
|
|
|
QuotaLimits - Pointer to structure containing the system resource
|
|
quota limits applicable to each session logged on to this account.
|
|
A zero value specified in any field indicates that the current
|
|
System Default Quota Limit is to be applied.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_INVALID_HANDLE - The specified AccountHandle is not valid.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
RpcTryExcept {
|
|
|
|
Status = LsarSetQuotasForAccount(
|
|
(LSAPR_HANDLE) AccountHandle,
|
|
QuotaLimits
|
|
);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaGetSystemAccessAccount(
|
|
IN LSA_HANDLE AccountHandle,
|
|
OUT PULONG SystemAccess
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaGetSystemAccessAccount() service returns the System Access
|
|
account flags for an Account object.
|
|
|
|
Arguments:
|
|
|
|
AccountHandle - The handle to the Account object whose system access
|
|
flags are to be read. This handle will have been returned
|
|
from a preceding LsaOpenAccount() or LsaCreateAccount() call
|
|
an must be open for ACCOUNT_VIEW access.
|
|
|
|
SystemAccess - Points to location that will receive the system access
|
|
flags for the account.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call was successful.
|
|
|
|
STATUS_ACCESS_DENIED - The AccountHandle does not specify
|
|
ACCOUNT_VIEW access.
|
|
|
|
STATUS_INVALID_HANDLE - The specified AccountHandle is invalid.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Avoid RPC stub code raising exception on NULL handle so that
|
|
// we can return the error code STATUS_INVALID_HANDLE in this case
|
|
// too.
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT(AccountHandle)) {
|
|
|
|
return(STATUS_INVALID_HANDLE);
|
|
}
|
|
|
|
RpcTryExcept{
|
|
|
|
Status = LsarGetSystemAccessAccount(
|
|
(LSAPR_HANDLE) AccountHandle,
|
|
SystemAccess
|
|
);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaSetSystemAccessAccount(
|
|
IN LSA_HANDLE AccountHandle,
|
|
IN ULONG SystemAccess
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaSetSystemAccessAccount() service sets the System Access
|
|
account flags for an Account object.
|
|
|
|
Arguments:
|
|
|
|
AccountHandle - The handle to the Account object whose system access
|
|
flags are to be read. This handle will have been returned
|
|
from a preceding LsaOpenAccount() or LsaCreateAccount() call
|
|
an must be open for ACCOUNT_ADJUST_SYSTEM_ACCESS access.
|
|
|
|
SystemAccess - A mask of the system access flags to assign to the
|
|
Account object. The valid access flags include:
|
|
|
|
POLICY_MODE_INTERACTIVE - Account can be accessed interactively
|
|
|
|
POLICY_MODE_NETWORK - Account can be accessed remotely
|
|
|
|
POLICY_MODE_SERVICE - TBS
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call was successful.
|
|
|
|
STATUS_ACCESS_DENIED - The AccountHandle does not specify
|
|
ACCOUNT_VIEW access.
|
|
|
|
STATUS_INVALID_HANDLE - The specified AccountHandle is invalid.
|
|
|
|
STATUS_INVALID_PARAMETER - The specified Access Flags are invalid.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Avoid RPC stub code raising exception on NULL handle so that
|
|
// we can return the error code STATUS_INVALID_HANDLE in this case
|
|
// too.
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT(AccountHandle)) {
|
|
|
|
return(STATUS_INVALID_HANDLE);
|
|
}
|
|
|
|
RpcTryExcept {
|
|
|
|
Status = LsarSetSystemAccessAccount(
|
|
(LSAPR_HANDLE) AccountHandle,
|
|
SystemAccess
|
|
);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaFreeMemory(
|
|
IN PVOID Buffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Some LSA services that return a potentially large amount of memory,
|
|
such as an enumeration might, allocate the buffer in which the data
|
|
is returned. This function is used to free those buffers when they
|
|
are no longer needed.
|
|
|
|
Parameters:
|
|
|
|
Buffer - Pointer to the buffer to be freed. This buffer must
|
|
have been allocated by a previous LSA service call.
|
|
|
|
Return Values:
|
|
|
|
STATUS_SUCCESS - normal, successful completion.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
MIDL_user_free( Buffer );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsaOpenSecret(
|
|
IN LSA_HANDLE PolicyHandle,
|
|
IN PUNICODE_STRING SecretName,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT PLSA_HANDLE SecretHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaOpenSecret API opens a Secret Object within the LSA Database.
|
|
A handle is returned which must be used to perform operations on the
|
|
secret object.
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - Handle from an LsaOpenLsa call.
|
|
|
|
SecretName - Pointer to a Unicode String structure that references the
|
|
name of the Secret object to be opened.
|
|
|
|
DesiredAccess - This is an access mask indicating accesses being
|
|
requested for the secret object being opened. These access types
|
|
are reconciled with the Discretionary Access Control List of the
|
|
target secret object to determine whether the accesses will be
|
|
granted or denied.
|
|
|
|
|
|
SecretHandle - Pointer to location that will receive a handle to the
|
|
newly opened Secret object.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_OBJECT_NAME_NOT_FOUND - There is no Secret object in the
|
|
target system's LSA Database having the specified SecretName.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
RpcTryExcept {
|
|
|
|
Status = LsarOpenSecret(
|
|
(LSAPR_HANDLE) PolicyHandle,
|
|
(PLSAPR_UNICODE_STRING) SecretName,
|
|
DesiredAccess,
|
|
(PLSAPR_HANDLE) SecretHandle
|
|
);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaSetSecret(
|
|
IN LSA_HANDLE SecretHandle,
|
|
IN OPTIONAL PUNICODE_STRING CurrentValue,
|
|
IN OPTIONAL PUNICODE_STRING OldValue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaSetSecret API optionally sets one or both values associated with
|
|
a secret. These values are known as the "current value" and "old value"
|
|
of the secret and have a meaning known to the creator of the Secret
|
|
object. The values given are stored in encrypted form.
|
|
|
|
Arguments:
|
|
|
|
SecretHandle - Handle from an LsaOpenSecret or LsaCreateSecret call.
|
|
|
|
CurrentValue - Optional pointer to Unicode String containing the
|
|
value to be assigned as the "current value" of the Secret
|
|
object. The meaning of "current value" is dependent on the
|
|
purpose for which the Secret object is being used.
|
|
|
|
OldValue - Optional pointer to Unicode String containing the
|
|
value to be assigned as the "old value" of the Secret object.
|
|
The meaning of "old value" is dependent on the purpose for
|
|
which the Secret object is being used.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_OBJECT_NAME_NOT_FOUND - There is no Secret object in the
|
|
target system's LSA Database having the specified SecretName.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PLSAP_CR_CIPHER_VALUE CipherCurrentValue = NULL;
|
|
PLSAP_CR_CIPHER_VALUE CipherOldValue = NULL;
|
|
LSAP_CR_CLEAR_VALUE ClearCurrentValue;
|
|
LSAP_CR_CLEAR_VALUE ClearOldValue;
|
|
PLSAP_CR_CIPHER_KEY SessionKey = NULL;
|
|
|
|
//
|
|
// Convert input from Unicode Structures to Clear Value Structures.
|
|
//
|
|
|
|
LsapCrUnicodeToClearValue( CurrentValue, &ClearCurrentValue );
|
|
LsapCrUnicodeToClearValue( OldValue, &ClearOldValue );
|
|
|
|
//
|
|
// Obtain the Session Key to be used to two-way encrypt the
|
|
// Current Value and/or Old Values.
|
|
//
|
|
|
|
RpcTryExcept {
|
|
|
|
Status = LsapCrClientGetSessionKey( SecretHandle, &SessionKey );
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecretError;
|
|
}
|
|
|
|
//
|
|
// Encrypt the Current Value if specified and not too long.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(CurrentValue)) {
|
|
|
|
Status = LsapCrEncryptValue(
|
|
&ClearCurrentValue,
|
|
SessionKey,
|
|
&CipherCurrentValue
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecretError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Encrypt the Old Value if specified and not too long.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(OldValue)) {
|
|
|
|
Status = LsapCrEncryptValue(
|
|
(PLSAP_CR_CLEAR_VALUE) &ClearOldValue,
|
|
SessionKey,
|
|
&CipherOldValue
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecretError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the Secret Values.
|
|
//
|
|
|
|
RpcTryExcept {
|
|
|
|
Status = LsarSetSecret(
|
|
(LSAPR_HANDLE) SecretHandle,
|
|
(PLSAPR_CR_CIPHER_VALUE) CipherCurrentValue,
|
|
(PLSAPR_CR_CIPHER_VALUE) CipherOldValue
|
|
);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetSecretError;
|
|
}
|
|
|
|
SetSecretFinish:
|
|
|
|
//
|
|
// If necessary, free memory allocated for the Encrypted Current Value.
|
|
//
|
|
|
|
if (CipherCurrentValue != NULL) {
|
|
|
|
LsaFreeMemory(CipherCurrentValue);
|
|
}
|
|
|
|
//
|
|
// If necessary, free memory allocated for the Encrypted Old Value.
|
|
//
|
|
|
|
if (CipherOldValue != NULL) {
|
|
|
|
LsaFreeMemory(CipherOldValue);
|
|
}
|
|
|
|
//
|
|
// If necessary, free memory allocated for the Session Key.
|
|
//
|
|
|
|
if (SessionKey != NULL) {
|
|
|
|
MIDL_user_free(SessionKey);
|
|
}
|
|
|
|
return(Status);
|
|
|
|
SetSecretError:
|
|
|
|
goto SetSecretFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaQuerySecret(
|
|
IN LSA_HANDLE SecretHandle,
|
|
IN OUT OPTIONAL PUNICODE_STRING *CurrentValue,
|
|
OUT PLARGE_INTEGER CurrentValueSetTime,
|
|
IN OUT OPTIONAL PUNICODE_STRING *OldValue,
|
|
OUT PLARGE_INTEGER OldValueSetTime
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LsaQuerySecret API optionally returns one or both of the values
|
|
assigned to a Secret object. These values are known as the "current value"
|
|
and the "old value", and they have a meaning known to the creator of the
|
|
Secret object. The values are returned in their original unencrypted form.
|
|
The caller must have LSA_QUERY_SECRET access to the Secret object.
|
|
|
|
Arguments:
|
|
|
|
SecretHandle - Handle from an LsaOpenSecret or LsaCreateSecret call.
|
|
|
|
CurrentValue - Optional pointer to location which will receive a pointer
|
|
to a Unicode String containing the value assigned as the "current
|
|
value" of the secret object. If no "current value" is assigned to
|
|
the Secret object, a NULL pointer is returned.
|
|
|
|
CurrentValueSetTime - The date/time at which the current secret value
|
|
was established.
|
|
|
|
OldValue - Optional pointer to location which will receive a pointer
|
|
to a Unicode String containing the value assigned as the "old
|
|
value" of the secret object. If no "current value" is assigned to
|
|
the Secret object, a NULL pointer is returned.
|
|
|
|
OldValueSetTime - The date/time at which the old secret value
|
|
was established.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_OBJECT_NAME_NOT_FOUND - There is no Secret object in the
|
|
target system's LSA Database having the specified SecretName.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PLSAP_CR_CIPHER_VALUE CipherCurrentValue = NULL;
|
|
PLSAP_CR_CIPHER_VALUE CipherOldValue = NULL;
|
|
PLSAP_CR_CLEAR_VALUE ClearCurrentValue = NULL;
|
|
PLSAP_CR_CLEAR_VALUE ClearOldValue = NULL;
|
|
PLSAP_CR_CIPHER_KEY SessionKey = NULL;
|
|
|
|
RpcTryExcept {
|
|
|
|
Status = LsarQuerySecret(
|
|
(PLSAPR_HANDLE) SecretHandle,
|
|
(PLSAPR_CR_CIPHER_VALUE *) &CipherCurrentValue,
|
|
CurrentValueSetTime,
|
|
(PLSAPR_CR_CIPHER_VALUE *) &CipherOldValue,
|
|
OldValueSetTime
|
|
);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto QuerySecretError;
|
|
}
|
|
|
|
//
|
|
// Obtain the Session Key to be used to two-way encrypt the
|
|
// Current Value and/or Old Values.
|
|
//
|
|
|
|
RpcTryExcept {
|
|
|
|
Status = LsapCrClientGetSessionKey( SecretHandle, &SessionKey );
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto QuerySecretError;
|
|
}
|
|
|
|
//
|
|
// If the Current Value is requested and a Current Value exists,
|
|
// decrypt it using the Session key. Otherwise store NULL for return.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(CurrentValue)) {
|
|
|
|
if (CipherCurrentValue != NULL) {
|
|
|
|
Status = LsapCrDecryptValue(
|
|
CipherCurrentValue,
|
|
SessionKey,
|
|
&ClearCurrentValue
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto QuerySecretError;
|
|
}
|
|
|
|
//
|
|
// Convert Clear Current Value to Unicode
|
|
//
|
|
|
|
LsapCrClearValueToUnicode(
|
|
ClearCurrentValue,
|
|
(PUNICODE_STRING) ClearCurrentValue
|
|
);
|
|
*CurrentValue = (PUNICODE_STRING) ClearCurrentValue;
|
|
|
|
} else {
|
|
|
|
*CurrentValue = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the Old Value is requested and an Old Value exists,
|
|
// decrypt it using the Session key. Otherwise store NULL for return.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(OldValue)) {
|
|
|
|
if (CipherOldValue != NULL) {
|
|
|
|
Status = LsapCrDecryptValue(
|
|
CipherOldValue,
|
|
SessionKey,
|
|
&ClearOldValue
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto QuerySecretError;
|
|
}
|
|
|
|
//
|
|
// Convert Clear Old Value to Unicode
|
|
//
|
|
|
|
LsapCrClearValueToUnicode(
|
|
ClearOldValue,
|
|
(PUNICODE_STRING) ClearOldValue
|
|
);
|
|
|
|
*OldValue = (PUNICODE_STRING) ClearOldValue;
|
|
|
|
} else {
|
|
|
|
*OldValue = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Getting here means that the operation completed successfully,
|
|
// but Status can be something other than STATUS_SUCCESS.
|
|
// For instance, if both output buffers are NULL, the value of Status
|
|
// at this point will be STATUS_LOCAL_USER_SESSION_KEY.
|
|
// Explicitly clear Status here and avoid confusion for the client.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
QuerySecretFinish:
|
|
|
|
//
|
|
// If necessary, free memory allocated for the Session Key.
|
|
//
|
|
|
|
if (SessionKey != NULL) {
|
|
|
|
MIDL_user_free(SessionKey);
|
|
}
|
|
|
|
//
|
|
// If necessary, free memory allocated for the returned Encrypted
|
|
// Current Value.
|
|
//
|
|
|
|
if (CipherCurrentValue != NULL) {
|
|
|
|
LsapCrFreeMemoryValue(CipherCurrentValue);
|
|
}
|
|
|
|
//
|
|
// If necessary, free memory allocated for the returned Encrypted
|
|
// Old Value.
|
|
//
|
|
|
|
if (CipherOldValue != NULL) {
|
|
|
|
LsapCrFreeMemoryValue(CipherOldValue);
|
|
}
|
|
|
|
return(Status);
|
|
|
|
QuerySecretError:
|
|
|
|
//
|
|
// If necessary, free memory allocated for the Clear Current Value
|
|
//
|
|
|
|
if (ClearCurrentValue != NULL) {
|
|
|
|
LsapCrFreeMemoryValue(ClearCurrentValue);
|
|
}
|
|
|
|
//
|
|
// If necessary, free memory allocated for the Clear Old Value
|
|
// Unicode string (buffer and structure).
|
|
//
|
|
|
|
if (ClearOldValue != NULL) {
|
|
|
|
LsapCrFreeMemoryValue(ClearOldValue);
|
|
}
|
|
|
|
|
|
if (ARGUMENT_PRESENT(CurrentValue)) {
|
|
|
|
*CurrentValue = NULL;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(OldValue)) {
|
|
|
|
*OldValue = NULL;
|
|
}
|
|
|
|
goto QuerySecretFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaGetUserName(
|
|
OUT PUNICODE_STRING * UserName,
|
|
OUT OPTIONAL PUNICODE_STRING * DomainName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the callers user name and domain name
|
|
|
|
|
|
Arguments:
|
|
|
|
UserName - Receives a pointer to the user's name.
|
|
|
|
DomainName - Optionally receives a pointer to the user's domain name.
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The privilege was found and returned.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
PLSAPR_UNICODE_STRING UserNameBuffer = NULL;
|
|
PLSAPR_UNICODE_STRING DomainNameBuffer = NULL;
|
|
|
|
RpcTryExcept {
|
|
|
|
//
|
|
// Call the Client Stub for LsaGetUserName
|
|
//
|
|
|
|
Status = LsarGetUserName(
|
|
NULL,
|
|
&UserNameBuffer,
|
|
ARGUMENT_PRESENT(DomainName) ? &DomainNameBuffer : NULL
|
|
);
|
|
|
|
(*UserName) = (PUNICODE_STRING)UserNameBuffer;
|
|
|
|
if (ARGUMENT_PRESENT(DomainName)) {
|
|
(*DomainName) = (PUNICODE_STRING)DomainNameBuffer;
|
|
}
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
//
|
|
// If memory was allocated for the return buffer, free it.
|
|
//
|
|
|
|
if (UserNameBuffer != NULL) {
|
|
|
|
MIDL_user_free(UserNameBuffer);
|
|
}
|
|
|
|
if (DomainNameBuffer != NULL) {
|
|
|
|
MIDL_user_free(DomainNameBuffer);
|
|
}
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
|
|
return(Status);
|
|
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsaGetRemoteUserName(
|
|
IN PUNICODE_STRING SystemName,
|
|
OUT PUNICODE_STRING * UserName,
|
|
OUT OPTIONAL PUNICODE_STRING * DomainName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the callers user name and domain name
|
|
|
|
|
|
Arguments:
|
|
|
|
SystemName - Name of system on which to obtain the user name.
|
|
|
|
UserName - Receives a pointer to the user's name.
|
|
|
|
DomainName - Optionally receives a pointer to the user's domain name.
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The privilege was found and returned.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
PLSAPR_UNICODE_STRING UserNameBuffer = NULL;
|
|
PLSAPR_UNICODE_STRING DomainNameBuffer = NULL;
|
|
PLSAPR_SERVER_NAME ServerName = NULL;
|
|
USHORT NullTerminatedServerNameLength;
|
|
|
|
if (ARGUMENT_PRESENT(SystemName) &&
|
|
(SystemName->Buffer != NULL) &&
|
|
(SystemName->Length > 0)) {
|
|
|
|
NullTerminatedServerNameLength = SystemName->Length + (USHORT) sizeof (WCHAR);
|
|
|
|
ServerName = MIDL_user_allocate( NullTerminatedServerNameLength );
|
|
|
|
if (ServerName != NULL) {
|
|
|
|
RtlZeroMemory( ServerName, NullTerminatedServerNameLength );
|
|
|
|
RtlMoveMemory(
|
|
ServerName,
|
|
SystemName->Buffer,
|
|
SystemName->Length
|
|
);
|
|
|
|
} else {
|
|
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
}
|
|
|
|
RpcTryExcept {
|
|
|
|
//
|
|
// Call the Client Stub for LsaGetUserName
|
|
//
|
|
|
|
Status = LsarGetUserName(
|
|
ServerName,
|
|
&UserNameBuffer,
|
|
ARGUMENT_PRESENT(DomainName) ? &DomainNameBuffer : NULL
|
|
);
|
|
|
|
(*UserName) = (PUNICODE_STRING)UserNameBuffer;
|
|
|
|
if (ARGUMENT_PRESENT(DomainName)) {
|
|
(*DomainName) = (PUNICODE_STRING)DomainNameBuffer;
|
|
}
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
//
|
|
// If memory was allocated for the return buffer, free it.
|
|
//
|
|
|
|
if (UserNameBuffer != NULL) {
|
|
|
|
MIDL_user_free(UserNameBuffer);
|
|
}
|
|
|
|
if (DomainNameBuffer != NULL) {
|
|
|
|
MIDL_user_free(DomainNameBuffer);
|
|
}
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
if (ServerName != NULL) {
|
|
MIDL_user_free(ServerName);
|
|
}
|
|
|
|
return(Status);
|
|
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
LsaICLookupNamesWithCreds(
|
|
IN LPWSTR ServerName,
|
|
IN LPWSTR ServerPrincipalName,
|
|
IN ULONG AuthnLevel,
|
|
IN ULONG AuthnSvc,
|
|
IN RPC_AUTH_IDENTITY_HANDLE AuthIdentity,
|
|
IN ULONG AuthzSvc,
|
|
IN ULONG Count,
|
|
IN PUNICODE_STRING Names,
|
|
OUT PLSA_REFERENCED_DOMAIN_LIST *ReferencedDomains,
|
|
OUT PLSA_TRANSLATED_SID_EX2 *Sids,
|
|
IN LSAP_LOOKUP_LEVEL LookupLevel,
|
|
IN OUT PULONG MappedCount
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs a lookup with the using an LSA context handle.
|
|
Its purpose is to facilitate lookups over NETLOGON's secure channel.
|
|
|
|
N.B. The routine uses only TCP/IP as the transport.
|
|
|
|
Arguments:
|
|
|
|
ServerName -- the destination server, NULL terminated
|
|
|
|
ServerPrincipalName,
|
|
AuthnLevel,
|
|
AuthSvc,
|
|
AuthIdentity,
|
|
AuthzSvc -- see RpcSetAuthInfo
|
|
|
|
Rset of the parameters -- see LsaLookupNames2
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_SOME_NOT_MAPPED - Some or all of the names provided could
|
|
not be mapped. This is an informational status only.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
|
|
to complete the call.
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
DWORD RpcError = 0;
|
|
RPC_BINDING_HANDLE BindingHandle = NULL;
|
|
WCHAR *StringBinding = NULL;
|
|
LSAPR_TRANSLATED_SIDS_EX2 ReturnedSidsEx2 = { 0, NULL };
|
|
|
|
//
|
|
// Init to NULL since these are considered to be a IN/OUT parameters
|
|
// for the Lsar Lookup API's
|
|
//
|
|
if ((ServerName == NULL)
|
|
|| (ReferencedDomains == NULL)
|
|
|| (Sids == NULL)
|
|
|| (MappedCount == NULL)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
*ReferencedDomains = NULL;
|
|
*Sids = NULL;
|
|
*MappedCount = 0;
|
|
|
|
if ( 0 == wcsncmp(ServerName, L"\\\\", 2) ) {
|
|
ServerName += 2;
|
|
}
|
|
|
|
RpcError = RpcStringBindingComposeW(
|
|
NULL,
|
|
L"ncacn_ip_tcp",
|
|
ServerName,
|
|
NULL,
|
|
NULL,
|
|
&StringBinding);
|
|
|
|
if (RPC_S_OK == RpcError) {
|
|
|
|
RpcError = RpcBindingFromStringBindingW(
|
|
StringBinding,
|
|
&BindingHandle);
|
|
|
|
if ( RPC_S_OK == RpcError ) {
|
|
|
|
RpcError = RpcEpResolveBinding(BindingHandle,
|
|
lsarpc_ClientIfHandle);
|
|
|
|
if ( RPC_S_OK == RpcError ) {
|
|
|
|
RpcError = RpcBindingSetAuthInfoW(
|
|
BindingHandle,
|
|
ServerPrincipalName,
|
|
AuthnLevel,
|
|
AuthnSvc,
|
|
AuthIdentity,
|
|
AuthzSvc
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (RPC_S_OK != RpcError) {
|
|
//
|
|
// This is fatal
|
|
//
|
|
Status = I_RpcMapWin32Status(RpcError);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
RpcTryExcept {
|
|
|
|
ReturnedSidsEx2.Entries = 0;
|
|
ReturnedSidsEx2.Sids = NULL;
|
|
|
|
Status = LsarLookupNames4(
|
|
BindingHandle,
|
|
Count,
|
|
(PLSAPR_UNICODE_STRING) Names,
|
|
(PLSAPR_REFERENCED_DOMAIN_LIST *) ReferencedDomains,
|
|
&ReturnedSidsEx2,
|
|
LookupLevel,
|
|
MappedCount,
|
|
0, // no flags currently defined
|
|
LSA_LOOKUP_REVISION_LATEST
|
|
);
|
|
|
|
*Sids = (PLSA_TRANSLATED_SID_EX2)ReturnedSidsEx2.Sids;
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
Cleanup:
|
|
|
|
//
|
|
// Make the handling of this unsupported condition simpler by returning
|
|
// one error code
|
|
//
|
|
if ( (Status == RPC_NT_UNKNOWN_IF) ||
|
|
(Status == RPC_NT_PROCNUM_OUT_OF_RANGE) ||
|
|
(Status == EPT_NT_NOT_REGISTERED) ) {
|
|
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
if (BindingHandle) {
|
|
RpcBindingFree(&BindingHandle);
|
|
}
|
|
|
|
if (StringBinding){
|
|
RpcStringFreeW(&StringBinding);
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
LsaICLookupSidsWithCreds(
|
|
IN LPWSTR ServerName,
|
|
IN LPWSTR ServerPrincipalName,
|
|
IN ULONG AuthnLevel,
|
|
IN ULONG AuthnSvc,
|
|
IN RPC_AUTH_IDENTITY_HANDLE AuthIdentity,
|
|
IN ULONG AuthzSvc,
|
|
IN ULONG Count,
|
|
IN PSID *Sids,
|
|
OUT PLSA_REFERENCED_DOMAIN_LIST *ReferencedDomains,
|
|
OUT PLSA_TRANSLATED_NAME_EX *Names,
|
|
IN LSAP_LOOKUP_LEVEL LookupLevel,
|
|
IN OUT PULONG MappedCount
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs a lookup with the using an LSA context handle.
|
|
Its purpose is to facilitate lookups over NETLOGON's secure channel.
|
|
|
|
N.B. The routine uses only TCP/IP as the transport.
|
|
|
|
Arguments:
|
|
|
|
ServerName -- the destination server, NULL terminated
|
|
|
|
ServerPrincipalName,
|
|
AuthnLevel,
|
|
AuthSvc,
|
|
AuthIdentity,
|
|
AuthzSvc -- see RpcSetAuthInfo
|
|
|
|
Rset of the parameters -- see LsaLookupSids
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|
to complete the operation.
|
|
|
|
STATUS_SOME_NOT_MAPPED - Some or all of the names provided could
|
|
not be mapped. This is an informational status only.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
|
|
to complete the call.
|
|
--*/
|
|
{
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
DWORD RpcError = 0;
|
|
RPC_BINDING_HANDLE BindingHandle = NULL;
|
|
WCHAR *StringBinding = NULL;
|
|
LSAPR_TRANSLATED_NAMES_EX ReturnedNames = { 0, NULL };
|
|
LSAPR_SID_ENUM_BUFFER SidEnumBuffer;
|
|
|
|
//
|
|
// Init to NULL since these are considered to be a IN/OUT parameters
|
|
// for the Lsar Lookup API's
|
|
//
|
|
if ((ServerName == NULL)
|
|
|| (ReferencedDomains == NULL)
|
|
|| (Names == NULL)
|
|
|| (MappedCount == NULL)
|
|
|| (Count == 0) ) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
*ReferencedDomains = NULL;
|
|
*Names = NULL;
|
|
*MappedCount = 0;
|
|
|
|
SidEnumBuffer.Entries = Count;
|
|
SidEnumBuffer.SidInfo = (PLSAPR_SID_INFORMATION) Sids;
|
|
|
|
if ( 0 == wcsncmp(ServerName, L"\\\\", 2) ) {
|
|
ServerName += 2;
|
|
}
|
|
|
|
RpcError = RpcStringBindingComposeW(
|
|
NULL,
|
|
L"ncacn_ip_tcp",
|
|
ServerName,
|
|
NULL,
|
|
NULL,
|
|
&StringBinding);
|
|
|
|
if (RPC_S_OK == RpcError) {
|
|
|
|
RpcError = RpcBindingFromStringBindingW(
|
|
StringBinding,
|
|
&BindingHandle);
|
|
|
|
if ( RPC_S_OK == RpcError ) {
|
|
|
|
RpcError = RpcEpResolveBinding(BindingHandle,
|
|
lsarpc_ClientIfHandle);
|
|
|
|
if ( RPC_S_OK == RpcError ) {
|
|
|
|
RpcError = RpcBindingSetAuthInfoW(
|
|
BindingHandle,
|
|
ServerPrincipalName,
|
|
AuthnLevel,
|
|
AuthnSvc,
|
|
AuthIdentity,
|
|
AuthzSvc
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (RPC_S_OK != RpcError) {
|
|
Status = I_RpcMapWin32Status(RpcError);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
RpcTryExcept {
|
|
|
|
ReturnedNames.Entries = 0;
|
|
ReturnedNames.Names = NULL;
|
|
|
|
//
|
|
// Lookup Sids on the Server..
|
|
//
|
|
|
|
Status = LsarLookupSids3(
|
|
BindingHandle,
|
|
&SidEnumBuffer,
|
|
(PLSAPR_REFERENCED_DOMAIN_LIST *) ReferencedDomains,
|
|
&ReturnedNames,
|
|
LookupLevel,
|
|
MappedCount,
|
|
0,
|
|
LSA_CLIENT_NT5
|
|
);
|
|
|
|
*Names = (PLSA_TRANSLATED_NAME_EX) ReturnedNames.Names;
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
|
|
|
|
} RpcEndExcept;
|
|
|
|
|
|
//
|
|
// Prevent against network hacks
|
|
//
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
Status = LsapVerifyReturnedNames(&ReturnedNames,
|
|
Count,
|
|
*ReferencedDomains);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
if (*Names) {
|
|
MIDL_user_free(*Names);
|
|
*Names = NULL;
|
|
}
|
|
|
|
if ( *ReferencedDomains ) {
|
|
MIDL_user_free( *ReferencedDomains );
|
|
*ReferencedDomains = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
//
|
|
// Make the handling of this unsupported condition simpler by returning
|
|
// one error code
|
|
//
|
|
if ( (Status == RPC_NT_UNKNOWN_IF) ||
|
|
(Status == RPC_NT_PROCNUM_OUT_OF_RANGE) ||
|
|
(Status == EPT_NT_NOT_REGISTERED) ) {
|
|
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
if (BindingHandle) {
|
|
RpcBindingFree(&BindingHandle);
|
|
}
|
|
|
|
if (StringBinding){
|
|
RpcStringFreeW(&StringBinding);
|
|
}
|
|
|
|
return(Status);
|
|
}
|