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.

1942 lines
44 KiB

  1. // Copyright (c) 1997-1999 Microsoft Corporation
  2. //
  3. // code common to several pages
  4. //
  5. // 12-16-97 sburns
  6. #include "headers.hxx"
  7. #include "common.hpp"
  8. #include "resource.h"
  9. #include "state.hpp"
  10. #include "ds.hpp"
  11. #include <DiagnoseDcNotFound.hpp>
  12. #include <ValidateDomainName.h>
  13. // Creates the fonts for setLargeFonts().
  14. //
  15. // hDialog - handle to a dialog to be used to retrieve a device
  16. // context.
  17. //
  18. // bigBoldFont - receives the handle of the big bold font created.
  19. void
  20. InitFonts(
  21. HWND hDialog,
  22. HFONT& bigBoldFont)
  23. {
  24. ASSERT(Win::IsWindow(hDialog));
  25. HRESULT hr = S_OK;
  26. do
  27. {
  28. NONCLIENTMETRICS ncm;
  29. // REVIEWED-2002/02/22-sburns call correctly passes byte count.
  30. ::ZeroMemory(&ncm, sizeof ncm);
  31. ncm.cbSize = sizeof ncm;
  32. // ISSUE-2002/02/27-sburns Seems to me that the second param here needs
  33. // to be sizeof ncm
  34. hr = Win::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
  35. BREAK_ON_FAILED_HRESULT(hr);
  36. LOGFONT bigBoldLogFont = ncm.lfMessageFont;
  37. bigBoldLogFont.lfWeight = FW_BOLD;
  38. String fontName = String::load(IDS_BIG_BOLD_FONT_NAME);
  39. // ensure null termination 260237
  40. // REVIEWED-2002/02/22-sburns call correctly passes byte count.
  41. ::ZeroMemory(bigBoldLogFont.lfFaceName, LF_FACESIZE * sizeof WCHAR);
  42. size_t fnLen = fontName.length();
  43. fontName.copy(
  44. bigBoldLogFont.lfFaceName,
  45. // don't copy over the last null
  46. min(LF_FACESIZE - 1, fnLen));
  47. unsigned fontSize = 0;
  48. String::load(IDS_BIG_BOLD_FONT_SIZE).convert(fontSize);
  49. ASSERT(fontSize);
  50. HDC hdc = 0;
  51. hr = Win::GetDC(hDialog, hdc);
  52. BREAK_ON_FAILED_HRESULT(hr);
  53. bigBoldLogFont.lfHeight =
  54. - ::MulDiv(
  55. static_cast<int>(fontSize),
  56. Win::GetDeviceCaps(hdc, LOGPIXELSY),
  57. 72);
  58. hr = Win::CreateFontIndirect(bigBoldLogFont, bigBoldFont);
  59. BREAK_ON_FAILED_HRESULT(hr);
  60. Win::ReleaseDC(hDialog, hdc);
  61. }
  62. while (0);
  63. }
  64. void
  65. SetControlFont(HWND parentDialog, int controlID, HFONT font)
  66. {
  67. ASSERT(Win::IsWindow(parentDialog));
  68. ASSERT(controlID);
  69. ASSERT(font);
  70. HWND control = Win::GetDlgItem(parentDialog, controlID);
  71. if (control)
  72. {
  73. Win::SetWindowFont(control, font, true);
  74. }
  75. }
  76. void
  77. SetLargeFont(HWND dialog, int bigBoldResID)
  78. {
  79. ASSERT(Win::IsWindow(dialog));
  80. ASSERT(bigBoldResID);
  81. static HFONT bigBoldFont = 0;
  82. if (!bigBoldFont)
  83. {
  84. InitFonts(dialog, bigBoldFont);
  85. }
  86. SetControlFont(dialog, bigBoldResID, bigBoldFont);
  87. }
  88. HRESULT
  89. GetDcName(const String& domainName, String& resultDcName)
  90. {
  91. LOG_FUNCTION(GetDcName);
  92. ASSERT(!domainName.empty());
  93. resultDcName.erase();
  94. HRESULT hr = S_OK;
  95. do
  96. {
  97. DOMAIN_CONTROLLER_INFO* info = 0;
  98. hr =
  99. MyDsGetDcName(
  100. 0,
  101. domainName,
  102. // pass the force rediscovery flag to make sure we don't pick up
  103. // a dc that is down 262221
  104. DS_DIRECTORY_SERVICE_REQUIRED | DS_FORCE_REDISCOVERY,
  105. info);
  106. BREAK_ON_FAILED_HRESULT(hr);
  107. ASSERT(info->DomainControllerName);
  108. if (info->DomainControllerName)
  109. {
  110. // we found an NT5 domain
  111. resultDcName =
  112. Computer::RemoveLeadingBackslashes(info->DomainControllerName);
  113. LOG(resultDcName);
  114. }
  115. ::NetApiBufferFree(info);
  116. if (resultDcName.empty())
  117. {
  118. hr = Win32ToHresult(ERROR_DOMAIN_CONTROLLER_NOT_FOUND);
  119. }
  120. BREAK_ON_FAILED_HRESULT(hr);
  121. }
  122. while (0);
  123. return hr;
  124. }
  125. HRESULT
  126. BrowserSetComputer(const SmartInterface<IDsBrowseDomainTree>& browser)
  127. {
  128. LOG_FUNCTION(BrowserSetComputer);
  129. HRESULT hr = S_OK;
  130. do
  131. {
  132. State& state = State::GetInstance();
  133. String username =
  134. MassageUserName(
  135. state.GetUserDomainName(),
  136. state.GetUsername());
  137. EncryptedString password = state.GetPassword();
  138. String computer;
  139. hr =
  140. GetDcName(
  141. state.GetUserDomainName(),
  142. computer);
  143. BREAK_ON_FAILED_HRESULT(hr);
  144. LOG(L"Calling IDsBrowseDomainTree::SetComputer");
  145. LOG(String::format(L"pszComputerName : %1", computer.c_str()));
  146. LOG(String::format(L"pszUserName : %1", username.c_str()));
  147. WCHAR* cleartext = password.GetClearTextCopy();
  148. hr =
  149. browser->SetComputer(
  150. computer.c_str(),
  151. username.c_str(),
  152. cleartext);
  153. password.DestroyClearTextCopy(cleartext);
  154. LOG_HRESULT(hr);
  155. BREAK_ON_FAILED_HRESULT(hr);
  156. }
  157. while (0);
  158. return hr;
  159. }
  160. HRESULT
  161. ReadDomainsHelper(bool bindWithCredentials, Callback* callback)
  162. {
  163. LOG_FUNCTION(ReadDomainsHelper);
  164. HRESULT hr = S_OK;
  165. do
  166. {
  167. AutoCoInitialize coInit;
  168. hr = coInit.Result();
  169. BREAK_ON_FAILED_HRESULT(hr);
  170. SmartInterface<IDsBrowseDomainTree> browser;
  171. hr = browser.AcquireViaCreateInstance(
  172. CLSID_DsDomainTreeBrowser,
  173. 0,
  174. CLSCTX_INPROC_SERVER,
  175. IID_IDsBrowseDomainTree);
  176. BREAK_ON_FAILED_HRESULT(hr);
  177. if (bindWithCredentials)
  178. {
  179. LOG(L"binding with credentials");
  180. hr = BrowserSetComputer(browser);
  181. BREAK_ON_FAILED_HRESULT(hr);
  182. }
  183. LOG(L"Calling IDsBrowseDomainTree::GetDomains");
  184. DOMAIN_TREE* tree = 0;
  185. hr = browser->GetDomains(&tree, 0);
  186. BREAK_ON_FAILED_HRESULT(hr);
  187. ASSERT(tree);
  188. if (tree && callback)
  189. {
  190. //lint -e534 ignore return value
  191. callback->Execute(tree);
  192. }
  193. hr = browser->FreeDomains(&tree);
  194. ASSERT(SUCCEEDED(hr));
  195. }
  196. while (0);
  197. return hr;
  198. }
  199. String
  200. BrowseForDomain(HWND parent)
  201. {
  202. LOG_FUNCTION(BrowseForDomain);
  203. ASSERT(Win::IsWindow(parent));
  204. String retval;
  205. HRESULT hr = S_OK;
  206. do
  207. {
  208. Win::WaitCursor cursor;
  209. AutoCoInitialize coInit;
  210. hr = coInit.Result();
  211. BREAK_ON_FAILED_HRESULT(hr);
  212. // CODEWORK: the credential page could cache an instance of the browser,
  213. // rebuilding and setting the search root when only when new credentials
  214. // are entered. Since the browser caches the last result, this would
  215. // make subsequent retreivals of the domain much faster.
  216. // addendum: tho the revised browser seems pretty quick
  217. SmartInterface<IDsBrowseDomainTree> browser;
  218. hr = browser.AcquireViaCreateInstance(
  219. CLSID_DsDomainTreeBrowser,
  220. 0,
  221. CLSCTX_INPROC_SERVER,
  222. IID_IDsBrowseDomainTree);
  223. BREAK_ON_FAILED_HRESULT(hr);
  224. hr = BrowserSetComputer(browser);
  225. BREAK_ON_FAILED_HRESULT(hr);
  226. PWSTR result = 0;
  227. hr = browser->BrowseTo(parent, &result, 0);
  228. BREAK_ON_FAILED_HRESULT(hr);
  229. if (result)
  230. {
  231. retval = result;
  232. ::CoTaskMemFree(result);
  233. }
  234. }
  235. while (0);
  236. if (FAILED(hr))
  237. {
  238. popup.Error(parent, hr, IDS_CANT_BROWSE_FOREST);
  239. }
  240. return retval;
  241. }
  242. class RootDomainCollectorCallback : public Callback
  243. {
  244. public:
  245. explicit
  246. RootDomainCollectorCallback(StringList& domains_)
  247. :
  248. Callback(),
  249. domains(domains_)
  250. {
  251. ASSERT(domains.empty());
  252. domains.clear();
  253. }
  254. virtual
  255. ~RootDomainCollectorCallback()
  256. {
  257. }
  258. virtual
  259. int
  260. Execute(void* param)
  261. {
  262. ASSERT(param);
  263. // root domains are all those on the sibling link of the root
  264. // node of the domain tree.
  265. DOMAIN_TREE* tree = reinterpret_cast<DOMAIN_TREE*>(param);
  266. if (tree)
  267. {
  268. for (
  269. DOMAIN_DESC* desc = &(tree->aDomains[0]);
  270. desc;
  271. desc = desc->pdNextSibling)
  272. {
  273. LOG(
  274. String::format(
  275. L"pushing root domain %1",
  276. desc->pszName));
  277. domains.push_back(desc->pszName);
  278. }
  279. }
  280. return 0;
  281. }
  282. private:
  283. StringList& domains;
  284. };
  285. HRESULT
  286. ReadRootDomains(bool bindWithCredentials, StringList& domains)
  287. {
  288. LOG_FUNCTION(ReadRootDomains);
  289. RootDomainCollectorCallback rdcc(domains);
  290. return
  291. ReadDomainsHelper(
  292. bindWithCredentials,
  293. &rdcc);
  294. }
  295. bool
  296. IsRootDomain(bool bindWithCredentials)
  297. {
  298. LOG_FUNCTION(IsRootDomain);
  299. static bool computed = false;
  300. static bool isRoot = false;
  301. if (!computed)
  302. {
  303. StringList domains;
  304. if (SUCCEEDED(ReadRootDomains(bindWithCredentials, domains)))
  305. {
  306. String domain =
  307. State::GetInstance().GetComputer().GetDomainDnsName();
  308. for (
  309. StringList::iterator i = domains.begin();
  310. i != domains.end();
  311. ++i)
  312. {
  313. if (Dns::CompareNames((*i), domain) == DnsNameCompareEqual)
  314. {
  315. LOG(String::format(L"found match: %1", (*i).c_str()));
  316. ASSERT(!(*i).empty());
  317. isRoot = true;
  318. break;
  319. }
  320. }
  321. computed = true;
  322. }
  323. }
  324. LOG(isRoot ? L"is root" : L"is not root");
  325. return isRoot;
  326. }
  327. // first = child, second = parent
  328. typedef std::pair<String, String> StringPair;
  329. typedef
  330. std::list<
  331. StringPair,
  332. Burnslib::Heap::Allocator<StringPair> >
  333. ChildParentList;
  334. class ChildDomainCollectorCallback : public Callback
  335. {
  336. public:
  337. explicit
  338. ChildDomainCollectorCallback(ChildParentList& domains_)
  339. :
  340. Callback(),
  341. domains(domains_)
  342. {
  343. ASSERT(domains.empty());
  344. domains.clear();
  345. }
  346. virtual
  347. ~ChildDomainCollectorCallback()
  348. {
  349. }
  350. virtual
  351. int
  352. Execute(void* param)
  353. {
  354. LOG_FUNCTION(ChildDomainCollectorCallback::Execute);
  355. ASSERT(param);
  356. DOMAIN_TREE* tree = reinterpret_cast<DOMAIN_TREE*>(param);
  357. if (tree)
  358. {
  359. typedef
  360. std::deque<
  361. DOMAIN_DESC*,
  362. Burnslib::Heap::Allocator<DOMAIN_DESC*> >
  363. DDDeque;
  364. std::stack<DOMAIN_DESC*, DDDeque> s;
  365. // first we push all the nodes for all root domains. These are
  366. // the nodes on the sibling link of the tree root. Hereafter,
  367. // the sibling link is only used to chase child domains.
  368. for (
  369. DOMAIN_DESC* desc = &(tree->aDomains[0]);
  370. desc;
  371. desc = desc->pdNextSibling)
  372. {
  373. LOG(
  374. String::format(
  375. L"pushing root domain %1",
  376. desc->pszName));
  377. s.push(desc);
  378. }
  379. // next, we work thru the stack, looking for nodes that have
  380. // nodes on their child links. When we find such a node, we
  381. // collect in the child list all the children on that link, and
  382. // push them so that they will in turn be evaluated.
  383. DWORD count = 0;
  384. while (!s.empty())
  385. {
  386. DOMAIN_DESC* desc = s.top();
  387. s.pop();
  388. ASSERT(desc);
  389. if (desc)
  390. {
  391. count++;
  392. LOG(String::format(L"evaluating %1", desc->pszName));
  393. String parentname = desc->pszName;
  394. for (
  395. DOMAIN_DESC* child = desc->pdChildList;
  396. child;
  397. child = child->pdNextSibling)
  398. {
  399. s.push(child);
  400. String childname = child->pszName;
  401. LOG(
  402. String::format(
  403. L"parent: %1 child: %2",
  404. parentname.c_str(),
  405. childname.c_str()));
  406. domains.push_back(std::make_pair(childname, parentname));
  407. }
  408. }
  409. }
  410. ASSERT(count == tree->dwCount);
  411. }
  412. return 0;
  413. }
  414. private:
  415. ChildParentList& domains;
  416. };
  417. HRESULT
  418. ReadChildDomains(bool bindWithCredentials, ChildParentList& domains)
  419. {
  420. LOG_FUNCTION(ReadChildDomains);
  421. ChildDomainCollectorCallback cdcc(domains);
  422. return
  423. ReadDomainsHelper(
  424. bindWithCredentials,
  425. &cdcc);
  426. }
  427. String
  428. GetParentDomainDnsName(
  429. const String& childDomainDNSName,
  430. bool bindWithCredentials)
  431. {
  432. LOG_FUNCTION2(GetParentDomainDnsName, childDomainDNSName);
  433. ASSERT(!childDomainDNSName.empty());
  434. ChildParentList domains;
  435. if (SUCCEEDED(ReadChildDomains(bindWithCredentials, domains)))
  436. {
  437. for (
  438. ChildParentList::iterator i = domains.begin();
  439. i != domains.end();
  440. ++i)
  441. {
  442. if (
  443. Dns::CompareNames(
  444. (*i).first,
  445. childDomainDNSName) == DnsNameCompareEqual)
  446. {
  447. LOG(
  448. String::format(
  449. L"found parent: %1",
  450. (*i).second.c_str()));
  451. ASSERT(!(*i).second.empty());
  452. return (*i).second;
  453. }
  454. }
  455. }
  456. LOG(L"domain is not a child");
  457. return String();
  458. }
  459. class DomainCollectorCallback : public Callback
  460. {
  461. public:
  462. explicit
  463. DomainCollectorCallback(StringList& domains_)
  464. :
  465. Callback(),
  466. domains(domains_)
  467. {
  468. ASSERT(domains.empty());
  469. domains.clear();
  470. }
  471. virtual
  472. ~DomainCollectorCallback()
  473. {
  474. }
  475. virtual
  476. int
  477. Execute(void* param)
  478. {
  479. LOG_FUNCTION(DomainCollectorCallback::Execute);
  480. ASSERT(param);
  481. DOMAIN_TREE* tree = reinterpret_cast<DOMAIN_TREE*>(param);
  482. if (tree)
  483. {
  484. for (DWORD i = 0; i < tree->dwCount; ++i)
  485. {
  486. PCWSTR name = tree->aDomains[i].pszName;
  487. LOG(String::format(L"domain found: %1", name));
  488. domains.push_back(name);
  489. }
  490. }
  491. return 0;
  492. }
  493. private:
  494. StringList& domains;
  495. };
  496. HRESULT
  497. ReadDomains(StringList& domains)
  498. {
  499. LOG_FUNCTION(ReadDomains);
  500. DomainCollectorCallback dcc(domains);
  501. return ReadDomainsHelper(true, &dcc);
  502. }
  503. String
  504. BrowseForFolder(HWND parent, int titleResID)
  505. {
  506. LOG_FUNCTION(BrowseForFolder);
  507. ASSERT(Win::IsWindow(parent));
  508. ASSERT(titleResID > 0);
  509. String result;
  510. HRESULT hr = S_OK;
  511. LPMALLOC pmalloc = 0;
  512. LPITEMIDLIST drives = 0;
  513. LPITEMIDLIST pidl = 0;
  514. do
  515. {
  516. hr = Win::SHGetMalloc(pmalloc);
  517. if (FAILED(hr) || !pmalloc)
  518. {
  519. break;
  520. }
  521. // get a pidl for the local drives (really My Computer)
  522. hr = Win::SHGetSpecialFolderLocation(parent, CSIDL_DRIVES, drives);
  523. BREAK_ON_FAILED_HRESULT(hr);
  524. BROWSEINFO info;
  525. // REVIEWED-2002/02/25-sburns proper byte count is passed.
  526. ::ZeroMemory(&info, sizeof info);
  527. String title = String::load(titleResID);
  528. wchar_t buf[MAX_PATH + 1];
  529. // REVIEWED-2002/02/25-sburns proper byte count is passed.
  530. ::ZeroMemory(buf, sizeof buf);
  531. info.hwndOwner = parent;
  532. info.pidlRoot = drives;
  533. info.pszDisplayName = buf;
  534. info.lpszTitle = title.c_str();
  535. info.ulFlags = BIF_RETURNONLYFSDIRS | BIF_RETURNFSANCESTORS;
  536. // REVIEWED-2002/02/25-sburns pszDisplayName in info is at least
  537. // MAX_PATH characters long.
  538. pidl = Win::SHBrowseForFolder(info);
  539. if (pidl)
  540. {
  541. result = Win::SHGetPathFromIDList(pidl);
  542. }
  543. }
  544. while (0);
  545. if (pmalloc)
  546. {
  547. pmalloc->Free(pidl);
  548. pmalloc->Free(drives);
  549. pmalloc->Release();
  550. }
  551. return result;
  552. }
  553. bool
  554. CheckDriveType(const String& path)
  555. {
  556. LOG_FUNCTION(CheckDriveType);
  557. ASSERT(!path.empty());
  558. UINT type = Win::GetDriveType(path);
  559. switch (type)
  560. {
  561. case DRIVE_FIXED:
  562. {
  563. return true;
  564. }
  565. case DRIVE_REMOVABLE:
  566. {
  567. // only legal iff volume = system volume
  568. String vol = FS::GetRootFolder(path);
  569. String sys = FS::GetRootFolder(Win::GetSystemDirectory());
  570. if (vol.icompare(sys) == 0)
  571. {
  572. return true;
  573. }
  574. break;
  575. }
  576. default:
  577. {
  578. // all others bad
  579. break;
  580. }
  581. }
  582. return false;
  583. }
  584. bool
  585. PathHasNonAsciiCharacters(const String& path, String& message)
  586. {
  587. LOG_FUNCTION2(PathHasNonAsciiCharacters, path);
  588. ASSERT(!path.empty());
  589. bool result = false;
  590. // This set is all 7-bit ascii characters above ascii 31 that are
  591. // not illegal FAT filename characters, plus : and \
  592. static const String ALLOWED_ASCII(
  593. L" !#$%&'()-."
  594. L"0123456789"
  595. L":@"
  596. L"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  597. L"\\^_`"
  598. L"abcdefghijklmnopqrstuvwxyz"
  599. L"{}~" );
  600. String illegalsFound;
  601. String::size_type pos = 0;
  602. while ((pos = path.find_first_not_of(ALLOWED_ASCII, pos)) != String::npos)
  603. {
  604. String illegal(1, path[pos]);
  605. if (illegalsFound.find_first_of(illegal) == String::npos)
  606. {
  607. illegalsFound += illegal;
  608. illegalsFound += L" ";
  609. }
  610. ++pos;
  611. }
  612. if (!illegalsFound.empty())
  613. {
  614. message =
  615. String::format(
  616. IDS_NON_ASCII_PATH_CHARS,
  617. path.c_str(),
  618. illegalsFound.c_str());
  619. result = true;
  620. }
  621. LOG_BOOL(result);
  622. return result;
  623. }
  624. bool
  625. ValidateDcInstallPath(
  626. const String& path,
  627. HWND parent,
  628. int editResId,
  629. bool requiresNtfs5,
  630. bool requiresAsciiCharacters,
  631. bool requiresUncompressedFolder)
  632. {
  633. LOG_FUNCTION(ValidateDcInstallPath);
  634. ASSERT(!path.empty());
  635. ASSERT(editResId);
  636. ASSERT(Win::IsWindow(parent));
  637. bool result = false;
  638. String message;
  639. do
  640. {
  641. if (
  642. (path.icompare(Win::GetWindowsDirectory()) == 0)
  643. || (path.icompare(Win::GetSystemWindowsDirectory()) == 0) )
  644. {
  645. message = String::format(IDS_PATH_IS_WINDIR, path.c_str());
  646. break;
  647. }
  648. if (path.icompare(Win::GetSystemDirectory()) == 0)
  649. {
  650. message = String::format(IDS_PATH_IS_SYSTEM32, path.c_str());
  651. break;
  652. }
  653. if (FS::GetPathSyntax(path) != FS::SYNTAX_ABSOLUTE_DRIVE)
  654. {
  655. message = String::format(IDS_BAD_PATH_FORMAT, path.c_str());
  656. break;
  657. }
  658. if (requiresAsciiCharacters)
  659. {
  660. if (PathHasNonAsciiCharacters(path, message))
  661. {
  662. ASSERT(!message.empty());
  663. break;
  664. }
  665. }
  666. if (!CheckDriveType(path))
  667. {
  668. message = String::format(IDS_BAD_DRIVE_TYPE, path.c_str());
  669. break;
  670. }
  671. if (requiresNtfs5 && (FS::GetFileSystemType(path) != FS::NTFS5))
  672. {
  673. message = String::format(IDS_NOT_NTFS5, path.c_str());
  674. break;
  675. }
  676. // prohibit paths that contain mounted volumes 325264
  677. // even when they don't exist 435428
  678. String mountRoot;
  679. HRESULT hr = FS::GetVolumePathName(path, mountRoot);
  680. ASSERT(SUCCEEDED(hr));
  681. // '3' == length of root of a "normal" volume ("C:\")
  682. if (mountRoot.length() > 3)
  683. {
  684. message =
  685. String::format(
  686. IDS_PATH_CONTAINS_MOUNTED_VOLUMES,
  687. path.c_str(),
  688. mountRoot.c_str());
  689. break;
  690. }
  691. DWORD attrs = 0;
  692. hr = Win::GetFileAttributes(path, attrs);
  693. if (SUCCEEDED(hr))
  694. {
  695. // path exists
  696. // reject paths that refer an existing file
  697. if (!(attrs & FILE_ATTRIBUTE_DIRECTORY))
  698. {
  699. message = String::format(IDS_PATH_NOT_DIRECTORY, path.c_str());
  700. break;
  701. }
  702. // if necessary, reject paths that are compressed
  703. if ((attrs & FILE_ATTRIBUTE_COMPRESSED) && requiresUncompressedFolder)
  704. {
  705. message = String::format(IDS_PATH_IS_COMPRESSED, path.c_str());
  706. break;
  707. }
  708. if (!FS::IsFolderEmpty(path))
  709. {
  710. if (
  711. popup.MessageBox(
  712. parent,
  713. String::format(IDS_EMPTY_PATH, path.c_str()),
  714. MB_ICONWARNING | MB_YESNO) == IDNO)
  715. {
  716. // don't gripe...silently disapprove
  717. HWND edit = Win::GetDlgItem(parent, editResId);
  718. Win::SendMessage(edit, EM_SETSEL, 0, -1);
  719. Win::SetFocus(edit);
  720. break;
  721. }
  722. }
  723. }
  724. result = true;
  725. }
  726. while (0);
  727. if (!message.empty())
  728. {
  729. popup.Gripe(parent, editResId, message);
  730. }
  731. return result;
  732. }
  733. bool
  734. DoLabelValidation(
  735. HWND dialog,
  736. int editResID,
  737. int badSyntaxResID,
  738. bool gripeOnNonRFC = true,
  739. bool gripeOnNumericLabel = true)
  740. {
  741. LOG_FUNCTION(DoLabelValidation);
  742. ASSERT(Win::IsWindow(dialog));
  743. ASSERT(editResID > 0);
  744. bool valid = false;
  745. String label = Win::GetTrimmedDlgItemText(dialog, editResID);
  746. switch (Dns::ValidateDnsLabelSyntax(label))
  747. {
  748. case Dns::NON_RFC:
  749. {
  750. if (gripeOnNonRFC)
  751. {
  752. // warn about non-rfc names
  753. popup.Info(
  754. dialog,
  755. String::format(IDS_NON_RFC_NAME, label.c_str()));
  756. }
  757. // fall thru
  758. }
  759. case Dns::VALID:
  760. {
  761. valid = true;
  762. break;
  763. }
  764. case Dns::TOO_LONG:
  765. {
  766. popup.Gripe(
  767. dialog,
  768. editResID,
  769. String::format(
  770. IDS_DNS_LABEL_TOO_LONG,
  771. label.c_str(),
  772. Dns::MAX_LABEL_LENGTH));
  773. break;
  774. }
  775. case Dns::NUMERIC:
  776. {
  777. if (!gripeOnNumericLabel)
  778. {
  779. valid = true;
  780. break;
  781. }
  782. // fall thru
  783. }
  784. case Dns::BAD_CHARS:
  785. case Dns::INVALID:
  786. default:
  787. {
  788. popup.Gripe(
  789. dialog,
  790. editResID,
  791. String::format(badSyntaxResID, label.c_str()));
  792. break;
  793. }
  794. }
  795. return valid;
  796. }
  797. bool
  798. ValidateChildDomainLeafNameLabel(
  799. HWND dialog,
  800. int editResID,
  801. bool gripeOnNonRfc)
  802. {
  803. LOG_FUNCTION(ValidateChildDomainLeafNameLabel);
  804. String name = Win::GetTrimmedDlgItemText(dialog, editResID);
  805. if (name.empty())
  806. {
  807. popup.Gripe(dialog, editResID, IDS_BLANK_LEAF_NAME);
  808. return false;
  809. }
  810. // If parent is non-RFC, then so will be the child. The user has been
  811. // griped to already, so don't gripe twice
  812. // 291558
  813. return
  814. DoLabelValidation(
  815. dialog,
  816. editResID,
  817. IDS_BAD_LABEL_SYNTAX,
  818. gripeOnNonRfc,
  819. // allow numeric labels. NTRAID#NTBUG9-321168-2001/02/20-sburns
  820. false);
  821. }
  822. bool
  823. ValidateSiteName(HWND dialog, int editResID)
  824. {
  825. LOG_FUNCTION(ValidateSiteName);
  826. String name = Win::GetTrimmedDlgItemText(dialog, editResID);
  827. if (name.empty())
  828. {
  829. popup.Gripe(dialog, editResID, IDS_BLANK_SITE_NAME);
  830. return false;
  831. }
  832. // A site name is just a DNS label
  833. return DoLabelValidation(dialog, editResID, IDS_BAD_SITE_SYNTAX);
  834. }
  835. void
  836. ShowTroubleshooter(HWND parent, int topicResID)
  837. {
  838. LOG_FUNCTION(ShowTroubleshooter);
  839. ASSERT(Win::IsWindow(parent));
  840. String file = String::load(IDS_HTML_HELP_FILE);
  841. String topic = String::load(topicResID);
  842. ASSERT(!topic.empty());
  843. LOG(String::format(L"file: %1 topic: %2", file.c_str(), topic.c_str()));
  844. Win::HtmlHelp(
  845. parent,
  846. file,
  847. HH_DISPLAY_TOPIC,
  848. reinterpret_cast<DWORD_PTR>(topic.c_str()));
  849. }
  850. String
  851. MassageUserName(const String& domainName, const String& userName)
  852. {
  853. LOG_FUNCTION2(MassageUserName, userName);
  854. ASSERT(!userName.empty());
  855. String result = userName;
  856. do
  857. {
  858. if (userName.find(L"@") != String::npos)
  859. {
  860. // userName includes an @, looks like a UPN to us, so don't
  861. // mess with it further 17699
  862. LOG(L"looks like a UPN");
  863. break;
  864. }
  865. if (!domainName.empty())
  866. {
  867. static const String DOMAIN_SEP_CHAR = L"\\";
  868. String name = userName;
  869. size_t pos = userName.find(DOMAIN_SEP_CHAR);
  870. if (pos != String::npos)
  871. {
  872. // remove the domain name in the userName string and replace it
  873. // with the domainName String
  874. name = userName.substr(pos + 1);
  875. ASSERT(!name.empty());
  876. }
  877. result = domainName + DOMAIN_SEP_CHAR + name;
  878. break;
  879. }
  880. // otherwise, the username appears as "foo\bar", so we don't touch it.
  881. }
  882. while (0);
  883. LOG(result);
  884. return result;
  885. }
  886. bool
  887. IsChildDomain(bool bindWithCredentials)
  888. {
  889. LOG_FUNCTION(IsChildDomain);
  890. static bool computed = false;
  891. static String parent;
  892. if (!computed)
  893. {
  894. parent =
  895. GetParentDomainDnsName(
  896. State::GetInstance().GetComputer().GetDomainDnsName(),
  897. bindWithCredentials);
  898. computed = true;
  899. }
  900. LOG(
  901. parent.empty()
  902. ? String(L"not a child")
  903. : String::format(L"is child. parent: %1", parent.c_str()));
  904. return !parent.empty();
  905. }
  906. bool
  907. IsForestRootDomain()
  908. {
  909. LOG_FUNCTION(IsForestRootDomain);
  910. const Computer& c = State::GetInstance().GetComputer();
  911. bool result = (c.GetDomainDnsName() == c.GetForestDnsName());
  912. LOG(
  913. String::format(
  914. L"%1 a forest root domain",
  915. result ? L"is" : L"is not"));
  916. return result;
  917. }
  918. bool
  919. ValidateDomainExists(HWND dialog, int editResID, String& domainDnsName)
  920. {
  921. return ValidateDomainExists(dialog, String(), editResID, domainDnsName);
  922. }
  923. bool
  924. ValidateDomainExists(
  925. HWND dialog,
  926. const String& domainName,
  927. int editResId,
  928. String& domainDnsName)
  929. {
  930. LOG_FUNCTION(ValidateDomainExists);
  931. ASSERT(Win::IsWindow(dialog));
  932. ASSERT(editResId > 0);
  933. String name =
  934. domainName.empty()
  935. ? Win::GetTrimmedDlgItemText(dialog, editResId)
  936. : domainName;
  937. // The invoking code should verify this condition, but we will handle
  938. // it just in case.
  939. ASSERT(!name.empty());
  940. domainDnsName.erase();
  941. Win::WaitCursor cursor;
  942. bool valid = false;
  943. DOMAIN_CONTROLLER_INFO* info = 0;
  944. do
  945. {
  946. if (name.empty())
  947. {
  948. popup.Gripe(
  949. dialog,
  950. editResId,
  951. String::load(IDS_MUST_ENTER_DOMAIN));
  952. break;
  953. }
  954. // determine whether we can reach a DC for the domain, and whether it is
  955. // a DS dc, and whether the name we're validating is truly the DNS name
  956. // of the domain.
  957. LOG(L"Validating " + name);
  958. HRESULT hr =
  959. MyDsGetDcName(
  960. 0,
  961. name,
  962. // force discovery to ensure that we don't pick up a cached
  963. // entry for a domain that may no longer exist
  964. DS_FORCE_REDISCOVERY | DS_DIRECTORY_SERVICE_PREFERRED,
  965. info);
  966. if (FAILED(hr) || !info)
  967. {
  968. ShowDcNotFoundErrorDialog(
  969. dialog,
  970. editResId,
  971. name,
  972. String::load(IDS_WIZARD_TITLE),
  973. String::format(IDS_DC_NOT_FOUND, name.c_str()),
  974. // name might be netbios
  975. false);
  976. break;
  977. }
  978. if (!(info->Flags & DS_DS_FLAG))
  979. {
  980. // domain is not a DS domain, or the locator could not find a DS DC
  981. // for that domain, so the candidate name is bad
  982. ShowDcNotFoundErrorDialog(
  983. dialog,
  984. editResId,
  985. name,
  986. String::load(IDS_WIZARD_TITLE),
  987. String::format(IDS_DC_NOT_FOUND, name.c_str()),
  988. // name might be netbios
  989. false);
  990. break;
  991. }
  992. LOG(name + L" refers to DS domain");
  993. // here we rely on the fact that if DsGetDcName is provided a flat
  994. // domain name, then info->DomainName will also be the (same,
  995. // normalized) flat name. Likewise, if provided a DNS domain name,
  996. // info->DomainName will be the (same, normalized) DNS domain name.
  997. if (info->Flags & DS_DNS_DOMAIN_FLAG)
  998. {
  999. // we can infer that name is a DNS domain name, since
  1000. // info->DomainName is a DNS domain name.
  1001. LOG(L"name is the DNS name");
  1002. ASSERT(
  1003. Dns::CompareNames(name, info->DomainName)
  1004. == DnsNameCompareEqual);
  1005. valid = true;
  1006. break;
  1007. }
  1008. LOG(name + L" is not the DNS domain name");
  1009. // the candidate name is not the DNS name of the domain. Make another
  1010. // call to DsGetDcName to determine the DNS domain name so we can get
  1011. // the user to confirm.
  1012. DOMAIN_CONTROLLER_INFO* info2 = 0;
  1013. hr = MyDsGetDcName(0, name, DS_RETURN_DNS_NAME, info2);
  1014. if (FAILED(hr) || !info2)
  1015. {
  1016. ShowDcNotFoundErrorDialog(
  1017. dialog,
  1018. editResId,
  1019. name,
  1020. String::load(IDS_WIZARD_TITLE),
  1021. String::format(IDS_DC_NOT_FOUND, name.c_str()),
  1022. // name is probably netbios
  1023. false);
  1024. break;
  1025. }
  1026. String message =
  1027. String::format(
  1028. IDS_CONFIRM_DNS_NAME,
  1029. name.c_str(),
  1030. info2->DomainName);
  1031. if (
  1032. popup.MessageBox(
  1033. dialog,
  1034. message,
  1035. MB_YESNO) == IDYES)
  1036. {
  1037. domainDnsName = info2->DomainName;
  1038. // The user accept the dns name as the name he meant to enter. As one
  1039. // last step, we call DsGetDcName with the dns domain name. If this
  1040. // fails, then we are in the situation where a DC can be found with
  1041. // netbios but not dns. So the user has a dns configuration problem.
  1042. // 28298
  1043. DOMAIN_CONTROLLER_INFO* info3 = 0;
  1044. hr =
  1045. MyDsGetDcName(
  1046. 0,
  1047. domainDnsName,
  1048. // force discovery to ensure that we don't pick up a cached
  1049. // entry for a domain that may no longer exist
  1050. DS_FORCE_REDISCOVERY | DS_DIRECTORY_SERVICE_PREFERRED,
  1051. info3);
  1052. if (FAILED(hr) || !info3)
  1053. {
  1054. ShowDcNotFoundErrorDialog(
  1055. dialog,
  1056. editResId,
  1057. domainDnsName,
  1058. String::load(IDS_WIZARD_TITLE),
  1059. String::format(IDS_DC_NOT_FOUND, domainDnsName.c_str()),
  1060. // we know the name is not netbios
  1061. true);
  1062. domainDnsName.erase();
  1063. break;
  1064. }
  1065. ::NetApiBufferFree(info3);
  1066. valid = true;
  1067. }
  1068. // the user rejected the dns name, so they are admitting that what
  1069. // they entered was bogus. Don't pop up an error box in this case,
  1070. // as we have pestered the user enough.
  1071. ::NetApiBufferFree(info2);
  1072. }
  1073. while (0);
  1074. if (info)
  1075. {
  1076. ::NetApiBufferFree(info);
  1077. }
  1078. #ifdef DBG
  1079. if (!valid)
  1080. {
  1081. ASSERT(domainDnsName.empty());
  1082. }
  1083. #endif
  1084. return valid;
  1085. }
  1086. bool
  1087. ValidateDomainDoesNotExist(
  1088. HWND dialog,
  1089. int editResID)
  1090. {
  1091. return ValidateDomainDoesNotExist(dialog, String(), editResID);
  1092. }
  1093. bool
  1094. ValidateDomainDoesNotExist(
  1095. HWND dialog,
  1096. const String& domainName,
  1097. int editResID)
  1098. {
  1099. LOG_FUNCTION(ValidateDomainDoesNotExist);
  1100. ASSERT(Win::IsWindow(dialog));
  1101. ASSERT(editResID > 0);
  1102. // this can take awhile.
  1103. Win::WaitCursor cursor;
  1104. String name =
  1105. domainName.empty()
  1106. ? Win::GetTrimmedDlgItemText(dialog, editResID)
  1107. : domainName;
  1108. // The invoking code should verify this condition, but we will handle
  1109. // it just in case.
  1110. ASSERT(!name.empty());
  1111. bool valid = true;
  1112. String message;
  1113. do
  1114. {
  1115. if (name.empty())
  1116. {
  1117. message = String::load(IDS_MUST_ENTER_DOMAIN);
  1118. valid = false;
  1119. break;
  1120. }
  1121. if (IsDomainReachable(name) || DS::IsDomainNameInUse(name))
  1122. {
  1123. message = String::format(IDS_DOMAIN_NAME_IN_USE, name.c_str());
  1124. valid = false;
  1125. break;
  1126. }
  1127. // otherwise the domain does not exist
  1128. }
  1129. while (0);
  1130. if (!valid)
  1131. {
  1132. popup.Gripe(dialog, editResID, message);
  1133. }
  1134. return valid;
  1135. }
  1136. void
  1137. DisableConsoleLocking()
  1138. {
  1139. LOG_FUNCTION(disableConsoleLocking);
  1140. HRESULT hr = S_OK;
  1141. do
  1142. {
  1143. BOOL screenSaverEnabled = FALSE;
  1144. hr =
  1145. // REVIEWED-2002/02/27-sburns the docs are vague, but we know that
  1146. // screenSaverEnabled as a BOOL works.
  1147. Win::SystemParametersInfo(
  1148. SPI_GETSCREENSAVEACTIVE,
  1149. 0,
  1150. &screenSaverEnabled,
  1151. 0);
  1152. BREAK_ON_FAILED_HRESULT(hr);
  1153. if (screenSaverEnabled)
  1154. {
  1155. // disable it.
  1156. screenSaverEnabled = FALSE;
  1157. hr =
  1158. // ISSUE-2002/02/27-sburns it looks to me from the docs that the
  1159. // second param should be used, and not the third
  1160. Win::SystemParametersInfo(
  1161. SPI_SETSCREENSAVEACTIVE,
  1162. 0,
  1163. &screenSaverEnabled,
  1164. SPIF_SENDCHANGE);
  1165. ASSERT(SUCCEEDED(hr));
  1166. }
  1167. }
  1168. while (0);
  1169. // turn off lock computer option in winlogon
  1170. do
  1171. {
  1172. RegistryKey key;
  1173. hr =
  1174. key.Create(
  1175. HKEY_LOCAL_MACHINE,
  1176. L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon");
  1177. BREAK_ON_FAILED_HRESULT(hr);
  1178. // '2' means "disable for this session, reset to 0 on reboot."
  1179. hr = key.SetValue(L"DisableLockWorkstation", 2);
  1180. BREAK_ON_FAILED_HRESULT(hr);
  1181. }
  1182. while (0);
  1183. }
  1184. void
  1185. EnableConsoleLocking()
  1186. {
  1187. LOG_FUNCTION(EnableConsoleLocking);
  1188. #ifdef DBG
  1189. State& state = State::GetInstance();
  1190. ASSERT(
  1191. state.GetRunContext() != State::PDC_UPGRADE &&
  1192. state.GetRunContext() != State::BDC_UPGRADE);
  1193. #endif
  1194. // CODEWORK: we don't re-enable the screensaver (we need to remember
  1195. // if it was enabled when we called DisableConsoleLocking)
  1196. HRESULT hr = S_OK;
  1197. do
  1198. {
  1199. RegistryKey key;
  1200. hr =
  1201. key.Create(
  1202. HKEY_LOCAL_MACHINE,
  1203. L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon");
  1204. BREAK_ON_FAILED_HRESULT(hr);
  1205. // 0 means "enable"
  1206. hr = key.SetValue(L"DisableLockWorkstation", 0);
  1207. BREAK_ON_FAILED_HRESULT(hr);
  1208. }
  1209. while (0);
  1210. }
  1211. bool
  1212. CheckDiskSpace(const String& path, unsigned minSpaceMB)
  1213. {
  1214. LOG_FUNCTION(checkDiskSpace);
  1215. ASSERT(FS::IsValidPath(path));
  1216. String vol = FS::GetRootFolder(path);
  1217. ULONGLONG spaceInBytes;
  1218. // REVIEWED-2002/02/25-sburns byte count is correctly passed.
  1219. ::ZeroMemory(&spaceInBytes, sizeof ULONGLONG);
  1220. HRESULT hr = FS::GetAvailableSpace(vol, spaceInBytes);
  1221. if (SUCCEEDED(hr))
  1222. {
  1223. // prefast is happiest if we start out with the same-width integers
  1224. // before we shift one, hence the cast.
  1225. // NTRAID#NTBUG9-540413-2002/03/12-sburns
  1226. ULONGLONG spaceInMb = spaceInBytes / ( (ULONGLONG) 1 << 20);
  1227. if (spaceInMb >= minSpaceMB)
  1228. {
  1229. return true;
  1230. }
  1231. }
  1232. return false;
  1233. }
  1234. String
  1235. GetFirstNtfs5HardDrive()
  1236. {
  1237. LOG_FUNCTION(GetFirstNtfs5HardDrive);
  1238. String result;
  1239. do
  1240. {
  1241. StringVector dl;
  1242. HRESULT hr = FS::GetValidDrives(std::back_inserter(dl));
  1243. BREAK_ON_FAILED_HRESULT(hr);
  1244. ASSERT(dl.size());
  1245. for (
  1246. StringVector::iterator i = dl.begin();
  1247. i != dl.end();
  1248. ++i)
  1249. {
  1250. LOG(*i);
  1251. if (
  1252. FS::GetFileSystemType(*i) == FS::NTFS5
  1253. && Win::GetDriveType(*i) == DRIVE_FIXED )
  1254. {
  1255. // found one. good to go
  1256. LOG(String::format(L"%1 is NTFS5", i->c_str()));
  1257. result = *i;
  1258. break;
  1259. }
  1260. }
  1261. }
  1262. while (0);
  1263. LOG(result);
  1264. return result;
  1265. }
  1266. bool
  1267. ComputerWasRenamedAndNeedsReboot()
  1268. {
  1269. LOG_FUNCTION(ComputerWasRenamedAndNeedsReboot);
  1270. bool result = false;
  1271. do
  1272. {
  1273. String active = Computer::GetActivePhysicalNetbiosName();
  1274. String future = Computer::GetFuturePhysicalNetbiosName();
  1275. if (active.icompare(future) != 0)
  1276. {
  1277. // a name change is pending reboot.
  1278. LOG(L"netbios name was changed");
  1279. LOG(active);
  1280. LOG(future);
  1281. result = true;
  1282. break;
  1283. }
  1284. // At this point, the netbios names are the same, or there is no future
  1285. // netbios name. So check the DNS names.
  1286. if (IsTcpIpInstalled())
  1287. {
  1288. // DNS names only exist if tcp/ip is installed.
  1289. active = Computer::GetActivePhysicalFullDnsName();
  1290. future = Computer::GetFuturePhysicalFullDnsName();
  1291. if (Dns::CompareNames(active, future) == DnsNameCompareNotEqual)
  1292. {
  1293. LOG(L"dns name was changed");
  1294. LOG(active);
  1295. LOG(future);
  1296. result = true;
  1297. break;
  1298. }
  1299. }
  1300. // At this point, we have confirmed that there is no pending name
  1301. // change.
  1302. LOG(L"No pending computer name change");
  1303. }
  1304. while (0);
  1305. LOG_BOOL(result);
  1306. return result;
  1307. }
  1308. String
  1309. GetForestName(const String& domain, HRESULT* hrOut)
  1310. {
  1311. LOG_FUNCTION2(GetForestName, domain);
  1312. ASSERT(!domain.empty());
  1313. String dnsForestName;
  1314. DOMAIN_CONTROLLER_INFO* info = 0;
  1315. HRESULT hr =
  1316. MyDsGetDcName(
  1317. 0,
  1318. domain,
  1319. DS_RETURN_DNS_NAME,
  1320. info);
  1321. if (SUCCEEDED(hr) && info)
  1322. {
  1323. ASSERT(info->DnsForestName);
  1324. if (info->DnsForestName)
  1325. {
  1326. dnsForestName = info->DnsForestName;
  1327. }
  1328. ::NetApiBufferFree(info);
  1329. }
  1330. if (hrOut)
  1331. {
  1332. *hrOut = hr;
  1333. }
  1334. return dnsForestName;
  1335. }
  1336. HRESULT
  1337. ValidatePasswordAgainstPolicy(
  1338. const EncryptedString& password,
  1339. const String& userName)
  1340. {
  1341. LOG_FUNCTION(ValidatePasswordAgainstPolicy);
  1342. HRESULT hr = S_OK;
  1343. PWSTR cleartext = password.GetClearTextCopy();
  1344. NET_VALIDATE_PASSWORD_RESET_INPUT_ARG inArg;
  1345. ::ZeroMemory(&inArg, sizeof inArg);
  1346. inArg.ClearPassword = cleartext;
  1347. // UserAccountName is used by one of the complexity checking tests to
  1348. // ensure that the candidate password does not contain that string
  1349. inArg.UserAccountName =
  1350. userName.empty()
  1351. // ISSUE-2002/04/19-sburns This should be null since UmitA has
  1352. // fixed his bug to allow this parameter to be null
  1353. ? L"aklsdjiwuerowierlkmclknlaksjdqweiquroijlkasjlkq" // 0
  1354. : (PWSTR) userName.c_str();
  1355. // HashedPassword is only used when checking password histories.
  1356. NET_VALIDATE_OUTPUT_ARG* outArg = 0;
  1357. hr =
  1358. Win32ToHresult(
  1359. ::NetValidatePasswordPolicy(
  1360. 0,
  1361. 0,
  1362. NetValidatePasswordReset,
  1363. &inArg,
  1364. (void**) &outArg));
  1365. password.DestroyClearTextCopy(cleartext);
  1366. if (SUCCEEDED(hr) && outArg)
  1367. {
  1368. hr = Win32ToHresult(outArg->ValidationStatus);
  1369. NetApiBufferFree(outArg);
  1370. }
  1371. LOG_HRESULT(hr);
  1372. return hr;
  1373. }
  1374. bool
  1375. IsValidPassword(
  1376. HWND dialog,
  1377. int passwordResID,
  1378. int confirmResID,
  1379. bool isForSafeMode,
  1380. EncryptedString& validatedPassword)
  1381. {
  1382. LOG_FUNCTION(IsValidPassword);
  1383. ASSERT(Win::IsWindow(dialog));
  1384. ASSERT(passwordResID);
  1385. ASSERT(confirmResID);
  1386. validatedPassword.Clear();
  1387. bool result = false;
  1388. int message = 0;
  1389. do
  1390. {
  1391. EncryptedString password =
  1392. Win::GetEncryptedDlgItemText(dialog, passwordResID);
  1393. EncryptedString confirm =
  1394. Win::GetEncryptedDlgItemText(dialog, confirmResID);
  1395. if (password != confirm)
  1396. {
  1397. message = IDS_PASSWORD_MISMATCH;
  1398. break;
  1399. }
  1400. State& state = State::GetInstance();
  1401. if (
  1402. isForSafeMode
  1403. && password.IsEmpty()
  1404. && state.RunHiddenUnattended()
  1405. && !state.IsSafeModeAdminPwdOptionPresent() )
  1406. {
  1407. // password is blank, we're running with an answerfile, and the
  1408. // user did not specify a password in the answerfile. In this
  1409. // case, we will use the local admin password as the domain admin
  1410. // password. So we should skip the policy check.
  1411. // NTRAID#NTBUG9-619502-2002/05/09-sburns
  1412. // this branch empty on purpose.
  1413. }
  1414. else
  1415. {
  1416. // CODEWORK: in v2 of this code, extract the name of the local admin
  1417. // sid
  1418. HRESULT hr = ValidatePasswordAgainstPolicy(password, String());
  1419. if (FAILED(hr))
  1420. {
  1421. // we translate the error codes into our own message IDs because
  1422. // the system error messages are too horrible to bear, and those
  1423. // that own them aren't inclined to change them.
  1424. switch (HRESULT_CODE(hr))
  1425. {
  1426. case NERR_PasswordTooShort:
  1427. {
  1428. message = IDS_PASSWORD_TOO_SHORT;
  1429. break;
  1430. }
  1431. case NERR_PasswordTooLong:
  1432. {
  1433. message = IDS_PASSWORD_TOO_LONG;
  1434. break;
  1435. }
  1436. case NERR_PasswordNotComplexEnough:
  1437. {
  1438. message = IDS_PASSWORD_TOO_SIMPLE;
  1439. break;
  1440. }
  1441. case NERR_PasswordFilterError:
  1442. {
  1443. message = IDS_PASSWORD_TOO_COARSE;
  1444. break;
  1445. }
  1446. default:
  1447. {
  1448. // in this case, the validation itself failed, so we accept
  1449. // the candidate password. Note that this is no worse than
  1450. // not checking the policy in the first place, and the
  1451. // ramifications are that the account will get a possibly
  1452. // weaker password than policy would like.
  1453. break;
  1454. }
  1455. }
  1456. if (message)
  1457. {
  1458. break;
  1459. }
  1460. }
  1461. }
  1462. result = true;
  1463. // at this point, the password has successfully run the gauntlet.
  1464. validatedPassword = password;
  1465. }
  1466. while (0);
  1467. if (!result)
  1468. {
  1469. String blank;
  1470. Win::SetDlgItemText(dialog, passwordResID, blank);
  1471. Win::SetDlgItemText(dialog, confirmResID, blank);
  1472. popup.Gripe(dialog, passwordResID, message);
  1473. }
  1474. return result;
  1475. }