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.

709 lines
16 KiB

  1. // Copyright (c) 1997-1999 Microsoft Corporation
  2. //
  3. // get credentials page
  4. //
  5. // 12-22-97 sburns
  6. #include "headers.hxx"
  7. #include "page.hpp"
  8. #include "CredentialsPage.hpp"
  9. #include "resource.h"
  10. #include "state.hpp"
  11. #include "ds.hpp"
  12. #include "CredentialUiHelpers.hpp"
  13. #include "common.hpp"
  14. #include <DiagnoseDcNotFound.hpp>
  15. CredentialsPage::CredentialsPage()
  16. :
  17. DCPromoWizardPage(
  18. IDD_GET_CREDENTIALS,
  19. IDS_CREDENTIALS_PAGE_TITLE,
  20. IDS_CREDENTIALS_PAGE_SUBTITLE),
  21. readAnswerfile(false),
  22. hwndCred(0),
  23. lastWizardButtonsState(PSWIZB_BACK)
  24. {
  25. LOG_CTOR(CredentialsPage);
  26. CredUIInitControls();
  27. }
  28. CredentialsPage::~CredentialsPage()
  29. {
  30. LOG_DTOR(CredentialsPage);
  31. }
  32. void
  33. CredentialsPage::OnInit()
  34. {
  35. LOG_FUNCTION(CredentialsPage::OnInit);
  36. Win::Edit_LimitText(
  37. Win::GetDlgItem(hwnd, IDC_DOMAIN),
  38. Dns::MAX_NAME_LENGTH);
  39. }
  40. void
  41. CredentialsPage::Enable()
  42. {
  43. // LOG_FUNCTION(CredentialsPage::Enable);
  44. DWORD nextState =
  45. PSWIZB_BACK
  46. | (( !CredUi::GetUsername(Win::GetDlgItem(hwnd, IDC_CRED)).empty()
  47. && !Win::GetTrimmedDlgItemText(hwnd, IDC_DOMAIN).empty() )
  48. ? PSWIZB_NEXT : 0);
  49. // only set the buttons when the state changes to prevent button
  50. // flicker when the user is typing in the user name field
  51. // NTRAID#NTBUG9-504441-2001/12/07-sburns
  52. if (nextState != lastWizardButtonsState)
  53. {
  54. Win::PropSheet_SetWizButtons(
  55. Win::GetParent(hwnd),
  56. nextState);
  57. lastWizardButtonsState = nextState;
  58. }
  59. }
  60. bool
  61. CredentialsPage::OnCommand(
  62. HWND /* windowFrom */ ,
  63. unsigned controlIDFrom,
  64. unsigned code)
  65. {
  66. // LOG_FUNCTION(CredentialsPage::OnCommand);
  67. switch (controlIDFrom)
  68. {
  69. case IDC_CRED:
  70. {
  71. if (code == CRN_USERNAMECHANGE)
  72. {
  73. SetChanged(controlIDFrom);
  74. Enable();
  75. return true;
  76. }
  77. break;
  78. }
  79. case IDC_DOMAIN:
  80. {
  81. if (code == EN_CHANGE)
  82. {
  83. SetChanged(controlIDFrom);
  84. Enable();
  85. return true;
  86. }
  87. break;
  88. }
  89. default:
  90. {
  91. // do nothing
  92. break;
  93. }
  94. }
  95. return false;
  96. }
  97. bool
  98. CredentialsPage::ShouldSkipPage()
  99. {
  100. LOG_FUNCTION(CredentialsPage::ShouldSkipPage);
  101. State& state = State::GetInstance();
  102. State::Operation oper = state.GetOperation();
  103. bool result = false;
  104. switch (oper)
  105. {
  106. case State::FOREST:
  107. {
  108. // never need credentials for new forest.
  109. result = true;
  110. break;
  111. }
  112. case State::DEMOTE:
  113. {
  114. // The demote page should circumvent this page if necessary, so if
  115. // we make it here, don't skip the page.
  116. break;
  117. }
  118. case State::ABORT_BDC_UPGRADE:
  119. case State::REPLICA:
  120. case State::TREE:
  121. case State::CHILD:
  122. {
  123. break;
  124. }
  125. case State::NONE:
  126. default:
  127. {
  128. ASSERT(false);
  129. break;
  130. }
  131. }
  132. return result;
  133. }
  134. int
  135. CredentialsPage::DetermineNextPage()
  136. {
  137. LOG_FUNCTION(CredentialsPage::DetermineNextPage);
  138. State& state = State::GetInstance();
  139. int id = IDD_PATHS;
  140. switch (state.GetOperation())
  141. {
  142. case State::DEMOTE:
  143. case State::ABORT_BDC_UPGRADE:
  144. {
  145. id = IDD_ADMIN_PASSWORD;
  146. break;
  147. }
  148. case State::FOREST:
  149. {
  150. id = IDD_NEW_FOREST;
  151. break;
  152. }
  153. case State::REPLICA:
  154. {
  155. if (state.GetRunContext() == State::BDC_UPGRADE)
  156. {
  157. id = IDD_PATHS;
  158. }
  159. else
  160. {
  161. id = IDD_REPLICA;
  162. }
  163. break;
  164. }
  165. case State::TREE:
  166. {
  167. id = IDD_NEW_TREE;
  168. break;
  169. }
  170. case State::CHILD:
  171. {
  172. id = IDD_NEW_CHILD;
  173. break;
  174. }
  175. case State::NONE:
  176. default:
  177. {
  178. ASSERT(false);
  179. break;
  180. }
  181. }
  182. return id;
  183. }
  184. String
  185. GetMessage()
  186. {
  187. String message;
  188. State& state = State::GetInstance();
  189. switch (state.GetOperation())
  190. {
  191. case State::ABORT_BDC_UPGRADE:
  192. {
  193. message = String::load(IDS_ABORT_BDC_UPGRADE_CREDENTIALS);
  194. break;
  195. }
  196. case State::TREE:
  197. case State::CHILD:
  198. case State::REPLICA:
  199. {
  200. message = String::load(IDS_PROMOTION_CREDENTIALS);
  201. break;
  202. }
  203. case State::DEMOTE:
  204. {
  205. // 318736 demote requires enterprise admin credentials -- for
  206. // root and child domains alike.
  207. message =
  208. String::format(
  209. IDS_ROOT_DOMAIN_CREDENTIALS,
  210. state.GetComputer().GetForestDnsName().c_str());
  211. break;
  212. }
  213. case State::FOREST:
  214. {
  215. // do nothing, the page will be skipped.
  216. break;
  217. }
  218. case State::NONE:
  219. default:
  220. {
  221. ASSERT(false);
  222. break;
  223. }
  224. }
  225. return message;
  226. }
  227. String
  228. DefaultUserDomainName()
  229. {
  230. String d;
  231. State& state = State::GetInstance();
  232. const Computer& computer = state.GetComputer();
  233. switch (state.GetOperation())
  234. {
  235. case State::ABORT_BDC_UPGRADE:
  236. {
  237. // NTRAID#NTBUG9-469647-2001/09/21-sburns
  238. d = computer.GetDomainNetbiosName();
  239. break;
  240. }
  241. case State::FOREST:
  242. {
  243. // do nothing
  244. break;
  245. }
  246. case State::DEMOTE: // 301361
  247. case State::TREE:
  248. {
  249. d = computer.GetForestDnsName();
  250. break;
  251. }
  252. case State::CHILD:
  253. {
  254. d = computer.GetDomainDnsName();
  255. break;
  256. }
  257. case State::REPLICA:
  258. {
  259. d = computer.GetDomainDnsName();
  260. if (d.empty() && state.ReplicateFromMedia())
  261. {
  262. d = state.GetReplicaDomainDNSName();
  263. }
  264. break;
  265. }
  266. case State::NONE:
  267. default:
  268. {
  269. ASSERT(false);
  270. break;
  271. }
  272. }
  273. return d;
  274. }
  275. bool
  276. AreSmartCardsAllowed()
  277. {
  278. LOG_FUNCTION(AreSmartCardsAllowed);
  279. bool result = false;
  280. // Only use the smartcard flag when the machine is joined to a domain. On a
  281. // standalone machine, the smartcard won't have access to any domain
  282. // authority to authenticate it.
  283. // NTRAID#NTBUG9-287538-2001/01/23-sburns
  284. State& state = State::GetInstance();
  285. Computer& computer = state.GetComputer();
  286. if (
  287. computer.IsJoinedToDomain()
  288. // can only use smartcards on replica promotions
  289. // NTRAID#NTBUG9-311150-2001/02/19-sburns
  290. && state.GetOperation() == State::REPLICA)
  291. {
  292. result = true;
  293. }
  294. LOG_BOOL(result);
  295. return result;
  296. }
  297. void
  298. CredentialsPage::CreateCredentialControl()
  299. {
  300. LOG_FUNCTION(CredentialsPage::CreateCredentialControl);
  301. HWND hwndPlaceholder = Win::GetDlgItem(hwnd, IDC_CRED_PLACEHOLDER);
  302. // Idea: Destroy the existing cred control, create a new one in the
  303. // same place as the placeholder
  304. RECT placeholderRect;
  305. Win::GetWindowRect(hwndPlaceholder, placeholderRect);
  306. // Don't use ScreenToClient: it's not BiDi-smart.
  307. // Win::ScreenToClient(hwnd, placeholderRect);
  308. // NTRAID#NTBUG9-524054-2003/01/20-sburns
  309. ::MapWindowPoints(HWND_DESKTOP, hwnd, (LPPOINT) &placeholderRect, 2);
  310. if (hwndCred)
  311. {
  312. Win::DestroyWindow(hwndCred);
  313. hwndCred = 0;
  314. }
  315. Win::CreateWindowEx(
  316. 0,
  317. L"SysCredential",
  318. L"",
  319. WS_CHILD | WS_VISIBLE | WS_TABSTOP | 0x30, // 0x50010030,
  320. placeholderRect.left,
  321. placeholderRect.top,
  322. placeholderRect.right - placeholderRect.left,
  323. placeholderRect.bottom - placeholderRect.top,
  324. hwnd,
  325. (HMENU) IDC_CRED,
  326. 0,
  327. hwndCred);
  328. Win::SetWindowPos(
  329. hwndCred,
  330. HWND_TOP,
  331. 0,0,0,0,
  332. SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING);
  333. DWORD flags = CRS_NORMAL | CRS_USERNAMES;
  334. if (AreSmartCardsAllowed())
  335. {
  336. flags |= CRS_SMARTCARDS;
  337. }
  338. Credential_InitStyle(hwndCred, flags);
  339. Credential_SetUserNameMaxChars(hwndCred, DS::MAX_USER_NAME_LENGTH);
  340. Credential_SetPasswordMaxChars(hwndCred, DS::MAX_PASSWORD_LENGTH);
  341. }
  342. bool
  343. CredentialsPage::OnSetActive()
  344. {
  345. LOG_FUNCTION(CredentialsPage::OnSetActive);
  346. Win::WaitCursor cursor;
  347. CreateCredentialControl();
  348. State& state = State::GetInstance();
  349. Wizard& wiz = GetWizard();
  350. if (ShouldSkipPage())
  351. {
  352. LOG(L"skipping CredentialsPage");
  353. if (wiz.IsBacktracking())
  354. {
  355. // backup once again
  356. wiz.Backtrack(hwnd);
  357. }
  358. else
  359. {
  360. wiz.SetNextPageID(hwnd, DetermineNextPage());
  361. }
  362. return true;
  363. }
  364. if (!readAnswerfile && state.UsingAnswerFile())
  365. {
  366. CredUi::SetUsername(
  367. hwndCred,
  368. state.GetAnswerFileOption(AnswerFile::OPTION_USERNAME));
  369. CredUi::SetPassword(
  370. hwndCred,
  371. state.GetEncryptedAnswerFileOption(AnswerFile::OPTION_PASSWORD));
  372. String domain =
  373. state.GetAnswerFileOption(AnswerFile::OPTION_USER_DOMAIN);
  374. if (domain.empty())
  375. {
  376. domain = DefaultUserDomainName();
  377. }
  378. Win::SetDlgItemText(
  379. hwnd,
  380. IDC_DOMAIN,
  381. domain);
  382. readAnswerfile = true;
  383. }
  384. else
  385. {
  386. // use the credentials last entered (for browsing, etc.)
  387. // Only set the state of the control if we're not allowing smartcards.
  388. // If we set the username, and the name actually corresponds to a
  389. // smartcard cert, then setting the name will cause the control to
  390. // attempt to match that name to a cert on the card in the reader. That
  391. // causes the control to appear frozen for awhile. Instead of setting
  392. // the username, the user will have to re-select the card or re-type
  393. // the username.
  394. // NTRAID#NTBUG9-499120-2001/11/28-sburns
  395. if (!AreSmartCardsAllowed())
  396. {
  397. CredUi::SetUsername(hwndCred, state.GetUsername());
  398. CredUi::SetPassword(hwndCred, state.GetPassword());
  399. }
  400. Win::SetDlgItemText(hwnd, IDC_DOMAIN, state.GetUserDomainName());
  401. }
  402. if (state.RunHiddenUnattended())
  403. {
  404. int nextPage = CredentialsPage::Validate();
  405. if (nextPage != -1)
  406. {
  407. wiz.SetNextPageID(hwnd, nextPage);
  408. return true;
  409. }
  410. else
  411. {
  412. state.ClearHiddenWhileUnattended();
  413. }
  414. }
  415. Win::PropSheet_SetWizButtons(
  416. Win::GetParent(hwnd),
  417. PSWIZB_BACK);
  418. // cause the button state to be re-evaluated on page activation
  419. // NTRAID#NTBUG9-509806-2002/01/03-sburns
  420. lastWizardButtonsState = 0;
  421. Enable();
  422. Win::SetDlgItemText(hwnd, IDC_MESSAGE, GetMessage());
  423. if (Win::GetTrimmedDlgItemText(hwnd, IDC_DOMAIN).empty())
  424. {
  425. // supply a default domain if none already present
  426. Win::SetDlgItemText(hwnd, IDC_DOMAIN, DefaultUserDomainName());
  427. }
  428. return true;
  429. }
  430. int
  431. CredentialsPage::Validate()
  432. {
  433. LOG_FUNCTION(CredentialsPage::Validate);
  434. int nextPage = -1;
  435. do
  436. {
  437. if (
  438. !WasChanged(IDC_CRED)
  439. && !WasChanged(IDC_DOMAIN))
  440. {
  441. // nothing changed => nothing to validate
  442. nextPage = DetermineNextPage();
  443. break;
  444. }
  445. State& state = State::GetInstance();
  446. String username = CredUi::GetUsername(hwndCred);
  447. if (username.empty())
  448. {
  449. popup.Gripe(hwnd, IDC_CRED, IDS_MUST_ENTER_USERNAME);
  450. break;
  451. }
  452. String domain = Win::GetTrimmedDlgItemText(hwnd, IDC_DOMAIN);
  453. if (domain.empty())
  454. {
  455. popup.Gripe(hwnd, IDC_DOMAIN, IDS_MUST_ENTER_USER_DOMAIN);
  456. break;
  457. }
  458. Win::WaitCursor cursor;
  459. // the domain must be an NT 5 domain: no user of a downlevel domain
  460. // could perform operations in an NT 5 forest. We get the forest name
  461. // of the domain ('cause that may be useful for the new tree scenario)
  462. // as a means of validating the domain name. If the domain does not
  463. // exist, or is not an NT5 domain, then this call will fail.
  464. String forest = GetForestName(domain);
  465. if (forest.empty())
  466. {
  467. ShowDcNotFoundErrorDialog(
  468. hwnd,
  469. IDC_DOMAIN,
  470. domain,
  471. String::load(IDS_WIZARD_TITLE),
  472. String::format(IDS_DC_NOT_FOUND, domain.c_str()),
  473. false);
  474. break;
  475. }
  476. if (state.GetOperation() == State::TREE)
  477. {
  478. // For the new tree case, we need to validate the forest name (a dns
  479. // domain name) by ensuring that we can find a writable DS DC in that
  480. // domain. The user may have supplied a netbios domain name, and it
  481. // is possible that the domain's DNS registration is broken. Since
  482. // we will use the forest name as the parent domain name in the call
  483. // to DsRoleDcAsDc, we need to make sure we can find DCs with that
  484. // name. 122886
  485. DOMAIN_CONTROLLER_INFO* info = 0;
  486. HRESULT hr =
  487. MyDsGetDcName(
  488. 0,
  489. forest,
  490. // force discovery to ensure that we don't pick up a cached
  491. // entry for a domain that may no longer exist, writeable
  492. // and DS because we happen to know that's what the
  493. // DsRoleDcAsDc API will require.
  494. DS_FORCE_REDISCOVERY
  495. | DS_WRITABLE_REQUIRED
  496. | DS_DIRECTORY_SERVICE_REQUIRED,
  497. info);
  498. if (FAILED(hr) || !info)
  499. {
  500. ShowDcNotFoundErrorDialog(
  501. hwnd,
  502. IDC_DOMAIN,
  503. forest,
  504. String::load(IDS_WIZARD_TITLE),
  505. String::format(IDS_DC_FOR_ROOT_NOT_FOUND, forest.c_str()),
  506. // we know the name can't be netbios: forest names are always
  507. // DNS names
  508. true);
  509. break;
  510. }
  511. ::NetApiBufferFree(info);
  512. }
  513. state.SetUserForestName(forest);
  514. // set these now so we can read the domain topology
  515. state.SetUsername(username);
  516. state.SetPassword(CredUi::GetPassword(hwndCred));
  517. state.SetUserDomainName(domain);
  518. // cache the domain topology: this is used to validate new tree,
  519. // child, and replica domain names in later pages. It's also a
  520. // pretty good validation of the credentials.
  521. HRESULT hr = state.ReadDomains();
  522. if (FAILED(hr))
  523. {
  524. if ( hr == Win32ToHresult(ERROR_NO_SUCH_DOMAIN)
  525. || hr == Win32ToHresult(ERROR_DOMAIN_CONTROLLER_NOT_FOUND))
  526. {
  527. // this could happen, I suppose, but it seems very unlikely
  528. // since ReadDomains calls DsGetDcName in the same fashion that
  529. // all the preceeding calls do, and would catch this problem.
  530. ShowDcNotFoundErrorDialog(
  531. hwnd,
  532. IDC_DOMAIN,
  533. domain,
  534. String::load(IDS_WIZARD_TITLE),
  535. String::format(IDS_DC_NOT_FOUND, domain.c_str()),
  536. false);
  537. break;
  538. }
  539. else if (hr == Win32ToHresult(RPC_S_SERVER_UNAVAILABLE))
  540. {
  541. // One way to hit this is change the IP address(es) of the DC(s),
  542. // but not update their DNS registrations. Thus, DNS points to the
  543. // wrong address. I would have thought that DsGetDcName would
  544. // account for that, but whatever....
  545. // NTRAID#NTBUG9-494232-2001/11/21-sburns
  546. popup.Gripe(
  547. hwnd,
  548. IDC_DOMAIN,
  549. hr,
  550. String::format(IDS_UNABLE_TO_READ_FOREST_WITH_LINK));
  551. break;
  552. }
  553. popup.Gripe(
  554. hwnd,
  555. IDC_DOMAIN,
  556. hr,
  557. String::format(IDS_UNABLE_TO_READ_FOREST));
  558. break;
  559. }
  560. // valid
  561. ClearChanges();
  562. nextPage = DetermineNextPage();
  563. }
  564. while (0);
  565. return nextPage;
  566. }