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