NT4/private/newsam/client/wrappers.c
2020-09-30 17:12:29 +02:00

8850 lines
241 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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)
///////////////////////////////////////////////////////////////////////////////
// //
// 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;
//
// 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;
//
// 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;
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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
//
// 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;
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;
//
// 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;
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;
//
// 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));
}