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