8954 lines
244 KiB
C
8954 lines
244 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1991 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
wrappers.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This file contains all SAM rpc wrapper routines.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Jim Kelly (JimK) 4-July-1991
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
User Mode - Win32
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
// //
|
|||
|
// Includes //
|
|||
|
// //
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
#include "samclip.h"
|
|||
|
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
// //
|
|||
|
// Private defines //
|
|||
|
// //
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
#define SAMP_MAXIMUM_SUB_LOOKUP_COUNT ((ULONG) 0x00000200)
|
|||
|
|
|||
|
// Simple tracing routine for client-side API -- this should be removed once
|
|||
|
// we understand how the DS-based SAM works. This macro is only called from
|
|||
|
// within wrappers.c.
|
|||
|
|
|||
|
#define SAMP_TRACE_CLIENT_API 0
|
|||
|
|
|||
|
#if SAMP_TRACE_CLIENT_API == 1
|
|||
|
|
|||
|
#define SampOutputDebugString(Message) \
|
|||
|
OutputDebugStringA("SAM API = "); \
|
|||
|
OutputDebugStringA(Message); \
|
|||
|
OutputDebugStringA("\n"); \
|
|||
|
|
|||
|
#else
|
|||
|
|
|||
|
#define SampOutputDebugString(Message)
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
// //
|
|||
|
// Local data types //
|
|||
|
// //
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
//
|
|||
|
// This structure is used to pass a (potentially) very large
|
|||
|
// user requested name lookup into (possibly many) smaller
|
|||
|
// remote lookup requests. This is necessary to prevent the
|
|||
|
// server from being asked to allocate huge chunks of memory,
|
|||
|
// potentially running out of memory.
|
|||
|
//
|
|||
|
// There will be one of these structures for each server call
|
|||
|
// that is necessary.
|
|||
|
//
|
|||
|
|
|||
|
typedef struct _SAMP_NAME_LOOKUP_CALL {
|
|||
|
|
|||
|
//
|
|||
|
// Each call is represented by one of these structures.
|
|||
|
// The structures are chained together to show the order
|
|||
|
// the calls were made (allowing an easy way to build the
|
|||
|
// buffer that is to be returned to the user).
|
|||
|
//
|
|||
|
|
|||
|
LIST_ENTRY Link;
|
|||
|
|
|||
|
//
|
|||
|
// These fields define the beginning and ending indexes into
|
|||
|
// the user passed Names buffer that are being represented by
|
|||
|
// this call.
|
|||
|
//
|
|||
|
|
|||
|
ULONG StartIndex;
|
|||
|
ULONG Count;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// These fields will receive the looked up RIDs and USE buffers.
|
|||
|
// Notice that the .Element fields of these fields will receive
|
|||
|
// pointers to the bulk of the returned information.
|
|||
|
//
|
|||
|
|
|||
|
SAMPR_ULONG_ARRAY RidBuffer;
|
|||
|
SAMPR_ULONG_ARRAY UseBuffer;
|
|||
|
|
|||
|
} SAMP_NAME_LOOKUP_CALL, *PSAMP_NAME_LOOKUP_CALL;
|
|||
|
|
|||
|
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
// //
|
|||
|
// private service prototypes //
|
|||
|
// //
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampLookupIdsInDomain(
|
|||
|
IN SAM_HANDLE DomainHandle,
|
|||
|
IN ULONG Count,
|
|||
|
IN PULONG RelativeIds,
|
|||
|
OUT PUNICODE_STRING *Names,
|
|||
|
OUT PSID_NAME_USE *Use OPTIONAL
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampMapCompletionStatus(
|
|||
|
IN NTSTATUS Status
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampCalculateLmPassword(
|
|||
|
IN PUNICODE_STRING NtPassword,
|
|||
|
OUT PCHAR *LmPasswordBuffer
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampCheckPasswordRestrictions(
|
|||
|
IN SAMPR_HANDLE UserHandle,
|
|||
|
IN PUNICODE_STRING NewNtPassword,
|
|||
|
OUT PBOOLEAN UseOwfPasswords
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
// //
|
|||
|
// Routines //
|
|||
|
// //
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
// //
|
|||
|
// General services //
|
|||
|
// //
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamFreeMemory(
|
|||
|
IN PVOID Buffer
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
|
|||
|
Some SAM 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 SAM service call.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - normal, successful completion.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
MIDL_user_free( Buffer );
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamSetSecurityObject(
|
|||
|
IN SAM_HANDLE ObjectHandle,
|
|||
|
IN SECURITY_INFORMATION SecurityInformation,
|
|||
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
|
|||
|
This function (SamSetSecurityObject) takes a well formed Security
|
|||
|
Descriptor provided by the caller 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 all of the security information associated with an
|
|||
|
object.
|
|||
|
|
|||
|
This is the only function available to users and applications for
|
|||
|
changing security information, including the owner ID, group ID, and
|
|||
|
the discretionary and system ACLs of an 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
|
|||
|
ACCESS_SYSTEM_SECURITY access to an object to assign a system ACL
|
|||
|
to the object.
|
|||
|
|
|||
|
This API is modelled after the NtSetSecurityObject() system service.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
ObjectHandle - A handle to an existing object.
|
|||
|
|
|||
|
SecurityInformation - Indicates which security information is to
|
|||
|
be applied to the object. The value(s) to be assigned are
|
|||
|
passed in the SecurityDescriptor parameter.
|
|||
|
|
|||
|
SecurityDescriptor - A pointer to a well formed Security
|
|||
|
Descriptor.
|
|||
|
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - normal, successful completion.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - The specified handle was not opened for
|
|||
|
either WRITE_OWNER, WRITE_DAC, or ACCESS_SYSTEM_SECURITY
|
|||
|
access.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The specified handle is not that of an
|
|||
|
opened SAM object.
|
|||
|
|
|||
|
STATUS_BAD_DESCRIPTOR_FORMAT - Indicates something about security descriptor
|
|||
|
is not valid. This may indicate that the structure of the descriptor is
|
|||
|
not valid or that a component of the descriptor specified via the
|
|||
|
SecurityInformation parameter is not present in the security descriptor.
|
|||
|
|
|||
|
STATUS_INVALID_PARAMETER - Indicates no security information was specified.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
ULONG SDLength;
|
|||
|
SAMPR_SR_SECURITY_DESCRIPTOR DescriptorToPass;
|
|||
|
|
|||
|
SampOutputDebugString("SamSetSecurityObject");
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Make a self relative security descriptor for use in the RPC call..
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
SDLength = 0;
|
|||
|
NtStatus = RtlMakeSelfRelativeSD(
|
|||
|
SecurityDescriptor,
|
|||
|
NULL,
|
|||
|
&SDLength
|
|||
|
);
|
|||
|
|
|||
|
if (NtStatus != STATUS_BUFFER_TOO_SMALL) {
|
|||
|
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
|
|||
|
DescriptorToPass.SecurityDescriptor = MIDL_user_allocate( SDLength );
|
|||
|
DescriptorToPass.Length = SDLength;
|
|||
|
if (DescriptorToPass.SecurityDescriptor == NULL) {
|
|||
|
|
|||
|
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// make an appropriate self-relative security descriptor
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = RtlMakeSelfRelativeSD(
|
|||
|
SecurityDescriptor,
|
|||
|
(PSECURITY_DESCRIPTOR)DescriptorToPass.SecurityDescriptor,
|
|||
|
&SDLength
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrSetSecurityObject(
|
|||
|
(SAMPR_HANDLE)ObjectHandle,
|
|||
|
SecurityInformation,
|
|||
|
&DescriptorToPass
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
}
|
|||
|
|
|||
|
MIDL_user_free( DescriptorToPass.SecurityDescriptor );
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamQuerySecurityObject(
|
|||
|
IN SAM_HANDLE ObjectHandle,
|
|||
|
IN SECURITY_INFORMATION SecurityInformation,
|
|||
|
OUT PSECURITY_DESCRIPTOR * SecurityDescriptor
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
|
|||
|
This function (SamQuerySecurityObject) returns to the caller requested
|
|||
|
security information currently assigned to an object.
|
|||
|
|
|||
|
Based on the caller's access rights 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 be granted ACCESS_SYSTEM_SECURITY
|
|||
|
access.
|
|||
|
|
|||
|
This API is modelled after the NtQuerySecurityObject() system
|
|||
|
service.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
ObjectHandle - A handle to an existing object.
|
|||
|
|
|||
|
SecurityInformation - Supplies a value describing which pieces of
|
|||
|
security information are being queried.
|
|||
|
|
|||
|
SecurityDescriptor - Receives a pointer to the buffer containing
|
|||
|
the requested security information. This information is
|
|||
|
returned in the form of a self-relative security descriptor.
|
|||
|
The caller is responsible for freeing the returned buffer
|
|||
|
(using SamFreeMemory()) when the security descriptor
|
|||
|
is no longer needed.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - normal, successful completion.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - The specified handle was not opened for
|
|||
|
either READ_CONTROL or ACCESS_SYSTEM_SECURITY
|
|||
|
access.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The specified handle is not that of an
|
|||
|
opened SAM object.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
SAMPR_SR_SECURITY_DESCRIPTOR ReturnedSD;
|
|||
|
PSAMPR_SR_SECURITY_DESCRIPTOR PReturnedSD;
|
|||
|
|
|||
|
SampOutputDebugString("SamQuerySecurityObject");
|
|||
|
|
|||
|
//
|
|||
|
// 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;
|
|||
|
NtStatus =
|
|||
|
SamrQuerySecurityObject(
|
|||
|
(SAMPR_HANDLE)ObjectHandle,
|
|||
|
SecurityInformation,
|
|||
|
&PReturnedSD
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
(*SecurityDescriptor) = ReturnedSD.SecurityDescriptor;
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamCloseHandle(
|
|||
|
OUT SAM_HANDLE SamHandle
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API closes a currently opened SAM object.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
SamHandle - Specifies the handle of a previously opened SAM object to
|
|||
|
close.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
|
|||
|
STATUS_SUCCESS - The object was successfully closed.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
SAMPR_HANDLE TmpHandle;
|
|||
|
|
|||
|
SampOutputDebugString("SamCloseHandle");
|
|||
|
|
|||
|
|
|||
|
if (SamHandle == NULL) {
|
|||
|
return(STATUS_INVALID_HANDLE);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
TmpHandle = (SAMPR_HANDLE)SamHandle;
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus = SamrCloseHandle(
|
|||
|
(SAMPR_HANDLE *)(&TmpHandle)
|
|||
|
);
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
if ((NtStatus != RPC_NT_SS_CONTEXT_MISMATCH) &&
|
|||
|
(NtStatus != RPC_NT_INVALID_BINDING)) {
|
|||
|
(void) RpcSsDestroyClientContext( &TmpHandle);
|
|||
|
}
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
// //
|
|||
|
// Server object related services //
|
|||
|
// //
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamConnect(
|
|||
|
IN PUNICODE_STRING ServerName,
|
|||
|
OUT PSAM_HANDLE ServerHandle,
|
|||
|
IN ACCESS_MASK DesiredAccess,
|
|||
|
IN POBJECT_ATTRIBUTES ObjectAttributes
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Establish a session with a SAM subsystem and subsequently open the
|
|||
|
SamServer object of that subsystem. The caller must have
|
|||
|
SAM_SERVER_CONNECT access to the SamServer object of the subsystem
|
|||
|
being connected to.
|
|||
|
|
|||
|
The handle returned is for use in future calls.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ServerName - Name of the server to use, or NULL if local.
|
|||
|
|
|||
|
ServerHandle - A handle to be used in future requests. This handle
|
|||
|
represents both the handle to the SamServer object and the RPC
|
|||
|
context handle for the connection to the SAM subsystem.
|
|||
|
|
|||
|
DesiredAccess - Is an access mask indicating which access types are
|
|||
|
desired to the SamServer. These access types are reconciled
|
|||
|
with the Discretionary Access Control list of the SamServer to
|
|||
|
determine whether the accesses will be granted or denied. The
|
|||
|
access type of SAM_SERVER_CONNECT is always implicitly included
|
|||
|
in this access mask.
|
|||
|
|
|||
|
ObjectAttributes - Pointer to the set of object attributes to use for
|
|||
|
this connection. Only the security Quality Of Service
|
|||
|
information is used and should provide SecurityIdentification
|
|||
|
level of impersonation.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Access was denied.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
PSAMPR_SERVER_NAME RServerName;
|
|||
|
PSAMPR_SERVER_NAME RServerNameWithNull;
|
|||
|
USHORT RServerNameWithNullLength;
|
|||
|
|
|||
|
|
|||
|
SampOutputDebugString("SamConnect");
|
|||
|
|
|||
|
//
|
|||
|
// Hmmm - what to do with security QOS???
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server, passing either a NULL Server Name pointer, or
|
|||
|
// a pointer to a Unicode Buffer with a Wide Character NULL terminator.
|
|||
|
// Since the input name is contained in a counted Unicode String, there
|
|||
|
// is no NULL terminator necessarily provided, so we must append one.
|
|||
|
//
|
|||
|
|
|||
|
RServerNameWithNull = NULL;
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(ServerName)) {
|
|||
|
|
|||
|
RServerName = (PSAMPR_SERVER_NAME)(ServerName->Buffer);
|
|||
|
RServerNameWithNullLength = ServerName->Length + (USHORT) sizeof(WCHAR);
|
|||
|
RServerNameWithNull = MIDL_user_allocate( RServerNameWithNullLength );
|
|||
|
|
|||
|
if (RServerNameWithNull == NULL) {
|
|||
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|||
|
}
|
|||
|
|
|||
|
RtlCopyMemory( RServerNameWithNull, RServerName, ServerName->Length);
|
|||
|
RServerNameWithNull[ServerName->Length/sizeof(WCHAR)] = L'\0';
|
|||
|
}
|
|||
|
|
|||
|
RpcTryExcept {
|
|||
|
|
|||
|
NtStatus = SamrConnect2(
|
|||
|
RServerNameWithNull,
|
|||
|
(SAMPR_HANDLE *)ServerHandle,
|
|||
|
DesiredAccess
|
|||
|
);
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
//
|
|||
|
// If the new connect call failed because it didn't exist,
|
|||
|
// try the old one.
|
|||
|
//
|
|||
|
|
|||
|
if ((NtStatus == RPC_NT_UNKNOWN_IF) ||
|
|||
|
(NtStatus == RPC_NT_PROCNUM_OUT_OF_RANGE)) {
|
|||
|
|
|||
|
RpcTryExcept {
|
|||
|
|
|||
|
NtStatus = SamrConnect(
|
|||
|
RServerNameWithNull,
|
|||
|
(SAMPR_HANDLE *)ServerHandle,
|
|||
|
DesiredAccess
|
|||
|
);
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
}
|
|||
|
|
|||
|
if (RServerNameWithNull != NULL) {
|
|||
|
MIDL_user_free( RServerNameWithNull );
|
|||
|
}
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
DBG_UNREFERENCED_PARAMETER(ObjectAttributes);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamShutdownSamServer(
|
|||
|
IN SAM_HANDLE ServerHandle
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the wrapper routine for SamShutdownSamServer().
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ServerHandle - Handle from a previous SamConnect() call.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
|
|||
|
STATUS_SUCCESS The service completed successfully or the server
|
|||
|
has already shutdown.
|
|||
|
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|||
|
to complete the operation.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus = SamrShutdownSamServer(ServerHandle);
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If the error status is one that would result from a server
|
|||
|
// not being there, then replace it with success.
|
|||
|
//
|
|||
|
|
|||
|
if (NtStatus == RPC_NT_CALL_FAILED) {
|
|||
|
NtStatus = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamLookupDomainInSamServer(
|
|||
|
IN SAM_HANDLE ServerHandle,
|
|||
|
IN PUNICODE_STRING Name,
|
|||
|
OUT PSID *DomainId
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This service returns the SID corresponding to the specified domain.
|
|||
|
The domain is specified by name.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ServerHandle - Handle from a previous SamConnect() call.
|
|||
|
|
|||
|
Name - The name of the domain whose ID is to be looked up. A
|
|||
|
case-insensitive comparison of this name will be performed for
|
|||
|
the lookup operation.
|
|||
|
|
|||
|
DomainId - Receives a pointer to a buffer containing the SID of the
|
|||
|
looked up domain. This buffer must be freed using
|
|||
|
SamFreeMemory() when no longer needed.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
|
|||
|
STATUS_SUCCESS - The service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|||
|
to complete the operation. SAM_SERVER_LOOKUP_DOMAIN access is
|
|||
|
needed.
|
|||
|
|
|||
|
STATUS_NO_SUCH_DOMAIN - The specified domain does not exist at this
|
|||
|
server.
|
|||
|
|
|||
|
STATUS_INVALID_SERVER_STATE - Indicates the SAM server is currently
|
|||
|
disabled.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
SampOutputDebugString("SamLookupDomainInSamServer");
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
(*DomainId) = 0;
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrLookupDomainInSamServer(
|
|||
|
(SAMPR_HANDLE)ServerHandle,
|
|||
|
(PRPC_UNICODE_STRING)Name,
|
|||
|
(PRPC_SID *)DomainId
|
|||
|
);
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamEnumerateDomainsInSamServer(
|
|||
|
IN SAM_HANDLE ServerHandle,
|
|||
|
IN OUT PSAM_ENUMERATE_HANDLE EnumerationContext,
|
|||
|
OUT PVOID * Buffer,
|
|||
|
IN ULONG PreferedMaximumLength,
|
|||
|
OUT PULONG CountReturned
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API lists all the domains defined in the account database.
|
|||
|
Since there may be more domains than can fit into a buffer, 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
|
|||
|
SAM_ENUMERATE_HANDLE variable that is set to 0.
|
|||
|
|
|||
|
If the API returns STATUS_MORE_ENTRIES, then the API should be
|
|||
|
called again with EnumerationContext. When the API returns
|
|||
|
STATUS_SUCCESS or any error return, the handle becomes invalid for
|
|||
|
future use.
|
|||
|
|
|||
|
This API requires SAM_SERVER_ENUMERATE_DOMAINS access to the
|
|||
|
SamServer object.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
ServerHandle - Handle obtained from a previous SamConnect call.
|
|||
|
|
|||
|
EnumerationContext - API specific handle to allow multiple calls
|
|||
|
(see routine description). This is a zero based index.
|
|||
|
|
|||
|
Buffer - Receives a pointer to the buffer where the information
|
|||
|
is placed. The information returned is contiguous
|
|||
|
SAM_RID_ENUMERATION data structures. However, the
|
|||
|
RelativeId field of each of these structures is not valid.
|
|||
|
This buffer must be freed when no longer needed using
|
|||
|
SamFreeMemory().
|
|||
|
|
|||
|
PreferedMaximumLength - Prefered maximum length of returned data
|
|||
|
(in 8-bit bytes). This is not a hard upper limit, but serves
|
|||
|
as a guide to the server. 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:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully, and there
|
|||
|
are no additional entries.
|
|||
|
|
|||
|
STATUS_MORE_ENTRIES - There are more entries, so call again.
|
|||
|
This is a successful return.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the access required
|
|||
|
to enumerate the domains.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_INVALID_SERVER_STATE - Indicates the SAM server is
|
|||
|
currently disabled.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
PSAMPR_ENUMERATION_BUFFER LocalBuffer;
|
|||
|
|
|||
|
SampOutputDebugString("SamEnumerateDomainsInSamServer");
|
|||
|
|
|||
|
//
|
|||
|
// Make sure we aren't trying to have RPC allocate the EnumerationContext.
|
|||
|
//
|
|||
|
|
|||
|
if ( !ARGUMENT_PRESENT(EnumerationContext) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
if ( !ARGUMENT_PRESENT(Buffer) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
if ( !ARGUMENT_PRESENT(CountReturned) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
(*Buffer) = NULL;
|
|||
|
LocalBuffer = NULL;
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus = SamrEnumerateDomainsInSamServer(
|
|||
|
(SAMPR_HANDLE)ServerHandle,
|
|||
|
EnumerationContext,
|
|||
|
(PSAMPR_ENUMERATION_BUFFER *)&LocalBuffer,
|
|||
|
PreferedMaximumLength,
|
|||
|
CountReturned
|
|||
|
);
|
|||
|
|
|||
|
if (LocalBuffer != NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// What comes back is a three level structure:
|
|||
|
//
|
|||
|
// Local +-------------+
|
|||
|
// Buffer ---> | EntriesRead |
|
|||
|
// |-------------| +-------+
|
|||
|
// | Enumeration |--->| Name0 | --- > (NameBuffer0)
|
|||
|
// | Return | |-------| o
|
|||
|
// | Buffer | | ... | o
|
|||
|
// +-------------+ |-------| o
|
|||
|
// | NameN | --- > (NameBufferN)
|
|||
|
// +-------+
|
|||
|
//
|
|||
|
// The buffer containing the EntriesRead field is not returned
|
|||
|
// to our caller. Only the buffers containing name information
|
|||
|
// are returned.
|
|||
|
//
|
|||
|
|
|||
|
if (LocalBuffer->Buffer != NULL) {
|
|||
|
(*Buffer) = LocalBuffer->Buffer;
|
|||
|
}
|
|||
|
|
|||
|
MIDL_user_free( LocalBuffer);
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
// //
|
|||
|
// Domain object related services //
|
|||
|
// //
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamOpenDomain(
|
|||
|
IN SAM_HANDLE ServerHandle,
|
|||
|
IN ACCESS_MASK DesiredAccess,
|
|||
|
IN PSID DomainId,
|
|||
|
OUT PSAM_HANDLE DomainHandle
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API opens a domain object. It returns a handle to the newly
|
|||
|
opened domain that must be used for successive operations on the
|
|||
|
domain. This handle may be closed with the SamCloseHandle API.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ServerHandle - Handle from a previous SamConnect() call.
|
|||
|
|
|||
|
DesiredAccess - Is an access mask indicating which access types are
|
|||
|
desired to the domain. These access types are reconciled with
|
|||
|
the Discretionary Access Control list of the domain to determine
|
|||
|
whether the accesses will be granted or denied.
|
|||
|
|
|||
|
DomainId - The SID assigned to the domain to open.
|
|||
|
|
|||
|
DomainHandle - Receives a handle referencing the newly opened domain.
|
|||
|
This handle will be required in successive calls to operate on
|
|||
|
the domain.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
|
|||
|
STATUS_SUCCESS - The domain was successfully opened.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|||
|
to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_SERVER_STATE - Indicates the SAM server is currently
|
|||
|
disabled.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
SampOutputDebugString("SamOpenDomain");
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
(*DomainHandle) = 0;
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrOpenDomain(
|
|||
|
(SAMPR_HANDLE)ServerHandle,
|
|||
|
DesiredAccess,
|
|||
|
(PRPC_SID)DomainId,
|
|||
|
(SAMPR_HANDLE *)DomainHandle
|
|||
|
);
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamQueryInformationDomain(
|
|||
|
IN SAM_HANDLE DomainHandle,
|
|||
|
IN DOMAIN_INFORMATION_CLASS DomainInformationClass,
|
|||
|
OUT PVOID *Buffer
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API retrieves the domain information. This API requires either
|
|||
|
DOMAIN_READ_PASSWORD_PARAMETERS or DOMAIN_READ_OTHER_PARAMETERS.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DomainHandle - Handle from a previous SamOpenDomain() call.
|
|||
|
|
|||
|
DomainInformationClass - Class of information desired. The accesses
|
|||
|
required for each class is shown below:
|
|||
|
|
|||
|
Info Level Required Access Type
|
|||
|
--------------------------- -------------------------------
|
|||
|
DomainGeneralInformation DOMAIN_READ_OTHER_PARAMETERS
|
|||
|
DomainPasswordInformation DOMAIN_READ_PASSWORD_PARAMS
|
|||
|
DomainLogoffInformation DOMAIN_READ_OTHER_PARAMETERS
|
|||
|
DomainOemInformation DOMAIN_READ_OTHER_PARAMETERS
|
|||
|
DomainNameInformation DOMAIN_READ_OTHER_PARAMETERS
|
|||
|
DomainServerRoleInformation DOMAIN_READ_OTHER_PARAMETERS
|
|||
|
DomainReplicationInformation DOMAIN_READ_OTHER_PARAMETERS
|
|||
|
DomainModifiedInformation DOMAIN_READ_OTHER_PARAMETERS
|
|||
|
DomainStateInformation DOMAIN_READ_OTHER_PARAMETERS
|
|||
|
DomainUasInformation DOMAIN_READ_OTHER_PARAMETERS
|
|||
|
Added for NT1.0A...
|
|||
|
DomainGeneralInformation2 DOMAIN_READ_OTHER_PARAMETERS
|
|||
|
DomainLockoutInformation DOMAIN_READ_OTHER_PARAMETERS
|
|||
|
|
|||
|
|
|||
|
Buffer - Receives a pointer to a buffer containing the requested
|
|||
|
information. When this information is no longer needed, this buffer
|
|||
|
must be freed using SamFreeMemory().
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|||
|
to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_INVALID_INFO_CLASS - The class provided was invalid.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
SampOutputDebugString("SamQueryInformationDomain");
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
(*Buffer) = NULL;
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
if (DomainInformationClass <= DomainUasInformation) {
|
|||
|
NtStatus = SamrQueryInformationDomain(
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
DomainInformationClass,
|
|||
|
(PSAMPR_DOMAIN_INFO_BUFFER *)Buffer
|
|||
|
);
|
|||
|
} else {
|
|||
|
NtStatus = SamrQueryInformationDomain2(
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
DomainInformationClass,
|
|||
|
(PSAMPR_DOMAIN_INFO_BUFFER *)Buffer
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
//
|
|||
|
// If the exception indicates the server doesn't have
|
|||
|
// the selected api, that means the server doesn't know
|
|||
|
// about the info level we passed. Set our completion
|
|||
|
// status appropriately.
|
|||
|
//
|
|||
|
|
|||
|
if (RpcExceptionCode() == RPC_S_INVALID_LEVEL ||
|
|||
|
RpcExceptionCode() == RPC_S_PROCNUM_OUT_OF_RANGE ||
|
|||
|
RpcExceptionCode() == RPC_NT_PROCNUM_OUT_OF_RANGE ) {
|
|||
|
NtStatus = STATUS_INVALID_INFO_CLASS;
|
|||
|
} else {
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
}
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamSetInformationDomain(
|
|||
|
IN SAM_HANDLE DomainHandle,
|
|||
|
IN DOMAIN_INFORMATION_CLASS DomainInformationClass,
|
|||
|
IN PVOID DomainInformation
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API sets the domain information to the values passed in the
|
|||
|
buffer.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
DomainHandle - A domain handle returned from a previous call to
|
|||
|
SamOpenDomain.
|
|||
|
|
|||
|
DomainInformationClass - Class of information desired. The
|
|||
|
accesses required for each class is shown below:
|
|||
|
|
|||
|
Info Level Required Access Type
|
|||
|
------------------------- ----------------------------
|
|||
|
|
|||
|
DomainPasswordInformation DOMAIN_WRITE_PASSWORD_PARAMS
|
|||
|
|
|||
|
DomainLogoffInformation DOMAIN_WRITE_OTHER_PARAMETERS
|
|||
|
|
|||
|
DomainOemInformation DOMAIN_WRITE_OTHER_PARAMETERS
|
|||
|
|
|||
|
DomainNameInformation (not valid for set operations.)
|
|||
|
|
|||
|
DomainServerRoleInformation DOMAIN_ADMINISTER_SERVER
|
|||
|
|
|||
|
DomainReplicationInformation DOMAIN_ADMINISTER_SERVER
|
|||
|
|
|||
|
DomainModifiedInformation (not valid for set operations.)
|
|||
|
|
|||
|
DomainStateInformation DOMAIN_ADMINISTER_SERVER
|
|||
|
|
|||
|
DomainUasInformation DOMAIN_WRITE_OTHER_PARAMETERS
|
|||
|
|
|||
|
DomainInformation - Buffer where the domain information can be
|
|||
|
found.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_INVALID_INFO_CLASS - The class provided was invalid.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be disabled before role
|
|||
|
changes can be made.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
SampOutputDebugString("SamSetInformationDomain");
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrSetInformationDomain(
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
DomainInformationClass,
|
|||
|
(PSAMPR_DOMAIN_INFO_BUFFER)DomainInformation
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamCreateGroupInDomain(
|
|||
|
IN SAM_HANDLE DomainHandle,
|
|||
|
IN PUNICODE_STRING AccountName,
|
|||
|
IN ACCESS_MASK DesiredAccess,
|
|||
|
OUT PSAM_HANDLE GroupHandle,
|
|||
|
OUT PULONG RelativeId
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API creates a new group in the account database. Initially,
|
|||
|
this group does not contain any users. Note that creating a group
|
|||
|
is a protected operation, and requires the DOMAIN_CREATE_GROUP
|
|||
|
access type.
|
|||
|
|
|||
|
This call returns a handle to the newly created group that may be
|
|||
|
used for successive operations on the group. This handle may be
|
|||
|
closed with the SamCloseHandle API.
|
|||
|
|
|||
|
A newly created group will have the following initial field value
|
|||
|
settings. If another value is desired, it must be explicitly
|
|||
|
changed using the group object manipulation services.
|
|||
|
|
|||
|
Name - The name of the group will be as specified in the
|
|||
|
creation API.
|
|||
|
|
|||
|
Attributes - The following attributes will be set:
|
|||
|
|
|||
|
Mandatory
|
|||
|
EnabledByDefault
|
|||
|
|
|||
|
MemberCount - Zero. Initially the group has no members.
|
|||
|
|
|||
|
RelativeId - will be a uniquelly allocated ID.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
DomainHandle - A domain handle returned from a previous call to
|
|||
|
SamOpenDomain.
|
|||
|
|
|||
|
AccountName - Points to the name of the new account. A
|
|||
|
case-insensitive comparison must not find a group, alias or user
|
|||
|
with this name already defined.
|
|||
|
|
|||
|
DesiredAccess - Is an access mask indicating which access types
|
|||
|
are desired to the group.
|
|||
|
|
|||
|
GroupHandle - Receives a handle referencing the newly created
|
|||
|
group. This handle will be required in successive calls to
|
|||
|
operate on the group.
|
|||
|
|
|||
|
RelativeId - Receives the relative ID of the newly created group
|
|||
|
account. The SID of the new group account is this relative
|
|||
|
ID value prefixed with the domain's SID value.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The group was added successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_INVALID_ACCOUNT_NAME - The name was poorly formed, e.g.
|
|||
|
contains non-printable characters.
|
|||
|
|
|||
|
STATUS_GROUP_EXISTS - The name is already in use as a group.
|
|||
|
|
|||
|
STATUS_USER_EXISTS - The name is already in use as a user.
|
|||
|
|
|||
|
STATUS_ALIAS_EXISTS - The name is already in use as an alias.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be enabled before groups
|
|||
|
can be created in it.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation. The domain server must be a primary server to
|
|||
|
create group accounts.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
SampOutputDebugString("SamCreateGroupInDomain");
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
(*GroupHandle) = NULL;
|
|||
|
(*RelativeId) = 0;
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrCreateGroupInDomain(
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
(PRPC_UNICODE_STRING)AccountName,
|
|||
|
DesiredAccess,
|
|||
|
(SAMPR_HANDLE *)GroupHandle,
|
|||
|
RelativeId
|
|||
|
);
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamEnumerateGroupsInDomain(
|
|||
|
IN SAM_HANDLE DomainHandle,
|
|||
|
IN OUT PSAM_ENUMERATE_HANDLE EnumerationContext,
|
|||
|
IN PVOID * Buffer,
|
|||
|
IN ULONG PreferedMaximumLength,
|
|||
|
OUT PULONG CountReturned
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API lists all the groups defined in the account database.
|
|||
|
Since there may be more groups than can fit into a buffer, 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
|
|||
|
SAM_ENUMERATE_HANDLE variable that is set to 0.
|
|||
|
|
|||
|
If the API returns STATUS_MORE_ENTRIES, then the API should be
|
|||
|
called again with EnumerationContext. When the API returns
|
|||
|
STATUS_SUCCESS or any error return, the handle becomes invalid for
|
|||
|
future use.
|
|||
|
|
|||
|
This API requires DOMAIN_LIST_ACCOUNTS access to the Domain object.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
DomainHandle - A domain handle returned from a previous call to
|
|||
|
SamOpenDomain.
|
|||
|
|
|||
|
EnumerationContext - API specific handle to allow multiple calls
|
|||
|
(see routine description). This is a zero based index.
|
|||
|
|
|||
|
Buffer - Receives a pointer to the buffer containing the
|
|||
|
requested information. The information returned is
|
|||
|
structured as an array of SAM_RID_ENUMERATION data
|
|||
|
structures. When this information is no longer needed, the
|
|||
|
buffer must be freed using SamFreeMemory().
|
|||
|
|
|||
|
PreferedMaximumLength - Prefered maximum length of returned data
|
|||
|
(in 8-bit bytes). This is not a hard upper limit, but serves
|
|||
|
as a guide to the server. 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:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully, and there
|
|||
|
are no additional entries.
|
|||
|
|
|||
|
STATUS_MORE_ENTRIES - There are more entries, so call again.
|
|||
|
This is a successful return.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have privilege required to
|
|||
|
request that data.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
PSAMPR_ENUMERATION_BUFFER LocalBuffer;
|
|||
|
|
|||
|
SampOutputDebugString("SamEnumerateGroupsInDomain");
|
|||
|
|
|||
|
//
|
|||
|
// Make sure we aren't trying to have RPC allocate the EnumerationContext.
|
|||
|
//
|
|||
|
|
|||
|
if ( !ARGUMENT_PRESENT(EnumerationContext) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
if ( !ARGUMENT_PRESENT(Buffer) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
if ( !ARGUMENT_PRESENT(CountReturned) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
(*Buffer) = NULL;
|
|||
|
LocalBuffer = NULL;
|
|||
|
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus = SamrEnumerateGroupsInDomain(
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
EnumerationContext,
|
|||
|
(PSAMPR_ENUMERATION_BUFFER *)&LocalBuffer,
|
|||
|
PreferedMaximumLength,
|
|||
|
CountReturned
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
if (LocalBuffer != NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// What comes back is a three level structure:
|
|||
|
//
|
|||
|
// Local +-------------+
|
|||
|
// Buffer ---> | EntriesRead |
|
|||
|
// |-------------| +-------+
|
|||
|
// | Enumeration |--->| Name0 | --- > (NameBuffer0)
|
|||
|
// | Return | |-------| o
|
|||
|
// | Buffer | | ... | o
|
|||
|
// +-------------+ |-------| o
|
|||
|
// | NameN | --- > (NameBufferN)
|
|||
|
// +-------+
|
|||
|
//
|
|||
|
// The buffer containing the EntriesRead field is not returned
|
|||
|
// to our caller. Only the buffers containing name information
|
|||
|
// are returned.
|
|||
|
//
|
|||
|
|
|||
|
if (LocalBuffer->Buffer != NULL) {
|
|||
|
(*Buffer) = LocalBuffer->Buffer;
|
|||
|
}
|
|||
|
|
|||
|
MIDL_user_free( LocalBuffer);
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamCreateUser2InDomain(
|
|||
|
IN SAM_HANDLE DomainHandle,
|
|||
|
IN PUNICODE_STRING AccountName,
|
|||
|
IN ULONG AccountType,
|
|||
|
IN ACCESS_MASK DesiredAccess,
|
|||
|
OUT PSAM_HANDLE UserHandle,
|
|||
|
OUT PULONG GrantedAccess,
|
|||
|
OUT PULONG RelativeId
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API adds a new user to the account database. The account is
|
|||
|
created in a disabled state. Default information is assigned to all
|
|||
|
fields except the account name. A password must be provided before
|
|||
|
the account may be enabled, unless the PasswordNotRequired control
|
|||
|
field is set.
|
|||
|
|
|||
|
This api may be used in either of two ways:
|
|||
|
|
|||
|
1) An administrative utility may use this api to create
|
|||
|
any type of user account. In this case, the DomainHandle
|
|||
|
is expected to be open for DOMAIN_CREATE_USER access.
|
|||
|
|
|||
|
2) A non-administrative user may use this api to create
|
|||
|
a machine account. In this case, the caller is expected
|
|||
|
to have the SE_CREATE_MACHINE_ACCOUNT_PRIV privilege
|
|||
|
and the DomainHandle is expected to be open for DOMAIN_LOOKUP
|
|||
|
access.
|
|||
|
|
|||
|
|
|||
|
For the normal administrative model ( #1 above), the creator will
|
|||
|
be assigned as the owner of the created user account. Furthermore,
|
|||
|
the new account will be give USER_WRITE access to itself.
|
|||
|
|
|||
|
For the special machine-account creation model (#2 above), the
|
|||
|
"Administrators" will be assigned as the owner of the account.
|
|||
|
Furthermore, the new account will be given NO access to itself.
|
|||
|
Instead, the creator of the account will be give USER_WRITE and
|
|||
|
DELETE access to the account.
|
|||
|
|
|||
|
|
|||
|
This call returns a handle to the newly created user that may be
|
|||
|
used for successive operations on the user. This handle may be
|
|||
|
closed with the SamCloseHandle() API. If a machine account is
|
|||
|
being created using model #2 above, then this handle will have
|
|||
|
only USER_WRITE and DELETE access. Otherwise, it will be open
|
|||
|
for USER_ALL_ACCESS.
|
|||
|
|
|||
|
|
|||
|
A newly created user will automatically be made a member of the
|
|||
|
DOMAIN_USERS group.
|
|||
|
|
|||
|
A newly created user will have the following initial field value
|
|||
|
settings. If another value is desired, it must be explicitly
|
|||
|
changed using the user object manipulation services.
|
|||
|
|
|||
|
UserName - the name of the account will be as specified in the
|
|||
|
creation API.
|
|||
|
|
|||
|
FullName - will be null.
|
|||
|
|
|||
|
UserComment - will be null.
|
|||
|
|
|||
|
Parameters - will be null.
|
|||
|
|
|||
|
CountryCode - will be zero.
|
|||
|
|
|||
|
UserId - will be a uniquelly allocated ID.
|
|||
|
|
|||
|
PrimaryGroupId - Will be DOMAIN_USERS.
|
|||
|
|
|||
|
PasswordLastSet - will be the time the account was created.
|
|||
|
|
|||
|
HomeDirectory - will be null.
|
|||
|
|
|||
|
HomeDirectoryDrive - will be null.
|
|||
|
|
|||
|
UserAccountControl - will have the following flags set:
|
|||
|
|
|||
|
UserAccountDisable,
|
|||
|
UserPasswordNotRequired,
|
|||
|
and the passed account type.
|
|||
|
|
|||
|
|
|||
|
ScriptPath - will be null.
|
|||
|
|
|||
|
WorkStations - will be null.
|
|||
|
|
|||
|
CaseInsensitiveDbcs - will be null.
|
|||
|
|
|||
|
CaseSensitiveUnicode - will be null.
|
|||
|
|
|||
|
LastLogon - will be zero delta time.
|
|||
|
|
|||
|
LastLogoff - will be zero delta time
|
|||
|
|
|||
|
AccountExpires - will be very far into the future.
|
|||
|
|
|||
|
BadPasswordCount - will be negative 1 (-1).
|
|||
|
|
|||
|
LastBadPasswordTime - will be SampHasNeverTime ( [High,Low] = [0,0] ).
|
|||
|
|
|||
|
LogonCount - will be negative 1 (-1).
|
|||
|
|
|||
|
AdminCount - will be zero.
|
|||
|
|
|||
|
AdminComment - will be null.
|
|||
|
|
|||
|
Password - will be "".
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
DomainHandle - A domain handle returned from a previous call to
|
|||
|
SamOpenDomain.
|
|||
|
|
|||
|
AccountName - Points to the name of the new account. A case-insensitive
|
|||
|
comparison must not find a group or user with this name already defined.
|
|||
|
|
|||
|
AccountType - Indicates what type of account is being created.
|
|||
|
Exactly one account type must be provided:
|
|||
|
|
|||
|
USER_INTERDOMAIN_TRUST_ACCOUNT
|
|||
|
USER_WORKSTATION_TRUST_ACCOUNT
|
|||
|
USER_SERVER_TRUST_ACCOUNT
|
|||
|
USER_TEMP_DUPLICATE_ACCOUNT
|
|||
|
USER_NORMAL_ACCOUNT
|
|||
|
USER_MACHINE_ACCOUNT_MASK
|
|||
|
|
|||
|
|
|||
|
DesiredAccess - Is an access mask indicating which access types
|
|||
|
are desired to the user.
|
|||
|
|
|||
|
UserHandle - Receives a handle referencing the newly created
|
|||
|
user. This handle will be required in successive calls to
|
|||
|
operate on the user.
|
|||
|
|
|||
|
GrantedAccess - Receives the accesses actually granted to via
|
|||
|
the UserHandle. When creating an account on a down-level
|
|||
|
server, this value may be unattainable. In this case, it
|
|||
|
will be returned as zero (0).
|
|||
|
|
|||
|
RelativeId - Receives the relative ID of the newly created user
|
|||
|
account. The SID of the new user account is this relative ID
|
|||
|
value prefixed with the domain's SID value.
|
|||
|
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_GROUP_EXISTS - The name is already in use as a group.
|
|||
|
|
|||
|
STATUS_USER_EXISTS - The name is already in use as a user.
|
|||
|
|
|||
|
STATUS_ALIAS_EXISTS - The name is already in use as an alias.
|
|||
|
|
|||
|
STATUS_INVALID_ACCOUNT_NAME - The name was poorly formed, e.g.
|
|||
|
contains non-printable characters.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be enabled before users
|
|||
|
can be created in it.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation. The domain server must be a primary server to
|
|||
|
create user accounts.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS
|
|||
|
NtStatus,
|
|||
|
IgnoreStatus;
|
|||
|
|
|||
|
|
|||
|
USER_CONTROL_INFORMATION
|
|||
|
UserControlInfoBuffer;
|
|||
|
|
|||
|
SampOutputDebugString("SamCreateUser2InDomain");
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
(*UserHandle) = NULL;
|
|||
|
(*RelativeId) = 0;
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrCreateUser2InDomain(
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
(PRPC_UNICODE_STRING)AccountName,
|
|||
|
AccountType,
|
|||
|
DesiredAccess,
|
|||
|
(SAMPR_HANDLE *)UserHandle,
|
|||
|
GrantedAccess,
|
|||
|
RelativeId
|
|||
|
);
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
if (RpcExceptionCode() == RPC_S_PROCNUM_OUT_OF_RANGE ||
|
|||
|
RpcExceptionCode() == RPC_NT_PROCNUM_OUT_OF_RANGE ) {
|
|||
|
NtStatus = RPC_NT_PROCNUM_OUT_OF_RANGE;
|
|||
|
} else {
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
}
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If the server doesn't support the new api, then
|
|||
|
// do the equivalent work with the old apis.
|
|||
|
//
|
|||
|
|
|||
|
if (NtStatus == RPC_NT_PROCNUM_OUT_OF_RANGE) {
|
|||
|
|
|||
|
DesiredAccess = DesiredAccess | USER_WRITE_ACCOUNT;
|
|||
|
NtStatus =
|
|||
|
SamCreateUserInDomain(
|
|||
|
DomainHandle,
|
|||
|
AccountName,
|
|||
|
DesiredAccess,
|
|||
|
UserHandle,
|
|||
|
RelativeId );
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Set the AccountType (unless it is normal)
|
|||
|
//
|
|||
|
|
|||
|
if (~(AccountType & USER_NORMAL_ACCOUNT)) {
|
|||
|
|
|||
|
UserControlInfoBuffer.UserAccountControl =
|
|||
|
AccountType |
|
|||
|
USER_ACCOUNT_DISABLED |
|
|||
|
USER_PASSWORD_NOT_REQUIRED;
|
|||
|
|
|||
|
NtStatus = SamSetInformationUser(
|
|||
|
(*UserHandle),
|
|||
|
UserControlInformation,
|
|||
|
&UserControlInfoBuffer
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
IgnoreStatus = SamDeleteUser( UserHandle );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We can't be positive what accesses have been
|
|||
|
// granted, so don't try lying.
|
|||
|
//
|
|||
|
|
|||
|
(*GrantedAccess) = 0;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamCreateUserInDomain(
|
|||
|
IN SAM_HANDLE DomainHandle,
|
|||
|
IN PUNICODE_STRING AccountName,
|
|||
|
IN ACCESS_MASK DesiredAccess,
|
|||
|
OUT PSAM_HANDLE UserHandle,
|
|||
|
OUT PULONG RelativeId
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API adds a new user to the account database. The account is
|
|||
|
created in a disabled state. Default information is assigned to all
|
|||
|
fields except the account name. A password must be provided before
|
|||
|
the account may be enabled, unless the PasswordNotRequired control
|
|||
|
field is set.
|
|||
|
|
|||
|
Note that DOMAIN_CREATE_USER access type is needed by this API.
|
|||
|
Also, the caller of this API becomes the owner of the user object
|
|||
|
upon creation.
|
|||
|
|
|||
|
This call returns a handle to the newly created user that may be
|
|||
|
used for successive operations on the user. This handle may be
|
|||
|
closed with the SamCloseHandle() API.
|
|||
|
|
|||
|
A newly created user will automatically be made a member of the
|
|||
|
DOMAIN_USERS group.
|
|||
|
|
|||
|
A newly created user will have the following initial field value
|
|||
|
settings. If another value is desired, it must be explicitly
|
|||
|
changed using the user object manipulation services.
|
|||
|
|
|||
|
UserName - the name of the account will be as specified in the
|
|||
|
creation API.
|
|||
|
|
|||
|
FullName - will be null.
|
|||
|
|
|||
|
UserComment - will be null.
|
|||
|
|
|||
|
Parameters - will be null.
|
|||
|
|
|||
|
CountryCode - will be zero.
|
|||
|
|
|||
|
UserId - will be a uniquelly allocated ID.
|
|||
|
|
|||
|
PrimaryGroupId - Will be DOMAIN_USERS.
|
|||
|
|
|||
|
PasswordLastSet - will be the time the account was created.
|
|||
|
|
|||
|
HomeDirectory - will be null.
|
|||
|
|
|||
|
HomeDirectoryDrive - will be null.
|
|||
|
|
|||
|
UserAccountControl - will have the following flags set:
|
|||
|
|
|||
|
USER_ACCOUNT_DISABLED,
|
|||
|
USER_NORMAL_ACCOUNT,
|
|||
|
USER_PASSWORD_NOT_REQUIRED
|
|||
|
|
|||
|
ScriptPath - will be null.
|
|||
|
|
|||
|
WorkStations - will be null.
|
|||
|
|
|||
|
CaseInsensitiveDbcs - will be null.
|
|||
|
|
|||
|
CaseSensitiveUnicode - will be null.
|
|||
|
|
|||
|
LastLogon - will be zero.
|
|||
|
|
|||
|
LastLogoff - will be zero.
|
|||
|
|
|||
|
AccountExpires - will be very far into the future.
|
|||
|
|
|||
|
BadPasswordCount - will be negative 1 (-1).
|
|||
|
|
|||
|
LogonCount - will be negative 1 (-1).
|
|||
|
|
|||
|
AdminComment - will be null.
|
|||
|
|
|||
|
Password - will contain any value, but is not used because the
|
|||
|
USER_PASSWORD_NOT_REQUIRED control flag is set. If a password
|
|||
|
is to be required, then this field must be set to a
|
|||
|
specific value and the USER_PASSWORD_NOT_REQUIRED flag must be
|
|||
|
cleared.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
DomainHandle - A domain handle returned from a previous call to
|
|||
|
SamOpenDomain.
|
|||
|
|
|||
|
AccountName - The name to be assigned to the new account.
|
|||
|
|
|||
|
DesiredAccess - Is an access mask indicating which access types
|
|||
|
are desired to the user.
|
|||
|
|
|||
|
UserHandle - Receives a handle referencing the newly created
|
|||
|
user. This handle will be required in successive calls to
|
|||
|
operate on the user.
|
|||
|
|
|||
|
RelativeId - Receives the relative ID of the newly created user
|
|||
|
account. The SID of the new user account is this relative ID
|
|||
|
value prefixed with the domain's SID value.
|
|||
|
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_GROUP_EXISTS - The name is already in use as a group.
|
|||
|
|
|||
|
STATUS_USER_EXISTS - The name is already in use as a user.
|
|||
|
|
|||
|
STATUS_ALIAS_EXISTS - The name is already in use as an alias.
|
|||
|
|
|||
|
STATUS_INVALID_ACCOUNT_NAME - The name was poorly formed, e.g.
|
|||
|
contains non-printable characters.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be enabled before users
|
|||
|
can be created in it.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation. The domain server must be a primary server to
|
|||
|
create user accounts.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
SampOutputDebugString("SamCreateUserInDomain");
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
(*UserHandle) = NULL;
|
|||
|
(*RelativeId) = 0;
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrCreateUserInDomain(
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
(PRPC_UNICODE_STRING)AccountName,
|
|||
|
DesiredAccess,
|
|||
|
(SAMPR_HANDLE *)UserHandle,
|
|||
|
RelativeId
|
|||
|
);
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamEnumerateUsersInDomain(
|
|||
|
IN SAM_HANDLE DomainHandle,
|
|||
|
IN OUT PSAM_ENUMERATE_HANDLE EnumerationContext,
|
|||
|
IN ULONG UserAccountControl,
|
|||
|
OUT PVOID * Buffer,
|
|||
|
IN ULONG PreferedMaximumLength,
|
|||
|
OUT PULONG CountReturned
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API lists all the users defined in the account database. Since
|
|||
|
there may be more users than can fit into a buffer, 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
|
|||
|
SAM_ENUMERATE_HANDLE variable that is set to 0.
|
|||
|
|
|||
|
If the API returns STATUS_MORE_ENTRIES, then the API should be
|
|||
|
called again with EnumerationContext. When the API returns
|
|||
|
STATUS_SUCCESS or any error return, the handle becomes invalid for
|
|||
|
future use.
|
|||
|
|
|||
|
This API requires DOMAIN_LIST_ACCOUNTS access to the Domain object.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
DomainHandle - A domain handle returned from a previous call to
|
|||
|
SamOpenDomain.
|
|||
|
|
|||
|
EnumerationContext - API specific handle to allow multiple calls
|
|||
|
(see routine description). This is a zero based index.
|
|||
|
|
|||
|
UserAccountControl - Provides enumeration filtering information. Any
|
|||
|
characteristics specified here will cause that type of User account
|
|||
|
to be included in the enumeration process.
|
|||
|
|
|||
|
Buffer - Receives a pointer to the buffer containing the
|
|||
|
requested information. The information returned is
|
|||
|
structured as an array of SAM_RID_ENUMERATION data
|
|||
|
structures. When this information is no longer needed, the
|
|||
|
buffer must be freed using SamFreeMemory().
|
|||
|
|
|||
|
PreferedMaximumLength - Prefered maximum length of returned data
|
|||
|
(in 8-bit bytes). This is not a hard upper limit, but serves
|
|||
|
as a guide to the server. 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:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully, and there
|
|||
|
are no additional entries.
|
|||
|
|
|||
|
STATUS_MORE_ENTRIES - There are more entries, so call again.
|
|||
|
This is a successful return.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have privilege required to
|
|||
|
request that data.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
PSAMPR_ENUMERATION_BUFFER LocalBuffer;
|
|||
|
|
|||
|
SampOutputDebugString("SamEnumerateUsersInDomain");
|
|||
|
|
|||
|
//
|
|||
|
// Make sure we aren't trying to have RPC allocate the EnumerationContext.
|
|||
|
//
|
|||
|
|
|||
|
if ( !ARGUMENT_PRESENT(EnumerationContext) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
if ( !ARGUMENT_PRESENT(Buffer) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
if ( !ARGUMENT_PRESENT(CountReturned) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
(*Buffer) = NULL;
|
|||
|
LocalBuffer = NULL;
|
|||
|
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus = SamrEnumerateUsersInDomain(
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
EnumerationContext,
|
|||
|
UserAccountControl,
|
|||
|
(PSAMPR_ENUMERATION_BUFFER *)&LocalBuffer,
|
|||
|
PreferedMaximumLength,
|
|||
|
CountReturned
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
if (LocalBuffer != NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// What comes back is a three level structure:
|
|||
|
//
|
|||
|
// Local +-------------+
|
|||
|
// Buffer ---> | EntriesRead |
|
|||
|
// |-------------| +-------+
|
|||
|
// | Enumeration |--->| Name0 | --- > (NameBuffer0)
|
|||
|
// | Return | |-------| o
|
|||
|
// | Buffer | | ... | o
|
|||
|
// +-------------+ |-------| o
|
|||
|
// | NameN | --- > (NameBufferN)
|
|||
|
// +-------+
|
|||
|
//
|
|||
|
// The buffer containing the EntriesRead field is not returned
|
|||
|
// to our caller. Only the buffers containing name information
|
|||
|
// are returned.
|
|||
|
//
|
|||
|
|
|||
|
if (LocalBuffer->Buffer != NULL) {
|
|||
|
(*Buffer) = LocalBuffer->Buffer;
|
|||
|
}
|
|||
|
|
|||
|
MIDL_user_free( LocalBuffer);
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamCreateAliasInDomain(
|
|||
|
IN SAM_HANDLE DomainHandle,
|
|||
|
IN PUNICODE_STRING AccountName,
|
|||
|
IN ACCESS_MASK DesiredAccess,
|
|||
|
OUT PSAM_HANDLE AliasHandle,
|
|||
|
OUT PULONG RelativeId
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API adds a new alias to the account database. Initially, this
|
|||
|
alias does not contain any members.
|
|||
|
|
|||
|
This call returns a handle to the newly created account that may be
|
|||
|
used for successive operations on the object. This handle may be
|
|||
|
closed with the SamCloseHandle API.
|
|||
|
|
|||
|
A newly created group will have the following initial field value
|
|||
|
settings. If another value is desired, it must be explicitly changed
|
|||
|
using the Alias object manipulation services.
|
|||
|
|
|||
|
Name - the name of the account will be as specified in the creation
|
|||
|
API.
|
|||
|
|
|||
|
MemberCount - Zero. Initially the alias has no members.
|
|||
|
|
|||
|
RelativeId - will be a uniquelly allocated ID.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
DomainHandle - A domain handle returned from a previous call to
|
|||
|
SamOpenDomain. The handle must be open for DOMAIN_CREATE_ALIAS
|
|||
|
access.
|
|||
|
|
|||
|
AccountName - The name of the alias to be added.
|
|||
|
|
|||
|
DesiredAccess - Is an access mask indicating which access types
|
|||
|
are desired to the alias.
|
|||
|
|
|||
|
AliasHandle - Receives a handle referencing the newly created
|
|||
|
alias. This handle will be required in successive calls to
|
|||
|
operate on the alias.
|
|||
|
|
|||
|
RelativeId - Receives the relative ID of the newly created alias.
|
|||
|
The SID of the new alias is this relative ID value prefixed with
|
|||
|
the domain's SID value.
|
|||
|
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The account was added successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_INVALID_ACCOUNT_NAME - The name was poorly formed, e.g.
|
|||
|
contains non-printable characters.
|
|||
|
|
|||
|
STATUS_GROUP_EXISTS - The name is already in use as a group.
|
|||
|
|
|||
|
STATUS_USER_EXISTS - The name is already in use as a user.
|
|||
|
|
|||
|
STATUS_ALIAS_EXISTS - The name is already in use as an alias.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be enabled before aliases
|
|||
|
can be created in it.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation. The domain server must be a primary server to
|
|||
|
create aliases.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
SampOutputDebugString("SamCreateAliasInDomain");
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
(*AliasHandle) = NULL;
|
|||
|
(*RelativeId) = 0;
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrCreateAliasInDomain(
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
(PRPC_UNICODE_STRING)AccountName,
|
|||
|
DesiredAccess,
|
|||
|
(SAMPR_HANDLE *)AliasHandle,
|
|||
|
RelativeId
|
|||
|
);
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamEnumerateAliasesInDomain(
|
|||
|
IN SAM_HANDLE DomainHandle,
|
|||
|
IN OUT PSAM_ENUMERATE_HANDLE EnumerationContext,
|
|||
|
IN PVOID *Buffer,
|
|||
|
IN ULONG PreferedMaximumLength,
|
|||
|
OUT PULONG CountReturned
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API lists all the aliases defined in the account database. Since
|
|||
|
there may be more aliases than can fit into a buffer, 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
|
|||
|
SAM_ENUMERATE_HANDLE variable that is set to 0.
|
|||
|
|
|||
|
If the API returns STATUS_MORE_ENTRIES, then the API should be
|
|||
|
called again with EnumerationContext. When the API returns
|
|||
|
STATUS_SUCCESS or any error return, the handle becomes invalid for
|
|||
|
future use.
|
|||
|
|
|||
|
This API requires DOMAIN_LIST_ACCOUNTS access to the Domain object.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
DomainHandle - A domain handle returned from a previous call to
|
|||
|
SamOpenDomain.
|
|||
|
|
|||
|
EnumerationContext - API specific handle to allow multiple calls
|
|||
|
(see routine description). This is a zero based index.
|
|||
|
|
|||
|
Buffer - Receives a pointer to the buffer containing the
|
|||
|
requested information. The information returned is
|
|||
|
structured as an array of SAM_RID_ENUMERATION data
|
|||
|
structures. When this information is no longer needed, the
|
|||
|
buffer must be freed using SamFreeMemory().
|
|||
|
|
|||
|
PreferedMaximumLength - Prefered maximum length of returned data
|
|||
|
(in 8-bit bytes). This is not a hard upper limit, but serves
|
|||
|
as a guide to the server. 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:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully, and there
|
|||
|
are no additional entries.
|
|||
|
|
|||
|
STATUS_MORE_ENTRIES - There are more entries, so call again.
|
|||
|
This is a successful return.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have privilege required to
|
|||
|
request that data.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
PSAMPR_ENUMERATION_BUFFER LocalBuffer;
|
|||
|
|
|||
|
SampOutputDebugString("SamEnumerateAliasesInDomain");
|
|||
|
|
|||
|
//
|
|||
|
// Make sure we aren't trying to have RPC allocate the EnumerationContext.
|
|||
|
//
|
|||
|
|
|||
|
if ( !ARGUMENT_PRESENT(EnumerationContext) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
if ( !ARGUMENT_PRESENT(Buffer) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
if ( !ARGUMENT_PRESENT(CountReturned) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
(*Buffer) = NULL;
|
|||
|
LocalBuffer = NULL;
|
|||
|
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus = SamrEnumerateAliasesInDomain(
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
EnumerationContext,
|
|||
|
(PSAMPR_ENUMERATION_BUFFER *)&LocalBuffer,
|
|||
|
PreferedMaximumLength,
|
|||
|
CountReturned
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
if (LocalBuffer != NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// What comes back is a three level structure:
|
|||
|
//
|
|||
|
// Local +-------------+
|
|||
|
// Buffer ---> | EntriesRead |
|
|||
|
// |-------------| +-------+
|
|||
|
// | Enumeration |--->| Name0 | --- > (NameBuffer0)
|
|||
|
// | Return | |-------| o
|
|||
|
// | Buffer | | ... | o
|
|||
|
// +-------------+ |-------| o
|
|||
|
// | NameN | --- > (NameBufferN)
|
|||
|
// +-------+
|
|||
|
//
|
|||
|
// The buffer containing the EntriesRead field is not returned
|
|||
|
// to our caller. Only the buffers containing name information
|
|||
|
// are returned.
|
|||
|
//
|
|||
|
|
|||
|
if (LocalBuffer->Buffer != NULL) {
|
|||
|
(*Buffer) = LocalBuffer->Buffer;
|
|||
|
}
|
|||
|
|
|||
|
MIDL_user_free( LocalBuffer);
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamGetAliasMembership(
|
|||
|
IN SAM_HANDLE DomainHandle,
|
|||
|
IN ULONG PassedCount,
|
|||
|
IN PSID *Sids,
|
|||
|
OUT PULONG MembershipCount,
|
|||
|
OUT PULONG *Aliases
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API searches the set of aliases in the specified domain to see
|
|||
|
which aliases, if any, the passed SIDs are members of. Any aliases
|
|||
|
that any of the SIDs are found to be members of are returned.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
DomainHandle - A domain handle returned from a previous call to
|
|||
|
SamOpenDomain.
|
|||
|
|
|||
|
PassedCount - Specifies the number of Sids being passed.
|
|||
|
|
|||
|
Sids - Pointer to an array of Count pointers to Sids whose alias
|
|||
|
memberships are to be looked up.
|
|||
|
|
|||
|
MembershipCount - Receives the number of aliases that are being
|
|||
|
returned via the Aliases parameter.
|
|||
|
|
|||
|
Aliases - Receives a pointer to an array of SIDs. This is the set
|
|||
|
of aliases the passed SIDs were found to be members of. If
|
|||
|
MembershipCount is returned as zero, then a null value will be
|
|||
|
returned here.
|
|||
|
|
|||
|
When this information is no longer needed, it must be released
|
|||
|
by passing the returned pointer to SamFreeMemory().
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have privilege required to
|
|||
|
request that data.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
SAMPR_PSID_ARRAY Accounts;
|
|||
|
SAMPR_ULONG_ARRAY Membership;
|
|||
|
|
|||
|
SampOutputDebugString("SamAliasMembership");
|
|||
|
|
|||
|
//
|
|||
|
// Make sure we aren't trying to have RPC allocate the EnumerationContext.
|
|||
|
//
|
|||
|
|
|||
|
if ( !ARGUMENT_PRESENT(Sids) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
if ( !ARGUMENT_PRESENT(MembershipCount) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
if ( !ARGUMENT_PRESENT(Aliases) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
Membership.Element = NULL;
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
Accounts.Count = PassedCount;
|
|||
|
Accounts.Sids = (PSAMPR_SID_INFORMATION)Sids;
|
|||
|
|
|||
|
NtStatus = SamrGetAliasMembership(
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
&Accounts,
|
|||
|
&Membership
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
(*MembershipCount) = Membership.Count;
|
|||
|
(*Aliases) = Membership.Element;
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Deallocate any returned buffers on error
|
|||
|
//
|
|||
|
|
|||
|
if (Membership.Element != NULL) {
|
|||
|
MIDL_user_free(Membership.Element);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamLookupNamesInDomain(
|
|||
|
IN SAM_HANDLE DomainHandle,
|
|||
|
IN ULONG Count,
|
|||
|
IN PUNICODE_STRING Names,
|
|||
|
OUT PULONG *RelativeIds,
|
|||
|
OUT PSID_NAME_USE *Use
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API attempts to find relative IDs corresponding to name
|
|||
|
strings. If a name can not be mapped to a relative ID, a zero is
|
|||
|
placed in the corresponding relative ID array entry, and translation
|
|||
|
continues.
|
|||
|
|
|||
|
DOMAIN_LOOKUP access to the domain is needed to use this service.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
DomainHandle - A domain handle returned from a previous call to
|
|||
|
SamOpenDomain.
|
|||
|
|
|||
|
Count - Number of names to translate.
|
|||
|
|
|||
|
Names - Pointer to an array of Count UNICODE_STRINGs that contain
|
|||
|
the names to map to relative IDs. Case-insensitive
|
|||
|
comparisons of these names will be performed for the lookup
|
|||
|
operation.
|
|||
|
|
|||
|
RelativeIds - Receives a pointer to an array of Count Relative IDs
|
|||
|
that have been filled in. The relative ID of the nth name will
|
|||
|
be the nth entry in this array. Any names that could not be
|
|||
|
translated will have a zero relative ID. This buffer must be
|
|||
|
freed when no longer needed using SamFreeMemory().
|
|||
|
|
|||
|
Use - Recieves a pointer to an array of Count SID_NAME_USE
|
|||
|
entries that have been filled in with what significance each
|
|||
|
name has. The nth entry in this array indicates the meaning
|
|||
|
of the nth name passed. This buffer must be freed when no longer
|
|||
|
needed using SamFreeMemory().
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The domain handle passed is invalid.
|
|||
|
|
|||
|
STATUS_SOME_NOT_MAPPED - Some of the names provided could not be
|
|||
|
mapped. This is a successful return.
|
|||
|
|
|||
|
STATUS_NONE_MAPPED - No names could be mapped. This is an error
|
|||
|
return.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ReturnStatus,
|
|||
|
NtStatus;
|
|||
|
|
|||
|
LIST_ENTRY
|
|||
|
CallHead;
|
|||
|
|
|||
|
PSAMP_NAME_LOOKUP_CALL
|
|||
|
Next;
|
|||
|
|
|||
|
PSID_NAME_USE
|
|||
|
UseBuffer;
|
|||
|
|
|||
|
PULONG
|
|||
|
RidBuffer;
|
|||
|
|
|||
|
ULONG
|
|||
|
Calls,
|
|||
|
CallLength,
|
|||
|
i;
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
NoneMapped = TRUE,
|
|||
|
SomeNotMapped = FALSE;
|
|||
|
|
|||
|
SampOutputDebugString("SamLookupNamesInDomain");
|
|||
|
|
|||
|
|
|||
|
if ( (Count == 0) || (Names == NULL) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Default error return
|
|||
|
//
|
|||
|
|
|||
|
(*Use) = UseBuffer = NULL;
|
|||
|
(*RelativeIds) = RidBuffer = NULL;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Set up the call structures list
|
|||
|
//
|
|||
|
|
|||
|
InitializeListHead( &CallHead );
|
|||
|
Calls = 0;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// By default we will return NONE_MAPPED.
|
|||
|
// This will get superseded by either STATUS_SUCCESS
|
|||
|
// or STATUS_SOME_NOT_MAPPED.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Now build up and make each call
|
|||
|
//
|
|||
|
|
|||
|
i = 0;
|
|||
|
while ( i < Count ) {
|
|||
|
|
|||
|
//
|
|||
|
// Make sure the next entry isn't too long.
|
|||
|
// That would put us in an infinite loop.
|
|||
|
//
|
|||
|
|
|||
|
if (Names[i].Length > SAM_MAXIMUM_LOOKUP_LENGTH) {
|
|||
|
ReturnStatus = STATUS_INVALID_PARAMETER;
|
|||
|
goto SampNameLookupFreeAndReturn;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the next call structure
|
|||
|
//
|
|||
|
|
|||
|
Next = (PSAMP_NAME_LOOKUP_CALL)MIDL_user_allocate( sizeof(SAMP_NAME_LOOKUP_CALL) );
|
|||
|
|
|||
|
if (Next == NULL) {
|
|||
|
ReturnStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
goto SampNameLookupFreeAndReturn;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the call structure.
|
|||
|
// It takes a little to figure out how many entries to send in
|
|||
|
// this call. It is limited by both Count (sam_MAXIMUM_LOOKUP_COUNT)
|
|||
|
// and by size (SAM_MAXIMUM_LOOKUP_LENGTH).
|
|||
|
//
|
|||
|
|
|||
|
Next->Count = 0;
|
|||
|
Next->StartIndex = i;
|
|||
|
Next->RidBuffer.Element = NULL;
|
|||
|
Next->UseBuffer.Element = NULL;
|
|||
|
|
|||
|
CallLength = 0;
|
|||
|
for ( i=i;
|
|||
|
( (i < Count) &&
|
|||
|
(CallLength+Names[i].Length < SAM_MAXIMUM_LOOKUP_LENGTH) &&
|
|||
|
(Next->Count < SAM_MAXIMUM_LOOKUP_COUNT)
|
|||
|
);
|
|||
|
i++ ) {
|
|||
|
|
|||
|
//
|
|||
|
// Add in the next length and increment the number of entries
|
|||
|
// being processed by this call.
|
|||
|
//
|
|||
|
|
|||
|
CallLength += Names[i].Length;
|
|||
|
Next->Count ++;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Add this call structure to the list of call structures
|
|||
|
//
|
|||
|
|
|||
|
Calls ++;
|
|||
|
InsertTailList( &CallHead, &Next->Link );
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Now make the call
|
|||
|
//
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus = SamrLookupNamesInDomain(
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
Next->Count,
|
|||
|
(PRPC_UNICODE_STRING)(&Names[Next->StartIndex]),
|
|||
|
&Next->RidBuffer,
|
|||
|
&Next->UseBuffer
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Keep track of what our completion status should be.
|
|||
|
//
|
|||
|
|
|||
|
if (!NT_SUCCESS(NtStatus) &&
|
|||
|
NtStatus != STATUS_NONE_MAPPED) {
|
|||
|
ReturnStatus = NtStatus; // Unexpected error
|
|||
|
goto SampNameLookupFreeAndReturn;
|
|||
|
}
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
NoneMapped = FALSE;
|
|||
|
if (NtStatus == STATUS_SOME_NOT_MAPPED) {
|
|||
|
SomeNotMapped = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Set our return status...
|
|||
|
//
|
|||
|
|
|||
|
if (NoneMapped) {
|
|||
|
ASSERT(SomeNotMapped == FALSE);
|
|||
|
ReturnStatus = STATUS_NONE_MAPPED;
|
|||
|
} else if (SomeNotMapped) {
|
|||
|
ReturnStatus = STATUS_SOME_NOT_MAPPED;
|
|||
|
} else {
|
|||
|
ReturnStatus = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// At this point we have (potentially) a lot of call structures.
|
|||
|
// The RidBuffer and UseBuffer elements of each call structure
|
|||
|
// is allocated and returned by the RPC call and looks
|
|||
|
// like:
|
|||
|
//
|
|||
|
// RidBuffer
|
|||
|
// +-------------+
|
|||
|
// | Count |
|
|||
|
// |-------------| +-------+ *
|
|||
|
// | Element ---|--->| Rid-0 | | /
|
|||
|
// +-------------+ |-------| | / Only this part
|
|||
|
// | ... | > < is allocated by
|
|||
|
// |-------| | \ the rpc call.
|
|||
|
// | Rid-N | | \
|
|||
|
// +-------+ *
|
|||
|
//
|
|||
|
// If only one RPC call was made, we can return this information
|
|||
|
// directly. Otherwise, we need to copy the information from
|
|||
|
// all the calls into a single large buffer and return that buffer
|
|||
|
// (freeing all the individual call buffers).
|
|||
|
//
|
|||
|
// The user is responsible for freeing whichever buffer we do
|
|||
|
// return.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(Calls != 0); // Error go around this path, success always has calls
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Optimize for a single call
|
|||
|
//
|
|||
|
|
|||
|
if (Calls == 1) {
|
|||
|
(*Use) = (PSID_NAME_USE)
|
|||
|
(((PSAMP_NAME_LOOKUP_CALL)(CallHead.Flink))->
|
|||
|
UseBuffer.Element);
|
|||
|
(*RelativeIds) = ((PSAMP_NAME_LOOKUP_CALL)(CallHead.Flink))->
|
|||
|
RidBuffer.Element;
|
|||
|
MIDL_user_free( CallHead.Flink ); // Free the call structure
|
|||
|
return(ReturnStatus);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// More than one call.
|
|||
|
// Allocate return buffers large enough to copy all the information into.
|
|||
|
//
|
|||
|
|
|||
|
RidBuffer = MIDL_user_allocate( sizeof(ULONG) * Count );
|
|||
|
if (RidBuffer == NULL) {
|
|||
|
ReturnStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
goto SampNameLookupFreeAndReturn;
|
|||
|
}
|
|||
|
|
|||
|
UseBuffer = MIDL_user_allocate( sizeof(SID_NAME_USE) * Count );
|
|||
|
if (UseBuffer == NULL) {
|
|||
|
MIDL_user_free( RidBuffer );
|
|||
|
RidBuffer = NULL;
|
|||
|
ReturnStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
goto SampNameLookupFreeAndReturn;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
SampNameLookupFreeAndReturn:
|
|||
|
|
|||
|
//
|
|||
|
// Walk the list of calls.
|
|||
|
// For each call:
|
|||
|
//
|
|||
|
// If we have a return buffer, copy the results into it.
|
|||
|
// Free the call buffers.
|
|||
|
// Free the call structure itself.
|
|||
|
//
|
|||
|
// Completion status has already been set appropriatly in ReturnStatus.
|
|||
|
//
|
|||
|
|
|||
|
Next = (PSAMP_NAME_LOOKUP_CALL)RemoveHeadList( &CallHead );
|
|||
|
while (Next != (PSAMP_NAME_LOOKUP_CALL)&CallHead) {
|
|||
|
|
|||
|
//
|
|||
|
// Copy RID information and then free the call buffer
|
|||
|
//
|
|||
|
|
|||
|
if (RidBuffer != NULL) {
|
|||
|
RtlMoveMemory(
|
|||
|
&RidBuffer[ Next->StartIndex ], // Destination
|
|||
|
&Next->RidBuffer.Element[0], // Source
|
|||
|
Next->Count * sizeof(ULONG) // Length
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
if (Next->RidBuffer.Element != NULL) {
|
|||
|
MIDL_user_free( Next->RidBuffer.Element );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Copy USE information and then free the call buffer
|
|||
|
//
|
|||
|
|
|||
|
if (UseBuffer != NULL) {
|
|||
|
RtlMoveMemory(
|
|||
|
&UseBuffer[ Next->StartIndex ], // Destination
|
|||
|
&Next->UseBuffer.Element[0], // Source
|
|||
|
Next->Count * sizeof(SID_NAME_USE) // Length
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
if (Next->UseBuffer.Element != NULL) {
|
|||
|
MIDL_user_free( Next->UseBuffer.Element );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Free the call structure itself
|
|||
|
//
|
|||
|
|
|||
|
MIDL_user_free( Next );
|
|||
|
|
|||
|
Next = (PSAMP_NAME_LOOKUP_CALL)RemoveHeadList( &CallHead );
|
|||
|
} // end-while
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// For better or worse, we're all done
|
|||
|
//
|
|||
|
|
|||
|
(*Use) = UseBuffer;
|
|||
|
(*RelativeIds) = RidBuffer;
|
|||
|
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamLookupIdsInDomain(
|
|||
|
IN SAM_HANDLE DomainHandle,
|
|||
|
IN ULONG Count,
|
|||
|
IN PULONG RelativeIds,
|
|||
|
OUT PUNICODE_STRING *Names,
|
|||
|
OUT PSID_NAME_USE *Use OPTIONAL
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API maps a number of relative IDs to their corresponding names.
|
|||
|
The use of the name (domain, group, alias, user, or unknown) is also
|
|||
|
returned.
|
|||
|
|
|||
|
The API stores the actual names in Buffer, then creates an array of
|
|||
|
UNICODE_STRINGs in the Names OUT parameter. If a relative ID can
|
|||
|
not be mapped, a NULL value is placed in the slot for the
|
|||
|
UNICODE_STRING, and STATUS_SOME_NOT_MAPPED is returned.
|
|||
|
|
|||
|
DOMAIN_LOOKUP access to the domain is needed to use this service.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
DomainHandle - A domain handle returned from a previous call to
|
|||
|
SamOpenDomain.
|
|||
|
|
|||
|
Count - Provides the number of relative IDs to translate.
|
|||
|
|
|||
|
RelativeIds - Array of Count relative IDs to be mapped.
|
|||
|
|
|||
|
Names - Receives a pointer to an array of Count UNICODE_STRINGs that
|
|||
|
have been filled in. The nth pointer within this array will
|
|||
|
correspond the nth relative id passed . Each name string buffer
|
|||
|
will be in a separately allocated block of memory. Any entry is
|
|||
|
not successfully translated will have a NULL name buffer pointer
|
|||
|
returned. This Names buffer must be freed using SamFreeMemory()
|
|||
|
when no longer needed.
|
|||
|
|
|||
|
Use - Optionally, receives a pointer to an array of Count SID_NAME_USE
|
|||
|
entries that have been filled in with what significance each
|
|||
|
name has. The nth entry in this array indicates the meaning
|
|||
|
of the nth name passed. This buffer must be freed when no longer
|
|||
|
needed using SamFreeMemory().
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The domain handle passed is invalid.
|
|||
|
|
|||
|
STATUS_SOME_NOT_MAPPED - Some of the names provided could not be
|
|||
|
mapped. This is a successful return.
|
|||
|
|
|||
|
STATUS_NONE_MAPPED - No names could be mapped. This is an error
|
|||
|
return.
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
ULONG SubRequest, SubRequests;
|
|||
|
ULONG TotalCountToDate;
|
|||
|
ULONG Index, UsedLength, Length, NamesLength;
|
|||
|
ULONG UsesLength, LastSubRequestCount;
|
|||
|
PULONG UstringStructDisps;
|
|||
|
PULONG Counts = NULL;
|
|||
|
PULONG RidIndices = NULL;
|
|||
|
PUNICODE_STRING *SubRequestNames = NULL;
|
|||
|
PSID_NAME_USE *SubRequestUses = NULL;
|
|||
|
PUNICODE_STRING OutputNames = NULL;
|
|||
|
PSID_NAME_USE OutputUses = NULL;
|
|||
|
PUCHAR Destination = NULL, Source = NULL;
|
|||
|
PUNICODE_STRING DestUstring = NULL;
|
|||
|
ULONG SomeNotMappedStatusCount = 0;
|
|||
|
ULONG NoneMappedStatusCount = 0;
|
|||
|
|
|||
|
SampOutputDebugString("SamLookupIdsInDomain");
|
|||
|
|
|||
|
//
|
|||
|
// If the Count for this request does not exceed the maximum limit that
|
|||
|
// can be looked up in a single call, just call the Sub Request version
|
|||
|
// of the routine.
|
|||
|
//
|
|||
|
|
|||
|
if (Count <= SAM_MAXIMUM_LOOKUP_COUNT) {
|
|||
|
|
|||
|
NtStatus = SampLookupIdsInDomain(
|
|||
|
DomainHandle,
|
|||
|
Count,
|
|||
|
RelativeIds,
|
|||
|
Names,
|
|||
|
Use
|
|||
|
);
|
|||
|
|
|||
|
return(NtStatus);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Break down larger requests into smaller chunks
|
|||
|
//
|
|||
|
|
|||
|
SubRequests = Count / SAMP_MAXIMUM_SUB_LOOKUP_COUNT;
|
|||
|
LastSubRequestCount = Count % SAMP_MAXIMUM_SUB_LOOKUP_COUNT;
|
|||
|
|
|||
|
if (LastSubRequestCount > 0) {
|
|||
|
|
|||
|
SubRequests++;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Allocate memory for array of starting Rid Indices, Rid Counts and
|
|||
|
// Unicode String block offsets for each SubRequest.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = STATUS_NO_MEMORY;
|
|||
|
|
|||
|
RidIndices = MIDL_user_allocate( SubRequests * sizeof(ULONG) );
|
|||
|
|
|||
|
if (RidIndices == NULL) {
|
|||
|
|
|||
|
goto LookupIdsInDomainError;
|
|||
|
}
|
|||
|
|
|||
|
Counts = MIDL_user_allocate( SubRequests * sizeof(ULONG) );
|
|||
|
|
|||
|
if (Counts == NULL) {
|
|||
|
|
|||
|
goto LookupIdsInDomainError;
|
|||
|
}
|
|||
|
|
|||
|
SubRequestNames = MIDL_user_allocate( SubRequests * sizeof(PUNICODE_STRING) );
|
|||
|
|
|||
|
if (SubRequestNames == NULL) {
|
|||
|
|
|||
|
goto LookupIdsInDomainError;
|
|||
|
}
|
|||
|
|
|||
|
SubRequestUses = MIDL_user_allocate( SubRequests * sizeof(SID_NAME_USE) );
|
|||
|
|
|||
|
if (SubRequestUses == NULL) {
|
|||
|
|
|||
|
goto LookupIdsInDomainError;
|
|||
|
}
|
|||
|
|
|||
|
UstringStructDisps = MIDL_user_allocate( SubRequests * sizeof(ULONG) );
|
|||
|
|
|||
|
if (UstringStructDisps == NULL) {
|
|||
|
|
|||
|
goto LookupIdsInDomainError;
|
|||
|
}
|
|||
|
|
|||
|
NtStatus = STATUS_SUCCESS;
|
|||
|
|
|||
|
TotalCountToDate = 0;
|
|||
|
|
|||
|
for (SubRequest = 0; SubRequest < SubRequests; SubRequest++) {
|
|||
|
|
|||
|
RidIndices[SubRequest] = TotalCountToDate;
|
|||
|
|
|||
|
if ((Count - TotalCountToDate) > SAMP_MAXIMUM_SUB_LOOKUP_COUNT) {
|
|||
|
|
|||
|
Counts[SubRequest] = SAMP_MAXIMUM_SUB_LOOKUP_COUNT;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
Counts[SubRequest] = Count - TotalCountToDate;
|
|||
|
}
|
|||
|
|
|||
|
TotalCountToDate += Counts[SubRequest];
|
|||
|
|
|||
|
NtStatus = SampLookupIdsInDomain(
|
|||
|
DomainHandle,
|
|||
|
Counts[SubRequest],
|
|||
|
&RelativeIds[RidIndices[SubRequest]],
|
|||
|
&SubRequestNames[SubRequest],
|
|||
|
&SubRequestUses[SubRequest]
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// We keep a tally of the number of times STATUS_SOME_NOT_MAPPED
|
|||
|
// and STATUS_NONE_MAPPED were returned. This is so that we
|
|||
|
// can return the appropriate status at the end based on the
|
|||
|
// global picture. We continue lookups after either status code
|
|||
|
// is encountered.
|
|||
|
//
|
|||
|
|
|||
|
if (NtStatus == STATUS_SOME_NOT_MAPPED) {
|
|||
|
|
|||
|
SomeNotMappedStatusCount++;
|
|||
|
|
|||
|
} else if (NtStatus == STATUS_NONE_MAPPED) {
|
|||
|
|
|||
|
NoneMappedStatusCount++;
|
|||
|
NtStatus = STATUS_SUCCESS;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
goto LookupIdsInDomainError;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now allocate a single buffer for the Names
|
|||
|
//
|
|||
|
|
|||
|
NamesLength = Count * sizeof(UNICODE_STRING);
|
|||
|
|
|||
|
for (SubRequest = 0; SubRequest < SubRequests; SubRequest++) {
|
|||
|
|
|||
|
for (Index = 0; Index < Counts[SubRequest]; Index++) {
|
|||
|
|
|||
|
NamesLength += (SubRequestNames[SubRequest] + Index)->MaximumLength;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
|
|||
|
OutputNames = MIDL_user_allocate( NamesLength );
|
|||
|
|
|||
|
if (OutputNames == NULL) {
|
|||
|
|
|||
|
goto LookupIdsInDomainError;
|
|||
|
}
|
|||
|
|
|||
|
NtStatus = STATUS_SUCCESS;
|
|||
|
|
|||
|
//
|
|||
|
// Now copy in the Unicode String Structures for the Names returned from
|
|||
|
// each subrequest. We will later overwrite the Buffer fields in them
|
|||
|
// when we assign space and move in each Unicode String.
|
|||
|
//
|
|||
|
|
|||
|
Destination = (PUCHAR) OutputNames;
|
|||
|
UsedLength = 0;
|
|||
|
|
|||
|
for (SubRequest = 0; SubRequest < SubRequests; SubRequest++) {
|
|||
|
|
|||
|
Source = (PUCHAR) SubRequestNames[SubRequest];
|
|||
|
Length = Counts[SubRequest] * sizeof(UNICODE_STRING);
|
|||
|
UstringStructDisps[SubRequest] = (Destination - Source);
|
|||
|
RtlMoveMemory( Destination, Source, Length );
|
|||
|
Destination += Length;
|
|||
|
UsedLength += Length;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now copy in the Unicode Strings themselves. These are appended to
|
|||
|
// the array of Unicode String structures. As we go, update the
|
|||
|
// Unicode string buffer pointers to point to the copied version
|
|||
|
// of each string.
|
|||
|
//
|
|||
|
|
|||
|
for (SubRequest = 0; SubRequest < SubRequests; SubRequest++) {
|
|||
|
|
|||
|
for (Index = 0; Index < Counts[SubRequest]; Index++) {
|
|||
|
|
|||
|
Source = (PUCHAR)(SubRequestNames[SubRequest] + Index)->Buffer;
|
|||
|
Length = (ULONG)(SubRequestNames[SubRequest] + Index)->MaximumLength;
|
|||
|
|
|||
|
//
|
|||
|
// It is possible that a returned Unicode String has 0 length
|
|||
|
// because an Id was not mapped. In this case, skip to the next
|
|||
|
// one.
|
|||
|
//
|
|||
|
|
|||
|
if (Length == 0) {
|
|||
|
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
DestUstring = (PUNICODE_STRING)
|
|||
|
(((PUCHAR)(SubRequestNames[SubRequest] + Index)) +
|
|||
|
UstringStructDisps[SubRequest]);
|
|||
|
|
|||
|
DestUstring->Buffer = (PWSTR) Destination;
|
|||
|
|
|||
|
ASSERT(UsedLength + Length <= NamesLength);
|
|||
|
|
|||
|
RtlMoveMemory( Destination, Source, Length );
|
|||
|
Destination += Length;
|
|||
|
UsedLength += Length;
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
goto LookupIdsInDomainError;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now allocate a single buffer for the Uses
|
|||
|
//
|
|||
|
|
|||
|
UsesLength = Count * sizeof(SID_NAME_USE);
|
|||
|
|
|||
|
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
|
|||
|
OutputUses = MIDL_user_allocate( UsesLength );
|
|||
|
|
|||
|
if (OutputUses == NULL) {
|
|||
|
|
|||
|
goto LookupIdsInDomainError;
|
|||
|
}
|
|||
|
|
|||
|
NtStatus = STATUS_SUCCESS;
|
|||
|
|
|||
|
//
|
|||
|
// Now copy in the SID_NAME_USE Structures for the Uses returned from
|
|||
|
// each subrequest.
|
|||
|
//
|
|||
|
|
|||
|
Destination = (PUCHAR) OutputUses;
|
|||
|
UsedLength = 0;
|
|||
|
|
|||
|
for (SubRequest = 0; SubRequest < SubRequests; SubRequest++) {
|
|||
|
|
|||
|
Source = (PUCHAR) SubRequestUses[SubRequest];
|
|||
|
Length = Counts[SubRequest] * sizeof(SID_NAME_USE);
|
|||
|
RtlMoveMemory( Destination, Source, Length );
|
|||
|
Destination += Length;
|
|||
|
UsedLength += Length;
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
goto LookupIdsInDomainError;
|
|||
|
}
|
|||
|
|
|||
|
*Names = OutputNames;
|
|||
|
*Use = OutputUses;
|
|||
|
|
|||
|
//
|
|||
|
// Determine the final status to return. This is the normal NtStatus code
|
|||
|
// except that if NtStatus is a success status and none/not all Ids were
|
|||
|
// mapped, NtStatus will be set to either STATUS_SOME_NOT_MAPPED or
|
|||
|
// STATUS_NONE_MAPPED as appropriate. If STATUS_SOME_NOT_MAPPED was
|
|||
|
// returned on at least one call, return that status. If STATUS_NONE_MAPPED
|
|||
|
// was returned on all calls, return that status.
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
if (SomeNotMappedStatusCount > 0) {
|
|||
|
|
|||
|
NtStatus = STATUS_SOME_NOT_MAPPED;
|
|||
|
|
|||
|
} else if (NoneMappedStatusCount == SubRequests) {
|
|||
|
|
|||
|
NtStatus = STATUS_NONE_MAPPED;
|
|||
|
|
|||
|
} else if (NoneMappedStatusCount > 0) {
|
|||
|
|
|||
|
//
|
|||
|
// One or more calls returned STATUS_NONE_MAPPED but not all.
|
|||
|
// So at least one Id was mapped, but not all ids.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = STATUS_SOME_NOT_MAPPED;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
LookupIdsInDomainFinish:
|
|||
|
|
|||
|
//
|
|||
|
// If necessary, free the buffer containing the starting Rid Indices for
|
|||
|
// each Sub Request.
|
|||
|
//
|
|||
|
|
|||
|
if (RidIndices != NULL) {
|
|||
|
|
|||
|
MIDL_user_free(RidIndices);
|
|||
|
RidIndices = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If necessary, free the buffer containing the Rid Counts for
|
|||
|
// each Sub Request.
|
|||
|
//
|
|||
|
|
|||
|
if (Counts != NULL) {
|
|||
|
|
|||
|
MIDL_user_free(Counts);
|
|||
|
Counts = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If necessary, free the buffer containing the offsets from the
|
|||
|
// source and destination Unicode String structures.
|
|||
|
//
|
|||
|
|
|||
|
if (UstringStructDisps != NULL) {
|
|||
|
|
|||
|
MIDL_user_free(UstringStructDisps);
|
|||
|
UstringStructDisps = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If necessary, free the SubRequestNames output returned for each
|
|||
|
// Sub Request.
|
|||
|
//
|
|||
|
|
|||
|
if (SubRequestNames != NULL) {
|
|||
|
|
|||
|
for (SubRequest = 0; SubRequest < SubRequests; SubRequest++) {
|
|||
|
|
|||
|
if (SubRequestNames[SubRequest] != NULL) {
|
|||
|
|
|||
|
MIDL_user_free(SubRequestNames[SubRequest]);
|
|||
|
SubRequestNames[SubRequest] = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
MIDL_user_free(SubRequestNames);
|
|||
|
SubRequestNames = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If necessary, free the SubRequestUses output returned for each
|
|||
|
// Sub Request.
|
|||
|
//
|
|||
|
|
|||
|
if (SubRequestUses != NULL) {
|
|||
|
|
|||
|
for (SubRequest = 0; SubRequest < SubRequests; SubRequest++) {
|
|||
|
|
|||
|
if (SubRequestUses[SubRequest] != NULL) {
|
|||
|
|
|||
|
MIDL_user_free(SubRequestUses[SubRequest]);
|
|||
|
SubRequestUses[SubRequest] = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
MIDL_user_free(SubRequestUses);
|
|||
|
SubRequestUses = NULL;
|
|||
|
}
|
|||
|
|
|||
|
return(NtStatus);
|
|||
|
|
|||
|
LookupIdsInDomainError:
|
|||
|
|
|||
|
//
|
|||
|
// If necessary, free the buffers we would hande returned for
|
|||
|
// Names and Use.
|
|||
|
//
|
|||
|
|
|||
|
if (OutputNames != NULL) {
|
|||
|
|
|||
|
MIDL_user_free(OutputNames);
|
|||
|
OutputNames = NULL;
|
|||
|
}
|
|||
|
|
|||
|
if (OutputUses != NULL) {
|
|||
|
|
|||
|
MIDL_user_free(OutputUses);
|
|||
|
OutputUses = NULL;
|
|||
|
}
|
|||
|
|
|||
|
*Names = NULL;
|
|||
|
*Use = NULL;
|
|||
|
|
|||
|
goto LookupIdsInDomainFinish;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampLookupIdsInDomain(
|
|||
|
IN SAM_HANDLE DomainHandle,
|
|||
|
IN ULONG Count,
|
|||
|
IN PULONG RelativeIds,
|
|||
|
OUT PUNICODE_STRING *Names,
|
|||
|
OUT PSID_NAME_USE *Use OPTIONAL
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API maps a number of relative IDs to their corresponding names.
|
|||
|
The use of the name (domain, group, alias, user, or unknown) is also
|
|||
|
returned.
|
|||
|
|
|||
|
The API stores the actual names in Buffer, then creates an array of
|
|||
|
UNICODE_STRINGs in the Names OUT parameter. If a relative ID can
|
|||
|
not be mapped, a NULL value is placed in the slot for the
|
|||
|
UNICODE_STRING, and STATUS_SOME_NOT_MAPPED is returned.
|
|||
|
|
|||
|
DOMAIN_LOOKUP access to the domain is needed to use this service.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
DomainHandle - A domain handle returned from a previous call to
|
|||
|
SamOpenDomain.
|
|||
|
|
|||
|
Count - Provides the number of relative IDs to translate.
|
|||
|
|
|||
|
RelativeIds - Array of Count relative IDs to be mapped.
|
|||
|
|
|||
|
Names - Receives a pointer to an array of Count UNICODE_STRINGs that
|
|||
|
have been filled in. The nth pointer within this array will
|
|||
|
correspond the nth relative id passed . Each name string buffer
|
|||
|
will be in a separately allocated block of memory. Any entry is
|
|||
|
not successfully translated will have a NULL name buffer pointer
|
|||
|
returned. This Names buffer must be freed using SamFreeMemory()
|
|||
|
when no longer needed.
|
|||
|
|
|||
|
Use - Optionally, receives a pointer to an array of Count SID_NAME_USE
|
|||
|
entries that have been filled in with what significance each
|
|||
|
name has. The nth entry in this array indicates the meaning
|
|||
|
of the nth name passed. This buffer must be freed when no longer
|
|||
|
needed using SamFreeMemory().
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The domain handle passed is invalid.
|
|||
|
|
|||
|
STATUS_SOME_NOT_MAPPED - Some of the names provided could not be
|
|||
|
mapped. This is a successful return.
|
|||
|
|
|||
|
STATUS_NONE_MAPPED - No names could be mapped. This is an error
|
|||
|
return.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
SAMPR_RETURNED_USTRING_ARRAY NameBuffer;
|
|||
|
SAMPR_ULONG_ARRAY UseBuffer;
|
|||
|
|
|||
|
SampOutputDebugString("SamLookupIdsInDomain");
|
|||
|
|
|||
|
//
|
|||
|
// Make sure our parameters are within bounds
|
|||
|
//
|
|||
|
|
|||
|
if (RelativeIds == NULL) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
|
|||
|
if (Count > SAM_MAXIMUM_LOOKUP_COUNT) {
|
|||
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
NameBuffer.Element = NULL;
|
|||
|
UseBuffer.Element = NULL;
|
|||
|
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus = SamrLookupIdsInDomain(
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
Count,
|
|||
|
RelativeIds,
|
|||
|
&NameBuffer,
|
|||
|
&UseBuffer
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// What comes back for the "Names" OUT parameter is a two level
|
|||
|
// structure, the first level of which is on our stack:
|
|||
|
//
|
|||
|
// NameBuffer
|
|||
|
// +-------------+
|
|||
|
// | Count |
|
|||
|
// |-------------| +-------+
|
|||
|
// | Element ---|--->| Name0 | --- > (NameBuffer0)
|
|||
|
// +-------------+ |-------| o
|
|||
|
// | ... | o
|
|||
|
// |-------| o
|
|||
|
// | NameN | --- > (NameBufferN)
|
|||
|
// +-------+
|
|||
|
//
|
|||
|
// The buffer containing the EntriesRead field is not returned
|
|||
|
// to our caller. Only the buffers containing name information
|
|||
|
// are returned.
|
|||
|
//
|
|||
|
// NOTE: The buffers containing name information are allocated
|
|||
|
// by the RPC runtime in a single buffer. This is caused
|
|||
|
// by a line the the client side .acf file.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// What comes back for the "Use" OUT parameter is a two level
|
|||
|
// structure, the first level of which is on our stack:
|
|||
|
//
|
|||
|
// UseBuffer
|
|||
|
// +-------------+
|
|||
|
// | Count |
|
|||
|
// |-------------| +-------+
|
|||
|
// | Element ---|--->| Use-0 |
|
|||
|
// +-------------+ |-------|
|
|||
|
// | ... |
|
|||
|
// |-------|
|
|||
|
// | Use-N |
|
|||
|
// +-------+
|
|||
|
//
|
|||
|
// The Pointer in the Element field is what gets returned
|
|||
|
// to our caller. The caller is responsible for deallocating
|
|||
|
// this when no longer needed.
|
|||
|
//
|
|||
|
|
|||
|
(*Names) = (PUNICODE_STRING)NameBuffer.Element;
|
|||
|
if ( ARGUMENT_PRESENT(Use) ) {
|
|||
|
(*Use) = (PSID_NAME_USE) UseBuffer.Element;
|
|||
|
} else {
|
|||
|
if (UseBuffer.Element != NULL) {
|
|||
|
MIDL_user_free(UseBuffer.Element);
|
|||
|
UseBuffer.Element = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Don't force our caller to deallocate things on unexpected
|
|||
|
// return value.
|
|||
|
//
|
|||
|
|
|||
|
if (NtStatus != STATUS_SUCCESS &&
|
|||
|
NtStatus != STATUS_SOME_NOT_MAPPED
|
|||
|
) {
|
|||
|
if ( ARGUMENT_PRESENT(Use) ) {
|
|||
|
(*Use) = NULL;
|
|||
|
}
|
|||
|
if (UseBuffer.Element != NULL) {
|
|||
|
MIDL_user_free(UseBuffer.Element);
|
|||
|
UseBuffer.Element = NULL;
|
|||
|
}
|
|||
|
|
|||
|
(*Names) = NULL;
|
|||
|
if (NameBuffer.Element != NULL) {
|
|||
|
MIDL_user_free(NameBuffer.Element);
|
|||
|
NameBuffer.Element = NULL;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamQueryDisplayInformation (
|
|||
|
IN SAM_HANDLE DomainHandle,
|
|||
|
IN DOMAIN_DISPLAY_INFORMATION DisplayInformation,
|
|||
|
IN ULONG Index,
|
|||
|
IN ULONG EntryCount,
|
|||
|
IN ULONG PreferredMaximumLength,
|
|||
|
OUT PULONG TotalAvailable,
|
|||
|
OUT PULONG TotalReturned,
|
|||
|
OUT PULONG ReturnedEntryCount,
|
|||
|
OUT PVOID *SortedBuffer
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine provides fast return of information commonly
|
|||
|
needed to be displayed in user interfaces.
|
|||
|
|
|||
|
NT User Interface has a requirement for quick enumeration of SAM
|
|||
|
accounts for display in list boxes. (Replication has similar but
|
|||
|
broader requirements.)
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
DomainHandle - A handle to an open domain for DOMAIN_LIST_ACCOUNTS.
|
|||
|
|
|||
|
DisplayInformation - Indicates which information is to be enumerated.
|
|||
|
|
|||
|
Index - The index of the first entry to be retrieved.
|
|||
|
|
|||
|
PreferedMaximumLength - A recommended upper limit to the number of
|
|||
|
bytes to be returned. The returned information is allocated by
|
|||
|
this routine.
|
|||
|
|
|||
|
TotalAvailable - Total number of bytes availabe in the specified info
|
|||
|
class. This parameter is optional (and is not returned) for
|
|||
|
the following info levels:
|
|||
|
|
|||
|
DomainDisplayOemUser
|
|||
|
DomainDisplayOemGroup
|
|||
|
|
|||
|
|
|||
|
TotalReturned - Number of bytes actually returned for this call. Zero
|
|||
|
indicates there are no entries with an index as large as that
|
|||
|
specified.
|
|||
|
|
|||
|
ReturnedEntryCount - Number of entries returned by this call. Zero
|
|||
|
indicates there are no entries with an index as large as that
|
|||
|
specified.
|
|||
|
|
|||
|
|
|||
|
SortedBuffer - Receives a pointer to a buffer containing a sorted
|
|||
|
list of the requested information. This buffer is allocated
|
|||
|
by this routine and contains the following structure:
|
|||
|
|
|||
|
|
|||
|
DomainDisplayUser --> An array of ReturnedEntryCount elements
|
|||
|
of type DOMAIN_DISPLAY_USER. This is
|
|||
|
followed by the bodies of the various
|
|||
|
strings pointed to from within the
|
|||
|
DOMAIN_DISPLAY_USER structures.
|
|||
|
|
|||
|
DomainDisplayMachine --> An array of ReturnedEntryCount elements
|
|||
|
of type DOMAIN_DISPLAY_MACHINE. This is
|
|||
|
followed by the bodies of the various
|
|||
|
strings pointed to from within the
|
|||
|
DOMAIN_DISPLAY_MACHINE structures.
|
|||
|
|
|||
|
DomainDisplayGroup --> An array of ReturnedEntryCount elements
|
|||
|
of type DOMAIN_DISPLAY_GROUP. This is
|
|||
|
followed by the bodies of the various
|
|||
|
strings pointed to from within the
|
|||
|
DOMAIN_DISPLAY_GROUP structures.
|
|||
|
|
|||
|
DomainDisplayOemUser --> An array of ReturnedEntryCount elements
|
|||
|
of type DOMAIN_DISPLAY_OEM_USER. This is
|
|||
|
followed by the bodies of the various
|
|||
|
strings pointed to from within the
|
|||
|
DOMAIN_DISPLAY_OEM_user structures.
|
|||
|
|
|||
|
DomainDisplayOemGroup --> An array of ReturnedEntryCount elements
|
|||
|
of type DOMAIN_DISPLAY_OEM_GROUP. This is
|
|||
|
followed by the bodies of the various
|
|||
|
strings pointed to from within the
|
|||
|
DOMAIN_DISPLAY_OEM_GROUP structures.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - normal, successful completion.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - The specified handle was not opened for
|
|||
|
the necessary access.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The specified handle is not that of an
|
|||
|
opened Domain object.
|
|||
|
|
|||
|
STATUS_INVALID_INFO_CLASS - The requested class of information
|
|||
|
is not legitimate for this service.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS
|
|||
|
NtStatus;
|
|||
|
|
|||
|
SAMPR_DISPLAY_INFO_BUFFER
|
|||
|
DisplayInformationBuffer;
|
|||
|
|
|||
|
ULONG
|
|||
|
LocalTotalAvailable;
|
|||
|
|
|||
|
SampOutputDebugString("SamQueryDisplayInformation");
|
|||
|
|
|||
|
//
|
|||
|
// Check parameters
|
|||
|
//
|
|||
|
|
|||
|
if ( !ARGUMENT_PRESENT(TotalAvailable) &&
|
|||
|
(DisplayInformation != DomainDisplayOemUser) &&
|
|||
|
(DisplayInformation != DomainDisplayOemGroup) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
if ( !ARGUMENT_PRESENT(TotalReturned) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
if ( !ARGUMENT_PRESENT(ReturnedEntryCount) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
if ( !ARGUMENT_PRESENT(SortedBuffer) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
if (DisplayInformation <= DomainDisplayMachine) {
|
|||
|
NtStatus = SamrQueryDisplayInformation(
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
DisplayInformation,
|
|||
|
Index,
|
|||
|
EntryCount,
|
|||
|
PreferredMaximumLength,
|
|||
|
&LocalTotalAvailable,
|
|||
|
TotalReturned,
|
|||
|
&DisplayInformationBuffer);
|
|||
|
|
|||
|
} else if (DisplayInformation <= DomainDisplayGroup) {
|
|||
|
NtStatus = SamrQueryDisplayInformation2(
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
DisplayInformation,
|
|||
|
Index,
|
|||
|
EntryCount,
|
|||
|
PreferredMaximumLength,
|
|||
|
&LocalTotalAvailable,
|
|||
|
TotalReturned,
|
|||
|
&DisplayInformationBuffer);
|
|||
|
} else {
|
|||
|
NtStatus = SamrQueryDisplayInformation3(
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
DisplayInformation,
|
|||
|
Index,
|
|||
|
EntryCount,
|
|||
|
PreferredMaximumLength,
|
|||
|
&LocalTotalAvailable,
|
|||
|
TotalReturned,
|
|||
|
&DisplayInformationBuffer);
|
|||
|
}
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(TotalAvailable)) {
|
|||
|
(*TotalAvailable) = LocalTotalAvailable;
|
|||
|
}
|
|||
|
|
|||
|
switch (DisplayInformation) {
|
|||
|
|
|||
|
case DomainDisplayUser:
|
|||
|
*ReturnedEntryCount = DisplayInformationBuffer.UserInformation.EntriesRead;
|
|||
|
*SortedBuffer = DisplayInformationBuffer.UserInformation.Buffer;
|
|||
|
break;
|
|||
|
|
|||
|
case DomainDisplayMachine:
|
|||
|
*ReturnedEntryCount = DisplayInformationBuffer.MachineInformation.EntriesRead;
|
|||
|
*SortedBuffer = DisplayInformationBuffer.MachineInformation.Buffer;
|
|||
|
break;
|
|||
|
|
|||
|
case DomainDisplayGroup:
|
|||
|
*ReturnedEntryCount = DisplayInformationBuffer.GroupInformation.EntriesRead;
|
|||
|
*SortedBuffer = DisplayInformationBuffer.GroupInformation.Buffer;
|
|||
|
break;
|
|||
|
|
|||
|
case DomainDisplayOemUser:
|
|||
|
*ReturnedEntryCount = DisplayInformationBuffer.OemUserInformation.EntriesRead;
|
|||
|
*SortedBuffer = DisplayInformationBuffer.OemUserInformation.Buffer;
|
|||
|
break;
|
|||
|
|
|||
|
case DomainDisplayOemGroup:
|
|||
|
*ReturnedEntryCount = DisplayInformationBuffer.OemGroupInformation.EntriesRead;
|
|||
|
*SortedBuffer = DisplayInformationBuffer.OemGroupInformation.Buffer;
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
*ReturnedEntryCount = 0;
|
|||
|
*SortedBuffer = NULL;
|
|||
|
}
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
//
|
|||
|
// If the exception indicates the server doesn't have
|
|||
|
// the selected api, that means the server doesn't know
|
|||
|
// about the info level we passed. Set our completion
|
|||
|
// status appropriately.
|
|||
|
//
|
|||
|
|
|||
|
if (RpcExceptionCode() == RPC_S_INVALID_LEVEL ||
|
|||
|
RpcExceptionCode() == RPC_S_PROCNUM_OUT_OF_RANGE ||
|
|||
|
RpcExceptionCode() == RPC_NT_PROCNUM_OUT_OF_RANGE ) {
|
|||
|
NtStatus = STATUS_INVALID_INFO_CLASS;
|
|||
|
} else {
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
}
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamGetDisplayEnumerationIndex (
|
|||
|
IN SAM_HANDLE DomainHandle,
|
|||
|
IN DOMAIN_DISPLAY_INFORMATION DisplayInformation,
|
|||
|
IN PUNICODE_STRING Prefix,
|
|||
|
OUT PULONG Index
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine returns the index of the entry which alphabetically
|
|||
|
immediatly preceeds a specified prefix. If no such entry exists,
|
|||
|
then zero is returned as the index.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
DomainHandle - A handle to an open domain for DOMAIN_LIST_ACCOUNTS.
|
|||
|
|
|||
|
DisplayInformation - Indicates which sorted information class is
|
|||
|
to be searched.
|
|||
|
|
|||
|
Prefix - The prefix to compare.
|
|||
|
|
|||
|
Index - Receives the index of the entry of the information class
|
|||
|
with a LogonName (or MachineName) which immediatly preceeds the
|
|||
|
provided prefix string. If there are no elements which preceed
|
|||
|
the prefix, then zero is returned.
|
|||
|
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - normal, successful completion.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - The specified handle was not opened for
|
|||
|
the necessary access.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The specified handle is not that of an
|
|||
|
opened Domain object.
|
|||
|
|
|||
|
STATUS_NO_MORE_ENTRIES - There are no entries for this information class.
|
|||
|
The returned index is invalid.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
//
|
|||
|
// Check parameters
|
|||
|
//
|
|||
|
|
|||
|
if ( !ARGUMENT_PRESENT(Prefix) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
if ( !ARGUMENT_PRESENT(Index) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
if (DisplayInformation <= DomainDisplayMachine) {
|
|||
|
//
|
|||
|
// Info levels supported via original API in NT1.0
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SamrGetDisplayEnumerationIndex (
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
DisplayInformation,
|
|||
|
(PRPC_UNICODE_STRING)Prefix,
|
|||
|
Index
|
|||
|
);
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Info levels added in NT1.0A via new API
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SamrGetDisplayEnumerationIndex2 (
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
DisplayInformation,
|
|||
|
(PRPC_UNICODE_STRING)Prefix,
|
|||
|
Index
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamOpenGroup(
|
|||
|
IN SAM_HANDLE DomainHandle,
|
|||
|
IN ACCESS_MASK DesiredAccess,
|
|||
|
IN ULONG GroupId,
|
|||
|
OUT PSAM_HANDLE GroupHandle
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API opens an existing group in the account database. The group
|
|||
|
is specified by a ID value that is relative to the SID of the
|
|||
|
domain. The operations that will be performed on the group must be
|
|||
|
declared at this time.
|
|||
|
|
|||
|
This call returns a handle to the newly opened group that may be
|
|||
|
used for successive operations on the group. This handle may be
|
|||
|
closed with the SamCloseHandle API.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
DomainHandle - A domain handle returned from a previous call to
|
|||
|
SamOpenDomain.
|
|||
|
|
|||
|
DesiredAccess - Is an access mask indicating which access types
|
|||
|
are desired to the group. These access types are reconciled
|
|||
|
with the Discretionary Access Control list of the group to
|
|||
|
determine whether the accesses will be granted or denied.
|
|||
|
|
|||
|
GroupId - Specifies the relative ID value of the group to be
|
|||
|
opened.
|
|||
|
|
|||
|
GroupHandle - Receives a handle referencing the newly opened
|
|||
|
group. This handle will be required in successive calls to
|
|||
|
operate on the group.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The group was successfully opened.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_NO_SUCH_GROUP - The specified group does not exist.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The domain handle passed is invalid.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
SampOutputDebugString("SamOpenGroup");
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
(*GroupHandle) = 0;
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrOpenGroup(
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
DesiredAccess,
|
|||
|
GroupId,
|
|||
|
(SAMPR_HANDLE *)GroupHandle
|
|||
|
);
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamQueryInformationGroup(
|
|||
|
IN SAM_HANDLE GroupHandle,
|
|||
|
IN GROUP_INFORMATION_CLASS GroupInformationClass,
|
|||
|
OUT PVOID * Buffer
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API retrieves information on the group specified.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
GroupHandle - The handle of an opened group to operate on.
|
|||
|
|
|||
|
GroupInformationClass - Class of information to retrieve. The
|
|||
|
accesses required for each class is shown below:
|
|||
|
|
|||
|
Info Level Required Access Type
|
|||
|
---------------------- ----------------------
|
|||
|
GroupGeneralInformation GROUP_READ_INFORMATION
|
|||
|
GroupNameInformation GROUP_READ_INFORMATION
|
|||
|
GroupAttributeInformation GROUP_READ_INFORMATION
|
|||
|
GroupAdminInformation GROUP_READ_INFORMATION
|
|||
|
|
|||
|
Buffer - Receives a pointer to a buffer containing the requested
|
|||
|
information. When this information is no longer needed, this
|
|||
|
buffer must be freed using SamFreeMemory().
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_INVALID_INFO_CLASS - The class provided was invalid.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
SampOutputDebugString("SamQueryInformationGroup");
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
(*Buffer) = NULL;
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrQueryInformationGroup(
|
|||
|
(SAMPR_HANDLE)GroupHandle,
|
|||
|
GroupInformationClass,
|
|||
|
(PSAMPR_GROUP_INFO_BUFFER *)Buffer
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamSetInformationGroup(
|
|||
|
IN SAM_HANDLE GroupHandle,
|
|||
|
IN GROUP_INFORMATION_CLASS GroupInformationClass,
|
|||
|
IN PVOID Buffer
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API allows the caller to modify group information.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
GroupHandle - The handle of an opened group to operate on.
|
|||
|
|
|||
|
GroupInformationClass - Class of information to retrieve. The
|
|||
|
accesses required for each class is shown below:
|
|||
|
|
|||
|
Info Level Required Access Type
|
|||
|
------------------------ -------------------------
|
|||
|
|
|||
|
GroupGeneralInformation (can't write)
|
|||
|
|
|||
|
GroupNameInformation GROUP_WRITE_ACCOUNT
|
|||
|
GroupAttributeInformation GROUP_WRITE_ACCOUNT
|
|||
|
GroupAdminInformation GROUP_WRITE_ACCOUNT
|
|||
|
|
|||
|
Buffer - Buffer where information retrieved is placed.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_INFO_CLASS - The class provided was invalid.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_NO_SUCH_GROUP - The group specified is unknown.
|
|||
|
|
|||
|
STATUS_SPECIAL_GROUP - The group specified is a special group and
|
|||
|
cannot be operated on in the requested fashion.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be enabled for this
|
|||
|
operation
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
SampOutputDebugString("SamSetInformationGroup");
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrSetInformationGroup(
|
|||
|
(SAMPR_HANDLE)GroupHandle,
|
|||
|
GroupInformationClass,
|
|||
|
Buffer
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamAddMemberToGroup(
|
|||
|
IN SAM_HANDLE GroupHandle,
|
|||
|
IN ULONG MemberId,
|
|||
|
IN ULONG Attributes
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API adds a member to a group. Note that this API requires the
|
|||
|
GROUP_ADD_MEMBER access type for the group.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
GroupHandle - The handle of an opened group to operate on.
|
|||
|
|
|||
|
MemberId - Relative ID of the member to add.
|
|||
|
|
|||
|
Attributes - The attributes of the group assigned to the user. The
|
|||
|
attributes assigned here must be compatible with the attributes
|
|||
|
assigned to the group as a whole. Compatible attribute
|
|||
|
assignments are:
|
|||
|
|
|||
|
Mandatory - If the Mandatory attribute is assigned to the
|
|||
|
group as a whole, then it must be assigned to the
|
|||
|
group for each member of the group.
|
|||
|
|
|||
|
EnabledByDefault - This attribute may be set to any value
|
|||
|
for each member of the group. It does not matter
|
|||
|
what the attribute value for the group as a whole
|
|||
|
is.
|
|||
|
|
|||
|
Enabled - This attribute may be set to any value for each
|
|||
|
member of the group. It does not matter what the
|
|||
|
attribute value for the group as a whole is.
|
|||
|
|
|||
|
Owner - The Owner attribute may be assigned any value.
|
|||
|
However, if the Owner attribute of the group as a
|
|||
|
whole is not set, then the value assigned to
|
|||
|
members is ignored.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_NO_SUCH_MEMBER - The member specified is unknown.
|
|||
|
|
|||
|
STATUS_MEMBER_IN_GROUP - The member already belongs to the group.
|
|||
|
|
|||
|
STATUS_INVALID_GROUP_ATTRIBUTES - Indicates the group attribute
|
|||
|
values being assigned to the member are not compatible with
|
|||
|
the attribute values of the group as a whole.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be enabled for this
|
|||
|
operation
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
|
|||
|
SampOutputDebugString("SamAddMemberToGroup");
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrAddMemberToGroup(
|
|||
|
(SAMPR_HANDLE)GroupHandle,
|
|||
|
MemberId,
|
|||
|
Attributes
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamDeleteGroup(
|
|||
|
IN SAM_HANDLE GroupHandle
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API removes a group from the account database. There may be no
|
|||
|
members in the group or the deletion request will be rejected. Note
|
|||
|
that this API requires DELETE access to the specific group being
|
|||
|
deleted.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
GroupHandle - The handle of an opened group to operate on.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_SPECIAL_GROUP - The group specified is a special group and
|
|||
|
cannot be operated on in the requested fashion.
|
|||
|
|
|||
|
STATUS_MEMBER_IN_GROUP - The group still has members. A group may
|
|||
|
not be deleted unless it has no members.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be enabled for this
|
|||
|
operation
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
SAMPR_HANDLE LocalHandle;
|
|||
|
|
|||
|
SampOutputDebugString("SamDeleteGroup");
|
|||
|
|
|||
|
LocalHandle = (SAMPR_HANDLE)GroupHandle;
|
|||
|
|
|||
|
if (LocalHandle == 0) {
|
|||
|
return(STATUS_INVALID_HANDLE);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus = SamrDeleteGroup( &LocalHandle );
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamRemoveMemberFromGroup(
|
|||
|
IN SAM_HANDLE GroupHandle,
|
|||
|
IN ULONG MemberId
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API removes a single member from a group. Note that this API
|
|||
|
requires the GROUP_REMOVE_MEMBER access type.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
GroupHandle - The handle of an opened group to operate on.
|
|||
|
|
|||
|
MemberId - Relative ID of the member to remove.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_SPECIAL_GROUP - The group specified is a special group and
|
|||
|
cannot be operated on in the requested fashion.
|
|||
|
|
|||
|
STATUS_MEMBER_NOT_IN_GROUP - The specified user does not belong
|
|||
|
to the group.
|
|||
|
|
|||
|
STATUS_MEMBERS_PRIMARY_GROUP - A user may not be removed from its
|
|||
|
primary group. The primary group of the user account must first
|
|||
|
be changed.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be enabled for this
|
|||
|
operation
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
SampOutputDebugString("SamRemoveMemberFromGroup");
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrRemoveMemberFromGroup(
|
|||
|
(SAMPR_HANDLE)GroupHandle,
|
|||
|
MemberId
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamGetMembersInGroup(
|
|||
|
IN SAM_HANDLE GroupHandle,
|
|||
|
OUT PULONG * MemberIds,
|
|||
|
OUT PULONG * Attributes,
|
|||
|
OUT PULONG MemberCount
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API lists all the members in a group. This API requires
|
|||
|
GROUP_LIST_MEMBERS access to the group.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
GroupHandle - The handle of an opened group to operate on.
|
|||
|
|
|||
|
MemberIds - Receives a pointer to a buffer containing An array of
|
|||
|
ULONGs. These ULONGs contain the relative IDs of the members
|
|||
|
of the group. When this information is no longer needed,
|
|||
|
this buffer must be freed using SamFreeMemory().
|
|||
|
|
|||
|
Attributes - Receives a pointer to a buffer containing an array of
|
|||
|
ULONGs. These ULONGs contain the attributes of the
|
|||
|
corresponding member ID returned via the MemberId paramter.
|
|||
|
|
|||
|
MemberCount - number of members in the group (and, thus, the
|
|||
|
number relative IDs returned).
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully, and there
|
|||
|
are no additional entries.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have privilege required to
|
|||
|
request that data.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
PSAMPR_GET_MEMBERS_BUFFER GetMembersBuffer;
|
|||
|
|
|||
|
|
|||
|
SampOutputDebugString("SamGetMembersInGroup");
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
GetMembersBuffer = NULL;
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrGetMembersInGroup(
|
|||
|
(SAMPR_HANDLE)GroupHandle,
|
|||
|
&GetMembersBuffer
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// What we get back is the following:
|
|||
|
//
|
|||
|
// +-------------+
|
|||
|
// --------->| MemberCount |
|
|||
|
// |-------------+ +-------+
|
|||
|
// | Members --|------------------->| Rid-0 |
|
|||
|
// |-------------| +------------+ | ... |
|
|||
|
// | Attributes-|-->| Attribute0 | | |
|
|||
|
// +-------------+ | ... | | Rid-N |
|
|||
|
// | AttributeN | +-------+
|
|||
|
// +------------+
|
|||
|
//
|
|||
|
// Each block allocated with MIDL_user_allocate. We return the
|
|||
|
// RID and ATTRIBUTE blocks, and free the block containing
|
|||
|
// the MemberCount ourselves.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
(*MemberCount) = GetMembersBuffer->MemberCount;
|
|||
|
(*MemberIds) = GetMembersBuffer->Members;
|
|||
|
(*Attributes) = GetMembersBuffer->Attributes;
|
|||
|
MIDL_user_free( GetMembersBuffer );
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Deallocate any returned buffers on error
|
|||
|
//
|
|||
|
|
|||
|
if (GetMembersBuffer != NULL) {
|
|||
|
if (GetMembersBuffer->Members != NULL) {
|
|||
|
MIDL_user_free(GetMembersBuffer->Members);
|
|||
|
}
|
|||
|
if (GetMembersBuffer->Attributes != NULL) {
|
|||
|
MIDL_user_free(GetMembersBuffer->Attributes);
|
|||
|
}
|
|||
|
MIDL_user_free(GetMembersBuffer);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamSetMemberAttributesOfGroup(
|
|||
|
IN SAM_HANDLE GroupHandle,
|
|||
|
IN ULONG MemberId,
|
|||
|
IN ULONG Attributes
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine modifies the group attributes of a member of the group.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
GroupHandle - The handle of an opened group to operate on. This
|
|||
|
handle must be open for GROUP_ADD_MEMBER access to the group.
|
|||
|
|
|||
|
MemberId - Contains the relative ID of member whose attributes
|
|||
|
are to be modified.
|
|||
|
|
|||
|
Attributes - The group attributes to set for the member. These
|
|||
|
attributes must not conflict with the attributes of the group
|
|||
|
as a whole. See SamAddMemberToGroup() for more information
|
|||
|
on compatible attribute settings.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully, and there
|
|||
|
are no additional entries.
|
|||
|
|
|||
|
STATUS_INVALID_INFO_CLASS - The class provided was invalid.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_NO_SUCH_USER - The user specified does not exist.
|
|||
|
|
|||
|
STATUS_MEMBER_NOT_IN_GROUP - Indicates the specified relative ID
|
|||
|
is not a member of the group.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be enabled for this
|
|||
|
operation
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
SampOutputDebugString("SamSetMemberAttributesOfGroup");
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrSetMemberAttributesOfGroup(
|
|||
|
(SAMPR_HANDLE)GroupHandle,
|
|||
|
MemberId,
|
|||
|
Attributes
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamOpenAlias(
|
|||
|
IN SAM_HANDLE DomainHandle,
|
|||
|
IN ACCESS_MASK DesiredAccess,
|
|||
|
IN ULONG AliasId,
|
|||
|
OUT PSAM_HANDLE AliasHandle
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API opens an existing Alias object. The Alias is specified by
|
|||
|
a ID value that is relative to the SID of the domain. The operations
|
|||
|
that will be performed on the Alias must be declared at this time.
|
|||
|
|
|||
|
This call returns a handle to the newly opened Alias that may be used
|
|||
|
for successive operations on the Alias. This handle may be closed
|
|||
|
with the SamCloseHandle API.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
DomainHandle - A domain handle returned from a previous call to
|
|||
|
SamOpenDomain.
|
|||
|
|
|||
|
DesiredAccess - Is an access mask indicating which access types are
|
|||
|
desired to the alias.
|
|||
|
|
|||
|
AliasId - Specifies the relative ID value of the Alias to be opened.
|
|||
|
|
|||
|
AliasHandle - Receives a handle referencing the newly opened Alias.
|
|||
|
This handle will be required in successive calls to operate on
|
|||
|
the Alias.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Alias was successfully opened.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|||
|
to complete the operation.
|
|||
|
|
|||
|
STATUS_NO_SUCH_ALIAS - The specified Alias does not exist.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The domain handle passed is invalid.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
SampOutputDebugString("SamOpenAlias");
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
(*AliasHandle) = 0;
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrOpenAlias(
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
DesiredAccess,
|
|||
|
AliasId,
|
|||
|
(SAMPR_HANDLE *)AliasHandle
|
|||
|
);
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamQueryInformationAlias(
|
|||
|
IN SAM_HANDLE AliasHandle,
|
|||
|
IN ALIAS_INFORMATION_CLASS AliasInformationClass,
|
|||
|
OUT PVOID * Buffer
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API retrieves information on the alias specified.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
AliasHandle - The handle of an opened alias to operate on.
|
|||
|
|
|||
|
AliasInformationClass - Class of information to retrieve. The
|
|||
|
accesses required for each class is shown below:
|
|||
|
|
|||
|
Info Level Required Access Type
|
|||
|
---------------------- ----------------------
|
|||
|
AliasGeneralInformation ALIAS_READ_INFORMATION
|
|||
|
AliasNameInformation ALIAS_READ_INFORMATION
|
|||
|
AliasAdminInformation ALIAS_READ_INFORMATION
|
|||
|
|
|||
|
Buffer - Receives a pointer to a buffer containing the requested
|
|||
|
information. When this information is no longer needed, this
|
|||
|
buffer and any memory pointed to through this buffer must be
|
|||
|
freed using SamFreeMemory().
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_INVALID_INFO_CLASS - The class provided was invalid.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
SampOutputDebugString("SamQueryInformationAlias");
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
(*Buffer) = NULL;
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrQueryInformationAlias(
|
|||
|
(SAMPR_HANDLE)AliasHandle,
|
|||
|
AliasInformationClass,
|
|||
|
(PSAMPR_ALIAS_INFO_BUFFER *)Buffer
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamSetInformationAlias(
|
|||
|
IN SAM_HANDLE AliasHandle,
|
|||
|
IN ALIAS_INFORMATION_CLASS AliasInformationClass,
|
|||
|
IN PVOID Buffer
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API allows the caller to modify alias information.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
AliasHandle - The handle of an opened alias to operate on.
|
|||
|
|
|||
|
AliasInformationClass - Class of information to retrieve. The
|
|||
|
accesses required for each class is shown below:
|
|||
|
|
|||
|
Info Level Required Access Type
|
|||
|
------------------------ -------------------------
|
|||
|
|
|||
|
AliasGeneralInformation (can't write)
|
|||
|
|
|||
|
AliasNameInformation ALIAS_WRITE_ACCOUNT
|
|||
|
AliasAdminInformation ALIAS_WRITE_ACCOUNT
|
|||
|
|
|||
|
Buffer - Buffer where information retrieved is placed.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_INFO_CLASS - The class provided was invalid.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_NO_SUCH_ALIAS - The alias specified is unknown.
|
|||
|
|
|||
|
STATUS_SPECIAL_ALIAS - The alias specified is a special alias and
|
|||
|
cannot be operated on in the requested fashion.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be enabled for this
|
|||
|
operation
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
SampOutputDebugString("SamSetInformationAlias");
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrSetInformationAlias(
|
|||
|
(SAMPR_HANDLE)AliasHandle,
|
|||
|
AliasInformationClass,
|
|||
|
Buffer
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamDeleteAlias(
|
|||
|
IN SAM_HANDLE AliasHandle
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API deletes an Alias from the account database. The Alias does
|
|||
|
not have to be empty.
|
|||
|
|
|||
|
Note that following this call, the AliasHandle is no longer valid.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
AliasHandle - The handle of an opened Alias to operate on.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be enabled for this
|
|||
|
operation
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
SAMPR_HANDLE LocalHandle;
|
|||
|
|
|||
|
SampOutputDebugString("SamDeleteAlias");
|
|||
|
|
|||
|
LocalHandle = (SAMPR_HANDLE)AliasHandle;
|
|||
|
|
|||
|
if (LocalHandle == 0) {
|
|||
|
return(STATUS_INVALID_HANDLE);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus = SamrDeleteAlias( &LocalHandle );
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamAddMemberToAlias(
|
|||
|
IN SAM_HANDLE AliasHandle,
|
|||
|
IN PSID MemberId
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API adds a member to an Alias.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
AliasHandle - The handle of an opened Alias object to operate on.
|
|||
|
The handle must be open for ALIAS_ADD_MEMBER.
|
|||
|
|
|||
|
MemberId - The SID of the member to add.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|||
|
to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_MEMBER_IN_ALIAS - The member already belongs to the Alias.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the correct
|
|||
|
state (disabled or enabled) to perform the requested operation.
|
|||
|
The domain server must be enabled for this operation.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the incorrect
|
|||
|
role (primary or backup) to perform the requested operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
SampOutputDebugString("SamAddMemberToAlias");
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrAddMemberToAlias(
|
|||
|
(SAMPR_HANDLE)AliasHandle,
|
|||
|
MemberId
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamRemoveMemberFromAlias(
|
|||
|
IN SAM_HANDLE AliasHandle,
|
|||
|
IN PSID MemberId
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API removes a member from an Alias.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
AliasHandle - The handle of an opened Alias object to operate on.
|
|||
|
The handle must be open for ALIAS_REMOVE_MEMBER.
|
|||
|
|
|||
|
MemberId - The SID of the member to remove.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|||
|
to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_SPECIAL_ALIAS - The group specified is a special alias and
|
|||
|
cannot be operated on in the requested fashion.
|
|||
|
|
|||
|
STATUS_MEMBER_NOT_IN_ALIAS - The specified member does not belong to
|
|||
|
the Alias.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the correct
|
|||
|
state (disabled or enabled) to perform the requested operation.
|
|||
|
The domain server must be enabled for this operation.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the incorrect
|
|||
|
role (primary or backup) to perform the requested operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
SampOutputDebugString("SamRemoveMemberFromAlias");
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrRemoveMemberFromAlias(
|
|||
|
(SAMPR_HANDLE)AliasHandle,
|
|||
|
MemberId
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamRemoveMemberFromForeignDomain(
|
|||
|
IN SAM_HANDLE DomainHandle,
|
|||
|
IN PSID MemberId
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API removes a member from an all Aliases in the domain specified.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
DomainHandle - The handle of an opened domain to operate in. All
|
|||
|
aliases in the domain that the member is a part of must be
|
|||
|
accessible by the caller with ALIAS_REMOVE_MEMBER.
|
|||
|
|
|||
|
MemberId - The SID of the member to remove.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
|
|||
|
to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the correct
|
|||
|
state (disabled or enabled) to perform the requested operation.
|
|||
|
The domain server must be enabled for this operation.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the incorrect
|
|||
|
role (primary or backup) to perform the requested operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
SampOutputDebugString("SamRemoveMemberFromForeignDomain");
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrRemoveMemberFromForeignDomain(
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
MemberId
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamGetMembersInAlias(
|
|||
|
IN SAM_HANDLE AliasHandle,
|
|||
|
OUT PSID **MemberIds,
|
|||
|
OUT PULONG MemberCount
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API lists all members in an Alias. This API requires
|
|||
|
ALIAS_LIST_MEMBERS access to the Alias.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
AliasHandle - The handle of an opened Alias to operate on.
|
|||
|
|
|||
|
MemberIds - Receives a pointer to a buffer containing an array of
|
|||
|
pointers to SIDs. These SIDs are the SIDs of the members of the
|
|||
|
Alias. When this information is no longer needed, this buffer
|
|||
|
must be freed using SamFreeMemory().
|
|||
|
|
|||
|
MemberCount - number of members in the Alias (and, thus, the number
|
|||
|
of relative IDs returned).
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully, and there are
|
|||
|
no additional entries.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have privilege required to
|
|||
|
request that data.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
SAMPR_PSID_ARRAY MembersBuffer;
|
|||
|
|
|||
|
SampOutputDebugString("SamGetMembersInAlias");
|
|||
|
|
|||
|
//
|
|||
|
// Validate parameters
|
|||
|
//
|
|||
|
|
|||
|
if ( !ARGUMENT_PRESENT(MemberIds) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER_2);
|
|||
|
}
|
|||
|
if ( !ARGUMENT_PRESENT(MemberCount) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER_3);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
//
|
|||
|
// Prepare for failure
|
|||
|
//
|
|||
|
|
|||
|
*MemberIds = NULL;
|
|||
|
*MemberCount = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
MembersBuffer.Sids = NULL;
|
|||
|
|
|||
|
NtStatus = SamrGetMembersInAlias(
|
|||
|
(SAMPR_HANDLE)AliasHandle,
|
|||
|
&MembersBuffer
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Return the member list
|
|||
|
//
|
|||
|
|
|||
|
*MemberCount = MembersBuffer.Count;
|
|||
|
*MemberIds = (PSID *)(MembersBuffer.Sids);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamAddMultipleMembersToAlias(
|
|||
|
IN SAM_HANDLE AliasHandle,
|
|||
|
IN PSID *MemberIds,
|
|||
|
IN ULONG MemberCount
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API adds the SIDs listed in MemberIds to the specified Alias.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
AliasHandle - The handle of an opened Alias to operate on.
|
|||
|
|
|||
|
MemberIds - Points to an array of SID pointers containing MemberCount
|
|||
|
entries.
|
|||
|
|
|||
|
MemberCount - number of members in the array.
|
|||
|
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully. All of the
|
|||
|
listed members are now members of the alias. However, some of
|
|||
|
the members may already have been members of the alias (this is
|
|||
|
NOT an error or warning condition).
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the object open for
|
|||
|
the required access.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_INVALID_MEMBER - The member has the wrong account type.
|
|||
|
|
|||
|
STATUS_INVALID_SID - The member sid is corrupted.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be enabled for this
|
|||
|
operation
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation.
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
SAMPR_PSID_ARRAY MembersBuffer;
|
|||
|
|
|||
|
SampOutputDebugString("SamAddMultipleMembersToAlias");
|
|||
|
|
|||
|
//
|
|||
|
// Validate parameters
|
|||
|
//
|
|||
|
|
|||
|
if ( !ARGUMENT_PRESENT(MemberIds) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER_2);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
MembersBuffer.Count = MemberCount;
|
|||
|
MembersBuffer.Sids = (PSAMPR_SID_INFORMATION)MemberIds;
|
|||
|
|
|||
|
NtStatus = SamrAddMultipleMembersToAlias(
|
|||
|
(SAMPR_HANDLE)AliasHandle,
|
|||
|
&MembersBuffer
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamRemoveMultipleMembersFromAlias(
|
|||
|
IN SAM_HANDLE AliasHandle,
|
|||
|
IN PSID *MemberIds,
|
|||
|
IN ULONG MemberCount
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API Removes the SIDs listed in MemberIds from the specified Alias.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
AliasHandle - The handle of an opened Alias to operate on.
|
|||
|
|
|||
|
MemberIds - Points to an array of SID pointers containing MemberCount
|
|||
|
entries.
|
|||
|
|
|||
|
MemberCount - number of members in the array.
|
|||
|
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully. All of the
|
|||
|
listed members are now NOT members of the alias. However, some of
|
|||
|
the members may already have not been members of the alias (this
|
|||
|
is NOT an error or warning condition).
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the object open for
|
|||
|
the required access.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
SAMPR_PSID_ARRAY MembersBuffer;
|
|||
|
|
|||
|
SampOutputDebugString("SamRemoveMultipleMembersFromAlias");
|
|||
|
|
|||
|
//
|
|||
|
// Validate parameters
|
|||
|
//
|
|||
|
|
|||
|
if ( !ARGUMENT_PRESENT(MemberIds) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER_2);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
MembersBuffer.Count = MemberCount;
|
|||
|
MembersBuffer.Sids = (PSAMPR_SID_INFORMATION)MemberIds;
|
|||
|
|
|||
|
NtStatus = SamrRemoveMultipleMembersFromAlias(
|
|||
|
(SAMPR_HANDLE)AliasHandle,
|
|||
|
&MembersBuffer
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamOpenUser(
|
|||
|
IN SAM_HANDLE DomainHandle,
|
|||
|
IN ACCESS_MASK DesiredAccess,
|
|||
|
IN ULONG UserId,
|
|||
|
OUT PSAM_HANDLE UserHandle
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API opens an existing user in the account database. The user
|
|||
|
is specified by SID value. The operations that will be performed on
|
|||
|
the user must be declared at this time.
|
|||
|
|
|||
|
This call returns a handle to the newly opened user that may be used
|
|||
|
for successive operations on the user. This handle may be closed
|
|||
|
with the SamCloseHandle API.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
DomainHandle - A domain handle returned from a previous call to
|
|||
|
SamOpenDomain.
|
|||
|
|
|||
|
DesiredAccess - Is an access mask indicating which access types
|
|||
|
are desired to the user. These access types are reconciled
|
|||
|
with the Discretionary Access Control list of the user to
|
|||
|
determine whether the accesses will be granted or denied.
|
|||
|
|
|||
|
UserId - Specifies the relative ID value of the user account to
|
|||
|
be opened.
|
|||
|
|
|||
|
UserHandle - Receives a handle referencing the newly opened User.
|
|||
|
This handle will be required in successive calls to operate
|
|||
|
on the user.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The group was successfully opened.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_NO_SUCH_USER - The specified user does not exist.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The domain handle passed is invalid.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
SampOutputDebugString("SamOpenUser");
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
(*UserHandle) = 0;
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrOpenUser(
|
|||
|
(SAMPR_HANDLE)DomainHandle,
|
|||
|
DesiredAccess,
|
|||
|
UserId,
|
|||
|
(SAMPR_HANDLE *)UserHandle
|
|||
|
);
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamDeleteUser(
|
|||
|
IN SAM_HANDLE UserHandle
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API deletes a user from the account database. If the account
|
|||
|
being deleted is the last account in the database in the ADMIN
|
|||
|
group, then STATUS_LAST_ADMIN is returned, and the Delete fails. Note
|
|||
|
that this API required DOMAIN_DELETE_USER access.
|
|||
|
|
|||
|
Note that following this call, the UserHandle is no longer valid.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
UserHandle - The handle of an opened user to operate on. The handle
|
|||
|
must be opened for DELETE access.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_LAST_ADMIN - Cannot delete the last administrator.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be enabled for this
|
|||
|
operation
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
SAMPR_HANDLE LocalHandle;
|
|||
|
|
|||
|
SampOutputDebugString("SamDeleteUser");
|
|||
|
|
|||
|
LocalHandle = (SAMPR_HANDLE)UserHandle;
|
|||
|
|
|||
|
if (LocalHandle == 0) {
|
|||
|
return(STATUS_INVALID_HANDLE);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus = SamrDeleteUser( &LocalHandle );
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamQueryInformationUser(
|
|||
|
IN SAM_HANDLE UserHandle,
|
|||
|
IN USER_INFORMATION_CLASS UserInformationClass,
|
|||
|
OUT PVOID * Buffer
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API looks up some level of information about a particular user.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
UserHandle - The handle of an opened user to operate on.
|
|||
|
|
|||
|
UserInformationClass - Class of information desired about this
|
|||
|
user. The accesses required for each class is shown below:
|
|||
|
|
|||
|
Info Level Required Access Type
|
|||
|
---------------------- --------------------------
|
|||
|
|
|||
|
UserGeneralInformation USER_READ_GENERAL
|
|||
|
UserPreferencesInformation USER_READ_PREFERENCES
|
|||
|
UserLogonInformation USER_READ_GENERAL and
|
|||
|
USER_READ_PREFERENCES and
|
|||
|
USER_READ_LOGON
|
|||
|
|
|||
|
UserLogonHoursInformation USER_READ_LOGON
|
|||
|
|
|||
|
UserAccountInformation USER_READ_GENERAL and
|
|||
|
USER_READ_PREFERENCES and
|
|||
|
USER_READ_LOGON and
|
|||
|
USER_READ_ACCOUNT
|
|||
|
|
|||
|
UserParametersInformation USER_READ_ACCOUNT
|
|||
|
|
|||
|
UserNameInformation USER_READ_GENERAL
|
|||
|
UserAccountNameInformation USER_READ_GENERAL
|
|||
|
UserFullNameInformation USER_READ_GENERAL
|
|||
|
UserPrimaryGroupInformation USER_READ_GENERAL
|
|||
|
UserHomeInformation USER_READ_LOGON
|
|||
|
UserScriptInformation USER_READ_LOGON
|
|||
|
UserProfileInformation USER_READ_LOGON
|
|||
|
UserAdminCommentInformation USER_READ_GENERAL
|
|||
|
UserWorkStationsInformation USER_READ_LOGON
|
|||
|
|
|||
|
UserSetPasswordInformation (Can't query)
|
|||
|
|
|||
|
UserControlInformation USER_READ_ACCOUNT
|
|||
|
UserExpiresInformation USER_READ_ACCOUNT
|
|||
|
|
|||
|
UserInternal1Information (trusted client use only)
|
|||
|
UserInternal2Information (trusted client use only)
|
|||
|
|
|||
|
UserAllInformation Will return fields that user
|
|||
|
has access to.
|
|||
|
|
|||
|
Buffer - Receives a pointer to a buffer containing the requested
|
|||
|
information. When this information is no longer needed, this
|
|||
|
buffer must be freed using SamFreeMemory().
|
|||
|
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_INVALID_INFO_CLASS - The class provided was invalid.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
SampOutputDebugString("SamQueryInformationUser");
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
(*Buffer) = NULL;
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrQueryInformationUser(
|
|||
|
(SAMPR_HANDLE)UserHandle,
|
|||
|
UserInformationClass,
|
|||
|
(PSAMPR_USER_INFO_BUFFER *)Buffer
|
|||
|
);
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampOwfPassword(
|
|||
|
IN SAM_HANDLE UserHandle,
|
|||
|
IN PUNICODE_STRING UnicodePassword,
|
|||
|
IN BOOLEAN IgnorePasswordRestrictions,
|
|||
|
OUT PBOOLEAN NtPasswordPresent,
|
|||
|
OUT PNT_OWF_PASSWORD NtOwfPassword,
|
|||
|
OUT PBOOLEAN LmPasswordPresent,
|
|||
|
OUT PLM_OWF_PASSWORD LmOwfPassword
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine takes a cleartext unicode NT password from the user,
|
|||
|
makes sure it meets our high standards for password quality,
|
|||
|
converts it to an LM password if possible, and runs both passwords
|
|||
|
through a one-way function (OWF).
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
UserHandle - The handle of an opened user to operate on.
|
|||
|
|
|||
|
UnicodePassword - the cleartext unicode NT password.
|
|||
|
|
|||
|
IgnorePasswordRestrictions - When TRUE, indicates that the password
|
|||
|
should be accepted as legitimate regardless of what the domain's
|
|||
|
password restrictions indicate (e.g., can be less than
|
|||
|
required password length). This is expected to be used when
|
|||
|
setting up a new machine account.
|
|||
|
|
|||
|
NtPasswordPresent - receives a boolean that says whether the NT
|
|||
|
password is present or not.
|
|||
|
|
|||
|
NtOwfPassword - receives the OWF'd version of the NT password.
|
|||
|
|
|||
|
LmPasswordPresent - receives a boolean that says whether the LM
|
|||
|
password is present or not.
|
|||
|
|
|||
|
LmOwfPassword - receives the OWF'd version of the LM password.
|
|||
|
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - the service has completed. The booleans say which
|
|||
|
of the OWFs are valid.
|
|||
|
|
|||
|
Errors are returned by SampCheckPasswordRestrictions(),
|
|||
|
RtlCalculateNtOwfPassword(), SampCalculateLmPassword(), and
|
|||
|
RtlCalculateLmOwfPassword().
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
PCHAR LmPasswordBuffer;
|
|||
|
BOOLEAN UseOwfPasswords;
|
|||
|
|
|||
|
//
|
|||
|
// We ignore the UseOwfPasswords flag since we already are.
|
|||
|
//
|
|||
|
|
|||
|
if (IgnorePasswordRestrictions) {
|
|||
|
NtStatus = STATUS_SUCCESS;
|
|||
|
} else {
|
|||
|
NtStatus = SampCheckPasswordRestrictions(
|
|||
|
UserHandle,
|
|||
|
UnicodePassword,
|
|||
|
&UseOwfPasswords
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Compute the NT One-Way-Function of the password
|
|||
|
//
|
|||
|
|
|||
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|||
|
|
|||
|
*NtPasswordPresent = TRUE;
|
|||
|
|
|||
|
NtStatus = RtlCalculateNtOwfPassword(
|
|||
|
UnicodePassword,
|
|||
|
NtOwfPassword
|
|||
|
);
|
|||
|
|
|||
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Calculate the LM version of the password
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampCalculateLmPassword(
|
|||
|
UnicodePassword,
|
|||
|
&LmPasswordBuffer);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Compute the One-Way-Function of the LM password
|
|||
|
//
|
|||
|
|
|||
|
*LmPasswordPresent = TRUE;
|
|||
|
|
|||
|
NtStatus = RtlCalculateLmOwfPassword(
|
|||
|
LmPasswordBuffer,
|
|||
|
LmOwfPassword);
|
|||
|
|
|||
|
//
|
|||
|
// We're finished with the LM password
|
|||
|
//
|
|||
|
|
|||
|
MIDL_user_free(LmPasswordBuffer);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return( NtStatus );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampRandomFill(
|
|||
|
IN ULONG BufferSize,
|
|||
|
IN OUT PUCHAR Buffer
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine fills a buffer with random data.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
BufferSize - Length of the input buffer, in bytes.
|
|||
|
|
|||
|
Buffer - Input buffer to be filled with random data.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
Errors from NtQuerySystemTime()
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
ULONG Index;
|
|||
|
LARGE_INTEGER Time;
|
|||
|
ULONG Seed;
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
|
|||
|
NtStatus = NtQuerySystemTime(&Time);
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
return(NtStatus);
|
|||
|
}
|
|||
|
|
|||
|
Seed = Time.LowPart ^ Time.HighPart;
|
|||
|
|
|||
|
for (Index = 0 ; Index < BufferSize ; Index++ )
|
|||
|
{
|
|||
|
*Buffer++ = (UCHAR) (RtlRandom(&Seed) % 256);
|
|||
|
}
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampEncryptClearPassword(
|
|||
|
IN SAM_HANDLE UserHandle,
|
|||
|
IN PUNICODE_STRING UnicodePassword,
|
|||
|
OUT PSAMPR_ENCRYPTED_USER_PASSWORD EncryptedUserPassword
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine takes a cleartext unicode NT password from the user,
|
|||
|
and encrypts it with the session key.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
UserHandle - SAM_HANDLE used to acquiring a session key.
|
|||
|
|
|||
|
UnicodePassword - the cleartext unicode NT password.
|
|||
|
|
|||
|
EncryptedUserPassword - receives the encrypted cleartext password.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - the service has completed. The booleans say which
|
|||
|
of the OWFs are valid.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
USER_SESSION_KEY UserSessionKey;
|
|||
|
struct RC4_KEYSTRUCT Rc4Key;
|
|||
|
PSAMPR_USER_PASSWORD UserPassword = (PSAMPR_USER_PASSWORD) EncryptedUserPassword;
|
|||
|
|
|||
|
if (UnicodePassword->Length > SAM_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) {
|
|||
|
return(STATUS_PASSWORD_RESTRICTION);
|
|||
|
}
|
|||
|
|
|||
|
NtStatus = RtlGetUserSessionKeyClient(
|
|||
|
(RPC_BINDING_HANDLE)UserHandle,
|
|||
|
&UserSessionKey
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Convert the session key into an RC4 key
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
rc4_key(
|
|||
|
&Rc4Key,
|
|||
|
sizeof(USER_SESSION_KEY),
|
|||
|
(PUCHAR) &UserSessionKey
|
|||
|
);
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
((PCHAR) UserPassword->Buffer) +
|
|||
|
(SAM_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) -
|
|||
|
UnicodePassword->Length,
|
|||
|
UnicodePassword->Buffer,
|
|||
|
UnicodePassword->Length
|
|||
|
);
|
|||
|
UserPassword->Length = UnicodePassword->Length;
|
|||
|
|
|||
|
NtStatus = SampRandomFill(
|
|||
|
(SAM_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) -
|
|||
|
UnicodePassword->Length,
|
|||
|
(PUCHAR) UserPassword->Buffer
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
rc4(
|
|||
|
&Rc4Key,
|
|||
|
sizeof(SAMPR_ENCRYPTED_USER_PASSWORD),
|
|||
|
(PUCHAR) UserPassword
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
return( NtStatus );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampEncryptOwfs(
|
|||
|
IN SAM_HANDLE UserHandle,
|
|||
|
IN BOOLEAN NtPasswordPresent,
|
|||
|
IN PNT_OWF_PASSWORD NtOwfPassword,
|
|||
|
OUT PENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword,
|
|||
|
IN BOOLEAN LmPasswordPresent,
|
|||
|
IN PLM_OWF_PASSWORD LmOwfPassword,
|
|||
|
OUT PENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine takes NT and LM passwords that have already been OWF'd,
|
|||
|
and encrypts them so that they can be safely sent to the server.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
UserHandle - The handle of an opened user to operate on.
|
|||
|
|
|||
|
NtPasswordPresent - indicates whether NtOwfPassword is valid or not.
|
|||
|
|
|||
|
NtOwfPassword - an OWF'd NT password, if NtPasswordPresent is true.
|
|||
|
|
|||
|
EncryptedNtOwfPassword - an encrypted version of the OWF'd NT password
|
|||
|
that can be safely sent to the server.
|
|||
|
|
|||
|
LmPasswordPresent - indicates whether LmOwfPassword is valid or not.
|
|||
|
|
|||
|
LmOwfPassword - an OWF'd LM password, if LmPasswordPresent is true.
|
|||
|
|
|||
|
EncryptedLmOwfPassword - an encrypted version of the OWF'd LM password
|
|||
|
that can be safely sent to the server.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - the passwords were encrypted and may be sent to the
|
|||
|
server.
|
|||
|
|
|||
|
Errors may be returned by RtlGetUserSessionKeyClient(),
|
|||
|
RtlEncryptNtOwfPwdWithUserKey(), and RtlEncryptLmOwfPwdWithUserKey().
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
USER_SESSION_KEY UserSessionKey;
|
|||
|
|
|||
|
NtStatus = RtlGetUserSessionKeyClient(
|
|||
|
(RPC_BINDING_HANDLE)UserHandle,
|
|||
|
&UserSessionKey
|
|||
|
);
|
|||
|
|
|||
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|||
|
|
|||
|
if (NtPasswordPresent) {
|
|||
|
|
|||
|
//
|
|||
|
// Encrypt the Nt OWF Password with the user session key
|
|||
|
// and store it the buffer to send
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = RtlEncryptNtOwfPwdWithUserKey(
|
|||
|
NtOwfPassword,
|
|||
|
&UserSessionKey,
|
|||
|
EncryptedNtOwfPassword
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|||
|
|
|||
|
if (LmPasswordPresent) {
|
|||
|
|
|||
|
//
|
|||
|
// Encrypt the Lm OWF Password with the user session key
|
|||
|
// and store it the buffer to send
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = RtlEncryptLmOwfPwdWithUserKey(
|
|||
|
LmOwfPassword,
|
|||
|
&UserSessionKey,
|
|||
|
EncryptedLmOwfPassword
|
|||
|
);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return( NtStatus );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamSetInformationUser(
|
|||
|
IN SAM_HANDLE UserHandle,
|
|||
|
IN USER_INFORMATION_CLASS UserInformationClass,
|
|||
|
IN PVOID Buffer
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API modifies information in a user record. The data modified
|
|||
|
is determined by the UserInformationClass parameter. To change
|
|||
|
information here requires access to the user object defined above.
|
|||
|
Each structure has both a read and write access type associated with
|
|||
|
it. In general, a user may call GetInformation with class
|
|||
|
UserLogonInformation, but may only call SetInformation with class
|
|||
|
UserPreferencesInformation. Access type USER_WRITE_ACCOUNT allows
|
|||
|
changes to be made to all fields.
|
|||
|
|
|||
|
NOTE: If the password is set to a new password then the password-
|
|||
|
set timestamp is reset as well.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
UserHandle - The handle of an opened user to operate on.
|
|||
|
|
|||
|
UserInformationClass - Class of information provided. The
|
|||
|
accesses required for each class is shown below:
|
|||
|
|
|||
|
Info Level Required Access Type
|
|||
|
----------------------- ------------------------
|
|||
|
UserGeneralInformation (Can't set)
|
|||
|
|
|||
|
UserPreferencesInformation USER_WRITE_PREFERENCES
|
|||
|
|
|||
|
UserParametersInformation USER_WRITE_ACCOUNT
|
|||
|
|
|||
|
UserLogonInformation (Can't set)
|
|||
|
|
|||
|
UserLogonHoursInformation USER_WRITE_ACCOUNT
|
|||
|
|
|||
|
UserAccountInformation (Can't set)
|
|||
|
|
|||
|
UserNameInformation USER_WRITE_ACCOUNT
|
|||
|
UserAccountNameInformation USER_WRITE_ACCOUNT
|
|||
|
UserFullNameInformation USER_WRITE_ACCOUNT
|
|||
|
UserPrimaryGroupInformation USER_WRITE_ACCOUNT
|
|||
|
UserHomeInformation USER_WRITE_ACCOUNT
|
|||
|
UserScriptInformation USER_WRITE_ACCOUNT
|
|||
|
UserProfileInformation USER_WRITE_ACCOUNT
|
|||
|
UserAdminCommentInformation USER_WRITE_ACCOUNT
|
|||
|
UserWorkStationsInformation USER_WRITE_ACCOUNT
|
|||
|
UserSetPasswordInformation USER_FORCE_PASSWORD_CHANGE (also see note below)
|
|||
|
UserControlInformation USER_WRITE_ACCOUNT
|
|||
|
UserExpiresInformation USER_WRITE_ACCOUNT
|
|||
|
UserInternal1Information USER_FORCE_PASSWORD_CHANGE (also see note below)
|
|||
|
UserInternal2Information (trusted client use only)
|
|||
|
UserAllInformation Will set fields that user
|
|||
|
specifies, if accesses are
|
|||
|
held as described above.
|
|||
|
|
|||
|
|
|||
|
NOTE: When setting a password (with either
|
|||
|
UserSetPasswordInformation or UserInternal1Information),
|
|||
|
you MUST open the user account via a DomainHandle that
|
|||
|
was opened for DOMAIN_READ_PASSWORD_PARAMETERS.
|
|||
|
|
|||
|
Buffer - Buffer containing a user info struct.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_INVALID_INFO_CLASS - The class provided was invalid.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be enabled for this
|
|||
|
operation
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
SAMPR_USER_INTERNAL1_INFORMATION Internal1RpcBuffer;
|
|||
|
USER_INTERNAL1_INFORMATION Internal1Buffer;
|
|||
|
SAMPR_USER_INTERNAL4_INFORMATION Internal4RpcBuffer;
|
|||
|
SAMPR_USER_INTERNAL5_INFORMATION Internal5RpcBuffer;
|
|||
|
PVOID BufferToPass;
|
|||
|
PUSER_ALL_INFORMATION UserAll;
|
|||
|
USER_ALL_INFORMATION LocalAll;
|
|||
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|||
|
BOOLEAN IgnorePasswordRestrictions;
|
|||
|
ULONG Pass = 0;
|
|||
|
USER_INFORMATION_CLASS ClassToUse = UserInformationClass;
|
|||
|
BOOLEAN SendOwfs = FALSE;
|
|||
|
|
|||
|
SampOutputDebugString("SamSetInformationUser");
|
|||
|
|
|||
|
do
|
|||
|
{
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
//
|
|||
|
// Normally just pass the info buffer through to rpc
|
|||
|
//
|
|||
|
|
|||
|
BufferToPass = Buffer;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Deal with special cases
|
|||
|
//
|
|||
|
|
|||
|
switch (UserInformationClass) {
|
|||
|
|
|||
|
|
|||
|
case UserPreferencesInformation: {
|
|||
|
|
|||
|
//
|
|||
|
// Field is unused, but make sure RPC doesn't choke on it.
|
|||
|
//
|
|||
|
|
|||
|
((PUSER_PREFERENCES_INFORMATION)(Buffer))->Reserved1.Length = 0;
|
|||
|
((PUSER_PREFERENCES_INFORMATION)(Buffer))->Reserved1.MaximumLength = 0;
|
|||
|
((PUSER_PREFERENCES_INFORMATION)(Buffer))->Reserved1.Buffer = NULL;
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case UserSetPasswordInformation:
|
|||
|
|
|||
|
if (Pass == 0) {
|
|||
|
|
|||
|
//
|
|||
|
// On the zeroth pass try sending a UserInternal5 structure.
|
|||
|
// This is only available on 3.51 and above releases.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Check password restrictions.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampCheckPasswordRestrictions(
|
|||
|
UserHandle,
|
|||
|
&((PUSER_SET_PASSWORD_INFORMATION)(Buffer))->Password,
|
|||
|
&SendOwfs
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// If password restrictions told us we could send reversibly
|
|||
|
// encrypted passwords, compute them. Otherwise drop through
|
|||
|
// to the OWF case.
|
|||
|
//
|
|||
|
|
|||
|
if (!SendOwfs) {
|
|||
|
|
|||
|
//
|
|||
|
// Encrypt the cleatext password - we don't need to
|
|||
|
// restrictions because that can be done on the server.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampEncryptClearPassword(
|
|||
|
UserHandle,
|
|||
|
&((PUSER_SET_PASSWORD_INFORMATION)(Buffer))->Password,
|
|||
|
&Internal5RpcBuffer.UserPassword
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
Internal5RpcBuffer.PasswordExpired =
|
|||
|
((PUSER_SET_PASSWORD_INFORMATION)(Buffer))->PasswordExpired;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Set the class and buffer to send over.
|
|||
|
//
|
|||
|
|
|||
|
ClassToUse = UserInternal5Information;
|
|||
|
BufferToPass = &Internal5RpcBuffer;
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Set the pass counter to one since we aren't trying a new
|
|||
|
// interface and don't want to retry.
|
|||
|
//
|
|||
|
|
|||
|
Pass = 1;
|
|||
|
SendOwfs = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
ASSERT(SendOwfs);
|
|||
|
|
|||
|
//
|
|||
|
// We're going to calculate the OWFs for the password and
|
|||
|
// turn this into an INTERNAL1 set info request by dropping
|
|||
|
// through to the INTERNAL1 code with Buffer pointing at our
|
|||
|
// local INTERNAL1 buffer. First, make sure that the password
|
|||
|
// meets our quality requirements.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampOwfPassword(
|
|||
|
UserHandle,
|
|||
|
&((PUSER_SET_PASSWORD_INFORMATION)(Buffer))->Password,
|
|||
|
FALSE, // Don't ignore password restrictions
|
|||
|
&Internal1Buffer.NtPasswordPresent,
|
|||
|
&Internal1Buffer.NtOwfPassword,
|
|||
|
&Internal1Buffer.LmPasswordPresent,
|
|||
|
&Internal1Buffer.LmOwfPassword
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Copy the PasswordExpired flag
|
|||
|
//
|
|||
|
|
|||
|
Internal1Buffer.PasswordExpired =
|
|||
|
((PUSER_SET_PASSWORD_INFORMATION)(Buffer))->PasswordExpired;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// We now have a USER_INTERNAL1_INFO buffer in Internal1Buffer.
|
|||
|
// Point Buffer at Internal1buffer and drop through to the code
|
|||
|
// that handles INTERNAL1 requests
|
|||
|
|
|||
|
Buffer = &Internal1Buffer;
|
|||
|
ClassToUse = UserInternal1Information;
|
|||
|
|
|||
|
//
|
|||
|
// drop through.....
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
case UserInternal1Information:
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// We're going to pass a different data structure to rpc
|
|||
|
//
|
|||
|
|
|||
|
BufferToPass = &Internal1RpcBuffer;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Copy the password present flags
|
|||
|
//
|
|||
|
|
|||
|
Internal1RpcBuffer.NtPasswordPresent =
|
|||
|
((PUSER_INTERNAL1_INFORMATION)Buffer)->NtPasswordPresent;
|
|||
|
|
|||
|
Internal1RpcBuffer.LmPasswordPresent =
|
|||
|
((PUSER_INTERNAL1_INFORMATION)Buffer)->LmPasswordPresent;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Copy the PasswordExpired flag
|
|||
|
//
|
|||
|
|
|||
|
Internal1RpcBuffer.PasswordExpired =
|
|||
|
((PUSER_INTERNAL1_INFORMATION)Buffer)->PasswordExpired;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Encrypt the OWFs with the user session key before we send
|
|||
|
// them over the Rpc link
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampEncryptOwfs(
|
|||
|
UserHandle,
|
|||
|
Internal1RpcBuffer.NtPasswordPresent,
|
|||
|
&((PUSER_INTERNAL1_INFORMATION)Buffer)->NtOwfPassword,
|
|||
|
&Internal1RpcBuffer.EncryptedNtOwfPassword,
|
|||
|
Internal1RpcBuffer.LmPasswordPresent,
|
|||
|
&((PUSER_INTERNAL1_INFORMATION)Buffer)->LmOwfPassword,
|
|||
|
&Internal1RpcBuffer.EncryptedLmOwfPassword
|
|||
|
);
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
case UserAllInformation:
|
|||
|
|
|||
|
UserAll = (PUSER_ALL_INFORMATION)Buffer;
|
|||
|
|
|||
|
//
|
|||
|
// If the caller is passing passwords we need to convert them
|
|||
|
// into OWFs and encrypt them.
|
|||
|
//
|
|||
|
|
|||
|
if (UserAll->WhichFields & (USER_ALL_LMPASSWORDPRESENT |
|
|||
|
USER_ALL_NTPASSWORDPRESENT) ) {
|
|||
|
|
|||
|
//
|
|||
|
// We'll need a private copy of the buffer which we can edit
|
|||
|
// and then send over RPC.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
if (UserAll->WhichFields & USER_ALL_OWFPASSWORD) {
|
|||
|
|
|||
|
LocalAll = *UserAll;
|
|||
|
BufferToPass = &LocalAll;
|
|||
|
SendOwfs = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// The caller is passing OWFS directly
|
|||
|
// Check they're valid and copy them into the
|
|||
|
// Internal1Buffer in preparation for encryption.
|
|||
|
//
|
|||
|
|
|||
|
if (LocalAll.WhichFields & USER_ALL_NTPASSWORDPRESENT) {
|
|||
|
|
|||
|
if (LocalAll.NtPasswordPresent) {
|
|||
|
|
|||
|
if (LocalAll.NtPassword.Length != NT_OWF_PASSWORD_LENGTH) {
|
|||
|
NtStatus = STATUS_INVALID_PARAMETER;
|
|||
|
} else {
|
|||
|
Internal1Buffer.NtOwfPassword =
|
|||
|
*((PNT_OWF_PASSWORD)LocalAll.NtPassword.Buffer);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
LocalAll.NtPasswordPresent = FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (LocalAll.WhichFields & USER_ALL_LMPASSWORDPRESENT) {
|
|||
|
|
|||
|
if (LocalAll.LmPasswordPresent) {
|
|||
|
|
|||
|
if (LocalAll.LmPassword.Length != LM_OWF_PASSWORD_LENGTH) {
|
|||
|
NtStatus = STATUS_INVALID_PARAMETER;
|
|||
|
} else {
|
|||
|
Internal1Buffer.LmOwfPassword =
|
|||
|
*((PNT_OWF_PASSWORD)LocalAll.LmPassword.Buffer);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
LocalAll.LmPasswordPresent = FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Always remove the OWF_PASSWORDS flag. This is used
|
|||
|
// only on the client side to determine the mode
|
|||
|
// of password input and will be rejected by the server
|
|||
|
//
|
|||
|
|
|||
|
LocalAll.WhichFields &= ~USER_ALL_OWFPASSWORD;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// The caller is passing text passwords.
|
|||
|
// Check for validity and convert to OWFs.
|
|||
|
//
|
|||
|
|
|||
|
if (UserAll->WhichFields & USER_ALL_LMPASSWORDPRESENT) {
|
|||
|
|
|||
|
//
|
|||
|
// User clients are only allowed to put a unicode string
|
|||
|
// in the NT password. We always calculate the LM password
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = STATUS_INVALID_PARAMETER;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// The caller might be simultaneously setting
|
|||
|
// the password and changing the account to be
|
|||
|
// a machine or trust account. In this case,
|
|||
|
// we don't validate the password (e.g., length).
|
|||
|
//
|
|||
|
|
|||
|
IgnorePasswordRestrictions = FALSE;
|
|||
|
if (UserAll->WhichFields &
|
|||
|
USER_ALL_USERACCOUNTCONTROL) {
|
|||
|
if (UserAll->UserAccountControl &
|
|||
|
(USER_WORKSTATION_TRUST_ACCOUNT | USER_SERVER_TRUST_ACCOUNT)
|
|||
|
) {
|
|||
|
IgnorePasswordRestrictions = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
SendOwfs = TRUE;
|
|||
|
if (Pass == 0) {
|
|||
|
|
|||
|
//
|
|||
|
// On the first pass, try sending the cleatext
|
|||
|
// password.
|
|||
|
//
|
|||
|
|
|||
|
Internal4RpcBuffer.I1 = *(PSAMPR_USER_ALL_INFORMATION)
|
|||
|
UserAll;
|
|||
|
|
|||
|
BufferToPass = &Internal4RpcBuffer;
|
|||
|
ClassToUse = UserInternal4Information;
|
|||
|
SendOwfs = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// Check the password restrictions. We also
|
|||
|
// want to get the information on whether
|
|||
|
// we can send reversibly encrypted passwords.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampCheckPasswordRestrictions(
|
|||
|
UserHandle,
|
|||
|
&UserAll->NtPassword,
|
|||
|
&SendOwfs
|
|||
|
);
|
|||
|
|
|||
|
if (IgnorePasswordRestrictions) {
|
|||
|
NtStatus = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
if (!SendOwfs) {
|
|||
|
//
|
|||
|
// Encrypt the clear password
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampEncryptClearPassword(
|
|||
|
UserHandle,
|
|||
|
&UserAll->NtPassword,
|
|||
|
&Internal4RpcBuffer.UserPassword
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Zero the password NT password
|
|||
|
//
|
|||
|
|
|||
|
RtlZeroMemory(
|
|||
|
&Internal4RpcBuffer.I1.NtOwfPassword,
|
|||
|
sizeof(UNICODE_STRING)
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (SendOwfs) {
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// On the second pass, do the normal thing.
|
|||
|
//
|
|||
|
|
|||
|
LocalAll = *UserAll;
|
|||
|
BufferToPass = &LocalAll;
|
|||
|
SendOwfs = TRUE;
|
|||
|
|
|||
|
ClassToUse = UserAllInformation;
|
|||
|
if ( LocalAll.WhichFields & USER_ALL_NTPASSWORDPRESENT ) {
|
|||
|
|
|||
|
//
|
|||
|
// The user specified a password. We must validate
|
|||
|
// it, convert it to LM, and calculate the OWFs
|
|||
|
//
|
|||
|
|
|||
|
LocalAll.WhichFields |= USER_ALL_LMPASSWORDPRESENT;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Stick the OWFs in the Internal1Buffer - just
|
|||
|
// until we use them in the SampEncryptOwfs().
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampOwfPassword(
|
|||
|
UserHandle,
|
|||
|
&LocalAll.NtPassword,
|
|||
|
IgnorePasswordRestrictions,
|
|||
|
&LocalAll.NtPasswordPresent,
|
|||
|
&(Internal1Buffer.NtOwfPassword),
|
|||
|
&LocalAll.LmPasswordPresent,
|
|||
|
&(Internal1Buffer.LmOwfPassword)
|
|||
|
);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// We now have one or more OWFs in Internal1 buffer.
|
|||
|
// We got these either directly or we calculated them
|
|||
|
// from the text strings.
|
|||
|
// Encrypt these OWFs with the session key and
|
|||
|
// store the result in Internal1RpcBuffer.
|
|||
|
//
|
|||
|
// Note the Password present flags are in LocalAll.
|
|||
|
// (The ones in Internal1Buffer are not used.)
|
|||
|
//
|
|||
|
|
|||
|
if ( NT_SUCCESS( NtStatus ) && SendOwfs ) {
|
|||
|
|
|||
|
//
|
|||
|
// Make all LocalAll's password strings point to
|
|||
|
// the buffers in Internal1RpcBuffer
|
|||
|
//
|
|||
|
|
|||
|
LocalAll.NtPassword.Length =
|
|||
|
sizeof( ENCRYPTED_NT_OWF_PASSWORD );
|
|||
|
LocalAll.NtPassword.MaximumLength =
|
|||
|
sizeof( ENCRYPTED_NT_OWF_PASSWORD );
|
|||
|
LocalAll.NtPassword.Buffer = (PWSTR)
|
|||
|
&Internal1RpcBuffer.EncryptedNtOwfPassword;
|
|||
|
|
|||
|
LocalAll.LmPassword.Length =
|
|||
|
sizeof( ENCRYPTED_LM_OWF_PASSWORD );
|
|||
|
LocalAll.LmPassword.MaximumLength =
|
|||
|
sizeof( ENCRYPTED_LM_OWF_PASSWORD );
|
|||
|
LocalAll.LmPassword.Buffer = (PWSTR)
|
|||
|
&Internal1RpcBuffer.EncryptedLmOwfPassword;
|
|||
|
|
|||
|
//
|
|||
|
// Encrypt the Owfs
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampEncryptOwfs(
|
|||
|
UserHandle,
|
|||
|
LocalAll.NtPasswordPresent,
|
|||
|
&Internal1Buffer.NtOwfPassword,
|
|||
|
&Internal1RpcBuffer.EncryptedNtOwfPassword,
|
|||
|
LocalAll.LmPasswordPresent,
|
|||
|
&Internal1Buffer.LmOwfPassword,
|
|||
|
&Internal1RpcBuffer.EncryptedLmOwfPassword
|
|||
|
);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
} // switch
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// If we are trying one of the new info levels, use the new
|
|||
|
// api.
|
|||
|
//
|
|||
|
|
|||
|
if ((ClassToUse == UserInternal4Information) ||
|
|||
|
(ClassToUse == UserInternal5Information)) {
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrSetInformationUser2(
|
|||
|
(SAMPR_HANDLE)UserHandle,
|
|||
|
ClassToUse,
|
|||
|
BufferToPass
|
|||
|
);
|
|||
|
|
|||
|
} else {
|
|||
|
NtStatus =
|
|||
|
SamrSetInformationUser(
|
|||
|
(SAMPR_HANDLE)UserHandle,
|
|||
|
ClassToUse,
|
|||
|
BufferToPass
|
|||
|
);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
Pass++;
|
|||
|
|
|||
|
//
|
|||
|
// If this is the first pass and the status indicated that the
|
|||
|
// server did not support the info class or the api
|
|||
|
// and we were trying one of the new info levels, try again.
|
|||
|
//
|
|||
|
|
|||
|
} while ( (Pass < 2) &&
|
|||
|
((NtStatus == RPC_NT_INVALID_TAG) ||
|
|||
|
(NtStatus == RPC_NT_UNKNOWN_IF) ||
|
|||
|
(NtStatus == RPC_NT_PROCNUM_OUT_OF_RANGE)));
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamiLmChangePasswordUser(
|
|||
|
IN SAM_HANDLE UserHandle,
|
|||
|
IN PENCRYPTED_LM_OWF_PASSWORD LmOldEncryptedWithLmNew,
|
|||
|
IN PENCRYPTED_LM_OWF_PASSWORD LmNewEncryptedWithLmOld
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Changes the password of a user account. This routine is intended to be
|
|||
|
called by down-level system clients who have only the cross-encrypted
|
|||
|
LM passwords available to them.
|
|||
|
Password will be set to NewPassword only if OldPassword matches the
|
|||
|
current user password for this user and the NewPassword is not the
|
|||
|
same as the domain password parameter PasswordHistoryLength
|
|||
|
passwords. This call allows users to change their own password if
|
|||
|
they have access USER_CHANGE_PASSWORD. Password update restrictions
|
|||
|
apply.
|
|||
|
|
|||
|
This api will fail unless UAS Compatibility is enabled for the domain.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
UserHandle - The handle of an opened user to operate on.
|
|||
|
|
|||
|
LmOldEncryptedWithLmNew - the OWF of the old LM password encrypted using
|
|||
|
the OWF of the new LM password as a key.
|
|||
|
|
|||
|
LmNewEncryptedWithLmOld - the OWF of the new LM password encrypted using
|
|||
|
the OWF of the old LM password as a key.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_PASSWORD_RESTRICTION - A restriction prevents the password
|
|||
|
from being changed. This may be for a number of reasons,
|
|||
|
including time restrictions on how often a password may be
|
|||
|
changed or length restrictions on the provided 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. These are kept
|
|||
|
in the password recent history log.
|
|||
|
|
|||
|
STATUS_WRONG_PASSWORD - The old password is incorrect.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be enabled for this
|
|||
|
operation
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
//
|
|||
|
// Check parameter validity
|
|||
|
//
|
|||
|
|
|||
|
if (LmOldEncryptedWithLmNew == NULL) {
|
|||
|
return(STATUS_INVALID_PARAMETER_1);
|
|||
|
}
|
|||
|
if (LmNewEncryptedWithLmOld == NULL) {
|
|||
|
return(STATUS_INVALID_PARAMETER_2);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus = SamrChangePasswordUser(
|
|||
|
(SAMPR_HANDLE)UserHandle,
|
|||
|
|
|||
|
TRUE, // LmOldPresent
|
|||
|
LmOldEncryptedWithLmNew,
|
|||
|
LmNewEncryptedWithLmOld,
|
|||
|
|
|||
|
FALSE, // NtPresent
|
|||
|
NULL, // NtOldEncryptedWithNtNew
|
|||
|
NULL, // NtNewEncryptedWithNtOld
|
|||
|
|
|||
|
FALSE, // NtCrossEncryptionPresent
|
|||
|
NULL,
|
|||
|
|
|||
|
FALSE, // LmCrossEncryptionPresent
|
|||
|
NULL
|
|||
|
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// We should never get asked for cross-encrypted data
|
|||
|
// since the server knows we don't have any NT data.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT (NtStatus != STATUS_NT_CROSS_ENCRYPTION_REQUIRED);
|
|||
|
ASSERT (NtStatus != STATUS_LM_CROSS_ENCRYPTION_REQUIRED);
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamiChangePasswordUser(
|
|||
|
IN SAM_HANDLE UserHandle,
|
|||
|
IN BOOLEAN LmOldPresent,
|
|||
|
IN PLM_OWF_PASSWORD LmOldOwfPassword,
|
|||
|
IN PLM_OWF_PASSWORD LmNewOwfPassword,
|
|||
|
IN BOOLEAN NtPresent,
|
|||
|
IN PNT_OWF_PASSWORD NtOldOwfPassword,
|
|||
|
IN PNT_OWF_PASSWORD NtNewOwfPassword
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Changes the password of a user account. This is the worker routine for
|
|||
|
SamChangePasswordUser and can be called by OWF-aware clients.
|
|||
|
Password will be set to NewPassword only if OldPassword matches the
|
|||
|
current user password for this user and the NewPassword is not the
|
|||
|
same as the domain password parameter PasswordHistoryLength
|
|||
|
passwords. This call allows users to change their own password if
|
|||
|
they have access USER_CHANGE_PASSWORD. Password update restrictions
|
|||
|
apply.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
UserHandle - The handle of an opened user to operate on.
|
|||
|
|
|||
|
LMOldPresent - TRUE if the LmOldOwfPassword is valid. This should only
|
|||
|
be FALSE if the old password is too long to be represented
|
|||
|
by a LM password. (Complex NT password).
|
|||
|
Note the LMNewOwfPassword must always be valid.
|
|||
|
If the new password is complex, the LMNewOwfPassword should
|
|||
|
be the well-known LM OWF of a NULL password.
|
|||
|
|
|||
|
LmOldOwfPassword - One-way-function of the current LM password for the user.
|
|||
|
- Ignored if LmOldPresent == FALSE
|
|||
|
|
|||
|
LmNewOwfPassword - One-way-function of the new LM password for the user.
|
|||
|
|
|||
|
NtPresent - TRUE if the NT one-way-functions are valid.
|
|||
|
- i.e. This will be FALSE if we're called from a down-level client.
|
|||
|
|
|||
|
NtOldOwfPassword - One-way-function of the current NT password for the user.
|
|||
|
|
|||
|
NtNewOwfPassword - One-way-function of the new NT password for the user.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_ILL_FORMED_PASSWORD - The new password is poorly formed,
|
|||
|
e.g. contains characters that can't be entered from the
|
|||
|
keyboard, etc.
|
|||
|
|
|||
|
STATUS_PASSWORD_RESTRICTION - A restriction prevents the password
|
|||
|
from being changed. This may be for a number of reasons,
|
|||
|
including time restrictions on how often a password may be
|
|||
|
changed or length restrictions on the provided 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. These are kept
|
|||
|
in the password recent history log.
|
|||
|
|
|||
|
STATUS_WRONG_PASSWORD - OldPassword does not contain the user's
|
|||
|
current password.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be enabled for this
|
|||
|
operation
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation.
|
|||
|
|
|||
|
STATUS_INVALID_PARAMETER_MIX - LmOldPresent or NtPresent or both
|
|||
|
must be TRUE.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
ENCRYPTED_NT_OWF_PASSWORD NtNewEncryptedWithNtOld;
|
|||
|
ENCRYPTED_NT_OWF_PASSWORD NtOldEncryptedWithNtNew;
|
|||
|
ENCRYPTED_NT_OWF_PASSWORD NtNewEncryptedWithLmNew;
|
|||
|
ENCRYPTED_LM_OWF_PASSWORD LmNewEncryptedWithLmOld;
|
|||
|
ENCRYPTED_LM_OWF_PASSWORD LmOldEncryptedWithLmNew;
|
|||
|
ENCRYPTED_LM_OWF_PASSWORD LmNewEncryptedWithNtNew;
|
|||
|
|
|||
|
PENCRYPTED_NT_OWF_PASSWORD pNtNewEncryptedWithNtOld;
|
|||
|
PENCRYPTED_NT_OWF_PASSWORD pNtOldEncryptedWithNtNew;
|
|||
|
PENCRYPTED_LM_OWF_PASSWORD pLmNewEncryptedWithLmOld;
|
|||
|
PENCRYPTED_LM_OWF_PASSWORD pLmOldEncryptedWithLmNew;
|
|||
|
|
|||
|
//
|
|||
|
// Check parameter validity
|
|||
|
//
|
|||
|
|
|||
|
if (!LmOldPresent && !NtPresent) {
|
|||
|
return(STATUS_INVALID_PARAMETER_MIX);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
//
|
|||
|
// We're going to encrypt the oldLM with the newLM and vice-versa.
|
|||
|
// We're going to encrypt the oldNT with the newNT and vice-versa.
|
|||
|
// We're going to send these 4 encryptions and see if we're successful.
|
|||
|
//
|
|||
|
// If we get a return code of STATUS_LM_CROSS_ENCRYPTION_REQUIRED,
|
|||
|
// we'll also encrypt the newLM with the newNT and send it all again.
|
|||
|
//
|
|||
|
// If we get a return code of STATUS_NT_CROSS_ENCRYPTION_REQUIRED,
|
|||
|
// we'll also encrypt the newNT with the newLM and send it all again.
|
|||
|
//
|
|||
|
// We don't always send the cross-encryption otherwise we would be
|
|||
|
// compromising security on pure NT systems with long passwords.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Do the LM Encryption
|
|||
|
//
|
|||
|
|
|||
|
if (!LmOldPresent) {
|
|||
|
|
|||
|
pLmOldEncryptedWithLmNew = NULL;
|
|||
|
pLmNewEncryptedWithLmOld = NULL;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
pLmOldEncryptedWithLmNew = &LmOldEncryptedWithLmNew;
|
|||
|
pLmNewEncryptedWithLmOld = &LmNewEncryptedWithLmOld;
|
|||
|
|
|||
|
NtStatus = RtlEncryptLmOwfPwdWithLmOwfPwd(
|
|||
|
LmOldOwfPassword,
|
|||
|
LmNewOwfPassword,
|
|||
|
&LmOldEncryptedWithLmNew);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
NtStatus = RtlEncryptLmOwfPwdWithLmOwfPwd(
|
|||
|
LmNewOwfPassword,
|
|||
|
LmOldOwfPassword,
|
|||
|
&LmNewEncryptedWithLmOld);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Do the NT Encryption
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
if (!NtPresent) {
|
|||
|
|
|||
|
pNtOldEncryptedWithNtNew = NULL;
|
|||
|
pNtNewEncryptedWithNtOld = NULL;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
pNtOldEncryptedWithNtNew = &NtOldEncryptedWithNtNew;
|
|||
|
pNtNewEncryptedWithNtOld = &NtNewEncryptedWithNtOld;
|
|||
|
|
|||
|
NtStatus = RtlEncryptNtOwfPwdWithNtOwfPwd(
|
|||
|
NtOldOwfPassword,
|
|||
|
NtNewOwfPassword,
|
|||
|
&NtOldEncryptedWithNtNew);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
NtStatus = RtlEncryptNtOwfPwdWithNtOwfPwd(
|
|||
|
NtNewOwfPassword,
|
|||
|
NtOldOwfPassword,
|
|||
|
&NtNewEncryptedWithNtOld);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server (with no cross-encryption)
|
|||
|
//
|
|||
|
|
|||
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|||
|
|
|||
|
NtStatus = SamrChangePasswordUser(
|
|||
|
(SAMPR_HANDLE)UserHandle,
|
|||
|
|
|||
|
LmOldPresent,
|
|||
|
pLmOldEncryptedWithLmNew,
|
|||
|
pLmNewEncryptedWithLmOld,
|
|||
|
|
|||
|
NtPresent,
|
|||
|
pNtOldEncryptedWithNtNew,
|
|||
|
pNtNewEncryptedWithNtOld,
|
|||
|
|
|||
|
FALSE, // NtCrossEncryptionPresent
|
|||
|
NULL,
|
|||
|
|
|||
|
FALSE, // LmCrossEncryptionPresent
|
|||
|
NULL
|
|||
|
|
|||
|
);
|
|||
|
|
|||
|
if (NtStatus == STATUS_NT_CROSS_ENCRYPTION_REQUIRED) {
|
|||
|
|
|||
|
//
|
|||
|
// We should only get this if we have both LM and NT data
|
|||
|
// (This is not obvious - it results from the server-side logic)
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(NtPresent && LmOldPresent);
|
|||
|
|
|||
|
//
|
|||
|
// Compute the cross-encryption of the new Nt password
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(LM_OWF_PASSWORD_LENGTH == NT_OWF_PASSWORD_LENGTH);
|
|||
|
|
|||
|
NtStatus = RtlEncryptNtOwfPwdWithNtOwfPwd(
|
|||
|
NtNewOwfPassword,
|
|||
|
(PNT_OWF_PASSWORD)LmNewOwfPassword,
|
|||
|
&NtNewEncryptedWithLmNew);
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server (with NT cross-encryption)
|
|||
|
//
|
|||
|
|
|||
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|||
|
|
|||
|
NtStatus = SamrChangePasswordUser(
|
|||
|
(SAMPR_HANDLE)UserHandle,
|
|||
|
|
|||
|
LmOldPresent,
|
|||
|
pLmOldEncryptedWithLmNew,
|
|||
|
pLmNewEncryptedWithLmOld,
|
|||
|
|
|||
|
NtPresent,
|
|||
|
pNtOldEncryptedWithNtNew,
|
|||
|
pNtNewEncryptedWithNtOld,
|
|||
|
|
|||
|
TRUE,
|
|||
|
&NtNewEncryptedWithLmNew,
|
|||
|
|
|||
|
FALSE,
|
|||
|
NULL
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
if (NtStatus == STATUS_LM_CROSS_ENCRYPTION_REQUIRED) {
|
|||
|
|
|||
|
//
|
|||
|
// We should only get this if we have NT but no old LM data
|
|||
|
// (This is not obvious - it results from the server-side logic)
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(NtPresent && !LmOldPresent);
|
|||
|
|
|||
|
//
|
|||
|
// Compute the cross-encryption of the new Nt password
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(LM_OWF_PASSWORD_LENGTH == NT_OWF_PASSWORD_LENGTH);
|
|||
|
|
|||
|
NtStatus = RtlEncryptLmOwfPwdWithLmOwfPwd(
|
|||
|
LmNewOwfPassword,
|
|||
|
(PLM_OWF_PASSWORD)NtNewOwfPassword,
|
|||
|
&LmNewEncryptedWithNtNew);
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server (with LM cross-encryption)
|
|||
|
//
|
|||
|
|
|||
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|||
|
|
|||
|
NtStatus = SamrChangePasswordUser(
|
|||
|
(SAMPR_HANDLE)UserHandle,
|
|||
|
|
|||
|
LmOldPresent,
|
|||
|
pLmOldEncryptedWithLmNew,
|
|||
|
pLmNewEncryptedWithLmOld,
|
|||
|
|
|||
|
NtPresent,
|
|||
|
pNtOldEncryptedWithNtNew,
|
|||
|
pNtNewEncryptedWithNtOld,
|
|||
|
|
|||
|
FALSE,
|
|||
|
NULL,
|
|||
|
|
|||
|
TRUE,
|
|||
|
&LmNewEncryptedWithNtNew
|
|||
|
);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamChangePasswordUser(
|
|||
|
IN SAM_HANDLE UserHandle,
|
|||
|
IN PUNICODE_STRING OldNtPassword,
|
|||
|
IN PUNICODE_STRING NewNtPassword
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Password will be set to NewPassword only if OldPassword matches the
|
|||
|
current user password for this user and the NewPassword is not the
|
|||
|
same as the domain password parameter PasswordHistoryLength
|
|||
|
passwords. This call allows users to change their own password if
|
|||
|
they have access USER_CHANGE_PASSWORD. Password update restrictions
|
|||
|
apply.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
UserHandle - The handle of an opened user to operate on.
|
|||
|
|
|||
|
OldPassword - Current password for the user.
|
|||
|
|
|||
|
NewPassword - Desired new password for the user.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_ILL_FORMED_PASSWORD - The new password is poorly formed,
|
|||
|
e.g. contains characters that can't be entered from the
|
|||
|
keyboard, etc.
|
|||
|
|
|||
|
STATUS_PASSWORD_RESTRICTION - A restriction prevents the password
|
|||
|
from being changed. This may be for a number of reasons,
|
|||
|
including time restrictions on how often a password may be
|
|||
|
changed or length restrictions on the provided 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. These are kept
|
|||
|
in the password recent history log.
|
|||
|
|
|||
|
STATUS_WRONG_PASSWORD - OldPassword does not contain the user's
|
|||
|
current password.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be enabled for this
|
|||
|
operation
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
LM_OWF_PASSWORD NewLmOwfPassword, OldLmOwfPassword;
|
|||
|
NT_OWF_PASSWORD NewNtOwfPassword, OldNtOwfPassword;
|
|||
|
BOOLEAN LmOldPresent;
|
|||
|
PCHAR LmPassword;
|
|||
|
NTSTATUS NtStatus;
|
|||
|
BOOLEAN UseOwfPasswords;
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus = SampCheckPasswordRestrictions(
|
|||
|
UserHandle,
|
|||
|
NewNtPassword,
|
|||
|
&UseOwfPasswords
|
|||
|
);
|
|||
|
|
|||
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Calculate the one-way-functions of the NT passwords
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = RtlCalculateNtOwfPassword(
|
|||
|
OldNtPassword,
|
|||
|
&OldNtOwfPassword
|
|||
|
);
|
|||
|
|
|||
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|||
|
|
|||
|
NtStatus = RtlCalculateNtOwfPassword(
|
|||
|
NewNtPassword,
|
|||
|
&NewNtOwfPassword
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Calculate the one-way-functions of the LM passwords
|
|||
|
//
|
|||
|
|
|||
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Calculate the LM version of the old password
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampCalculateLmPassword(
|
|||
|
OldNtPassword,
|
|||
|
&LmPassword);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
if (NtStatus == STATUS_NULL_LM_PASSWORD) {
|
|||
|
LmOldPresent = FALSE;
|
|||
|
} else {
|
|||
|
LmOldPresent = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Compute the One-Way-Function of the old LM password
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = RtlCalculateLmOwfPassword(
|
|||
|
LmPassword,
|
|||
|
&OldLmOwfPassword);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We're finished with the LM password
|
|||
|
//
|
|||
|
|
|||
|
MIDL_user_free(LmPassword);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Calculate the LM version of the new password
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
NtStatus = SampCalculateLmPassword(
|
|||
|
NewNtPassword,
|
|||
|
&LmPassword);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Compute the One-Way-Function of the new LM password
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = RtlCalculateLmOwfPassword(
|
|||
|
LmPassword,
|
|||
|
&NewLmOwfPassword);
|
|||
|
|
|||
|
//
|
|||
|
// We're finished with the LM password
|
|||
|
//
|
|||
|
|
|||
|
MIDL_user_free(LmPassword);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call our worker routine with the one-way-functions
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
NtStatus = SamiChangePasswordUser(
|
|||
|
UserHandle,
|
|||
|
LmOldPresent,
|
|||
|
&OldLmOwfPassword,
|
|||
|
&NewLmOwfPassword,
|
|||
|
TRUE, // NT present
|
|||
|
&OldNtOwfPassword,
|
|||
|
&NewNtOwfPassword
|
|||
|
);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamGetGroupsForUser(
|
|||
|
IN SAM_HANDLE UserHandle,
|
|||
|
OUT PGROUP_MEMBERSHIP * Groups,
|
|||
|
OUT PULONG MembershipCount
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This service returns the list of groups that a user is a member of.
|
|||
|
It returns a structure for each group that includes the relative ID
|
|||
|
of the group, and the attributes of the group that are assigned to
|
|||
|
the user.
|
|||
|
|
|||
|
This service requires USER_LIST_GROUPS access to the user account
|
|||
|
object.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
UserHandle - The handle of an opened user to operate on.
|
|||
|
|
|||
|
Groups - Receives a pointer to a buffer containing an array of
|
|||
|
GROUP_MEMBERSHIPs data structures. When this information is
|
|||
|
no longer needed, this buffer must be freed using
|
|||
|
SamFreeMemory().
|
|||
|
|
|||
|
MembershipCount - Receives the number of groups the user is a
|
|||
|
member of, and, thus, the number elements returned in the
|
|||
|
Groups array.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
PSAMPR_GET_GROUPS_BUFFER GetGroupsBuffer;
|
|||
|
|
|||
|
|
|||
|
SampOutputDebugString("SamGetGroupsForUser");
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server ...
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
GetGroupsBuffer = NULL;
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
NtStatus =
|
|||
|
SamrGetGroupsForUser(
|
|||
|
(SAMPR_HANDLE)UserHandle,
|
|||
|
&GetGroupsBuffer
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
(*MembershipCount) = GetGroupsBuffer->MembershipCount;
|
|||
|
(*Groups) = GetGroupsBuffer->Groups;
|
|||
|
MIDL_user_free( GetGroupsBuffer );
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Deallocate any returned buffers on error
|
|||
|
//
|
|||
|
|
|||
|
if (GetGroupsBuffer != NULL) {
|
|||
|
if (GetGroupsBuffer->Groups != NULL) {
|
|||
|
MIDL_user_free(GetGroupsBuffer->Groups);
|
|||
|
}
|
|||
|
MIDL_user_free(GetGroupsBuffer);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamTestPrivateFunctionsDomain(
|
|||
|
IN SAMPR_HANDLE DomainHandle
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This service is called to test functions that are normally only
|
|||
|
accessible inside the security process.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DomainHandle - Handle to a domain to be tested.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - The tests completed successfully.
|
|||
|
|
|||
|
Any errors are as propogated from the tests.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
#ifdef SAM_SERVER_TESTS
|
|||
|
return( SamrTestPrivateFunctionsDomain( DomainHandle ) );
|
|||
|
#else
|
|||
|
return( STATUS_NOT_IMPLEMENTED );
|
|||
|
UNREFERENCED_PARAMETER(DomainHandle);
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamTestPrivateFunctionsUser(
|
|||
|
IN SAMPR_HANDLE UserHandle
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This service is called to test functions that are normally only
|
|||
|
accessible inside the security process.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
UserHandle - Handle to a user to be tested.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - The tests completed successfully.
|
|||
|
|
|||
|
Any errors are as propogated from the tests.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
#ifdef SAM_SERVER_TESTS
|
|||
|
return( SamrTestPrivateFunctionsUser( UserHandle ) );
|
|||
|
#else
|
|||
|
return( STATUS_NOT_IMPLEMENTED );
|
|||
|
UNREFERENCED_PARAMETER(UserHandle);
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
// //
|
|||
|
// private services //
|
|||
|
// //
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampMapCompletionStatus(
|
|||
|
IN NTSTATUS Status
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This service maps completion status received back from an RPC call
|
|||
|
into a completion status to be returned from SAM api.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
Status - Status value to be mapped.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
The mapped SAM status value.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
if (Status == RPC_NT_INVALID_BINDING) {
|
|||
|
Status = STATUS_INVALID_HANDLE;
|
|||
|
}
|
|||
|
// if (Status == RPC_ACCESS_DENIED) {
|
|||
|
// Status = STATUS_ACCESS_DENIED;
|
|||
|
// }
|
|||
|
|
|||
|
|
|||
|
|
|||
|
return( Status );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampCalculateLmPassword(
|
|||
|
IN PUNICODE_STRING NtPassword,
|
|||
|
OUT PCHAR *LmPasswordBuffer
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This service converts an NT password into a LM password.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
NtPassword - The Nt password to be converted.
|
|||
|
|
|||
|
LmPasswordBuffer - On successful return, points at the LM password
|
|||
|
The buffer should be freed using MIDL_user_free
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - LMPassword contains the LM version of the password.
|
|||
|
|
|||
|
STATUS_NULL_LM_PASSWORD - The password is too complex to be represented
|
|||
|
by a LM password. The LM password returned is a NULL string.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
#define LM_BUFFER_LENGTH (LM20_PWLEN + 1)
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
ANSI_STRING LmPassword;
|
|||
|
|
|||
|
//
|
|||
|
// Prepare for failure
|
|||
|
//
|
|||
|
|
|||
|
*LmPasswordBuffer = NULL;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Compute the Ansi version to the Unicode password.
|
|||
|
//
|
|||
|
// The Ansi version of the Cleartext password is at most 14 bytes long,
|
|||
|
// exists in a trailing zero filled 15 byte buffer,
|
|||
|
// is uppercased.
|
|||
|
//
|
|||
|
|
|||
|
LmPassword.Buffer = MIDL_user_allocate(LM_BUFFER_LENGTH);
|
|||
|
if (LmPassword.Buffer == NULL) {
|
|||
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|||
|
}
|
|||
|
|
|||
|
LmPassword.MaximumLength = LmPassword.Length = LM_BUFFER_LENGTH;
|
|||
|
RtlZeroMemory( LmPassword.Buffer, LM_BUFFER_LENGTH );
|
|||
|
|
|||
|
NtStatus = RtlUpcaseUnicodeStringToOemString( &LmPassword, NtPassword, FALSE );
|
|||
|
|
|||
|
|
|||
|
if ( !NT_SUCCESS(NtStatus) ) {
|
|||
|
|
|||
|
//
|
|||
|
// The password is longer than the max LM password length
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = STATUS_NULL_LM_PASSWORD; // Informational return code
|
|||
|
RtlZeroMemory( LmPassword.Buffer, LM_BUFFER_LENGTH );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Return a pointer to the allocated LM password
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
*LmPasswordBuffer = LmPassword.Buffer;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
MIDL_user_free(LmPassword.Buffer);
|
|||
|
}
|
|||
|
|
|||
|
return(NtStatus);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampCheckPasswordRestrictions(
|
|||
|
IN SAMPR_HANDLE UserHandle,
|
|||
|
IN PUNICODE_STRING NewNtPassword,
|
|||
|
OUT PBOOLEAN UseOwfPasswords
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This service is called to make sure that the password presented meets
|
|||
|
our quality requirements.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
UserHandle - Handle to a user.
|
|||
|
|
|||
|
NewNtPassword - Pointer to the UNICODE_STRING containing the new
|
|||
|
password.
|
|||
|
|
|||
|
UseOwfPasswords - Indicates that reversibly encrypted passwords should
|
|||
|
not be sent over the network.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - The password is acceptable.
|
|||
|
|
|||
|
STATUS_PASSWORD_RESTRICTION - The password is too short, or is not
|
|||
|
complex enough, etc.
|
|||
|
|
|||
|
STATUS_INVALID_RESOURCES - There was not enough memory to do the
|
|||
|
password checking.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
USER_DOMAIN_PASSWORD_INFORMATION DomainPasswordInformationBuffer;
|
|||
|
NTSTATUS NtStatus;
|
|||
|
PWORD CharInfoBuffer = NULL;
|
|||
|
ULONG i;
|
|||
|
|
|||
|
//
|
|||
|
// If the new password is zero length the server side will do
|
|||
|
// the necessary checking.
|
|||
|
//
|
|||
|
|
|||
|
if (NewNtPassword->Length == 0) {
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
|
|||
|
*UseOwfPasswords = FALSE;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Query information domain to get password length and
|
|||
|
// complexity requirements.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SamrGetUserDomainPasswordInformation(
|
|||
|
UserHandle,
|
|||
|
&DomainPasswordInformationBuffer
|
|||
|
);
|
|||
|
|
|||
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|||
|
|
|||
|
if ( (USHORT)( NewNtPassword->Length / sizeof(WCHAR) ) < DomainPasswordInformationBuffer.MinPasswordLength ) {
|
|||
|
|
|||
|
NtStatus = STATUS_PASSWORD_RESTRICTION;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Check whether policy allows us to send reversibly encrypted
|
|||
|
// passwords.
|
|||
|
//
|
|||
|
|
|||
|
if ( DomainPasswordInformationBuffer.PasswordProperties &
|
|||
|
DOMAIN_PASSWORD_NO_CLEAR_CHANGE ) {
|
|||
|
*UseOwfPasswords = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check password complexity.
|
|||
|
//
|
|||
|
|
|||
|
if ( DomainPasswordInformationBuffer.PasswordProperties & DOMAIN_PASSWORD_COMPLEX ) {
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that the password meets our requirements for
|
|||
|
// complexity. If it's got an odd byte count, it's
|
|||
|
// obviously not a hand-entered UNICODE string so we'll
|
|||
|
// consider it complex by default.
|
|||
|
//
|
|||
|
|
|||
|
if ( !( NewNtPassword->Length & 1 ) ) {
|
|||
|
|
|||
|
USHORT NumsInPassword = 0;
|
|||
|
USHORT UppersInPassword = 0;
|
|||
|
USHORT LowersInPassword = 0;
|
|||
|
USHORT OthersInPassword = 0;
|
|||
|
|
|||
|
CharInfoBuffer = MIDL_user_allocate( NewNtPassword->Length );
|
|||
|
|
|||
|
if ( CharInfoBuffer == NULL ) {
|
|||
|
|
|||
|
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
if ( GetStringTypeW(
|
|||
|
CT_CTYPE1,
|
|||
|
NewNtPassword->Buffer,
|
|||
|
NewNtPassword->Length / 2,
|
|||
|
CharInfoBuffer ) ) {
|
|||
|
|
|||
|
for ( i = 0; i < (ULONG)( NewNtPassword->Length / sizeof(WCHAR) ); i++ ) {
|
|||
|
|
|||
|
if ( CharInfoBuffer[i] & C1_DIGIT ) {
|
|||
|
|
|||
|
NumsInPassword = 1;
|
|||
|
}
|
|||
|
|
|||
|
if ( CharInfoBuffer[i] & C1_UPPER ) {
|
|||
|
|
|||
|
UppersInPassword = 1;
|
|||
|
}
|
|||
|
|
|||
|
if ( CharInfoBuffer[i] & C1_LOWER ) {
|
|||
|
|
|||
|
LowersInPassword = 1;
|
|||
|
}
|
|||
|
|
|||
|
if ( !( CharInfoBuffer[i] & ( C1_ALPHA | C1_DIGIT ) ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Having any "other" characters is
|
|||
|
// sufficient to make the password
|
|||
|
// complex.
|
|||
|
//
|
|||
|
|
|||
|
OthersInPassword = 2;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( ( NumsInPassword + UppersInPassword +
|
|||
|
LowersInPassword + OthersInPassword ) < 2 ) {
|
|||
|
|
|||
|
//
|
|||
|
// It didn't have at least two of the four
|
|||
|
// types of characters, so it's not complex
|
|||
|
// enough.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = STATUS_PASSWORD_RESTRICTION;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// GetStringTypeW failed; dunno why. Perhaps the
|
|||
|
// password is binary. Consider it complex by
|
|||
|
// default.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
MIDL_user_free( CharInfoBuffer );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return( NtStatus );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamiEncryptPasswords(
|
|||
|
IN PUNICODE_STRING OldPassword,
|
|||
|
IN PUNICODE_STRING NewPassword,
|
|||
|
OUT PSAMPR_ENCRYPTED_USER_PASSWORD NewEncryptedWithOldNt,
|
|||
|
OUT PENCRYPTED_NT_OWF_PASSWORD OldNtOwfEncryptedWithNewNt,
|
|||
|
OUT PBOOLEAN LmPresent,
|
|||
|
OUT PSAMPR_ENCRYPTED_USER_PASSWORD NewEncryptedWithOldLm,
|
|||
|
OUT PENCRYPTED_NT_OWF_PASSWORD OldLmOwfEncryptedWithNewNt
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine takes old and new cleartext passwords, converts them to
|
|||
|
LM passwords, generates OWF passwords, and produces reversibly
|
|||
|
encrypted cleartext and OWF passwords.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
OldPassword - The current cleartext password for the user.
|
|||
|
|
|||
|
NewPassword - The new cleartext password for the user.
|
|||
|
|
|||
|
NewEncryptedWithOldNt - The new password, in an SAMPR_USER_PASSWORD
|
|||
|
structure, reversibly encrypted with the old NT OWF password.
|
|||
|
|
|||
|
OldNtOwfEncryptedWithNewNt - The old NT OWF password reversibly
|
|||
|
encrypted with the new NT OWF password.
|
|||
|
|
|||
|
LmPresent - Indicates whether or not LM versions of the passwords could
|
|||
|
be calculated.
|
|||
|
|
|||
|
NewEncryptedWithOldLm - The new password, in an SAMPR_USER_PASSWORD
|
|||
|
structure, reversibly encrypted with the old LM OWF password.
|
|||
|
|
|||
|
OldLmOwfEncryptedWithNewNt - The old LM OWF password reversibly
|
|||
|
encrypted with the new NT OWF password.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Errors from RtlEncryptXXX functions
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PCHAR OldLmPassword = NULL;
|
|||
|
PCHAR NewLmPassword = NULL;
|
|||
|
LM_OWF_PASSWORD OldLmOwfPassword;
|
|||
|
NT_OWF_PASSWORD OldNtOwfPassword;
|
|||
|
NT_OWF_PASSWORD NewNtOwfPassword;
|
|||
|
PSAMPR_USER_PASSWORD NewNt = (PSAMPR_USER_PASSWORD) NewEncryptedWithOldNt;
|
|||
|
PSAMPR_USER_PASSWORD NewLm = (PSAMPR_USER_PASSWORD) NewEncryptedWithOldLm;
|
|||
|
struct RC4_KEYSTRUCT Rc4Key;
|
|||
|
NTSTATUS NtStatus;
|
|||
|
BOOLEAN OldLmPresent = TRUE;
|
|||
|
BOOLEAN NewLmPresent = TRUE;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Initialization
|
|||
|
//
|
|||
|
|
|||
|
*LmPresent = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Make sure the password isn't too long.
|
|||
|
//
|
|||
|
|
|||
|
if (NewPassword->Length > SAM_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) {
|
|||
|
return(STATUS_PASSWORD_RESTRICTION);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Calculate the LM passwords. This may fail because the passwords are
|
|||
|
// too complex, but we can deal with that, so just remember what failed.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampCalculateLmPassword(
|
|||
|
OldPassword,
|
|||
|
&OldLmPassword
|
|||
|
);
|
|||
|
|
|||
|
if (NtStatus != STATUS_SUCCESS) {
|
|||
|
OldLmPresent = FALSE;
|
|||
|
*LmPresent = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// If the error was that it couldn't calculate the password, that
|
|||
|
// is o.k.
|
|||
|
//
|
|||
|
|
|||
|
if (NtStatus == STATUS_NULL_LM_PASSWORD) {
|
|||
|
NtStatus = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Calculate the LM OWF passwords
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus) && OldLmPresent) {
|
|||
|
NtStatus = RtlCalculateLmOwfPassword(
|
|||
|
OldLmPassword,
|
|||
|
&OldLmOwfPassword
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Calculate the NT OWF passwords
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
NtStatus = RtlCalculateNtOwfPassword(
|
|||
|
OldPassword,
|
|||
|
&OldNtOwfPassword
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
NtStatus = RtlCalculateNtOwfPassword(
|
|||
|
NewPassword,
|
|||
|
&NewNtOwfPassword
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Calculate the encrypted old passwords
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
NtStatus = RtlEncryptNtOwfPwdWithNtOwfPwd(
|
|||
|
&OldNtOwfPassword,
|
|||
|
&NewNtOwfPassword,
|
|||
|
OldNtOwfEncryptedWithNewNt
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Compute the encrypted old LM password. Always use the new NT OWF
|
|||
|
// to encrypt it, since we may not have a new LM OWF password.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus) && OldLmPresent) {
|
|||
|
ASSERT(LM_OWF_PASSWORD_LENGTH == NT_OWF_PASSWORD_LENGTH);
|
|||
|
|
|||
|
NtStatus = RtlEncryptLmOwfPwdWithLmOwfPwd(
|
|||
|
&OldLmOwfPassword,
|
|||
|
(PLM_OWF_PASSWORD) &NewNtOwfPassword,
|
|||
|
OldLmOwfEncryptedWithNewNt
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Calculate the encrypted new passwords
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
ASSERT(sizeof(SAMPR_ENCRYPTED_USER_PASSWORD) == sizeof(SAMPR_USER_PASSWORD));
|
|||
|
|
|||
|
//
|
|||
|
// Compute the encrypted new password with NT key.
|
|||
|
//
|
|||
|
|
|||
|
rc4_key(
|
|||
|
&Rc4Key,
|
|||
|
NT_OWF_PASSWORD_LENGTH,
|
|||
|
(PUCHAR) &OldNtOwfPassword
|
|||
|
);
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
((PUCHAR) NewNt->Buffer) +
|
|||
|
SAM_MAX_PASSWORD_LENGTH * sizeof(WCHAR) -
|
|||
|
NewPassword->Length,
|
|||
|
NewPassword->Buffer,
|
|||
|
NewPassword->Length
|
|||
|
);
|
|||
|
|
|||
|
*(ULONG UNALIGNED *) &NewNt->Length = NewPassword->Length;
|
|||
|
|
|||
|
//
|
|||
|
// Fill the rest of the buffer with random numbers
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampRandomFill(
|
|||
|
(SAM_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) -
|
|||
|
NewPassword->Length,
|
|||
|
(PUCHAR) NewNt->Buffer
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus))
|
|||
|
{
|
|||
|
rc4(&Rc4Key,
|
|||
|
sizeof(SAMPR_USER_PASSWORD),
|
|||
|
(PUCHAR) NewEncryptedWithOldNt
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Compute the encrypted new password with LM key if it exists.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus) && OldLmPresent) {
|
|||
|
|
|||
|
rc4_key(
|
|||
|
&Rc4Key,
|
|||
|
LM_OWF_PASSWORD_LENGTH,
|
|||
|
(PUCHAR) &OldLmOwfPassword
|
|||
|
);
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
((PUCHAR) NewLm->Buffer) +
|
|||
|
(SAM_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) -
|
|||
|
NewPassword->Length,
|
|||
|
NewPassword->Buffer,
|
|||
|
NewPassword->Length
|
|||
|
);
|
|||
|
|
|||
|
*(ULONG UNALIGNED *) &NewLm->Length = NewPassword->Length;
|
|||
|
|
|||
|
NtStatus = SampRandomFill(
|
|||
|
(SAM_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) -
|
|||
|
NewPassword->Length,
|
|||
|
(PUCHAR) NewLm->Buffer
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Encrypt the password (or, if the old LM OWF password does not exist,
|
|||
|
// zero it).
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus) && OldLmPresent) {
|
|||
|
|
|||
|
rc4(&Rc4Key,
|
|||
|
sizeof(SAMPR_USER_PASSWORD),
|
|||
|
(PUCHAR) NewEncryptedWithOldLm
|
|||
|
);
|
|||
|
|
|||
|
} else {
|
|||
|
RtlZeroMemory(
|
|||
|
NewLm,
|
|||
|
sizeof(SAMPR_ENCRYPTED_USER_PASSWORD)
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Make sure to zero the passwords before freeing so we don't have
|
|||
|
// passwords floating around in the page file.
|
|||
|
//
|
|||
|
|
|||
|
if (OldLmPassword != NULL) {
|
|||
|
|
|||
|
RtlZeroMemory(
|
|||
|
OldLmPassword,
|
|||
|
lstrlenA(OldLmPassword)
|
|||
|
);
|
|||
|
|
|||
|
MIDL_user_free(OldLmPassword);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
return(NtStatus);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampChangePasswordUser2(
|
|||
|
IN PUNICODE_STRING ServerName,
|
|||
|
IN PUNICODE_STRING UserName,
|
|||
|
IN PUNICODE_STRING OldPassword,
|
|||
|
IN PUNICODE_STRING NewPassword
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Password will be set to NewPassword only if OldPassword matches the
|
|||
|
current user password for this user and the NewPassword is not the
|
|||
|
same as the domain password parameter PasswordHistoryLength
|
|||
|
passwords. This call allows users to change their own password if
|
|||
|
they have access USER_CHANGE_PASSWORD. Password update restrictions
|
|||
|
apply.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
UserHandle - The handle of an opened user to operate on.
|
|||
|
|
|||
|
OldPassword - Current password for the user.
|
|||
|
|
|||
|
NewPassword - Desired new password for the user.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_ILL_FORMED_PASSWORD - The new password is poorly formed,
|
|||
|
e.g. contains characters that can't be entered from the
|
|||
|
keyboard, etc.
|
|||
|
|
|||
|
STATUS_PASSWORD_RESTRICTION - A restriction prevents the password
|
|||
|
from being changed. This may be for a number of reasons,
|
|||
|
including time restrictions on how often a password may be
|
|||
|
changed or length restrictions on the provided 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. These are kept
|
|||
|
in the password recent history log.
|
|||
|
|
|||
|
STATUS_WRONG_PASSWORD - OldPassword does not contain the user's
|
|||
|
current password.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be enabled for this
|
|||
|
operation
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
SAM_HANDLE SamServerHandle = NULL;
|
|||
|
SAM_HANDLE DomainHandle = NULL;
|
|||
|
SAM_HANDLE UserHandle = NULL;
|
|||
|
LSA_HANDLE PolicyHandle = NULL;
|
|||
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|||
|
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
|
|||
|
PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomainInfo = NULL;
|
|||
|
PULONG UserId = NULL;
|
|||
|
PSID_NAME_USE NameUse = NULL;
|
|||
|
|
|||
|
InitializeObjectAttributes(
|
|||
|
&ObjectAttributes,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// The InitializeObjectAttributes call doesn't initialize the
|
|||
|
// quality of serivce, so do that separately.
|
|||
|
//
|
|||
|
|
|||
|
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
|
|||
|
SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
|
|||
|
SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
|
|||
|
SecurityQualityOfService.EffectiveOnly = FALSE;
|
|||
|
|
|||
|
ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NtStatus = LsaOpenPolicy(
|
|||
|
ServerName,
|
|||
|
&ObjectAttributes,
|
|||
|
POLICY_VIEW_LOCAL_INFORMATION,
|
|||
|
&PolicyHandle
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
NtStatus = LsaQueryInformationPolicy(
|
|||
|
PolicyHandle,
|
|||
|
PolicyAccountDomainInformation,
|
|||
|
&AccountDomainInfo
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
NtStatus = SamConnect(
|
|||
|
ServerName,
|
|||
|
&SamServerHandle,
|
|||
|
SAM_SERVER_LOOKUP_DOMAIN,
|
|||
|
&ObjectAttributes
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
NtStatus = SamOpenDomain(
|
|||
|
SamServerHandle,
|
|||
|
GENERIC_EXECUTE,
|
|||
|
AccountDomainInfo->DomainSid,
|
|||
|
&DomainHandle
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
NtStatus = SamLookupNamesInDomain(
|
|||
|
DomainHandle,
|
|||
|
1,
|
|||
|
UserName,
|
|||
|
&UserId,
|
|||
|
&NameUse
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
if (NtStatus == STATUS_NONE_MAPPED) {
|
|||
|
NtStatus = STATUS_NO_SUCH_USER;
|
|||
|
}
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
NtStatus = SamOpenUser(
|
|||
|
DomainHandle,
|
|||
|
USER_CHANGE_PASSWORD,
|
|||
|
*UserId,
|
|||
|
&UserHandle
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
NtStatus = SamChangePasswordUser(
|
|||
|
UserHandle,
|
|||
|
OldPassword,
|
|||
|
NewPassword
|
|||
|
);
|
|||
|
Cleanup:
|
|||
|
if (UserHandle != NULL) {
|
|||
|
SamCloseHandle(UserHandle);
|
|||
|
}
|
|||
|
if (DomainHandle != NULL) {
|
|||
|
SamCloseHandle(DomainHandle);
|
|||
|
}
|
|||
|
if (SamServerHandle != NULL) {
|
|||
|
SamCloseHandle(SamServerHandle);
|
|||
|
}
|
|||
|
if (PolicyHandle != NULL){
|
|||
|
LsaClose(PolicyHandle);
|
|||
|
}
|
|||
|
if (AccountDomainInfo != NULL) {
|
|||
|
LsaFreeMemory(AccountDomainInfo);
|
|||
|
}
|
|||
|
if (UserId != NULL) {
|
|||
|
SamFreeMemory(UserId);
|
|||
|
}
|
|||
|
if (NameUse != NULL) {
|
|||
|
SamFreeMemory(NameUse);
|
|||
|
}
|
|||
|
|
|||
|
return(NtStatus);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamiChangePasswordUser2(
|
|||
|
PUNICODE_STRING ServerName,
|
|||
|
PUNICODE_STRING UserName,
|
|||
|
PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldNt,
|
|||
|
PENCRYPTED_NT_OWF_PASSWORD OldNtOwfPasswordEncryptedWithNewNt,
|
|||
|
BOOLEAN LmPresent,
|
|||
|
PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldLm,
|
|||
|
PENCRYPTED_LM_OWF_PASSWORD OldLmOwfPasswordEncryptedWithNewLmOrNt
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Changes the password of a user account. This is the worker routine for
|
|||
|
SamChangePasswordUser2 and can be called by OWF-aware clients.
|
|||
|
Password will be set to NewPassword only if OldPassword matches the
|
|||
|
current user password for this user and the NewPassword is not the
|
|||
|
same as the domain password parameter PasswordHistoryLength
|
|||
|
passwords. This call allows users to change their own password if
|
|||
|
they have access USER_CHANGE_PASSWORD. Password update restrictions
|
|||
|
apply.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
ServerName - The server to operate on, or NULL for this machine.
|
|||
|
|
|||
|
UserName - Name of user whose password is to be changed
|
|||
|
|
|||
|
NewPasswordEncryptedWithOldNt - The new cleartext password encrypted
|
|||
|
with the old NT OWF password.
|
|||
|
|
|||
|
OldNtOwfPasswordEncryptedWithNewNt - The old NT OWF password encrypted
|
|||
|
with the new NT OWF password.
|
|||
|
|
|||
|
LmPresent - If TRUE, indicates that the following two last parameter
|
|||
|
was encrypted with the LM OWF password not the NT OWF password.
|
|||
|
|
|||
|
NewPasswordEncryptedWithOldLm - The new cleartext password encrypted
|
|||
|
with the old LM OWF password.
|
|||
|
|
|||
|
OldLmOwfPasswordEncryptedWithNewLmOrNt - The old LM OWF password encrypted
|
|||
|
with the new LM OWF password.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_ILL_FORMED_PASSWORD - The new password is poorly formed,
|
|||
|
e.g. contains characters that can't be entered from the
|
|||
|
keyboard, etc.
|
|||
|
|
|||
|
STATUS_PASSWORD_RESTRICTION - A restriction prevents the password
|
|||
|
from being changed. This may be for a number of reasons,
|
|||
|
including time restrictions on how often a password may be
|
|||
|
changed or length restrictions on the provided 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. These are kept
|
|||
|
in the password recent history log.
|
|||
|
|
|||
|
STATUS_WRONG_PASSWORD - OldPassword does not contain the user's
|
|||
|
current password.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be enabled for this
|
|||
|
operation
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
handle_t BindingHandle;
|
|||
|
PSAMPR_SERVER_NAME RServerNameWithNull;
|
|||
|
USHORT RServerNameWithNullLength;
|
|||
|
PSAMPR_SERVER_NAME RServerName;
|
|||
|
ULONG Tries = 2;
|
|||
|
NTSTATUS NtStatus;
|
|||
|
USER_DOMAIN_PASSWORD_INFORMATION PasswordInformation;
|
|||
|
|
|||
|
RServerNameWithNull = NULL;
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(ServerName)) {
|
|||
|
|
|||
|
RServerName = (PSAMPR_SERVER_NAME)(ServerName->Buffer);
|
|||
|
RServerNameWithNullLength = ServerName->Length + (USHORT) sizeof(WCHAR);
|
|||
|
RServerNameWithNull = MIDL_user_allocate( RServerNameWithNullLength );
|
|||
|
|
|||
|
if (RServerNameWithNull == NULL) {
|
|||
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|||
|
}
|
|||
|
|
|||
|
RtlCopyMemory( RServerNameWithNull, RServerName, ServerName->Length);
|
|||
|
RServerNameWithNull[ServerName->Length/sizeof(WCHAR)] = L'\0';
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
do
|
|||
|
{
|
|||
|
//
|
|||
|
// Try privacy level first, and if that failed with unknown authn
|
|||
|
// level or invalid binding try with a lower level (none).
|
|||
|
//
|
|||
|
|
|||
|
if (Tries == 2) {
|
|||
|
BindingHandle = SampSecureBind(
|
|||
|
RServerNameWithNull,
|
|||
|
RPC_C_AUTHN_LEVEL_PKT_PRIVACY
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
} else if ((NtStatus == RPC_NT_UNKNOWN_AUTHN_LEVEL) ||
|
|||
|
(NtStatus == RPC_NT_UNKNOWN_AUTHN_TYPE) ||
|
|||
|
(NtStatus == RPC_NT_UNKNOWN_AUTHN_SERVICE) ||
|
|||
|
(NtStatus == RPC_NT_INVALID_BINDING) ||
|
|||
|
(NtStatus == STATUS_ACCESS_DENIED) ) {
|
|||
|
SampSecureUnbind(BindingHandle);
|
|||
|
|
|||
|
BindingHandle = SampSecureBind(
|
|||
|
RServerNameWithNull,
|
|||
|
RPC_C_AUTHN_LEVEL_NONE
|
|||
|
);
|
|||
|
|
|||
|
} else {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (BindingHandle != NULL) {
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
//
|
|||
|
// Get password information to make sure this operation
|
|||
|
// is allowed. We do it now because we wanted to bind
|
|||
|
// before trying it.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SamrGetDomainPasswordInformation(
|
|||
|
BindingHandle,
|
|||
|
(PRPC_UNICODE_STRING) ServerName,
|
|||
|
&PasswordInformation
|
|||
|
);
|
|||
|
|
|||
|
if (NtStatus == STATUS_SUCCESS) {
|
|||
|
|
|||
|
if (!( PasswordInformation.PasswordProperties &
|
|||
|
DOMAIN_PASSWORD_NO_CLEAR_CHANGE) ) {
|
|||
|
|
|||
|
NtStatus = SamrUnicodeChangePasswordUser2(
|
|||
|
BindingHandle,
|
|||
|
(PRPC_UNICODE_STRING) ServerName,
|
|||
|
(PRPC_UNICODE_STRING) UserName,
|
|||
|
NewPasswordEncryptedWithOldNt,
|
|||
|
OldNtOwfPasswordEncryptedWithNewNt,
|
|||
|
LmPresent,
|
|||
|
NewPasswordEncryptedWithOldLm,
|
|||
|
OldLmOwfPasswordEncryptedWithNewLmOrNt
|
|||
|
);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Set the error to indicate that we should try the
|
|||
|
// downlevel way to change passwords.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = STATUS_NOT_SUPPORTED;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// The mapping function doesn't handle this error so
|
|||
|
// special case it by hand.
|
|||
|
//
|
|||
|
NtStatus = RpcExceptionCode();
|
|||
|
|
|||
|
if (NtStatus == RPC_S_SEC_PKG_ERROR) {
|
|||
|
NtStatus = STATUS_ACCESS_DENIED;
|
|||
|
} else {
|
|||
|
NtStatus = I_RpcMapWin32Status(NtStatus);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
} else {
|
|||
|
NtStatus = RPC_NT_INVALID_BINDING;
|
|||
|
}
|
|||
|
|
|||
|
Tries--;
|
|||
|
} while ( (Tries > 0) && (!NT_SUCCESS(NtStatus)) );
|
|||
|
if (RServerNameWithNull != NULL) {
|
|||
|
MIDL_user_free( RServerNameWithNull );
|
|||
|
}
|
|||
|
|
|||
|
if (BindingHandle != NULL) {
|
|||
|
SampSecureUnbind(BindingHandle);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Map these errors to STATUS_NOT_SUPPORTED
|
|||
|
//
|
|||
|
|
|||
|
if ((NtStatus == RPC_NT_UNKNOWN_IF) ||
|
|||
|
(NtStatus == RPC_NT_PROCNUM_OUT_OF_RANGE)) {
|
|||
|
|
|||
|
NtStatus = STATUS_NOT_SUPPORTED;
|
|||
|
}
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamiOemChangePasswordUser2(
|
|||
|
PSTRING ServerName,
|
|||
|
PSTRING UserName,
|
|||
|
PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldLm,
|
|||
|
PENCRYPTED_LM_OWF_PASSWORD OldLmOwfPasswordEncryptedWithNewLm
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Changes the password of a user account. This can be called by OWF-aware
|
|||
|
clients. Password will be set to NewPassword only if OldPassword matches
|
|||
|
the current user password for this user and the NewPassword is not the
|
|||
|
same as the domain password parameter PasswordHistoryLength
|
|||
|
passwords. This call allows users to change their own password if
|
|||
|
they have access USER_CHANGE_PASSWORD. Password update restrictions
|
|||
|
apply.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
ServerName - The server to operate on, or NULL for this machine.
|
|||
|
|
|||
|
UserName - Name of user whose password is to be changed
|
|||
|
|
|||
|
|
|||
|
NewPasswordEncryptedWithOldLm - The new cleartext password encrypted
|
|||
|
with the old LM OWF password.
|
|||
|
|
|||
|
OldLmOwfPasswordEncryptedWithNewLm - The old LM OWF password encrypted
|
|||
|
with the new LM OWF password.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_ILL_FORMED_PASSWORD - The new password is poorly formed,
|
|||
|
e.g. contains characters that can't be entered from the
|
|||
|
keyboard, etc.
|
|||
|
|
|||
|
STATUS_PASSWORD_RESTRICTION - A restriction prevents the password
|
|||
|
from being changed. This may be for a number of reasons,
|
|||
|
including time restrictions on how often a password may be
|
|||
|
changed or length restrictions on the provided 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. These are kept
|
|||
|
in the password recent history log.
|
|||
|
|
|||
|
STATUS_WRONG_PASSWORD - OldPassword does not contain the user's
|
|||
|
current password.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be enabled for this
|
|||
|
operation
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
handle_t BindingHandle;
|
|||
|
UNICODE_STRING RemoteServerName;
|
|||
|
ULONG Tries = 2;
|
|||
|
NTSTATUS NtStatus;
|
|||
|
USER_DOMAIN_PASSWORD_INFORMATION PasswordInformation;
|
|||
|
|
|||
|
RemoteServerName.Buffer = NULL;
|
|||
|
RemoteServerName.Length = 0;
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(ServerName)) {
|
|||
|
|
|||
|
NtStatus = RtlAnsiStringToUnicodeString(
|
|||
|
&RemoteServerName,
|
|||
|
ServerName,
|
|||
|
TRUE // allocate destination
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
return(NtStatus);
|
|||
|
}
|
|||
|
ASSERT(RemoteServerName.Buffer[RemoteServerName.Length/sizeof(WCHAR)] == L'\0');
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
do
|
|||
|
{
|
|||
|
//
|
|||
|
// Try privacy level first, and if that failed with unknown authn
|
|||
|
// level or invalid binding try with a lower level (none).
|
|||
|
//
|
|||
|
|
|||
|
if (Tries == 2) {
|
|||
|
BindingHandle = SampSecureBind(
|
|||
|
RemoteServerName.Buffer,
|
|||
|
RPC_C_AUTHN_LEVEL_PKT_PRIVACY
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
} else if ((NtStatus == RPC_NT_UNKNOWN_AUTHN_LEVEL) ||
|
|||
|
(NtStatus == RPC_NT_UNKNOWN_AUTHN_TYPE) ||
|
|||
|
(NtStatus == RPC_NT_INVALID_BINDING) ||
|
|||
|
(NtStatus == STATUS_ACCESS_DENIED) ) {
|
|||
|
SampSecureUnbind(BindingHandle);
|
|||
|
|
|||
|
BindingHandle = SampSecureBind(
|
|||
|
RemoteServerName.Buffer,
|
|||
|
RPC_C_AUTHN_LEVEL_NONE
|
|||
|
);
|
|||
|
|
|||
|
} else {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (BindingHandle != NULL) {
|
|||
|
|
|||
|
RpcTryExcept{
|
|||
|
|
|||
|
//
|
|||
|
// Get password information to make sure this operation
|
|||
|
// is allowed. We do it now because we wanted to bind
|
|||
|
// before trying it.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SamrGetDomainPasswordInformation(
|
|||
|
BindingHandle,
|
|||
|
(PRPC_UNICODE_STRING) ServerName,
|
|||
|
&PasswordInformation
|
|||
|
);
|
|||
|
|
|||
|
if (NtStatus == STATUS_SUCCESS) {
|
|||
|
|
|||
|
if (!( PasswordInformation.PasswordProperties &
|
|||
|
DOMAIN_PASSWORD_NO_CLEAR_CHANGE) ) {
|
|||
|
|
|||
|
NtStatus = SamrOemChangePasswordUser2(
|
|||
|
BindingHandle,
|
|||
|
(PRPC_STRING) ServerName,
|
|||
|
(PRPC_STRING) UserName,
|
|||
|
NewPasswordEncryptedWithOldLm,
|
|||
|
OldLmOwfPasswordEncryptedWithNewLm
|
|||
|
);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Set the error to indicate that we should try the
|
|||
|
// downlevel way to change passwords.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = STATUS_NOT_SUPPORTED;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// The mappin function doesn't handle this error so
|
|||
|
// special case it by hand.
|
|||
|
//
|
|||
|
|
|||
|
if (NtStatus == RPC_S_SEC_PKG_ERROR) {
|
|||
|
NtStatus = STATUS_ACCESS_DENIED;
|
|||
|
} else {
|
|||
|
NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
} RpcEndExcept;
|
|||
|
|
|||
|
} else {
|
|||
|
NtStatus = RPC_NT_INVALID_BINDING;
|
|||
|
}
|
|||
|
|
|||
|
Tries--;
|
|||
|
} while ( (Tries > 0) && (!NT_SUCCESS(NtStatus)) );
|
|||
|
|
|||
|
RtlFreeUnicodeString( &RemoteServerName );
|
|||
|
|
|||
|
if (BindingHandle != NULL) {
|
|||
|
SampSecureUnbind(BindingHandle);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Map these errors to STATUS_NOT_SUPPORTED
|
|||
|
//
|
|||
|
|
|||
|
if ((NtStatus == RPC_NT_UNKNOWN_IF) ||
|
|||
|
(NtStatus == RPC_NT_PROCNUM_OUT_OF_RANGE)) {
|
|||
|
|
|||
|
NtStatus = STATUS_NOT_SUPPORTED;
|
|||
|
}
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamChangePasswordUser2(
|
|||
|
IN PUNICODE_STRING ServerName,
|
|||
|
IN PUNICODE_STRING UserName,
|
|||
|
IN PUNICODE_STRING OldPassword,
|
|||
|
IN PUNICODE_STRING NewPassword
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Password will be set to NewPassword only if OldPassword matches the
|
|||
|
current user password for this user and the NewPassword is not the
|
|||
|
same as the domain password parameter PasswordHistoryLength
|
|||
|
passwords. This call allows users to change their own password if
|
|||
|
they have access USER_CHANGE_PASSWORD. Password update restrictions
|
|||
|
apply.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
ServerName - The server to operate on, or NULL for this machine.
|
|||
|
|
|||
|
UserName - Name of user whose password is to be changed
|
|||
|
|
|||
|
OldPassword - Current password for the user.
|
|||
|
|
|||
|
NewPassword - Desired new password for the user.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
|
|||
|
STATUS_ILL_FORMED_PASSWORD - The new password is poorly formed,
|
|||
|
e.g. contains characters that can't be entered from the
|
|||
|
keyboard, etc.
|
|||
|
|
|||
|
STATUS_PASSWORD_RESTRICTION - A restriction prevents the password
|
|||
|
from being changed. This may be for a number of reasons,
|
|||
|
including time restrictions on how often a password may be
|
|||
|
changed or length restrictions on the provided 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. These are kept
|
|||
|
in the password recent history log.
|
|||
|
|
|||
|
STATUS_WRONG_PASSWORD - OldPassword does not contain the user's
|
|||
|
current password.
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
|
|||
|
correct state (disabled or enabled) to perform the requested
|
|||
|
operation. The domain server must be enabled for this
|
|||
|
operation
|
|||
|
|
|||
|
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
|
|||
|
incorrect role (primary or backup) to perform the requested
|
|||
|
operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
SAMPR_ENCRYPTED_USER_PASSWORD NewNtEncryptedWithOldNt;
|
|||
|
SAMPR_ENCRYPTED_USER_PASSWORD NewNtEncryptedWithOldLm;
|
|||
|
ENCRYPTED_NT_OWF_PASSWORD OldNtOwfEncryptedWithNewNt;
|
|||
|
ENCRYPTED_NT_OWF_PASSWORD OldLmOwfEncryptedWithNewNt;
|
|||
|
NTSTATUS NtStatus;
|
|||
|
BOOLEAN LmPresent = TRUE;
|
|||
|
ULONG AuthnLevel;
|
|||
|
ULONG Tries = 2;
|
|||
|
USER_DOMAIN_PASSWORD_INFORMATION PasswordInformation;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Call the server, passing either a NULL Server Name pointer, or
|
|||
|
// a pointer to a Unicode Buffer with a Wide Character NULL terminator.
|
|||
|
// Since the input name is contained in a counted Unicode String, there
|
|||
|
// is no NULL terminator necessarily provided, so we must append one.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Encrypted the passwords
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SamiEncryptPasswords(
|
|||
|
OldPassword,
|
|||
|
NewPassword,
|
|||
|
&NewNtEncryptedWithOldNt,
|
|||
|
&OldNtOwfEncryptedWithNewNt,
|
|||
|
&LmPresent,
|
|||
|
&NewNtEncryptedWithOldLm,
|
|||
|
&OldLmOwfEncryptedWithNewNt
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
return(NtStatus);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Try the remote call...
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
NtStatus = SamiChangePasswordUser2(
|
|||
|
ServerName,
|
|||
|
UserName,
|
|||
|
&NewNtEncryptedWithOldNt,
|
|||
|
&OldNtOwfEncryptedWithNewNt,
|
|||
|
LmPresent,
|
|||
|
&NewNtEncryptedWithOldLm,
|
|||
|
&OldLmOwfEncryptedWithNewNt
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If the new API failed, try calling the old API.
|
|||
|
//
|
|||
|
|
|||
|
if (NtStatus == STATUS_NOT_SUPPORTED) {
|
|||
|
|
|||
|
NtStatus = SampChangePasswordUser2(
|
|||
|
ServerName,
|
|||
|
UserName,
|
|||
|
OldPassword,
|
|||
|
NewPassword
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
return(SampMapCompletionStatus(NtStatus));
|
|||
|
|
|||
|
}
|