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.

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