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.

2409 lines
73 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name: chngpwd.c
  3. *
  4. * Copyright (c) 1991, Microsoft Corporation
  5. *
  6. * Implementation of change-password functionality of winlogon
  7. *
  8. * History:
  9. * 12-09-91 Davidc Created.
  10. \***************************************************************************/
  11. #include "msgina.h"
  12. #include <stdio.h>
  13. #include <wchar.h>
  14. #include <align.h>
  15. #include <keymgr.h>
  16. #include <netlib.h>
  17. typedef void (WINAPI *RUNDLLPROC)(HWND hWndStub,HINSTANCE hInstance,LPWSTR szCommandLine,int nShow);
  18. // #define VERBOSE_UTILS
  19. #ifdef VERBOSE_UTILS
  20. #define VerbosePrint(s) WLPrint(s)
  21. #else
  22. #define VerbosePrint(s)
  23. #endif
  24. //
  25. // Define the structure used to pass data into the change password dialog
  26. //
  27. typedef struct {
  28. PGLOBALS pGlobals;
  29. PWCHAR UserName;
  30. PWCHAR Domain;
  31. PWCHAR OldPassword;
  32. ULONG Options ;
  33. BOOL Impersonate;
  34. BOOL AllowProviderOnly;
  35. WCHAR UserNameBuffer[MAX_STRING_BYTES];
  36. } CHANGE_PASSWORD_DATA;
  37. typedef CHANGE_PASSWORD_DATA *PCHANGE_PASSWORD_DATA;
  38. typedef
  39. NTSTATUS
  40. (WINAPI * GINA_CHANGEPW_FUNC)(
  41. PCHANGE_PASSWORD_DATA pChangePasswordData,
  42. PWSTR UserName,
  43. PWSTR Domain,
  44. PWSTR OldPassword,
  45. PWSTR NewPassword,
  46. PNTSTATUS SubStatus,
  47. DOMAIN_PASSWORD_INFORMATION * DomainInfo
  48. );
  49. //
  50. // Private prototypes
  51. //
  52. NTSTATUS
  53. ProviderChangePassword(
  54. PCHANGE_PASSWORD_DATA pChangePasswordData,
  55. PWSTR UserName,
  56. PWSTR Domain,
  57. PWSTR OldPassword,
  58. PWSTR NewPassword,
  59. PNTSTATUS SubStatus,
  60. DOMAIN_PASSWORD_INFORMATION * DomainInfo
  61. );
  62. NTSTATUS
  63. MitChangePassword(
  64. PCHANGE_PASSWORD_DATA pChangePasswordData,
  65. PWSTR UserName,
  66. PWSTR Domain,
  67. PWSTR OldPassword,
  68. PWSTR NewPassword,
  69. PNTSTATUS SubStatus,
  70. DOMAIN_PASSWORD_INFORMATION * DomainInfo
  71. );
  72. NTSTATUS
  73. NtChangePassword(
  74. PCHANGE_PASSWORD_DATA pChangePasswordData,
  75. PWSTR UserName,
  76. PWSTR Domain,
  77. PWSTR OldPassword,
  78. PWSTR NewPassword,
  79. PNTSTATUS SubStatus,
  80. DOMAIN_PASSWORD_INFORMATION * DomainInfo
  81. );
  82. INT_PTR WINAPI ChangePasswordDlgProc(HWND, UINT, WPARAM, LPARAM);
  83. BOOL ChangePasswordDlgInit(HWND, LPARAM);
  84. INT_PTR AttemptPasswordChange(HWND);
  85. BOOL IsAutologonUser(LPCTSTR szUser, LPCTSTR szDomain);
  86. NTSTATUS SetAutologonPassword(LPCTSTR szPassword);
  87. INT_PTR
  88. HandleFailedChangePassword(
  89. HWND hDlg,
  90. PGLOBALS pGlobals,
  91. NTSTATUS Status,
  92. PWCHAR UserName,
  93. PWCHAR Domain,
  94. NTSTATUS SubStatus,
  95. DOMAIN_PASSWORD_INFORMATION * DomainInfo
  96. );
  97. //
  98. // This table corresponds to the DOMAIN_ENTRY_TYPE from domain.h
  99. //
  100. GINA_CHANGEPW_FUNC
  101. ChangePasswordWorkers[] = {
  102. NULL, // DomainInvalid
  103. NtChangePassword, // DomainUPN
  104. NtChangePassword, // DomainMachine
  105. NtChangePassword, // DomainNt4
  106. NtChangePassword, // DomainNt5
  107. MitChangePassword, // DomainMitRealm
  108. MitChangePassword, // DomainMitUntrusted
  109. ProviderChangePassword // DomainNetworkProvider
  110. };
  111. // Control arrays for dynamically dorking with the dialog
  112. static UINT ctrlNoDomain[] =
  113. {
  114. IDD_CHANGEPWD_OLD_LABEL,
  115. IDD_CHANGEPWD_OLD,
  116. IDD_CHANGEPWD_NEW_LABEL,
  117. IDD_CHANGEPWD_NEW,
  118. IDD_CHANGEPWD_CONFIRM_LABEL,
  119. IDD_CHANGEPWD_CONFIRM,
  120. IDD_KBLAYOUT_ICON,
  121. IDC_BACKUP,
  122. IDOK,
  123. IDCANCEL
  124. };
  125. // Do not show the [Backup] button on the msgina dialog if:
  126. //
  127. // 1. The default domain is not the local machine
  128. // 2. Over a terminal server session
  129. // 3. The user name is a UPN name (domain combo box also disabled but not by this fn)
  130. //
  131. BOOL ShowBackupButton(HWND hDlg, PGLOBALS pGlobals)
  132. {
  133. INT_PTR iItem;
  134. LPARAM lp;
  135. int cchBuffer;
  136. TCHAR* pszLogonName = NULL;
  137. HWND hwU = GetDlgItem(hDlg,IDD_CHANGEPWD_NAME);
  138. HWND hwD = GetDlgItem(hDlg,IDD_CHANGEPWD_DOMAIN);
  139. HWND hwB = GetDlgItem(hDlg,IDC_BACKUP);
  140. BOOL fEnable = TRUE;
  141. cchBuffer = (int)SendMessage(hwU, WM_GETTEXTLENGTH, 0, 0) + 1;
  142. pszLogonName = (TCHAR*) Alloc(cchBuffer * sizeof(TCHAR));
  143. if (pszLogonName != NULL)
  144. {
  145. SendMessage(hwU, WM_GETTEXT, (WPARAM) cchBuffer, (LPARAM) pszLogonName);
  146. // turn off the button if the user is using a
  147. // UPN (if there is a "@") - ie [email protected] OR
  148. // domain\username
  149. fEnable = (NULL == wcspbrk(pszLogonName, TEXT("@\\")));
  150. Free(pszLogonName);
  151. }
  152. if (fEnable)
  153. {
  154. // turn off button if is remote session
  155. fEnable = (0 == GetSystemMetrics(SM_REMOTESESSION));
  156. }
  157. if (fEnable)
  158. {
  159. // turn off button if selected domain is not local machine
  160. if (hwD)
  161. {
  162. iItem = SendMessage(hwD,CB_GETCURSEL,0,0);
  163. if (LB_ERR != iItem)
  164. {
  165. // now window active and something selected
  166. fEnable = FALSE;
  167. lp = SendMessage(hwD, CB_GETITEMDATA,iItem,0);
  168. if ((LB_ERR != lp) && (0 != lp))
  169. {
  170. if (DomainMachine == ((PDOMAIN_CACHE_ENTRY)lp)->Type)
  171. {
  172. fEnable = TRUE;
  173. }
  174. }
  175. }
  176. }
  177. }
  178. //EnableWindow(hwB,fEnable);
  179. if (fEnable) ShowWindow(hwB,SW_SHOWNORMAL);
  180. else ShowWindow(hwB,SW_HIDE);
  181. return fEnable;
  182. }
  183. BOOL
  184. NetworkProvidersPresent(
  185. VOID
  186. )
  187. {
  188. HKEY ProviderKey;
  189. DWORD Error;
  190. DWORD ValueType;
  191. LPTSTR Value;
  192. BOOL NeedToNotify = TRUE;
  193. #define NET_PROVIDER_ORDER_KEY TEXT("system\\CurrentControlSet\\Control\\NetworkProvider\\Order")
  194. #define NET_PROVIDER_ORDER_VALUE TEXT("ProviderOrder")
  195. #define NET_ORDER_SEPARATOR TEXT(',')
  196. Error = RegOpenKeyEx(
  197. HKEY_LOCAL_MACHINE, // hKey
  198. NET_PROVIDER_ORDER_KEY, // lpSubKey
  199. 0, // Must be 0
  200. KEY_QUERY_VALUE, // Desired access
  201. &ProviderKey // Newly Opened Key Handle
  202. );
  203. if (Error == ERROR_SUCCESS) {
  204. Value = AllocAndRegQueryValueEx(
  205. ProviderKey, // Key
  206. NET_PROVIDER_ORDER_VALUE,// Value name
  207. NULL, // Must be NULL
  208. &ValueType // Type returned here
  209. );
  210. if (Value != NULL) {
  211. if (ValueType == REG_SZ) {
  212. LPTSTR p = Value;
  213. while (*p) {
  214. if (*p == NET_ORDER_SEPARATOR) {
  215. break;
  216. }
  217. p = CharNext(p);
  218. }
  219. if (*p == 0) {
  220. //
  221. // We got to the end without finding a separator
  222. // Only one provider is installed.
  223. //
  224. #pragma prefast(suppress: 400, "PREfast noise: lstrcmpi")
  225. if (lstrcmpi(Value, SERVICE_WORKSTATION) == 0) {
  226. //
  227. // it's Lanman, don't notify
  228. //
  229. NeedToNotify = FALSE;
  230. } else {
  231. //
  232. // it isn't Lanman, notify
  233. //
  234. NeedToNotify = TRUE;
  235. }
  236. }
  237. } else {
  238. DebugLog((DEB_ERROR, "NoNeedToNotify - provider order key unexpected type: %d, assuming notification is necessary", ValueType));
  239. }
  240. Free(Value);
  241. } else {
  242. DebugLog((DEB_ERROR, "NoNeedToNotify - failed to query provider order value, assuming notification is necessary\n"));
  243. }
  244. Error = RegCloseKey(ProviderKey);
  245. ASSERT(Error == ERROR_SUCCESS);
  246. }
  247. return NeedToNotify ;
  248. }
  249. BOOL
  250. ShowDomain(
  251. VOID
  252. )
  253. {
  254. return (SafeBootMode != SAFEBOOT_MINIMAL);
  255. }
  256. /***************************************************************************\
  257. * FUNCTION: ChangePassword
  258. *
  259. * PURPOSE: Attempts to change a user's password
  260. *
  261. * ARGUMENTS:
  262. *
  263. * hwnd - the most recent parent window
  264. * pGlobals - pointer to global data for this instance.
  265. * The password information of this data will be
  266. * updated upon successful change of the primary
  267. * authenticator's password information.
  268. * UserName - the name of the user to change
  269. * Domain - the domain name to change the password on
  270. * AnyDomain - if TRUE the user may select any trusted domain, or
  271. * enter the name of any other domain
  272. *
  273. * RETURNS:
  274. *
  275. * MSGINA_DLG_SUCCESS - the password was changed successfully.
  276. * MSGINA_DLG_FAILURE - the user's password could not be changed.
  277. * DLG_INTERRUPTED() - this is a set of possible interruptions (see winlogon.h)
  278. *
  279. * HISTORY:
  280. *
  281. * 12-09-91 Davidc Created.
  282. *
  283. \***************************************************************************/
  284. INT_PTR
  285. ChangePassword(
  286. HWND hwnd,
  287. PGLOBALS pGlobals,
  288. PWCHAR UserName,
  289. PWCHAR Domain,
  290. ULONG Options
  291. )
  292. {
  293. CHANGE_PASSWORD_DATA PasswordData;
  294. INT_PTR Result;
  295. HWND hwndOldFocus = GetFocus();
  296. ULONG LocalOptions = 0 ;
  297. PasswordData.pGlobals = pGlobals;
  298. PasswordData.UserName = UserName;
  299. PasswordData.Domain = Domain;
  300. PasswordData.OldPassword = NULL;
  301. PasswordData.Impersonate = TRUE;
  302. PasswordData.AllowProviderOnly = TRUE;
  303. if ( NetworkProvidersPresent() )
  304. {
  305. LocalOptions |= CHANGEPWD_OPTION_SHOW_NETPROV |
  306. CHANGEPWD_OPTION_SHOW_DOMAIN ;
  307. }
  308. if ( ShowDomain() )
  309. {
  310. LocalOptions |= CHANGEPWD_OPTION_EDIT_DOMAIN |
  311. CHANGEPWD_OPTION_SHOW_DOMAIN ;
  312. }
  313. if ( SafeBootMode == SAFEBOOT_MINIMAL )
  314. {
  315. LocalOptions = 0 ;
  316. }
  317. PasswordData.Options = (Options & LocalOptions);
  318. pWlxFuncs->WlxSetTimeout(pGlobals->hGlobalWlx, LOGON_TIMEOUT);
  319. Result = pWlxFuncs->WlxDialogBoxParam( pGlobals->hGlobalWlx,
  320. hDllInstance,
  321. MAKEINTRESOURCE(IDD_CHANGEPWD_DIALOG),
  322. hwnd,
  323. ChangePasswordDlgProc,
  324. (LPARAM)&PasswordData);
  325. SetFocus(hwndOldFocus);
  326. return(Result);
  327. }
  328. /***************************************************************************\
  329. * FUNCTION: ChangePasswordLogon
  330. *
  331. * PURPOSE: Attempts to change a user's password during the logon process.
  332. * This is the same as a normal change password except that the user
  333. * does not have to enter the old password and can only change the
  334. * password in the specified domain. This routine is intended to be
  335. * called during logon when it is discovered that the user's
  336. * password has expired.
  337. *
  338. * ARGUMENTS:
  339. *
  340. * hwnd - the most recent parent window
  341. * pGlobals - pointer to global data for this instance
  342. * UserName - the name of the user to change
  343. * Domain - the domain name to change the password on
  344. * OldPassword - the old user password
  345. * NewPassword - points to a buffer that the new password is written
  346. * into if the password is changed successfully.
  347. * NewPasswordMaxBytes - the size of the newpassword buffer.
  348. *
  349. * RETURNS:
  350. *
  351. * MSGINA_DLG_SUCCESS - the password was changed successfully, NewPassword
  352. * contains the new password text.
  353. * MSGINA_DLG_FAILURE - the user's password could not be changed.
  354. * DLG_INTERRUPTED() - this is a set of possible interruptions (see winlogon.h)
  355. *
  356. * HISTORY:
  357. *
  358. * 12-09-91 Davidc Created.
  359. *
  360. \***************************************************************************/
  361. INT_PTR
  362. ChangePasswordLogon(
  363. HWND hwnd,
  364. PGLOBALS pGlobals,
  365. PWCHAR UserName,
  366. PWCHAR Domain,
  367. PWCHAR OldPassword
  368. )
  369. {
  370. CHANGE_PASSWORD_DATA PasswordData;
  371. INT_PTR Result;
  372. PasswordData.pGlobals = pGlobals;
  373. PasswordData.UserName = UserName;
  374. PasswordData.Domain = Domain;
  375. PasswordData.OldPassword = OldPassword;
  376. PasswordData.Options = CHANGEPWD_OPTION_NO_UPDATE ;
  377. PasswordData.Impersonate = FALSE;
  378. PasswordData.AllowProviderOnly = FALSE;
  379. if ( ShowDomain() )
  380. {
  381. PasswordData.Options |= CHANGEPWD_OPTION_SHOW_DOMAIN |
  382. CHANGEPWD_OPTION_KEEP_ARRAY ;
  383. }
  384. pWlxFuncs->WlxSetTimeout(pGlobals->hGlobalWlx, LOGON_TIMEOUT);
  385. Result = pWlxFuncs->WlxDialogBoxParam( pGlobals->hGlobalWlx,
  386. hDllInstance,
  387. MAKEINTRESOURCE( IDD_CHANGEPWD_DIALOG ),
  388. hwnd,
  389. ChangePasswordDlgProc,
  390. (LPARAM)&PasswordData);
  391. return(Result);
  392. }
  393. /****************************************************************************\
  394. *
  395. * FUNCTION: ChangePasswordDlgProc
  396. *
  397. * PURPOSE: Processes messages for ChangePassword dialog
  398. *
  399. * HISTORY:
  400. *
  401. * 12-09-91 Davidc Created.
  402. *
  403. \****************************************************************************/
  404. INT_PTR WINAPI
  405. ChangePasswordDlgProc(
  406. HWND hDlg,
  407. UINT message,
  408. WPARAM wParam,
  409. LPARAM lParam
  410. )
  411. {
  412. PCHANGE_PASSWORD_DATA pPasswordData = (PCHANGE_PASSWORD_DATA)GetWindowLongPtr(hDlg, GWLP_USERDATA);
  413. PGLOBALS pGlobals;
  414. INT_PTR Result;
  415. switch (message) {
  416. case WM_INITDIALOG:
  417. {
  418. if (!ChangePasswordDlgInit(hDlg, lParam)) {
  419. EndDialog(hDlg, MSGINA_DLG_FAILURE);
  420. }
  421. return(SetPasswordFocus(hDlg));
  422. }
  423. case WM_DESTROY:
  424. pGlobals = pPasswordData->pGlobals ;
  425. if ( pGlobals->ActiveArray &&
  426. ((pPasswordData->Options & CHANGEPWD_OPTION_KEEP_ARRAY) == 0 ) )
  427. {
  428. DCacheFreeArray( pGlobals->ActiveArray );
  429. pGlobals->ActiveArray = NULL ;
  430. }
  431. FreeLayoutInfo(LAYOUT_CUR_USER);
  432. return( TRUE );
  433. case WM_ERASEBKGND:
  434. return PaintBranding(hDlg, (HDC)wParam, 0, FALSE, FALSE, COLOR_BTNFACE);
  435. case WM_QUERYNEWPALETTE:
  436. return BrandingQueryNewPalete(hDlg);
  437. case WM_PALETTECHANGED:
  438. return BrandingPaletteChanged(hDlg, (HWND)wParam);
  439. case WM_SYSCOMMAND:
  440. if ( wParam == SC_CLOSE )
  441. {
  442. EndDialog( hDlg, MSGINA_DLG_FAILURE );
  443. return TRUE ;
  444. }
  445. break;
  446. case WM_COMMAND:
  447. {
  448. if (HIWORD(wParam) == CBN_SELCHANGE)
  449. {
  450. ShowBackupButton(hDlg,pPasswordData->pGlobals);
  451. return TRUE;
  452. }
  453. switch (LOWORD(wParam)) {
  454. case IDD_CHANGEPWD_NAME:
  455. switch (HIWORD(wParam))
  456. {
  457. case EN_CHANGE:
  458. // Ensure the domain box is enabled/disabled correctly
  459. // in case of a UPN name
  460. if ( pPasswordData->Options & CHANGEPWD_OPTION_EDIT_DOMAIN )
  461. {
  462. EnableDomainForUPN((HWND) lParam, GetDlgItem(hDlg,IDD_CHANGEPWD_DOMAIN));
  463. ShowBackupButton(hDlg,pPasswordData->pGlobals);
  464. }
  465. return TRUE;
  466. default:
  467. break;
  468. }
  469. break;
  470. case IDC_BACKUP:
  471. {
  472. BOOL fWrongDomain = TRUE;
  473. PDOMAIN_CACHE_ENTRY Entry;
  474. HWND hwndDomain = GetDlgItem(hDlg,IDD_CHANGEPWD_DOMAIN);
  475. INT iDomainSelection = (INT)SendMessage(hwndDomain,CB_GETCURSEL,0,0);
  476. // Get the user's input. Decide if he has selected other than the local machine
  477. if (pPasswordData->Options & CHANGEPWD_OPTION_EDIT_DOMAIN)
  478. {
  479. // see if selected domain is local machine
  480. Entry = (PDOMAIN_CACHE_ENTRY)SendMessage(hwndDomain,CB_GETITEMDATA,iDomainSelection,0);
  481. // warning.... Entry can turn out to be ffffffff (CB_ERR)
  482. if (CB_ERR == (ULONG_PTR) Entry)
  483. {
  484. fWrongDomain = TRUE;
  485. }
  486. else if (NULL != Entry)
  487. {
  488. if (Entry->Type == DomainMachine)
  489. {
  490. fWrongDomain = FALSE;
  491. }
  492. }
  493. }
  494. else fWrongDomain = FALSE;
  495. // Show UI or message box
  496. if (fWrongDomain)
  497. {
  498. pGlobals = pPasswordData->pGlobals ;
  499. if (NULL == pGlobals) return TRUE;
  500. TimeoutMessageBox(hDlg, pGlobals, IDS_MBMWRONGDOMAIN,
  501. IDS_MBTWRONGDOMAIN,
  502. MB_OK | MB_ICONEXCLAMATION,
  503. TIMEOUT_CURRENT);
  504. return TRUE;
  505. }
  506. else
  507. {
  508. // standalone case
  509. // We use a single export from KEYMGR.DLL for this operation. When this operation completes,
  510. // we don't use the DLL again without unlikely user intervention. We could DELAYLOAD keymgr.dll,
  511. // but explicitly loading and unloading this DLL permits us to minimize the memory footprint of msgina.
  512. RUNDLLPROC fptr;
  513. HMODULE hDll;
  514. //
  515. hDll = LoadLibrary(L"keymgr.dll");
  516. if (hDll)
  517. {
  518. fptr = (RUNDLLPROC) GetProcAddress(hDll,(LPCSTR)"PRShowSaveFromMsginaW");
  519. if (fptr)
  520. {
  521. WCHAR szUser[UNLEN+1];
  522. if (0 != SendMessage(GetDlgItem(hDlg,IDD_CHANGEPWD_NAME),WM_GETTEXT,UNLEN,(LPARAM)szUser))
  523. fptr(hDlg,NULL,szUser,0);
  524. }
  525. FreeLibrary(hDll);
  526. }
  527. return TRUE;
  528. }
  529. // determine if this domain entered is not the local machine
  530. // if not, show a message box and bow out.
  531. }
  532. case IDOK:
  533. {
  534. pGlobals = pPasswordData->pGlobals;
  535. //
  536. // Deal with combo-box UI requirements
  537. //
  538. if (HandleComboBoxOK(hDlg, IDD_CHANGEPWD_DOMAIN)) {
  539. return(TRUE);
  540. }
  541. Result = AttemptPasswordChange(hDlg);
  542. //
  543. // Can't hurt to get the edit controls to forget their contents in
  544. // any case. It used to be done only in the failure case
  545. //
  546. SetDlgItemText(hDlg, IDD_CHANGEPWD_OLD, NULL );
  547. SetDlgItemText(hDlg, IDD_CHANGEPWD_NEW, NULL );
  548. SetDlgItemText(hDlg, IDD_CHANGEPWD_CONFIRM, NULL );
  549. if (Result == MSGINA_DLG_FAILURE) {
  550. //
  551. // Let the user try again
  552. // We always make the user re-enter at least the new password.
  553. //
  554. SetPasswordFocus(hDlg);
  555. //EndDialog(hDlg, Result);
  556. return(TRUE);
  557. }
  558. //
  559. // We're finished - either success or an interrupt
  560. //
  561. EndDialog(hDlg, Result);
  562. return(TRUE);
  563. }
  564. case IDCANCEL:
  565. {
  566. SetDlgItemText(hDlg, IDD_CHANGEPWD_OLD, NULL );
  567. SetDlgItemText(hDlg, IDD_CHANGEPWD_NEW, NULL );
  568. SetDlgItemText(hDlg, IDD_CHANGEPWD_CONFIRM, NULL );
  569. EndDialog(hDlg, MSGINA_DLG_FAILURE);
  570. return(TRUE);
  571. }
  572. break;
  573. }
  574. }
  575. case WLX_WM_SAS:
  576. {
  577. // Ignore it
  578. return(TRUE);
  579. }
  580. case WM_TIMER:
  581. {
  582. if (wParam == TIMER_MYLANGUAGECHECK)
  583. {
  584. LayoutCheckHandler(hDlg, LAYOUT_CUR_USER);
  585. }
  586. break;
  587. }
  588. }
  589. // We didn't process this message
  590. return FALSE;
  591. }
  592. /****************************************************************************\
  593. *
  594. * FUNCTION: ChangePasswordDlgInit
  595. *
  596. * PURPOSE: Handles initialization of change password dialog
  597. *
  598. * RETURNS: TRUE on success, FALSE on failure
  599. *
  600. * HISTORY:
  601. *
  602. * 12-09-91 Davidc Created.
  603. *
  604. \****************************************************************************/
  605. BOOL
  606. ChangePasswordDlgInit(
  607. HWND hDlg,
  608. LPARAM lParam
  609. )
  610. {
  611. PCHANGE_PASSWORD_DATA pPasswordData = (PCHANGE_PASSWORD_DATA)lParam;
  612. PGLOBALS pGlobals = pPasswordData->pGlobals;
  613. // Store our structure pointer
  614. SetWindowLongPtr(hDlg, GWLP_USERDATA, lParam);
  615. // Size for the branding image we are going to add.
  616. SizeForBranding(hDlg, FALSE);
  617. // Set up the initial text field contents
  618. SetDlgItemText(hDlg, IDD_CHANGEPWD_NAME, pPasswordData->UserName);
  619. SetDlgItemText(hDlg, IDD_CHANGEPWD_OLD, pPasswordData->OldPassword);
  620. // Limit the maximum password length to 127
  621. SendDlgItemMessage(hDlg, IDD_CHANGEPWD_OLD, EM_SETLIMITTEXT, (WPARAM) 127, 0);
  622. SendDlgItemMessage(hDlg, IDD_CHANGEPWD_NEW, EM_SETLIMITTEXT, (WPARAM) 127, 0);
  623. SendDlgItemMessage(hDlg, IDD_CHANGEPWD_CONFIRM, EM_SETLIMITTEXT, (WPARAM) 127, 0);
  624. // ShowBackupButton(hDlg,pPasswordData->pGlobals); moved to after populate domain list
  625. // If this is the domain case and we aren't force to hide the domain ui
  626. if (( pPasswordData->Options & CHANGEPWD_OPTION_SHOW_DOMAIN ) &&
  627. (!ForceNoDomainUI()))
  628. {
  629. // If the user can choose their domain, fill the domain combobox
  630. // with the known domains and the local machine name. Otherwise
  631. // disable the domain combobox.
  632. if ( pPasswordData->Options & CHANGEPWD_OPTION_EDIT_DOMAIN ) {
  633. ASSERT( (pPasswordData->Options & CHANGEPWD_OPTION_KEEP_ARRAY) == 0 );
  634. if ( !DCacheValidateCache( pGlobals->Cache ) )
  635. {
  636. ASSERT( pGlobals->ActiveArray == NULL );
  637. DCacheUpdateFull( pGlobals->Cache,
  638. pGlobals->Domain );
  639. }
  640. pGlobals->ActiveArray = DCacheCopyCacheArray( pGlobals->Cache );
  641. if ( pPasswordData->Options & CHANGEPWD_OPTION_SHOW_NETPROV )
  642. {
  643. DCacheAddNetworkProviders( pGlobals->ActiveArray );
  644. }
  645. if ( pGlobals->ActiveArray )
  646. {
  647. // Fill combo box list, set domain type item data
  648. DCachePopulateListBoxFromArray( pGlobals->ActiveArray,
  649. GetDlgItem( hDlg, IDD_CHANGEPWD_DOMAIN ),
  650. NULL );
  651. }
  652. else
  653. {
  654. EndDialog( hDlg, MSGINA_DLG_FAILURE );
  655. }
  656. EnableDomainForUPN( GetDlgItem( hDlg, IDD_CHANGEPWD_NAME),
  657. GetDlgItem(hDlg,IDD_CHANGEPWD_DOMAIN) );
  658. } else {
  659. SendDlgItemMessage(hDlg, IDD_CHANGEPWD_DOMAIN, CB_ADDSTRING, 0, (LPARAM)pPasswordData->Domain);
  660. SendDlgItemMessage(hDlg, IDD_CHANGEPWD_DOMAIN, CB_SETCURSEL, 0, 0);
  661. EnableDlgItem(hDlg, IDD_CHANGEPWD_DOMAIN, FALSE);
  662. }
  663. }
  664. else // workgroup case or we're forced to hide the domain UI
  665. {
  666. RECT rcDomain, rcUsername;
  667. // Hide the domain box
  668. ShowWindow(GetDlgItem(hDlg, IDD_CHANGEPWD_DOMAIN), SW_HIDE);
  669. ShowWindow(GetDlgItem(hDlg, IDD_CHANGEPWD_DOMAIN_LABEL), SW_HIDE);
  670. EnableDlgItem(hDlg, IDD_CHANGEPWD_DOMAIN, FALSE);
  671. // Shorten the window since the domain box isn't used
  672. GetWindowRect(GetDlgItem(hDlg, IDD_CHANGEPWD_NAME), &rcUsername);
  673. GetWindowRect(GetDlgItem(hDlg, IDD_CHANGEPWD_DOMAIN), &rcDomain);
  674. MoveControls(hDlg, ctrlNoDomain,
  675. ARRAYSIZE(ctrlNoDomain),
  676. 0, -(rcDomain.bottom-rcUsername.bottom),
  677. TRUE);
  678. }
  679. ShowBackupButton(hDlg,pPasswordData->pGlobals);
  680. DisplayLanguageIcon(hDlg, LAYOUT_CUR_USER, GetKeyboardLayout(0));
  681. CentreWindow(hDlg);
  682. SetupSystemMenu(hDlg);
  683. return TRUE;
  684. }
  685. VOID
  686. UpdateWithChangedPassword(
  687. PGLOBALS pGlobals,
  688. HWND ActiveWindow,
  689. BOOL Hash,
  690. PWSTR UserName,
  691. PWSTR Domain,
  692. PWSTR Password,
  693. PWSTR NewPassword,
  694. PMSV1_0_INTERACTIVE_PROFILE NewProfile
  695. )
  696. {
  697. WLX_MPR_NOTIFY_INFO MprInfo;
  698. int MprResult;
  699. PDOMAIN_CACHE_ENTRY Entry ;
  700. UNICODE_STRING String ;
  701. DWORD ChangeInfo = 0;
  702. HWND hwndOwner;
  703. PMSV1_0_CHANGEPASSWORD_REQUEST Request = NULL;
  704. ULONG RequestSize = 0;
  705. ULONG PackageId = 0;
  706. PVOID Response = NULL;
  707. ULONG ResponseSize;
  708. NTSTATUS SubStatus = STATUS_SUCCESS, Status = STATUS_SUCCESS;
  709. PBYTE Where;
  710. STRING Name;
  711. DWORD MaxPasswordAge ;
  712. LARGE_INTEGER Now ;
  713. LARGE_INTEGER EndOfPassword ;
  714. HANDLE ImpHandle ;
  715. BOOL InteractiveUser = FALSE;
  716. if (pGlobals->AutoAdminLogon)
  717. {
  718. if (IsAutologonUser(UserName, Domain))
  719. {
  720. SetAutologonPassword(NewPassword);
  721. }
  722. }
  723. //
  724. // Determine if this is the interactive user
  725. //
  726. if ( (_wcsicmp( Domain, pGlobals->Domain ) == 0 ) &&
  727. (_wcsicmp( UserName, pGlobals->UserName ) == 0 ) )
  728. {
  729. InteractiveUser = TRUE ;
  730. }
  731. else if ( ( pGlobals->FlatDomain.Buffer ) &&
  732. ( _wcsicmp( Domain, pGlobals->FlatDomain.Buffer ) == 0 ) &&
  733. ( _wcsicmp( UserName, pGlobals->FlatUserName.Buffer ) == 0 ) )
  734. {
  735. InteractiveUser = TRUE ;
  736. }
  737. else
  738. {
  739. // More complicated stuff for the domain\username NT4 style
  740. PWSTR BackSlash;
  741. if ((BackSlash = wcschr(pGlobals->UserName, L'\\')) != NULL)
  742. {
  743. // size of domain in domain\username
  744. ResponseSize = (ULONG)(BackSlash - pGlobals->UserName);
  745. if ((ResponseSize == (ULONG)wcslen(Domain)) &&
  746. (_wcsnicmp(Domain, pGlobals->UserName, ResponseSize) == 0) &&
  747. (_wcsicmp(UserName, BackSlash+1 ) == 0))
  748. {
  749. InteractiveUser = TRUE ;
  750. }
  751. }
  752. }
  753. if ( InteractiveUser )
  754. {
  755. //
  756. // Update the in-memory copy of the password for unlock.
  757. //
  758. RtlInitUnicodeString( &String, NewPassword );
  759. if ( Hash )
  760. {
  761. HashPassword( &String, pGlobals->PasswordHash );
  762. }
  763. else
  764. {
  765. //
  766. // Don't hash the password away. This is only
  767. // set when the password is changed during logon.
  768. // (all the callers stored NewPassword in buffer of same length)
  769. // Erase the old password first as it might be shorter than the new one
  770. // It is still in cleartext at this point!
  771. ErasePassword( &pGlobals->PasswordString );
  772. wcscpy( pGlobals->Password, NewPassword );
  773. RtlInitUnicodeString(
  774. &pGlobals->PasswordString,
  775. pGlobals->Password);
  776. HidePassword(
  777. &pGlobals->Seed,
  778. &pGlobals->PasswordString);
  779. }
  780. //
  781. // Update password expiration time
  782. //
  783. if ( pGlobals->Profile )
  784. {
  785. if ( NewProfile )
  786. {
  787. pGlobals->Profile->PasswordMustChange = NewProfile->PasswordMustChange;
  788. }
  789. else
  790. {
  791. GetSystemTimeAsFileTime( (PFILETIME)&Now );
  792. if ( GetMaxPasswordAge( Domain, &MaxPasswordAge ) == 0 )
  793. {
  794. EndOfPassword.QuadPart = Now.QuadPart + (LONGLONG)MaxPasswordAge * (LONGLONG)10000000 ;
  795. }
  796. else
  797. {
  798. //
  799. // Compute the new expiration based on the last delta
  800. //
  801. EndOfPassword.QuadPart = pGlobals->Profile->PasswordMustChange.QuadPart -
  802. pGlobals->Profile->PasswordLastSet.QuadPart +
  803. Now.QuadPart;
  804. }
  805. //
  806. // Make sure we're not shortening the expiration time
  807. //
  808. if ( pGlobals->Profile->PasswordMustChange.QuadPart < EndOfPassword.QuadPart )
  809. {
  810. pGlobals->Profile->PasswordMustChange.QuadPart = EndOfPassword.QuadPart;
  811. }
  812. }
  813. }
  814. //
  815. // Update the security packages:
  816. //
  817. RtlInitString(
  818. &Name,
  819. MSV1_0_PACKAGE_NAME
  820. );
  821. Status = LsaLookupAuthenticationPackage(
  822. pGlobals->LsaHandle,
  823. &Name,
  824. &PackageId
  825. );
  826. if ( NT_SUCCESS( Status ) )
  827. {
  828. RequestSize = sizeof(MSV1_0_CHANGEPASSWORD_REQUEST) +
  829. (DWORD) (wcslen(UserName) +
  830. wcslen(Domain) +
  831. wcslen(NewPassword) + 3) * sizeof(WCHAR);
  832. Request = (PMSV1_0_CHANGEPASSWORD_REQUEST) LocalAlloc(LMEM_ZEROINIT,RequestSize);
  833. if ( Request )
  834. {
  835. Where = (PBYTE) (Request + 1);
  836. Request->MessageType = MsV1_0ChangeCachedPassword;
  837. wcscpy(
  838. (LPWSTR) Where,
  839. Domain
  840. );
  841. RtlInitUnicodeString(
  842. &Request->DomainName,
  843. (LPWSTR) Where
  844. );
  845. Where += Request->DomainName.MaximumLength;
  846. wcscpy(
  847. (LPWSTR) Where,
  848. UserName
  849. );
  850. RtlInitUnicodeString(
  851. &Request->AccountName,
  852. (LPWSTR) Where
  853. );
  854. Where += Request->AccountName.MaximumLength;
  855. wcscpy(
  856. (LPWSTR) Where,
  857. NewPassword
  858. );
  859. RtlInitUnicodeString(
  860. &Request->NewPassword,
  861. (LPWSTR) Where
  862. );
  863. Where += Request->NewPassword.MaximumLength;
  864. //
  865. // Make the call
  866. //
  867. ImpHandle = ImpersonateUser( &pGlobals->UserProcessData, NULL );
  868. if ( ImpHandle )
  869. {
  870. Request->Impersonating = TRUE ;
  871. Status = LsaCallAuthenticationPackage(
  872. pGlobals->LsaHandle,
  873. PackageId,
  874. Request,
  875. RequestSize,
  876. &Response,
  877. &ResponseSize,
  878. &SubStatus
  879. );
  880. StopImpersonating( ImpHandle );
  881. }
  882. // this buffer contains passwords so we zeroize it before freeing it
  883. ZeroMemory(Request, RequestSize);
  884. LocalFree( Request );
  885. if ( NT_SUCCESS( Status ) && ImpHandle )
  886. {
  887. LsaFreeReturnBuffer( Response );
  888. }
  889. }
  890. }
  891. }
  892. //
  893. // Let other providers know about the change
  894. //
  895. //
  896. // If the domain is one from our combo-box
  897. // then it is valid for logons.
  898. //
  899. if ( pGlobals->ActiveArray )
  900. {
  901. RtlInitUnicodeString( &String, Domain );
  902. Entry = DCacheSearchArray(
  903. pGlobals->ActiveArray,
  904. &String );
  905. if ( (Entry) && (Entry->Type != DomainNetworkProvider) )
  906. {
  907. ChangeInfo |= WN_VALID_LOGON_ACCOUNT ;
  908. }
  909. }
  910. //
  911. // Hide this dialog and pass our parent as the owner
  912. // of any provider dialogs
  913. //
  914. ShowWindow(ActiveWindow, SW_HIDE);
  915. hwndOwner = GetParent( ActiveWindow );
  916. MprInfo.pszUserName = DupString(UserName);
  917. MprInfo.pszDomain = DupString(Domain);
  918. MprInfo.pszPassword = DupString(NewPassword);
  919. MprInfo.pszOldPassword = DupString(Password);
  920. MprResult = pWlxFuncs->WlxChangePasswordNotify(
  921. pGlobals->hGlobalWlx,
  922. &MprInfo,
  923. ChangeInfo | WN_NT_PASSWORD_CHANGED);
  924. }
  925. /****************************************************************************\
  926. *
  927. * FUNCTION: AttemptPasswordChange
  928. *
  929. * PURPOSE: Tries to change the user's password using the current values in
  930. * the change-password dialog controls
  931. *
  932. * RETURNS: MSGINA_DLG_SUCCESS if the password was changed successfully.
  933. * MSGINA_DLG_FAILURE if the change failed
  934. * DLG_INTERRUPTED() - this is a set of possible interruptions (see winlogon.h)
  935. *
  936. * NOTES: If the password change failed, this routine displays the necessary
  937. * dialogs explaining what failed and why before returning.
  938. * This routine also clears the fields that need re-entry before
  939. * returning so the calling routine can call SetPasswordFocus on
  940. * the dialog to put the focus in the appropriate place.
  941. *
  942. * HISTORY:
  943. *
  944. * 12-09-91 Davidc Created.
  945. *
  946. \****************************************************************************/
  947. void MyZeroMemory(PVOID lpv, SIZE_T size)
  948. {
  949. ZeroMemory(lpv, size);
  950. }
  951. INT_PTR
  952. AttemptPasswordChange(
  953. HWND hDlg
  954. )
  955. {
  956. PCHANGE_PASSWORD_DATA pPasswordData = (PCHANGE_PASSWORD_DATA)GetWindowLongPtr(hDlg, GWLP_USERDATA);
  957. PGLOBALS pGlobals = pPasswordData->pGlobals;
  958. TCHAR UserName[MAX_STRING_BYTES];
  959. TCHAR Domain[MAX_STRING_BYTES];
  960. TCHAR Password[MAX_STRING_BYTES];
  961. TCHAR NewPassword[MAX_STRING_BYTES];
  962. TCHAR ConfirmNewPassword[MAX_STRING_BYTES];
  963. INT_PTR Result;
  964. INT_PTR ReturnResult = MSGINA_DLG_SUCCESS;
  965. NTSTATUS Status;
  966. NTSTATUS SubStatus ;
  967. PDOMAIN_CACHE_ENTRY Entry ;
  968. PDOMAIN_CACHE_ENTRY Search ;
  969. UNICODE_STRING Domain_U ;
  970. ULONG Size ;
  971. HWND hwndDomain = GetDlgItem(hDlg, IDD_CHANGEPWD_DOMAIN);
  972. INT iDomainSelection = (INT)SendMessage(hwndDomain, CB_GETCURSEL, 0, 0);
  973. DOMAIN_PASSWORD_INFORMATION DomainInfo ;
  974. PWSTR UpnSuffix = NULL;
  975. BOOL RetryWithFlat = FALSE ;
  976. UserName[0] = TEXT('\0');
  977. Domain[0] = TEXT('\0');
  978. Password[0] = TEXT('\0');
  979. NewPassword[0] = TEXT('\0');
  980. ConfirmNewPassword[0] = TEXT('\0');
  981. ZeroMemory( &DomainInfo, sizeof( DomainInfo ) );
  982. GetDlgItemText(hDlg, IDD_CHANGEPWD_NAME, UserName, MAX_STRING_BYTES);
  983. if (wcschr(UserName, L'\\')) // Found a backslash
  984. { // wcscpy is OK since all buffers have the same size
  985. wcscpy(Domain, UserName); // domain\username in Domain
  986. UpnSuffix = wcschr(Domain, L'\\');
  987. *UpnSuffix = 0; // domain in Domain
  988. UpnSuffix++; // points to username in Domain
  989. wcscpy(UserName, UpnSuffix); // username in Username
  990. // Force iDomainSelection to CB_ERR since the combo is disabled
  991. iDomainSelection = CB_ERR;
  992. // we'll use the UpnSuffix has a trigger below to remember
  993. // about the backslash
  994. }
  995. //
  996. // The selected domain may really be a special entry: the local machine
  997. // (this is also set in the logon path (expired password))
  998. //
  999. if ( pPasswordData->Options & CHANGEPWD_OPTION_EDIT_DOMAIN )
  1000. {
  1001. Entry = (PDOMAIN_CACHE_ENTRY) SendMessage( hwndDomain, CB_GETITEMDATA, iDomainSelection, 0 );
  1002. if ( CB_ERR == (ULONG_PTR) Entry )
  1003. {
  1004. Entry = NULL ;
  1005. }
  1006. if ( Entry == NULL )
  1007. {
  1008. if (NULL == UpnSuffix)
  1009. {
  1010. //
  1011. // User typed in a new string, so there is no entry for this string. Create
  1012. // an entry here, and use it later.
  1013. //
  1014. GetDlgItemText( hDlg, IDD_CHANGEPWD_DOMAIN, Domain, MAX_STRING_BYTES );
  1015. }
  1016. //else Domain was already set above (user entered domain\username)
  1017. RtlInitUnicodeString( &Domain_U, Domain );
  1018. Entry = DCacheCreateEntry(
  1019. DomainNt4,
  1020. &Domain_U,
  1021. NULL,
  1022. NULL );
  1023. }
  1024. else
  1025. {
  1026. //
  1027. // Maybe DNS, maybe not:
  1028. //
  1029. if ( Entry->Type == DomainNt5 )
  1030. {
  1031. wcscpy( Domain, Entry->DnsName.Buffer );
  1032. RetryWithFlat = TRUE ;
  1033. }
  1034. else
  1035. {
  1036. wcscpy( Domain, Entry->FlatName.Buffer );
  1037. }
  1038. //
  1039. // Reference it here. The case above will create an entry with a reference
  1040. // that we will need to deref when we're done. So, bump it now to make it
  1041. // cleaner later.
  1042. //
  1043. DCacheReferenceEntry( Entry );
  1044. }
  1045. }
  1046. else
  1047. {
  1048. if (NULL == UpnSuffix)
  1049. {
  1050. //
  1051. // Standalone case. Force the machine name entry
  1052. //
  1053. Size = MAX_STRING_BYTES ;
  1054. GetDlgItemText( hDlg, IDD_CHANGEPWD_DOMAIN, Domain, MAX_STRING_BYTES );
  1055. //
  1056. // If nothing there, use the domain from the logon:
  1057. //
  1058. if ( Domain[0] == L'\0' )
  1059. {
  1060. wcscpy( Domain, pGlobals->Domain );
  1061. }
  1062. }
  1063. else
  1064. {
  1065. //
  1066. // NT4 style name as detected above
  1067. //
  1068. // No need to do anything as Domain is already set.
  1069. }
  1070. RtlInitUnicodeString( &Domain_U, Domain );
  1071. Entry = DCacheCreateEntry(
  1072. (NULL == UpnSuffix) ? DomainMachine : DomainNt4,
  1073. &Domain_U,
  1074. NULL,
  1075. NULL );
  1076. }
  1077. if ( !Entry )
  1078. {
  1079. // No need to do cleanup here as we haven't read the passwords yet
  1080. return DLG_FAILURE ;
  1081. }
  1082. GetDlgItemText(hDlg, IDD_CHANGEPWD_OLD, Password, MAX_STRING_BYTES);
  1083. GetDlgItemText(hDlg, IDD_CHANGEPWD_NEW, NewPassword, MAX_STRING_BYTES);
  1084. GetDlgItemText(hDlg, IDD_CHANGEPWD_CONFIRM, ConfirmNewPassword, MAX_STRING_BYTES);
  1085. // If we are forcing a NoDomainUI, populate the domain with the local machine name now
  1086. if ((NULL == UpnSuffix) && (ForceNoDomainUI()))
  1087. {
  1088. DWORD chSize = ARRAYSIZE(Domain);
  1089. if (!GetComputerName(Domain, &chSize))
  1090. {
  1091. *Domain = 0;
  1092. }
  1093. }
  1094. //
  1095. // If there is a at-sign in the name, assume that means that a UPN
  1096. // attempt is being made. Set the domain to NULL.
  1097. //
  1098. if ( wcschr( UserName, L'@' ) )
  1099. {
  1100. Domain[0] = TEXT('\0');
  1101. }
  1102. //
  1103. // Validate user entries:
  1104. //
  1105. // Check that new passwords match
  1106. //
  1107. if (lstrcmp(NewPassword, ConfirmNewPassword) != 0) {
  1108. Result = TimeoutMessageBox(hDlg, pGlobals, IDS_NO_PASSWORD_CONFIRM,
  1109. IDS_CHANGE_PASSWORD,
  1110. MB_OK | MB_ICONEXCLAMATION,
  1111. TIMEOUT_CURRENT);
  1112. if (DLG_INTERRUPTED(Result)) {
  1113. Result = SetInterruptFlag( MSGINA_DLG_FAILURE );
  1114. }
  1115. else
  1116. {
  1117. Result = MSGINA_DLG_FAILURE ;
  1118. }
  1119. ReturnResult = Result;
  1120. goto Exit;
  1121. }
  1122. if ( Domain[0] == L'\0' )
  1123. {
  1124. UpnSuffix = wcschr( UserName, L'@' );
  1125. if ( UpnSuffix == NULL )
  1126. {
  1127. Result = TimeoutMessageBox( hDlg, pGlobals,
  1128. IDS_NO_DOMAIN_AND_NO_UPN,
  1129. IDS_CHANGE_PASSWORD,
  1130. MB_OK | MB_ICONEXCLAMATION,
  1131. TIMEOUT_CURRENT );
  1132. if (DLG_INTERRUPTED(Result)) {
  1133. Result = SetInterruptFlag( MSGINA_DLG_FAILURE );
  1134. }
  1135. else
  1136. {
  1137. Result = MSGINA_DLG_FAILURE ;
  1138. }
  1139. ReturnResult = Result;
  1140. goto Exit;
  1141. }
  1142. else
  1143. {
  1144. //
  1145. // Ok, the UPN suffix is present. Check if it is part of an
  1146. // MIT domain. MIT domains have the flat and DNS fields identical.
  1147. //
  1148. UpnSuffix++ ;
  1149. Search = DCacheLocateEntry(
  1150. pGlobals->Cache,
  1151. UpnSuffix );
  1152. if ( Search )
  1153. {
  1154. DCacheDereferenceEntry( Entry );
  1155. Entry = Search ;
  1156. }
  1157. }
  1158. }
  1159. //
  1160. // Check if the password exceeds the LM limit of 14 characters.
  1161. //
  1162. if ( ( lstrlen( NewPassword ) > LM20_PWLEN ) &&
  1163. ( ( Entry->Type == DomainUPN ) ||
  1164. ( Entry->Type == DomainMachine ) ||
  1165. ( Entry->Type == DomainNt4 ) ||
  1166. ( Entry->Type == DomainNt5 ) ) )
  1167. {
  1168. //
  1169. // For long passwords, confirm with the user.
  1170. //
  1171. Result = TimeoutMessageBox(
  1172. hDlg, pGlobals,
  1173. IDS_LONG_PASSWORD_WARNING,
  1174. IDS_CHANGE_PASSWORD,
  1175. MB_OKCANCEL | MB_ICONEXCLAMATION,
  1176. TIMEOUT_CURRENT );
  1177. if ( DLG_INTERRUPTED(Result) )
  1178. {
  1179. Result = SetInterruptFlag( MSGINA_DLG_FAILURE );
  1180. }
  1181. else
  1182. {
  1183. if ( Result == IDCANCEL )
  1184. {
  1185. Result = MSGINA_DLG_FAILURE ;
  1186. }
  1187. }
  1188. if ( ResultNoFlags( Result ) == MSGINA_DLG_FAILURE )
  1189. {
  1190. ReturnResult = Result;
  1191. goto Exit;
  1192. }
  1193. }
  1194. //
  1195. // Call the Appropriate Change Password Engine:
  1196. //
  1197. Status = ChangePasswordWorkers[ Entry->Type ](
  1198. pPasswordData,
  1199. UserName,
  1200. Domain,
  1201. Password,
  1202. NewPassword,
  1203. &SubStatus,
  1204. &DomainInfo );
  1205. if ( RetryWithFlat )
  1206. {
  1207. //
  1208. // If we just used the DNS name, restore the flat name,
  1209. // since all later comparisons on the name for stored
  1210. // password update will be based on this
  1211. //
  1212. wcscpy( Domain, Entry->FlatName.Buffer );
  1213. }
  1214. if ( ( Status == STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ||
  1215. ( Status == STATUS_CANT_ACCESS_DOMAIN_INFO ) )
  1216. {
  1217. Status = ChangePasswordWorkers[ Entry->Type ](
  1218. pPasswordData,
  1219. UserName,
  1220. Domain,
  1221. Password,
  1222. NewPassword,
  1223. &SubStatus,
  1224. &DomainInfo );
  1225. }
  1226. if ( NT_SUCCESS( Status ) )
  1227. {
  1228. Result = TimeoutMessageBox(hDlg,
  1229. pGlobals,
  1230. IDS_PASSWORD_CHANGED,
  1231. IDS_CHANGE_PASSWORD,
  1232. MB_OK | MB_ICONINFORMATION,
  1233. TIMEOUT_CURRENT);
  1234. } else {
  1235. ReturnResult = MSGINA_DLG_FAILURE;
  1236. //
  1237. // Failure, explain it to the user
  1238. //
  1239. Result = HandleFailedChangePassword(hDlg,
  1240. pGlobals,
  1241. Status,
  1242. UserName,
  1243. Domain,
  1244. SubStatus,
  1245. &DomainInfo
  1246. );
  1247. }
  1248. //
  1249. // Only call other providers if the change password attempt succeeded.
  1250. //
  1251. if (NT_SUCCESS(Status)) {
  1252. //
  1253. // Update our own state:
  1254. //
  1255. UpdateWithChangedPassword(
  1256. pGlobals,
  1257. hDlg,
  1258. (pPasswordData->Options & CHANGEPWD_OPTION_NO_UPDATE ? FALSE : TRUE ),
  1259. UserName,
  1260. Domain,
  1261. Password,
  1262. NewPassword,
  1263. NULL );
  1264. }
  1265. //
  1266. // Find out what happened to the message box:
  1267. //
  1268. if ( Result != IDOK )
  1269. {
  1270. //
  1271. // mbox was interrupted
  1272. //
  1273. ReturnResult = SetInterruptFlag( ReturnResult );
  1274. }
  1275. Exit:
  1276. DCacheDereferenceEntry( Entry );
  1277. // Zeroize these buffers for obvious security reasons
  1278. // Need to call this stub, otherwise the compiler optimize this out!
  1279. MyZeroMemory(Password, sizeof(Password));
  1280. MyZeroMemory(NewPassword, sizeof(NewPassword));
  1281. MyZeroMemory(ConfirmNewPassword, sizeof(ConfirmNewPassword));
  1282. return(ReturnResult);
  1283. }
  1284. /****************************************************************************\
  1285. *
  1286. * FUNCTION: HandleFailedChangePassword
  1287. *
  1288. * PURPOSE: Tells the user why their change-password attempt failed.
  1289. *
  1290. * RETURNS: MSGINA_DLG_FAILURE - we told them what the problem was successfully.
  1291. * DLG_INTERRUPTED() - a set of return values - see winlogon.h
  1292. *
  1293. * HISTORY:
  1294. *
  1295. * 21-Sep-92 Davidc Created.
  1296. *
  1297. \****************************************************************************/
  1298. INT_PTR
  1299. HandleFailedChangePassword(
  1300. HWND hDlg,
  1301. PGLOBALS pGlobals,
  1302. NTSTATUS Status,
  1303. PWCHAR UserName,
  1304. PWCHAR Domain,
  1305. NTSTATUS SubStatus,
  1306. DOMAIN_PASSWORD_INFORMATION * DomainInfo
  1307. )
  1308. {
  1309. INT_PTR Result;
  1310. DWORD Win32Error ;
  1311. TCHAR* Buffer1 = NULL;
  1312. TCHAR* Buffer2 = NULL;
  1313. TCHAR* Buffer3 = NULL;
  1314. LONGLONG OneDay;
  1315. Buffer1 = (TCHAR*) Alloc(MAX_STRING_BYTES * sizeof(TCHAR));
  1316. Buffer2 = (TCHAR*) Alloc(MAX_STRING_BYTES * sizeof(TCHAR));
  1317. Buffer3 = (TCHAR*) Alloc(MAX_STRING_BYTES * sizeof(TCHAR));
  1318. if( (NULL == Buffer1) || (NULL == Buffer2) || (NULL == Buffer3) )
  1319. {
  1320. return MSGINA_DLG_FAILURE;
  1321. }
  1322. Buffer1[ 0 ] = L'\0';
  1323. Buffer2[ 0 ] = L'\0';
  1324. Buffer3[ 0 ] = L'\0';
  1325. switch (Status) {
  1326. case STATUS_CANT_ACCESS_DOMAIN_INFO:
  1327. case STATUS_NO_SUCH_DOMAIN:
  1328. LoadString(hDllInstance,
  1329. IDS_CHANGE_PWD_NO_DOMAIN,
  1330. Buffer1,
  1331. MAX_STRING_BYTES);
  1332. _snwprintf(Buffer2, MAX_STRING_BYTES - 1, Buffer1, Domain);
  1333. Buffer2[MAX_STRING_BYTES - 1] = 0;
  1334. LoadString(hDllInstance,
  1335. IDS_CHANGE_PASSWORD,
  1336. Buffer1,
  1337. MAX_STRING_BYTES);
  1338. Result = TimeoutMessageBoxlpstr(hDlg, pGlobals,
  1339. Buffer2,
  1340. Buffer1,
  1341. MB_OK | MB_ICONEXCLAMATION,
  1342. TIMEOUT_CURRENT);
  1343. break;
  1344. case STATUS_NO_SUCH_USER:
  1345. case STATUS_WRONG_PASSWORD_CORE:
  1346. case STATUS_WRONG_PASSWORD:
  1347. Result = TimeoutMessageBox(hDlg, pGlobals, IDS_INCORRECT_NAME_OR_PWD_CHANGE,
  1348. IDS_CHANGE_PASSWORD,
  1349. MB_OK | MB_ICONEXCLAMATION,
  1350. TIMEOUT_CURRENT);
  1351. // Force re-entry of the old password
  1352. if (GetWindowLong(GetDlgItem(hDlg, IDD_CHANGEPWD_OLD), GWL_STYLE) & WS_VISIBLE) {
  1353. SetDlgItemText(hDlg, IDD_CHANGEPWD_OLD, NULL);
  1354. }
  1355. break;
  1356. case STATUS_ACCESS_DENIED:
  1357. Result = TimeoutMessageBox(hDlg, pGlobals, IDS_NO_PERMISSION_CHANGE_PWD,
  1358. IDS_CHANGE_PASSWORD,
  1359. MB_OK | MB_ICONEXCLAMATION,
  1360. TIMEOUT_CURRENT);
  1361. break;
  1362. case STATUS_ACCOUNT_RESTRICTION:
  1363. Result = TimeoutMessageBox(hDlg, pGlobals, IDS_ACCOUNT_RESTRICTION_CHANGE,
  1364. IDS_CHANGE_PASSWORD,
  1365. MB_OK | MB_ICONEXCLAMATION,
  1366. TIMEOUT_CURRENT);
  1367. break;
  1368. case STATUS_BACKUP_CONTROLLER:
  1369. Result = TimeoutMessageBox(hDlg, pGlobals, IDS_REQUIRES_PRIMARY_CONTROLLER,
  1370. IDS_CHANGE_PASSWORD,
  1371. MB_OK | MB_ICONEXCLAMATION,
  1372. TIMEOUT_CURRENT);
  1373. break;
  1374. case STATUS_PASSWORD_RESTRICTION:
  1375. if ( SubStatus == STATUS_UNSUCCESSFUL )
  1376. {
  1377. LoadString(hDllInstance, IDS_GENERAL_PASSWORD_SPEC, Buffer2, MAX_STRING_BYTES);
  1378. }
  1379. else
  1380. {
  1381. if ( SubStatus == STATUS_ILL_FORMED_PASSWORD )
  1382. {
  1383. LoadString(hDllInstance, IDS_COMPLEX_PASSWORD_SPEC, Buffer1, MAX_STRING_BYTES);
  1384. } else {
  1385. LoadString(hDllInstance, IDS_PASSWORD_SPEC, Buffer1, MAX_STRING_BYTES);
  1386. }
  1387. // this is the way filetimes are generated
  1388. OneDay = (LONGLONG)(-10000000) * 60 * 60 * 24;
  1389. _snwprintf(Buffer2, MAX_STRING_BYTES - 1, Buffer1,
  1390. DomainInfo->MinPasswordLength,
  1391. DomainInfo->PasswordHistoryLength,
  1392. (LONG)(DomainInfo->MinPasswordAge.QuadPart / OneDay)
  1393. );
  1394. Buffer2[MAX_STRING_BYTES - 1] = 0;
  1395. }
  1396. LoadString(hDllInstance, IDS_ENTER_PASSWORDS, Buffer1, MAX_STRING_BYTES);
  1397. wcsncat(Buffer2, TEXT(" "), MAX_STRING_BYTES - lstrlen(Buffer2) - 1);
  1398. wcsncat(Buffer2, Buffer1, MAX_STRING_BYTES - lstrlen(Buffer2) - 1);
  1399. LoadString(hDllInstance, IDS_CHANGE_PASSWORD, Buffer1, MAX_STRING_BYTES );
  1400. Result = TimeoutMessageBoxlpstr(hDlg, pGlobals,
  1401. Buffer2,
  1402. Buffer1,
  1403. MB_OK | MB_ICONEXCLAMATION,
  1404. TIMEOUT_CURRENT);
  1405. break;
  1406. #ifdef LATER
  1407. //
  1408. // LATER Check for minimum password age
  1409. //
  1410. if ( FALSE ) {
  1411. int PasswordAge = 0, RequiredAge = 0;
  1412. TCHAR Buffer1[MAX_STRING_BYTES];
  1413. TCHAR Buffer2[MAX_STRING_BYTES];
  1414. LoadString(hDllInstance, IDS_PASSWORD_MINIMUM_AGE, Buffer1, sizeof(Buffer1) / sizeof( TCHAR ));
  1415. _snwprintf(Buffer2, sizeof(Buffer2) / sizeof( TCHAR ), Buffer1, PasswordAge, RequiredAge);
  1416. LoadString(hDllInstance, IDS_NO_PERMISSION_CHANGE_PWD, Buffer1, sizeof(Buffer1) / sizeof( TCHAR ));
  1417. #_#_lstrcat(Buffer1, Buffer2);
  1418. LoadString(hDllInstance, IDS_CHANGE_PASSWORD, Buffer2, sizeof(Buffer2) / sizeof( TCHAR ));
  1419. Result = TimeoutMessageBoxlpstr(hDlg, pGlobals,
  1420. Buffer1,
  1421. Buffer2,
  1422. MB_OK | MB_ICONEXCLAMATION,
  1423. TIMEOUT_CURRENT);
  1424. }
  1425. #endif
  1426. default:
  1427. DebugLog((DEB_ERROR, "Change password failure status = 0x%lx\n", Status));
  1428. LoadString(hDllInstance, IDS_UNKNOWN_CHANGE_PWD_FAILURE, Buffer1, MAX_STRING_BYTES);
  1429. Win32Error = RtlNtStatusToDosError( Status );
  1430. GetErrorDescription( Win32Error, Buffer3, MAX_STRING_BYTES );
  1431. _snwprintf(Buffer2, MAX_STRING_BYTES - 1, Buffer1, Win32Error, Buffer3 );
  1432. Buffer2[MAX_STRING_BYTES - 1] = 0;
  1433. LoadString(hDllInstance, IDS_CHANGE_PASSWORD, Buffer1, MAX_STRING_BYTES);
  1434. Result = TimeoutMessageBoxlpstr(hDlg, pGlobals,
  1435. Buffer2,
  1436. Buffer1,
  1437. MB_OK | MB_ICONEXCLAMATION,
  1438. TIMEOUT_CURRENT);
  1439. break;
  1440. }
  1441. Free(Buffer1);
  1442. Free(Buffer2);
  1443. Free(Buffer3);
  1444. return(Result);
  1445. UNREFERENCED_PARAMETER(UserName);
  1446. }
  1447. BOOL IsAutologonUser(LPCTSTR szUser, LPCTSTR szDomain)
  1448. {
  1449. BOOL fIsUser = FALSE;
  1450. HKEY hkey = NULL;
  1451. TCHAR szAutologonUser[UNLEN + 1];
  1452. TCHAR szAutologonDomain[DNLEN + 1];
  1453. TCHAR szTempDomainBuffer[DNLEN + 1];
  1454. DWORD cbBuffer;
  1455. DWORD dwType;
  1456. *szTempDomainBuffer = 0;
  1457. // Domain may be a null string. If this is the case...
  1458. if (0 == *szDomain)
  1459. {
  1460. DWORD cchBuffer;
  1461. // We really mean the local machine name
  1462. // Point to our local buffer
  1463. szDomain = szTempDomainBuffer;
  1464. cchBuffer = ARRAYSIZE(szTempDomainBuffer);
  1465. GetComputerName(szTempDomainBuffer, &cchBuffer);
  1466. }
  1467. // See if the domain and user name
  1468. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, WINLOGON_KEY, 0, KEY_READ, &hkey))
  1469. {
  1470. // Check the user name
  1471. cbBuffer = sizeof (szAutologonUser);
  1472. if (ERROR_SUCCESS == RegQueryValueEx(hkey, DEFAULT_USER_NAME_KEY, 0, &dwType, (LPBYTE) szAutologonUser, &cbBuffer))
  1473. {
  1474. // Does it match?
  1475. #pragma prefast(suppress: 400, "PREfast noise: lstrcmpi")
  1476. if (0 == lstrcmpi(szAutologonUser, szUser))
  1477. {
  1478. // Yes. Now check domain
  1479. cbBuffer = sizeof(szAutologonDomain);
  1480. if (ERROR_SUCCESS == RegQueryValueEx(hkey, DEFAULT_DOMAIN_NAME_KEY, 0, &dwType, (LPBYTE) szAutologonDomain, &cbBuffer))
  1481. {
  1482. // Make sure domain matches
  1483. #pragma prefast(suppress: 400, "PREfast noise: lstrcmpi")
  1484. if (0 == lstrcmpi(szAutologonDomain, szDomain))
  1485. {
  1486. // Success - the users match
  1487. fIsUser = TRUE;
  1488. }
  1489. }
  1490. }
  1491. }
  1492. RegCloseKey(hkey);
  1493. }
  1494. return fIsUser;
  1495. }
  1496. NTSTATUS SetAutologonPassword(LPCWSTR szPassword)
  1497. {
  1498. NTSTATUS Status = STATUS_SUCCESS;
  1499. OBJECT_ATTRIBUTES ObjectAttributes;
  1500. LSA_HANDLE LsaHandle = NULL;
  1501. UNICODE_STRING SecretName;
  1502. UNICODE_STRING SecretValue;
  1503. InitializeObjectAttributes(&ObjectAttributes, NULL, 0L, (HANDLE)NULL, NULL);
  1504. Status = LsaOpenPolicy(NULL, &ObjectAttributes, POLICY_CREATE_SECRET, &LsaHandle);
  1505. if (!NT_SUCCESS(Status))
  1506. return Status;
  1507. RtlInitUnicodeString(&SecretName, DEFAULT_PASSWORD_KEY);
  1508. RtlInitUnicodeString(&SecretValue, szPassword);
  1509. Status = LsaStorePrivateData(LsaHandle, &SecretName, &SecretValue);
  1510. LsaClose(LsaHandle);
  1511. return Status;
  1512. }
  1513. NTSTATUS
  1514. NtChangePassword(
  1515. PCHANGE_PASSWORD_DATA pChangePasswordData,
  1516. PWSTR UserName,
  1517. PWSTR Domain,
  1518. PWSTR OldPassword,
  1519. PWSTR NewPassword,
  1520. PNTSTATUS SubStatus,
  1521. DOMAIN_PASSWORD_INFORMATION * DomainInfo
  1522. )
  1523. {
  1524. NTSTATUS Status ;
  1525. NTSTATUS ProtocolStatus = STATUS_SUCCESS;
  1526. PGLOBALS pGlobals = pChangePasswordData->pGlobals ;
  1527. PMSV1_0_CHANGEPASSWORD_REQUEST pChangePasswordRequest = NULL;
  1528. PMSV1_0_CHANGEPASSWORD_RESPONSE pChangePasswordResponse = NULL;
  1529. PWCHAR DomainU;
  1530. PWCHAR UserNameU;
  1531. PWCHAR PasswordU;
  1532. PWCHAR NewPasswordU;
  1533. int Length;
  1534. ULONG RequestBufferSize;
  1535. ULONG ResponseBufferSize;
  1536. HANDLE ImpersonationHandle = NULL;
  1537. ULONG MsvPackage;
  1538. STRING PackageName;
  1539. //
  1540. // Determine request buffer size needed, including room for
  1541. // strings. Set string pointers to offsets as we step through
  1542. // sizing each one.
  1543. //
  1544. RequestBufferSize = sizeof(*pChangePasswordRequest);
  1545. UserNameU = UIntToPtr(RequestBufferSize);
  1546. RequestBufferSize += (lstrlen(UserName)+1) * sizeof(WCHAR);
  1547. DomainU = UIntToPtr(RequestBufferSize);
  1548. RequestBufferSize += (lstrlen(Domain)+1) * sizeof(WCHAR);
  1549. PasswordU = UIntToPtr(RequestBufferSize);
  1550. RequestBufferSize += (lstrlen(OldPassword)+1) * sizeof(WCHAR);
  1551. NewPasswordU = UIntToPtr(RequestBufferSize);
  1552. RequestBufferSize += (lstrlen(NewPassword)+1) * sizeof(WCHAR);
  1553. //
  1554. // Allocate request buffer
  1555. //
  1556. pChangePasswordRequest = Alloc(RequestBufferSize);
  1557. if (NULL == pChangePasswordRequest) {
  1558. DebugLog((DEB_ERROR, "cannot allocate change password request buffer (%ld bytes).", RequestBufferSize));
  1559. return MSGINA_DLG_FAILURE;
  1560. }
  1561. //
  1562. // Fixup string offsets to string pointers for request.
  1563. //
  1564. UserNameU = (PVOID) ((PBYTE)pChangePasswordRequest + (ULONG_PTR)UserNameU);
  1565. DomainU = (PVOID) ((PBYTE)pChangePasswordRequest + (ULONG_PTR)DomainU);
  1566. PasswordU = (PVOID) ((PBYTE)pChangePasswordRequest + (ULONG_PTR)PasswordU);
  1567. NewPasswordU = (PVOID) ((PBYTE)pChangePasswordRequest + (ULONG_PTR)NewPasswordU);
  1568. //
  1569. // Setup MSV1_0ChangePassword request.
  1570. //
  1571. pChangePasswordRequest->MessageType = MsV1_0ChangePassword;
  1572. // strings are already unicode, just copy them // lhb tracks //REVIEW
  1573. lstrcpy((LPTSTR)UserNameU,UserName);
  1574. lstrcpy((LPTSTR)DomainU,Domain);
  1575. lstrcpy((LPTSTR)PasswordU,OldPassword);
  1576. lstrcpy((LPTSTR)NewPasswordU,NewPassword);
  1577. Length = lstrlen(UserName);
  1578. UserNameU[Length] = 0;
  1579. RtlInitUnicodeString(
  1580. &pChangePasswordRequest->AccountName,
  1581. UserNameU
  1582. );
  1583. Length = lstrlen(Domain);
  1584. DomainU[Length] = 0;
  1585. RtlInitUnicodeString(
  1586. &pChangePasswordRequest->DomainName,
  1587. DomainU
  1588. );
  1589. Length = lstrlen(OldPassword);
  1590. PasswordU[Length] = 0;
  1591. RtlInitUnicodeString(
  1592. &pChangePasswordRequest->OldPassword,
  1593. PasswordU
  1594. );
  1595. Length = lstrlen(NewPassword);
  1596. NewPasswordU[Length] = 0;
  1597. RtlInitUnicodeString(
  1598. &pChangePasswordRequest->NewPassword,
  1599. NewPasswordU
  1600. );
  1601. //
  1602. // Make sure the passwords are short enough that we can run-encode them.
  1603. //
  1604. if ((pChangePasswordRequest->OldPassword.Length > 127 * sizeof( WCHAR ) ) ||
  1605. (pChangePasswordRequest->NewPassword.Length > 127 * sizeof( WCHAR ) )) {
  1606. Status = STATUS_ILL_FORMED_PASSWORD;
  1607. } else {
  1608. HidePassword(NULL,&pChangePasswordRequest->OldPassword);
  1609. HidePassword(NULL,&pChangePasswordRequest->NewPassword);
  1610. Status = STATUS_SUCCESS ;
  1611. }
  1612. //
  1613. // If that succeeded, try to change the password
  1614. //
  1615. if (NT_SUCCESS(Status)) {
  1616. //
  1617. // This could take some time, put up a wait cursor
  1618. //
  1619. SetupCursor(TRUE);
  1620. //
  1621. // Call off to the authentication package (MSV/NTLM) to do the work, This
  1622. // is the NT change password function. The Kerb one calls the kerb package.
  1623. //
  1624. RtlInitString(&PackageName, MSV1_0_PACKAGE_NAME );
  1625. Status = LsaLookupAuthenticationPackage (
  1626. pGlobals->LsaHandle,
  1627. &PackageName,
  1628. &MsvPackage
  1629. );
  1630. if (!NT_SUCCESS(Status)) {
  1631. DebugLog((DEB_ERROR, "Failed to find %s authentication package, status = 0x%lx",
  1632. PackageName.Buffer, Status));
  1633. Status = MSGINA_DLG_FAILURE;
  1634. goto Exit;
  1635. }
  1636. //
  1637. // We want to impersonate if and only if the user is actually logged
  1638. // on. Otherwise we'll be impersonating SYSTEM, which is bad.
  1639. //
  1640. if (pChangePasswordData->Impersonate) {
  1641. ImpersonationHandle = ImpersonateUser(
  1642. &pGlobals->UserProcessData,
  1643. NULL
  1644. );
  1645. if (NULL == ImpersonationHandle) {
  1646. DebugLog((DEB_ERROR, "cannot impersonate user"));
  1647. Status = MSGINA_DLG_FAILURE;
  1648. goto Exit;
  1649. }
  1650. }
  1651. //
  1652. // Tell msv1_0 whether or not we're impersonating.
  1653. //
  1654. pChangePasswordRequest->Impersonating = (UCHAR)pChangePasswordData->Impersonate;
  1655. Status = LsaCallAuthenticationPackage(
  1656. pGlobals->LsaHandle,
  1657. MsvPackage,
  1658. pChangePasswordRequest,
  1659. RequestBufferSize,
  1660. (PVOID)&pChangePasswordResponse,
  1661. &ResponseBufferSize,
  1662. &ProtocolStatus
  1663. );
  1664. if (pChangePasswordData->Impersonate) {
  1665. if (!StopImpersonating(ImpersonationHandle)) {
  1666. DebugLog((DEB_ERROR, "AttemptPasswordChange: Failed to revert to self"));
  1667. //
  1668. // Blow up
  1669. //
  1670. ASSERT(FALSE);
  1671. }
  1672. }
  1673. //
  1674. // Restore the normal cursor
  1675. //
  1676. SetupCursor(FALSE);
  1677. }
  1678. //
  1679. // Get the most informative status code
  1680. //
  1681. if ( NT_SUCCESS(Status) ) {
  1682. Status = ProtocolStatus;
  1683. }
  1684. else
  1685. {
  1686. DebugLog((DEB_TRACE, "FAILED in call to LsaCallAuthenticationPackage, status %x\n", Status ));
  1687. }
  1688. if (NT_SUCCESS(Status)) {
  1689. //
  1690. // Success
  1691. //
  1692. //
  1693. // if they changed their logon password, update the
  1694. // change time in their profile info so we don't keep
  1695. // pestering them.
  1696. //
  1697. if ( (_wcsicmp( pGlobals->Domain, Domain ) == 0) &&
  1698. (_wcsicmp( pGlobals->UserName, UserName ) == 0 ))
  1699. {
  1700. //
  1701. // This is code to handle the disconnected (preferred) domain. This
  1702. // was to be devl-only code and removed eventually, but some customers
  1703. // liked it so much, it stayed.
  1704. //
  1705. {
  1706. HKEY Key ;
  1707. int err ;
  1708. PWSTR PreferredDomain ;
  1709. DWORD Type ;
  1710. DWORD Size ;
  1711. NET_API_STATUS NetStatus ;
  1712. err = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  1713. TEXT("System\\CurrentControlSet\\Control\\Lsa\\MSV1_0"),
  1714. 0,
  1715. KEY_READ,
  1716. &Key );
  1717. if ( err == 0 )
  1718. {
  1719. Size = 0 ;
  1720. err = RegQueryValueEx( Key,
  1721. TEXT("PreferredDomain" ),
  1722. NULL,
  1723. &Type,
  1724. NULL,
  1725. &Size );
  1726. if ( err == 0 )
  1727. {
  1728. PreferredDomain = LocalAlloc( LMEM_FIXED, Size );
  1729. if ( PreferredDomain )
  1730. {
  1731. err = RegQueryValueEx( Key,
  1732. TEXT("PreferredDomain"),
  1733. NULL,
  1734. &Type,
  1735. (PBYTE) PreferredDomain,
  1736. &Size );
  1737. if ( err == 0 )
  1738. {
  1739. //
  1740. // If we are logged on to our preferred domain, don't
  1741. // do the update magic.
  1742. //
  1743. if ( _wcsicmp( PreferredDomain, pGlobals->Domain ) == 0 )
  1744. {
  1745. err = 2 ;
  1746. }
  1747. }
  1748. if ( err == 0 )
  1749. {
  1750. NetStatus = NetUserChangePassword(
  1751. PreferredDomain,
  1752. UserName,
  1753. OldPassword,
  1754. NewPassword );
  1755. if ( NetStatus )
  1756. {
  1757. DebugLog((DEB_ERROR, "Could not update password on %ws, %x\n", PreferredDomain, NetStatus ));
  1758. }
  1759. }
  1760. LocalFree( PreferredDomain );
  1761. }
  1762. }
  1763. RegCloseKey( Key );
  1764. }
  1765. }
  1766. }
  1767. }
  1768. else
  1769. {
  1770. *SubStatus = STATUS_UNSUCCESSFUL ;
  1771. if ( pChangePasswordResponse )
  1772. {
  1773. if ( pChangePasswordResponse->PasswordInfoValid )
  1774. {
  1775. *DomainInfo = pChangePasswordResponse->DomainPasswordInfo ;
  1776. }
  1777. }
  1778. if ( Status == STATUS_PASSWORD_RESTRICTION )
  1779. {
  1780. *SubStatus = STATUS_PASSWORD_RESTRICTION ;
  1781. if ( pChangePasswordResponse->PasswordInfoValid )
  1782. {
  1783. if ( pChangePasswordResponse->DomainPasswordInfo.PasswordProperties & DOMAIN_PASSWORD_COMPLEX )
  1784. {
  1785. *SubStatus = STATUS_ILL_FORMED_PASSWORD ;
  1786. }
  1787. }
  1788. }
  1789. }
  1790. //
  1791. // Free up the return buffer
  1792. //
  1793. if (pChangePasswordResponse != NULL) {
  1794. LsaFreeReturnBuffer(pChangePasswordResponse);
  1795. }
  1796. Exit:
  1797. //
  1798. // Free up the request buffer
  1799. //
  1800. if (pChangePasswordRequest)
  1801. {
  1802. // this buffer contains passwords so we zeroize it before freeing it
  1803. ZeroMemory(pChangePasswordRequest, RequestBufferSize);
  1804. Free(pChangePasswordRequest);
  1805. }
  1806. return Status ;
  1807. }
  1808. NTSTATUS
  1809. MitChangePassword(
  1810. PCHANGE_PASSWORD_DATA pChangePasswordData,
  1811. PWSTR UserName,
  1812. PWSTR DomainName,
  1813. PWSTR OldPassword,
  1814. PWSTR NewPassword,
  1815. PNTSTATUS pSubStatus,
  1816. DOMAIN_PASSWORD_INFORMATION * DomainInfo
  1817. )
  1818. {
  1819. PGLOBALS pGlobals = pChangePasswordData->pGlobals ;
  1820. NTSTATUS Status;
  1821. STRING Name;
  1822. ULONG PackageId;
  1823. PVOID Response = NULL ;
  1824. ULONG ResponseSize;
  1825. NTSTATUS SubStatus;
  1826. PKERB_CHANGEPASSWORD_REQUEST ChangeRequest = NULL;
  1827. ULONG ChangeSize = 0;
  1828. UNICODE_STRING User,Domain,OldPass,NewPass;
  1829. RtlInitString(
  1830. &Name,
  1831. MICROSOFT_KERBEROS_NAME_A
  1832. );
  1833. Status = LsaLookupAuthenticationPackage(
  1834. pGlobals->LsaHandle,
  1835. &Name,
  1836. &PackageId
  1837. );
  1838. if (!NT_SUCCESS(Status))
  1839. {
  1840. goto Cleanup;
  1841. }
  1842. RtlInitUnicodeString(
  1843. &User,
  1844. UserName
  1845. );
  1846. RtlInitUnicodeString(
  1847. &Domain,
  1848. DomainName
  1849. );
  1850. RtlInitUnicodeString(
  1851. &OldPass,
  1852. OldPassword
  1853. );
  1854. RtlInitUnicodeString(
  1855. &NewPass,
  1856. NewPassword
  1857. );
  1858. ChangeSize = ROUND_UP_COUNT(sizeof(KERB_CHANGEPASSWORD_REQUEST),4)+
  1859. User.Length +
  1860. Domain.Length +
  1861. OldPass.Length +
  1862. NewPass.Length ;
  1863. ChangeRequest = (PKERB_CHANGEPASSWORD_REQUEST) LocalAlloc(LMEM_ZEROINIT, ChangeSize );
  1864. if ( ChangeRequest == NULL )
  1865. {
  1866. Status = STATUS_NO_MEMORY ;
  1867. goto Cleanup ;
  1868. }
  1869. ChangeRequest->MessageType = KerbChangePasswordMessage;
  1870. ChangeRequest->AccountName = User;
  1871. ChangeRequest->AccountName.Buffer = (LPWSTR) ROUND_UP_POINTER(sizeof(KERB_CHANGEPASSWORD_REQUEST) + (PBYTE) ChangeRequest,4);
  1872. RtlCopyMemory(
  1873. ChangeRequest->AccountName.Buffer,
  1874. User.Buffer,
  1875. User.Length
  1876. );
  1877. ChangeRequest->DomainName = Domain;
  1878. ChangeRequest->DomainName.Buffer = ChangeRequest->AccountName.Buffer + ChangeRequest->AccountName.Length / sizeof(WCHAR);
  1879. RtlCopyMemory(
  1880. ChangeRequest->DomainName.Buffer,
  1881. Domain.Buffer,
  1882. Domain.Length
  1883. );
  1884. ChangeRequest->OldPassword = OldPass;
  1885. ChangeRequest->OldPassword.Buffer = ChangeRequest->DomainName.Buffer + ChangeRequest->DomainName.Length / sizeof(WCHAR);
  1886. RtlCopyMemory(
  1887. ChangeRequest->OldPassword.Buffer,
  1888. OldPass.Buffer,
  1889. OldPass.Length
  1890. );
  1891. ChangeRequest->NewPassword = NewPass;
  1892. ChangeRequest->NewPassword.Buffer = ChangeRequest->OldPassword.Buffer + ChangeRequest->OldPassword.Length / sizeof(WCHAR);
  1893. RtlCopyMemory(
  1894. ChangeRequest->NewPassword.Buffer,
  1895. NewPass.Buffer,
  1896. NewPass.Length
  1897. );
  1898. //
  1899. // We are running as the caller, so state we are impersonating
  1900. //
  1901. ChangeRequest->Impersonating = TRUE;
  1902. Status = LsaCallAuthenticationPackage(
  1903. pGlobals->LsaHandle,
  1904. PackageId,
  1905. ChangeRequest,
  1906. ChangeSize,
  1907. &Response,
  1908. &ResponseSize,
  1909. &SubStatus
  1910. );
  1911. if (!NT_SUCCESS(Status) || !NT_SUCCESS(SubStatus))
  1912. {
  1913. if (NT_SUCCESS(Status))
  1914. {
  1915. Status = SubStatus;
  1916. *pSubStatus = STATUS_UNSUCCESSFUL ;
  1917. }
  1918. else
  1919. {
  1920. *pSubStatus = SubStatus;
  1921. }
  1922. }
  1923. Cleanup:
  1924. if (Response != NULL)
  1925. {
  1926. LsaFreeReturnBuffer(Response);
  1927. }
  1928. if (ChangeRequest != NULL)
  1929. {
  1930. // this buffer contains passwords so we zeroize it before freeing it
  1931. ZeroMemory(ChangeRequest, ChangeSize);
  1932. LocalFree(ChangeRequest);
  1933. }
  1934. return(Status);
  1935. }
  1936. NTSTATUS
  1937. ProviderChangePassword(
  1938. PCHANGE_PASSWORD_DATA pChangePasswordData,
  1939. PWSTR UserName,
  1940. PWSTR Domain,
  1941. PWSTR OldPassword,
  1942. PWSTR NewPassword,
  1943. PNTSTATUS SubStatus,
  1944. DOMAIN_PASSWORD_INFORMATION * DomainInfo
  1945. )
  1946. {
  1947. WLX_MPR_NOTIFY_INFO MprInfo;
  1948. DWORD Result ;
  1949. PGLOBALS pGlobals = pChangePasswordData->pGlobals ;
  1950. MprInfo.pszUserName = DupString( UserName );
  1951. MprInfo.pszDomain = DupString( Domain );
  1952. MprInfo.pszOldPassword = DupString( OldPassword );
  1953. MprInfo.pszPassword = DupString( NewPassword );
  1954. //
  1955. // Hide this dialog and pass our parent as the owner
  1956. // of any provider dialogs
  1957. //
  1958. Result = pWlxFuncs->WlxChangePasswordNotifyEx(
  1959. pGlobals->hGlobalWlx,
  1960. &MprInfo,
  1961. 0,
  1962. Domain,
  1963. NULL );
  1964. return STATUS_SUCCESS ;
  1965. }