Source code of Windows XP (NT5)
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.

572 lines
12 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. {
  23. LOG_CTOR(CredentialsPage);
  24. CredUIInitControls();
  25. }
  26. CredentialsPage::~CredentialsPage()
  27. {
  28. LOG_DTOR(CredentialsPage);
  29. }
  30. void
  31. CredentialsPage::OnInit()
  32. {
  33. LOG_FUNCTION(CredentialsPage::OnInit);
  34. HWND hwndCred = Win::GetDlgItem(hwnd, IDC_CRED);
  35. Credential_SetUserNameMaxChars(hwndCred, DS::MAX_USER_NAME_LENGTH);
  36. Credential_SetPasswordMaxChars(hwndCred, DS::MAX_PASSWORD_LENGTH);
  37. // Only use the smartcard flag when the machine is joined to a domain. On a
  38. // standalone machine, the smartcard won't have access to any domain
  39. // authority to authenticate it.
  40. // NTRAID#NTBUG9-287538-2001/01/23-sburns
  41. State& state = State::GetInstance();
  42. Computer& computer = state.GetComputer();
  43. DWORD flags = CRS_NORMAL | CRS_USERNAMES;
  44. if (
  45. computer.IsJoinedToDomain()
  46. // can only use smartcards on replica promotions
  47. // NTRAID#NTBUG9-311150-2001/02/19-sburns
  48. && state.GetOperation() == State::REPLICA)
  49. {
  50. flags |= CRS_SMARTCARDS;
  51. }
  52. Credential_InitStyle(hwndCred, flags);
  53. Win::Edit_LimitText(
  54. Win::GetDlgItem(hwnd, IDC_DOMAIN),
  55. Dns::MAX_NAME_LENGTH);
  56. }
  57. void
  58. CredentialsPage::Enable()
  59. {
  60. // LOG_FUNCTION(CredentialsPage::Enable);
  61. int next =
  62. ( !CredUi::GetUsername(Win::GetDlgItem(hwnd, IDC_CRED)).empty()
  63. && !Win::GetTrimmedDlgItemText(hwnd, IDC_DOMAIN).empty() )
  64. ? PSWIZB_NEXT : 0;
  65. Win::PropSheet_SetWizButtons(
  66. Win::GetParent(hwnd),
  67. PSWIZB_BACK | next);
  68. }
  69. bool
  70. CredentialsPage::OnCommand(
  71. HWND /* windowFrom */ ,
  72. unsigned controlIDFrom,
  73. unsigned code)
  74. {
  75. // LOG_FUNCTION(CredentialsPage::OnCommand);
  76. switch (controlIDFrom)
  77. {
  78. case IDC_CRED:
  79. {
  80. if (code == CRN_USERNAMECHANGE)
  81. {
  82. SetChanged(controlIDFrom);
  83. Enable();
  84. return true;
  85. }
  86. break;
  87. }
  88. case IDC_DOMAIN:
  89. {
  90. if (code == EN_CHANGE)
  91. {
  92. SetChanged(controlIDFrom);
  93. Enable();
  94. return true;
  95. }
  96. break;
  97. }
  98. default:
  99. {
  100. // do nothing
  101. break;
  102. }
  103. }
  104. return false;
  105. }
  106. bool
  107. CredentialsPage::ShouldSkipPage()
  108. {
  109. LOG_FUNCTION(CredentialsPage::ShouldSkipPage);
  110. State& state = State::GetInstance();
  111. State::Operation oper = state.GetOperation();
  112. bool result = false;
  113. switch (oper)
  114. {
  115. case State::FOREST:
  116. {
  117. // never need credentials for new forest.
  118. result = true;
  119. break;
  120. }
  121. case State::DEMOTE:
  122. {
  123. // The demote page should circumvent this page if necessary, so if
  124. // we make it here, don't skip the page.
  125. break;
  126. }
  127. case State::ABORT_BDC_UPGRADE:
  128. case State::REPLICA:
  129. case State::TREE:
  130. case State::CHILD:
  131. {
  132. break;
  133. }
  134. case State::NONE:
  135. default:
  136. {
  137. ASSERT(false);
  138. break;
  139. }
  140. }
  141. return result;
  142. }
  143. int
  144. CredentialsPage::DetermineNextPage()
  145. {
  146. LOG_FUNCTION(CredentialsPage::DetermineNextPage);
  147. State& state = State::GetInstance();
  148. int id = IDD_PATHS;
  149. switch (state.GetOperation())
  150. {
  151. case State::DEMOTE:
  152. case State::ABORT_BDC_UPGRADE:
  153. {
  154. id = IDD_ADMIN_PASSWORD;
  155. break;
  156. }
  157. case State::FOREST:
  158. {
  159. id = IDD_NEW_FOREST;
  160. break;
  161. }
  162. case State::REPLICA:
  163. {
  164. if (state.GetRunContext() == State::BDC_UPGRADE)
  165. {
  166. id = IDD_PATHS;
  167. }
  168. else
  169. {
  170. id = IDD_REPLICA;
  171. }
  172. break;
  173. }
  174. case State::TREE:
  175. {
  176. id = IDD_NEW_TREE;
  177. break;
  178. }
  179. case State::CHILD:
  180. {
  181. id = IDD_NEW_CHILD;
  182. break;
  183. }
  184. case State::NONE:
  185. default:
  186. {
  187. ASSERT(false);
  188. break;
  189. }
  190. }
  191. return id;
  192. }
  193. String
  194. GetMessage()
  195. {
  196. String message;
  197. State& state = State::GetInstance();
  198. switch (state.GetOperation())
  199. {
  200. case State::ABORT_BDC_UPGRADE:
  201. {
  202. message = String::load(IDS_ABORT_BDC_UPGRADE_CREDENTIALS);
  203. break;
  204. }
  205. case State::TREE:
  206. case State::CHILD:
  207. case State::REPLICA:
  208. {
  209. message = String::load(IDS_PROMOTION_CREDENTIALS);
  210. break;
  211. }
  212. case State::DEMOTE:
  213. {
  214. // 318736 demote requires enterprise admin credentials -- for
  215. // root and child domains alike.
  216. message =
  217. String::format(
  218. IDS_ROOT_DOMAIN_CREDENTIALS,
  219. state.GetComputer().GetForestDnsName().c_str());
  220. break;
  221. }
  222. case State::FOREST:
  223. {
  224. // do nothing, the page will be skipped.
  225. break;
  226. }
  227. case State::NONE:
  228. default:
  229. {
  230. ASSERT(false);
  231. break;
  232. }
  233. }
  234. return message;
  235. }
  236. String
  237. DefaultUserDomainName()
  238. {
  239. String d;
  240. State& state = State::GetInstance();
  241. const Computer& computer = state.GetComputer();
  242. switch (state.GetOperation())
  243. {
  244. case State::ABORT_BDC_UPGRADE:
  245. case State::FOREST:
  246. {
  247. // do nothing
  248. break;
  249. }
  250. case State::DEMOTE: // 301361
  251. case State::TREE:
  252. {
  253. d = computer.GetForestDnsName();
  254. break;
  255. }
  256. case State::CHILD:
  257. {
  258. d = computer.GetDomainDnsName();
  259. break;
  260. }
  261. case State::REPLICA:
  262. {
  263. d = computer.GetDomainDnsName();
  264. if (d.empty() && state.ReplicateFromMedia())
  265. {
  266. d = state.GetReplicaDomainDNSName();
  267. }
  268. break;
  269. }
  270. case State::NONE:
  271. default:
  272. {
  273. ASSERT(false);
  274. break;
  275. }
  276. }
  277. return d;
  278. }
  279. bool
  280. CredentialsPage::OnSetActive()
  281. {
  282. LOG_FUNCTION(CredentialsPage::OnSetActive);
  283. Win::WaitCursor cursor;
  284. State& state = State::GetInstance();
  285. Wizard& wiz = GetWizard();
  286. HWND hwndCred = Win::GetDlgItem(hwnd, IDC_CRED);
  287. if (ShouldSkipPage())
  288. {
  289. LOG(L"skipping CredentialsPage");
  290. if (wiz.IsBacktracking())
  291. {
  292. // backup once again
  293. wiz.Backtrack(hwnd);
  294. }
  295. else
  296. {
  297. wiz.SetNextPageID(hwnd, DetermineNextPage());
  298. }
  299. return true;
  300. }
  301. if (!readAnswerfile && state.UsingAnswerFile())
  302. {
  303. CredUi::SetUsername(
  304. hwndCred,
  305. state.GetAnswerFileOption(State::OPTION_USERNAME));
  306. CredUi::SetPassword(
  307. hwndCred,
  308. state.GetEncodedAnswerFileOption(State::OPTION_PASSWORD));
  309. String domain = state.GetAnswerFileOption(State::OPTION_USER_DOMAIN);
  310. if (domain.empty())
  311. {
  312. domain = DefaultUserDomainName();
  313. }
  314. Win::SetDlgItemText(
  315. hwnd,
  316. IDC_DOMAIN,
  317. domain);
  318. readAnswerfile = true;
  319. }
  320. if (state.RunHiddenUnattended())
  321. {
  322. int nextPage = CredentialsPage::Validate();
  323. if (nextPage != -1)
  324. {
  325. wiz.SetNextPageID(hwnd, nextPage);
  326. return true;
  327. }
  328. else
  329. {
  330. state.ClearHiddenWhileUnattended();
  331. }
  332. }
  333. // use the credentials last entered (for browsing, etc.)
  334. CredUi::SetUsername(hwndCred, state.GetUsername());
  335. CredUi::SetPassword(hwndCred, state.GetPassword());
  336. Win::SetDlgItemText(hwnd, IDC_DOMAIN, state.GetUserDomainName());
  337. Win::PropSheet_SetWizButtons(
  338. Win::GetParent(hwnd),
  339. PSWIZB_BACK);
  340. Enable();
  341. Win::SetDlgItemText(hwnd, IDC_MESSAGE, GetMessage());
  342. if (Win::GetTrimmedDlgItemText(hwnd, IDC_DOMAIN).empty())
  343. {
  344. // supply a default domain if none already present
  345. Win::SetDlgItemText(hwnd, IDC_DOMAIN, DefaultUserDomainName());
  346. }
  347. return true;
  348. }
  349. int
  350. CredentialsPage::Validate()
  351. {
  352. LOG_FUNCTION(CredentialsPage::Validate);
  353. int nextPage = -1;
  354. do
  355. {
  356. if (
  357. !WasChanged(IDC_CRED)
  358. && !WasChanged(IDC_DOMAIN))
  359. {
  360. // nothing changed => nothing to validate
  361. nextPage = DetermineNextPage();
  362. break;
  363. }
  364. State& state = State::GetInstance();
  365. HWND hwndCred = Win::GetDlgItem(hwnd, IDC_CRED);
  366. String username = CredUi::GetUsername(hwndCred);
  367. if (username.empty())
  368. {
  369. popup.Gripe(hwnd, IDC_CRED, IDS_MUST_ENTER_USERNAME);
  370. break;
  371. }
  372. String domain = Win::GetTrimmedDlgItemText(hwnd, IDC_DOMAIN);
  373. if (domain.empty())
  374. {
  375. popup.Gripe(hwnd, IDC_DOMAIN, IDS_MUST_ENTER_USER_DOMAIN);
  376. break;
  377. }
  378. Win::WaitCursor cursor;
  379. // the domain must be an NT 5 domain: no user of a downlevel domain
  380. // could perform operations in an NT 5 forest. We get the forest name
  381. // of the domain ('cause that may be useful for the new tree scenario)
  382. // as a means of validating the domain name. If the domain does not
  383. // exist, or is not an NT5 domain, then this call will fail.
  384. String forest = GetForestName(domain);
  385. if (forest.empty())
  386. {
  387. ShowDcNotFoundErrorDialog(
  388. hwnd,
  389. IDC_DOMAIN,
  390. domain,
  391. String::load(IDS_WIZARD_TITLE),
  392. String::format(IDS_DC_NOT_FOUND, domain.c_str()),
  393. false);
  394. break;
  395. }
  396. if (state.GetOperation() == State::TREE)
  397. {
  398. // For the new tree case, we need to validate the forest name (a dns
  399. // domain name) by ensuring that we can find a writable DS DC in that
  400. // domain. The user may have supplied a netbios domain name, and it
  401. // is possible that the domain's DNS registration is broken. Since
  402. // we will use the forest name as the parent domain name in the call
  403. // to DsRoleDcAsDc, we need to make sure we can find DCs with that
  404. // name. 122886
  405. DOMAIN_CONTROLLER_INFO* info = 0;
  406. HRESULT hr =
  407. MyDsGetDcName(
  408. 0,
  409. forest,
  410. // force discovery to ensure that we don't pick up a cached
  411. // entry for a domain that may no longer exist, writeable
  412. // and DS because we happen to know that's what the
  413. // DsRoleDcAsDc API will require.
  414. DS_FORCE_REDISCOVERY
  415. | DS_WRITABLE_REQUIRED
  416. | DS_DIRECTORY_SERVICE_REQUIRED,
  417. info);
  418. if (FAILED(hr) || !info)
  419. {
  420. ShowDcNotFoundErrorDialog(
  421. hwnd,
  422. IDC_DOMAIN,
  423. forest,
  424. String::load(IDS_WIZARD_TITLE),
  425. String::format(IDS_DC_FOR_ROOT_NOT_FOUND, forest.c_str()),
  426. // we know the name can't be netbios: forest names are always
  427. // DNS names
  428. true);
  429. break;
  430. }
  431. ::NetApiBufferFree(info);
  432. }
  433. state.SetUserForestName(forest);
  434. // set these now so we can read the domain topology
  435. state.SetUsername(username);
  436. state.SetPassword(CredUi::GetPassword(hwndCred));
  437. state.SetUserDomainName(domain);
  438. // cache the domain topology: this is used to validate new tree,
  439. // child, and replica domain names in later pages. It's also a
  440. // pretty good validation of the credentials.
  441. HRESULT hr = state.ReadDomains();
  442. if (FAILED(hr))
  443. {
  444. popup.Gripe(
  445. hwnd,
  446. IDC_DOMAIN,
  447. hr,
  448. String::format(IDS_UNABLE_TO_READ_FOREST));
  449. break;
  450. }
  451. // valid
  452. ClearChanges();
  453. nextPage = DetermineNextPage();
  454. }
  455. while (0);
  456. return nextPage;
  457. }