Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2338 lines
63 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
rpcapi.c
Abstract:
This module contains the routines for the dssetup APIs that use RPC. The
routines in this module are merely wrappers that work as follows:
Fuke copied lsa\uclient and hacked on to make it work for DsRole apis
Author:
Mac McLain (MacM) April 14, 1997
Revision History:
--*/
#include <lsacomp.h>
#include "dssetup_c.h"
#include <rpcndr.h>
#include <dssetp.h>
#include <winreg.h>
#include <rpcasync.h>
#include <wxlpc.h>
#include <crypt.h>
#include <rc4.h>
#include <md5.h>
#include <winbase.h> //for RtlSecureZeroMemory
//
// Local prototypes
//
DWORD
DsRolepGetPrimaryDomainInformationDownlevel(
IN LPWSTR Server,
OUT PBYTE *Buffer
);
DWORD
DsRolepGetProductTypeForServer(
IN LPWSTR Server,
IN OUT PNT_PRODUCT_TYPE ProductType
);
DWORD
DsRolepEncryptPasswordStart(
IN LPCWSTR ServerName OPTIONAL,
IN LPCWSTR *Passwords,
IN ULONG Count,
OUT RPC_BINDING_HANDLE *RpcBindingHandle,
OUT HANDLE *RedirHandle,
OUT OPTIONAL PUSER_SESSION_KEY UserSessionKey,
IN OUT PDSROLEPR_ENCRYPTED_USER_PASSWORD *EncryptedUserPassword
);
VOID
DsRolepEncryptPasswordEnd(
IN RPC_BINDING_HANDLE RpcBindingHandle,
IN HANDLE RedirHandle OPTIONAL,
IN PDSROLEPR_ENCRYPTED_USER_PASSWORD *EncryptedUserPassword OPTIONAL,
IN ULONG Count
);
DWORD
DsRolepHashkey(
IN OUT LPWSTR key,
OUT PUNICODE_STRING Hash
);
DWORD
DsRolepEncryptHash(
IN PUSER_SESSION_KEY UserSessionKey,
IN OUT PUNICODE_STRING Syskey,
OUT PDSROLEPR_ENCRYPTED_HASH EncryptedSyskey
);
////////////////////////////////////////////////////////////////////////////
// //
// DS Setup and initialization routines //
// //
////////////////////////////////////////////////////////////////////////////
BOOLEAN
DsRolepIsSetupRunning(
VOID
)
/*++
Routine Description:
This routine determines if this call is being made during setup or not
Arguments:
VOID
Return Value:
TRUE -- The call is being made during setup
FALSE -- The call is not being made during setup
--*/
{
NTSTATUS Status;
HANDLE InstallationEvent;
OBJECT_ATTRIBUTES EventAttributes;
UNICODE_STRING EventName;
BOOLEAN Setup = FALSE;
//
// If the following event exists, then we are in setup mode
//
RtlInitUnicodeString( &EventName,
L"\\INSTALLATION_SECURITY_HOLD" );
InitializeObjectAttributes( &EventAttributes,
&EventName, 0, 0, NULL );
Status = NtOpenEvent( &InstallationEvent,
SYNCHRONIZE,
&EventAttributes );
if ( NT_SUCCESS( Status) ) {
NtClose( InstallationEvent );
Setup = TRUE;
}
return( Setup );
}
DWORD
DsRolepGetPrimaryDomainInfoServerBind(
IN OPTIONAL PDSROLE_SERVER_NAME ServerName,
OUT handle_t *BindHandle
)
/*++
Routine Description:
This routine is called from the LSA client stubs when
it is necessary to bind to the LSA on some server.
Arguments:
ServerName - A pointer to a string containing the name of the server
to bind with.
Return Value:
The binding handle is returned to the stub routine. If the
binding is unsuccessful, a NULL will be returned.
--*/
{
handle_t BindingHandle = NULL;
NTSTATUS Status;
//
// Can't go remote when running in setup mode
//
if ( DsRolepIsSetupRunning() && ServerName != NULL ) {
return( STATUS_INVALID_SERVER_STATE );
}
Status = RpcpBindRpc ( ServerName,
L"lsarpc",
0,
&BindingHandle );
if (!NT_SUCCESS(Status)) {
DbgPrint("DsRolepGetPrimaryDomainInfoServerBind: RpcpBindRpc failed 0x%lx\n", Status);
} else {
*BindHandle = BindingHandle;
}
return RtlNtStatusToDosError( Status );
}
DWORD
DsRolepServerBind(
IN OPTIONAL PDSROLE_SERVER_NAME ServerName,
OUT handle_t *BindHandle
)
/*++
Routine Description:
This routine is called from the LSA client stubs when
it is necessary to bind to the LSA on some server.
Arguments:
ServerName - A pointer to a string containing the name of the server
to bind with.
Return Value:
The binding handle is returned to the stub routine. If the
binding is unsuccessful, a NULL will be returned.
--*/
{
handle_t BindingHandle = NULL;
DWORD Win32Err = ERROR_SUCCESS;
WCHAR *pwszStringBinding = NULL;
BOOL fAuth = TRUE;
//
// Can't go remote when running in setup mode
//
if ( DsRolepIsSetupRunning() && ServerName != NULL ) {
return( STATUS_INVALID_SERVER_STATE );
}
RpcTryExcept
{
Win32Err = RpcStringBindingComposeW(NULL,
L"ncalrpc",
NULL,
L"dsrole",
NULL,
&pwszStringBinding);
if (RPC_S_OK != Win32Err) {
__leave;
}
Win32Err = RpcBindingFromStringBindingW(pwszStringBinding,
&BindingHandle);
if (RPC_S_OK != Win32Err) {
__leave;
}
Win32Err = RpcEpResolveBinding(BindingHandle,
dsrole_ClientIfHandle);
if ( RPC_S_OK != Win32Err ) {
__leave;
}
// Set authentication info using our process's credentials.
Win32Err = RpcBindingSetAuthInfo(BindingHandle,
NULL,
/*Uses the default authentication
level for the specified
authentication service.*/
RPC_C_AUTHN_LEVEL_DEFAULT,
/*RPC_C_AUTHN_DEFAULT is specified,
the RPC run-time library uses
the RPC_C_AUTHN_WINNT authentication
service for remote procedure calls
made using hBinding */
RPC_C_AUTHN_DEFAULT,
NULL,
//Default authentication service.
RPC_C_AUTHN_DEFAULT);
if (RPC_S_OK != Win32Err) {
__leave;
}
}
RpcExcept( I_RpcExceptionFilter( RpcExceptionCode() ) )
{
Win32Err = RpcExceptionCode();
}
RpcEndExcept;
if ( RPC_S_OK == Win32Err ) {
*BindHandle = BindingHandle;
} else {
if (BindingHandle) {
RpcpUnbindRpc ( BindingHandle );
}
}
if ( pwszStringBinding )
{
RpcStringFree(&pwszStringBinding);
}
return( Win32Err );
}
VOID
DsRolepServerUnbind (
IN OPTIONAL PDSROLE_SERVER_NAME ServerName,
IN handle_t BindingHandle
)
/*++
Routine Description:
This routine is called from the LSA client stubs when
it is necessary to unbind from the LSA server.
Arguments:
ServerName - This is the name of the server from which to unbind.
BindingHandle - This is the binding handle that is to be closed.
Return Value:
none.
--*/
{
UNREFERENCED_PARAMETER( ServerName ); // This parameter is not used
RpcpUnbindRpc ( BindingHandle );
return;
}
DWORD
DsRolepApiReturnResult(
ULONG ExceptionCode
)
/*++
Routine Description:
This function converts an exception code or status value returned
from the client stub to a value suitable for return by the API to
the client.
Arguments:
ExceptionCode - The exception code to be converted.
Return Value:
DWORD - The converted Nt Status code.
--*/
{
//
// Return the actual value if compatible with Nt status codes,
// otherwise, return STATUS_UNSUCCESSFUL.
//
NTSTATUS Status;
DWORD Results;
if ( !NT_SUCCESS( ( NTSTATUS )ExceptionCode ) ) {
Results = RtlNtStatusToDosError( ( NTSTATUS )ExceptionCode );
} else {
Results = ExceptionCode;
}
return( Results );
}
VOID
WINAPI
DsRoleFreeMemory(
IN PVOID Buffer
)
/*++
Routine Description:
Some setup 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 dssetup service call.
Return Values:
STATUS_SUCCESS - normal, successful completion.
--*/
{
MIDL_user_free( Buffer );
}
DWORD
WINAPI
DsRoleDnsNameToFlatName(
IN LPCWSTR lpServer OPTIONAL,
IN LPCWSTR lpDnsName,
OUT LPWSTR *lpFlatName,
OUT PULONG lpStatusFlag
)
/*++
Routine Description:
This routine will get the default NetBIOS (or flat) domain name for the given Dns domain name
Arguments:
lpServer - Server on which to remote the call (NULL is local)
lpDnsName - Dns domain name to generate the flat name for
lpFlatName - Where the flat name is returned. Must be freed via MIDL_user_free
(or DsRoleFreeMemory)
lpStatusFlag - Flags that indicate information about the returned name. Valid flags are:
DSROLE_FLATNAME_DEFAULT -- This is the default NetBIOS name for this dns domain name
DSROLE_FLATNAME_UPGRADE -- This is the name that is current in use by this machine as
a flat name and cannot be changed.
Return Values:
ERROR_SUCCESS - Success
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
handle_t Handle = NULL;
Win32Err = DsRolepServerBind( (PDSROLE_SERVER_NAME)lpServer,
&Handle );
if ( Win32Err != ERROR_SUCCESS ) {
return( Win32Err );
}
RpcTryExcept {
*lpFlatName = NULL;
*lpStatusFlag = 0;
Win32Err = DsRolerDnsNameToFlatName(
Handle,
( LPWSTR )lpDnsName,
lpFlatName,
lpStatusFlag );
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );
} RpcEndExcept;
DsRolepServerUnbind( (PDSROLE_SERVER_NAME)lpServer, Handle );
return( Win32Err );
}
DWORD
WINAPI
DsRoleDcAsDc(
IN LPCWSTR lpServer OPTIONAL,
IN LPCWSTR lpDnsDomainName,
IN LPCWSTR lpFlatDomainName,
IN LPCWSTR lpDomainAdminPassword OPTIONAL,
IN LPCWSTR lpSiteName OPTIONAL,
IN LPCWSTR lpDsDatabasePath,
IN LPCWSTR lpDsLogPath,
IN LPCWSTR lpSystemVolumeRootPath,
IN LPCWSTR lpParentDnsDomainName OPTIONAL,
IN LPCWSTR lpParentServer OPTIONAL,
IN LPCWSTR lpAccount OPTIONAL,
IN LPCWSTR lpPassword OPTIONAL,
IN LPCWSTR lpDsRepairPassword OPTIONAL,
IN ULONG Options,
OUT DSROLE_SERVEROP_HANDLE *DsOperationHandle
)
/*++
Routine Description:
This routine will get the promote a server to be a DC in a domain
Arguments:
lpServer - Server on which to remote the call (NULL is local)
lpDnsDomainName - Dns domain name of the domain to install
lpFlatDomainName - NetBIOS domain name of the domain to install
lpDomainAdminPassword - Password to set on the administrator account if it is a new install
SiteName - Name of the site this DC should belong to
lpDsDatabasePath - Absolute path on the local machine where the Ds DIT should go
lpDsLogPath - Absolute path on the local machine where the Ds log files should go
lpSystemVolumeRootPath - Absolute path on the local machine which will be the root of
the system volume.
lpParentDnsDomainName - Optional. If present, set this domain up as a child of the
specified domain
lpParentServer - Optional. If present, use this server in the parent domain to replicate
the required information from
lpAccount - User account to use when setting up as a child domain
lpPassword - Password to use with the above account
lpDsRepairPassword - Password to use for the admin account of the repair mode
Options - Options to control the creation of the domain
DsOperationHandle - Handle to the operation is returned here.
Return Values:
ERROR_SUCCESS - Success
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
handle_t Handle = NULL;
HANDLE RedirHandle = NULL;
#define DSROLEP_DC_AS_DC_DA_PWD_INDEX 0
#define DSROLEP_DC_AS_DC_PWD_INDEX 1
#define DSROLEP_DC_AS_DC_DS_REPAIR_PWD_INDEX 2
#define DSROLEP_DC_AS_DC_MAX_PWD_COUNT 3
PDSROLEPR_ENCRYPTED_USER_PASSWORD EncryptedPasswords[DSROLEP_DC_AS_DC_MAX_PWD_COUNT];
LPCWSTR Passwords[DSROLEP_DC_AS_DC_MAX_PWD_COUNT];
Passwords[DSROLEP_DC_AS_DC_DA_PWD_INDEX] = lpDomainAdminPassword;
Passwords[DSROLEP_DC_AS_DC_PWD_INDEX] = lpPassword;
Passwords[DSROLEP_DC_AS_DC_DS_REPAIR_PWD_INDEX] = lpDsRepairPassword;
RtlZeroMemory( EncryptedPasswords, sizeof(EncryptedPasswords) );
Win32Err = DsRolepEncryptPasswordStart( lpServer,
Passwords,
NELEMENTS(Passwords),
&Handle,
&RedirHandle,
NULL,
EncryptedPasswords );
if ( Win32Err != ERROR_SUCCESS ) {
return( Win32Err );
}
RpcTryExcept {
Win32Err = DsRolerDcAsDc( Handle,
( LPWSTR )lpDnsDomainName,
( LPWSTR )lpFlatDomainName,
EncryptedPasswords[DSROLEP_DC_AS_DC_DA_PWD_INDEX],
( LPWSTR )lpSiteName,
( LPWSTR )lpDsDatabasePath,
( LPWSTR )lpDsLogPath,
( LPWSTR )lpSystemVolumeRootPath,
( LPWSTR )lpParentDnsDomainName,
( LPWSTR )lpParentServer,
( LPWSTR )lpAccount,
EncryptedPasswords[DSROLEP_DC_AS_DC_PWD_INDEX],
EncryptedPasswords[DSROLEP_DC_AS_DC_DS_REPAIR_PWD_INDEX],
Options,
( PDSROLER_HANDLE )DsOperationHandle );
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );
} RpcEndExcept;
DsRolepEncryptPasswordEnd( Handle,
RedirHandle,
EncryptedPasswords,
NELEMENTS(EncryptedPasswords) );
return( Win32Err );
}
DWORD
WINAPI
DsRoleDcAsReplica(
IN LPCWSTR lpServer OPTIONAL,
IN LPCWSTR lpDnsDomainName,
IN LPCWSTR lpReplicaServer,
IN LPCWSTR lpSiteName OPTIONAL,
IN LPCWSTR lpDsDatabasePath,
IN LPCWSTR lpDsLogPath,
IN LPCWSTR lpRestorePath OPTIONAL,
IN LPCWSTR lpSystemVolumeRootPath,
IN OUT LPWSTR lpBootkey OPTIONAL,
IN LPCWSTR lpAccount OPTIONAL,
IN LPCWSTR lpPassword OPTIONAL,
IN LPCWSTR lpDsRepairPassword OPTIONAL,
IN ULONG Options,
OUT DSROLE_SERVEROP_HANDLE *DsOperationHandle
)
/*++
Routine Description:
This routine will install a server as a replica of an existing domain.
Arguments:
lpServer - OPTIONAL. Server to remote the call to.
lpDnsDomainName - Dns domain name of the domain to install into
lpReplicaServer - The name of a Dc within the existing domain, against which to replicate
lpSiteName - Name of the site this DC should belong to
lpDsDatabasePath - Absolute path on the local machine where the Ds DIT should go
lpDsLogPath - Absolute path on the local machine where the Ds log files should go
lpRestorepath - This is the path to a restored directory.
lpSystemVolumeRootPath - Absolute path on the local machine which will be the root of
the system volume.
lpAccount - User account to use when setting up as a child domain
lpPassword - Password to use with the above account
lpDsRepairPassword - Password to use for the admin account of the repair mode
Options - Options to control the creation of the domain
DsOperationHandle - Handle to the operation is returned here.
Return Values:
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
NTSTATUS Status = STATUS_SUCCESS;
handle_t Handle = NULL;
HANDLE RedirHandle = NULL;
USER_SESSION_KEY UserSessionKey;
UNICODE_STRING ClearBootKeyHash;
DSROLEPR_ENCRYPTED_HASH EncryptedBootKey;
#define DSROLEP_DC_AS_REPLICA_PWD_INDEX 0
#define DSROLEP_DC_AS_REPLICA_DS_REPAIR_PWD_INDEX 1
#define DSROLEP_DC_AS_REPLICA_MAX_PWD_COUNT 2
PDSROLEPR_ENCRYPTED_USER_PASSWORD EncryptedPasswords[DSROLEP_DC_AS_REPLICA_MAX_PWD_COUNT];
LPCWSTR Passwords[DSROLEP_DC_AS_REPLICA_MAX_PWD_COUNT];
Passwords[DSROLEP_DC_AS_REPLICA_PWD_INDEX] = lpPassword;
Passwords[DSROLEP_DC_AS_REPLICA_DS_REPAIR_PWD_INDEX] = lpDsRepairPassword;
RtlZeroMemory( EncryptedPasswords, sizeof(EncryptedPasswords) );
Win32Err = DsRolepEncryptPasswordStart( lpServer,
Passwords,
NELEMENTS(Passwords),
&Handle,
&RedirHandle,
&UserSessionKey,
EncryptedPasswords );
if ( Win32Err != ERROR_SUCCESS ) {
return( Win32Err );
}
if(lpBootkey)
{
Win32Err = DsRolepHashkey(lpBootkey,
&ClearBootKeyHash
);
if (Win32Err != ERROR_SUCCESS) {
return Win32Err;
}
Win32Err = DsRolepEncryptHash(&UserSessionKey,
&ClearBootKeyHash,
&EncryptedBootKey);
if (Win32Err != ERROR_SUCCESS) {
return Win32Err;
}
} else {
EncryptedBootKey.EncryptedHash.Buffer = NULL;
EncryptedBootKey.EncryptedHash.Length = 0;
EncryptedBootKey.EncryptedHash.MaximumLength = 0;
}
RpcTryExcept {
Win32Err = DsRolerDcAsReplica( Handle,
( LPWSTR )lpDnsDomainName,
( LPWSTR )lpReplicaServer,
( LPWSTR )lpSiteName,
( LPWSTR )lpDsDatabasePath,
( LPWSTR )lpDsLogPath,
( LPWSTR )lpRestorePath,
( LPWSTR )lpSystemVolumeRootPath,
EncryptedBootKey.EncryptedHash.Buffer==NULL?
NULL:&EncryptedBootKey,
( LPWSTR )lpAccount,
EncryptedPasswords[DSROLEP_DC_AS_REPLICA_PWD_INDEX],
EncryptedPasswords[DSROLEP_DC_AS_REPLICA_DS_REPAIR_PWD_INDEX],
Options,
( PDSROLER_HANDLE )DsOperationHandle );
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );
} RpcEndExcept;
if(EncryptedBootKey.EncryptedHash.Buffer)
{
MIDL_user_free(EncryptedBootKey.EncryptedHash.Buffer);
EncryptedBootKey.EncryptedHash.Length=0;
EncryptedBootKey.EncryptedHash.MaximumLength=0;
}
DsRolepEncryptPasswordEnd( Handle,
RedirHandle,
EncryptedPasswords,
NELEMENTS(EncryptedPasswords) );
return( Win32Err );
}
DWORD
WINAPI
DsRoleDemoteDc(
IN LPCWSTR lpServer OPTIONAL,
IN LPCWSTR lpDnsDomainName,
IN DSROLE_SERVEROP_DEMOTE_ROLE ServerRole,
IN LPCWSTR lpAccount OPTIONAL,
IN LPCWSTR lpPassword OPTIONAL,
IN ULONG Options,
IN BOOL fLastDcInDomain,
IN ULONG cRemoveNCs,
IN LPCWSTR *pszRemoveNCs OPTIONAL,
IN LPCWSTR lpAdminPassword OPTIONAL,
OUT DSROLE_SERVEROP_HANDLE *DsOperationHandle
)
/*++
Routine Description:
This routine will demote an existing Dc to a standalone or member server.
Arguments:
lpServer - Server to remote the call to
lpDnsDomainName - Name of the domain on this machine to demote. If NULL, demote all of the
domains on this machine
ServerRole - The new role this machine should take
lpAccount - OPTIONAL User account to use when deleting the trusted domain object
lpPassword - Password to use with the above account
Options - Options to control the demotion of the domain
fLastDcInDomain - If TRUE, this is the last dc in the domain
cRemoveNCs - Count of string pointers in pszRemoveNCs
pszRemoveNCs - Array of (cRemoveNCs) strings. Strings are DNs of NDNCs to be removed
lpAdminPassword - New local addmin password
DsOperationHandle - Handle to the operation is returned here.
Return Values:
ERROR_SUCCESS - Success
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
handle_t Handle = NULL;
HANDLE RedirHandle = NULL;
#define DSROLEP_DEMOTE_PWD_INDEX 0
#define DSROLEP_DEMOTE_ADMIN_PWD_INDEX 1
#define DSROLEP_DEMOTE_MAX_PWD_COUNT 2
PDSROLEPR_ENCRYPTED_USER_PASSWORD EncryptedPasswords[DSROLEP_DEMOTE_MAX_PWD_COUNT];
LPCWSTR Passwords[DSROLEP_DEMOTE_MAX_PWD_COUNT];
Passwords[DSROLEP_DEMOTE_PWD_INDEX] = lpPassword;
Passwords[DSROLEP_DEMOTE_ADMIN_PWD_INDEX] = lpAdminPassword;
RtlZeroMemory( EncryptedPasswords, sizeof(EncryptedPasswords) );
Win32Err = DsRolepEncryptPasswordStart( lpServer,
Passwords,
NELEMENTS(Passwords),
&Handle,
&RedirHandle,
NULL,
EncryptedPasswords );
if ( Win32Err != ERROR_SUCCESS ) {
return( Win32Err );
}
RpcTryExcept {
Win32Err = DsRolerDemoteDc( Handle,
( LPWSTR )lpDnsDomainName,
ServerRole,
( LPWSTR )lpAccount,
EncryptedPasswords[DSROLEP_DEMOTE_PWD_INDEX],
Options,
fLastDcInDomain,
cRemoveNCs,
pszRemoveNCs,
EncryptedPasswords[DSROLEP_DEMOTE_ADMIN_PWD_INDEX],
( PDSROLER_HANDLE )DsOperationHandle );
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );
} RpcEndExcept;
DsRolepEncryptPasswordEnd( Handle,
RedirHandle,
EncryptedPasswords,
NELEMENTS(EncryptedPasswords) );
return( Win32Err );
}
DWORD
WINAPI
DsRoleGetDcOperationProgress(
IN LPCWSTR lpServer OPTIONAL,
IN DSROLE_SERVEROP_HANDLE DsOperationHandle,
OUT PDSROLE_SERVEROP_STATUS *ServerOperationStatus
)
/*++
Routine Description:
Gets the progress of the currently running operation
Arguments:
lpServer - Server to remote the call to
DsOperationHandle - Handle of currently running operation. Returned by one of the DsRoleDcAs
apis
ServerOperationStatus - Where the current operation status is returned. Must be freed via
MIDL_user_free (or DsRoleFreeMemory)
Return Values:
ERROR_SUCCESS - Success
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
PDSROLER_SERVEROP_STATUS ServerStatus = NULL;
handle_t Handle = NULL;
Win32Err = DsRolepServerBind( (PDSROLE_SERVER_NAME)lpServer,
&Handle );
if ( Win32Err != ERROR_SUCCESS ) {
return( Win32Err );
}
RpcTryExcept {
Win32Err = DsRolerGetDcOperationProgress(
Handle,
(PDSROLER_HANDLE)&DsOperationHandle,
&ServerStatus );
if ( Win32Err == ERROR_SUCCESS || Win32Err == ERROR_IO_PENDING ) {
*ServerOperationStatus = ( PDSROLE_SERVEROP_STATUS )ServerStatus;
}
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );
} RpcEndExcept;
DsRolepServerUnbind( (PDSROLE_SERVER_NAME)lpServer, Handle );
return( Win32Err );
}
DWORD
WINAPI
DsRoleGetDcOperationResults(
IN LPCWSTR lpServer OPTIONAL,
IN DSROLE_SERVEROP_HANDLE DsOperationHandle,
OUT PDSROLE_SERVEROP_RESULTS *ServerOperationResults
)
/*++
Routine Description:
Gets the final results of an attempted promotion/demotion operation
Arguments:
lpServer - Server to remote the call to
DsOperationHandle - Handle of currently running operation. Returned by one of the DsRoleDcAs
apis
ServerOperationResults - Where the current operation result is returned. Must be freed via
MIDL_user_free (or DsRoleFreeMemory)
Return Values:
ERROR_SUCCESS - Success
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
PDSROLER_SERVEROP_RESULTS ServerResults = NULL;
handle_t Handle = NULL;
Win32Err = DsRolepServerBind( (PDSROLE_SERVER_NAME)lpServer,
&Handle );
if ( Win32Err != ERROR_SUCCESS ) {
return( Win32Err );
}
RpcTryExcept {
*ServerOperationResults = 0;
Win32Err = DsRolerGetDcOperationResults(
Handle,
(PDSROLER_HANDLE)&DsOperationHandle,
&ServerResults );
if ( Win32Err == ERROR_SUCCESS ) {
*ServerOperationResults = ( PDSROLE_SERVEROP_RESULTS )ServerResults;
}
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );
} RpcEndExcept;
DsRolepServerUnbind( (PDSROLE_SERVER_NAME)lpServer, Handle );
return( Win32Err );
}
DWORD
WINAPI
DsRoleGetPrimaryDomainInformation(
IN LPCWSTR lpServer OPTIONAL,
IN DSROLE_PRIMARY_DOMAIN_INFO_LEVEL InfoLevel,
OUT PBYTE *Buffer )
/*++
Routine Description:
Gets information on the machine
Arguments:
lpServer - Server to remote the call to
InfoLevel - What level of information is being requested. Currently supported levels are:
DsRolePrimaryDomainInfoBasic
Buffer - Where the information is returned. The returned pointer should be cast to the
appropriate type for the info level passed in. The returned buffer should be freed via
MIDL_user_free (or DsRoleFreeMemory)
Return Values:
ERROR_SUCCESS - Success
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
PDSROLER_PRIMARY_DOMAIN_INFORMATION PrimaryDomainInfo = NULL;
handle_t Handle = NULL;
NTSTATUS Status = STATUS_SUCCESS;
if ( Buffer == NULL ) {
return( ERROR_INVALID_PARAMETER );
}
Win32Err = DsRolepGetPrimaryDomainInfoServerBind( (PDSROLE_SERVER_NAME)lpServer,
&Handle );
if ( Win32Err != ERROR_SUCCESS ) {
return( Win32Err );
}
if ( NULL == Handle) {
return( ERROR_NOT_ENOUGH_MEMORY );
}
RpcTryExcept {
*Buffer = NULL;
Win32Err = DsRolerGetPrimaryDomainInformation(
Handle,
InfoLevel,
&PrimaryDomainInfo );
*Buffer = ( PBYTE )PrimaryDomainInfo;
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
Status = I_RpcMapWin32Status( RpcExceptionCode() );
Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );
} RpcEndExcept;
DsRolepServerUnbind( (PDSROLE_SERVER_NAME)lpServer, Handle );
//
// If this fails because we are calling a downlevel server, cobble up the information here
//
if ( ( Status == RPC_NT_UNKNOWN_IF || Status == RPC_NT_PROCNUM_OUT_OF_RANGE ) &&
InfoLevel == DsRolePrimaryDomainInfoBasic ) {
Win32Err = DsRolepGetPrimaryDomainInformationDownlevel( ( LPWSTR )lpServer, Buffer );
}
return( Win32Err );
}
DWORD
WINAPI
DsRoleCancel(
IN LPCWSTR lpServer OPTIONAL,
IN DSROLE_SERVEROP_HANDLE DsOperationHandle
)
/*++
Routine Description:
Cancels a currently running operation
Arguments:
lpServer - Server to remote the call to
DsOperationHandle - Handle of currently running operation. Returned by one of the DsRoleDcAs
apis
Return Values:
ERROR_SUCCESS - Success
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
handle_t Handle = NULL;
Win32Err = DsRolepServerBind( (PDSROLE_SERVER_NAME)lpServer,
&Handle );
if ( Win32Err != ERROR_SUCCESS ) {
return( Win32Err );
}
RpcTryExcept {
Win32Err = DsRolerCancel( Handle,
( PDSROLER_HANDLE )&DsOperationHandle );
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );
} RpcEndExcept;
DsRolepServerUnbind( (PDSROLE_SERVER_NAME)lpServer, Handle );
return( Win32Err );
}
DWORD
WINAPI
DsRoleIfmHandleFree(
IN LPCWSTR lpServer OPTIONAL,
IN DSROLE_IFM_OPERATION_HANDLE * pIfmHandle
)
/*++
Routine Description:
Cancels a currently running operation
Arguments:
lpServer - Server to remote the call to
DsOperationHandle - Handle of currently running operation. Returned by one of the DsRoleDcAs
apis
Return Values:
ERROR_SUCCESS - Success
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
handle_t Handle = NULL;
Win32Err = DsRolepServerBind( (PDSROLE_SERVER_NAME)lpServer,
&Handle );
if ( Win32Err != ERROR_SUCCESS ) {
return( Win32Err );
}
RpcTryExcept {
Win32Err = DsRolerIfmHandleFree( Handle,
( DSROLER_IFM_HANDLE ) pIfmHandle );
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );
} RpcEndExcept;
DsRolepServerUnbind( (PDSROLE_SERVER_NAME)lpServer, Handle );
return( Win32Err );
}
DWORD
WINAPI
DsRoleServerSaveStateForUpgrade(
IN LPCWSTR lpAnswerFile OPTIONAL
)
/*++
Routine Description:
This function is to be invoked during setup and saves the required server state to
complete the promotion following the reboot. Following the successful completion
of this API call, the server will be demoted to a member server in the same domain.
Arguments:
lpAnswerFile -- Optional path to an answer file to be used by DCPROMO when it is
invoked to do the upgrade
Return Values:
ERROR_SUCCESS - Success
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
handle_t Handle = NULL;
Win32Err = DsRolepServerBind( NULL,
&Handle );
if ( Win32Err != ERROR_SUCCESS ) {
return( Win32Err );
}
RpcTryExcept {
Win32Err = DsRolerServerSaveStateForUpgrade( Handle, ( LPWSTR )lpAnswerFile );
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );
} RpcEndExcept;
DsRolepServerUnbind( NULL, Handle );
return( Win32Err );
}
DWORD
WINAPI
DsRoleUpgradeDownlevelServer(
IN LPCWSTR lpDnsDomainName,
IN LPCWSTR lpSiteName,
IN LPCWSTR lpDsDatabasePath,
IN LPCWSTR lpDsLogPath,
IN LPCWSTR lpSystemVolumeRootPath,
IN LPCWSTR lpParentDnsDomainName OPTIONAL,
IN LPCWSTR lpParentServer OPTIONAL,
IN LPCWSTR lpAccount OPTIONAL,
IN LPCWSTR lpPassword OPTIONAL,
IN LPCWSTR lpDsRepairPassword OPTIONAL,
IN ULONG Options,
OUT DSROLE_SERVEROP_HANDLE *DsOperationHandle
)
/*++
Routine Description:
This routine process the information saved from a DsRoleServerSaveStateForUpgrade to
promote a downlevel (NT4 or previous) server to an NT5 DC
Arguments:
lpDnsDomainName - Dns domain name of the domain to install
SiteName - Name of the site this DC should belong to
lpDsDatabasePath - Absolute path on the local machine where the Ds DIT should go
lpDsLogPath - Absolute path on the local machine where the Ds log files should go
lpSystemVolumeRootPath - Absolute path on the local machine which will be the root of
the system volume.
lpParentDnsDomainName - Optional. If present, set this domain up as a child of the
specified domain
lpParentServer - Optional. If present, use this server in the parent domain to replicate
the required information from
lpAccount - User account to use when setting up as a child domain
lpPassword - Password to use with the above account
lpDsRepairPassword - Password to use for the admin account of the repair mode
Options - Options to control the creation of the domain
DsOperationHandle - Handle to the operation is returned here.
Return Values:
ERROR_SUCCESS - Success
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
handle_t Handle = NULL;
HANDLE RedirHandle = NULL;
#define DSROLEP_UPGRADE_PWD_INDEX 0
#define DSROLEP_UPGRADE_DS_REPAIR_PWD_INDEX 1
#define DSROLEP_UPGRADE_MAX_PWD_COUNT 2
PDSROLEPR_ENCRYPTED_USER_PASSWORD EncryptedPasswords[DSROLEP_UPGRADE_MAX_PWD_COUNT];
LPCWSTR Passwords[DSROLEP_UPGRADE_MAX_PWD_COUNT];
Passwords[DSROLEP_UPGRADE_PWD_INDEX] = lpPassword;
Passwords[DSROLEP_UPGRADE_DS_REPAIR_PWD_INDEX] = lpDsRepairPassword;
RtlZeroMemory( EncryptedPasswords, sizeof(EncryptedPasswords) );
Win32Err = DsRolepEncryptPasswordStart( NULL,
Passwords,
NELEMENTS(Passwords),
&Handle,
&RedirHandle,
NULL,
EncryptedPasswords );
if ( Win32Err != ERROR_SUCCESS ) {
return( Win32Err );
}
RpcTryExcept {
Win32Err = DsRolerUpgradeDownlevelServer( Handle,
( LPWSTR )lpDnsDomainName,
( LPWSTR )lpSiteName,
( LPWSTR )lpDsDatabasePath,
( LPWSTR )lpDsLogPath,
( LPWSTR )lpSystemVolumeRootPath,
( LPWSTR )lpParentDnsDomainName,
( LPWSTR )lpParentServer,
( LPWSTR )lpAccount,
EncryptedPasswords[DSROLEP_UPGRADE_PWD_INDEX],
EncryptedPasswords[DSROLEP_UPGRADE_DS_REPAIR_PWD_INDEX],
Options,
( PDSROLER_HANDLE )DsOperationHandle );
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );
} RpcEndExcept;
DsRolepEncryptPasswordEnd( Handle,
RedirHandle,
EncryptedPasswords,
NELEMENTS(EncryptedPasswords) );
return( Win32Err );
}
DWORD
WINAPI
DsRoleAbortDownlevelServerUpgrade(
IN LPCWSTR lpAdminPassword,
IN LPCWSTR lpAccount, OPTIONAL
IN LPCWSTR lpPassword, OPTIONAL
IN ULONG Options
)
/*++
Routine Description:
This routine cleans up the information saved from a DsRoleSaveServerStateForUpgrade call,
leaving the machine as a member or standalone server
Arguments:
lpAdminPassword - New local administrator account password
lpAccount - User account to use when setting up as a child domain
lpPassword - Password to use with the above account
Return Values:
ERROR_SUCCESS - Success
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
handle_t Handle = NULL;
HANDLE RedirHandle = NULL;
#define DSROLEP_ABORT_PWD_INDEX 0
#define DSROLEP_ABORT_ADMIN_PWD_INDEX 1
#define DSROLEP_ABORT_MAX_PWD_COUNT 2
PDSROLEPR_ENCRYPTED_USER_PASSWORD EncryptedPasswords[DSROLEP_ABORT_MAX_PWD_COUNT];
LPCWSTR Passwords[DSROLEP_ABORT_MAX_PWD_COUNT];
Passwords[DSROLEP_ABORT_PWD_INDEX] = lpPassword;
Passwords[DSROLEP_ABORT_ADMIN_PWD_INDEX] = lpAdminPassword;
RtlZeroMemory( EncryptedPasswords, sizeof(EncryptedPasswords) );
Win32Err = DsRolepEncryptPasswordStart( NULL,
Passwords,
NELEMENTS(Passwords),
&Handle,
&RedirHandle,
NULL,
EncryptedPasswords );
if ( Win32Err != ERROR_SUCCESS ) {
return( Win32Err );
}
RpcTryExcept {
Win32Err = DsRolerAbortDownlevelServerUpgrade( Handle,
( LPWSTR )lpAccount,
EncryptedPasswords[DSROLEP_ABORT_PWD_INDEX],
EncryptedPasswords[DSROLEP_ABORT_ADMIN_PWD_INDEX],
Options );
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );
} RpcEndExcept;
DsRolepEncryptPasswordEnd( Handle,
RedirHandle,
EncryptedPasswords,
NELEMENTS(EncryptedPasswords) );
return( Win32Err );
}
DWORD
WINAPI
DsRoleGetDatabaseFacts(
IN LPCWSTR lpServer OPTIONAL,
IN LPCWSTR lpRestorePath,
OUT LPWSTR *lpDNSDomainName,
OUT PULONG State,
OUT DSROLE_IFM_OPERATION_HANDLE * pIfmHandle
)
/*++
Routine Description:
This function is the RPC procedure exposed to setup the server side
IFM handle DsRolepCurrentIfmOperationHandle, which caches the information
we'll need from the IFM system's registry. This function also returns
the relevant subset of this IFM system information to the caller (dcpromo).
Note: We do this only once, because in the case that the IFM registry
is in a non-writeable location (such as a CD), we will need to copy off
the registry to a temporary location to use it.
This function returns to the caller:
1. the way the syskey is stored (State)
2. the domain that the database came from (lpDNSDomainName)
3. where the backup was taken from a GC or not (State)
Arguments:
lpServer - The server to get the Facts from
lpRestorePath - The location of the restored files.
lpDNSDomainName - This parameter will recieve the name of the domain that this backup came
from
State - The return Values that report How the syskey is stored and If the back was likely
taken form a GC or not.
pIfmHandle - Pointer to the IFM handle handed back. This is primarily used
to "free" the IFM System Info.
Return Values:
Win32 Error
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
handle_t Handle = NULL;
if(lpDNSDomainName == NULL ||
IsBadWritePtr(lpDNSDomainName, sizeof(LPWSTR)) ||
State == NULL ||
IsBadWritePtr(State, sizeof(DWORD)) ||
pIfmHandle == NULL ||
IsBadWritePtr(pIfmHandle, sizeof(void *)) ){
return ERROR_INVALID_PARAMETER;
}
Win32Err = DsRolepServerBind( (PDSROLE_SERVER_NAME)lpServer,
&Handle );
if ( Win32Err != ERROR_SUCCESS ) {
return( Win32Err );
}
RpcTryExcept {
Win32Err = DsRolerGetDatabaseFacts( Handle,
( LPWSTR )lpRestorePath,
( LPWSTR * )lpDNSDomainName,
( PULONG )State,
( DSROLER_IFM_HANDLE * ) pIfmHandle );
} RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );
} RpcEndExcept;
DsRolepServerUnbind( (PDSROLE_SERVER_NAME)lpServer, Handle );
return Win32Err;
}
//
// Local functions
//
DWORD
DsRolepGetPrimaryDomainInformationDownlevel(
IN LPWSTR Server,
OUT PBYTE *Buffer
)
{
DWORD Win32Err = ERROR_SUCCESS;
NTSTATUS Status;
LSA_HANDLE PolicyHandle;
PPOLICY_PRIMARY_DOMAIN_INFO PDI = NULL;
PPOLICY_LSA_SERVER_ROLE_INFO ServerRole = NULL;
PPOLICY_ACCOUNT_DOMAIN_INFO ADI = NULL;
UNICODE_STRING UnicodeServer;
OBJECT_ATTRIBUTES OA;
PDSROLE_PRIMARY_DOMAIN_INFO_BASIC PDIB = NULL;
DSROLE_MACHINE_ROLE MachineRole = DsRole_RoleStandaloneServer;
NT_PRODUCT_TYPE ProductType;
Win32Err = DsRolepGetProductTypeForServer( Server, &ProductType );
if ( Win32Err != ERROR_SUCCESS ) {
return( Win32Err );
}
InitializeObjectAttributes( &OA, NULL, 0, NULL, NULL);
if ( Server ) {
RtlInitUnicodeString( &UnicodeServer, Server );
}
Status = LsaOpenPolicy( Server ? &UnicodeServer : NULL,
&OA,
POLICY_VIEW_LOCAL_INFORMATION,
&PolicyHandle );
if ( NT_SUCCESS( Status ) ) {
Status = LsaQueryInformationPolicy( PolicyHandle,
PolicyPrimaryDomainInformation,
( PVOID * ) &PDI );
if ( NT_SUCCESS( Status ) ) {
switch ( ProductType ) {
case NtProductWinNt:
if ( PDI->Sid == NULL ) {
MachineRole = DsRole_RoleStandaloneWorkstation;
} else {
MachineRole = DsRole_RoleMemberWorkstation;
}
break;
case NtProductServer:
if ( PDI->Sid == NULL ) {
MachineRole = DsRole_RoleStandaloneServer;
} else {
MachineRole = DsRole_RoleMemberServer;
}
break;
case NtProductLanManNt:
Status = LsaQueryInformationPolicy( PolicyHandle,
PolicyLsaServerRoleInformation,
( PVOID * )&ServerRole );
if ( NT_SUCCESS( Status ) ) {
if ( ServerRole->LsaServerRole == PolicyServerRolePrimary ) {
//
// If we think we're a primary domain controller, we'll need to
// guard against the case where we're actually standalone during setup
//
Status = LsaQueryInformationPolicy( PolicyHandle,
PolicyAccountDomainInformation,
( PVOID * )&ADI );
if ( NT_SUCCESS( Status ) ) {
if ( PDI->Sid == NULL ||
ADI->DomainSid == NULL ||
!RtlEqualSid( ADI->DomainSid, PDI->Sid ) ) {
MachineRole = DsRole_RoleStandaloneServer;
} else {
MachineRole = DsRole_RolePrimaryDomainController;
}
}
} else {
MachineRole = DsRole_RoleBackupDomainController;
}
}
break;
default:
Status = STATUS_INVALID_PARAMETER;
break;
}
}
//
// Build the return buffer
//
if ( NT_SUCCESS( Status ) ) {
PDIB = MIDL_user_allocate( sizeof( DSROLE_PRIMARY_DOMAIN_INFO_BASIC ) +
PDI->Name.Length + sizeof( WCHAR ) );
if ( PDIB == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
RtlZeroMemory( PDIB, sizeof( DSROLE_PRIMARY_DOMAIN_INFO_BASIC ) +
PDI->Name.Length + sizeof( WCHAR ) );
PDIB->MachineRole = MachineRole;
PDIB->DomainNameFlat = ( LPWSTR ) ( ( PBYTE )PDIB +
sizeof( DSROLE_PRIMARY_DOMAIN_INFO_BASIC ) );
RtlCopyMemory( PDIB->DomainNameFlat, PDI->Name.Buffer, PDI->Name.Length );
*Buffer = ( PBYTE )PDIB;
}
}
LsaClose( PolicyHandle );
LsaFreeMemory( PDI );
if ( ADI != NULL ) {
LsaFreeMemory( ADI );
}
if ( ServerRole != NULL ) {
LsaFreeMemory( ServerRole );
}
}
Win32Err = RtlNtStatusToDosError( Status );
return( Win32Err );
}
DWORD
DsRolepGetProductTypeForServer(
IN LPWSTR Server,
IN OUT PNT_PRODUCT_TYPE ProductType
)
{
DWORD Win32Err = ERROR_SUCCESS;
PWSTR RegServer = NULL;
HKEY RemoteKey, ProductKey;
PBYTE Buffer = NULL;
ULONG Type, Size = 0;
if ( Server == NULL ) {
if ( RtlGetNtProductType( ProductType ) == FALSE ) {
Win32Err = RtlNtStatusToDosError( STATUS_UNSUCCESSFUL );
}
} else {
if ( wcslen( Server ) > 2 && *Server == L'\\' && *( Server + 1 ) == L'\\' ) {
RegServer = Server;
} else {
RegServer = LocalAlloc( LMEM_FIXED, ( wcslen( Server ) + 3 ) * sizeof( WCHAR ) );
if ( RegServer ) {
swprintf( RegServer, L"\\\\%ws", Server );
} else {
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
}
}
if ( Win32Err == ERROR_SUCCESS ) {
Win32Err = RegConnectRegistry( RegServer,
HKEY_LOCAL_MACHINE,
&RemoteKey );
if ( Win32Err == ERROR_SUCCESS ) {
Win32Err = RegOpenKeyEx( RemoteKey,
L"system\\currentcontrolset\\control\\productoptions",
0,
KEY_READ,
&ProductKey );
if ( Win32Err == ERROR_SUCCESS ) {
Win32Err = RegQueryValueEx( ProductKey,
L"ProductType",
0,
&Type,
0,
&Size );
if ( Win32Err == ERROR_SUCCESS ) {
Buffer = LocalAlloc( LMEM_FIXED, Size );
if ( Buffer ) {
Win32Err = RegQueryValueEx( ProductKey,
L"ProductType",
0,
&Type,
Buffer,
&Size );
if ( Win32Err == ERROR_SUCCESS ) {
if ( !_wcsicmp( ( PWSTR )Buffer, L"LanmanNt" ) ) {
*ProductType = NtProductLanManNt;
} else if ( !_wcsicmp( ( PWSTR )Buffer, L"ServerNt" ) ) {
*ProductType = NtProductServer;
} else if ( !_wcsicmp( ( PWSTR )Buffer, L"WinNt" ) ) {
*ProductType = NtProductWinNt;
} else {
Win32Err = ERROR_UNKNOWN_PRODUCT;
}
}
LocalFree( Buffer );
} else {
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
}
}
RegCloseKey( ProductKey );
}
RegCloseKey( RemoteKey );
}
}
if ( RegServer != Server ) {
LocalFree( RegServer );
}
}
return( Win32Err );
}
NTSTATUS
DsRolepRandomFill(
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()
--*/
{
if( RtlGenRandom(Buffer, BufferSize) )
{
return STATUS_SUCCESS;
}
return STATUS_UNSUCCESSFUL;
}
DWORD
DsRolepEncryptPasswordStart(
IN LPCWSTR ServerName OPTIONAL,
IN LPCWSTR *Passwords,
IN ULONG Count,
OUT RPC_BINDING_HANDLE *RpcBindingHandle,
OUT HANDLE *RedirHandle,
OUT OPTIONAL PUSER_SESSION_KEY pUserSessionKey,
IN OUT PDSROLEPR_ENCRYPTED_USER_PASSWORD *EncryptedUserPasswords
)
/*++
Routine Description:
This routine takes a number of cleartext unicode NT password from the user,
and encrypts them with the session key.
This routine's algorithm was taken from CliffV's work when encrypting the
passwords for the NetrJoinDomain2 interface.
Parameters:
ServerName - UNC server name of the server to remote the API to
Passwords - the cleartext unicode NT passwords.
Count - the number of password
RpcBindingHandle - RPC handle used for acquiring a session key.
RedirHandle - Returns a handle to the redir. Since RpcBindingHandles don't represent
and open connection to the server, we have to ensure the connection stays open
until the server side has a chance to get this same UserSessionKey. The only
way to do that is to keep the connect open.
Returns NULL if no handle is needed.
UserSessionKey - OPTIONAL - Session Key used to encrypt passwords
EncryptedUserPassword - receives the encrypted cleartext passwords.
If lpPassword is NULL, a NULL is returned for that entry.
Return Values:
If this routine returns NO_ERROR, the returned data must be freed using
LocalFree.
--*/
{
DWORD WinError = ERROR_SUCCESS;
NTSTATUS NtStatus;
RC4_KEYSTRUCT Rc4Key;
MD5_CTX Md5Context;
USER_SESSION_KEY UserSessionKey;
PDSROLEPR_USER_PASSWORD UserPassword = NULL;
ULONG PasswordSize;
ULONG i;
//
// Initialization
//
*RpcBindingHandle = NULL;
*RedirHandle = NULL;
for ( i = 0; i < Count; i++ ) {
EncryptedUserPasswords[i] = NULL;
}
//
// Verify parameters
//
for ( i = 0; i < Count; i++ ) {
if ( Passwords[i] ) {
PasswordSize = wcslen( Passwords[i] ) * sizeof(WCHAR);
if ( PasswordSize > DSROLE_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) {
WinError = ERROR_PASSWORD_RESTRICTION;
goto Cleanup;
}
}
}
//
// Get an RPC handle to the server.
//
WinError = DsRolepServerBind( (PDSROLE_SERVER_NAME) ServerName,
RpcBindingHandle );
if ( ERROR_SUCCESS != WinError ) {
goto Cleanup;
}
//
// Get the session key.
//
NtStatus = RtlGetUserSessionKeyClientBinding(
*RpcBindingHandle,
RedirHandle,
&UserSessionKey );
if ( !NT_SUCCESS(NtStatus) ) {
WinError = RtlNtStatusToDosError( NtStatus );
goto Cleanup;
}
//Return the UserSessionKey if requested
if (pUserSessionKey) {
CopyMemory(pUserSessionKey, &UserSessionKey, sizeof(UserSessionKey));
}
//
// Encrypt the passwords
//
for ( i = 0; i < Count; i++ ) {
if ( NULL == Passwords[i] ) {
// Nothing to encrypt
continue;
}
PasswordSize = wcslen( Passwords[i] ) * sizeof(WCHAR);
//
// Allocate a buffer to encrypt and fill it in.
//
UserPassword = LocalAlloc( 0, sizeof(DSROLEPR_USER_PASSWORD) );
if ( UserPassword == NULL ) {
WinError = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
//
// Copy the password into the tail end of the buffer.
//
RtlCopyMemory(
((PCHAR) UserPassword->Buffer) +
(DSROLE_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) -
PasswordSize,
Passwords[i],
PasswordSize );
UserPassword->Length = PasswordSize;
//
// Fill the front of the buffer with random data
//
NtStatus = DsRolepRandomFill(
(DSROLE_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) -
PasswordSize,
(PUCHAR) UserPassword->Buffer );
if ( !NT_SUCCESS(NtStatus) ) {
WinError = RtlNtStatusToDosError( NtStatus );
goto Cleanup;
}
NtStatus = DsRolepRandomFill(
DSROLE_OBFUSCATOR_LENGTH,
(PUCHAR) UserPassword->Obfuscator );
if ( !NT_SUCCESS(NtStatus) ) {
WinError = RtlNtStatusToDosError( NtStatus );
goto Cleanup;
}
//
// The UserSessionKey is the same for the life of the session. RC4'ing multiple
// strings with a single key is weak (if you crack one you've cracked them all).
// So compute a key that's unique for this particular encryption.
//
//
MD5Init(&Md5Context);
MD5Update( &Md5Context, (LPBYTE)&UserSessionKey, sizeof(UserSessionKey) );
MD5Update( &Md5Context, UserPassword->Obfuscator, sizeof(UserPassword->Obfuscator) );
MD5Final( &Md5Context );
rc4_key( &Rc4Key, MD5DIGESTLEN, Md5Context.digest );
//
// Encrypt it.
// Don't encrypt the obfuscator. The server needs that to compute the key.
//
rc4( &Rc4Key, sizeof(UserPassword->Buffer)+sizeof(UserPassword->Length), (LPBYTE) UserPassword->Buffer );
EncryptedUserPasswords[i] = (PDSROLEPR_ENCRYPTED_USER_PASSWORD) UserPassword;
UserPassword = NULL;
}
WinError = ERROR_SUCCESS;
Cleanup:
if ( WinError != ERROR_SUCCESS ) {
if ( UserPassword != NULL ) {
LocalFree( UserPassword );
}
if ( *RpcBindingHandle != NULL ) {
DsRolepServerUnbind( NULL, *RpcBindingHandle );
*RpcBindingHandle = NULL;
}
if ( *RedirHandle != NULL ) {
NtClose( *RedirHandle );
*RedirHandle = NULL;
}
for ( i = 0; i < Count; i++ ) {
if ( EncryptedUserPasswords[i] ) {
LocalFree( EncryptedUserPasswords[i] );
EncryptedUserPasswords[i] = NULL;
}
}
}
return WinError;
}
VOID
DsRolepEncryptPasswordEnd(
IN RPC_BINDING_HANDLE RpcBindingHandle,
IN HANDLE RedirHandle OPTIONAL,
IN PDSROLEPR_ENCRYPTED_USER_PASSWORD *EncryptedUserPasswords OPTIONAL,
IN ULONG Count
)
/*++
Routine Description:
This routine takes the variables returned by DsRolepEncryptPasswordStart and
frees them.
Parameters:
RpcBindingHandle - RPC handle used for acquiring a session key.
RedirHandle - Handle to the redirector
EncryptedUserPasswords - the encrypted cleartext passwords.
Count - the number of passwords
Return Values:
--*/
{
ULONG i;
//
// Free the RPC binding handle.
//
if ( RpcBindingHandle != NULL ) {
(VOID) DsRolepServerUnbind ( NULL, RpcBindingHandle );
}
//
// Close the redir handle.
//
if ( RedirHandle != NULL ) {
NtClose( RedirHandle );
}
//
// Free the encrypted passwords.
//
for ( i = 0; i < Count; i++ ) {
if ( EncryptedUserPasswords[i] != NULL ) {
LocalFree( EncryptedUserPasswords[i] );
}
}
return;
}
DWORD
DsRolepHashkey(
IN OUT LPWSTR key,
OUT PUNICODE_STRING Hash
)
/*++
Routine Description
This routine is used to store the boot type
in the registry
Paramaeters
key - the user passed in Key
Hash - the hash of the key
Return Values
STATUS_SUCCESS
STATUS_UNSUCCESSFUL
--*/
{
MD5_CTX Md5;
if (!key) {
return ERROR_INVALID_PARAMETER;
}
MD5Init( &Md5 );
MD5Update( &Md5, (PUCHAR) key, wcslen(key)*sizeof(WCHAR) );
MD5Final( &Md5 );
RtlSecureZeroMemory( key, wcslen(key)*sizeof(WCHAR) );
Hash->Buffer = (LPWSTR) MIDL_user_allocate(SYSKEY_SIZE);
if (!Hash->Buffer) {
return ERROR_NOT_ENOUGH_MEMORY;
}
RtlCopyMemory(Hash->Buffer, Md5.digest, SYSKEY_SIZE);
RtlSecureZeroMemory( Md5.digest, SYSKEY_SIZE );
Hash->Length=SYSKEY_SIZE;
Hash->MaximumLength=SYSKEY_SIZE;
return ERROR_SUCCESS;
}
DWORD
DsRolepEncryptHash(
IN PUSER_SESSION_KEY UserSessionKey,
IN OUT PUNICODE_STRING Syskey,
OUT PDSROLEPR_ENCRYPTED_HASH EncryptedSyskey
)
/*++
Routine Description
This routine is used to store the boot type
in the registry
Paramaeters
NewType Indicates the new boot type
Return Values
STATUS_SUCCESS
STATUS_UNSUCCESSFUL
--*/
{
DWORD WinError = ERROR_SUCCESS;
NTSTATUS NtStatus;
RC4_KEYSTRUCT Rc4Key;
MD5_CTX Md5Context;
//Init the buffer
if (EncryptedSyskey) {
EncryptedSyskey->EncryptedHash.Buffer = NULL;
} else {
WinError = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
//
//parameter checking
//
if ( !Syskey || !Syskey->Buffer || (Syskey->Length < 1)
|| ! UserSessionKey) {
WinError = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
//Init PDSROLEPR_ENCRYPTED_HASH structure
EncryptedSyskey->EncryptedHash.Buffer = Syskey->Buffer;
EncryptedSyskey->EncryptedHash.Length = Syskey->Length;
EncryptedSyskey->EncryptedHash.MaximumLength = Syskey->MaximumLength;
//Clear out the Syskey
RtlSecureZeroMemory(Syskey,sizeof(*Syskey));
//Create a Random Salt
NtStatus = DsRolepRandomFill(
DSROLE_SALT_LENGTH,
EncryptedSyskey->Salt );
if ( !NT_SUCCESS(NtStatus) ) {
WinError = RtlNtStatusToDosError( NtStatus );
goto Cleanup;
}
//
// The UserSessionKey is the same for the life of the session. RC4'ing multiple
// strings with a single key is weak (if you crack one you've cracked them all).
// So compute a key that's unique for this particular encryption.
//
//
MD5Init(&Md5Context);
MD5Update( &Md5Context, (LPBYTE)UserSessionKey, sizeof(*UserSessionKey) );
MD5Update( &Md5Context, EncryptedSyskey->Salt, sizeof(EncryptedSyskey->Salt) );
MD5Final( &Md5Context );
//
// Encrypt it.
// Don't encrypt the salt. The server needs that to compute the key.
//
rc4_key( &Rc4Key, MD5DIGESTLEN, Md5Context.digest );
rc4( &Rc4Key, EncryptedSyskey->EncryptedHash.Length, (LPBYTE) EncryptedSyskey->EncryptedHash.Buffer );
WinError = ERROR_SUCCESS;
Cleanup:
if ( ERROR_SUCCESS != WinError ) {
if (Syskey && Syskey->Buffer) {
MIDL_user_free(Syskey->Buffer);
}
else if (EncryptedSyskey && EncryptedSyskey->EncryptedHash.Buffer) {
MIDL_user_free(EncryptedSyskey->EncryptedHash.Buffer);
}
}
return WinError;
}