windows-nt/Source/XPSP1/NT/admin/dcpromo/exe/ds.cpp
2020-09-26 16:20:57 +08:00

1391 lines
36 KiB
C++

// Copyright (c) 1997-1999 Microsoft Corporation
//
// DS API wrappers
//
// 12-16-97 sburns
#include "headers.hxx"
#include "ds.hpp"
#include "resource.h"
#include "state.hpp"
#include "common.hpp"
#include "ProgressDialog.hpp"
// CODEWORK: remove the exception throwing architecture.
DS::Error::Error(HRESULT hr_, int summaryResID)
:
Win::Error(hr_, summaryResID)
{
}
DS::Error::Error(HRESULT hr_, const String& msg, const String& sum)
:
Win::Error(hr_, msg, sum)
{
}
bool
DS::IsDomainNameInUse(const String& domainName)
{
LOG_FUNCTION(DS::IsDomainNameInUse);
ASSERT(!domainName.empty());
bool result = false;
if (!domainName.empty())
{
HRESULT hr = MyNetValidateName(domainName, ::NetSetupNonExistentDomain);
if (hr == Win32ToHresult(ERROR_DUP_NAME))
{
result = true;
}
}
LOG(
String::format(
L"The domain name %1 %2 in use.",
domainName.c_str(),
result ? L"is" : L"is NOT"));
return result;
}
bool
DS::DisjoinDomain()
throw (DS::Error)
{
LOG_FUNCTION(DS::DisjoinDomain);
// make 1st attempt assuming that the current user has account
// deletion priv on the domain.
LOG(L"Calling NetUnjoinDomain (w/ account delete)");
HRESULT hr =
Win32ToHresult(
::NetUnjoinDomain(
0, // this server
0, // current account,
0, // current password
NETSETUP_ACCT_DELETE));
LOG_HRESULT(hr);
if (FAILED(hr))
{
// make another attempt, not removing the computer account
LOG(L"Calling NetUnjoinDomain again, w/o account delete");
hr = Win32ToHresult(::NetUnjoinDomain(0, 0, 0, 0));
LOG_HRESULT(hr);
if (SUCCEEDED(hr))
{
// the unjoin was successful, but the computer account was
// left behind.
return false;
}
}
if (FAILED(hr))
{
throw DS::Error(hr, IDS_DISJOIN_DOMAIN_FAILED);
}
return true;
}
void
DS::JoinDomain(
const String& domainDNSName,
const String& dcName,
const String& userName,
const EncodedString& password,
const String& userDomainName)
throw (DS::Error)
{
LOG_FUNCTION(DS::JoinDomain);
ASSERT(!domainDNSName.empty());
ASSERT(!userName.empty());
// password may be empty
ULONG flags =
NETSETUP_JOIN_DOMAIN
| NETSETUP_ACCT_CREATE
| NETSETUP_DOMAIN_JOIN_IF_JOINED
| NETSETUP_ACCT_DELETE;
String massagedUserName = MassageUserName(userDomainName, userName);
String domain = domainDNSName;
if (!dcName.empty())
{
domain += L"\\" + dcName;
}
WCHAR* cleartext = password.GetDecodedCopy();
HRESULT hr =
MyNetJoinDomain(
domain.c_str(),
massagedUserName.c_str(),
cleartext,
flags);
::ZeroMemory(cleartext, sizeof(WCHAR) * password.GetLength());
delete[] cleartext;
LOG_HRESULT(hr);
if (FAILED(hr))
{
State& state = State::GetInstance();
state.SetOperationResultsMessage(
String::format(IDS_UNABLE_TO_JOIN_DOMAIN, domainDNSName.c_str()));
throw DS::Error(hr, IDS_JOIN_DOMAIN_FAILED);
}
}
DWORD
MyDsRoleCancel(DSROLE_SERVEROP_HANDLE& handle)
{
LOG(L"Calling DsRoleCancel");
LOG(L"lpServer : (null)");
DWORD status = ::DsRoleCancel(0, handle);
LOG(String::format(L"Error 0x%1!X! (!0 => error)", status));
return status;
}
DWORD
MyDsRoleGetDcOperationProgress(
DSROLE_SERVEROP_HANDLE& handle,
DSROLE_SERVEROP_STATUS*& status)
{
// LOG(L"Calling DsRoleGetDcOperationProgress");
status = 0;
DWORD err = ::DsRoleGetDcOperationProgress(0, handle, &status);
// LOG(
// String::format(
// L"Error 0x%1!X! (!0 => error, 0x%2!X! = ERROR_IO_PENDING)",
// err,
// ERROR_IO_PENDING));
// if (status)
// {
// LOG(
// String::format(
// L"OperationStatus : 0x%1!X!",
// status->OperationStatus));
// LOG(status->CurrentOperationDisplayString);
// }
// DWORD err = ERROR_IO_PENDING;
// status = new DSROLE_SERVEROP_STATUS;
// status->CurrentOperationDisplayString = L"proceeding";
// status->OperationStatus = 0;
return err;
}
void
DoProgressLoop(
DSROLE_SERVEROP_HANDLE& handle,
ProgressDialog& progressDialog)
throw (DS::Error)
{
LOG_FUNCTION(DoProgressLoop);
State& state = State::GetInstance();
if (state.GetOperation() == State::DEMOTE)
{
// not cancelable -- turn off the cancel button.
progressDialog.UpdateButton(String());
}
else
{
// turn on the cancel button.
progressDialog.UpdateButton(IDS_PROGRESS_CANCEL);
}
DWORD netErr = 0;
bool criticalComplete = false;
bool buttonUpdatedToFinishLater = false;
String lastMessage;
ProgressDialog::WaitCode cancelButton;
do
{
// wait 1500ms or for the user to hit cancel
cancelButton = progressDialog.WaitForButton(1500);
// get the current status of the operation
DSROLE_SERVEROP_STATUS* status = 0;
netErr = MyDsRoleGetDcOperationProgress(handle, status);
if (netErr != ERROR_SUCCESS && netErr != ERROR_IO_PENDING)
{
// operation complete
break;
}
if (!status)
{
LOG(L"Operation status not returned!");
ASSERT(false);
continue;
}
// update the message display
String message = status->CurrentOperationDisplayString;
if (message != lastMessage)
{
progressDialog.UpdateText(message);
lastMessage = message;
}
// save the status flags for later use.
ULONG statusFlags = status->OperationStatus;
::DsRoleFreeMemory(status);
do
{
if (cancelButton != ProgressDialog::PRESSED)
{
break;
}
// if we make it here, user pressed the cancel button
LOG(L"DoProgressLoop: handling cancel");
ASSERT(state.GetOperation() != State::DEMOTE);
if (criticalComplete)
{
// inform the user that the install is done, and that they're
// cancelling the non-critical replication.
popup.Info(
progressDialog.GetHWND(),
String::load(IDS_CANCEL_NON_CRITICAL_REPLICATION));
// this should return ERROR_SUCCESS, and the promotion will
// be considered complete.
progressDialog.UpdateText(IDS_CANCELLING_REPLICATION);
netErr = MyDsRoleCancel(handle);
// fall out of the inner, then the outer, loop. Then we will
// get the operation results, which should indicate that the
// promotion is complete, and non-critical replication was
// canceled.
break;
}
// Still doing promote, verify that the user really wants to roll
// back to server state.
if (
popup.MessageBox(
progressDialog.GetHWND(),
String::load(IDS_CANCEL_PROMOTE),
MB_YESNO | MB_ICONWARNING) == IDYES)
{
// this should return ERROR_SUCCESS, and the promotion will
// be rolled back.
progressDialog.UpdateText(IDS_CANCELLING);
netErr = MyDsRoleCancel(handle);
// fall out of the inner, then the outer, loop. Then we will
// get the operation results, which should indicate that the
// promotion was cancelled as a failure code. We handle this
// failure code in the same manner as all others.
break;
}
// user decided to press on. reset the cancel button
progressDialog.UpdateButton(IDS_PROGRESS_CANCEL);
buttonUpdatedToFinishLater = false;
}
while (0);
criticalComplete =
criticalComplete
|| statusFlags & DSROLE_CRITICAL_OPERATIONS_COMPLETED;
if (criticalComplete)
{
if (cancelButton == ProgressDialog::PRESSED)
{
// we add this message without actually checking the operation
// results flags because for all we know, the replication will
// finish before we get around to checking. It is still correct
// to say the the replication has stopped, and will start after
// reboot. This is always the case.
state.AddFinishMessage(
String::load(IDS_NON_CRITICAL_REPLICATION_CANCELED));
}
else
{
if (!buttonUpdatedToFinishLater)
{
progressDialog.UpdateButton(IDS_FINISH_REPLICATION_LATER);
buttonUpdatedToFinishLater = true;
}
}
}
}
while (netErr == ERROR_IO_PENDING);
progressDialog.UpdateButton(String());
buttonUpdatedToFinishLater = false;
LOG(L"Progress loop complete.");
if (netErr == ERROR_SUCCESS)
{
// we successfully endured the wait. let's see how it turned out.
DSROLE_SERVEROP_RESULTS* results;
LOG(L"Calling DsRoleGetDcOperationResults");
netErr = ::DsRoleGetDcOperationResults(0, handle, &results);
LOG(String::format(L"Error 0x%1!X! (!0 => error)", netErr));
if (netErr == ERROR_SUCCESS)
{
// we got the results
ASSERT(results);
if (results)
{
LOG(L"Operation results:");
LOG(
String::format(
L"OperationStatus : 0x%1!X! !0 => error",
results->OperationStatus));
LOG(
String::format(
L"DisplayString : %1",
results->OperationStatusDisplayString));
LOG(
String::format(
L"ServerInstalledSite : %1",
results->ServerInstalledSite));
LOG(
String::format(
L"OperationResultsFlags: 0x%1!X!",
results->OperationResultsFlags));
netErr = results->OperationStatus;
// here, netErr will be some error code if the promote was
// cancelled and successfully rolled back. Since it may be
// possible that the cancel was too late (e.g. the user took
// too long to confirm the cancel), the promote may have
// finished. If that's the case, tell the user that the cancel
// failed.
if (
netErr == ERROR_SUCCESS
&& cancelButton == ProgressDialog::PRESSED)
{
// the promote finished, and the cancel button was pressed.
if (!criticalComplete) // 363590
{
// we didn't find out if the non-critical replication phase
// started. So the cancel button still said 'Cancel', and
// yet, the operation finished. so, this means that the
// promote simply completed before the cancel was received.
popup.Info(
progressDialog.GetHWND(),
IDS_CANCEL_TOO_LATE);
}
}
String message =
results->OperationStatusDisplayString
? results->OperationStatusDisplayString
: L"";
String site =
results->ServerInstalledSite
? results->ServerInstalledSite
: L"";
progressDialog.UpdateText(message);
if (!site.empty())
{
state.SetInstalledSite(site);
}
if (!message.empty())
{
state.SetOperationResultsMessage(message);
}
state.SetOperationResultsFlags(results->OperationResultsFlags);
if (
results->OperationResultsFlags
& DSROLE_NON_FATAL_ERROR_OCCURRED)
{
state.AddFinishMessage(
String::load(IDS_NON_FATAL_ERRORS_OCCURRED));
}
if (
(results->OperationResultsFlags
& DSROLE_NON_CRITICAL_REPL_NOT_FINISHED)
&& cancelButton != ProgressDialog::PRESSED )
{
// cancel not pressed and critial replication bombed
state.AddFinishMessage(
String::load(IDS_NON_CRITICAL_REPL_FAILED));
}
if (
results->OperationResultsFlags
& DSROLE_IFM_RESTORED_DATABASE_FILES_MOVED)
{
LOG(L"restored files were moved");
if (netErr != ERROR_SUCCESS)
{
// only need to mention this in the case of a fatal failure;
// e.g. don't add this finish text if non-fatal errors
// occurred.
// NTRAID#NTBUG9-330378-2001/02/28-sburns
state.AddFinishMessage(
String::load(IDS_MUST_RESTORE_IFM_FILES_AGAIN));
}
}
::DsRoleFreeMemory(results);
}
}
}
if (netErr != ERROR_SUCCESS)
{
// couldn't get progress updates, or couldn't get results,
// or result was a failure
throw DS::Error(Win32ToHresult(netErr), IDS_SET_ROLE_AS_DC_FAILED);
}
}
void
EmptyFolder(const String& path)
throw (DS::Error)
{
LOG_FUNCTION2(EmptyFolder, path);
ASSERT(!path.empty());
// check for files/subfolders once again (we checked the first time on
// validating the path), in case something has written to the folder
// since we validated it last.
if (!FS::IsFolderEmpty(path))
{
// nuke the files in the directory
LOG(String::format(L"Emptying %1", path.c_str()));
String wild = path;
if (wild[wild.length() - 1] != L'\\')
{
wild += L"\\";
}
wild += L"*.*";
FS::Iterator iter(
wild,
FS::Iterator::INCLUDE_FILES
| FS::Iterator::RETURN_FULL_PATHS);
HRESULT hr = S_OK;
String current;
while ((hr = iter.GetCurrent(current)) == S_OK)
{
LOG(String::format(L"Deleting %1", current.c_str()));
hr = Win::DeleteFile(current);
if (FAILED(hr))
{
int msgId = IDS_EMPTY_DIR_FAILED;
if (hr == Win32ToHresult(ERROR_ACCESS_DENIED))
{
msgId = IDS_EMPTY_DIR_FAILED_ACCESS_DENIED;
}
throw
DS::Error(
S_OK, // so as not to trigger credentials dialog
String::format(
msgId,
GetErrorMessage(hr).c_str(),
path.c_str()),
String::load(IDS_ERROR_PREPARING_OPERATION));
}
hr = iter.Increment();
BREAK_ON_FAILED_HRESULT(hr);
}
}
}
HRESULT
SetupPaths()
{
LOG_FUNCTION(SetupPaths);
State& state = State::GetInstance();
String dbPath = state.GetDatabasePath();
String logPath = state.GetLogPath();
String sysvolPath = state.GetSYSVOLPath();
ASSERT(!dbPath.empty());
ASSERT(!logPath.empty());
ASSERT(!sysvolPath.empty());
HRESULT hr = S_OK;
do
{
if (FS::PathExists(dbPath))
{
EmptyFolder(dbPath);
}
else
{
hr = FS::CreateFolder(dbPath);
BREAK_ON_FAILED_HRESULT(hr);
}
if (FS::PathExists(logPath))
{
EmptyFolder(logPath);
}
else
{
hr = FS::CreateFolder(logPath);
BREAK_ON_FAILED_HRESULT(hr);
}
if (FS::PathExists(sysvolPath))
{
EmptyFolder(sysvolPath);
}
else
{
hr = FS::CreateFolder(sysvolPath);
BREAK_ON_FAILED_HRESULT(hr);
}
}
while (0);
return hr;
}
// Sets the promotion flags based on options set in the unattended execution
// answerfile.
//
// state - IN reference to the global State object
//
// flags - IN/OUT promote API flags, may be modified on exit
void
SetAnswerFilePromoteFlags(
State& state,
ULONG& flags)
{
LOG_FUNCTION(SetAnswerFilePromoteFlags);
if (state.UsingAnswerFile())
{
// set flags based on unattended execution options
// if the safe mode admin password is not specified, then we set a
// flag to cause the promote APIs to copy the current local admin
// password.
EncodedString safeModePassword = state.GetSafeModeAdminPassword();
if (safeModePassword.IsEmpty() && state.RunHiddenUnattended())
{
// user did not supply a safemode password, and he did not have
// an opportunity to do so (if the wizard went interactive)
if (!state.IsSafeModeAdminPwdOptionPresent())
{
// the safe mode pwd key is not present in the answerfile
flags |= DSROLE_DC_DEFAULT_REPAIR_PWD;
}
}
String option =
state.GetAnswerFileOption(State::OPTION_CRITICAL_REPLICATION_ONLY);
if (option.icompare(State::VALUE_YES) == 0)
{
flags |= DSROLE_DC_CRITICAL_REPLICATION_ONLY;
}
}
LOG(String::format(L"0x%1!X!", flags));
}
void
DS::CreateReplica(ProgressDialog& progressDialog, bool invokeForUpgrade)
throw (DS::Error)
{
LOG_FUNCTION(DS::CreateReplica);
State& state = State::GetInstance();
String domain = state.GetReplicaDomainDNSName();
String dbPath = state.GetDatabasePath();
String logPath = state.GetLogPath();
String sysvolPath = state.GetSYSVOLPath();
String site = state.GetSiteName();
String username = state.GetUsername();
String replicationDc = state.GetReplicationPartnerDC();
String sourcePath = state.GetReplicationSourcePath();
bool useSourcePath = state.ReplicateFromMedia();
EncodedString syskey = state.GetSyskey();
EncodedString safeModePassword = state.GetSafeModeAdminPassword();
EncodedString password = state.GetPassword();
bool useCurrentUserCreds = username.empty();
ULONG flags =
DSROLE_DC_FORCE_TIME_SYNC
| DSROLE_DC_CREATE_TRUST_AS_REQUIRED;
if (invokeForUpgrade)
{
flags |= DSROLE_DC_DOWNLEVEL_UPGRADE;
}
if (state.GetDomainControllerReinstallFlag())
{
flags |= DSROLE_DC_ALLOW_DC_REINSTALL;
}
SetAnswerFilePromoteFlags(state, flags);
if (useSourcePath)
{
if (state.GetRestoreGc())
{
flags |= DSROLE_DC_REQUEST_GC;
}
}
ASSERT(!domain.empty());
if (!useCurrentUserCreds)
{
String user_domain = state.GetUserDomainName();
username = MassageUserName(user_domain, username);
}
HRESULT hr = S_OK;
do
{
hr = SetupPaths();
BREAK_ON_FAILED_HRESULT(hr);
LOG(L"Calling DsRoleDcAsReplica");
LOG( L"lpServer : (null)");
LOG(String::format(L"lpDnsDomainName : %1", domain.c_str()));
LOG(String::format(L"lpReplicaServer : %1", replicationDc.empty() ? L"(null)" : replicationDc.c_str()));
LOG(String::format(L"lpSiteName : %1", site.empty() ? L"(null)" : site.c_str()));
LOG(String::format(L"lpDsDatabasePath : %1", dbPath.c_str()));
LOG(String::format(L"lpDsLogPath : %1", logPath.c_str()));
LOG(String::format(L"lpRestorePath : %1", useSourcePath ? sourcePath.c_str() : L"(null)"));
LOG(String::format(L"lpSystemVolumeRootPath : %1", sysvolPath.c_str()));
LOG(String::format(L"lpAccount : %1", useCurrentUserCreds ? L"(null)" : username.c_str()));
LOG(String::format(L"Options : 0x%1!X!", flags));
WCHAR* safeModePasswordCopy = 0;
if (!safeModePassword.IsEmpty())
{
safeModePasswordCopy = safeModePassword.GetDecodedCopy();
}
WCHAR* passwordCopy = 0;
if (!useCurrentUserCreds)
{
passwordCopy = password.GetDecodedCopy();
}
// The api wants to scribble over the syskey, so we make a copy for
// it to do so.
WCHAR* syskeyCopy = 0;
if (useSourcePath && !syskey.IsEmpty())
{
syskeyCopy = syskey.GetDecodedCopy();
}
DSROLE_SERVEROP_HANDLE handle = 0;
hr =
Win32ToHresult(
::DsRoleDcAsReplica(
0, // this server
domain.c_str(),
// possibly empty, e.g. if we didn't join a domain...
replicationDc.empty() ? 0 : replicationDc.c_str(),
site.empty() ? 0 : site.c_str(),
dbPath.c_str(),
logPath.c_str(),
useSourcePath ? sourcePath.c_str() : 0,
sysvolPath.c_str(),
syskeyCopy,
(useCurrentUserCreds ? 0 : username.c_str()),
passwordCopy,
safeModePasswordCopy,
flags,
&handle));
if (safeModePasswordCopy)
{
::ZeroMemory(
safeModePasswordCopy,
sizeof(WCHAR) * safeModePassword.GetLength());
delete[] safeModePasswordCopy;
}
if (passwordCopy)
{
::ZeroMemory(passwordCopy, sizeof(WCHAR) * password.GetLength());
delete[] passwordCopy;
}
// the copy of the syskey has already been scribbled on, so just
// delete it.
delete[] syskeyCopy;
BREAK_ON_FAILED_HRESULT(hr);
DoProgressLoop(handle, progressDialog);
}
while (0);
if (FAILED(hr))
{
throw DS::Error(hr, IDS_SET_ROLE_AS_DC_FAILED);
}
}
void
DS::CreateNewDomain(ProgressDialog& progressDialog)
throw (DS::Error)
{
LOG_FUNCTION(DS::CreateNewDomain);
State& state = State::GetInstance();
String domain = state.GetNewDomainDNSName();
String flatName = state.GetNewDomainNetbiosName();
String site = state.GetSiteName();
String dbPath = state.GetDatabasePath();
String logPath = state.GetLogPath();
String sysvolPath = state.GetSYSVOLPath();
String parent = state.GetParentDomainDnsName();
String username = state.GetUsername();
EncodedString password = state.GetPassword();
EncodedString safeModePassword = state.GetSafeModeAdminPassword();
EncodedString adminPassword = state.GetAdminPassword();
State::Operation operation = state.GetOperation();
bool useParent =
( operation == State::TREE
|| operation == State::CHILD);
bool useCurrentUserCreds = username.empty();
ULONG flags =
DSROLE_DC_CREATE_TRUST_AS_REQUIRED
| DSROLE_DC_FORCE_TIME_SYNC;
if (state.GetDomainReinstallFlag())
{
flags |= DSROLE_DC_ALLOW_DOMAIN_REINSTALL;
}
if (state.GetDomainControllerReinstallFlag())
{
flags |= DSROLE_DC_ALLOW_DC_REINSTALL;
}
if (operation == State::TREE)
{
flags |= DSROLE_DC_TRUST_AS_ROOT;
ASSERT(!parent.empty());
}
SetAnswerFilePromoteFlags(state, flags);
if (state.ShouldAllowAnonymousAccess())
{
flags |= DSROLE_DC_ALLOW_ANONYMOUS_ACCESS;
}
if (operation == State::FOREST)
{
flags |= DSROLE_DC_NO_NET;
ASSERT(!site.empty());
}
#ifdef DBG
else if (operation == State::CHILD)
{
ASSERT(!parent.empty());
}
ASSERT(!domain.empty());
ASSERT(!flatName.empty());
// parent may be empty
#endif
if (!useCurrentUserCreds)
{
String userDomain = state.GetUserDomainName();
username = MassageUserName(userDomain, username);
}
HRESULT hr = S_OK;
do
{
hr = SetupPaths();
BREAK_ON_FAILED_HRESULT(hr);
LOG(L"Calling DsRoleDcAsDc");
LOG( L"lpServer : (null)");
LOG(String::format(L"lpDnsDomainName : %1", domain.c_str()));
LOG(String::format(L"lpFlatDomainName : %1", flatName.c_str()));
LOG(String::format(L"lpSiteName : %1", site.empty() ? L"(null)" : site.c_str()));
LOG(String::format(L"lpDsDatabasePath : %1", dbPath.c_str()));
LOG(String::format(L"lpDsLogPath : %1", logPath.c_str()));
LOG(String::format(L"lpSystemVolumeRootPath : %1", sysvolPath.c_str()));
LOG(String::format(L"lpParentDnsDomainName : %1", useParent ? parent.c_str() : L"(null)"));
LOG( L"lpParentServer : (null)");
LOG(String::format(L"lpAccount : %1", useCurrentUserCreds ? L"(null)" : username.c_str()));
LOG(String::format(L"Options : 0x%1!X!", flags));
WCHAR* safeModePasswordCopy = 0;
if (!safeModePassword.IsEmpty())
{
safeModePasswordCopy = safeModePassword.GetDecodedCopy();
}
WCHAR* adminPasswordCopy = 0;
if (!adminPassword.IsEmpty())
{
adminPasswordCopy = adminPassword.GetDecodedCopy();
}
WCHAR* passwordCopy = 0;
if (!useCurrentUserCreds)
{
passwordCopy = password.GetDecodedCopy();
}
DSROLE_SERVEROP_HANDLE handle = 0;
hr =
Win32ToHresult(
DsRoleDcAsDc(
0, // this server
domain.c_str(),
flatName.c_str(),
adminPasswordCopy,
site.empty() ? 0 : site.c_str(),
dbPath.c_str(),
logPath.c_str(),
sysvolPath.c_str(),
(useParent ? parent.c_str() : 0),
0, // let API pick a server
(useCurrentUserCreds ? 0 : username.c_str()),
passwordCopy,
safeModePasswordCopy,
flags,
&handle));
BREAK_ON_FAILED_HRESULT(hr);
if (safeModePasswordCopy)
{
::ZeroMemory(
safeModePasswordCopy,
sizeof(WCHAR) * safeModePassword.GetLength());
delete[] safeModePasswordCopy;
}
if (adminPasswordCopy)
{
::ZeroMemory(
adminPasswordCopy,
sizeof(WCHAR) * adminPassword.GetLength());
delete[] adminPasswordCopy;
}
if (passwordCopy)
{
::ZeroMemory(passwordCopy, sizeof(WCHAR) * password.GetLength());
delete[] passwordCopy;
}
DoProgressLoop(handle, progressDialog);
}
while (0);
if (FAILED(hr))
{
throw DS::Error(hr, IDS_SET_ROLE_AS_DC_FAILED);
}
}
void
DS::UpgradeBDC(ProgressDialog& progressDialog)
throw (DS::Error)
{
LOG_FUNCTION(DS::UpgradeBDC);
// seems non-intuitive to abort the upgrade to do the upgrade, but here
// the abort removes dcpromo autostart, and turns the machine into a
// standalone server. Then we proceed to make it a replica.
DS::AbortBDCUpgrade(true);
DS::CreateReplica(progressDialog, true);
}
void
DS::UpgradePDC(ProgressDialog& progressDialog)
throw (DS::Error)
{
LOG_FUNCTION(DS::UpgradePDC);
State& state = State::GetInstance();
ASSERT(state.GetRunContext() == State::PDC_UPGRADE);
String domain = state.GetNewDomainDNSName();
String site = state.GetSiteName();
String dbPath = state.GetDatabasePath();
String logPath = state.GetLogPath();
String sysvolPath = state.GetSYSVOLPath();
String parent = state.GetParentDomainDnsName();
String username = state.GetUsername();
EncodedString password = state.GetPassword();
EncodedString safeModePassword = state.GetSafeModeAdminPassword();
State::Operation operation = state.GetOperation();
bool useParent =
( operation == State::TREE
|| operation == State::CHILD);
bool useCurrentUserCreds = username.empty();
ULONG flags = DSROLE_DC_CREATE_TRUST_AS_REQUIRED;
if (state.GetDomainReinstallFlag())
{
flags |= DSROLE_DC_ALLOW_DOMAIN_REINSTALL;
}
if (state.GetDomainControllerReinstallFlag())
{
flags |= DSROLE_DC_ALLOW_DC_REINSTALL;
}
if (state.GetSetForestVersionFlag())
{
flags |= DSROLE_DC_SET_FOREST_CURRENT;
}
if (operation == State::TREE)
{
flags |= DSROLE_DC_TRUST_AS_ROOT | DSROLE_DC_FORCE_TIME_SYNC;
ASSERT(!parent.empty());
}
else if (operation == State::CHILD)
{
flags |= DSROLE_DC_FORCE_TIME_SYNC;
ASSERT(!parent.empty());
}
SetAnswerFilePromoteFlags(state, flags);
if (state.ShouldAllowAnonymousAccess())
{
flags |= DSROLE_DC_ALLOW_ANONYMOUS_ACCESS;
}
#ifdef DBG
ASSERT(!domain.empty());
// parent may be empty
if (operation == State::FOREST)
{
ASSERT(!site.empty());
}
#endif
if (!useCurrentUserCreds)
{
String userDomain = state.GetUserDomainName();
username = MassageUserName(userDomain, username);
}
HRESULT hr = S_OK;
do
{
hr = SetupPaths();
BREAK_ON_FAILED_HRESULT(hr);
LOG(L"Calling DsRoleUpgradeDownlevelServer");
LOG(String::format(L"lpDnsDomainName : %1", domain.c_str()));
LOG(String::format(L"lpSiteName : %1", site.empty() ? L"(null)" : site.c_str()));
LOG(String::format(L"lpDsDatabasePath : %1", dbPath.c_str()));
LOG(String::format(L"lpDsLogPath : %1", logPath.c_str()));
LOG(String::format(L"lpSystemVolumeRootPath : %1", sysvolPath.c_str()));
LOG(String::format(L"lpParentDnsDomainName : %1", useParent ? parent.c_str() : L"(null)"));
LOG( L"lpParentServer : (null)");
LOG(String::format(L"lpAccount : %1", useCurrentUserCreds ? L"(null)" : username.c_str()));
LOG(String::format(L"Options : 0x%1!X!", flags));
WCHAR* safeModePasswordCopy = 0;
if (!safeModePassword.IsEmpty())
{
safeModePasswordCopy = safeModePassword.GetDecodedCopy();
}
WCHAR* passwordCopy = 0;
if (!useCurrentUserCreds)
{
passwordCopy = password.GetDecodedCopy();
}
DSROLE_SERVEROP_HANDLE handle = 0;
hr =
Win32ToHresult(
::DsRoleUpgradeDownlevelServer(
domain.c_str(),
site.empty() ? 0 : site.c_str(),
dbPath.c_str(),
logPath.c_str(),
sysvolPath.c_str(),
(useParent ? parent.c_str() : 0),
0, // let API pick a server
(useCurrentUserCreds ? 0 : username.c_str()),
passwordCopy,
safeModePasswordCopy,
flags,
&handle));
BREAK_ON_FAILED_HRESULT(hr);
if (safeModePasswordCopy)
{
::ZeroMemory(
safeModePasswordCopy,
sizeof(WCHAR) * safeModePassword.GetLength());
delete[] safeModePasswordCopy;
}
if (passwordCopy)
{
::ZeroMemory(passwordCopy, sizeof(WCHAR) * password.GetLength());
delete[] passwordCopy;
}
DoProgressLoop(handle, progressDialog);
}
while (0);
if (FAILED(hr))
{
throw DS::Error(hr, IDS_UPGRADE_DC_FAILED);
}
}
void
DS::DemoteDC(ProgressDialog& progressDialog)
throw (DS::Error)
{
LOG_FUNCTION(DS::DemoteDC);
State& state = State::GetInstance();
String username = state.GetUsername();
bool useCurrentUserCreds = username.empty();
bool isLastDc = state.IsLastDCInDomain();
EncodedString adminPassword= state.GetAdminPassword();
EncodedString password = state.GetPassword();
if (!useCurrentUserCreds)
{
String userDomain = state.GetUserDomainName();
username = MassageUserName(userDomain, username);
}
ULONG options = DSROLE_DC_CREATE_TRUST_AS_REQUIRED;
if (isLastDc)
{
options |= DSROLE_DC_DELETE_PARENT_TRUST;
}
LOG(L"Calling DsRoleDemoteDc");
LOG( L"lpServer : (null)");
LOG( L"lpDnsDomainName : (null)");
LOG(String::format(L"ServerRole : %1", isLastDc ? L"DsRoleServerStandalone" : L"DsRoleServerMember"));
LOG(String::format(L"lpAccount : %1", useCurrentUserCreds ? L"(null)" : username.c_str()));
LOG(String::format(L"Options : 0x%1!X!", options));
LOG(String::format(L"fLastDcInDomain : %1", isLastDc ? L"true" : L"false"));
WCHAR* adminPasswordCopy = 0;
if (!adminPassword.IsEmpty())
{
adminPasswordCopy = adminPassword.GetDecodedCopy();
}
WCHAR* passwordCopy = 0;
if (!useCurrentUserCreds)
{
passwordCopy = password.GetDecodedCopy();
}
DSROLE_SERVEROP_HANDLE handle = 0;
HRESULT hr =
Win32ToHresult(
::DsRoleDemoteDc(
0, // this server
0, // "default" domain hosted by this server
isLastDc ? DsRoleServerStandalone : DsRoleServerMember,
(useCurrentUserCreds ? 0 : username.c_str()),
passwordCopy,
options,
isLastDc ? TRUE : FALSE,
adminPasswordCopy,
&handle));
LOG_HRESULT(hr);
if (adminPasswordCopy)
{
::ZeroMemory(
adminPasswordCopy,
sizeof(WCHAR) * adminPassword.GetLength());
delete[] adminPasswordCopy;
}
if (passwordCopy)
{
::ZeroMemory(passwordCopy, sizeof(WCHAR) * password.GetLength());
delete[] passwordCopy;
}
if (SUCCEEDED(hr))
{
DoProgressLoop(handle, progressDialog);
}
else
{
throw DS::Error(hr, IDS_DEMOTE_DC_FAILED);
}
}
void
DS::AbortBDCUpgrade(bool abortForReplica)
throw (DS::Error)
{
LOG_FUNCTION(DS::AbortBDCUpgrade);
State& state = State::GetInstance();
ASSERT(state.GetRunContext() == State::BDC_UPGRADE);
String username = state.GetUsername();
bool useCurrentUserCreds = username.empty();
EncodedString adminPassword = state.GetAdminPassword();
EncodedString password = state.GetPassword();
if (!useCurrentUserCreds)
{
String userDomain = state.GetUserDomainName();
username = MassageUserName(userDomain, username);
}
ULONG options =
abortForReplica ? DSROLEP_ABORT_FOR_REPLICA_INSTALL : 0;
LOG(L"Calling DsRoleAbortDownlevelServerUpgrade");
LOG(String::format(L"lpAccount : %1", useCurrentUserCreds ? L"(null)" : username.c_str()));
LOG(String::format(L"Options : 0x%1!X!", options));
WCHAR* adminPasswordCopy = 0;
if (!adminPassword.IsEmpty())
{
adminPasswordCopy = adminPassword.GetDecodedCopy();
}
WCHAR* passwordCopy = 0;
if (!useCurrentUserCreds)
{
passwordCopy = password.GetDecodedCopy();
}
HRESULT hr =
Win32ToHresult(
::DsRoleAbortDownlevelServerUpgrade(
adminPasswordCopy,
(useCurrentUserCreds ? 0 : username.c_str()),
passwordCopy,
options));
LOG_HRESULT(hr);
if (adminPasswordCopy)
{
::ZeroMemory(
adminPasswordCopy,
sizeof(WCHAR) * adminPassword.GetLength());
delete[] adminPasswordCopy;
}
if (passwordCopy)
{
::ZeroMemory(passwordCopy, sizeof(WCHAR) * password.GetLength());
delete[] passwordCopy;
}
if (FAILED(hr))
{
throw DS::Error(hr, IDS_ABORT_UPGRADE_FAILED);
}
}
DS::PriorServerRole
DS::GetPriorServerRole(bool& isUpgrade)
{
LOG_FUNCTION(DS::GetPriorServerRole);
isUpgrade = false;
DSROLE_UPGRADE_STATUS_INFO* info = 0;
HRESULT hr = MyDsRoleGetPrimaryDomainInformation(0, info);
if (SUCCEEDED(hr) && info)
{
isUpgrade =
( (info->OperationState & DSROLE_UPGRADE_IN_PROGRESS)
? true
: false );
DSROLE_SERVER_STATE state = info->PreviousServerState;
::DsRoleFreeMemory(info);
switch (state)
{
case DsRoleServerPrimary:
{
return PDC;
}
case DsRoleServerBackup:
{
return BDC;
}
case DsRoleServerUnknown:
default:
{
return UNKNOWN;
}
}
}
return UNKNOWN;
}