Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1886 lines
49 KiB

  1. // Copyright (c) 1997-1999 Microsoft Corporation
  2. //
  3. // Tab state
  4. //
  5. // 03-31-98 sburns
  6. // 10-05-00 jonn changed to CredUIGetPassword
  7. #include "headers.hxx"
  8. #include "state.hpp"
  9. #include "resource.h"
  10. #include "cred.hpp"
  11. TCHAR const c_szWizardFilename[] = L"netplwiz.dll";
  12. class Settings
  13. {
  14. public:
  15. // default ctor, copy ctor, op=, dtor used
  16. void
  17. Refresh();
  18. String ComputerDomainDnsName;
  19. String DomainName;
  20. String FullComputerName;
  21. String PolicyDomainDnsName;
  22. String ShortComputerName;
  23. String NetbiosComputerName;
  24. bool SyncDNSNames;
  25. bool JoinedToWorkgroup;
  26. bool NeedsReboot;
  27. };
  28. static State* instance = 0;
  29. static bool machineIsDc = false;
  30. static bool networkingInstalled = false;
  31. static bool policyInEffect = false;
  32. static bool mustReboot = false;
  33. // no static initialization worries here, as these are rebuilt when the State
  34. // instance is constructed/initialized/refreshed.
  35. static Settings original;
  36. static Settings current;
  37. // not static String instances to avoid order of static initialization any
  38. // problems
  39. static const wchar_t* TCPIP_PARAMS_KEY =
  40. L"System\\CurrentControlSet\\Services\\Tcpip\\Parameters";
  41. static const wchar_t* SYNC_VALUE =
  42. L"SyncDomainWithMembership";
  43. static const wchar_t* NEW_HOSTNAME_VALUE = L"NV Hostname";
  44. static const wchar_t* NEW_SUFFIX_VALUE = L"NV Domain";
  45. bool
  46. readSyncFlag()
  47. {
  48. bool retval = true;
  49. do
  50. {
  51. RegistryKey key;
  52. HRESULT hr = key.Open(HKEY_LOCAL_MACHINE, TCPIP_PARAMS_KEY);
  53. BREAK_ON_FAILED_HRESULT(hr);
  54. // default is to sync.
  55. DWORD data = 1;
  56. hr = key.GetValue(SYNC_VALUE, data);
  57. BREAK_ON_FAILED_HRESULT(hr);
  58. retval = data ? true : false;
  59. }
  60. while (0);
  61. return retval;
  62. }
  63. // JonN 1/03/01 106601
  64. // When the dns suffix checkbox is unchecked,
  65. // domain join fails with a confusing message
  66. //
  67. // LevonE: When Join fails with ERROR_DS_COULDNT_UPDATE_SPNS the UI must check
  68. // if (HKLM/System/CCS/Services/Tcpip/Parameters/SyncDomainWithMembership
  69. // == 0x0 &&
  70. // HKLM/System/CCS/Services/Tcpip/Parameters/NV Domain
  71. // != AD_Domain_To_Be_Joined)
  72. bool WarnDnsSuffix( const String& refNewDomainName )
  73. {
  74. if (readSyncFlag())
  75. return false;
  76. String strNVDomain;
  77. RegistryKey key;
  78. HRESULT hr2 = key.Open(HKEY_LOCAL_MACHINE, TCPIP_PARAMS_KEY);
  79. if (!SUCCEEDED(hr2))
  80. return false;
  81. hr2 = key.GetValue(NEW_SUFFIX_VALUE, strNVDomain);
  82. if (!SUCCEEDED(hr2))
  83. return false;
  84. return !!strNVDomain.icompare( refNewDomainName );
  85. }
  86. HRESULT
  87. WriteSyncFlag(HWND dialog, bool flag)
  88. {
  89. LOG_FUNCTION(WriteSyncFlag);
  90. ASSERT(Win::IsWindow(dialog));
  91. HRESULT hr = S_OK;
  92. do
  93. {
  94. RegistryKey key;
  95. hr = key.Create(HKEY_LOCAL_MACHINE, TCPIP_PARAMS_KEY);
  96. BREAK_ON_FAILED_HRESULT(hr);
  97. hr = key.SetValue(SYNC_VALUE, flag ? 1 : 0);
  98. BREAK_ON_FAILED_HRESULT(hr);
  99. }
  100. while (0);
  101. if (FAILED(hr))
  102. {
  103. popup.Error(
  104. dialog,
  105. hr,
  106. IDS_CHANGE_SYNC_FLAG_FAILED);
  107. }
  108. return hr;
  109. }
  110. // returns true if the machine is a domain controller running under ds repair
  111. // mode, false if not
  112. bool
  113. IsDcInDsRepairMode()
  114. {
  115. LOG_FUNCTION(IsDcInDsRepairMode);
  116. // We infer a DC in repair mode if we're told that the machine is a server
  117. // and the safe boot option is ds repair, and the real product type is
  118. // LanManNT.
  119. //
  120. // By "real" product type, I mean that which is written in the registry,
  121. // not that which is reported by RtlGetNtProductType. The API gets the
  122. // result from shared memory which is adjusted at boot to reflect the
  123. // ds repair mode (from LanManNt to Server). The registry entry is not
  124. // changed by repair mode.
  125. //
  126. // We have to check both because it is possible to boot a normal server
  127. // in ds repair mode.
  128. DWORD safeBoot = 0;
  129. NT_PRODUCT_TYPE product = NtProductWinNt;
  130. HRESULT hr = Computer::GetSafebootOption(HKEY_LOCAL_MACHINE, safeBoot);
  131. // don't assert the result: the key may not be present
  132. hr = Computer::GetProductTypeFromRegistry(HKEY_LOCAL_MACHINE, product);
  133. ASSERT(SUCCEEDED(hr));
  134. if (safeBoot == SAFEBOOT_DSREPAIR and product == NtProductLanManNt)
  135. {
  136. return true;
  137. }
  138. return false;
  139. }
  140. void
  141. Settings::Refresh()
  142. {
  143. LOG_FUNCTION(Settings::Refresh);
  144. String unknown = String::load(IDS_UNKNOWN);
  145. ComputerDomainDnsName = unknown;
  146. DomainName = unknown;
  147. FullComputerName = unknown;
  148. ShortComputerName = unknown;
  149. PolicyDomainDnsName = unknown;
  150. SyncDNSNames = readSyncFlag();
  151. JoinedToWorkgroup = true;
  152. // CODEWORK: we should reconcile this with the Computer object added
  153. // to idpage.cpp
  154. DSROLE_PRIMARY_DOMAIN_INFO_BASIC* info = 0;
  155. HRESULT hr = MyDsRoleGetPrimaryDomainInformation(0, info);
  156. if (SUCCEEDED(hr))
  157. {
  158. if (info->DomainNameDns)
  159. {
  160. DomainName = info->DomainNameDns;
  161. }
  162. else if (info->DomainNameFlat)
  163. {
  164. DomainName = info->DomainNameFlat;
  165. }
  166. // this is the workgroup name iff JoinedToWorkgroup == true
  167. switch (info->MachineRole)
  168. {
  169. case DsRole_RoleBackupDomainController:
  170. case DsRole_RolePrimaryDomainController:
  171. {
  172. machineIsDc = true;
  173. JoinedToWorkgroup = false;
  174. break;
  175. }
  176. case DsRole_RoleStandaloneWorkstation:
  177. {
  178. machineIsDc = false;
  179. JoinedToWorkgroup = true;
  180. if (DomainName.empty())
  181. {
  182. LOG(L"empty domain name, using default WORKGROUP");
  183. DomainName = String::load(IDS_DEFAULT_WORKGROUP);
  184. }
  185. break;
  186. }
  187. case DsRole_RoleStandaloneServer:
  188. {
  189. machineIsDc = false;
  190. JoinedToWorkgroup = true;
  191. // I wonder if we're really a DC booted in ds repair mode?
  192. if (IsDcInDsRepairMode())
  193. {
  194. LOG(L"machine is in ds repair mode");
  195. machineIsDc = true;
  196. JoinedToWorkgroup = false;
  197. // we can't determine the domain name (LSA won't tell
  198. // us when running ds repair mode), so we fall back to
  199. // unknown. This is better than "WORKGROUP" -- which is
  200. // what info contains.
  201. DomainName = unknown;
  202. }
  203. else
  204. {
  205. if (DomainName.empty())
  206. {
  207. LOG(L"empty domain name, using default WORKGROUP");
  208. DomainName = String::load(IDS_DEFAULT_WORKGROUP);
  209. }
  210. }
  211. break;
  212. }
  213. case DsRole_RoleMemberWorkstation:
  214. case DsRole_RoleMemberServer:
  215. {
  216. machineIsDc = false;
  217. JoinedToWorkgroup = false;
  218. break;
  219. }
  220. default:
  221. {
  222. ASSERT(false);
  223. break;
  224. }
  225. }
  226. ::DsRoleFreeMemory(info);
  227. }
  228. else
  229. {
  230. popup.Error(
  231. Win::GetDesktopWindow(),
  232. hr,
  233. String::load(IDS_ERROR_READING_MEMBERSHIP));
  234. // fall back to other APIs to fill in the holes as best we can.
  235. JoinedToWorkgroup = false;
  236. machineIsDc = false;
  237. // workstation, server, or DC? (imprescise, but better than a stick
  238. // in the eye)
  239. NT_PRODUCT_TYPE ntp = NtProductWinNt;
  240. BOOLEAN result = ::RtlGetNtProductType(&ntp);
  241. if (result)
  242. {
  243. switch (ntp)
  244. {
  245. case NtProductWinNt:
  246. {
  247. break;
  248. }
  249. case NtProductServer:
  250. {
  251. break;
  252. }
  253. case NtProductLanManNt:
  254. {
  255. machineIsDc = true;
  256. break;
  257. }
  258. default:
  259. {
  260. ASSERT(false);
  261. }
  262. }
  263. }
  264. }
  265. networkingInstalled = IsNetworkingInstalled();
  266. bool isTcpInstalled = networkingInstalled && IsTcpIpInstalled();
  267. String activeFullName;
  268. NetbiosComputerName = Computer::GetFuturePhysicalNetbiosName();
  269. if (isTcpInstalled)
  270. {
  271. // When TCP/IP is installed on the computer, then we are interested
  272. // in the computer DNS domain name suffix, and the short name is the
  273. // computer's DNS hostname.
  274. String activeShortName;
  275. String futureShortName;
  276. String activeDomainName;
  277. String futureDomainName;
  278. RegistryKey key;
  279. hr = key.Open(HKEY_LOCAL_MACHINE, TCPIP_PARAMS_KEY);
  280. if (SUCCEEDED(hr))
  281. {
  282. // Read these values without checking for failure, as empty string
  283. // is ok.
  284. activeShortName = key.GetString(L"Hostname");
  285. activeDomainName = key.GetString(L"Domain");
  286. futureShortName = key.GetString(NEW_HOSTNAME_VALUE);
  287. ShortComputerName =
  288. futureShortName.empty() ? activeShortName : futureShortName;
  289. // here, check that the value was successfully read, because
  290. // it may not be present.
  291. hr = key.GetValue(NEW_SUFFIX_VALUE, futureDomainName);
  292. if (SUCCEEDED(hr))
  293. {
  294. ComputerDomainDnsName = futureDomainName;
  295. }
  296. else
  297. {
  298. ComputerDomainDnsName = activeDomainName;
  299. }
  300. }
  301. // Determine if DNS domain name policy is in effect. This may change
  302. // at any moment, asynchronously, so we save the result as a setting.
  303. policyInEffect =
  304. Computer::IsDnsSuffixPolicyInEffect(PolicyDomainDnsName);
  305. // The full computer name is the short name + . + dns domain name
  306. // if policy is in effect, the policy dns domain name takes precedence
  307. // over the computer's dns domain name.
  308. FullComputerName =
  309. Computer::ComposeFullDnsComputerName(
  310. ShortComputerName,
  311. policyInEffect ? PolicyDomainDnsName : ComputerDomainDnsName);
  312. activeFullName =
  313. Computer::ComposeFullDnsComputerName(
  314. activeShortName,
  315. policyInEffect ? PolicyDomainDnsName : activeDomainName);
  316. }
  317. else
  318. {
  319. // 371944
  320. activeFullName = Computer::GetActivePhysicalNetbiosName();
  321. // when there is no TCP/IP, the short name is the NetBIOS name
  322. ShortComputerName = NetbiosComputerName;
  323. FullComputerName = ShortComputerName;
  324. }
  325. // This test does not take into account domain membership changes, as we
  326. // have no prior membership info to compare the current membership to.
  327. NeedsReboot = activeFullName != FullComputerName;
  328. }
  329. void
  330. State::Delete()
  331. {
  332. LOG_FUNCTION(State::Delete);
  333. delete instance;
  334. instance = 0;
  335. }
  336. State&
  337. State::GetInstance()
  338. {
  339. ASSERT(instance);
  340. return *instance;
  341. }
  342. void
  343. State::Init()
  344. {
  345. LOG_FUNCTION(State::Init);
  346. ASSERT(!instance);
  347. if (!instance)
  348. {
  349. instance = new State();
  350. }
  351. }
  352. void
  353. State::Refresh()
  354. {
  355. LOG_FUNCTION(State::Refresh);
  356. State::Delete();
  357. State::Init();
  358. }
  359. State::State()
  360. {
  361. LOG_CTOR(State);
  362. original.Refresh();
  363. current = original;
  364. }
  365. State::~State()
  366. {
  367. LOG_DTOR(State);
  368. }
  369. bool
  370. State::NeedsReboot() const
  371. {
  372. return original.NeedsReboot;
  373. }
  374. bool
  375. State::IsMachineDc() const
  376. {
  377. return machineIsDc;
  378. }
  379. bool
  380. State::IsNetworkingInstalled() const
  381. {
  382. return networkingInstalled;
  383. }
  384. String
  385. State::GetFullComputerName() const
  386. {
  387. return current.FullComputerName;
  388. }
  389. String
  390. State::GetDomainName() const
  391. {
  392. return current.DomainName;
  393. }
  394. void
  395. State::SetDomainName(const String& name)
  396. {
  397. // LOG_FUNCTION2(State::SetDomainName, name);
  398. current.DomainName = name;
  399. }
  400. bool
  401. State::IsMemberOfWorkgroup() const
  402. {
  403. return current.JoinedToWorkgroup;
  404. }
  405. void
  406. State::SetIsMemberOfWorkgroup(bool yesNo)
  407. {
  408. current.JoinedToWorkgroup = yesNo;
  409. }
  410. String
  411. State::GetShortComputerName() const
  412. {
  413. return current.ShortComputerName;
  414. }
  415. void
  416. State::SetShortComputerName(const String& name)
  417. {
  418. current.ShortComputerName = name;
  419. if (!name.empty())
  420. {
  421. current.NetbiosComputerName = Dns::HostnameToNetbiosName(name);
  422. SetFullComputerName();
  423. }
  424. else
  425. {
  426. // This avoids an assert in Dns::HostnameToNetbiosName and
  427. // Computer::ComposeFullDnsComputerName. 119901
  428. current.NetbiosComputerName = name;
  429. current.FullComputerName = name;
  430. }
  431. }
  432. bool
  433. State::WasShortComputerNameChanged() const
  434. {
  435. return
  436. original.ShortComputerName.icompare(current.ShortComputerName) != 0;
  437. }
  438. bool
  439. State::WasNetbiosComputerNameChanged() const
  440. {
  441. return
  442. original.NetbiosComputerName.icompare(current.NetbiosComputerName) != 0;
  443. }
  444. String
  445. State::GetComputerDomainDnsName() const
  446. {
  447. return current.ComputerDomainDnsName;
  448. }
  449. void
  450. State::SetComputerDomainDnsName(const String& newName)
  451. {
  452. current.ComputerDomainDnsName = newName;
  453. SetFullComputerName();
  454. }
  455. void
  456. State::SetFullComputerName()
  457. {
  458. current.FullComputerName =
  459. Computer::ComposeFullDnsComputerName(
  460. current.ShortComputerName,
  461. policyInEffect
  462. ? current.PolicyDomainDnsName
  463. : current.ComputerDomainDnsName);
  464. }
  465. bool
  466. State::WasMembershipChanged() const
  467. {
  468. if (current.DomainName.empty())
  469. {
  470. // this can happen when the domain name is not yet set or has been
  471. // cleared by the user
  472. return true;
  473. }
  474. return
  475. (Dns::CompareNames(
  476. original.DomainName,
  477. current.DomainName) != DnsNameCompareEqual) // 97064
  478. || original.JoinedToWorkgroup != current.JoinedToWorkgroup;
  479. }
  480. bool
  481. State::ChangesNeedSaving() const
  482. {
  483. if (
  484. original.ComputerDomainDnsName.icompare(
  485. current.ComputerDomainDnsName) != 0
  486. || WasMembershipChanged()
  487. || WasShortComputerNameChanged()
  488. || SyncDNSNamesWasChanged())
  489. {
  490. return true;
  491. }
  492. return false;
  493. }
  494. bool
  495. State::GetSyncDNSNames() const
  496. {
  497. return current.SyncDNSNames;
  498. }
  499. void
  500. State::SetSyncDNSNames(bool yesNo)
  501. {
  502. current.SyncDNSNames = yesNo;
  503. }
  504. bool
  505. State::SyncDNSNamesWasChanged() const
  506. {
  507. return original.SyncDNSNames != current.SyncDNSNames;
  508. }
  509. // Prepend the domain name to the user name (making it a fully-qualified name
  510. // in the form "domain\username") if the username does not appear to be an
  511. // UPN, and the username does not appear to be fully-qualified already.
  512. //
  513. // domainName - netbios or DNS domain name.
  514. //
  515. // userName - user account name
  516. //
  517. // JonN 6/27/01 26151
  518. // Attempting to join domain with username "someone\" gives a cryptic error
  519. // returntype becomes HRESULT, userName becomes IN/OUT parameter
  520. //
  521. HRESULT
  522. MassageUserName(const String& domainName, String& userName)
  523. {
  524. LOG_FUNCTION(MassageUserName);
  525. // ASSERT(!userName.empty()); JonN 2/6/01 306520
  526. static const String UPN_DELIMITER(L"@");
  527. if (userName.find(UPN_DELIMITER) != String::npos)
  528. {
  529. // assume the name is a UPN: [email protected]. This is not
  530. // necessarily true, as account names may contain an '@' symbol.
  531. // If that's the case, then they had better fully-qualify the name
  532. // as domain\foo@bar....
  533. return S_OK;
  534. }
  535. if (!domainName.empty() && !userName.empty())
  536. {
  537. static const String DOMAIN_DELIMITER(L"\\");
  538. size_t pos = userName.find(DOMAIN_DELIMITER);
  539. if (pos == String::npos)
  540. {
  541. userName = domainName + DOMAIN_DELIMITER + userName;
  542. }
  543. //
  544. // JonN 6/27/01 26151
  545. // Attempting to join domain with username "someone\" gives a cryptic error
  546. //
  547. else if (pos == userName.length() - 1)
  548. {
  549. return HRESULT_FROM_WIN32(NERR_BadUsername);
  550. }
  551. }
  552. return S_OK;
  553. }
  554. // Calls NetJoinDomain. The first call specifies the create computer account
  555. // flag. If that fails with an access denied error, the call is repeated
  556. // without the flag. (This is to cover the case where the domain
  557. // administrator may have pre-created the computer account.)
  558. //
  559. // dialog - window handle to dialog window to be used as a parent window
  560. // for any child dialogs that may need to be raised.
  561. //
  562. // domain - domain to join. May be the netbios or DNS domain name.
  563. //
  564. // username - user account to be used. If empty, the currently logged in
  565. // user's context is used.
  566. //
  567. // password - password for the above account. May be empty.
  568. HRESULT
  569. JoinDomain(
  570. HWND dialog,
  571. const String& domainName,
  572. const String& username,
  573. const EncryptedString& password,
  574. const String& computerDomainDnsName, // 106601
  575. bool deferSpn)
  576. {
  577. LOG_FUNCTION(JoinDomain);
  578. ASSERT(!domainName.empty());
  579. ASSERT(Win::IsWindow(dialog));
  580. Win::CursorSetting cursor(IDC_WAIT);
  581. // first attempt without create flag in case account was precreated
  582. // 105306
  583. DWORD flags =
  584. NETSETUP_JOIN_DOMAIN
  585. | NETSETUP_DOMAIN_JOIN_IF_JOINED
  586. | NETSETUP_ACCT_DELETE;
  587. if (deferSpn)
  588. {
  589. flags |= NETSETUP_DEFER_SPN_SET;
  590. }
  591. HRESULT hr = MyNetJoinDomain(domainName, username, password, flags);
  592. if (FAILED(hr))
  593. {
  594. LOG(L"Retry with account create flag");
  595. flags |= NETSETUP_ACCT_CREATE;
  596. hr = MyNetJoinDomain(domainName, username, password, flags);
  597. }
  598. if (SUCCEEDED(hr))
  599. {
  600. popup.Info(
  601. dialog,
  602. String::format(
  603. IDS_DOMAIN_WELCOME,
  604. domainName.c_str()));
  605. HINSTANCE hNetWiz = LoadLibrary(c_szWizardFilename);
  606. if (hNetWiz) {
  607. HRESULT (*pfnClearAutoLogon)(VOID) =
  608. (HRESULT (*)(VOID)) GetProcAddress(
  609. hNetWiz,
  610. "ClearAutoLogon"
  611. );
  612. if (pfnClearAutoLogon) {
  613. (*pfnClearAutoLogon)();
  614. }
  615. FreeLibrary(hNetWiz);
  616. }
  617. }
  618. else if (hr == Win32ToHresult(ERROR_DISK_FULL)) // 17367
  619. {
  620. popup.Error(
  621. dialog,
  622. String::format(IDS_DISK_FULL, domainName.c_str()));
  623. }
  624. // JonN 1/03/01 106601
  625. // When the dns suffix checkbox is unchecked,
  626. // domain join fails with a confusing message
  627. else if (hr == Win32ToHresult(ERROR_DS_COULDNT_UPDATE_SPNS)) // 106601
  628. {
  629. bool fWarnDnsSuffix = WarnDnsSuffix(domainName);
  630. popup.Error(
  631. dialog,
  632. String::format( (fWarnDnsSuffix)
  633. ? IDS_JOIN_DOMAIN_COULDNT_UPDATE_SPNS_SUFFIX
  634. : IDS_JOIN_DOMAIN_COULDNT_UPDATE_SPNS,
  635. domainName.c_str(),
  636. computerDomainDnsName.c_str()));
  637. }
  638. else // any other error
  639. {
  640. popup.Error(
  641. dialog,
  642. hr,
  643. String::format(IDS_JOIN_DOMAIN_FAILED, domainName.c_str()));
  644. }
  645. return hr;
  646. }
  647. // Changes the local computer's DNS domain suffix.
  648. //
  649. // dialog - window handle to dialog window to be used as a parent window
  650. // for any child dialogs that may need to be raised.
  651. HRESULT
  652. SetDomainDnsName(HWND dialog)
  653. {
  654. LOG_FUNCTION2(SetDomainDnsName, current.ComputerDomainDnsName);
  655. ASSERT(Win::IsWindow(dialog));
  656. HRESULT hr =
  657. Win::SetComputerNameEx(
  658. ComputerNamePhysicalDnsDomain,
  659. current.ComputerDomainDnsName);
  660. if (FAILED(hr))
  661. {
  662. // 335055
  663. popup.Error(
  664. dialog,
  665. hr,
  666. String::format(
  667. IDS_SET_DOMAIN_DNS_NAME_FAILED,
  668. current.ComputerDomainDnsName.c_str()));
  669. }
  670. return hr;
  671. }
  672. // Changes the local netbios computer name, and, if tcp/ip is installed,
  673. // the local DNS hostname.
  674. //
  675. // dialog - window handle to dialog window to be used as a parent window
  676. // for any child dialogs that may need to be raised.
  677. HRESULT
  678. SetShortName(HWND dialog)
  679. {
  680. LOG_FUNCTION2(setShortName, current.ShortComputerName);
  681. ASSERT(!current.ShortComputerName.empty());
  682. HRESULT hr = S_OK;
  683. bool isTcpInstalled = networkingInstalled && IsTcpIpInstalled();
  684. if (isTcpInstalled)
  685. {
  686. // also sets the netbios name
  687. hr =
  688. Win::SetComputerNameEx(
  689. ComputerNamePhysicalDnsHostname,
  690. current.ShortComputerName);
  691. }
  692. else
  693. {
  694. String netbiosName =
  695. Dns::HostnameToNetbiosName(current.ShortComputerName);
  696. hr =
  697. Win::SetComputerNameEx(ComputerNamePhysicalNetBIOS, netbiosName);
  698. }
  699. // the only reason that this is likely to fail is if the user is not
  700. // a local administrator. The other cases are that the machine is
  701. // in a hosed state.
  702. if (FAILED(hr))
  703. {
  704. popup.Error(
  705. dialog,
  706. hr,
  707. String::format(
  708. IDS_SHORT_NAME_CHANGE_FAILED,
  709. current.ShortComputerName.c_str()));
  710. }
  711. return hr;
  712. }
  713. // Returns true if a new netbios computer name has been saved, but the machine
  714. // has not yet been rebooted. In other words, true if the netbios computer
  715. // name will change on next reboot. 417570
  716. bool
  717. ShortComputerNameHasChangedSinceReboot()
  718. {
  719. LOG_FUNCTION(ShortComputerNameHasChangedSinceReboot());
  720. String active = Computer::GetActivePhysicalNetbiosName();
  721. String future = Computer::GetFuturePhysicalNetbiosName();
  722. return (active != future) ? true : false;
  723. }
  724. // Return true if all changes were successful, false if not. Called when a
  725. // machine is to be joined to a domain, or if a machine is changing membership
  726. // from one domain to another.
  727. //
  728. // workgroup -> domain
  729. // domain A -> domain B
  730. //
  731. // dialog - window handle to dialog window to be used as a parent window
  732. // for any child dialogs that may need to be raised.
  733. bool
  734. State::DoSaveDomainChange(HWND dialog)
  735. {
  736. LOG_FUNCTION(State::DoSaveDomainChange);
  737. ASSERT(Win::IsWindow(dialog));
  738. String username;
  739. EncryptedString password;
  740. if (!RetrieveCredentials(dialog,
  741. IDS_JOIN_CREDENTIALS,
  742. username,
  743. password))
  744. {
  745. return false;
  746. }
  747. HRESULT hr = S_OK;
  748. bool result = true;
  749. bool joinFailed = false;
  750. bool changedSyncFlag = false;
  751. bool shortNameNeedsChange = false;
  752. bool changedShortName = false;
  753. bool dnsSuffixNeedsChange = false;
  754. bool changedDnsSuffix = false;
  755. bool isTcpInstalled = networkingInstalled && IsTcpIpInstalled();
  756. do
  757. {
  758. //
  759. // JonN 6/27/01 26151
  760. // Attempting to join domain with username "someone\" gives a cryptic error
  761. //
  762. hr = MassageUserName(current.DomainName, username);
  763. if (FAILED(hr))
  764. {
  765. break;
  766. }
  767. // update the sync dns suffix flag, if necessary. We do this before
  768. // calling NetJoinDomain, so that it will see the new flag and set the
  769. // DNS suffix accordingly. This means if the join fails, we need to
  770. // undo the change to the flag.
  771. if (original.SyncDNSNames != current.SyncDNSNames)
  772. {
  773. hr = WriteSyncFlag(dialog, current.SyncDNSNames);
  774. if (SUCCEEDED(hr))
  775. {
  776. changedSyncFlag = true;
  777. }
  778. // we don't break on failure, as the flag is less consequential
  779. // than the joined state and computer name.
  780. }
  781. // update NV Hostname and NV Domain, if necessary. This is required
  782. // before calling NetJoinDomain in order to fix bugs 31084 and 40496.
  783. // If the join fails, then we need to undo this change
  784. if (
  785. // short name changed since changes last saved in this session
  786. (original.ShortComputerName.icompare(
  787. current.ShortComputerName) != 0)
  788. or ShortComputerNameHasChangedSinceReboot() )
  789. {
  790. shortNameNeedsChange = true;
  791. }
  792. if (original.ComputerDomainDnsName.icompare(
  793. current.ComputerDomainDnsName) != 0 )
  794. {
  795. dnsSuffixNeedsChange = true;
  796. }
  797. // JonN 12/5/00 244762
  798. // NV Domain only applies when TCP/IP is present.
  799. if (isTcpInstalled && dnsSuffixNeedsChange)
  800. {
  801. RegistryKey key;
  802. hr = key.Open(HKEY_LOCAL_MACHINE,
  803. TCPIP_PARAMS_KEY,
  804. KEY_WRITE);
  805. BREAK_ON_FAILED_HRESULT(hr);
  806. hr = key.SetValue(NEW_SUFFIX_VALUE,
  807. current.ComputerDomainDnsName);
  808. BREAK_ON_FAILED_HRESULT(hr);
  809. changedDnsSuffix = true;
  810. }
  811. hr =
  812. JoinDomain(
  813. dialog,
  814. current.DomainName,
  815. username,
  816. password,
  817. current.ComputerDomainDnsName,
  818. shortNameNeedsChange);
  819. if (FAILED(hr))
  820. {
  821. joinFailed = true;
  822. // JonN 12/5/00 244762
  823. // If we set NW Domain before the join attempt,
  824. // and the join failed, we need to undo that setting now.
  825. if (isTcpInstalled && changedDnsSuffix)
  826. {
  827. RegistryKey key;
  828. HRESULT hr2 = key.Open(HKEY_LOCAL_MACHINE,
  829. TCPIP_PARAMS_KEY,
  830. KEY_WRITE);
  831. ASSERT(SUCCEEDED(hr2));
  832. hr2 = key.SetValue(NEW_SUFFIX_VALUE,
  833. original.ComputerDomainDnsName);
  834. ASSERT(SUCCEEDED(hr2));
  835. }
  836. // don't attempt to save any other changes. If the machine is
  837. // already joined to a domain, changing the short name will cause the
  838. // netbios machine name to not match the machine account, and the
  839. // user will not be able to log in with a domain account.
  840. //
  841. // If the machine is not already joined to the domain, then it
  842. // is possible to change the short name and the dns suffix, and
  843. // emit a message that those things were changed even though
  844. // the join failed.
  845. break;
  846. }
  847. // At this point, the machine is joined to the new domain. But, it will
  848. // have joined with the old netbios computer name. So, if the user has
  849. // changed the name, or the name has been changed at all since the last
  850. // reboot, then we must rename the machine.
  851. //
  852. // ever get the feeling that NetJoinDomain is a poor API?
  853. if (shortNameNeedsChange)
  854. {
  855. // short name changed.
  856. // JonN 12/5/00 244762
  857. // We don't set NV Hostname until after the join succeeds.
  858. if (isTcpInstalled)
  859. {
  860. RegistryKey key;
  861. hr = key.Open(HKEY_LOCAL_MACHINE, TCPIP_PARAMS_KEY, KEY_WRITE);
  862. BREAK_ON_FAILED_HRESULT(hr);
  863. hr = key.SetValue(NEW_HOSTNAME_VALUE, current.ShortComputerName);
  864. BREAK_ON_FAILED_HRESULT(hr);
  865. changedShortName = true;
  866. }
  867. bool renameFailed = false;
  868. hr =
  869. MyNetRenameMachineInDomain(
  870. // We need to pass the hostname instead of the
  871. // netbios name here in order to get the correct DNS hostname
  872. // and SPN set on the computer object. See ntraid (ntbug9)
  873. // #128204
  874. current.ShortComputerName,
  875. username,
  876. password,
  877. NETSETUP_ACCT_CREATE);
  878. if (FAILED(hr))
  879. {
  880. renameFailed = true;
  881. // JonN 12/5/00 244762
  882. // If we set NV Hostname before the rename attempt,
  883. // and the rename failed, we need to undo that setting now.
  884. if (isTcpInstalled)
  885. {
  886. RegistryKey key;
  887. HRESULT hr2 = key.Open(HKEY_LOCAL_MACHINE,
  888. TCPIP_PARAMS_KEY,
  889. KEY_WRITE);
  890. ASSERT(SUCCEEDED(hr2));
  891. hr2 = key.SetValue(NEW_HOSTNAME_VALUE,
  892. original.ShortComputerName);
  893. ASSERT(SUCCEEDED(hr2));
  894. }
  895. // don't fail the whole operation 'cause the rename failed.
  896. // We make a big noise about how the join worked under the old
  897. // name. We need to succeed with the operation as a whole so
  898. // the change dialog will close and the joined state of the
  899. // machine is refreshed. Otherwise, the change dialog stays up,
  900. // the domain name has changed but we don't realize it, so if
  901. // the user types a new domain name in the still open change
  902. // dialog that is the same as the domain the machine was joined
  903. // to (matching the stale state), we don't attempt to join
  904. // again. Whew.
  905. // JonN 1/03/01 106601
  906. // When the dns suffix checkbox is unchecked,
  907. // domain join fails with a confusing message
  908. if (hr == Win32ToHresult(ERROR_DS_COULDNT_UPDATE_SPNS)) // 106601
  909. {
  910. bool fWarnDnsSuffix = WarnDnsSuffix(current.DomainName);
  911. popup.Error(
  912. dialog,
  913. String::format( (fWarnDnsSuffix)
  914. ? IDS_RENAME_JOINED_WITH_OLD_NAME_COULDNT_UPDATE_SPNS_SUFFIX
  915. : IDS_RENAME_JOINED_WITH_OLD_NAME_COULDNT_UPDATE_SPNS,
  916. current.ShortComputerName.c_str(),
  917. current.DomainName.c_str(),
  918. original.ShortComputerName.c_str(),
  919. current.ComputerDomainDnsName.c_str()));
  920. } else {
  921. popup.Error(
  922. dialog,
  923. hr,
  924. String::format(
  925. IDS_RENAME_FAILED_JOINED_WITH_OLD_NAME,
  926. current.ShortComputerName.c_str(),
  927. current.DomainName.c_str(),
  928. original.ShortComputerName.c_str()));
  929. }
  930. hr = S_FALSE;
  931. }
  932. // don't change the hostname if the rename failed, as this will
  933. // prevent the user from logging in as the new computer name will not
  934. // match the sam account name.
  935. if (!renameFailed) // 401355
  936. {
  937. // now set the new hostname and netbios name
  938. hr = SetShortName(dialog);
  939. // this had better work...
  940. ASSERT(SUCCEEDED(hr));
  941. }
  942. }
  943. // NetJoinDomain will change the DNS suffix, if it succeeded. If it
  944. // failed, then we shouldn't change the suffix anyway. 421824
  945. // this is only true if the sync flag is true: otherwise, we need to
  946. // save the suffix when join succeeds.
  947. if (
  948. !current.SyncDNSNames
  949. && dnsSuffixNeedsChange
  950. && !changedDnsSuffix)
  951. {
  952. hr = SetDomainDnsName(dialog);
  953. // this had better work...
  954. ASSERT(SUCCEEDED(hr));
  955. }
  956. }
  957. while (0);
  958. if (joinFailed)
  959. {
  960. HRESULT hr2 = S_OK;
  961. if (changedSyncFlag)
  962. {
  963. // change the sync flag back to its original state.
  964. hr2 = WriteSyncFlag(dialog, original.SyncDNSNames);
  965. // if we can't restore the flag (unlikely), then that's just tough
  966. // potatos.
  967. ASSERT(SUCCEEDED(hr2));
  968. }
  969. // JonN 11/27/00 233783 JoinDomain reports its own errors
  970. } else if (FAILED(hr))
  971. {
  972. popup.Error(dialog, hr, IDS_JOIN_FAILED);
  973. }
  974. return SUCCEEDED(hr) ? true : false;
  975. }
  976. // Call NetUnjoinDomain, first with the account delete flag, if that fails
  977. // then again without it (which almost always "works"). If the first
  978. // attempt fails, but the second succeeds, raise a message to the user
  979. // informing him of the orphaned computer account.
  980. //
  981. // dialog - window handle to dialog window to be used as a parent window
  982. // for any child dialogs that may need to be raised.
  983. //
  984. // domain - domain to unjoin, i.e. the domain the machine is currently
  985. // a member of.
  986. //
  987. // username - user account to be used. If empty, the currently logged in
  988. // user's context is used.
  989. //
  990. // password - password for the above account. May be empty.
  991. HRESULT
  992. UnjoinDomain(
  993. HWND dialog,
  994. const String& domain,
  995. const String& username,
  996. const EncryptedString& password)
  997. {
  998. LOG_FUNCTION(UnjoinDomain);
  999. ASSERT(Win::IsWindow(dialog));
  1000. ASSERT(!domain.empty());
  1001. // username and password may be empty
  1002. Win::CursorSetting cursor(IDC_WAIT);
  1003. HRESULT hr = S_OK;
  1004. do
  1005. {
  1006. hr =
  1007. MyNetUnjoinDomain(
  1008. username,
  1009. password,
  1010. NETSETUP_ACCT_DELETE);
  1011. if (SUCCEEDED(hr))
  1012. {
  1013. break;
  1014. }
  1015. // try again: not trying to delete the computer account. If the
  1016. // user cancelled the credential dialog from the second attempt, then
  1017. // this attempt will use the current context.
  1018. LOG(L"Calling NetUnjoinDomain again, w/o account delete");
  1019. hr =
  1020. MyNetUnjoinDomain(
  1021. username,
  1022. password,
  1023. 0);
  1024. BREAK_ON_FAILED_HRESULT(hr);
  1025. // if we make it here, then the attempt to unjoin and remove the
  1026. // account failed, but the attempt to unjoin and abandon the account
  1027. // succeeded. So we tell the user about the abandonment, and hope
  1028. // they feel really guilty about it.
  1029. // Don't hassle them. They just panic. 95386
  1030. LOG(
  1031. String::format(
  1032. IDS_COMPUTER_ACCOUNT_ORPHANED,
  1033. domain.c_str()));
  1034. // Win::MessageBox(
  1035. // dialog,
  1036. // String::format(
  1037. // IDS_COMPUTER_ACCOUNT_ORPHANED,
  1038. // domain.c_str()),
  1039. // String::load(IDS_APP_TITLE),
  1040. // MB_OK | MB_ICONWARNING);
  1041. }
  1042. while (0);
  1043. if (FAILED(hr))
  1044. {
  1045. popup.Error(
  1046. dialog,
  1047. hr,
  1048. String::format(
  1049. IDS_UNJOIN_FAILED,
  1050. domain.c_str()));
  1051. }
  1052. return hr;
  1053. }
  1054. // Return true if all changes were successful, false if not. Called when the
  1055. // current domain membership is to be severed, or when changing from one
  1056. // workgroup to another.
  1057. //
  1058. // domain -> workgroup
  1059. // workgroup A -> workgroup B
  1060. //
  1061. // dialog - window handle to dialog window to be used as a parent window
  1062. // for any child dialogs that may need to be raised.
  1063. bool
  1064. State::DoSaveWorkgroupChange(HWND dialog)
  1065. {
  1066. LOG_FUNCTION(State::DoSaveWorkgroupChange);
  1067. ASSERT(Win::IsWindow(dialog));
  1068. HRESULT hr = S_OK;
  1069. bool result = true;
  1070. bool unjoinFailed = false;
  1071. bool changedSyncFlag = false;
  1072. do
  1073. {
  1074. // update the sync dns suffix flag, if the user changed it. Do this
  1075. // before calling NetUnjoinDomain, which will clear the dns suffix
  1076. // for us.
  1077. if (original.SyncDNSNames != current.SyncDNSNames)
  1078. {
  1079. hr = WriteSyncFlag(dialog, current.SyncDNSNames);
  1080. if (FAILED(hr))
  1081. {
  1082. result = false;
  1083. }
  1084. else
  1085. {
  1086. changedSyncFlag = true;
  1087. }
  1088. // we don't break on failure, as the flag is less consequential
  1089. // than the joined state and computer name.
  1090. }
  1091. // only unjoin if we were previously joined to a domain
  1092. if (!original.JoinedToWorkgroup and networkingInstalled)
  1093. {
  1094. // get credentials for removing the computer account
  1095. String username;
  1096. EncryptedString password;
  1097. if (!RetrieveCredentials(dialog,
  1098. IDS_UNJOIN_CREDENTIALS,
  1099. username,
  1100. password))
  1101. {
  1102. result = false;
  1103. unjoinFailed = true;
  1104. break;
  1105. }
  1106. //
  1107. // JonN 6/27/01 26151
  1108. // Attempting to join domain with username "someone\" gives a cryptic error
  1109. //
  1110. hr = MassageUserName(original.DomainName, username);
  1111. if (FAILED(hr))
  1112. {
  1113. break;
  1114. }
  1115. hr =
  1116. UnjoinDomain(
  1117. dialog,
  1118. original.DomainName,
  1119. username,
  1120. password);
  1121. // Don't try to change anything else, especially the hostname. If
  1122. // the unjoin failed, and we change the name locally, this will
  1123. // prevent the user from logging in, as the new computer name will
  1124. // not match the computer account name in the domain.
  1125. if (FAILED(hr))
  1126. {
  1127. result = false;
  1128. unjoinFailed = true;
  1129. break;
  1130. }
  1131. }
  1132. // join the workgroup
  1133. hr = MyNetJoinDomain(current.DomainName, String(), EncryptedString(), 0);
  1134. if (FAILED(hr))
  1135. {
  1136. // this is extremely unlikely to fail, and if it did, the
  1137. // workgroup would simply be "WORKGROUP"
  1138. result = false;
  1139. popup.Error(
  1140. dialog,
  1141. hr,
  1142. String::format(
  1143. IDS_JOIN_WORKGROUP_FAILED,
  1144. current.DomainName.c_str()));
  1145. break;
  1146. }
  1147. popup.Info(
  1148. dialog,
  1149. String::format(
  1150. IDS_WORKGROUP_WELCOME,
  1151. current.DomainName.c_str()));
  1152. // change the host name, if the user has changed it.
  1153. if (
  1154. original.ShortComputerName.icompare(
  1155. current.ShortComputerName) != 0)
  1156. {
  1157. hr = SetShortName(dialog);
  1158. if (FAILED(hr))
  1159. {
  1160. result = false;
  1161. }
  1162. }
  1163. // change the domain name, if the user changed it.
  1164. if (
  1165. original.ComputerDomainDnsName.icompare(
  1166. current.ComputerDomainDnsName) != 0 )
  1167. {
  1168. hr = SetDomainDnsName(dialog);
  1169. if (FAILED(hr))
  1170. {
  1171. result = false;
  1172. }
  1173. }
  1174. }
  1175. while (0);
  1176. if (unjoinFailed and changedSyncFlag)
  1177. {
  1178. // change the sync flag back to its original state.
  1179. hr = WriteSyncFlag(dialog, original.SyncDNSNames);
  1180. // if we can't restore the flag (unlikely), then that's just tough
  1181. // potatos.
  1182. ASSERT(SUCCEEDED(hr));
  1183. }
  1184. return result;
  1185. }
  1186. // Return true if all changes succeeded, false otherwise. Called only
  1187. // when domain membership is not to be changed.
  1188. //
  1189. // dialog - window handle to dialog window to be used as a parent window
  1190. // for any child dialogs that may need to be raised.
  1191. bool
  1192. State::DoSaveNameChange(HWND dialog)
  1193. {
  1194. LOG_FUNCTION(State::DoSaveNameChange);
  1195. ASSERT(Win::IsWindow(dialog));
  1196. bool result = true;
  1197. HRESULT hr = S_OK;
  1198. do
  1199. {
  1200. // change the hostname, if the user has made changes
  1201. if (
  1202. original.ShortComputerName.icompare(
  1203. current.ShortComputerName) != 0)
  1204. {
  1205. if (!original.JoinedToWorkgroup and networkingInstalled)
  1206. {
  1207. // machine is joined to a domain -- we need to rename the
  1208. // machine's domain account
  1209. String username;
  1210. EncryptedString password;
  1211. if (!RetrieveCredentials(dialog,
  1212. IDS_RENAME_CREDENTIALS,
  1213. username,
  1214. password))
  1215. {
  1216. result = false;
  1217. break;
  1218. }
  1219. //
  1220. // JonN 6/27/01 26151
  1221. // Attempting to join domain with username "someone\" gives a cryptic error
  1222. //
  1223. hr = MassageUserName(current.DomainName, username);
  1224. if (FAILED(hr))
  1225. {
  1226. break;
  1227. }
  1228. hr =
  1229. MyNetRenameMachineInDomain(
  1230. // We need to pass the full hostname instead of just the
  1231. // netbios name here in order to get the correct DNS
  1232. // hostname and SPN set on the computer object. See ntraid
  1233. // (ntbug9) #128204
  1234. current.ShortComputerName,
  1235. username,
  1236. password,
  1237. NETSETUP_ACCT_CREATE);
  1238. if (FAILED(hr))
  1239. {
  1240. result = false;
  1241. // JonN 1/03/01 106601
  1242. // When the dns suffix checkbox is unchecked,
  1243. // domain join fails with a confusing message
  1244. if (hr == Win32ToHresult(ERROR_DS_COULDNT_UPDATE_SPNS)) // 106601
  1245. {
  1246. bool fWarnDnsSuffix = WarnDnsSuffix(current.DomainName);
  1247. popup.Error(
  1248. dialog,
  1249. String::format( (fWarnDnsSuffix)
  1250. ? IDS_RENAME_COULDNT_UPDATE_SPNS_SUFFIX
  1251. : IDS_RENAME_COULDNT_UPDATE_SPNS,
  1252. current.ShortComputerName.c_str(),
  1253. current.DomainName.c_str(),
  1254. current.ComputerDomainDnsName.c_str()));
  1255. } else {
  1256. popup.Error(
  1257. dialog,
  1258. hr,
  1259. String::format(
  1260. IDS_RENAME_FAILED,
  1261. current.ShortComputerName.c_str()));
  1262. }
  1263. }
  1264. // Don't try to change anything else, especially the netbios name.
  1265. // If the rename failed, and we change the name locally, this will
  1266. // prevent the user from logging in, as the new computer name will
  1267. // not match the computer account name in the domain.
  1268. BREAK_ON_FAILED_HRESULT(hr);
  1269. }
  1270. // Set the dns hostname and the netbios name. If we called
  1271. // NetRenameMachineInDomain, this may redundantly set netbios name
  1272. // (as NetRenameMachineInDomain calls SetComputerNameEx with the
  1273. // netbios name).
  1274. hr = SetShortName(dialog);
  1275. // Since NetRenameMachineInDomain calls SetComputerNameEx, if that
  1276. // failed, the rename would also have failed. So our 2nd call to
  1277. // SetComputerNameEx in SetShortName is almost certain to succeed.
  1278. // If it does fail, we're not going to attempt to roll back the
  1279. // rename.
  1280. if (FAILED(hr))
  1281. {
  1282. result = false;
  1283. break;
  1284. }
  1285. }
  1286. // update the sync dns suffix flag, if the user changed it
  1287. if (original.SyncDNSNames != current.SyncDNSNames)
  1288. {
  1289. hr = WriteSyncFlag(dialog, current.SyncDNSNames);
  1290. if (FAILED(hr))
  1291. {
  1292. result = false;
  1293. }
  1294. }
  1295. // change the domain name, if the user changed it.
  1296. if (
  1297. original.ComputerDomainDnsName.icompare(
  1298. current.ComputerDomainDnsName) != 0 )
  1299. {
  1300. hr = SetDomainDnsName(dialog);
  1301. if (FAILED(hr))
  1302. {
  1303. result = false;
  1304. }
  1305. }
  1306. }
  1307. while (0);
  1308. return result;
  1309. }
  1310. bool
  1311. State::SaveChanges(HWND dialog)
  1312. {
  1313. LOG_FUNCTION(State::SaveChanges);
  1314. ASSERT(Win::IsWindow(dialog));
  1315. // Changes to domain membership are made first, then to the computer name.
  1316. //
  1317. // workgroup -> domain
  1318. // domain A -> domain B
  1319. //
  1320. if (
  1321. (original.JoinedToWorkgroup && !current.JoinedToWorkgroup)
  1322. ||
  1323. ( !original.JoinedToWorkgroup
  1324. && !current.JoinedToWorkgroup
  1325. && original.DomainName.icompare(current.DomainName) != 0) )
  1326. {
  1327. return DoSaveDomainChange(dialog);
  1328. }
  1329. //
  1330. // domain -> workgroup
  1331. // workgroup A -> workgroup B
  1332. //
  1333. else if (
  1334. !original.JoinedToWorkgroup && current.JoinedToWorkgroup
  1335. ||
  1336. original.JoinedToWorkgroup && current.JoinedToWorkgroup
  1337. && original.DomainName.icompare(current.DomainName) != 0)
  1338. {
  1339. return DoSaveWorkgroupChange(dialog);
  1340. }
  1341. //
  1342. // name change only
  1343. //
  1344. ASSERT(original.JoinedToWorkgroup == current.JoinedToWorkgroup);
  1345. ASSERT(original.DomainName == original.DomainName);
  1346. return DoSaveNameChange(dialog);
  1347. }
  1348. void
  1349. State::SetChangesMadeThisSession(bool yesNo)
  1350. {
  1351. LOG_FUNCTION2(
  1352. State::SetChangesMadeThisSession,
  1353. yesNo ? L"true" : L"false");
  1354. mustReboot = yesNo;
  1355. }
  1356. bool
  1357. State::ChangesMadeThisSession() const
  1358. {
  1359. LOG_FUNCTION2(
  1360. State::ChangesMadeThisSession,
  1361. mustReboot ? L"true" : L"false");
  1362. return mustReboot;
  1363. }
  1364. String
  1365. State::GetNetbiosComputerName() const
  1366. {
  1367. return current.NetbiosComputerName;
  1368. }
  1369. String
  1370. State::GetOriginalShortComputerName() const
  1371. {
  1372. return original.ShortComputerName;
  1373. }
  1374. DSROLE_OPERATION_STATE
  1375. GetDsRoleChangeState()
  1376. {
  1377. LOG_FUNCTION(GetDsRoleChangeState);
  1378. DSROLE_OPERATION_STATE result = ::DsRoleOperationIdle;
  1379. DSROLE_OPERATION_STATE_INFO* info = 0;
  1380. HRESULT hr = MyDsRoleGetPrimaryDomainInformation(0, info);
  1381. if (SUCCEEDED(hr))
  1382. {
  1383. if (info)
  1384. {
  1385. result = info->OperationState;
  1386. ::DsRoleFreeMemory(info);
  1387. }
  1388. }
  1389. return result;
  1390. }
  1391. bool
  1392. IsUpgradingDc()
  1393. {
  1394. LOG_FUNCTION(IsUpgradingDc);
  1395. bool isUpgrade = false;
  1396. DSROLE_UPGRADE_STATUS_INFO* info = 0;
  1397. HRESULT hr = MyDsRoleGetPrimaryDomainInformation(0, info);
  1398. if (SUCCEEDED(hr))
  1399. {
  1400. isUpgrade = ( (info->OperationState & DSROLE_UPGRADE_IN_PROGRESS) ? true : false );
  1401. ::DsRoleFreeMemory(info);
  1402. }
  1403. return isUpgrade;
  1404. }
  1405. // Evaluate a list of preconditions that must be met before a name change can
  1406. // be committed. Return a string describing the first unmet condition, or
  1407. // an empty string if all conditions are met.
  1408. //
  1409. // These preconditions are a subset of those checked before enabling the
  1410. // button to allow the user to enter changes. The conditions not checked here
  1411. // are those that cannot be changed while the ui is running (logged on as
  1412. // local admin, machine is DC)
  1413. //
  1414. // 389646
  1415. String
  1416. CheckPreconditions()
  1417. {
  1418. LOG_FUNCTION(CheckPreconditions);
  1419. String result;
  1420. do
  1421. {
  1422. // could have started dcpromo after opening netid
  1423. if (IsDcpromoRunning())
  1424. {
  1425. result = String::load(IDS_PRECHK_DCPROMO_RUNNING);
  1426. break;
  1427. }
  1428. else
  1429. {
  1430. // this test is redundant if dcpromo is running, so only perform
  1431. // it when dcpromo is not running.
  1432. if (IsUpgradingDc())
  1433. {
  1434. result = String::load(IDS_PRECHK_MUST_COMPLETE_DCPROMO);
  1435. }
  1436. }
  1437. // could have installed cert svc after opening netid
  1438. NTService certsvc(L"CertSvc");
  1439. if (certsvc.IsInstalled())
  1440. {
  1441. // sorry- renaming cert issuers invalidates their certs.
  1442. result = String::load(IDS_PRECHK_CANT_RENAME_CERT_SVC);
  1443. }
  1444. // could have completed dcpromo after opening netid
  1445. switch (GetDsRoleChangeState())
  1446. {
  1447. case ::DsRoleOperationIdle:
  1448. {
  1449. // do nothing
  1450. break;
  1451. }
  1452. case ::DsRoleOperationActive:
  1453. {
  1454. // a role change operation is underway
  1455. result = String::load(IDS_PRECHK_ROLE_CHANGE_IN_PROGRESS);
  1456. break;
  1457. }
  1458. case ::DsRoleOperationNeedReboot:
  1459. {
  1460. // a role change has already taken place, need to reboot before
  1461. // attempting another.
  1462. result = String::load(IDS_PRECHK_ROLE_CHANGE_NEEDS_REBOOT);
  1463. break;
  1464. }
  1465. default:
  1466. {
  1467. ASSERT(false);
  1468. break;
  1469. }
  1470. }
  1471. if (!result.empty())
  1472. {
  1473. break;
  1474. }
  1475. // could have installed/uninstalled networking after opening
  1476. // netid
  1477. // re-evaluate this here again, which will be globally visible.
  1478. networkingInstalled = IsNetworkingInstalled();
  1479. State& state = State::GetInstance();
  1480. if (!state.IsNetworkingInstalled() && !state.IsMemberOfWorkgroup())
  1481. {
  1482. // domain members need to be able to reach a dc
  1483. result = String::load(IDS_PRECHK_NETWORKING_NEEDED);
  1484. }
  1485. }
  1486. while (0);
  1487. return result;
  1488. }