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.
3095 lines
88 KiB
3095 lines
88 KiB
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dispatch.c
|
|
|
|
Abstract:
|
|
|
|
Implementation of the server side of the DsRole API's
|
|
|
|
Author:
|
|
|
|
Colin Brace (ColinBr) April 5, 1999.
|
|
|
|
Environment:
|
|
|
|
User Mode
|
|
|
|
Revision History:
|
|
|
|
Reorg'ed from code written by
|
|
|
|
Mac McLain (MacM) Feb 10, 1997
|
|
|
|
--*/
|
|
|
|
#include <setpch.h>
|
|
#include <dssetp.h>
|
|
#include <dsgetdc.h>
|
|
#include <samrpc.h>
|
|
#include <samisrv.h>
|
|
#include <lmcons.h>
|
|
#include <lmapibuf.h>
|
|
#include <lmaccess.h>
|
|
#include <lmsname.h>
|
|
#include <lsarpc.h>
|
|
#include <db.h>
|
|
#include <lsasrvmm.h>
|
|
#include <lsaisrv.h>
|
|
#include <loadfn.h>
|
|
#include <lmjoin.h>
|
|
#include <netsetup.h>
|
|
#include <lmcons.h>
|
|
#include <lmerr.h>
|
|
#include <icanon.h>
|
|
#include <dsrole.h>
|
|
#include <dsrolep.h>
|
|
#include <dsconfig.h>
|
|
#include <winbase.h> //for RtlSecureZeroMemory
|
|
|
|
#include <crypt.h>
|
|
#include <rc4.h>
|
|
#include <md5.h>
|
|
#include <wxlpc.h>
|
|
|
|
#include "secure.h"
|
|
#include "threadman.h"
|
|
#include "upgrade.h"
|
|
#include "cancel.h"
|
|
|
|
|
|
//
|
|
// Static global. This flag is used to indicate that the system is installed enough to get
|
|
// us going. There is no need to protect it, since it is only toggled from off to on
|
|
//
|
|
static BOOLEAN DsRolepSamInitialized = FALSE;
|
|
|
|
//
|
|
// Local forwards
|
|
//
|
|
DWORD
|
|
DsRolepWaitForSam(
|
|
VOID
|
|
);
|
|
|
|
DWORD
|
|
DsRolepCheckFilePaths(
|
|
IN LPWSTR DsDirPath,
|
|
IN LPWSTR DsLogPath,
|
|
IN LPWSTR SysVolPath
|
|
);
|
|
|
|
DWORD
|
|
DsRolepIsValidProductSuite(
|
|
IN BOOL fNewForest,
|
|
IN BOOL fReplica,
|
|
IN LPWSTR DomainName
|
|
);
|
|
|
|
DWORD
|
|
DsRolepDecryptPasswordsWithKey(
|
|
IN handle_t RpcBindingHandle,
|
|
IN PDSROLEPR_ENCRYPTED_USER_PASSWORD * EncryptedPasswords,
|
|
IN ULONG Count,
|
|
IN OUT UNICODE_STRING *EncodedPasswords,
|
|
OUT OPTIONAL PUSER_SESSION_KEY UserSessionKey,
|
|
OUT PUCHAR Seed
|
|
);
|
|
|
|
VOID
|
|
DsRolepFreePasswords(
|
|
IN OUT UNICODE_STRING *Passwords,
|
|
IN ULONG Count
|
|
);
|
|
|
|
DWORD
|
|
DsRolepDecryptHash(
|
|
IN PUSER_SESSION_KEY pUserSessionKey,
|
|
IN PDSROLEPR_ENCRYPTED_HASH EncryptedBootkey,
|
|
OUT PUNICODE_STRING *Bootkey
|
|
);
|
|
|
|
|
|
//
|
|
// RPC dispatch routines
|
|
//
|
|
DWORD
|
|
DsRolerDcAsDc(
|
|
IN handle_t RpcBindingHandle,
|
|
IN LPWSTR DnsDomainName,
|
|
IN LPWSTR FlatDomainName,
|
|
IN PDSROLEPR_ENCRYPTED_USER_PASSWORD EDomainAdminPassword, OPTIONAL
|
|
IN LPWSTR SiteName OPTIONAL,
|
|
IN LPWSTR DsDatabasePath,
|
|
IN LPWSTR DsLogPath,
|
|
IN LPWSTR SystemVolumeRootPath,
|
|
IN LPWSTR ParentDnsDomainName OPTIONAL,
|
|
IN LPWSTR ParentServer OPTIONAL,
|
|
IN LPWSTR Account OPTIONAL,
|
|
IN PDSROLEPR_ENCRYPTED_USER_PASSWORD EPassword, OPTIONAL
|
|
IN PDSROLEPR_ENCRYPTED_USER_PASSWORD EDsRepairPassword, OPTIONAL
|
|
IN ULONG Options,
|
|
OUT PDSROLER_HANDLE DsOperationHandle)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Rpc server routine for installing a server as a DC
|
|
|
|
Arguments:
|
|
|
|
RpcBindingHandle - the RPC context, used to decrypt the passwords
|
|
|
|
DnsDomainName - Dns domain name of the domain to install
|
|
|
|
FlatDomainName - NetBIOS domain name of the domain to install
|
|
|
|
EDomainAdminPassword - Encrypted password to set on the administrator account if it is a new install
|
|
|
|
SiteName - Name of the site this DC should belong to
|
|
|
|
DsDatabasePath - Absolute path on the local machine where the Ds DIT should go
|
|
|
|
DsLogPath - Absolute path on the local machine where the Ds log files should go
|
|
|
|
SystemVolumeRootPath - Absolute path on the local machine which will be the root of
|
|
the system volume.
|
|
|
|
ParentDnsDomainName - Optional. If present, set this domain up as a child of the
|
|
specified domain
|
|
|
|
Account - User account to use when setting up as a child domain
|
|
|
|
EPassword - Encrypted password to use with the above account
|
|
|
|
EDsRepairPassword - Encrypted 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.
|
|
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS - Success
|
|
|
|
ERROR_INVALID_PARAMETER - A NULL return parameter was given
|
|
|
|
ERROR_INVALID_STATE - This machine is not a server
|
|
|
|
--*/
|
|
{
|
|
DWORD Win32Err = ERROR_SUCCESS;
|
|
DSROLEP_MACHINE_TYPE MachineRole;
|
|
PDSROLEP_OPERATION_PROMOTE_ARGS PromoteArgs;
|
|
UCHAR Seed = 0;
|
|
|
|
HANDLE Policy = NULL;
|
|
PDSROLE_PRIMARY_DOMAIN_INFO_BASIC ServerInfo = NULL;
|
|
|
|
BOOL fHandleInit = FALSE;
|
|
|
|
#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];
|
|
UNICODE_STRING Passwords[DSROLEP_DC_AS_DC_MAX_PWD_COUNT];
|
|
|
|
EncryptedPasswords[DSROLEP_DC_AS_DC_DA_PWD_INDEX] = EDomainAdminPassword;
|
|
EncryptedPasswords[DSROLEP_DC_AS_DC_PWD_INDEX] = EPassword;
|
|
EncryptedPasswords[DSROLEP_DC_AS_DC_DS_REPAIR_PWD_INDEX] = EDsRepairPassword;
|
|
|
|
RtlZeroMemory( Passwords, sizeof(Passwords) );
|
|
|
|
//
|
|
// Do some parameter checking
|
|
//
|
|
if ( !DsDatabasePath
|
|
|| !DsLogPath
|
|
|| !SystemVolumeRootPath ) {
|
|
|
|
return( ERROR_INVALID_PARAMETER );
|
|
|
|
}
|
|
|
|
//
|
|
// UI Enforces MAX_PATH as the max length.
|
|
//
|
|
if ( (wcslen(DsDatabasePath) > MAX_PATH) ||
|
|
(wcslen(DsLogPath) > MAX_PATH) ||
|
|
(wcslen(SystemVolumeRootPath) > MAX_PATH) )
|
|
{
|
|
return( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// Do some parameter checking
|
|
//
|
|
if ( !DnsDomainName
|
|
|| !DsDatabasePath
|
|
|| !DsLogPath
|
|
|| !FlatDomainName
|
|
|| !SystemVolumeRootPath ) {
|
|
|
|
return( ERROR_INVALID_PARAMETER );
|
|
|
|
}
|
|
|
|
if ( !ParentDnsDomainName
|
|
&& !SiteName )
|
|
{
|
|
// Site name must be specified when installing the root of the forest
|
|
return ( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
if ( FLAG_ON( Options, DSROLE_DC_TRUST_AS_ROOT )
|
|
&& !ParentDnsDomainName ) {
|
|
|
|
//
|
|
// When installing a new tree in an existing forest,
|
|
// the root domain DNS name must be present.
|
|
//
|
|
return ( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
if ( FLAG_ON( Options, DSROLE_DC_NO_NET )
|
|
&& ParentDnsDomainName ) {
|
|
|
|
//
|
|
// No net option when installing a child domain
|
|
// does not make sense
|
|
//
|
|
return ( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// Do our necessary initializations
|
|
//
|
|
Win32Err = DsRolepInitializeOperationHandle( );
|
|
if ( Win32Err != ERROR_SUCCESS ) {
|
|
|
|
goto Cleanup;
|
|
}
|
|
fHandleInit = TRUE;
|
|
|
|
//
|
|
// Check the access of the caller
|
|
//
|
|
Win32Err = DsRolepCheckPromoteAccess( TRUE );
|
|
if ( ERROR_SUCCESS != Win32Err ) {
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// Init the logging
|
|
//
|
|
DsRolepInitializeLog();
|
|
|
|
//
|
|
// Check that the current OS configuration supports this request
|
|
//
|
|
Win32Err = DsRolepIsValidProductSuite((ParentDnsDomainName == NULL) ? TRUE : FALSE,
|
|
FALSE,
|
|
DnsDomainName);
|
|
if ( ERROR_SUCCESS != Win32Err ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Dump the parameters to the log
|
|
//
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"Promotion request for domain controller of new domain\n" ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"DnsDomainName %ws\n",
|
|
DnsDomainName ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tFlatDomainName %ws\n",
|
|
FlatDomainName ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tSiteName %ws\n",
|
|
DsRolepDisplayOptional( SiteName ) ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tSystemVolumeRootPath %ws\n",
|
|
SystemVolumeRootPath ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tDsDatabasePath %ws, DsLogPath %ws\n",
|
|
DsDatabasePath, DsLogPath ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tParentDnsDomainName %ws\n",
|
|
DsRolepDisplayOptional( ParentDnsDomainName ) ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tParentServer %ws\n",
|
|
DsRolepDisplayOptional( ParentServer ) ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tAccount %ws\n",
|
|
DsRolepDisplayOptional( Account ) ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tOptions %lu\n",
|
|
Options ));
|
|
|
|
//
|
|
// Make sure that we are not a member of a domain
|
|
//
|
|
|
|
Win32Err = DsRolerGetPrimaryDomainInformation(NULL,
|
|
DsRolePrimaryDomainInfoBasic,
|
|
(PDSROLER_PRIMARY_DOMAIN_INFORMATION*)&ServerInfo);
|
|
if (ERROR_SUCCESS == Win32Err) {
|
|
ASSERT(ServerInfo);
|
|
if(ServerInfo->MachineRole != DsRole_RoleStandaloneServer) {
|
|
Win32Err = ERROR_CURRENT_DOMAIN_NOT_ALLOWED;
|
|
DsRolepLogOnFailure( Win32Err,
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"Verifying domain membership failed: %lu\n",
|
|
Win32Err )) );
|
|
goto Cleanup;
|
|
}
|
|
} else if (ERROR_SUCCESS == Win32Err){
|
|
DsRoleFreeMemory(ServerInfo);
|
|
ServerInfo = NULL;
|
|
} else {
|
|
DsRolepLogOnFailure( Win32Err,
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"DsRoleGetPrimaryDomainInformation failed: %lu\n",
|
|
Win32Err )) );
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Verify the path names we are given
|
|
//
|
|
DsRolepLogPrint(( DEB_TRACE,"Validate supplied paths\n" ));
|
|
Win32Err = DsRolepCheckFilePaths( DsDatabasePath,
|
|
DsLogPath,
|
|
SystemVolumeRootPath );
|
|
if ( ERROR_SUCCESS != Win32Err ) {
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// If we are doing a parent/child setup, verify our name
|
|
//
|
|
if ( ParentDnsDomainName &&
|
|
!FLAG_ON( Options, DSROLE_DC_TRUST_AS_ROOT ) ) {
|
|
|
|
DsRolepLogPrint(( DEB_TRACE, "Child domain creation -- check the new domain name is child of parent domain name.\n" ));
|
|
|
|
Win32Err = DsRolepIsDnsNameChild( ParentDnsDomainName, DnsDomainName );
|
|
if ( ERROR_SUCCESS != Win32Err ) {
|
|
|
|
DsRolepLogOnFailure( Win32Err,
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"Verifying the child domain dns name failed: %lu\n",
|
|
Win32Err )) );
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Validate the netbios domain name is not in use
|
|
//
|
|
DsRolepLogPrint(( DEB_TRACE,"Domain Creation -- check that the flat name is unique.\n" ));
|
|
|
|
Win32Err = NetpValidateName( NULL,
|
|
FlatDomainName,
|
|
NULL,
|
|
NULL,
|
|
NetSetupNonExistentDomain );
|
|
|
|
if ( FLAG_ON( Options, DSROLE_DC_NO_NET )
|
|
&& (Win32Err == ERROR_NETWORK_UNREACHABLE)) {
|
|
|
|
//
|
|
// See NT bug 386193. This option allows a first DC in forest
|
|
// to installed with no network (useful for evaluation)
|
|
//
|
|
DsRolepLogPrint(( DEB_TRACE,"Ignoring network unreachable status\n" ));
|
|
|
|
Win32Err = ERROR_SUCCESS;
|
|
}
|
|
|
|
if ( Win32Err != ERROR_SUCCESS ) {
|
|
|
|
DsRolepLogOnFailure( Win32Err,
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"Flat name validation of %ws failed with %lu\n",
|
|
FlatDomainName,
|
|
Win32Err )) );
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
// No workstations or domain controllers allowed
|
|
|
|
Win32Err = DsRolepGetMachineType( &MachineRole );
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
switch ( MachineRole ) {
|
|
case DSROLEP_MT_CLIENT:
|
|
case DSROLEP_MT_MEMBER:
|
|
|
|
Win32Err = ERROR_INVALID_SERVER_STATE;
|
|
break;
|
|
|
|
}
|
|
}
|
|
if ( Win32Err != ERROR_SUCCESS ) {
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,"This operation is not valid on a workstation or domain controller\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// At this point, we are good to go
|
|
//
|
|
DsRolepLogPrint(( DEB_TRACE,"Start the worker task\n" ));
|
|
|
|
Win32Err = DsRolepDecryptPasswordsWithKey ( RpcBindingHandle,
|
|
EncryptedPasswords,
|
|
NELEMENTS(EncryptedPasswords),
|
|
Passwords,
|
|
NULL,
|
|
&Seed );
|
|
|
|
if ( ERROR_SUCCESS != Win32Err ) {
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// If everything is fine, go ahead and do the setup
|
|
//
|
|
Win32Err = DsRolepBuildPromoteArgumentBlock( DnsDomainName,
|
|
FlatDomainName,
|
|
SiteName,
|
|
DsDatabasePath,
|
|
DsLogPath,
|
|
NULL,
|
|
SystemVolumeRootPath,
|
|
NULL,
|
|
ParentDnsDomainName,
|
|
ParentServer,
|
|
Account,
|
|
&Passwords[DSROLEP_DC_AS_DC_PWD_INDEX],
|
|
&Passwords[DSROLEP_DC_AS_DC_DA_PWD_INDEX],
|
|
&Passwords[DSROLEP_DC_AS_DC_DS_REPAIR_PWD_INDEX],
|
|
Options,
|
|
Seed,
|
|
&PromoteArgs );
|
|
|
|
DsRolepFreePasswords( Passwords,
|
|
NELEMENTS(Passwords) );
|
|
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
Win32Err = DsRolepSpinWorkerThread( DSROLEP_OPERATION_DC,
|
|
( PVOID )PromoteArgs );
|
|
|
|
//
|
|
// Once the thread has started, no more errors can occur in this
|
|
// function
|
|
//
|
|
if ( Win32Err != ERROR_SUCCESS ) {
|
|
|
|
DsRolepFreeArgumentBlock( &PromoteArgs, TRUE );
|
|
}
|
|
}
|
|
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
*DsOperationHandle = (DSROLER_HANDLE)&DsRolepCurrentOperationHandle;
|
|
|
|
}
|
|
|
|
//
|
|
// That's it
|
|
//
|
|
|
|
Cleanup:
|
|
|
|
// Always reset to a known state
|
|
if ( ERROR_SUCCESS != Win32Err && fHandleInit )
|
|
{
|
|
DsRolepResetOperationHandle( DSROLEP_IDLE );
|
|
}
|
|
|
|
if ( ServerInfo ) {
|
|
DsRoleFreeMemory(ServerInfo);
|
|
ServerInfo = NULL;
|
|
}
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,"Request for promotion returning %lu\n", Win32Err ));
|
|
|
|
return( Win32Err );
|
|
}
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
DsRolerDcAsReplica(
|
|
IN handle_t RpcBindingHandle,
|
|
IN LPWSTR DnsDomainName,
|
|
IN LPWSTR ReplicaPartner,
|
|
IN LPWSTR SiteName OPTIONAL,
|
|
IN LPWSTR DsDatabasePath,
|
|
IN LPWSTR DsLogPath,
|
|
IN LPWSTR RestorePath OPTIONAL,
|
|
IN LPWSTR SystemVolumeRootPath,
|
|
IN PDSROLEPR_ENCRYPTED_HASH lpEncryptedBootkey,
|
|
IN LPWSTR Account,
|
|
IN PDSROLEPR_ENCRYPTED_USER_PASSWORD EPassword,
|
|
IN PDSROLEPR_ENCRYPTED_USER_PASSWORD EDsRepairPassword,
|
|
IN ULONG Options,
|
|
OUT PDSROLER_HANDLE DsOperationHandle)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Rpc server routine for installing a server a replica in an existing domain
|
|
|
|
Arguments:
|
|
|
|
RpcBindingHandle - the RPC context, used to decrypt the passwords
|
|
|
|
DnsDomainName - Dns domain name of the domain to install into
|
|
|
|
ReplicaPartner - The name of a Dc within the existing domain, against which to replicate
|
|
|
|
SiteName - Name of the site this DC should belong to
|
|
|
|
DsDatabasePath - Absolute path on the local machine where the Ds DIT should go
|
|
|
|
DsLogPath - Absolute path on the local machine where the Ds log files should go
|
|
|
|
SystemVolumeRootPath - Absolute path on the local machine which will be the root of
|
|
the system volume.
|
|
|
|
BootKey - needed for media installs where the password is not in the registry or on a disk
|
|
|
|
cbBootKey - size of the bootkey
|
|
|
|
Account - User account to use when setting up as a child domain
|
|
|
|
EPassword - Encrypted password to use with the above account
|
|
|
|
EDsRepairPassword - Encrypted 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.
|
|
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS - Success
|
|
|
|
ERROR_INVALID_PARAMETER - A NULL return parameter was given
|
|
|
|
ERROR_INVALID_STATE - This machine is not a server
|
|
|
|
--*/
|
|
{
|
|
DWORD Win32Err = ERROR_SUCCESS;
|
|
DSROLEP_MACHINE_TYPE MachineRole;
|
|
PDSROLEP_OPERATION_PROMOTE_ARGS PromoteArgs;
|
|
USER_SESSION_KEY UserSessionKey;
|
|
PUNICODE_STRING lpBootkey = NULL;
|
|
UCHAR Seed;
|
|
BOOL fLostRace;
|
|
|
|
#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];
|
|
UNICODE_STRING Passwords[DSROLEP_DC_AS_REPLICA_MAX_PWD_COUNT];
|
|
|
|
EncryptedPasswords[DSROLEP_DC_AS_REPLICA_PWD_INDEX] = EPassword;
|
|
EncryptedPasswords[DSROLEP_DC_AS_REPLICA_DS_REPAIR_PWD_INDEX] = EDsRepairPassword;
|
|
|
|
RtlZeroMemory( Passwords, sizeof(Passwords) );
|
|
|
|
//
|
|
// Do some parameter checking
|
|
//
|
|
if ( !DnsDomainName
|
|
|| !DsDatabasePath
|
|
|| !DsLogPath
|
|
|| !SystemVolumeRootPath
|
|
) {
|
|
|
|
return( ERROR_INVALID_PARAMETER );
|
|
|
|
}
|
|
|
|
//
|
|
// UI Enforces MAX_PATH as the max length.
|
|
//
|
|
if ( ( wcslen(DsDatabasePath) > MAX_PATH) ||
|
|
( wcslen(DsLogPath) > MAX_PATH) ||
|
|
( (RestorePath && (wcslen(RestorePath) > MAX_PATH)) ) ||
|
|
( wcslen(SystemVolumeRootPath) > MAX_PATH) )
|
|
{
|
|
return( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// Do our necessary initializations
|
|
//
|
|
Win32Err = DsRolepInitializeOperationHandle( );
|
|
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
Win32Err = DsRolepGetMachineType( &MachineRole );
|
|
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
switch ( MachineRole ) {
|
|
case DSROLEP_MT_CLIENT:
|
|
case DSROLEP_MT_MEMBER:
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,"This operation is not valid on a workstation or domain controller\n" ));
|
|
Win32Err = ERROR_INVALID_SERVER_STATE;
|
|
break;
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
Win32Err = DsRolepCheckPromoteAccess( TRUE );
|
|
if ( ERROR_SUCCESS != Win32Err ) {
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// Init the logging
|
|
//
|
|
DsRolepInitializeLog();
|
|
|
|
|
|
//
|
|
// Check that the current OS configuration supports this request
|
|
//
|
|
Win32Err = DsRolepIsValidProductSuite(FALSE,
|
|
TRUE,
|
|
DnsDomainName);
|
|
if ( ERROR_SUCCESS != Win32Err ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"Promotion request for replica domain controller\n" ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"DnsDomainName %ws\n",
|
|
DnsDomainName ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tReplicaPartner %ws\n",
|
|
DsRolepDisplayOptional( ReplicaPartner ) ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tSiteName %ws\n",
|
|
DsRolepDisplayOptional( SiteName ) ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tDsDatabasePath %ws, DsLogPath %ws\n",
|
|
DsDatabasePath, DsLogPath ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tSystemVolumeRootPath %ws\n",
|
|
SystemVolumeRootPath ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tAccount %ws\n",
|
|
DsRolepDisplayOptional(Account) ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tOptions %lu\n",
|
|
Options ));
|
|
|
|
//
|
|
// Verify the path names we are given
|
|
//
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,"Validate supplied paths\n" ));
|
|
Win32Err = DsRolepCheckFilePaths( DsDatabasePath,
|
|
DsLogPath,
|
|
SystemVolumeRootPath );
|
|
if ( ERROR_SUCCESS != Win32Err ) {
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
if (RestorePath && Win32Err == ERROR_SUCCESS) {
|
|
|
|
ASSERT(DsRolepCurrentOperationHandle.OperationState != DSROLEP_IDLE);
|
|
|
|
// Grab the lock for the IFM info before Init Op handle.
|
|
fLostRace = InterlockedCompareExchange(&(DsRolepCurrentIfmOperationHandle.fIfmOpHandleLock),
|
|
TRUE,
|
|
FALSE);
|
|
|
|
if (fLostRace ||
|
|
!DsRolepCurrentIfmOperationHandle.fIfmSystemInfoSet ||
|
|
wcscmp(RestorePath, DsRolepCurrentIfmOperationHandle.IfmSystemInfo.wszRestorePath)) {
|
|
// Something changed, the data (restore path) isn't the same as when
|
|
// we initialized the handle for this restore operation! Or we just
|
|
// lost some sort of race, Dcpromo shouldn't be putting us in.
|
|
ASSERT(!"inconsistency in model used to communicated with dcpromo.exe and lsasrv.dll");
|
|
Win32Err = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
if (!fLostRace && Win32Err) {
|
|
// We grabbed the lock but there was an error, so release
|
|
// the lock, because we're not promoting.
|
|
DsRolepCurrentIfmOperationHandle.fIfmOpHandleLock = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
Win32Err = DsRolepDecryptPasswordsWithKey ( RpcBindingHandle,
|
|
EncryptedPasswords,
|
|
NELEMENTS(EncryptedPasswords),
|
|
Passwords,
|
|
&UserSessionKey,
|
|
&Seed );
|
|
|
|
if ( Win32Err == ERROR_SUCCESS && lpEncryptedBootkey ) {
|
|
|
|
Win32Err = DsRolepDecryptHash(&UserSessionKey,
|
|
lpEncryptedBootkey,
|
|
&lpBootkey);
|
|
|
|
}
|
|
|
|
//
|
|
// If everything is fine, go ahead and do the setup
|
|
//
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,"Start the worker task\n" ));
|
|
|
|
Win32Err = DsRolepBuildPromoteArgumentBlock( DnsDomainName,
|
|
NULL,
|
|
SiteName,
|
|
DsDatabasePath,
|
|
DsLogPath,
|
|
RestorePath ? &(DsRolepCurrentIfmOperationHandle.IfmSystemInfo) : NULL,
|
|
SystemVolumeRootPath,
|
|
lpBootkey,
|
|
NULL,
|
|
ReplicaPartner,
|
|
Account,
|
|
&Passwords[DSROLEP_DC_AS_REPLICA_PWD_INDEX],
|
|
NULL,
|
|
&Passwords[DSROLEP_DC_AS_REPLICA_DS_REPAIR_PWD_INDEX],
|
|
Options,
|
|
Seed,
|
|
&PromoteArgs );
|
|
|
|
DsRolepFreePasswords( Passwords,
|
|
NELEMENTS(Passwords) );
|
|
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
Win32Err = DsRolepSpinWorkerThread( DSROLEP_OPERATION_REPLICA,
|
|
( PVOID )PromoteArgs );
|
|
|
|
if ( Win32Err != ERROR_SUCCESS ) {
|
|
|
|
DsRolepFreeArgumentBlock( &PromoteArgs, TRUE );
|
|
}
|
|
}
|
|
|
|
if ( Win32Err != ERROR_SUCCESS ) {
|
|
|
|
DsRolepResetOperationHandle( DSROLEP_IDLE );
|
|
}
|
|
}
|
|
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
*DsOperationHandle = (DSROLER_HANDLE)&DsRolepCurrentOperationHandle;
|
|
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,"Request for promotion returning %lu\n", Win32Err ));
|
|
|
|
return( Win32Err );
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
DsRolerDemoteDc(
|
|
IN handle_t RpcBindingHandle,
|
|
IN LPWSTR DnsDomainName OPTIONAL,
|
|
IN DSROLE_SERVEROP_DEMOTE_ROLE ServerRole,
|
|
IN LPWSTR Account OPTIONAL,
|
|
IN PDSROLEPR_ENCRYPTED_USER_PASSWORD EPassword,
|
|
IN ULONG Options,
|
|
IN BOOL LastDcInDomain,
|
|
IN ULONG cRemoveNCs,
|
|
IN LPCWSTR * pszRemoveNCs,
|
|
IN PDSROLEPR_ENCRYPTED_USER_PASSWORD EDomainAdminPassword,
|
|
OUT PDSROLER_HANDLE DsOperationHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Rpc server routine for demoting a dc to a server
|
|
|
|
Arguments:
|
|
|
|
RpcBindingHandle - the RPC context, used to decrypt the passwords
|
|
|
|
DnsDomainName - Dns domain name of the domain to be demoted. Null means all of the supported
|
|
domain names
|
|
|
|
ServerRole - The new role this machine should take
|
|
|
|
Account - OPTIONAL User account to use when deleting the trusted domain object
|
|
|
|
EPassword - Encrypted password to use with the above account
|
|
|
|
Options - Options to control the demotion of the domain
|
|
|
|
LastDcInDomain - If TRUE, the Dc being demoted 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
|
|
|
|
EDomainAdminPassword - Encrypted password to set on the administrator account if it is a new install
|
|
|
|
DsOperationHandle - Handle to the operation is returned here.
|
|
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS - Success
|
|
|
|
ERROR_INVALID_PARAMETER - A NULL return parameter was given
|
|
|
|
ERROR_INVALID_STATE - This machine is not a server
|
|
|
|
--*/
|
|
{
|
|
DSROLEP_MACHINE_TYPE MachineRole;
|
|
DWORD Win32Err;
|
|
PDSROLEP_OPERATION_DEMOTE_ARGS DemoteArgs;
|
|
UCHAR Seed;
|
|
BOOL HandleInit = FALSE;
|
|
|
|
#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];
|
|
UNICODE_STRING Passwords[DSROLEP_DEMOTE_MAX_PWD_COUNT];
|
|
|
|
EncryptedPasswords[DSROLEP_DEMOTE_PWD_INDEX] = EPassword;
|
|
EncryptedPasswords[DSROLEP_DEMOTE_ADMIN_PWD_INDEX] = EDomainAdminPassword;
|
|
|
|
RtlZeroMemory( Passwords, sizeof(Passwords) );
|
|
|
|
if ( (LastDcInDomain && (DsRoleServerMember == ServerRole))
|
|
|| ( (!LastDcInDomain && (DsRoleServerStandalone == ServerRole)) && !(Options&DSROLE_DC_FORCE_DEMOTE) )
|
|
|| ((Options&DSROLE_DC_FORCE_DEMOTE) && (DsRoleServerMember == ServerRole)) ){
|
|
|
|
//
|
|
// These configurations are not supported
|
|
//
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ( (pszRemoveNCs == NULL) && (cRemoveNCs != 0) )
|
|
{
|
|
//
|
|
// cRemoveNCs must be zero if pszRemoveNCs is NULL
|
|
//
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Do our necessary initializations
|
|
//
|
|
Win32Err = DsRolepInitializeOperationHandle( );
|
|
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
HandleInit = TRUE;
|
|
|
|
Win32Err = DsRolepGetMachineType( &MachineRole );
|
|
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
switch ( MachineRole ) {
|
|
case DSROLEP_MT_CLIENT:
|
|
case DSROLEP_MT_STANDALONE:
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,"This operation is only valid on a domain controller\n" ));
|
|
Win32Err = ERROR_INVALID_SERVER_STATE;
|
|
goto Cleanup;
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check the access right for demote
|
|
//
|
|
Win32Err = DsRolepCheckDemoteAccess( TRUE );
|
|
if ( ERROR_SUCCESS != Win32Err ) {
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// Initialize return value to NULL
|
|
//
|
|
|
|
*DsOperationHandle = NULL;
|
|
|
|
DsRolepInitializeLog();
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"Request for demotion of domain controller\n" ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"DnsDomainName %ws\n",
|
|
DsRolepDisplayOptional( DnsDomainName ) ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tServerRole %lu\n",
|
|
ServerRole ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tAccount %ws ",
|
|
DsRolepDisplayOptional( Account ) ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tOptions %lu\n",
|
|
Options ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tLastDcInDomain %S\n",
|
|
LastDcInDomain ? "TRUE" : "FALSE" ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tForced Demote %S\n",
|
|
(Options&DSROLE_DC_FORCE_DEMOTE) ? "TRUE" : "FALSE" ));
|
|
|
|
Win32Err = DsRolepDecryptPasswordsWithKey ( RpcBindingHandle,
|
|
EncryptedPasswords,
|
|
NELEMENTS(EncryptedPasswords),
|
|
Passwords,
|
|
NULL,
|
|
&Seed );
|
|
|
|
|
|
//
|
|
// Spawn the demotion thread
|
|
//
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,"Start the worker task\n" ));
|
|
|
|
Win32Err = DsRolepBuildDemoteArgumentBlock( ServerRole,
|
|
DnsDomainName,
|
|
Account,
|
|
&Passwords[DSROLEP_DEMOTE_PWD_INDEX],
|
|
Options,
|
|
( BOOLEAN )LastDcInDomain,
|
|
cRemoveNCs,
|
|
(LPWSTR *) pszRemoveNCs,
|
|
&Passwords[DSROLEP_DEMOTE_ADMIN_PWD_INDEX],
|
|
Seed,
|
|
&DemoteArgs );
|
|
|
|
DsRolepFreePasswords( Passwords,
|
|
NELEMENTS(Passwords) );
|
|
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
Win32Err = DsRolepSpinWorkerThread( DSROLEP_OPERATION_DEMOTE,
|
|
( PVOID )DemoteArgs );
|
|
|
|
if ( Win32Err != ERROR_SUCCESS ) {
|
|
|
|
DsRolepFreeArgumentBlock( &DemoteArgs, FALSE );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
*DsOperationHandle = (DSROLER_HANDLE)&DsRolepCurrentOperationHandle;
|
|
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if ( ( Win32Err != ERROR_SUCCESS ) && HandleInit ) {
|
|
|
|
DsRolepResetOperationHandle( DSROLEP_IDLE );
|
|
|
|
}
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,"Request for demotion returning %lu\n", Win32Err ));
|
|
|
|
return( Win32Err );
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
DsRolerGetDcOperationProgress(
|
|
IN PDSROLE_SERVER_NAME Server,
|
|
IN PDSROLER_HANDLE DsOperationHandle,
|
|
IN OUT PDSROLER_SERVEROP_STATUS *ServerOperationStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Rpc server routine for determining the present state of the operation
|
|
|
|
Arguments:
|
|
|
|
Server - Server the call was remoted to
|
|
|
|
DsOperationHandle - Handle returned from a previous call
|
|
|
|
ServerOperationStatus - Where the status information is returned
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS - Success
|
|
|
|
ERROR_INVALID_HANDLE - A bad Operation handle was supplied
|
|
|
|
--*/
|
|
{
|
|
DWORD Win32Err = ERROR_SUCCESS;
|
|
|
|
__try {
|
|
|
|
if ( DsOperationHandle == NULL ||
|
|
*DsOperationHandle != ( DSROLER_HANDLE )&DsRolepCurrentOperationHandle) {
|
|
|
|
Win32Err = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
} __except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
Win32Err = GetExceptionCode();
|
|
}
|
|
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
Win32Err = DsRolepGetDcOperationProgress( ( PDSROLE_SERVEROP_HANDLE )DsOperationHandle,
|
|
ServerOperationStatus );
|
|
}
|
|
|
|
return( Win32Err );
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
DsRolerGetDcOperationResults(
|
|
IN PDSROLE_SERVER_NAME Server,
|
|
IN PDSROLER_HANDLE DsOperationHandle,
|
|
OUT PDSROLER_SERVEROP_RESULTS *ServerOperationResults
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Rpc server routine for determining the final results of the operation. If the operation
|
|
is not yet completed, this function will block until it does complete.
|
|
|
|
Arguments:
|
|
|
|
Server - Server the call was remoted to
|
|
|
|
DsOperationHandle - Handle returned from a previous call
|
|
|
|
ServerOperationResults - Where the final operation results are returned.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS - Success
|
|
|
|
ERROR_INVALID_HANDLE - A bad Operation handle was supplied
|
|
|
|
--*/
|
|
{
|
|
DWORD Win32Err = ERROR_SUCCESS;
|
|
|
|
__try {
|
|
|
|
if ( DsOperationHandle == NULL ||
|
|
*DsOperationHandle != ( DSROLER_HANDLE )&DsRolepCurrentOperationHandle) {
|
|
|
|
Win32Err = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
} __except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
Win32Err = GetExceptionCode();
|
|
}
|
|
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
|
|
Win32Err = DsRolepGetDcOperationResults( ( PDSROLE_SERVEROP_HANDLE )DsOperationHandle,
|
|
ServerOperationResults );
|
|
|
|
DsRolepCloseLog();
|
|
}
|
|
|
|
|
|
return( Win32Err );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
DsRolerDnsNameToFlatName(
|
|
IN LPWSTR Server OPTIONAL,
|
|
IN LPWSTR DnsName,
|
|
OUT LPWSTR *FlatName,
|
|
OUT PULONG StatusFlag
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Rpc server routine for determining the default flat (netbios) domain name for the given
|
|
Dns domain name
|
|
|
|
Arguments:
|
|
|
|
Server - Server the call was remoted to
|
|
|
|
DnsName - Dns name to convert
|
|
|
|
FlatName - Where the flat name is returned. Alocated via MIDL_user_allocate
|
|
|
|
StatusFlag - Where the status flag is returned
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS - Success
|
|
|
|
ERROR_INVALID_PARAMETER - A bad input or NULL return parameter was given
|
|
--*/
|
|
{
|
|
DWORD Win32Err = ERROR_SUCCESS;
|
|
|
|
if ( DnsName == NULL || FlatName == NULL || StatusFlag == NULL ) {
|
|
|
|
return( ERROR_INVALID_PARAMETER );
|
|
|
|
}
|
|
|
|
Win32Err = DsRolepDnsNameToFlatName( DnsName,
|
|
FlatName,
|
|
StatusFlag );
|
|
|
|
return( Win32Err );
|
|
}
|
|
|
|
|
|
#define GET_PDI_COPY_STRING_AND_INSERT( _unicode_, _buffer_ ) \
|
|
if ( ( _unicode_)->Length == 0 ) { \
|
|
\
|
|
( _buffer_ ) = NULL; \
|
|
\
|
|
} else { \
|
|
\
|
|
( _buffer_ ) = MIDL_user_allocate( (_unicode_)->Length + sizeof( WCHAR ) ); \
|
|
if ( ( _buffer_ ) == NULL ) { \
|
|
\
|
|
Win32Err = ERROR_NOT_ENOUGH_MEMORY; \
|
|
goto GetInfoError; \
|
|
\
|
|
} else { \
|
|
\
|
|
BuffersToFree[ BuffersCnt++ ] = ( PBYTE )( _buffer_ ); \
|
|
RtlCopyMemory( ( _buffer_ ), \
|
|
( _unicode_ )->Buffer, \
|
|
( _unicode_ )->Length ); \
|
|
( _buffer_ )[ ( _unicode_ )->Length / sizeof( WCHAR ) ] = UNICODE_NULL; \
|
|
} \
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
DsRolerGetDatabaseFacts(
|
|
IN handle_t RpcBindingHandle,
|
|
IN LPWSTR lpRestorePath,
|
|
OUT LPWSTR *lpDNSDomainName,
|
|
OUT PULONG State,
|
|
OUT DSROLER_IFM_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:
|
|
|
|
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;
|
|
DWORD fLostRace;
|
|
DWORD cbSize;
|
|
|
|
//
|
|
// 1) Check parameters
|
|
//
|
|
Win32Err = DsRolepCheckPromoteAccess( FALSE );
|
|
if ( ERROR_SUCCESS != Win32Err ) {
|
|
return Win32Err;
|
|
}
|
|
|
|
if( lpDNSDomainName == NULL ||
|
|
IsBadWritePtr(lpDNSDomainName, sizeof(LPWSTR*)) ||
|
|
State == NULL ||
|
|
IsBadWritePtr(State, sizeof(DWORD)) ||
|
|
pIfmHandle == NULL ||
|
|
IsBadWritePtr(pIfmHandle, sizeof(DSROLER_IFM_HANDLE))
|
|
){
|
|
ASSERT(!"inconsistency in model used to communicated with dcpromo.exe and lsasrv.dll");
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
// Get the IFM Handle lock.
|
|
fLostRace = InterlockedCompareExchange(&(DsRolepCurrentIfmOperationHandle.fIfmOpHandleLock),
|
|
TRUE,
|
|
FALSE);
|
|
if (fLostRace ||
|
|
DsRolepCurrentIfmOperationHandle.fIfmSystemInfoSet) {
|
|
// Either we lost the race for the handle, or someone didn't
|
|
// free before resetting the IFM sys info,in all cases we fail.
|
|
ASSERT(!"inconsistency in model used to communicated with dcpromo.exe and lsasrv.dll");
|
|
DsRolepLogPrint(( DEB_ERROR, "Couldn't get the IFM Handle lock during GetDbFacts().\n"));
|
|
Win32Err = ERROR_INVALID_PARAMETER;
|
|
if (!fLostRace) {
|
|
// Won race, but erroring out.
|
|
DsRolepCurrentIfmOperationHandle.fIfmOpHandleLock = FALSE;
|
|
}
|
|
return(Win32Err);
|
|
}
|
|
|
|
//
|
|
// 2) Get IFM System Info
|
|
//
|
|
// This sets up the Ifm operation context with all the valueable
|
|
// information from the IFM system's registry.
|
|
//
|
|
Win32Err = DsRolepGetDatabaseFacts(lpRestorePath);
|
|
|
|
//
|
|
// 3) Set the out parameters
|
|
//
|
|
// If successful, the DsRolepCurrentIfmOperationHandle is setup.
|
|
//
|
|
if ( ERROR_SUCCESS == Win32Err ) {
|
|
|
|
DsRolepCurrentIfmOperationHandle.fIfmSystemInfoSet = TRUE;
|
|
|
|
// ASSERT( lpDNSDomainName && State...
|
|
cbSize = wcslen(DsRolepCurrentIfmOperationHandle.IfmSystemInfo.wszDnsDomainName) + 1;
|
|
cbSize *= sizeof(WCHAR);
|
|
*lpDNSDomainName = MIDL_user_allocate(cbSize);
|
|
if (*lpDNSDomainName == NULL) {
|
|
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
} else {
|
|
wcscpy(*lpDNSDomainName, DsRolepCurrentIfmOperationHandle.IfmSystemInfo.wszDnsDomainName);
|
|
*State = DsRolepCurrentIfmOperationHandle.IfmSystemInfo.dwState;
|
|
// Success, set IfmHandle out param ...
|
|
*pIfmHandle = (DSROLER_IFM_HANDLE) &DsRolepCurrentIfmOperationHandle;
|
|
|
|
}
|
|
|
|
}
|
|
DsRolepCurrentIfmOperationHandle.fIfmOpHandleLock = FALSE;
|
|
|
|
|
|
return(Win32Err);
|
|
}
|
|
|
|
|
|
DWORD
|
|
DsRolerIfmHandleFree(
|
|
IN PDSROLE_SERVER_NAME Server,
|
|
IN DSROLER_IFM_HANDLE * pIfmHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free's the server side allocated context information for an IFM install.
|
|
|
|
Arguments:
|
|
|
|
Server - Server to remote the call to
|
|
|
|
IfmHandle - Handle of currently IFM system info. Returned by
|
|
DsRolerGetDatabaseFacts().
|
|
|
|
|
|
Return Values:
|
|
|
|
Win32 Error.
|
|
|
|
--*/
|
|
{
|
|
DWORD Win32Err = ERROR_SUCCESS;
|
|
BOOL fLostRace;
|
|
|
|
Win32Err = DsRolepCheckPromoteAccess( FALSE );
|
|
if ( ERROR_SUCCESS != Win32Err ) {
|
|
return Win32Err;
|
|
}
|
|
|
|
if ( pIfmHandle == NULL ||
|
|
IsBadWritePtr(pIfmHandle, sizeof (DSROLER_IFM_HANDLE *)) ||
|
|
*pIfmHandle != (( DSROLER_IFM_HANDLE )&DsRolepCurrentIfmOperationHandle)) {
|
|
ASSERT(!"inconsistency in model used to communicated with dcpromo.exe and lsasrv.dll");
|
|
Win32Err = ERROR_INVALID_HANDLE;
|
|
return(Win32Err);
|
|
}
|
|
|
|
// We can clear the current state, as long as the handle itself isn't
|
|
// locked, and we're not currently in an install operation consuming
|
|
// the IFM data.
|
|
fLostRace = InterlockedCompareExchange(&(DsRolepCurrentIfmOperationHandle.fIfmOpHandleLock),
|
|
TRUE,
|
|
FALSE);
|
|
if (fLostRace ||
|
|
!DsRolepCurrentIfmOperationHandle.fIfmSystemInfoSet) {
|
|
// Either we lost the race for the handle, or there is no
|
|
// data set, either way we fail and assert.
|
|
ASSERT(!"inconsistency in model used to communicated with dcpromo.exe and lsasrv.dll");
|
|
DsRolepLogPrint(( DEB_ERROR, "Couldn't get the IFM Handle lock during Free.\n"));
|
|
Win32Err = ERROR_INVALID_HANDLE;
|
|
if (!fLostRace) {
|
|
// Won race, but erroring out.
|
|
DsRolepCurrentIfmOperationHandle.fIfmOpHandleLock = FALSE;
|
|
}
|
|
return(Win32Err);
|
|
}
|
|
|
|
DsRolepCurrentIfmOperationHandle.fIfmSystemInfoSet = FALSE;
|
|
|
|
DsRolepClearIfmParams();
|
|
|
|
*pIfmHandle = NULL;
|
|
|
|
DsRolepCurrentIfmOperationHandle.fIfmOpHandleLock = FALSE;
|
|
|
|
return( Win32Err );
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
DsRolerGetPrimaryDomainInformation(
|
|
IN PDSROLE_SERVER_NAME Server,
|
|
IN DSROLE_PRIMARY_DOMAIN_INFO_LEVEL InfoLevel,
|
|
OUT PDSROLER_PRIMARY_DOMAIN_INFORMATION *DomainInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine the principal name to use for authenticated Rpc
|
|
|
|
Arguments:
|
|
|
|
Server - Server the call was remoted to
|
|
|
|
ServerPrincipal - Where the server principal name is returned
|
|
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS - Success
|
|
|
|
ERROR_INVALID_PARAMETER - A NULL return parameter was given
|
|
|
|
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
|
|
|
|
--*/
|
|
{
|
|
DWORD Win32Err = ERROR_SUCCESS;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PBYTE BuffersToFree[ 6 ];
|
|
ULONG BuffersCnt = 0, i = 0;
|
|
PDSROLER_PRIMARY_DOMAIN_INFO_BASIC BasicInfo = NULL;
|
|
PDSROLE_UPGRADE_STATUS_INFO Upgrade = NULL;
|
|
PDSROLE_OPERATION_STATE_INFO OperationStateInfo = 0;
|
|
BOOLEAN IsUpgrade = FALSE;
|
|
ULONG PreviousServerRole = 0;
|
|
PPOLICY_LSA_SERVER_ROLE_INFO ServerRole = NULL;
|
|
PPOLICY_DNS_DOMAIN_INFO CurrentDnsInfo = NULL;
|
|
PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomainInfo = NULL;
|
|
LSAPR_HANDLE PolicyHandle = NULL;
|
|
LSAPR_OBJECT_ATTRIBUTES PolicyObject;
|
|
GUID NullGuid;
|
|
|
|
memset( &NullGuid, 0, sizeof(GUID) );
|
|
#define IS_GUID_PRESENT(x) (memcmp(&(x), &NullGuid, sizeof(GUID)))
|
|
|
|
if ( DomainInfo == NULL ) {
|
|
|
|
return( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// This particular interface cannot be called until the Lsa and Sam are fully initialized.
|
|
// As such, we will have to wait until they are..
|
|
//
|
|
if ( !DsRolepSamInitialized ) {
|
|
|
|
Win32Err = DsRolepWaitForSam();
|
|
if ( Win32Err != ERROR_SUCCESS ) {
|
|
|
|
return( Win32Err );
|
|
}
|
|
|
|
DsRolepSamInitialized = TRUE;
|
|
}
|
|
|
|
switch ( InfoLevel ) {
|
|
case DsRolePrimaryDomainInfoBasic:
|
|
|
|
BasicInfo = MIDL_user_allocate( sizeof( DSROLER_PRIMARY_DOMAIN_INFO_BASIC ) );
|
|
|
|
if ( BasicInfo == NULL ) {
|
|
|
|
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto GetInfoError;
|
|
|
|
} else {
|
|
|
|
BuffersToFree[ BuffersCnt++ ] = ( PBYTE )BasicInfo;
|
|
}
|
|
|
|
//
|
|
// Open a handle to the policy and ensure the callers has access to read it
|
|
//
|
|
|
|
RtlZeroMemory(&PolicyObject, sizeof(PolicyObject));
|
|
|
|
Status = LsarOpenPolicy(
|
|
NULL, // Local LSA
|
|
&PolicyObject,
|
|
POLICY_VIEW_LOCAL_INFORMATION,
|
|
&PolicyHandle );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
Win32Err = RtlNtStatusToDosError( Status );
|
|
goto GetInfoError;
|
|
}
|
|
|
|
|
|
//
|
|
// Get the current information
|
|
//
|
|
Status = LsarQueryInformationPolicy(
|
|
PolicyHandle,
|
|
PolicyDnsDomainInformationInt,
|
|
(PLSAPR_POLICY_INFORMATION *) &CurrentDnsInfo );
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
//
|
|
// Get the machine role
|
|
//
|
|
switch ( LsapProductType ) {
|
|
case NtProductWinNt:
|
|
if ( CurrentDnsInfo->Sid == NULL ) {
|
|
|
|
BasicInfo->MachineRole = DsRole_RoleStandaloneWorkstation;
|
|
|
|
} else {
|
|
|
|
BasicInfo->MachineRole = DsRole_RoleMemberWorkstation;
|
|
|
|
}
|
|
break;
|
|
|
|
case NtProductServer:
|
|
if ( CurrentDnsInfo->Sid == NULL ) {
|
|
|
|
BasicInfo->MachineRole = DsRole_RoleStandaloneServer;
|
|
|
|
} else {
|
|
|
|
BasicInfo->MachineRole = DsRole_RoleMemberServer;
|
|
|
|
}
|
|
break;
|
|
|
|
case NtProductLanManNt:
|
|
|
|
Status = LsarQueryInformationPolicy(
|
|
PolicyHandle,
|
|
PolicyLsaServerRoleInformation,
|
|
(PLSAPR_POLICY_INFORMATION *) &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 = LsarQueryInformationPolicy(
|
|
PolicyHandle,
|
|
PolicyAccountDomainInformation,
|
|
(PLSAPR_POLICY_INFORMATION *) &AccountDomainInfo );
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
|
|
if ( CurrentDnsInfo->Sid == NULL ||
|
|
AccountDomainInfo->DomainSid == NULL ||
|
|
!RtlEqualSid( AccountDomainInfo->DomainSid,
|
|
CurrentDnsInfo->Sid ) ) {
|
|
|
|
BasicInfo->MachineRole = DsRole_RoleStandaloneServer;
|
|
|
|
} else {
|
|
|
|
BasicInfo->MachineRole = DsRole_RolePrimaryDomainController;
|
|
|
|
}
|
|
LsaIFree_LSAPR_POLICY_INFORMATION(
|
|
PolicyAccountDomainInformation,
|
|
( PLSAPR_POLICY_INFORMATION )AccountDomainInfo );
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
BasicInfo->MachineRole = DsRole_RoleBackupDomainController;
|
|
}
|
|
|
|
LsaIFree_LSAPR_POLICY_INFORMATION( PolicyLsaServerRoleInformation,
|
|
( PLSAPR_POLICY_INFORMATION )ServerRole );
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Now, build the rest of the information
|
|
//
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
|
|
if ( LsapDsIsRunning ) {
|
|
|
|
BasicInfo->Flags = DSROLE_PRIMARY_DS_RUNNING;
|
|
|
|
Status = DsRolepGetMixedModeFlags( CurrentDnsInfo->Sid, &( BasicInfo->Flags ) );
|
|
|
|
} else {
|
|
|
|
BasicInfo->Flags = 0;
|
|
|
|
}
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
//
|
|
// Flat name
|
|
//
|
|
GET_PDI_COPY_STRING_AND_INSERT( &CurrentDnsInfo->Name, BasicInfo->DomainNameFlat );
|
|
|
|
//
|
|
// Dns domain name
|
|
//
|
|
GET_PDI_COPY_STRING_AND_INSERT( &CurrentDnsInfo->DnsDomainName, BasicInfo->DomainNameDns );
|
|
|
|
//
|
|
// Dns tree name
|
|
//
|
|
GET_PDI_COPY_STRING_AND_INSERT( &CurrentDnsInfo->DnsForestName, BasicInfo->DomainForestName );
|
|
|
|
//
|
|
// Finally, the Guid.
|
|
//
|
|
if ( IS_GUID_PRESENT(CurrentDnsInfo->DomainGuid) ) {
|
|
|
|
RtlCopyMemory( &BasicInfo->DomainGuid,
|
|
&CurrentDnsInfo->DomainGuid,
|
|
sizeof( GUID ) );
|
|
|
|
BasicInfo->Flags |= DSROLE_PRIMARY_DOMAIN_GUID_PRESENT;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
*DomainInfo = ( PDSROLER_PRIMARY_DOMAIN_INFORMATION )BasicInfo;
|
|
BuffersCnt = 0;
|
|
|
|
} else {
|
|
|
|
Win32Err = RtlNtStatusToDosError( Status );
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
case DsRoleUpgradeStatus:
|
|
|
|
Win32Err = DsRolepQueryUpgradeInfo( &IsUpgrade,
|
|
&PreviousServerRole );
|
|
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
Upgrade = MIDL_user_allocate( sizeof( DSROLE_UPGRADE_STATUS_INFO ) );
|
|
|
|
if ( Upgrade == NULL ) {
|
|
|
|
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto GetInfoError;
|
|
|
|
} else {
|
|
|
|
BuffersToFree[ BuffersCnt++ ] = ( PBYTE )Upgrade;
|
|
|
|
//
|
|
// Now, build the information
|
|
//
|
|
if ( IsUpgrade ) {
|
|
|
|
Upgrade->OperationState = DSROLE_UPGRADE_IN_PROGRESS;
|
|
|
|
switch ( PreviousServerRole ) {
|
|
case PolicyServerRoleBackup:
|
|
Upgrade->PreviousServerState = DsRoleServerBackup;
|
|
break;
|
|
|
|
case PolicyServerRolePrimary:
|
|
Upgrade->PreviousServerState = DsRoleServerPrimary;
|
|
break;
|
|
|
|
default:
|
|
|
|
Win32Err = ERROR_INVALID_SERVER_STATE;
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
RtlZeroMemory( Upgrade, sizeof( DSROLE_UPGRADE_STATUS_INFO ) );
|
|
}
|
|
|
|
//
|
|
// Make sure to return the values if we should
|
|
//
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
*DomainInfo = ( PDSROLER_PRIMARY_DOMAIN_INFORMATION )Upgrade;
|
|
BuffersCnt = 0;
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DsRoleOperationState:
|
|
|
|
OperationStateInfo = MIDL_user_allocate( sizeof( DSROLE_OPERATION_STATE_INFO ) );
|
|
|
|
if ( OperationStateInfo == NULL ) {
|
|
|
|
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto GetInfoError;
|
|
|
|
}
|
|
|
|
if ( RtlAcquireResourceExclusive( &DsRolepCurrentOperationHandle.CurrentOpLock, TRUE ) ) {
|
|
|
|
DsRoleDebugOut(( DEB_TRACE_LOCK,
|
|
"Lock grabbed in DsRolerGetPrimaryDomainInformation\n"));
|
|
|
|
if ( DSROLEP_OPERATION_ACTIVE( DsRolepCurrentOperationHandle.OperationState ) ) {
|
|
|
|
OperationStateInfo->OperationState = DsRoleOperationActive;
|
|
|
|
} else if ( DSROLEP_IDLE == DsRolepCurrentOperationHandle.OperationState ) {
|
|
|
|
OperationStateInfo->OperationState = DsRoleOperationIdle;
|
|
|
|
} else {
|
|
|
|
ASSERT( DSROLEP_NEED_REBOOT == DsRolepCurrentOperationHandle.OperationState );
|
|
|
|
//
|
|
// If the assert isn't true, then we are very confused and should probably
|
|
// indicate we need a reboot.
|
|
//
|
|
OperationStateInfo->OperationState = DsRoleOperationNeedReboot;
|
|
}
|
|
|
|
RtlReleaseResource( &DsRolepCurrentOperationHandle.CurrentOpLock );
|
|
DsRoleDebugOut(( DEB_TRACE_LOCK, "Lock released\n" ));
|
|
|
|
//
|
|
// Set the out param
|
|
//
|
|
*DomainInfo = ( PDSROLER_PRIMARY_DOMAIN_INFORMATION )OperationStateInfo;
|
|
|
|
} else {
|
|
|
|
Win32Err = ERROR_BUSY;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
Win32Err = ERROR_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
GetInfoError:
|
|
|
|
if ( CurrentDnsInfo != NULL ) {
|
|
|
|
LsaIFree_LSAPR_POLICY_INFORMATION( PolicyDnsDomainInformation,
|
|
( PLSAPR_POLICY_INFORMATION )CurrentDnsInfo );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Free any buffers that we may have allocated
|
|
//
|
|
for ( i = 0; i < BuffersCnt; i++ ) {
|
|
|
|
MIDL_user_free( BuffersToFree[ i ] );
|
|
}
|
|
|
|
if ( PolicyHandle != NULL ) {
|
|
LsarClose( &PolicyHandle );
|
|
}
|
|
|
|
return( Win32Err );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
DsRolerCancel(
|
|
IN PDSROLE_SERVER_NAME Server,
|
|
IN PDSROLER_HANDLE DsOperationHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cancels a currently running operation
|
|
|
|
Arguments:
|
|
|
|
Server - 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;
|
|
|
|
__try {
|
|
|
|
if ( DsOperationHandle == NULL ||
|
|
*DsOperationHandle != ( DSROLER_HANDLE )&DsRolepCurrentOperationHandle) {
|
|
|
|
Win32Err = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
} __except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
Win32Err = GetExceptionCode();
|
|
}
|
|
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
Win32Err = DsRolepCancel( TRUE ); // Block until done
|
|
|
|
}
|
|
|
|
return( Win32Err );
|
|
}
|
|
|
|
|
|
DWORD
|
|
DsRolerServerSaveStateForUpgrade(
|
|
IN PDSROLE_SERVER_NAME Server,
|
|
IN LPWSTR AnswerFile 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:
|
|
|
|
AnswerFile -- Optional path to an answer file to be used by DCPROMO during the subsequent
|
|
invocation
|
|
|
|
|
|
Return Values:
|
|
|
|
ERROR_SUCCESS - Success
|
|
|
|
--*/
|
|
{
|
|
DWORD Win32Err = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Check the access of the caller
|
|
//
|
|
// N.B another access check would be to check that this is GUI mode
|
|
// setup, but checking for admin is safer. It works because setup.exe
|
|
// runs under local system which has builtin\administrators in its
|
|
// token.
|
|
//
|
|
Win32Err = DsRolepCheckPromoteAccess( FALSE );
|
|
if ( ERROR_SUCCESS != Win32Err ) {
|
|
|
|
return Win32Err;
|
|
|
|
}
|
|
|
|
(VOID) DsRolepInitializeLog();
|
|
|
|
Win32Err = DsRolepSaveUpgradeState( AnswerFile );
|
|
|
|
return( Win32Err );
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
DsRolerUpgradeDownlevelServer(
|
|
IN handle_t RpcBindingHandle,
|
|
IN LPWSTR DnsDomainName,
|
|
IN LPWSTR SiteName,
|
|
IN LPWSTR DsDatabasePath,
|
|
IN LPWSTR DsLogPath,
|
|
IN LPWSTR SystemVolumeRootPath,
|
|
IN LPWSTR ParentDnsDomainName OPTIONAL,
|
|
IN LPWSTR ParentServer OPTIONAL,
|
|
IN LPWSTR Account OPTIONAL,
|
|
IN PDSROLEPR_ENCRYPTED_USER_PASSWORD EPassword,
|
|
IN PDSROLEPR_ENCRYPTED_USER_PASSWORD EDsRepairPassword,
|
|
IN ULONG Options,
|
|
OUT PDSROLER_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:
|
|
|
|
RpcBindingHandle - the RPC context, used to decrypt the passwords
|
|
|
|
DnsDomainName - Dns domain name of the domain to install
|
|
|
|
SiteName - Name of the site this DC should belong to
|
|
|
|
DsDatabasePath - Absolute path on the local machine where the Ds DIT should go
|
|
|
|
DsLogPath - Absolute path on the local machine where the Ds log files should go
|
|
|
|
SystemVolumeRootPath - Absolute path on the local machine which will be the root of
|
|
the system volume.
|
|
|
|
ParentDnsDomainName - Optional. If present, set this domain up as a child of the
|
|
specified domain
|
|
|
|
ParentServer - Optional. If present, use this server in the parent domain to replicate
|
|
the required information from
|
|
|
|
Account - User account to use when contacting other servers
|
|
|
|
EPassword - Encrypted password to use with the above account
|
|
|
|
EDsRepairPassword - Encrypted 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
|
|
ERROR_INVALID_SERVER_STATE - Not in upgrade mode
|
|
|
|
--*/
|
|
{
|
|
DWORD Win32Err = ERROR_SUCCESS;
|
|
BOOLEAN IsUpgrade;
|
|
ULONG PreviousServerState;
|
|
PDSROLEP_OPERATION_PROMOTE_ARGS PromoteArgs;
|
|
NTSTATUS Status;
|
|
HANDLE LocalPolicy = NULL;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
PPOLICY_DNS_DOMAIN_INFO PrimaryDomainInfo = NULL;
|
|
UCHAR Seed = 0;
|
|
|
|
#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];
|
|
UNICODE_STRING Passwords[DSROLEP_UPGRADE_MAX_PWD_COUNT];
|
|
|
|
EncryptedPasswords[DSROLEP_UPGRADE_PWD_INDEX] = EPassword;
|
|
EncryptedPasswords[DSROLEP_UPGRADE_DS_REPAIR_PWD_INDEX] = EDsRepairPassword;
|
|
|
|
RtlZeroMemory( Passwords, sizeof(Passwords) );
|
|
|
|
*DsOperationHandle = NULL;
|
|
|
|
//
|
|
// Do some parameter checking
|
|
//
|
|
if ( !DnsDomainName || !DsDatabasePath || !DsLogPath || !SystemVolumeRootPath ) {
|
|
|
|
Win32Err = ERROR_INVALID_PARAMETER;
|
|
goto DsRolepUpgradeError;
|
|
|
|
}
|
|
|
|
Win32Err = DsRolepInitializeOperationHandle( );
|
|
if (ERROR_SUCCESS != Win32Err) {
|
|
|
|
goto DsRolepUpgradeError;
|
|
|
|
}
|
|
|
|
//
|
|
// Check the access of the caller
|
|
//
|
|
Win32Err = DsRolepCheckPromoteAccess( TRUE );
|
|
if ( ERROR_SUCCESS != Win32Err ) {
|
|
|
|
goto DsRolepUpgradeError;
|
|
|
|
}
|
|
|
|
DsRolepInitializeLog();
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"DsRolerDcAsDc: DnsDomainName %ws\n",
|
|
DnsDomainName ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tSiteName %ws\n",
|
|
DsRolepDisplayOptional( SiteName ) ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tSystemVolumeRootPath %ws\n",
|
|
SystemVolumeRootPath ));
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tDsDatabasePath %ws, DsLogPath %ws\n",
|
|
DsDatabasePath, DsLogPath ));
|
|
|
|
if ( ParentDnsDomainName ) {
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tParentDnsDomainName %ws\n",
|
|
ParentDnsDomainName ));
|
|
|
|
}
|
|
|
|
if ( ParentServer ) {
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tParentServer %ws\n",
|
|
ParentServer ));
|
|
|
|
}
|
|
|
|
if ( Account ) {
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tAccount %ws\n",
|
|
Account ));
|
|
}
|
|
|
|
DsRolepLogPrint(( DEB_TRACE,
|
|
"\tOptions %lu\n",
|
|
Options ));
|
|
|
|
Win32Err = DsRolepQueryUpgradeInfo( &IsUpgrade, &PreviousServerState );
|
|
|
|
if ( Win32Err != ERROR_SUCCESS ) {
|
|
|
|
goto DsRolepUpgradeError;
|
|
}
|
|
|
|
if ( !IsUpgrade || PreviousServerState == PolicyServerRoleBackup ) {
|
|
|
|
Win32Err = ERROR_INVALID_SERVER_STATE;
|
|
goto DsRolepUpgradeError;
|
|
|
|
}
|
|
|
|
//
|
|
// Verify the path names we are given
|
|
//
|
|
DsRolepLogPrint(( DEB_TRACE,"Validate supplied paths\n" ));
|
|
Win32Err = DsRolepCheckFilePaths( DsDatabasePath,
|
|
DsLogPath,
|
|
SystemVolumeRootPath );
|
|
if ( ERROR_SUCCESS != Win32Err ) {
|
|
|
|
goto DsRolepUpgradeError;
|
|
|
|
}
|
|
|
|
//
|
|
// If we are doing a parent/child setup, verify our name
|
|
//
|
|
if ( Win32Err == ERROR_SUCCESS && ParentDnsDomainName &&
|
|
!FLAG_ON( Options, DSROLE_DC_TRUST_AS_ROOT ) ) {
|
|
|
|
Win32Err = DsRolepIsDnsNameChild( ParentDnsDomainName, DnsDomainName );
|
|
}
|
|
|
|
//
|
|
// Get the current domain name
|
|
//
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
RtlZeroMemory( &ObjectAttributes, sizeof( ObjectAttributes ) );
|
|
|
|
Status = LsaOpenPolicy( NULL,
|
|
&ObjectAttributes,
|
|
MAXIMUM_ALLOWED,
|
|
&LocalPolicy );
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
Status = LsaQueryInformationPolicy( LocalPolicy,
|
|
PolicyPrimaryDomainInformation,
|
|
&PrimaryDomainInfo );
|
|
|
|
LsaClose( LocalPolicy );
|
|
}
|
|
|
|
Win32Err = RtlNtStatusToDosError( Status );
|
|
|
|
}
|
|
|
|
Win32Err = DsRolepDecryptPasswordsWithKey ( RpcBindingHandle,
|
|
EncryptedPasswords,
|
|
NELEMENTS(EncryptedPasswords),
|
|
Passwords,
|
|
NULL,
|
|
&Seed );
|
|
|
|
//
|
|
// Finally, we'll do the promotion
|
|
//
|
|
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
Win32Err = DsRolepBuildPromoteArgumentBlock( DnsDomainName,
|
|
PrimaryDomainInfo->Name.Buffer,
|
|
SiteName,
|
|
DsDatabasePath,
|
|
DsLogPath,
|
|
NULL,
|
|
SystemVolumeRootPath,
|
|
NULL,
|
|
ParentDnsDomainName,
|
|
ParentServer,
|
|
Account,
|
|
&Passwords[DSROLEP_UPGRADE_PWD_INDEX],
|
|
NULL,
|
|
&Passwords[DSROLEP_UPGRADE_DS_REPAIR_PWD_INDEX],
|
|
Options | DSROLE_DC_DOWNLEVEL_UPGRADE,
|
|
Seed,
|
|
&PromoteArgs );
|
|
|
|
}
|
|
|
|
DsRolepFreePasswords( Passwords,
|
|
NELEMENTS(Passwords) );
|
|
|
|
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
Win32Err = DsRolepSpinWorkerThread( DSROLEP_OPERATION_DC,
|
|
( PVOID )PromoteArgs );
|
|
|
|
if ( Win32Err != ERROR_SUCCESS ) {
|
|
|
|
DsRolepFreeArgumentBlock( &PromoteArgs, TRUE );
|
|
}
|
|
}
|
|
|
|
if ( Win32Err != ERROR_SUCCESS ) {
|
|
|
|
DsRolepResetOperationHandle( DSROLEP_IDLE );
|
|
}
|
|
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
*DsOperationHandle = (DSROLER_HANDLE)&DsRolepCurrentOperationHandle;
|
|
|
|
}
|
|
|
|
LsaFreeMemory( PrimaryDomainInfo );
|
|
|
|
|
|
DsRolepUpgradeError:
|
|
|
|
return( Win32Err );
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
DsRolerAbortDownlevelServerUpgrade(
|
|
IN handle_t RpcBindingHandle,
|
|
IN LPWSTR Account, OPTIONAL
|
|
IN PDSROLEPR_ENCRYPTED_USER_PASSWORD EAccountPassword,
|
|
IN PDSROLEPR_ENCRYPTED_USER_PASSWORD EAdminPassword,
|
|
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:
|
|
|
|
RpcBindingHandle - the RPC context, used to decrypt the passwords
|
|
|
|
Account - User account to use when contacting other servers
|
|
|
|
EPassword - Encrypted password to use with the above account
|
|
|
|
EAdminPassword - Encrypted new local administrator account password
|
|
|
|
Options - Options to control the behavior. Currently support flags are:
|
|
DSROLEP_ABORT_FOR_REPLICA_INSTALL - The upgrade is being aborted to do a replica install
|
|
|
|
Return Values:
|
|
|
|
ERROR_SUCCESS - Success
|
|
ERROR_INVALID_PARAMETER - An invalid machine role was specified
|
|
|
|
--*/
|
|
{
|
|
DWORD Win32Err = ERROR_SUCCESS, Win32Err2;
|
|
PDOMAIN_CONTROLLER_INFO DomainControllerInfo = NULL;
|
|
BOOLEAN AccountInfoSet = FALSE, Impersonated = FALSE;
|
|
UCHAR Seed = 0;
|
|
UNICODE_STRING EPassword, EPassword2;
|
|
WCHAR *OldAccountDn = NULL;
|
|
WCHAR SecurityLogPath[MAX_PATH+1];
|
|
PUNICODE_STRING Password = NULL;
|
|
PUNICODE_STRING AdminPassword = NULL;
|
|
HANDLE ClientToken = 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];
|
|
UNICODE_STRING Passwords[DSROLEP_ABORT_MAX_PWD_COUNT];
|
|
|
|
EncryptedPasswords[DSROLEP_ABORT_PWD_INDEX] = EAccountPassword;
|
|
EncryptedPasswords[DSROLEP_ABORT_ADMIN_PWD_INDEX] = EAdminPassword;
|
|
RtlZeroMemory( Passwords, sizeof(Passwords) );
|
|
|
|
EPassword.Buffer = NULL;
|
|
EPassword2.Buffer = NULL;
|
|
|
|
//
|
|
// Initialize the operation handle so we pull in the dynamically
|
|
// loaded libraries
|
|
//
|
|
Win32Err = DsRolepInitializeOperationHandle( );
|
|
|
|
if ( Win32Err != ERROR_SUCCESS ) {
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
Win32Err = DsRolepCheckPromoteAccess( FALSE );
|
|
if ( ERROR_SUCCESS != Win32Err ) {
|
|
|
|
goto Exit;
|
|
|
|
}
|
|
|
|
Win32Err = DsRolepDecryptPasswordsWithKey ( RpcBindingHandle,
|
|
EncryptedPasswords,
|
|
NELEMENTS(EncryptedPasswords),
|
|
Passwords,
|
|
NULL,
|
|
&Seed );
|
|
|
|
if ( Win32Err != ERROR_SUCCESS ) {
|
|
|
|
goto Exit;
|
|
|
|
}
|
|
RtlCopyMemory( &EPassword, &Passwords[DSROLEP_ABORT_ADMIN_PWD_INDEX], sizeof(UNICODE_STRING));
|
|
RtlCopyMemory( &EPassword2, &Passwords[DSROLEP_ABORT_PWD_INDEX], sizeof(UNICODE_STRING));
|
|
|
|
DsRolepInitializeLog();
|
|
|
|
if ( FLAG_ON( Options, DSROLEP_ABORT_FOR_REPLICA_INSTALL ) )
|
|
{
|
|
//
|
|
// This is the NT4 to NT5 BDC upgrade. Nothing to do
|
|
//
|
|
//
|
|
DsRolepLogPrint(( DEB_TRACE, "Performing NT4 to NT5 BDC upgrade.\n"));
|
|
Win32Err = ERROR_SUCCESS;
|
|
goto Exit;
|
|
|
|
}
|
|
|
|
Win32Err = DsRolepGetImpersonationToken( &ClientToken );
|
|
if (ERROR_SUCCESS != Win32Err) {
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// First, find the Dc that has this account
|
|
//
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
Win32Err = DsRolepDsGetDcForAccount( NULL,
|
|
NULL,
|
|
NULL,
|
|
DS_DIRECTORY_SERVICE_REQUIRED |
|
|
DS_WRITABLE_REQUIRED |
|
|
DS_FORCE_REDISCOVERY |
|
|
DS_AVOID_SELF,
|
|
UF_SERVER_TRUST_ACCOUNT,
|
|
&DomainControllerInfo );
|
|
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// Set the machine account type
|
|
//
|
|
|
|
DsRolepLogPrint(( DEB_TRACE, "Searching for the machine account ...\n"));
|
|
|
|
RtlRunDecodeUnicodeString( Seed, &EPassword2 );
|
|
Win32Err = DsRolepSetMachineAccountType( DomainControllerInfo->DomainControllerName,
|
|
ClientToken,
|
|
Account,
|
|
EPassword2.Buffer,
|
|
NULL,
|
|
UF_WORKSTATION_TRUST_ACCOUNT,
|
|
&OldAccountDn );
|
|
RtlRunEncodeUnicodeString( &Seed, &EPassword2 );
|
|
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
AccountInfoSet = TRUE;
|
|
|
|
} else {
|
|
|
|
DsRolepLogPrint(( DEB_TRACE, "DsRolepSetMachineAccountType returned %d\n",
|
|
Win32Err ));
|
|
}
|
|
|
|
if ( OldAccountDn ) {
|
|
|
|
// the machine object was moved
|
|
DsRolepLogPrint(( DEB_TRACE, "Moved account %ws to %ws\n",
|
|
Account,
|
|
OldAccountDn ));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if ( ERROR_SUCCESS != Win32Err ) {
|
|
|
|
goto Exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Set the security for a freshly installed NT5 server. See bug 391574
|
|
//
|
|
|
|
DsRolepLogPrint(( DEB_TRACE, "Setting security for server ...\n"));
|
|
|
|
#define SECURITY_SRV_INF_FILE L"defltsv.inf"
|
|
|
|
ZeroMemory( SecurityLogPath, (MAX_PATH+1)*sizeof(WCHAR) );
|
|
if ( GetWindowsDirectory( SecurityLogPath, MAX_PATH ) )
|
|
{
|
|
//Ensure that the last Char is L'\0'
|
|
SecurityLogPath[MAX_PATH] = L'\0'; // -1 to ensure last char is not overwritten
|
|
wcsncat( SecurityLogPath, L"\\security\\logs\\scesetup.log", ((sizeof(SecurityLogPath)/sizeof(WCHAR))-wcslen(SecurityLogPath)-1) );
|
|
|
|
Win32Err = DsrSceSetupSystemByInfName(SECURITY_SRV_INF_FILE,
|
|
SecurityLogPath,
|
|
AREA_FILE_SECURITY | AREA_REGISTRY_SECURITY,
|
|
SCESETUP_CONFIGURE_SECURITY,
|
|
NULL, // used only for GUI mode
|
|
NULL ); // used only for GUI mode
|
|
|
|
} else {
|
|
|
|
Win32Err = GetLastError();
|
|
|
|
}
|
|
|
|
if ( ERROR_SUCCESS != Win32Err ) {
|
|
|
|
DsRolepLogOnFailure( Win32Err,
|
|
DsRolepLogPrint(( DEB_ERROR,
|
|
"Setting security on server files failed with %lu\n",
|
|
Win32Err )) );
|
|
|
|
// This error has been handled
|
|
Win32Err = ERROR_SUCCESS;
|
|
}
|
|
|
|
DsRolepLogPrint(( DEB_TRACE, "Setting security for server finished\n"));
|
|
|
|
|
|
|
|
//
|
|
// Change the user password
|
|
//
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
RtlRunDecodeUnicodeString( Seed, &EPassword );
|
|
Win32Err = DsRolepSetBuiltinAdminAccountPassword( EPassword.Buffer );
|
|
RtlRunEncodeUnicodeString( &Seed, &EPassword );
|
|
|
|
//
|
|
// Delete the upgrade information
|
|
//
|
|
if ( Win32Err == ERROR_SUCCESS ) {
|
|
|
|
Win32Err = DsRolepDeleteUpgradeInfo();
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If that failed, try and restore the machine account info
|
|
//
|
|
if ( Win32Err != ERROR_SUCCESS && AccountInfoSet ) {
|
|
|
|
RtlRunDecodeUnicodeString( Seed, &EPassword2 );
|
|
Win32Err2 = DsRolepSetMachineAccountType( DomainControllerInfo->DomainControllerName,
|
|
ClientToken,
|
|
Account,
|
|
EPassword2.Buffer,
|
|
NULL,
|
|
UF_SERVER_TRUST_ACCOUNT,
|
|
&OldAccountDn ); //don't care about dn
|
|
RtlRunEncodeUnicodeString( &Seed, &EPassword2 );
|
|
|
|
if ( Win32Err2 != ERROR_SUCCESS ) {
|
|
|
|
DsRolepLogPrint(( DEB_TRACE, "DsRolepSetMachineAccountType returned %d\n", Win32Err2 ));
|
|
|
|
} else {
|
|
|
|
if ( OldAccountDn ) {
|
|
|
|
//
|
|
// the machine object was moved back
|
|
//
|
|
DsRolepLogPrint(( DEB_TRACE, "Attempted to move account %ws to %ws\n",
|
|
Account,
|
|
OldAccountDn ));
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
DsRolepFreePasswords( Passwords,
|
|
NELEMENTS(Passwords) );
|
|
|
|
NetApiBufferFree( DomainControllerInfo );
|
|
|
|
if ( OldAccountDn ) {
|
|
|
|
RtlFreeHeap( RtlProcessHeap(), 0, OldAccountDn );
|
|
}
|
|
|
|
if (ClientToken) {
|
|
CloseHandle(ClientToken);
|
|
}
|
|
|
|
(VOID) DsRolepResetOperationHandle( DSROLEP_IDLE );
|
|
|
|
return( Win32Err );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// Local function definitions
|
|
//
|
|
DWORD
|
|
DsRolepWaitForSam(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine waits for the SAM_SERVICE_STARTED event
|
|
|
|
Arguments:
|
|
|
|
VOID
|
|
Return Values:
|
|
|
|
ERROR_SUCCESS - Success
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
UNICODE_STRING EventName;
|
|
OBJECT_ATTRIBUTES EventAttributes;
|
|
HANDLE EventHandle = NULL;
|
|
|
|
//
|
|
// Open the event
|
|
//
|
|
RtlInitUnicodeString( &EventName, L"\\SAM_SERVICE_STARTED" );
|
|
InitializeObjectAttributes( &EventAttributes, &EventName, 0, 0, NULL );
|
|
|
|
Status = NtCreateEvent( &EventHandle,
|
|
SYNCHRONIZE,
|
|
&EventAttributes,
|
|
NotificationEvent,
|
|
FALSE );
|
|
|
|
|
|
//
|
|
// If the event already exists, just open it.
|
|
//
|
|
if( Status == STATUS_OBJECT_NAME_EXISTS || Status == STATUS_OBJECT_NAME_COLLISION ) {
|
|
|
|
Status = NtOpenEvent( &EventHandle,
|
|
SYNCHRONIZE,
|
|
&EventAttributes );
|
|
}
|
|
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
Status = NtWaitForSingleObject( EventHandle, TRUE, NULL );
|
|
|
|
NtClose( EventHandle );
|
|
}
|
|
|
|
|
|
|
|
return( RtlNtStatusToDosError( Status ) );
|
|
}
|
|
|
|
DWORD
|
|
DsRolepCheckFilePaths(
|
|
IN LPWSTR DsDatabasePath,
|
|
IN LPWSTR DsLogPath,
|
|
IN LPWSTR SystemVolumeRootPath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS - Success
|
|
|
|
--*/
|
|
{
|
|
DWORD WinError = ERROR_SUCCESS;
|
|
ULONG VerifyOptions, VerifyResults;
|
|
ULONG Length;
|
|
|
|
//
|
|
// Make sure that niether the log path nor the datapath path
|
|
// is a subset of the SystemVolumeRootPath
|
|
//
|
|
Length = wcslen( SystemVolumeRootPath );
|
|
if ( !_wcsnicmp( SystemVolumeRootPath, DsDatabasePath, Length )
|
|
|| !_wcsnicmp( SystemVolumeRootPath, DsLogPath, Length ) ) {
|
|
|
|
DsRolepLogPrint(( DEB_TRACE, "Database paths subset of sysvol\n" ));
|
|
|
|
WinError = ERROR_BAD_PATHNAME;
|
|
|
|
}
|
|
|
|
if ( WinError == ERROR_SUCCESS ) {
|
|
|
|
VerifyOptions = DSROLEP_PATH_VALIDATE_LOCAL | DSROLEP_PATH_VALIDATE_EXISTENCE;
|
|
WinError = DsRolepValidatePath( DsDatabasePath, VerifyOptions, &VerifyResults );
|
|
|
|
if ( WinError == ERROR_SUCCESS ) {
|
|
|
|
if ( VerifyResults != VerifyOptions ) {
|
|
|
|
WinError = ERROR_BAD_PATHNAME;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( WinError == ERROR_SUCCESS ) {
|
|
|
|
WinError = DsRolepValidatePath( DsLogPath, VerifyOptions, &VerifyResults );
|
|
|
|
if ( WinError == ERROR_SUCCESS ) {
|
|
|
|
if ( VerifyResults != VerifyOptions ) {
|
|
|
|
WinError = ERROR_BAD_PATHNAME;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( WinError == ERROR_SUCCESS ) {
|
|
|
|
VerifyOptions = DSROLEP_PATH_VALIDATE_LOCAL | DSROLEP_PATH_VALIDATE_NTFS;
|
|
WinError = DsRolepValidatePath( SystemVolumeRootPath, VerifyOptions, &VerifyResults );
|
|
|
|
if ( WinError == ERROR_SUCCESS ) {
|
|
|
|
if ( VerifyResults != VerifyOptions ) {
|
|
|
|
WinError = ERROR_BAD_PATHNAME;
|
|
}
|
|
}
|
|
}
|
|
|
|
return WinError;
|
|
}
|
|
|
|
BOOL
|
|
IsProductSuiteConfigured(
|
|
WORD Suite
|
|
)
|
|
{
|
|
|
|
OSVERSIONINFOEXA osvi;
|
|
DWORDLONG dwlConditionMask = 0;
|
|
|
|
//
|
|
// Setup the request for the desired suite
|
|
//
|
|
ZeroMemory(&osvi, sizeof(osvi));
|
|
osvi.dwOSVersionInfoSize = sizeof(osvi);
|
|
osvi.wSuiteMask = Suite;
|
|
|
|
//
|
|
// Setup the condition
|
|
//
|
|
VER_SET_CONDITION(dwlConditionMask, VER_SUITENAME, VER_AND);
|
|
|
|
return VerifyVersionInfoA(&osvi,
|
|
VER_SUITENAME,
|
|
dwlConditionMask);
|
|
|
|
}
|
|
|
|
BOOL
|
|
IsWebBlade(
|
|
VOID
|
|
)
|
|
{
|
|
return IsProductSuiteConfigured(VER_SUITE_BLADE);
|
|
}
|
|
|
|
BOOL
|
|
IsSBS(
|
|
VOID
|
|
)
|
|
{
|
|
return IsProductSuiteConfigured(VER_SUITE_SMALLBUSINESS_RESTRICTED);
|
|
}
|
|
|
|
DWORD
|
|
DsRolepIsValidProductSuite(
|
|
IN BOOL fNewForest,
|
|
IN BOOL fReplica,
|
|
IN LPWSTR DomainName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines if the promotion request is valid for the
|
|
current configuration of the OS.
|
|
|
|
Arguments:
|
|
|
|
fNewForest -- a new forest is requested.
|
|
|
|
fReplica -- a replica is requested
|
|
|
|
DomainName -- the name of the domain to create or join
|
|
|
|
Return Values:
|
|
|
|
ERROR_SUCCESS, ERROR_NOT_SUPPORTED, resource errors otherwise
|
|
|
|
--*/
|
|
{
|
|
DWORD err = ERROR_SUCCESS;
|
|
PDOMAIN_CONTROLLER_INFOW DCInfo = NULL;
|
|
|
|
if (IsWebBlade()) {
|
|
// See Windows RAID issue 195265
|
|
err = ERROR_NOT_SUPPORTED;
|
|
goto Exit;
|
|
}
|
|
|
|
if (IsSBS()) {
|
|
|
|
if (fReplica) {
|
|
|
|
err = DsGetDcNameW(NULL,
|
|
DomainName,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
&DCInfo);
|
|
if (ERROR_SUCCESS != err) {
|
|
|
|
// Return the resource or configuration error
|
|
DsRolepLogPrint((DEB_ERROR,
|
|
"Request to find a DC for %ws failed (%d)\n",
|
|
DomainName,
|
|
err));
|
|
goto Exit;
|
|
}
|
|
|
|
if ( !(DnsNameCompareEqual == DnsNameCompareEx_W(DomainName,
|
|
DCInfo->DnsForestName,
|
|
0 ))) {
|
|
// See Windows issue 373388
|
|
// Must be the root of the forest
|
|
err = ERROR_NOT_SUPPORTED;
|
|
goto Exit;
|
|
}
|
|
|
|
} else if (!fNewForest) {
|
|
|
|
// See Windows NT issue 353854
|
|
err = ERROR_NOT_SUPPORTED;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (DCInfo) {
|
|
NetApiBufferFree(DCInfo);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
DsRolepDecryptPasswordsWithKey(
|
|
IN handle_t RpcBindingHandle,
|
|
IN PDSROLEPR_ENCRYPTED_USER_PASSWORD * EncryptedPasswords,
|
|
IN ULONG Count,
|
|
IN OUT UNICODE_STRING *EncodedPasswords,
|
|
OUT OPTIONAL PUSER_SESSION_KEY pUserSessionKey,
|
|
OUT PUCHAR Seed
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Decrypts a set of passwords encrypted with the user session key.
|
|
|
|
Arguments:
|
|
|
|
RpcBindingHandle - Rpc Binding handle describing the session key to use.
|
|
|
|
EncryptedPasswords - Encrypted passwords to decrypt.
|
|
|
|
Count - the number of passwords
|
|
|
|
EncodedPassword - Returns the Encoded password.
|
|
The password has been encoded
|
|
Buffer should be freed using LocalFree.
|
|
|
|
UserSessionKey - the Key used for decryption
|
|
|
|
Seed - the seed that was used to encode the password
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS; a resource or parameter error otherwise
|
|
|
|
--*/
|
|
{
|
|
DWORD WinError = ERROR_SUCCESS;
|
|
NTSTATUS Status;
|
|
USER_SESSION_KEY UserSessionKey;
|
|
RC4_KEYSTRUCT Rc4Key;
|
|
MD5_CTX Md5Context;
|
|
|
|
LPWSTR PasswordPart;
|
|
|
|
ULONG i;
|
|
|
|
//
|
|
// Get the session key
|
|
//
|
|
Status = RtlGetUserSessionKeyServer(
|
|
(RPC_BINDING_HANDLE)RpcBindingHandle,
|
|
&UserSessionKey );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return RtlNtStatusToDosError( Status );
|
|
}
|
|
//
|
|
// Return the Key if requested
|
|
//
|
|
if (pUserSessionKey) {
|
|
CopyMemory(pUserSessionKey, &UserSessionKey, sizeof(UserSessionKey));
|
|
}
|
|
|
|
for ( i = 0; i < Count; i++ ) {
|
|
|
|
PDSROLEPR_USER_PASSWORD Password = (PDSROLEPR_USER_PASSWORD) EncryptedPasswords[i];
|
|
LPWSTR ClearPassword;
|
|
|
|
//
|
|
// Handle the trivial case
|
|
//
|
|
RtlInitUnicodeString( &EncodedPasswords[i], NULL );
|
|
if ( Password == NULL ) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// 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, Password->Obfuscator, sizeof(Password->Obfuscator) );
|
|
|
|
MD5Final( &Md5Context );
|
|
|
|
rc4_key( &Rc4Key, MD5DIGESTLEN, Md5Context.digest );
|
|
|
|
|
|
//
|
|
// Decrypt the Buffer
|
|
//
|
|
|
|
rc4( &Rc4Key, sizeof(Password->Buffer)+sizeof(Password->Length), (LPBYTE) Password->Buffer );
|
|
|
|
//
|
|
// Check that the length is valid. If it isn't bail here.
|
|
//
|
|
|
|
if (Password->Length > DSROLE_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) {
|
|
WinError = ERROR_INVALID_PASSWORD;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Return the password to the caller.
|
|
//
|
|
|
|
ClearPassword = LocalAlloc( 0, Password->Length + sizeof(WCHAR) );
|
|
|
|
if ( ClearPassword == NULL ) {
|
|
WinError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Copy the password into the buffer
|
|
//
|
|
RtlCopyMemory( ClearPassword,
|
|
((PCHAR) Password->Buffer) +
|
|
(DSROLE_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) -
|
|
Password->Length,
|
|
Password->Length );
|
|
|
|
ClearPassword[Password->Length/sizeof(WCHAR)] = L'\0';
|
|
|
|
RtlInitUnicodeString( &EncodedPasswords[i], ClearPassword );
|
|
|
|
//
|
|
// Now encode it
|
|
//
|
|
RtlRunEncodeUnicodeString( Seed, &EncodedPasswords[i] );
|
|
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if ( WinError != ERROR_SUCCESS ) {
|
|
|
|
for ( i = 0; i < Count; i++ ) {
|
|
if ( EncodedPasswords[i].Buffer ) {
|
|
LocalFree( EncodedPasswords[i].Buffer );
|
|
RtlInitUnicodeString( &EncodedPasswords[i], NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
return WinError;
|
|
}
|
|
|
|
|
|
VOID
|
|
DsRolepFreePasswords(
|
|
IN OUT UNICODE_STRING *Passwords,
|
|
IN ULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Frees the variables returned from DsRolepDecryptPasswordsWithKey
|
|
|
|
Arguments:
|
|
|
|
Passwords - the encoded passwords to free
|
|
|
|
Count - the number of passwords
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS; a resource or parameter error otherwise
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
|
|
for ( i = 0; i < Count; i++ ) {
|
|
|
|
if ( Passwords[i].Buffer ) {
|
|
|
|
RtlSecureZeroMemory( Passwords[i].Buffer, Passwords[i].Length );
|
|
LocalFree( Passwords[i].Buffer );
|
|
RtlInitUnicodeString( &Passwords[i], NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
DsRolepDecryptHash(
|
|
IN PUSER_SESSION_KEY UserSessionKey,
|
|
IN PDSROLEPR_ENCRYPTED_HASH EncryptedBootkey,
|
|
OUT PUNICODE_STRING *Bootkey
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Decrypts the Boot key
|
|
|
|
Arguments:
|
|
|
|
pUserSessionKey - Key used to encrypt the key
|
|
|
|
Bootkey - The key to decrypt
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS; a resource or parameter error otherwise
|
|
|
|
--*/
|
|
{
|
|
DWORD WinError = ERROR_SUCCESS;
|
|
NTSTATUS Status;
|
|
RC4_KEYSTRUCT Rc4Key;
|
|
MD5_CTX Md5Context;
|
|
|
|
//
|
|
// Check that the length is valid. If it isn't bail here.
|
|
//
|
|
|
|
if (EncryptedBootkey->EncryptedHash.Length != SYSKEY_SIZE) {
|
|
WinError = ERROR_INVALID_PASSWORD;
|
|
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, EncryptedBootkey->Salt, sizeof(EncryptedBootkey->Salt) );
|
|
|
|
MD5Final( &Md5Context );
|
|
|
|
//
|
|
// Decrypt the Buffer
|
|
//
|
|
|
|
rc4_key( &Rc4Key, MD5DIGESTLEN, Md5Context.digest );
|
|
rc4( &Rc4Key, EncryptedBootkey->EncryptedHash.Length, (LPBYTE) EncryptedBootkey->EncryptedHash.Buffer );
|
|
|
|
//
|
|
// Return the password to the caller.
|
|
//
|
|
|
|
*Bootkey = &EncryptedBootkey->EncryptedHash;
|
|
|
|
Cleanup:
|
|
|
|
return WinError;
|
|
|
|
}
|