windows-nt/Source/XPSP1/NT/ds/security/dsrole/server/threadman.c
2020-09-26 16:20:57 +08:00

3401 lines
110 KiB
C
Raw Blame History

This file contains invisible Unicode characters

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

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
threadman.c
Abstract:
Implementation of the thread and thread management routines
Author:
Mac McLain (MacM) Feb 10, 1997
Environment:
User Mode
Revision History:
--*/
#include <setpch.h>
#include <dssetp.h>
#include <dsgetdc.h>
#include <lmcons.h>
#include <lmapibuf.h>
#include <lmsname.h>
#include <loadfn.h>
#include <lsarpc.h>
#include <db.h>
#include <lsasrvmm.h>
#include <lsaisrv.h>
#include <lmaccess.h>
#include <netsetp.h>
#include <samrpc.h> // for samisrv.h
#include <samisrv.h> // for nlrepl.h
#include <nlrepl.h> // for I_NetNotifyDsChange
#include <Lmshare.h> // for NetShareDel()
#include <autoenr.h> // for CertAutoRemove()
#include "secure.h"
#include "services.h"
#include "upgrade.h"
#include "trustdom.h"
#include "sysvol.h"
#include "lsa.h"
#include "ds.h"
#include "threadman.h"
// forward from setutl.h
DWORD
DsRolepDeregisterNetlogonDnsRecords(
PNTDS_DNS_RR_INFO pInfo
);
//
// Helpful macros
//
#define DSROLEP_MAKE_DNS_RELATIVE(name) \
if(name) { \
DWORD _StripAbsoluteLength_ = wcslen( name ); \
if ( *(name + _StripAbsoluteLength_ - 1 ) == L'.' ) { \
*(name + _StripAbsoluteLength_ - 1 ) = UNICODE_NULL; \
} \
}
#define DSROLEP_ALLOC_AND_COPY_STRING_EXIT( dest, src, label ) \
if ( (src) ) { \
(dest) = RtlAllocateHeap( RtlProcessHeap(), 0, (wcslen( (src) ) + 1) * sizeof( WCHAR ) ); \
if ( !(dest) ) { \
goto label; \
} else { \
wcscpy((dest), (src)); \
} \
} else { \
(dest) = NULL; \
}
#define DSROLEP_ALLOC_AND_COPY_UNICODE_STRING_EXIT( dest, src, label ) \
if ( (src) && (src)->Buffer ) { \
(dest)->Buffer = RtlAllocateHeap( RtlProcessHeap(), 0, (src)->MaximumLength ); \
if ( (dest)->Buffer == NULL ) { \
goto label; \
} else { \
(dest)->Length = (src)->Length; \
(dest)->MaximumLength = (src)->MaximumLength; \
RtlCopyMemory( (dest)->Buffer, (src)->Buffer, (src)->MaximumLength ); \
} \
} else { \
RtlZeroMemory( (dest), sizeof( UNICODE_STRING ) ); \
}
//
// Function definitions
//
DWORD
DsRolepBuildPromoteArgumentBlock(
IN LPWSTR DnsDomainName,
IN LPWSTR FlatDomainName,
IN LPWSTR SiteName,
IN LPWSTR DsDatabasePath,
IN LPWSTR DsLogPath,
IN LPWSTR RestorePath,
IN LPWSTR SystemVolumeRootPath,
IN PUNICODE_STRING Bootkey,
IN LPWSTR Parent,
IN LPWSTR Server,
IN LPWSTR Account,
IN PUNICODE_STRING Password,
IN PUNICODE_STRING DomainAdminPassword,
IN PUNICODE_STRING SafeModePassword,
IN ULONG Options,
IN UCHAR PasswordSeed,
IN OUT PDSROLEP_OPERATION_PROMOTE_ARGS *Promote
)
/*++
Routine Description:
Builds an argument structure to pass into one of the promote worker functions. Since the
rpc call will return before the thread completes, we'll have to copy all our argument strings.
Since parameters may be changed through out the course of promotion, we assume allocations
are made from the process heap.
Resultant argument block should be freed via DsRolepFreeArgumentBlock
Arguments:
DnsDomainName - Dns domain name of the domain to install
FlatDomainName - Flat (NetBIOS) 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
RestorePath - Location of a restored database.
SystemVolumeRootPath - Absolute path on the local machine to be the root of the system
volume root path.
Bootkey - Needed when you don't have the key in the registry or on a disk
cbBootkey - size of the bootkey
Parent - Optional. Parent domain name
Server -- Optional. Replica partner or server in parent domain
Account - User account to use when setting up as a child domain
Password - Password to use with the above account
DomainAdminPassword - Password to set the domain administartor account
Options - Options to control the creation of the domain
PasswordSeed - Seed used to hide the passwords
Promote - Where the allocated argument block is returned
Returns:
ERROR_SUCCESS - Success
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
--*/
{
DWORD WinError = ERROR_NOT_ENOUGH_MEMORY;
*Promote = RtlAllocateHeap( RtlProcessHeap(), 0, sizeof( DSROLEP_OPERATION_PROMOTE_ARGS ) );
if ( *Promote == NULL ) {
goto BuildPromoteDone;
}
RtlZeroMemory( *Promote, sizeof( DSROLEP_OPERATION_PROMOTE_ARGS ) );
DSROLEP_ALLOC_AND_COPY_STRING_EXIT( (*Promote)->DnsDomainName, DnsDomainName, BuildPromoteDone );
DSROLEP_ALLOC_AND_COPY_STRING_EXIT( (*Promote)->FlatDomainName, FlatDomainName, BuildPromoteDone );
DSROLEP_ALLOC_AND_COPY_STRING_EXIT( (*Promote)->SiteName, SiteName, BuildPromoteDone );
DSROLEP_ALLOC_AND_COPY_STRING_EXIT( (*Promote)->DsDatabasePath, DsDatabasePath, BuildPromoteDone );
DSROLEP_ALLOC_AND_COPY_STRING_EXIT( (*Promote)->DsLogPath, DsLogPath, BuildPromoteDone );
DSROLEP_ALLOC_AND_COPY_STRING_EXIT( (*Promote)->RestorePath, RestorePath, BuildPromoteDone );
DSROLEP_ALLOC_AND_COPY_STRING_EXIT( (*Promote)->SysVolRootPath, SystemVolumeRootPath, BuildPromoteDone );
DSROLEP_ALLOC_AND_COPY_STRING_EXIT( (*Promote)->Parent, Parent, BuildPromoteDone );
DSROLEP_ALLOC_AND_COPY_STRING_EXIT( (*Promote)->Server, Server, BuildPromoteDone );
DSROLEP_ALLOC_AND_COPY_STRING_EXIT( (*Promote)->Account, Account, BuildPromoteDone );
DSROLEP_ALLOC_AND_COPY_UNICODE_STRING_EXIT( &((*Promote)->Password), Password,
BuildPromoteDone );
DSROLEP_ALLOC_AND_COPY_UNICODE_STRING_EXIT( &((*Promote)->DomainAdminPassword),
DomainAdminPassword, BuildPromoteDone );
DSROLEP_ALLOC_AND_COPY_UNICODE_STRING_EXIT( &((*Promote)->SafeModePassword),
SafeModePassword, BuildPromoteDone );
DSROLEP_ALLOC_AND_COPY_UNICODE_STRING_EXIT( &((*Promote)->Bootkey),
Bootkey, BuildPromoteDone );
(*Promote)->Options = Options;
(*Promote)->Decode = PasswordSeed;
WinError = DsRolepGetImpersonationToken( &(*Promote)->ImpersonateToken );
BuildPromoteDone:
if ( WinError != ERROR_SUCCESS ) {
DsRolepFreeArgumentBlock( Promote, TRUE );
}
return( WinError );
}
DWORD
DsRolepBuildDemoteArgumentBlock(
IN DSROLE_SERVEROP_DEMOTE_ROLE ServerRole,
IN LPWSTR DnsDomainName,
IN LPWSTR Account,
IN PUNICODE_STRING Password,
IN ULONG Options,
IN BOOL LastDcInDomain,
IN PUNICODE_STRING AdminPassword,
IN UCHAR PasswordSeed,
IN OUT PDSROLEP_OPERATION_DEMOTE_ARGS *Demote
)
/*++
Routine Description:
Builds an argument structure to pass into the demote worker functions. Since the rpc call
will return before the thread completes, we'll have to copy all our argument strings.
Resultant argument block should be freed via DsRolepFreeArgumentBlock
Arguments:
ServerRole - New role for the server
DnsDomainName - Dns domain name of the domain to uninstall. NULL means all of them
Account - User account to use when setting up as a child domain
Password - Password to use with the above account
Options - Options to control the creation of the domain
LastDcInDomain - If TRUE, the Dc being demoted is the last Dc in the domain.
AdminPassword - Password to set on the administrator account if it is a new install
PasswordSeed - Seed used to hide the passwords
Demote - Where the allocated argument block is returned
Returns:
ERROR_SUCCESS - Success
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
--*/
{
DWORD WinError = ERROR_NOT_ENOUGH_MEMORY;
*Demote = RtlAllocateHeap( RtlProcessHeap(), 0, sizeof( DSROLEP_OPERATION_DEMOTE_ARGS ) );
if ( *Demote == NULL ) {
goto BuildDemoteDone;
}
RtlZeroMemory( *Demote, sizeof( DSROLEP_OPERATION_DEMOTE_ARGS ) );
(*Demote)->ServerRole = ServerRole;
DSROLEP_ALLOC_AND_COPY_STRING_EXIT( (*Demote)->DomainName, DnsDomainName, BuildDemoteDone );
DSROLEP_ALLOC_AND_COPY_STRING_EXIT( (*Demote)->Account, Account, BuildDemoteDone );
DSROLEP_ALLOC_AND_COPY_UNICODE_STRING_EXIT( &((*Demote)->Password), Password, BuildDemoteDone );
(*Demote)->LastDcInDomain = ( LastDcInDomain != 0 );
DSROLEP_ALLOC_AND_COPY_UNICODE_STRING_EXIT( &((*Demote)->AdminPassword),
AdminPassword,
BuildDemoteDone );
(*Demote)->Options = Options;
(*Demote)->Decode = PasswordSeed;
WinError = DsRolepGetImpersonationToken( & (*Demote)->ImpersonateToken );
BuildDemoteDone:
if ( WinError != ERROR_SUCCESS ) {
DsRolepFreeArgumentBlock( Demote, FALSE );
}
return( WinError );
}
VOID
DsRolepFreeArgumentBlock(
IN PVOID *ArgumentBlock,
IN BOOLEAN Promote
)
/*++
Routine Description:
Frees an arugment block allocated via DsRolepBuildPromote/DemoteArgumentBlock
Since parameters may be changed through out the course of promotion, we assume allocations
are made from the process heap.
Arguments:
ArgumentBlock - Argument block to free
Promote - If TRUE, this is a promote argument block. If FALSE, it's a demote arg block
Returns:
VOID
--*/
{
PDSROLEP_OPERATION_PROMOTE_ARGS PromoteArg;
PDSROLEP_OPERATION_DEMOTE_ARGS Demote;
PVOID HeapHandle = RtlProcessHeap();
if ( !ArgumentBlock ) {
return;
}
//
// Free it all
//
if ( Promote ) {
PromoteArg = ( PDSROLEP_OPERATION_PROMOTE_ARGS )*ArgumentBlock;
RtlFreeHeap( HeapHandle, 0, PromoteArg->DnsDomainName );
RtlFreeHeap( HeapHandle, 0, PromoteArg->FlatDomainName );
RtlFreeHeap( HeapHandle, 0, PromoteArg->SiteName );
RtlFreeHeap( HeapHandle, 0, PromoteArg->DsDatabasePath );
RtlFreeHeap( HeapHandle, 0, PromoteArg->DsLogPath );
RtlFreeHeap( HeapHandle, 0, PromoteArg->SysVolRootPath );
RtlFreeHeap( HeapHandle, 0, PromoteArg->Parent );
RtlFreeHeap( HeapHandle, 0, PromoteArg->Server );
RtlFreeHeap( HeapHandle, 0, PromoteArg->Account );
RtlFreeHeap( HeapHandle, 0, PromoteArg->Password.Buffer );
RtlFreeHeap( HeapHandle, 0, PromoteArg->DomainAdminPassword.Buffer );
if ( PromoteArg->ImpersonateToken ) {
NtClose( PromoteArg->ImpersonateToken );
}
} else {
Demote = ( PDSROLEP_OPERATION_DEMOTE_ARGS )*ArgumentBlock;
RtlFreeHeap( HeapHandle, 0, Demote->Account );
RtlFreeHeap( HeapHandle, 0, Demote->Password.Buffer );
RtlFreeHeap( HeapHandle, 0, Demote->DomainName );
RtlFreeHeap( HeapHandle, 0, Demote->AdminPassword.Buffer );
if ( Demote->ImpersonateToken ) {
NtClose( Demote->ImpersonateToken );
}
}
RtlFreeHeap( HeapHandle, 0, *ArgumentBlock );
}
DWORD
DsRolepSpinWorkerThread(
IN DSROLEP_OPERATION_TYPE Operation,
IN PVOID ArgumentBlock
)
/*++
Routine Description:
This function actually creates the worker thread that will do the promot/demote
Arguments:
Operation - Demote, Promote as DC, or Promote as Replica
ArgumentBlock - Block of arguments appropriate for the operation
Returns:
ERROR_SUCCESS - Success
INVALID_PARAMETER - An unexpected operation type encounterd
--*/
{
DWORD WinError = ERROR_SUCCESS, IgnoreError;
NTSTATUS NtStatus;
DWORD ThreadId;
//
// The basic premise is that we'll utilize the Completion event to indicate when
// the thread is full initialized.
//
NtStatus = NtResetEvent( DsRolepCurrentOperationHandle.CompletionEvent, NULL );
WinError = RtlNtStatusToDosError( NtStatus );
if ( ERROR_SUCCESS == WinError ) {
switch ( Operation) {
case DSROLEP_OPERATION_DC:
DsRolepCurrentOperationHandle.OperationThread = CreateThread(
NULL,
0,
( LPTHREAD_START_ROUTINE )DsRolepThreadPromoteDc,
ArgumentBlock,
0,
&ThreadId );
break;
case DSROLEP_OPERATION_REPLICA:
DsRolepCurrentOperationHandle.OperationThread = CreateThread(
NULL,
0,
( LPTHREAD_START_ROUTINE )DsRolepThreadPromoteReplica,
ArgumentBlock,
0,
&ThreadId );
break;
case DSROLEP_OPERATION_DEMOTE:
DsRolepCurrentOperationHandle.OperationThread = CreateThread(
NULL,
0,
( LPTHREAD_START_ROUTINE )DsRolepThreadDemote,
ArgumentBlock,
0,
&ThreadId );
break;
default:
DsRoleDebugOut(( DEB_ERROR,
"Unexpected operation %lu encountered\n", Operation ));
WinError = ERROR_INVALID_PARAMETER;
break;
}
//
// Check for failure
//
if ( WinError == ERROR_SUCCESS &&
DsRolepCurrentOperationHandle.OperationThread == NULL ) {
WinError = GetLastError();
}
//
// If it worked, wait for the thread to indicate its ready
//
if ( WinError == ERROR_SUCCESS ) {
if ( WaitForSingleObject( DsRolepCurrentOperationHandle.CompletionEvent,
INFINITE ) == WAIT_FAILED ) {
WinError = GetLastError();
} else {
NtResetEvent( DsRolepCurrentOperationHandle.CompletionEvent, NULL );
}
}
}
if ( WinError == ERROR_SUCCESS ) {
DsRoleDebugOut(( DEB_TRACE,
"Thread %lu successfully started\n", ThreadId ));
} else {
DsRolepLogPrint(( DEB_ERROR,
"Thread %lu unsuccessfully started: %lu\n", ThreadId, WinError ));
}
return( WinError );
}
DWORD
DsRolepThreadPromoteDc(
IN PVOID ArgumentBlock
)
/*++
Routine Description:
This function actually "promotes" a server to a dc of an new domain. Additionally, this
domain can be set up as a child of an existing domain. This is accomplished by:
Installing the Ds as a replica
Setting the DnsDomainTree LSA information
Optionally configuring it as a child of an existing domain
Configuring the KDC
Arguments:
ArgumentBlock - Block of arguments appropriate for the operation
Returns:
ERROR_SUCCESS - Success
--*/
{
DWORD WinError = ERROR_SUCCESS;
DWORD IgnoreError;
PWSTR ParentDc = NULL;
PDOMAIN_CONTROLLER_INFO DomainControllerInfo = NULL;
PDSROLEP_OPERATION_PROMOTE_ARGS PromoteArgs = ( PDSROLEP_OPERATION_PROMOTE_ARGS )ArgumentBlock;
DSROLEP_DOMAIN_POLICY_INFO BackupDomainPolicyInfo;
ULONG FindOptions;
GUID DomainGuid;
PWSTR InstalledSite = NULL;
PSID NewDomainSid = NULL;
PPOLICY_DNS_DOMAIN_INFO ParentDnsDomainInfo = NULL;
PWSTR DnsDomainTreeName = NULL;
//
// BOOLEAN's to maintain state
//
// N.B. The order of these booleans is the order in which they
// are changed -- please maintain order and make sure that
// the PromoteUndo section undoes them in the reverse order
//
BOOLEAN IPCConnection = FALSE; // resource -- release on exit
BOOLEAN RestartNetlogon = FALSE;
BOOLEAN SysVolCreated = FALSE;
BOOLEAN CleanupNetlogon = FALSE; // nothing to undo
BOOLEAN DsInstalled = FALSE;
BOOLEAN DsRunning = FALSE;
BOOLEAN DomainPolicyInfoChanged = FALSE;
BOOLEAN DomainServicesChanged = FALSE;
BOOLEAN DomainControllerServicesChanged = FALSE;
BOOLEAN TrustCreated = FALSE;
BOOLEAN ProductTypeChanged = FALSE;
//
// Init the stack space
//
RtlZeroMemory(&BackupDomainPolicyInfo, sizeof(BackupDomainPolicyInfo));
//
// Set our event to indicate we're starting
//
NtSetEvent( DsRolepCurrentOperationHandle.CompletionEvent, NULL );
//
// If we have an existing domain in the forest to install from and we
// weren't given a site or source server name, we need to make a dsgetdc
// name.
//
if ( PromoteArgs->Server ) {
ParentDc = PromoteArgs->Server;
}
if ( PromoteArgs->Parent != NULL &&
( (PromoteArgs->Server == NULL)
|| (PromoteArgs->SiteName == NULL) ) ) {
DsRolepLogPrint(( DEB_TRACE,
"No source DC or no site name specified. Searching for dc in domain %ws: ( DS_REQUIRED | WRITABLE )\n",
PromoteArgs->Parent ));
DSROLEP_CURRENT_OP1( DSROLEEVT_SEARCH_DC, PromoteArgs->Parent );
FindOptions = DS_DIRECTORY_SERVICE_REQUIRED | DS_WRITABLE_REQUIRED | DS_FORCE_REDISCOVERY;
WinError = DsGetDcName(NULL,
PromoteArgs->Parent,
NULL,
NULL,
FindOptions,
&DomainControllerInfo );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_TRACE,
"Couldn't find domain controller in domain %ws (error: %d)\n",
ParentDc,
WinError ));
if ( PromoteArgs->Server == NULL ) {
//
// This is a fatal error if we can't find a dc in the parent domain
// If we have a server, then we can derive a site name later on if
// necessary
//
DSROLEP_FAIL1( WinError, DSROLERES_FIND_DC, PromoteArgs->Parent );
DsRolepLogPrint(( DEB_ERROR,
"Failed to find a dc for %ws: %lu\n",
PromoteArgs->Parent,
WinError ));
goto PromoteUndo;
}
//
// This isn't fatal since we are a source server
//
DsRolepLogPrint(( DEB_TRACE, "Using supplied domain controller: %ws\n", ParentDc ));
WinError = ERROR_SUCCESS;
} else {
//
// The dsgetdcname succeeded
//
if ( PromoteArgs->Server == NULL ) {
//
// Use the found domain controller
//
DSROLEP_CURRENT_OP2( DSROLEEVT_FOUND_DC,
PromoteArgs->Parent,
ParentDc );
DsRolepLogPrint(( DEB_TRACE_DS, "No user specified source DC\n" ));
ParentDc = DomainControllerInfo->DomainControllerName;
}
//
// Determine the site that we are going to be installed in
// the results of the parent query
//
if ( PromoteArgs->SiteName == NULL ) {
DsRolepLogPrint(( DEB_TRACE_DS, "No user specified site\n" ));
PromoteArgs->SiteName = DomainControllerInfo->ClientSiteName;
if ( (PromoteArgs->SiteName == NULL)
&& (!_wcsicmp(ParentDc, DomainControllerInfo->DomainControllerName)) ) {
DsRolepLogPrint(( DEB_TRACE_DS, "This machine is not in a configured site ... using source DC's site.\n" ));
PromoteArgs->SiteName = DomainControllerInfo->DcSiteName;
} else {
//
// We can't find a site. That's ok -- the ds will find one for
// us
//
}
}
if ( PromoteArgs->SiteName ) {
DSROLEP_CURRENT_OP2( DSROLEEVT_FOUND_SITE,
PromoteArgs->SiteName,
PromoteArgs->Parent );
} else {
DsRolepLogPrint(( DEB_TRACE_DS, "This machine is not in a configured site\n" ));
}
}
} else {
//
// The caller supplied both the source server and site name
//
ParentDc = PromoteArgs->Server;
DsRolepLogPrint(( DEB_TRACE, "Using supplied domain controller: %ws\n", ParentDc ));
DsRolepLogPrint(( DEB_TRACE, "Using supplied site: %ws\n", PromoteArgs->SiteName ));
}
//
// Ok, we have determined the our source domain controller and destination
// site
//
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// Force the time synch
//
if ( ParentDc
&& FLAG_ON( PromoteArgs->Options, DSROLE_DC_FORCE_TIME_SYNC ) ) {
WinError = DsRolepForceTimeSync( PromoteArgs->ImpersonateToken,
ParentDc );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_WARN, "Time sync with %ws failed with %d\n",
ParentDc,
WinError ));
//
// This is not a fatal error
//
WinError = ERROR_SUCCESS;
}
}
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// If we are setting up a child domain, establish a session first
//
if ( ParentDc ) {
RtlRunDecodeUnicodeString( PromoteArgs->Decode, &PromoteArgs->Password );
WinError = ImpNetpManageIPCConnect( PromoteArgs->ImpersonateToken,
ParentDc,
PromoteArgs->Account,
PromoteArgs->Password.Buffer,
NETSETUPP_CONNECT_IPC );
RtlRunEncodeUnicodeString( &PromoteArgs->Decode, &PromoteArgs->Password );
if ( ERROR_SUCCESS != WinError ) {
DSROLEP_FAIL1( WinError, DSROLERES_NET_USE, ParentDc );
DsRolepLogPrint(( DEB_ERROR,
"Failed to establish the session with %ws: 0x%lx\n", ParentDc,
WinError ));
goto PromoteUndo;
}
IPCConnection = TRUE;
}
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// If we have a parent dc, get the LSA policy from it
//
//
// Strip the trailing '.' from the Dns name if we happen to have an absolute name
//
DSROLEP_MAKE_DNS_RELATIVE( PromoteArgs->DnsDomainName );
DnsDomainTreeName = PromoteArgs->DnsDomainName;
if ( ParentDc ) {
NTSTATUS Status;
UNICODE_STRING ParentServer;
HANDLE ParentPolicy = NULL;
OBJECT_ATTRIBUTES ObjectAttributes;
DSROLEP_CURRENT_OP1( DSROLEEVT_MACHINE_POLICY, ParentDc );
RtlInitUnicodeString( &ParentServer, ParentDc );
RtlZeroMemory( &ObjectAttributes, sizeof( ObjectAttributes ) );
Status = ImpLsaOpenPolicy( PromoteArgs->ImpersonateToken,
&ParentServer,
&ObjectAttributes,
MAXIMUM_ALLOWED,
&ParentPolicy );
if ( NT_SUCCESS( Status ) ) {
Status = ImpLsaQueryInformationPolicy( PromoteArgs->ImpersonateToken,
ParentPolicy,
PolicyDnsDomainInformation,
&ParentDnsDomainInfo );
ImpLsaClose( PromoteArgs->ImpersonateToken, ParentPolicy );
}
//
// We'll have to build it as a NULL terminated string
//
if ( NT_SUCCESS( Status ) && ParentDnsDomainInfo->DnsForestName.Length ) {
if ( ParentDnsDomainInfo->DnsForestName.Buffer[
ParentDnsDomainInfo->DnsForestName.Length / sizeof( WCHAR ) ] == UNICODE_NULL ) {
DnsDomainTreeName = ( PWSTR )ParentDnsDomainInfo->DnsForestName.Buffer;
} else {
DnsDomainTreeName = RtlAllocateHeap(
RtlProcessHeap(), 0,
ParentDnsDomainInfo->DnsForestName.Length + sizeof( WCHAR ) );
if ( DnsDomainTreeName == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
RtlCopyMemory( DnsDomainTreeName,
ParentDnsDomainInfo->DnsForestName.Buffer,
ParentDnsDomainInfo->DnsForestName.Length );
DnsDomainTreeName[ ParentDnsDomainInfo->DnsForestName.Length /
sizeof( WCHAR ) ] = UNICODE_NULL;
}
}
}
WinError = RtlNtStatusToDosError( Status );
DSROLEP_FAIL1( WinError, DSROLERES_POLICY_READ_REMOTE, ParentDc );
if ( ERROR_SUCCESS != WinError ) {
goto PromoteUndo;
}
}
//
// If we are doing a root install, make sure we were given the forest root
// as our parent
//
if ( FLAG_ON( PromoteArgs->Options, DSROLE_DC_TRUST_AS_ROOT ) ) {
DSROLEP_MAKE_DNS_RELATIVE( PromoteArgs->Parent );
DSROLEP_MAKE_DNS_RELATIVE( DnsDomainTreeName );
if ( _wcsicmp( PromoteArgs->Parent, DnsDomainTreeName ) ) {
//
// Names don't match... We can't allow this...
//
DsRolepLogPrint(( DEB_ERROR,
"Tried to specify domain %ws as a forest root but "
"%ws is the actual root\n",
PromoteArgs->Parent,
DnsDomainTreeName ));
WinError = ERROR_INVALID_DOMAINNAME;
DSROLEP_FAIL1( WinError, DSROLERES_NOT_FOREST_ROOT, PromoteArgs->Parent );
goto PromoteUndo;
}
}
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// Make a back up of the local policy...
//
WinError = DsRolepBackupDomainPolicyInfo( NULL, &BackupDomainPolicyInfo );
if ( ERROR_SUCCESS != WinError ) {
DSROLEP_FAIL0( WinError, DSROLERES_POLICY_READ_LOCAL );
goto PromoteUndo;
}
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// Stop netlogon
//
DSROLEP_CURRENT_OP1( DSROLEEVT_STOP_SERVICE, SERVICE_NETLOGON );
WinError = DsRolepStopNetlogon( &RestartNetlogon );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_WARN, "Failed to stop NETLOGON (%d)\n", WinError ));
goto PromoteUndo;
}
DsRolepLogPrint(( DEB_TRACE, "Stopped NETLOGON\n" ));
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// Create the system volume information so we can seed the system volume while the Ds is
// installing
//
DSROLEP_CURRENT_OP1( DSROLEEVT_CREATE_SYSVOL, PromoteArgs->SysVolRootPath );
RtlRunDecodeUnicodeString( PromoteArgs->Decode, &PromoteArgs->Password );
WinError = DsRolepCreateSysVolPath( PromoteArgs->SysVolRootPath,
PromoteArgs->DnsDomainName,
ParentDc,
PromoteArgs->Account,
PromoteArgs->Password.Buffer,
PromoteArgs->SiteName,
TRUE );
RtlRunEncodeUnicodeString( &PromoteArgs->Decode, &PromoteArgs->Password );
DSROLEP_CURRENT_OP1( DSROLEEVT_SVSETUP, PromoteArgs->SysVolRootPath );
if ( WinError != ERROR_SUCCESS ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to create the system volume (%d)\n", WinError ));
goto PromoteUndo;
}
SysVolCreated = TRUE;
DsRolepLogPrint(( DEB_TRACE, "Created the system volume\n" ));
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// Setup the Ds
//
RtlRunDecodeUnicodeString( PromoteArgs->Decode, &PromoteArgs->Password );
RtlRunDecodeUnicodeString( PromoteArgs->Decode, &PromoteArgs->DomainAdminPassword );
RtlRunDecodeUnicodeString( PromoteArgs->Decode, &PromoteArgs->SafeModePassword );
WinError = DsRolepInstallDs( PromoteArgs->DnsDomainName,
PromoteArgs->FlatDomainName,
DnsDomainTreeName,
PromoteArgs->SiteName,
PromoteArgs->DsDatabasePath,
PromoteArgs->DsLogPath,
PromoteArgs->RestorePath,
PromoteArgs->SysVolRootPath,
&(PromoteArgs->Bootkey),
PromoteArgs->DomainAdminPassword.Buffer,
PromoteArgs->Parent,
ParentDc,
PromoteArgs->Account,
PromoteArgs->Password.Buffer,
PromoteArgs->SafeModePassword.Buffer,
PromoteArgs->Parent,
PromoteArgs->Options,
FALSE,
PromoteArgs->ImpersonateToken,
&InstalledSite,
&DomainGuid,
&NewDomainSid );
RtlRunEncodeUnicodeString( &PromoteArgs->Decode, &PromoteArgs->Password );
RtlRunEncodeUnicodeString( &PromoteArgs->Decode, &PromoteArgs->DomainAdminPassword );
RtlRunEncodeUnicodeString( &PromoteArgs->Decode, &PromoteArgs->SafeModePassword );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to install the directory service (%d)\n", WinError ));
goto PromoteUndo;
}
DsRunning = TRUE;
DsInstalled = TRUE;
DsRolepLogPrint(( DEB_TRACE, "Installed the directory service\n", WinError ));
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// Set the LSA domain policy
//
WinError = DsRolepSetLsaDomainPolicyInfo( PromoteArgs->DnsDomainName,
PromoteArgs->FlatDomainName,
DnsDomainTreeName,
&DomainGuid,
NewDomainSid,
NTDS_INSTALL_DOMAIN,
&BackupDomainPolicyInfo );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to set the LSA policy (%d)\n", WinError ));
DSROLEP_FAIL0( WinError, DSROLERES_POLICY_WRITE_LOCAL );
goto PromoteUndo;
}
DomainPolicyInfoChanged = TRUE;
DsRolepLogPrint(( DEB_TRACE, "Set the LSA policy\n"));
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// Configure the domain relative services
//
WinError = DsRolepConfigureDomainServices( DSROLEP_SERVICES_ON );
if ( WinError != ERROR_SUCCESS ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to configure the domain services (%d)\n", WinError ));
goto PromoteUndo;
}
DomainServicesChanged = TRUE;
DsRolepLogPrint(( DEB_TRACE, "Configured the domain services\n" ));
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// Configure the domain controller relative services
//
WinError = DsRolepConfigureDomainControllerServices( DSROLEP_SERVICES_ON );
if ( WinError != ERROR_SUCCESS ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to configure the domain controller services (%d)\n", WinError ));
goto PromoteUndo;
}
DomainControllerServicesChanged = TRUE;
DsRolepLogPrint(( DEB_TRACE, "Configured the domain controller services\n" ));
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// Finally, upgrade the Lsa to the Ds.
//
WinError = DsRolepUpgradeLsaToDs( TRUE );
if ( ERROR_SUCCESS != WinError ) {
DSROLEP_FAIL0( WinError, DSROLERES_LSA_UPGRADE );
DsRolepLogOnFailure( WinError,
DsRolepLogPrint(( DEB_TRACE,
"Upgrade of the LSA into the DS failed with %lu\n",
WinError )) );
goto PromoteUndo;
}
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// Create the trust objects and set the DnsDomainTree information
//
if ( ParentDc ) {
WinError = DsRolepCreateTrustedDomainObjects( PromoteArgs->ImpersonateToken,
ParentDc,
PromoteArgs->DnsDomainName,
ParentDnsDomainInfo,
PromoteArgs->Options );
if ( WinError != ERROR_SUCCESS ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to create trusted domain objects (%d)\n", WinError ));
goto PromoteUndo;
}
TrustCreated = TRUE;
DsRolepLogPrint(( DEB_TRACE, "Created trusted domain objects\n" ));
}
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// Create the GPO for policy
//
WinError = ( *DsrSceDcPromoCreateGPOsInSysvolEx )( PromoteArgs->ImpersonateToken,
PromoteArgs->DnsDomainName,
PromoteArgs->SysVolRootPath,
FLAG_ON( PromoteArgs->Options,
DSROLE_DC_DOWNLEVEL_UPGRADE ) ?
SCE_PROMOTE_FLAG_UPGRADE :
0,
DsRolepStringUpdateCallback );
if ( ERROR_SUCCESS != WinError ) {
DSROLEP_FAIL1( WinError, DSROLERES_GPO_CREATION, PromoteArgs->DnsDomainName );
DsRolepLogOnFailure( WinError,
DsRolepLogPrint(( DEB_TRACE,
"Creation of GPO failed with %lu\n",
WinError )) );
goto PromoteUndo;
}
DsRolepLogPrint(( DEB_TRACE,
"Created GPO\n" ));
//
// Stop the Ds
//
DsRolepStopDs( DsRunning );
DsRunning = FALSE;
//
// If the install succeeded, make sure to save off the new site name
//
WinError = DsRolepSetOperationHandleSiteName( InstalledSite );
if ( WinError != ERROR_SUCCESS ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to copy site name (%d)\n", WinError ));
goto PromoteUndo;
}
//
// If we update it, NULL out the local parameter so we don't attempt to delete it
//
InstalledSite = NULL;
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// Set the computers Dns domain name
//
DSROLEP_CURRENT_OP1( DSROLEEVT_SET_COMPUTER_DNS, PromoteArgs->DnsDomainName );
WinError = NetpSetDnsComputerNameAsRequired( PromoteArgs->DnsDomainName );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogOnFailure( WinError,
DsRolepLogPrint(( DEB_TRACE,
"NetpSetDnsComputerNameAsRequired to %ws failed with %lu\n",
PromoteArgs->DnsDomainName,
WinError )) );
DSROLEP_FAIL1( WinError, DSROLERES_SET_COMPUTER_DNS, PromoteArgs->DnsDomainName );
goto PromoteUndo;
}
//
// Restart netlogon if it was stopped and if a failure occurred
//
//
// Complete the sysvol replication
//
WinError = DsRolepFinishSysVolPropagation( TRUE, TRUE );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to complete system volume replication (%d)\n", WinError ));
goto PromoteUndo;
}
DsRolepLogPrint(( DEB_TRACE, "Completed system volume replication\n"));
//
// Next, set the sysvol path for netlogon
//
WinError = DsRolepSetNetlogonSysVolPath( PromoteArgs->SysVolRootPath,
PromoteArgs->DnsDomainName,
( BOOLEAN )FLAG_ON( PromoteArgs->Options,
DSROLE_DC_DOWNLEVEL_UPGRADE ),
&CleanupNetlogon );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to set system volume path for NETLOGON (%d)\n", WinError ));
goto PromoteUndo;
}
DsRolepLogPrint(( DEB_TRACE, "Set system volume path for NETLOGON\n" ));
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// Set the machine role
//
WinError = DsRolepSetProductType( DSROLEP_MT_MEMBER );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to set the product type (%d)\n", WinError ));
goto PromoteUndo;
}
ProductTypeChanged = TRUE;
DsRolepLogPrint(( DEB_TRACE, "Set the product type\n" ));
//
// Set the security on the dc files
//
WinError = DsRolepSetDcSecurity( PromoteArgs->ImpersonateToken,
PromoteArgs->SysVolRootPath,
PromoteArgs->DsDatabasePath,
PromoteArgs->DsLogPath,
( BOOLEAN )FLAG_ON( PromoteArgs->Options,
DSROLE_DC_DOWNLEVEL_UPGRADE ),
FALSE );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to set security on domain controller (%d)\n", WinError ));
goto PromoteUndo;
}
DsRolepLogPrint(( DEB_TRACE, "Set security on domain controller\n"));
DsRolepSetCriticalOperationsDone();
//
// From here to do the end, perform, and only perform, non critical
// operations
//
//
// Indicate that we are no longer doing upgrades, if applicable
//
if ( FLAG_ON( PromoteArgs->Options, DSROLE_DC_DOWNLEVEL_UPGRADE ) ) {
WinError = DsRolepDeleteUpgradeInfo();
DsRolepLogOnFailure( WinError,
DsRolepLogPrint(( DEB_WARN,
"Failed to cleanup upgrade info (%d)\n",
WinError )) );
if (ERROR_SUCCESS == WinError) {
DsRolepLogPrint(( DEB_TRACE,
"Removed upgrade info\n" ));
}
// This error isn't interesting to propogate
WinError = ERROR_SUCCESS;
}
//
// Remove any old netlogon stuff if we got that far
//
if ( CleanupNetlogon ) {
WinError = DsRolepCleanupOldNetlogonInformation();
DsRolepLogOnFailure( WinError,
DsRolepLogPrint(( DEB_WARN,
"Failed to cleanup old netlogon information (%d)\n",
WinError )) );
if (ERROR_SUCCESS == WinError) {
DsRolepLogPrint(( DEB_TRACE,
"Removed old netlogon information\n" ));
}
// This error isn't interesting to propogate
WinError = ERROR_SUCCESS;
}
//
// Set the default logon domain to the current domain name
//
WinError = DsRolepSetLogonDomain( PromoteArgs->FlatDomainName, FALSE );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogOnFailure( WinError,
DsRolepLogPrint(( DEB_WARN,
"Failed to set default logon domain to %ws (%d)\n",
PromoteArgs->FlatDomainName,
WinError )) );
if (ERROR_SUCCESS == WinError) {
DsRolepLogPrint(( DEB_TRACE,
"Set default logon domain to %ws\n",
PromoteArgs->FlatDomainName ));
}
//
// This is no reason to fail
//
WinError = ERROR_SUCCESS;
}
//
// Notify the time server we have completed the promotion
//
{
DWORD dwTimeFlags = W32TIME_PROMOTE;
if ( FLAG_ON( PromoteArgs->Options, DSROLE_DC_TRUST_AS_ROOT )
|| (NULL == PromoteArgs->Parent) ) {
//
// Any tree root, including the root of the forest
// should have this flag.
//
dwTimeFlags |= W32TIME_PROMOTE_FIRST_DC_IN_TREE;
}
(*DsrW32TimeDcPromo)( dwTimeFlags );
}
//
// By this time, we have successfully completed the promotion operation
//
ASSERT( ERROR_SUCCESS == WinError );
PromoteExit:
// The DS should not be running at this point
ASSERT( FALSE == DsRunning );
//
// Release any resources
//
//
// Tear down the session to the parent, if we have one
//
if ( IPCConnection ) {
RtlRunDecodeUnicodeString( PromoteArgs->Decode, &PromoteArgs->Password );
IgnoreError = ImpNetpManageIPCConnect( PromoteArgs->ImpersonateToken,
ParentDc,
PromoteArgs->Account,
PromoteArgs->Password.Buffer,
(NETSETUPP_DISCONNECT_IPC | NETSETUPP_USE_LOTS_FORCE));
RtlRunEncodeUnicodeString( &PromoteArgs->Decode, &PromoteArgs->Password );
if ( IgnoreError != ERROR_SUCCESS ) {
DsRolepLogPrint(( DEB_WARN,
"Failed to destroy the session with %ws: 0x%lx\n", ParentDc,
IgnoreError ));
}
IPCConnection = FALSE;
}
if ( ParentDnsDomainInfo ) {
if ( DnsDomainTreeName != ParentDnsDomainInfo->DnsForestName.Buffer ) {
RtlFreeHeap( RtlProcessHeap(), 0, DnsDomainTreeName );
}
LsaFreeMemory( ParentDnsDomainInfo );
}
if ( InstalledSite ) {
RtlFreeHeap( RtlProcessHeap(), 0, InstalledSite );
}
if ( NewDomainSid ) {
RtlFreeHeap( RtlProcessHeap(), 0, NewDomainSid );
}
DsRolepFreeDomainPolicyInfo( &BackupDomainPolicyInfo );
if ( DomainControllerInfo != NULL ) {
if ( PromoteArgs->SiteName == DomainControllerInfo->DcSiteName ||
PromoteArgs->SiteName == DomainControllerInfo->ClientSiteName ) {
PromoteArgs->SiteName = NULL;
}
NetApiBufferFree( DomainControllerInfo );
}
DsRolepFreeArgumentBlock( &ArgumentBlock, TRUE );
//
// Reset our operation handle and set the final operation status
//
DsRolepSetOperationDone( DSROLEP_OP_PROMOTION, WinError );
ExitThread( WinError );
return( WinError );
PromoteUndo:
//
// Something must have failed if we are undoing
//
ASSERT( WinError != ERROR_SUCCESS );
if ( ProductTypeChanged ) {
IgnoreError = DsRolepSetProductType( DSROLEP_MT_STANDALONE );
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to rollback product type (%d)\n",
IgnoreError )) );
ProductTypeChanged = FALSE;
}
if ( TrustCreated ) {
IgnoreError = DsRolepRemoveTrustedDomainObjects( PromoteArgs->ImpersonateToken,
ParentDc,
ParentDnsDomainInfo,
FLAG_ON( PromoteArgs->Options,
DSROLE_DC_PARENT_TRUST_EXISTS ) ?
0 :
DSROLE_DC_DELETE_PARENT_TRUST );
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to rollback trusted domain object creations (%d)\n",
IgnoreError )) );
TrustCreated = FALSE;
}
if ( DomainControllerServicesChanged ) {
IgnoreError = DsRolepConfigureDomainControllerServices( DSROLEP_SERVICES_REVERT );
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to rollback domain controller services configuration (%d)\n",
IgnoreError )) );
DomainControllerServicesChanged = FALSE;
}
if ( DomainServicesChanged ) {
IgnoreError = DsRolepConfigureDomainServices( DSROLEP_SERVICES_REVERT );
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to rollback domain services configuration (%d)\n",
IgnoreError )) );
DomainServicesChanged = FALSE;
}
if ( DomainPolicyInfoChanged ) {
IgnoreError = DsRolepRestoreDomainPolicyInfo(&BackupDomainPolicyInfo);
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to rollback domain policy information (%d)\n",
IgnoreError )) );
DomainPolicyInfoChanged = FALSE;
}
if ( DsRunning ) {
IgnoreError = DsRolepStopDs( DsRunning );
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to stop the directory service (%d)\n",
IgnoreError )) );
DsRunning = FALSE;
}
if ( DsInstalled ) {
IgnoreError = DsRolepUninstallDs( );
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to rollback directory service installation (%d)\n",
IgnoreError )) );
DsInstalled = FALSE;
}
if ( SysVolCreated ) {
IgnoreError = DsRolepFinishSysVolPropagation( FALSE, TRUE );
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to abort system volume installation (%d)\n",
IgnoreError )) );
IgnoreError = DsRolepRemoveSysVolPath( PromoteArgs->SysVolRootPath,
PromoteArgs->DnsDomainName,
&DomainGuid );
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to remove system volume path (%d)\n",
IgnoreError )) );
SysVolCreated = FALSE;
}
if ( RestartNetlogon ) {
IgnoreError = DsRolepStartNetlogon();
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to restart netlogon (%d)\n",
IgnoreError )) );
RestartNetlogon = FALSE;
}
//
// We are finished the undo -- exit the thread
//
ASSERT( ERROR_SUCCESS != WinError );
goto PromoteExit;
}
DWORD
DsRolepThreadPromoteReplica(
IN PVOID ArgumentBlock
)
/*++
Routine Description:
This function actually "promotes" a server to a replica of an existing domain. This is
accomplished by:
Installing the Ds as a replica
Setting the DnsDomainTree LSA information
Configuring the KDC
Required are the Dns domain name and the name of a replica within the domain, and the
Db and Log paths
Arguments:
ArgumentBlock - Block of arguments appropriate for the operation
Returns:
ERROR_SUCCESS - Success
--*/
{
DWORD WinError = ERROR_SUCCESS, IgnoreError;
PDSROLEP_OPERATION_PROMOTE_ARGS PromoteArgs = (PDSROLEP_OPERATION_PROMOTE_ARGS)ArgumentBlock;
PDOMAIN_CONTROLLER_INFO DomainControllerInfo = NULL;
DSROLEP_DOMAIN_POLICY_INFO BackupDomainPolicyInfo;
ULONG FindOptions = 0;
GUID DomainGuid;
PWSTR InstalledSite = NULL, ReplicaServer = NULL;
PSID NewDomainSid = NULL;
WCHAR LocalMachineAccountName[ MAX_COMPUTERNAME_LENGTH + 2 ];
ULONG Length = MAX_COMPUTERNAME_LENGTH + 1;
//
// BOOLEAN's to maintain state
//
// N.B. The order of these booleans is the order in which they
// are changed -- please maintain order and make sure that
// the PromoteUndo section undoes them in the reverse order
//
BOOLEAN IPCConnection = FALSE; // resource -- release on exit
BOOLEAN RestartNetlogon = FALSE;
BOOLEAN SysVolCreated = FALSE;
BOOLEAN DsInstalled = FALSE;
BOOLEAN DsRunning = FALSE;
BOOLEAN DomainPolicyInfoChanged = FALSE;
BOOLEAN DomainControllerServicesChanged = FALSE;
BOOLEAN ProductTypeChanged = FALSE;
RtlZeroMemory(&BackupDomainPolicyInfo, sizeof(BackupDomainPolicyInfo));
//
// Set our event to indicate we're starting
//
NtSetEvent( DsRolepCurrentOperationHandle.CompletionEvent, NULL );
//
// Get the account name
//
if ( GetComputerName( LocalMachineAccountName, &Length ) == FALSE ) {
WinError = GetLastError();
DsRolepLogPrint(( DEB_ERROR, "Failed to get computer name (%d)\n", WinError ));
goto PromoteUndo;
} else {
wcscat( LocalMachineAccountName, L"$" );
}
//
// Strip the trailing '.' from the Dns name if we happen to have an absolute name
//
DSROLEP_MAKE_DNS_RELATIVE( PromoteArgs->DnsDomainName );
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
if (PromoteArgs->Server) {
if ( FLAG_ON( PromoteArgs->Options, DSROLE_DC_FORCE_TIME_SYNC ) ) {
WinError = DsRolepForceTimeSync( PromoteArgs->ImpersonateToken,
PromoteArgs->Server );
if ( ERROR_SUCCESS != WinError ) {
// the machine object was moved
DsRolepLogPrint(( DEB_WARN, "Time sync with %ws failed with %d\n",
PromoteArgs->Server,
WinError ));
WinError = ERROR_SUCCESS;
}
}
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// Start a connection to the ReplicaServer
//
RtlRunDecodeUnicodeString( PromoteArgs->Decode, &PromoteArgs->Password );
WinError = ImpNetpManageIPCConnect( PromoteArgs->ImpersonateToken,
PromoteArgs->Server,
PromoteArgs->Account,
PromoteArgs->Password.Buffer,
NETSETUPP_CONNECT_IPC );
RtlRunEncodeUnicodeString( &PromoteArgs->Decode, &PromoteArgs->Password );
if ( WinError != ERROR_SUCCESS ) {
DSROLEP_FAIL1( WinError, DSROLERES_NET_USE, PromoteArgs->Server );
DsRolepLogPrint(( DEB_ERROR,
"Failed to establish the session with %ws: 0x%lx\n", PromoteArgs->Server,
WinError ));
goto PromoteUndo;
}
ReplicaServer = PromoteArgs->Server;
IPCConnection = TRUE;
}
//
// Find the server that holds the machine account for this machine
//
FindOptions = DS_DIRECTORY_SERVICE_REQUIRED | DS_WRITABLE_REQUIRED | DS_FORCE_REDISCOVERY |
DS_RETURN_DNS_NAME;
WinError = ImpDsRolepDsGetDcForAccount( PromoteArgs->ImpersonateToken,
PromoteArgs->Server,
PromoteArgs->DnsDomainName,
LocalMachineAccountName,
FindOptions,
UF_WORKSTATION_TRUST_ACCOUNT |
UF_SERVER_TRUST_ACCOUNT,
&DomainControllerInfo );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to get domain controller for account %ws (%d)\n", LocalMachineAccountName, WinError ));
DSROLEP_FAIL1( WinError, DSROLERES_FIND_DC, PromoteArgs->DnsDomainName );
goto PromoteUndo;
}
//
// Determine source server
//
if ( NULL == PromoteArgs->Server ) {
//
// No server was passed -- use the result of the dsgetdc
//
ReplicaServer = DomainControllerInfo->DomainControllerName;
} else {
ReplicaServer = PromoteArgs->Server;
if ( !DnsNameCompare_W(*(PromoteArgs->Server)==L'\\'?(PromoteArgs->Server)+2:PromoteArgs->Server,
*(DomainControllerInfo->DomainControllerName)==L'\\'?(DomainControllerInfo->DomainControllerName)+2:DomainControllerInfo->DomainControllerName ) ) {
WinError = ERROR_DS_UNWILLING_TO_PERFORM;
DsRolepLogPrint(( DEB_ERROR, "DsGetDcForAccount Failed to get the requested domain controller %ws for account %ws (%d)\n",
PromoteArgs->Server,
LocalMachineAccountName,
WinError));
DSROLEP_FAIL3( WinError,
DSROLERES_FAILED_FIND_REQUESTED_DC,
PromoteArgs->Server,
LocalMachineAccountName,
DomainControllerInfo->DomainControllerName );
goto PromoteUndo;
}
}
//
// Determine destination site
//
if ( PromoteArgs->SiteName == NULL ) {
PromoteArgs->SiteName = DomainControllerInfo->ClientSiteName;
if ( PromoteArgs->SiteName == NULL ) {
PromoteArgs->SiteName = DomainControllerInfo->DcSiteName;
}
}
DSROLEP_CURRENT_OP2( DSROLEEVT_FOUND_SITE,
PromoteArgs->SiteName,
ReplicaServer );
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
if (!IPCConnection) {
//
// Force the time synch
//
DsRolepLogPrint(( DEB_TRACE, "Forcing time sync\n"));
if ( FLAG_ON( PromoteArgs->Options, DSROLE_DC_FORCE_TIME_SYNC ) ) {
WinError = DsRolepForceTimeSync( PromoteArgs->ImpersonateToken,
ReplicaServer );
if ( ERROR_SUCCESS != WinError ) {
// the machine object was moved
DsRolepLogPrint(( DEB_WARN, "Time sync with %ws failed with %d\n",
ReplicaServer,
WinError ));
WinError = ERROR_SUCCESS;
}
}
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// Attempt to start a RDR connection because we will need one later on
//
RtlRunDecodeUnicodeString( PromoteArgs->Decode, &PromoteArgs->Password );
WinError = ImpNetpManageIPCConnect( PromoteArgs->ImpersonateToken,
ReplicaServer,
PromoteArgs->Account,
PromoteArgs->Password.Buffer,
NETSETUPP_CONNECT_IPC );
RtlRunEncodeUnicodeString( &PromoteArgs->Decode, &PromoteArgs->Password );
if ( WinError != ERROR_SUCCESS ) {
DSROLEP_FAIL1( WinError, DSROLERES_NET_USE, ReplicaServer );
DsRolepLogPrint(( DEB_ERROR,
"Failed to establish the session with %ws: 0x%lx\n", ReplicaServer,
WinError ));
goto PromoteUndo;
}
IPCConnection = TRUE;
}
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// Stop netlogon
//
DSROLEP_CURRENT_OP1( DSROLEEVT_STOP_SERVICE, SERVICE_NETLOGON );
WinError = DsRolepStopNetlogon( &RestartNetlogon );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to stop NETLOGON (%d)\n", WinError ));
goto PromoteUndo;
}
DsRolepLogPrint(( DEB_TRACE, "Stopped NETLOGON\n" ));
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// Create the system volume information
//
RtlRunDecodeUnicodeString( PromoteArgs->Decode, &PromoteArgs->Password );
WinError = DsRolepCreateSysVolPath( PromoteArgs->SysVolRootPath,
PromoteArgs->DnsDomainName,
ReplicaServer,
PromoteArgs->Account,
PromoteArgs->Password.Buffer,
PromoteArgs->SiteName,
FALSE );
RtlRunEncodeUnicodeString( &PromoteArgs->Decode, &PromoteArgs->Password );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to create system volume path (%d)\n", WinError ));
goto PromoteUndo;
}
DsRolepLogPrint(( DEB_TRACE, "Created system volume path\n" ));
SysVolCreated = TRUE;
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// Setup the Ds
//
RtlRunDecodeUnicodeString( PromoteArgs->Decode, &PromoteArgs->DomainAdminPassword );
RtlRunDecodeUnicodeString( PromoteArgs->Decode, &PromoteArgs->Password );
RtlRunDecodeUnicodeString( PromoteArgs->Decode, &PromoteArgs->SafeModePassword );
WinError = DsRolepInstallDs( PromoteArgs->DnsDomainName,
PromoteArgs->FlatDomainName,
NULL, // DnsTreeRoot not used for replica installs
PromoteArgs->SiteName,
PromoteArgs->DsDatabasePath,
PromoteArgs->DsLogPath,
PromoteArgs->RestorePath,
PromoteArgs->SysVolRootPath,
&(PromoteArgs->Bootkey),
PromoteArgs->DomainAdminPassword.Buffer,
PromoteArgs->Parent,
ReplicaServer,
PromoteArgs->Account,
PromoteArgs->Password.Buffer,
PromoteArgs->SafeModePassword.Buffer,
PromoteArgs->DnsDomainName,
PromoteArgs->Options,
TRUE,
PromoteArgs->ImpersonateToken,
&InstalledSite,
&DomainGuid,
&NewDomainSid );
RtlRunEncodeUnicodeString( &PromoteArgs->Decode, &PromoteArgs->Password );
RtlRunEncodeUnicodeString( &PromoteArgs->Decode, &PromoteArgs->DomainAdminPassword );
RtlRunEncodeUnicodeString( &PromoteArgs->Decode, &PromoteArgs->SafeModePassword );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to install to Directory Service (%d)\n", WinError ));
goto PromoteUndo;
}
DsRunning = TRUE;
DsInstalled = TRUE;
DsRolepLogPrint(( DEB_TRACE, "Installed Directory Service\n" ));
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// Set the lsa domain information to reflect the new security database
// that was brought in. The Set below does not set the DnsDomainInformation,
// since the flat name is not yet known. The DnsDomainInformation gets
// set by the DsRolepSetLsaInformationForReplica call following.
//
WinError = DsRolepBackupDomainPolicyInfo( NULL, &BackupDomainPolicyInfo );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to make backup of LSA policy (%d)\n", WinError ));
DSROLEP_FAIL0( WinError, DSROLERES_POLICY_READ_LOCAL );
goto PromoteUndo;
}
WinError = DsRolepSetLsaDomainPolicyInfo( PromoteArgs->DnsDomainName,
PromoteArgs->FlatDomainName,
NULL,
&DomainGuid,
NewDomainSid,
NTDS_INSTALL_REPLICA,
&BackupDomainPolicyInfo );
if ( ERROR_SUCCESS != WinError ) {
DSROLEP_FAIL0( WinError, DSROLERES_POLICY_WRITE_LOCAL );
goto PromoteUndo;
}
DomainPolicyInfoChanged = TRUE;
DsRolepLogPrint(( DEB_TRACE, "Wrote the LSA policy information for the local machine\n" ));
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// This extra call is necessary to get the dns tree information
//
RtlRunDecodeUnicodeString( PromoteArgs->Decode, &PromoteArgs->Password );
WinError = DsRolepSetLsaInformationForReplica( PromoteArgs->ImpersonateToken,
ReplicaServer,
PromoteArgs->Account,
PromoteArgs->Password.Buffer );
RtlRunEncodeUnicodeString( &PromoteArgs->Decode, &PromoteArgs->Password );
if ( ERROR_SUCCESS != WinError ) {
DSROLEP_FAIL1( WinError, DSROLERES_POLICY_READ_REMOTE, ReplicaServer );
goto PromoteUndo;
}
DsRolepLogPrint(( DEB_TRACE, "Read the LSA policy information from %ws\n",
ReplicaServer ));
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// Configure the services for a domain controller
//
WinError = DsRolepConfigureDomainControllerServices( DSROLEP_SERVICES_ON );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to configure domain controller services (%d)\n", WinError ));
goto PromoteUndo;
}
DomainControllerServicesChanged = TRUE;
DsRolepLogPrint(( DEB_TRACE, "Configured domain controller services\n" ));
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, PromoteUndo );
//
// Set the computers Dns domain name
//
DSROLEP_CURRENT_OP1( DSROLEEVT_SET_COMPUTER_DNS, PromoteArgs->DnsDomainName );
WinError = NetpSetDnsComputerNameAsRequired( PromoteArgs->DnsDomainName );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogOnFailure( WinError,
DsRolepLogPrint(( DEB_TRACE,
"NetpSetDnsComputerNameAsRequired to %ws failed with %lu\n",
PromoteArgs->DnsDomainName,
WinError )) );
DSROLEP_FAIL1( WinError, DSROLERES_SET_COMPUTER_DNS, PromoteArgs->DnsDomainName );
goto PromoteUndo;
}
DsRolepLogPrint(( DEB_TRACE, "Set the computer's Dns domain name to %ws.\n",
PromoteArgs->DnsDomainName ));
//
// Complete the sysvol replication
//
if ( SysVolCreated ) {
WinError = DsRolepFinishSysVolPropagation( TRUE, TRUE );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to complete system volume replication (%d)\n", WinError ));
goto PromoteUndo;
}
DsRolepLogPrint(( DEB_TRACE, "Completed system volume replication\n" ));
}
//
// Set the machine role
//
WinError = DsRolepSetProductType( DSROLEP_MT_MEMBER );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to set the product type (%d)\n", WinError ));
goto PromoteUndo;
}
DsRolepLogPrint(( DEB_TRACE, "Set the product type\n" ));
ProductTypeChanged = TRUE;
//
// Save off the new site name
//
WinError = DsRolepSetOperationHandleSiteName( InstalledSite );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to set the operation handle(%d)\n", WinError ));
goto PromoteUndo;
}
//
// If we update it, NULL out the local parameter so we don't attempt to delete it
//
InstalledSite = NULL;
//
// Next, set the sysvol path for netlogon
//
WinError = DsRolepSetNetlogonSysVolPath( PromoteArgs->SysVolRootPath,
PromoteArgs->DnsDomainName,
FALSE,
NULL );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to set the system volume path for NETLOGON (%d)\n", WinError ));
goto PromoteUndo;
}
DsRolepLogPrint(( DEB_TRACE, "Set the system volume path for NETLOGON\n" ));
//
// Finally, set the security on the dc files
//
WinError = DsRolepSetDcSecurity( PromoteArgs->ImpersonateToken,
PromoteArgs->SysVolRootPath,
PromoteArgs->DsDatabasePath,
PromoteArgs->DsLogPath,
( BOOLEAN )FLAG_ON( PromoteArgs->Options,
DSROLE_DC_DOWNLEVEL_UPGRADE ),
TRUE );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to set security for the domain controller (%d)\n", WinError ));
goto PromoteUndo;
}
DsRolepLogPrint(( DEB_TRACE, "Set security for the domain controller\n" ));
//
// We have done all operations for the promotion; now continue replicating
// ds information until done, or cancelled
//
DsRolepLogPrint(( DEB_TRACE, "Replicating non critical information\n" ));
DsRolepSetCriticalOperationsDone();
if ( !FLAG_ON( PromoteArgs->Options, DSROLE_DC_CRITICAL_REPLICATION_ONLY ) ) {
//in the Install From Media case we do not want to do a full sync of the
//Non-Critical objects
if ((PromoteArgs->RestorePath != NULL)) {
WinError = (*DsrNtdsInstallReplicateFull) ( DsRolepStringUpdateCallback, PromoteArgs->ImpersonateToken, NTDS_IFM_PROMOTION );
} else {
WinError = (*DsrNtdsInstallReplicateFull) ( DsRolepStringUpdateCallback, PromoteArgs->ImpersonateToken, 0 );
}
if ( WinError != ERROR_SUCCESS ) {
//
// Error code doesn't matter, but we'll log it anyway
//
DsRolepLogOnFailure( WinError,
DsRolepLogPrint(( DEB_WARN,
"Non critical replication returned %lu\n", WinError )) );
if (ERROR_SUCCESS == WinError) {
DsRolepLogPrint(( DEB_TRACE, "Replicating non critical information (Complete)\n" ));
}
if ( ERROR_SUCCESS != WinError ) {
DSROLEP_SET_NON_CRIT_REPL_ERROR();
}
WinError = ERROR_SUCCESS;
}
} else {
DsRolepLogPrint(( DEB_TRACE, "User specified to not replicate non-critical data\n" ));
}
//
// Indicate that we are no longer doing upgrades, if applicable
//
if ( FLAG_ON( PromoteArgs->Options, DSROLE_DC_DOWNLEVEL_UPGRADE ) ) {
WinError = DsRolepDeleteUpgradeInfo();
DsRolepLogOnFailure( WinError,
DsRolepLogPrint(( DEB_WARN,
"Failed to remove upgrade information (%d)\n",
WinError )) );
// This error isn't interesting to propogate
WinError = ERROR_SUCCESS;
}
//
// Remove any old netlogon stuff if we got that far
//
WinError = DsRolepCleanupOldNetlogonInformation();
if ( (FLAG_ON( PromoteArgs->Options, DSROLE_DC_DOWNLEVEL_UPGRADE )) && ERROR_SUCCESS != WinError ) {
if (ERROR_SUCCESS == WinError) {
DsRolepLogPrint(( DEB_TRACE, "Removed any old netlogon information\n" ));
}
DsRolepLogOnFailure( WinError,
DsRolepLogPrint(( DEB_WARN,
"Failed to clean up old netlogon information (%d)\n",
WinError )) );
}
WinError = ERROR_SUCCESS;
//
// Set the default logon domain to the current domain name
//
//
// We'll have to get it from the backed up policy information, since it isn't actually
// passed in
//
WinError = DsRolepSetLogonDomain(
( PWSTR )BackupDomainPolicyInfo.DnsDomainInfo->Name.Buffer,
FALSE );
if ( ERROR_SUCCESS != WinError ) {
PWCHAR bufDnsDomainInfo = NULL;
bufDnsDomainInfo = (WCHAR*)malloc(BackupDomainPolicyInfo.DnsDomainInfo->Name.Length+1);
if (bufDnsDomainInfo) {
CopyMemory(bufDnsDomainInfo,
BackupDomainPolicyInfo.DnsDomainInfo->Name.Buffer,
BackupDomainPolicyInfo.DnsDomainInfo->Name.Length);
bufDnsDomainInfo[BackupDomainPolicyInfo.DnsDomainInfo->Name.Length/sizeof(WCHAR)] = L'\0';
DsRolepLogOnFailure( WinError,
DsRolepLogPrint(( DEB_WARN,
"Failed to set default logon domain to %ws (%d)\n",
bufDnsDomainInfo,
WinError )) );
if (ERROR_SUCCESS == WinError) {
DsRolepLogPrint(( DEB_TRACE, "Set default logon domain to %ws\n",
bufDnsDomainInfo ));
}
free(bufDnsDomainInfo);
}
//
// This is not worth failing for
//
WinError = ERROR_SUCCESS;
}
//
// Stop the ds
//
DsRolepStopDs( DsRunning );
DsRunning = FALSE;
DsRolepLogPrint(( DEB_TRACE, "Stopped the DS\n" ));
//
// Notify the time server we have completed the promotion
//
(*DsrW32TimeDcPromo)( W32TIME_PROMOTE );
//
// Set Netlogon registry key during DCPromo to ensure that kerberos is talking
// to a DC w/ new User AccountControl flag
//
IgnoreError = NetpStoreIntialDcRecord(DomainControllerInfo);
if ( IgnoreError != ERROR_SUCCESS ) {
DsRolepLogPrint(( DEB_WARN,
"Failed to set Netlogon registry key during DCPromo %ws\r\n",
IgnoreError ));
}
//
// At this point we have succeeded the promotion
//
ASSERT( ERROR_SUCCESS == WinError );
PromoteExit:
//
// Released acquired resources
//
if ( IPCConnection ) {
RtlRunDecodeUnicodeString( PromoteArgs->Decode, &PromoteArgs->Password );
IgnoreError = ImpNetpManageIPCConnect( PromoteArgs->ImpersonateToken,
ReplicaServer,
PromoteArgs->Account,
PromoteArgs->Password.Buffer,
(NETSETUPP_DISCONNECT_IPC | NETSETUPP_USE_LOTS_FORCE ) );
RtlRunEncodeUnicodeString( &PromoteArgs->Decode, &PromoteArgs->Password );
if ( IgnoreError != ERROR_SUCCESS ) {
DsRolepLogPrint(( DEB_ERROR,
"Failed to destroy the session with %ws: 0x%lx\n", ReplicaServer,
IgnoreError ));
}
IPCConnection = FALSE;
}
if ( DomainControllerInfo != NULL ) {
if ( PromoteArgs->SiteName == DomainControllerInfo->ClientSiteName ||
PromoteArgs->SiteName == DomainControllerInfo->DcSiteName ) {
PromoteArgs->SiteName = NULL;
}
NetApiBufferFree( DomainControllerInfo );
}
RtlFreeHeap( RtlProcessHeap(), 0, InstalledSite );
RtlFreeHeap( RtlProcessHeap(), 0, NewDomainSid );
DsRolepFreeDomainPolicyInfo(&BackupDomainPolicyInfo);
//
// Reset our operation handle
//
DsRolepSetOperationDone( DSROLEP_OP_PROMOTION, WinError );
DsRolepFreeArgumentBlock( &ArgumentBlock, TRUE );
ExitThread( WinError );
return( WinError );
PromoteUndo:
//
// Something must have failed to have gotten us here
//
ASSERT( ERROR_SUCCESS != WinError );
if ( ProductTypeChanged ) {
IgnoreError = DsRolepSetProductType( DSROLEP_MT_STANDALONE );
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to rollback product type (%d)\n",
IgnoreError )) );
ProductTypeChanged = FALSE;
}
if ( DomainControllerServicesChanged ) {
IgnoreError = DsRolepConfigureDomainControllerServices( DSROLEP_SERVICES_REVERT );
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to rollback domain controller services configuration (%d)\n",
IgnoreError )) );
DomainControllerServicesChanged = FALSE;
}
if ( DomainPolicyInfoChanged ) {
IgnoreError = DsRolepRestoreDomainPolicyInfo(&BackupDomainPolicyInfo);
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to restore domain policy information (%d)\n",
IgnoreError )) );
DomainPolicyInfoChanged = FALSE;
}
if ( DsRunning ) {
IgnoreError = DsRolepStopDs( DsRunning );
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to stop the directory service (%d)\n",
IgnoreError )) );
DsRunning = FALSE;
}
if ( DsInstalled ) {
IgnoreError = DsRolepUninstallDs( );
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to undo the directory service installation (%d)\n",
IgnoreError )) );
DsInstalled = FALSE;
}
if ( SysVolCreated ) {
IgnoreError = DsRolepFinishSysVolPropagation( FALSE, TRUE );
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to abort system volume installation (%d)\n",
IgnoreError )) );
IgnoreError = DsRolepRemoveSysVolPath( PromoteArgs->SysVolRootPath,
PromoteArgs->DnsDomainName,
&DomainGuid );
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to remove system volume path (%d)\n",
IgnoreError )) );
SysVolCreated = FALSE;
}
if ( RestartNetlogon ) {
IgnoreError = DsRolepStartNetlogon();
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to restart NETLOGON (%d)\n",
IgnoreError )) );
RestartNetlogon = FALSE;
}
//
// That's it -- terminate the operation
//
ASSERT( ERROR_SUCCESS != WinError );
goto PromoteExit;
}
DWORD
DsRolepThreadDemote(
IN PVOID ArgumentBlock
)
/*++
Routine Description:
This function actually "demotes" a dc to standalone or member server. This is
accomplished by:
Uninstalling the Ds
Configuring the KDC
Changing the product type
Removing the system volume tree
Required is the new server role
Arguments:
ArgumentBlock - Block of arguments appropriate for the operation
Returns:
ERROR_SUCCESS - Success
ERROR_NO_SUCH_DOMAIN - The local domain information could not be located
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
ERROR_DS_CANT_ON_NON_LEAF - The domain is not a leaf domain
--*/
{
DWORD WinError = ERROR_SUCCESS, IgnoreError;
NET_API_STATUS NetStatus = ERROR_SUCCESS;
PDOMAIN_CONTROLLER_INFO DomainControllerInfo = NULL;
PDSROLEP_OPERATION_DEMOTE_ARGS DemoteArgs = ( PDSROLEP_OPERATION_DEMOTE_ARGS )ArgumentBlock;
DSROLEP_DOMAIN_POLICY_INFO BackupDomainPolicyInfo;
PPOLICY_DNS_DOMAIN_INFO DnsDomainInfo = NULL;
HANDLE Policy = NULL;
NTSTATUS Status;
PWSTR ParentDomainName = NULL, CurrentDomain = NULL, SupportDc = NULL;
PWSTR SupportDomain = NULL;
OBJECT_ATTRIBUTES ObjectAttributes;
ULONG ServicesOffFlags = DSROLEP_SERVICES_OFF | DSROLEP_SERVICES_STOP;
ULONG ServicesOnFlags = DSROLEP_SERVICES_REVERT;
PNTDS_DNS_RR_INFO pDnsRRInfo = NULL;
ULONG Flags = 0;
PSEC_WINNT_AUTH_IDENTITY Credentials = NULL;
//
// BOOLEAN's to maintain state
//
// N.B. The order of these booleans is the order in which they
// are changed -- please maintain order and make sure that
// the DemoteUndo section undoes them in the reverse order
//
BOOLEAN IPCConnection = FALSE; // resource -- release on exit
BOOLEAN DsPrepareDemote = FALSE;
BOOLEAN FrsDemote = FALSE;
BOOLEAN NotifiedNetlogonToDeregister = FALSE;
BOOLEAN RestartNetlogon = FALSE;
BOOLEAN DomainControllerServicesChanged = FALSE;
BOOLEAN DomainServicesChanged = FALSE;
BOOLEAN Unrollable = FALSE; // at this point, don't
// try to rollback
//
// Set our event to indicate we're starting
//
NtSetEvent( DsRolepCurrentOperationHandle.CompletionEvent, NULL );
//
// Get the current domain information, potentially the parent Domain and see if
// we are valid to be demoted
//
DSROLEP_CURRENT_OP0( DSROLEEVT_LOCAL_POLICY );
RtlZeroMemory( &ObjectAttributes, sizeof( ObjectAttributes ) );
Status = LsaOpenPolicy( NULL,
&ObjectAttributes,
POLICY_VIEW_LOCAL_INFORMATION,
&Policy );
if ( NT_SUCCESS( Status ) ) {
Status = LsaQueryInformationPolicy( Policy,
PolicyDnsDomainInformation,
&DnsDomainInfo );
}
if ( !NT_SUCCESS( Status ) ) {
WinError = RtlNtStatusToDosError( Status );
DSROLEP_FAIL0( WinError, DSROLERES_POLICY_READ_LOCAL );
goto DemoteUndo;
}
if ( DemoteArgs->DomainName == NULL ) {
CurrentDomain = DnsDomainInfo->DnsDomainName.Buffer;
} else {
//
// Strip the trailing '.' from the Dns name if we happen to have an absolute name
//
DSROLEP_MAKE_DNS_RELATIVE( DemoteArgs->DomainName );
CurrentDomain = DemoteArgs->DomainName;
}
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, DemoteUndo );
//
// Determine whether it is legal to demote this domain. Also, get the parent Dns domain name
//
if ( DemoteArgs->LastDcInDomain ) {
PLSAPR_FOREST_TRUST_INFO ForestTrustInfo = NULL;
PLSAPR_TREE_TRUST_INFO OwnEntry = NULL, ParentEntry = NULL;
Status = LsaIQueryForestTrustInfo( Policy,
&ForestTrustInfo );
WinError = RtlNtStatusToDosError( Status );
if ( WinError == ERROR_SUCCESS ) {
//
// Check the root
//
if ( RtlCompareUnicodeString(
( PUNICODE_STRING )&ForestTrustInfo->RootTrust.DnsDomainName,
&DnsDomainInfo->DnsDomainName,
TRUE ) == 0 ) {
OwnEntry = &ForestTrustInfo->RootTrust;
ParentEntry = NULL;
} else {
//
// Find our own entry in the list and our parent...
//
DsRolepFindSelfAndParentInForest( ForestTrustInfo,
&ForestTrustInfo->RootTrust,
&DnsDomainInfo->DnsDomainName,
&ParentEntry,
&OwnEntry );
}
if ( OwnEntry == NULL ) {
WinError = ERROR_NO_SUCH_DOMAIN;
} else {
//
// If we have children, it's an error
//
if ( OwnEntry->Children != 0 ) {
WCHAR *BufOwnEntry = NULL;
DsRolepUnicodestringtowstr( BufOwnEntry, OwnEntry->DnsDomainName )
if (BufOwnEntry) {
DsRolepLogPrint(( DEB_TRACE,
"We ( %ws ) think we have %lu children\n",
BufOwnEntry,
OwnEntry->Children ));
free(BufOwnEntry);
} else {
DsRolepLogPrint(( DEB_TRACE,
"We think we have %lu children: Can display string ERROR_NOT_ENOUGH_MEMORY\n",
OwnEntry->Children ));
}
WinError = ERROR_DS_CANT_ON_NON_LEAF;
}
//
// Copy off our parent information
//
if ( WinError == ERROR_SUCCESS && ParentEntry != NULL ) {
WCHAR *BufOwnEntry = NULL;
DsRolepUnicodestringtowstr( BufOwnEntry, OwnEntry->DnsDomainName )
if (BufOwnEntry) {
DsRolepLogPrint((DEB_TRACE,
"Domain %ws is our parent parent\n",
BufOwnEntry));
free(BufOwnEntry);
} else {
DsRolepLogPrint(( DEB_TRACE,
"Domain (?) is our parent parent: Can display domain string ERROR_NOT_ENOUGH_MEMORY\n"));
}
ParentDomainName = RtlAllocateHeap(
RtlProcessHeap(), 0,
ParentEntry->DnsDomainName.Length + sizeof( WCHAR ) );
if ( ParentDomainName == NULL ) {
WinError = ERROR_NOT_ENOUGH_MEMORY;
} else {
RtlCopyMemory( ParentDomainName,
ParentEntry->DnsDomainName.Buffer,
ParentEntry->DnsDomainName.Length );
ParentDomainName[
ParentEntry->DnsDomainName.Length / sizeof( WCHAR ) ] = UNICODE_NULL;
}
}
}
}
LsaIFreeForestTrustInfo( ForestTrustInfo );
if ( ERROR_SUCCESS != WinError ) {
DSROLEP_FAIL1( WinError, DSROLERES_LEAF_DOMAIN, CurrentDomain );
goto DemoteUndo;
}
}
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, DemoteUndo );
//
// Locate a Dc to help with the demotion
//
if ( DemoteArgs->LastDcInDomain ) {
SupportDomain = ParentDomainName;
} else {
SupportDomain = CurrentDomain;
}
//
// If this is the last domain in the enterprise, there will be no
// parent domain and possibly no replicas to assist.
//
// Note: netlogon is still running, so use the avoid self flag
//
if ( SupportDomain ) {
DSROLEP_CURRENT_OP1( DSROLEEVT_SEARCH_DC, SupportDomain );
if ( !DemoteArgs->LastDcInDomain )
{
//
// Demoting a replica - find someone with our machine account
//
ULONG FindOptions = DS_DIRECTORY_SERVICE_REQUIRED |
DS_WRITABLE_REQUIRED |
DS_FORCE_REDISCOVERY |
DS_AVOID_SELF;
WCHAR LocalMachineAccountName[ MAX_COMPUTERNAME_LENGTH + 2 ];
ULONG Length = sizeof(LocalMachineAccountName) / sizeof(LocalMachineAccountName[0]);
//
// Get the account name
//
if ( GetComputerName( LocalMachineAccountName, &Length ) == FALSE ) {
WinError = GetLastError();
DsRolepLogPrint(( DEB_ERROR, "Failed to get computer name (%d)\n", WinError ));
goto DemoteUndo;
} else {
wcscat( LocalMachineAccountName, L"$" );
WinError = DsRolepDsGetDcForAccount( NULL,
SupportDomain,
LocalMachineAccountName,
FindOptions,
UF_WORKSTATION_TRUST_ACCOUNT |
UF_SERVER_TRUST_ACCOUNT,
&DomainControllerInfo );
}
} else {
WinError = DsGetDcName( NULL, SupportDomain, NULL, NULL,
DS_DIRECTORY_SERVICE_REQUIRED |
DS_WRITABLE_REQUIRED |
DS_AVOID_SELF |
DS_FORCE_REDISCOVERY,
&DomainControllerInfo );
}
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR,
"Failed to find a domain controller for %ws: %lu\n",
SupportDomain, WinError ));
DSROLEP_FAIL1( WinError, DSROLERES_FIND_DC, SupportDomain );
goto DemoteUndo;
}
SupportDc = DomainControllerInfo->DomainControllerName;
if ( *SupportDc == L'\\' ) {
SupportDc += 2;
}
DsRolepLogPrint(( DEB_TRACE_DS, "Support Dc in %ws is %ws\n",
SupportDomain,
SupportDc ));
DSROLEP_CURRENT_OP2( DSROLEEVT_FOUND_DC,
SupportDc,
SupportDomain );
}
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, DemoteUndo );
//
// Attempt to establish a RDR session with our support DC
// if necessary
//
if ( SupportDc ) {
//
// Impersonate to get logon id of caller
//
RtlRunDecodeUnicodeString( DemoteArgs->Decode, &DemoteArgs->Password );
WinError = ImpNetpManageIPCConnect( DemoteArgs->ImpersonateToken,
SupportDc,
DemoteArgs->Account,
DemoteArgs->Password.Buffer,
NETSETUPP_CONNECT_IPC );
RtlRunEncodeUnicodeString( &DemoteArgs->Decode, &DemoteArgs->Password );
if ( ERROR_SUCCESS != WinError ) {
DSROLEP_FAIL1( WinError, DSROLERES_NET_USE, SupportDc );
DsRolepLogPrint(( DEB_ERROR,
"Failed to establish the session with %ws: 0x%lx\n", SupportDc,
WinError ));
goto DemoteUndo;
}
IPCConnection = TRUE;
}
//
// Prepare the ds for demotion
//
DSROLE_GET_SETUP_FUNC( WinError, DsrNtdsPrepareForDemotion );
ASSERT( ERROR_SUCCESS == WinError );
RtlRunDecodeUnicodeString( DemoteArgs->Decode, &DemoteArgs->Password );
WinError = DsRolepCreateAuthIdentForCreds(DemoteArgs->Account,
DemoteArgs->Password.Buffer,
&Credentials);
if ( ERROR_SUCCESS == WinError ) {
if ( DemoteArgs->LastDcInDomain ) {
Flags |= NTDS_LAST_DC_IN_DOMAIN;
}
Flags |= DsRolepDemoteFlagsToNtdsFlags( DemoteArgs->Options );
DSROLEP_CURRENT_OP0( DSROLEEVT_PREPARE_DEMOTION );
WinError = ( *DsrNtdsPrepareForDemotion ) ( Flags,
SupportDc,
Credentials,
DsRolepStringUpdateCallback,
DsRolepStringErrorUpdateCallback,
DemoteArgs->ImpersonateToken,
&pDnsRRInfo );
RtlRunEncodeUnicodeString( &DemoteArgs->Decode, &DemoteArgs->Password );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to prepare the Directory Service for uninstallation (%d)\n", WinError ));
goto DemoteUndo;
}
DsPrepareDemote = TRUE;
} else {
DsRolepLogPrint(( DEB_ERROR, "Failed to create authentication credentials (%d)\n", WinError ));
goto DemoteUndo;
}
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, DemoteUndo );
//
// Start the sysvol demotions
//
RtlRunDecodeUnicodeString( DemoteArgs->Decode, &DemoteArgs->Password );
WinError = ( *DsrNtFrsApi_PrepareForDemotionUsingCredW ) ( Credentials,
DemoteArgs->ImpersonateToken,
DsRolepStringErrorUpdateCallback );
RtlRunEncodeUnicodeString( &DemoteArgs->Decode, &DemoteArgs->Password );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR, "Failed to get computer name (%d)\n", WinError ));
DSROLEP_FAIL0( WinError, DSROLERES_SYSVOL_DEMOTION );
goto DemoteUndo;
}
WinError = ( *DsrNtFrsApi_StartDemotionW )( CurrentDomain,
DsRolepStringErrorUpdateCallback );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR,
"Failed to start system volume demotion on domain (%d)\n",
WinError ));
DSROLEP_FAIL0( WinError, DSROLERES_SYSVOL_DEMOTION );
goto DemoteUndo;
}
// At this point we have signalled one frs replica set to be demote so
// we must wait on it
FrsDemote = TRUE;
WinError = ( *DsrNtFrsApi_StartDemotionW )( L"ENTERPRISE",
DsRolepStringErrorUpdateCallback );
if ( WinError != ERROR_SUCCESS ) {
DsRolepLogPrint(( DEB_ERROR,
"Failed to start system volume demotion on enterprise (%d)\n",
WinError ));
DSROLEP_FAIL0( WinError, DSROLERES_SYSVOL_DEMOTION );
goto DemoteUndo;
}
DsRolepLogPrint(( DEB_TRACE,
"Started system volume demotion on enterprise\n" ));
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, DemoteUndo );
//
// Note that if a failure occurs after we uninstall the Ds, than we will not attempt to
// reinstall it, since we don't have enough information to do so. In that case, the machine
// will be in a somewhat inconsistent state. However, some errors are acceptable:
//
// Failure to delete the trusted domain object - Continuable
// Stoping the KDC - Continuable
//
//
// Also, note that "uninstalling the DS" also sets the LSA account domain
// sid and the server role so no errors should be returned to the caller
// after uninstalling the DS. The machine will become the new role on the
// next reboot.
//
WinError = DsRolepBackupDomainPolicyInfo( NULL, &BackupDomainPolicyInfo );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR,
"Failed to backup LSA domain policy (%d)\n",
WinError ));
DSROLEP_FAIL0( WinError, DSROLERES_POLICY_READ_LOCAL );
goto DemoteUndo;
}
DsRolepLogPrint(( DEB_TRACE,
"Read the LSA policy information from the local machine\n" ));
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, DemoteUndo );
//
// Set netlogon we are demoting so it will deregister the DNS records
//
Status = I_NetNotifyDsChange( NlDcDemotionInProgress );
if ( !NT_SUCCESS( Status ) ) {
WinError = RtlNtStatusToDosError( Status );
DsRolepLogPrint(( DEB_ERROR,
"Failed to tell NETLOGON to deregister records (%d)\n",
WinError ));
goto DemoteUndo;
}
DsRolepLogPrint(( DEB_TRACE,
"Informed NETLOGON to deregister records\n" ));
NotifiedNetlogonToDeregister = TRUE;
//
// Stop netlogon
//
WinError = DsRolepStopNetlogon( &RestartNetlogon );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR,
"Failed to stop NETLOGON (%d)\n",
WinError ));
goto DemoteUndo;
}
DsRolepLogPrint(( DEB_TRACE,
"Stopped NETLOGON\n" ));
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, DemoteUndo );
//
// Disable the domain controller services
//
WinError = DsRolepConfigureDomainControllerServices( ServicesOffFlags );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR,
"Failed to configure domain controller services (%d)\n",
WinError ));
goto DemoteUndo;
}
DsRolepLogPrint(( DEB_TRACE,
"Configured domain controller services\n" ));
DomainControllerServicesChanged = TRUE;
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, DemoteUndo );
//
// Disable the domain related services if necessary
//
if ( DemoteArgs->ServerRole == DsRoleServerStandalone ) {
WinError = DsRolepConfigureDomainServices( ServicesOffFlags );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR,
"Failed to configure domain services (%d)\n",
WinError ));
goto DemoteUndo;
}
DsRolepLogPrint(( DEB_TRACE,
"Configured domain services\n" ));
DomainServicesChanged = TRUE;
}
DSROLEP_CHECK_FOR_CANCEL_EX( WinError, DemoteUndo );
//
// Remove the Ds
//
RtlRunDecodeUnicodeString( DemoteArgs->Decode, &DemoteArgs->Password );
RtlRunDecodeUnicodeString( DemoteArgs->Decode, &DemoteArgs->AdminPassword );
WinError = DsRolepDemoteDs( CurrentDomain,
DemoteArgs->Account,
DemoteArgs->Password.Buffer,
DemoteArgs->AdminPassword.Buffer,
SupportDc,
SupportDomain,
DemoteArgs->ImpersonateToken,
DemoteArgs->LastDcInDomain );
RtlRunEncodeUnicodeString( &DemoteArgs->Decode, &DemoteArgs->Password );
RtlRunEncodeUnicodeString( &DemoteArgs->Decode, &DemoteArgs->AdminPassword );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogPrint(( DEB_ERROR,
"Failed to demote the directory service (%d)\n",
WinError ));
goto DemoteUndo;
}
DsRolepLogPrint(( DEB_TRACE, "This machine is no longer a domain controller\n" ));
//
// The operation cannot be cancelled at this point since the ds has
// been removed from the machine and from the enterprise
//
Unrollable = TRUE;
//
// Optionally remove the trust with the parent
//
if ( DemoteArgs->LastDcInDomain &&
ParentDomainName != NULL ) {
//
// Establish a session first -- should be a no-op since we already
// have a connection
//
RtlRunDecodeUnicodeString( DemoteArgs->Decode, &DemoteArgs->Password );
WinError = ImpNetpManageIPCConnect( DemoteArgs->ImpersonateToken,
SupportDc,
DemoteArgs->Account,
DemoteArgs->Password.Buffer,
NETSETUPP_CONNECT_IPC );
RtlRunEncodeUnicodeString( &DemoteArgs->Decode, &DemoteArgs->Password );
if ( WinError == ERROR_SUCCESS ) {
WinError = DsRolepDeleteParentTrustObject( DemoteArgs->ImpersonateToken,
SupportDc,
DnsDomainInfo );
if ( WinError != ERROR_SUCCESS ) {
DsRolepLogOnFailure( WinError,
DsRolepLogPrint(( DEB_WARN,
"Failed to delete the "
"trust on %ws: %lu\n",
SupportDc,
WinError )) );
if (ERROR_SUCCESS == WinError) {
DsRolepLogPrint(( DEB_TRACE,
"Deleted the trust on %ws\n",
SupportDc ));
}
}
} else {
// This is not a fatal error
DsRolepLogPrint(( DEB_WARN,
"Failed to establish the session with %ws: 0x%lx\n", SupportDc,
WinError ));
}
//
// This error is not fatal
//
if ( ERROR_SUCCESS != WinError )
{
SpmpReportEvent( TRUE,
EVENTLOG_WARNING_TYPE,
DSROLERES_FAILED_TO_DELETE_TRUST,
0,
sizeof( ULONG ),
&WinError,
1,
ParentDomainName );
DSROLEP_SET_NON_FATAL_ERROR( WinError );
// Error case is handled
WinError = ERROR_SUCCESS;
}
}
//
// Finish our NTFRS demotion
//
if ( FrsDemote ) {
WinError = DsRolepFinishSysVolPropagation( TRUE,
FALSE );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogOnFailure( WinError,
DsRolepLogPrint(( DEB_TRACE,
"Failed to finish system volume demotion (%d)\n",
WinError )) );
if (ERROR_SUCCESS == WinError) {
DsRolepLogPrint(( DEB_TRACE,
"Finished system volume demotion\n" ));
}
}
//
// It is not fatal if the FRS fails at this point
//
if ( ERROR_SUCCESS != WinError )
{
SpmpReportEvent( TRUE,
EVENTLOG_WARNING_TYPE,
DSROLERES_FAILED_TO_DEMOTE_FRS,
0,
sizeof( ULONG ),
&WinError,
0,
NULL );
DSROLEP_SET_NON_FATAL_ERROR( WinError );
}
// Reset status code
WinError = ERROR_SUCCESS;
}
//
// Call into the SCE so we can be configured to be a server
//
WinError = ( *DsrSceDcPromoteSecurityEx )( DemoteArgs->ImpersonateToken,
SCE_PROMOTE_FLAG_DEMOTE,
DsRolepStringUpdateCallback );
if ( ERROR_SUCCESS != WinError ) {
DsRolepLogOnFailure( WinError,
DsRolepLogPrint(( DEB_ERROR,
"Setting security on server files failed with %lu\n",
WinError )) );
if (ERROR_SUCCESS == WinError) {
DsRolepLogPrint(( DEB_TRACE,
"Set security on server files\n" ));
}
// This error has been handled
WinError = ERROR_SUCCESS;
}
//
// remove all trusted root certificates from DC when the machine will dis-join from the enterprise
//
if (DemoteArgs->ServerRole == DsRoleServerStandalone) {
if (!CertAutoRemove(CERT_AUTO_REMOVE_COMMIT)){
DsRolepLogPrint(( DEB_WARN,
"Failed to remove all trusted root certificates from this machine: (%d)\n",
GetLastError()));
}
}
//
// Notify the time server we have completed the demotion
//
(*DsrW32TimeDcPromo)( W32TIME_DEMOTE );
//
// At this point we have successfully completed the demotion
//
ASSERT( ERROR_SUCCESS == WinError );
//
// Clear errors components may have erroneously set while running
//
DsRolepClearErrors();
DemoteExit:
if ( Policy ) {
LsaClose( Policy );
}
if ( Credentials ) {
RtlFreeHeap( RtlProcessHeap(), 0, Credentials );
}
if ( pDnsRRInfo ) {
( *DsrNtdsFreeDnsRRInfo )(pDnsRRInfo);
}
//
// Tear down the session to the parent, if we have one
//
if ( IPCConnection ) {
RtlRunDecodeUnicodeString( DemoteArgs->Decode, &DemoteArgs->Password );
IgnoreError = ImpNetpManageIPCConnect( DemoteArgs->ImpersonateToken,
SupportDc,
DemoteArgs->Account,
DemoteArgs->Password.Buffer,
(NETSETUPP_DISCONNECT_IPC|NETSETUPP_USE_LOTS_FORCE) );
RtlRunEncodeUnicodeString( &DemoteArgs->Decode, &DemoteArgs->Password );
if ( IgnoreError != ERROR_SUCCESS ) {
DsRolepLogPrint(( DEB_WARN,
"Failed to destroy the session with %ws: 0x%lx\n", SupportDc,
IgnoreError ));
}
IPCConnection = FALSE;
}
//Delete persistent shares
NetStatus = NetShareDel( NULL, L"SYSVOL", 0);
if(NetStatus != ERROR_SUCCESS) {
DsRolepLogPrint(( DEB_WARN,
"Failed to destroy the share SYSVOL. Failed with %d\n", NetStatus ));
}
NetStatus = NetShareDel( NULL, L"NETLOGON", 0);
if(NetStatus != ERROR_SUCCESS) {
DsRolepLogPrint(( DEB_WARN,
"Failed to destroy the share NETLOGON. Failed with %d\n", NetStatus ));
}
//
// Reset our operation handle
//
DsRolepSetOperationDone( DSROLEP_OP_DEMOTION, WinError );
DsRolepFreeArgumentBlock( &ArgumentBlock, FALSE );
LsaFreeMemory( DnsDomainInfo );
RtlFreeHeap( RtlProcessHeap(), 0, ParentDomainName );
NetApiBufferFree( DomainControllerInfo );
ExitThread( WinError );
return( WinError );
DemoteUndo:
//
// Assert that aomething went wrong if we are here
//
ASSERT( ERROR_SUCCESS != WinError );
//
// We shouldn't be here if we are in an unrollable state
//
ASSERT( FALSE == Unrollable );
if ( FrsDemote ) {
IgnoreError = DsRolepFinishSysVolPropagation( FALSE,
FALSE );
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to abort system volume demotion (%d)\n",
IgnoreError )) );
FrsDemote = FALSE;
}
if ( NotifiedNetlogonToDeregister ) {
//
// "NlDcDemotionCompleted" sounds strange here since the demotion
// failed. However, the meaning is that netlogon should now continue
// to perform as if demotion is not running. No need to set in the
// success case since NETLOGON won't be restarted.
//
Status = I_NetNotifyDsChange( NlDcDemotionCompleted );
IgnoreError = RtlNtStatusToDosError( Status );
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to tell NETLOGON that demotion is over (%d)\n",
IgnoreError )) );
NotifiedNetlogonToDeregister = FALSE;
}
if ( RestartNetlogon ) {
IgnoreError = DsRolepStartNetlogon();
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to restart netlogon (%d)\n",
IgnoreError )) );
RestartNetlogon = FALSE;
}
if ( DomainControllerServicesChanged ) {
IgnoreError = DsRolepConfigureDomainControllerServices( DSROLEP_SERVICES_REVERT );
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to rollback domain controller services configuration (%d)\n",
IgnoreError )) );
DomainControllerServicesChanged = FALSE;
}
if ( DomainServicesChanged ) {
IgnoreError = DsRolepConfigureDomainServices( DSROLEP_SERVICES_REVERT );
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to rollback domain controller services configuration (%d)\n",
IgnoreError )) );
DomainServicesChanged = FALSE;
}
if ( DsPrepareDemote ) {
IgnoreError = ( *DsrNtdsPrepareForDemotionUndo ) ();
DsRolepLogOnFailure( IgnoreError,
DsRolepLogPrint(( DEB_WARN,
"Failed to undo directory service preparation for demotion (%d)\n",
IgnoreError )) );
DsPrepareDemote = FALSE;
}
//
// Ok -- we have rolled back, make sure we still have an error and then
// exit
//
ASSERT( ERROR_SUCCESS != WinError );
goto DemoteExit;
}