/****************************** Module Header ******************************\ * Module Name: chngpwd.c * * Copyright (c) 1991, Microsoft Corporation * * Implementation of change-password functionality of winlogon * * History: * 12-09-91 Davidc Created. \***************************************************************************/ #include "msgina.h" #include #include #include #include #include typedef void (WINAPI *RUNDLLPROC)(HWND hWndStub,HINSTANCE hInstance,LPWSTR szCommandLine,int nShow); // #define VERBOSE_UTILS #ifdef VERBOSE_UTILS #define VerbosePrint(s) WLPrint(s) #else #define VerbosePrint(s) #endif // // Define the structure used to pass data into the change password dialog // typedef struct { PGLOBALS pGlobals; PWCHAR UserName; PWCHAR Domain; PWCHAR OldPassword; ULONG Options ; BOOL Impersonate; BOOL AllowProviderOnly; WCHAR UserNameBuffer[MAX_STRING_BYTES]; } CHANGE_PASSWORD_DATA; typedef CHANGE_PASSWORD_DATA *PCHANGE_PASSWORD_DATA; typedef NTSTATUS (WINAPI * GINA_CHANGEPW_FUNC)( PCHANGE_PASSWORD_DATA pChangePasswordData, PWSTR UserName, PWSTR Domain, PWSTR OldPassword, PWSTR NewPassword, PNTSTATUS SubStatus, DOMAIN_PASSWORD_INFORMATION * DomainInfo ); // // Private prototypes // NTSTATUS ProviderChangePassword( PCHANGE_PASSWORD_DATA pChangePasswordData, PWSTR UserName, PWSTR Domain, PWSTR OldPassword, PWSTR NewPassword, PNTSTATUS SubStatus, DOMAIN_PASSWORD_INFORMATION * DomainInfo ); NTSTATUS MitChangePassword( PCHANGE_PASSWORD_DATA pChangePasswordData, PWSTR UserName, PWSTR Domain, PWSTR OldPassword, PWSTR NewPassword, PNTSTATUS SubStatus, DOMAIN_PASSWORD_INFORMATION * DomainInfo ); NTSTATUS NtChangePassword( PCHANGE_PASSWORD_DATA pChangePasswordData, PWSTR UserName, PWSTR Domain, PWSTR OldPassword, PWSTR NewPassword, PNTSTATUS SubStatus, DOMAIN_PASSWORD_INFORMATION * DomainInfo ); INT_PTR WINAPI ChangePasswordDlgProc(HWND, UINT, WPARAM, LPARAM); BOOL ChangePasswordDlgInit(HWND, LPARAM); INT_PTR AttemptPasswordChange(HWND); BOOL IsAutologonUser(LPCTSTR szUser, LPCTSTR szDomain); NTSTATUS SetAutologonPassword(LPCTSTR szPassword); INT_PTR HandleFailedChangePassword( HWND hDlg, PGLOBALS pGlobals, NTSTATUS Status, PWCHAR UserName, PWCHAR Domain, NTSTATUS SubStatus, DOMAIN_PASSWORD_INFORMATION * DomainInfo ); // // This table corresponds to the DOMAIN_ENTRY_TYPE from domain.h // GINA_CHANGEPW_FUNC ChangePasswordWorkers[] = { NULL, // DomainInvalid NtChangePassword, // DomainUPN NtChangePassword, // DomainMachine NtChangePassword, // DomainNt4 NtChangePassword, // DomainNt5 MitChangePassword, // DomainMitRealm MitChangePassword, // DomainMitUntrusted ProviderChangePassword // DomainNetworkProvider }; // Control arrays for dynamically dorking with the dialog static UINT ctrlNoDomain[] = { IDD_CHANGEPWD_OLD_LABEL, IDD_CHANGEPWD_OLD, IDD_CHANGEPWD_NEW_LABEL, IDD_CHANGEPWD_NEW, IDD_CHANGEPWD_CONFIRM_LABEL, IDD_CHANGEPWD_CONFIRM, IDD_KBLAYOUT_ICON, IDC_BACKUP, IDOK, IDCANCEL }; // Do not show the [Backup] button on the msgina dialog if: // // 1. The default domain is not the local machine // 2. Over a terminal server session // 3. The user name is a UPN name (domain combo box also disabled but not by this fn) // BOOL ShowBackupButton(HWND hDlg, PGLOBALS pGlobals) { INT_PTR iItem; LPARAM lp; int cchBuffer; TCHAR* pszLogonName = NULL; HWND hwU = GetDlgItem(hDlg,IDD_CHANGEPWD_NAME); HWND hwD = GetDlgItem(hDlg,IDD_CHANGEPWD_DOMAIN); HWND hwB = GetDlgItem(hDlg,IDC_BACKUP); BOOL fEnable = TRUE; cchBuffer = (int)SendMessage(hwU, WM_GETTEXTLENGTH, 0, 0) + 1; pszLogonName = (TCHAR*) Alloc(cchBuffer * sizeof(TCHAR)); if (pszLogonName != NULL) { SendMessage(hwU, WM_GETTEXT, (WPARAM) cchBuffer, (LPARAM) pszLogonName); // turn off the button if the user is using a // UPN (if there is a "@") - ie foo@microsoft.com OR // domain\username fEnable = (NULL == wcspbrk(pszLogonName, TEXT("@\\"))); Free(pszLogonName); } if (fEnable) { // turn off button if is remote session fEnable = (0 == GetSystemMetrics(SM_REMOTESESSION)); } if (fEnable) { // turn off button if selected domain is not local machine if (hwD) { iItem = SendMessage(hwD,CB_GETCURSEL,0,0); if (LB_ERR != iItem) { // now window active and something selected fEnable = FALSE; lp = SendMessage(hwD, CB_GETITEMDATA,iItem,0); if ((LB_ERR != lp) && (0 != lp)) { if (DomainMachine == ((PDOMAIN_CACHE_ENTRY)lp)->Type) { fEnable = TRUE; } } } } } //EnableWindow(hwB,fEnable); if (fEnable) ShowWindow(hwB,SW_SHOWNORMAL); else ShowWindow(hwB,SW_HIDE); return fEnable; } BOOL NetworkProvidersPresent( VOID ) { HKEY ProviderKey; DWORD Error; DWORD ValueType; LPTSTR Value; BOOL NeedToNotify = TRUE; #define NET_PROVIDER_ORDER_KEY TEXT("system\\CurrentControlSet\\Control\\NetworkProvider\\Order") #define NET_PROVIDER_ORDER_VALUE TEXT("ProviderOrder") #define NET_ORDER_SEPARATOR TEXT(',') Error = RegOpenKeyEx( HKEY_LOCAL_MACHINE, // hKey NET_PROVIDER_ORDER_KEY, // lpSubKey 0, // Must be 0 KEY_QUERY_VALUE, // Desired access &ProviderKey // Newly Opened Key Handle ); if (Error == ERROR_SUCCESS) { Value = AllocAndRegQueryValueEx( ProviderKey, // Key NET_PROVIDER_ORDER_VALUE,// Value name NULL, // Must be NULL &ValueType // Type returned here ); if (Value != NULL) { if (ValueType == REG_SZ) { LPTSTR p = Value; while (*p) { if (*p == NET_ORDER_SEPARATOR) { break; } p = CharNext(p); } if (*p == 0) { // // We got to the end without finding a separator // Only one provider is installed. // #pragma prefast(suppress: 400, "PREfast noise: lstrcmpi") if (lstrcmpi(Value, SERVICE_WORKSTATION) == 0) { // // it's Lanman, don't notify // NeedToNotify = FALSE; } else { // // it isn't Lanman, notify // NeedToNotify = TRUE; } } } else { DebugLog((DEB_ERROR, "NoNeedToNotify - provider order key unexpected type: %d, assuming notification is necessary", ValueType)); } Free(Value); } else { DebugLog((DEB_ERROR, "NoNeedToNotify - failed to query provider order value, assuming notification is necessary\n")); } Error = RegCloseKey(ProviderKey); ASSERT(Error == ERROR_SUCCESS); } return NeedToNotify ; } BOOL ShowDomain( VOID ) { return (SafeBootMode != SAFEBOOT_MINIMAL); } /***************************************************************************\ * FUNCTION: ChangePassword * * PURPOSE: Attempts to change a user's password * * ARGUMENTS: * * hwnd - the most recent parent window * pGlobals - pointer to global data for this instance. * The password information of this data will be * updated upon successful change of the primary * authenticator's password information. * UserName - the name of the user to change * Domain - the domain name to change the password on * AnyDomain - if TRUE the user may select any trusted domain, or * enter the name of any other domain * * RETURNS: * * MSGINA_DLG_SUCCESS - the password was changed successfully. * MSGINA_DLG_FAILURE - the user's password could not be changed. * DLG_INTERRUPTED() - this is a set of possible interruptions (see winlogon.h) * * HISTORY: * * 12-09-91 Davidc Created. * \***************************************************************************/ INT_PTR ChangePassword( HWND hwnd, PGLOBALS pGlobals, PWCHAR UserName, PWCHAR Domain, ULONG Options ) { CHANGE_PASSWORD_DATA PasswordData; INT_PTR Result; HWND hwndOldFocus = GetFocus(); ULONG LocalOptions = 0 ; PasswordData.pGlobals = pGlobals; PasswordData.UserName = UserName; PasswordData.Domain = Domain; PasswordData.OldPassword = NULL; PasswordData.Impersonate = TRUE; PasswordData.AllowProviderOnly = TRUE; if ( NetworkProvidersPresent() ) { LocalOptions |= CHANGEPWD_OPTION_SHOW_NETPROV | CHANGEPWD_OPTION_SHOW_DOMAIN ; } if ( ShowDomain() ) { LocalOptions |= CHANGEPWD_OPTION_EDIT_DOMAIN | CHANGEPWD_OPTION_SHOW_DOMAIN ; } if ( SafeBootMode == SAFEBOOT_MINIMAL ) { LocalOptions = 0 ; } PasswordData.Options = (Options & LocalOptions); pWlxFuncs->WlxSetTimeout(pGlobals->hGlobalWlx, LOGON_TIMEOUT); Result = pWlxFuncs->WlxDialogBoxParam( pGlobals->hGlobalWlx, hDllInstance, MAKEINTRESOURCE(IDD_CHANGEPWD_DIALOG), hwnd, ChangePasswordDlgProc, (LPARAM)&PasswordData); SetFocus(hwndOldFocus); return(Result); } /***************************************************************************\ * FUNCTION: ChangePasswordLogon * * PURPOSE: Attempts to change a user's password during the logon process. * This is the same as a normal change password except that the user * does not have to enter the old password and can only change the * password in the specified domain. This routine is intended to be * called during logon when it is discovered that the user's * password has expired. * * ARGUMENTS: * * hwnd - the most recent parent window * pGlobals - pointer to global data for this instance * UserName - the name of the user to change * Domain - the domain name to change the password on * OldPassword - the old user password * NewPassword - points to a buffer that the new password is written * into if the password is changed successfully. * NewPasswordMaxBytes - the size of the newpassword buffer. * * RETURNS: * * MSGINA_DLG_SUCCESS - the password was changed successfully, NewPassword * contains the new password text. * MSGINA_DLG_FAILURE - the user's password could not be changed. * DLG_INTERRUPTED() - this is a set of possible interruptions (see winlogon.h) * * HISTORY: * * 12-09-91 Davidc Created. * \***************************************************************************/ INT_PTR ChangePasswordLogon( HWND hwnd, PGLOBALS pGlobals, PWCHAR UserName, PWCHAR Domain, PWCHAR OldPassword ) { CHANGE_PASSWORD_DATA PasswordData; INT_PTR Result; PasswordData.pGlobals = pGlobals; PasswordData.UserName = UserName; PasswordData.Domain = Domain; PasswordData.OldPassword = OldPassword; PasswordData.Options = CHANGEPWD_OPTION_NO_UPDATE ; PasswordData.Impersonate = FALSE; PasswordData.AllowProviderOnly = FALSE; if ( ShowDomain() ) { PasswordData.Options |= CHANGEPWD_OPTION_SHOW_DOMAIN | CHANGEPWD_OPTION_KEEP_ARRAY ; } pWlxFuncs->WlxSetTimeout(pGlobals->hGlobalWlx, LOGON_TIMEOUT); Result = pWlxFuncs->WlxDialogBoxParam( pGlobals->hGlobalWlx, hDllInstance, MAKEINTRESOURCE( IDD_CHANGEPWD_DIALOG ), hwnd, ChangePasswordDlgProc, (LPARAM)&PasswordData); return(Result); } /****************************************************************************\ * * FUNCTION: ChangePasswordDlgProc * * PURPOSE: Processes messages for ChangePassword dialog * * HISTORY: * * 12-09-91 Davidc Created. * \****************************************************************************/ INT_PTR WINAPI ChangePasswordDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) { PCHANGE_PASSWORD_DATA pPasswordData = (PCHANGE_PASSWORD_DATA)GetWindowLongPtr(hDlg, GWLP_USERDATA); PGLOBALS pGlobals; INT_PTR Result; switch (message) { case WM_INITDIALOG: { if (!ChangePasswordDlgInit(hDlg, lParam)) { EndDialog(hDlg, MSGINA_DLG_FAILURE); } return(SetPasswordFocus(hDlg)); } case WM_DESTROY: pGlobals = pPasswordData->pGlobals ; if ( pGlobals->ActiveArray && ((pPasswordData->Options & CHANGEPWD_OPTION_KEEP_ARRAY) == 0 ) ) { DCacheFreeArray( pGlobals->ActiveArray ); pGlobals->ActiveArray = NULL ; } FreeLayoutInfo(LAYOUT_CUR_USER); return( TRUE ); case WM_ERASEBKGND: return PaintBranding(hDlg, (HDC)wParam, 0, FALSE, FALSE, COLOR_BTNFACE); case WM_QUERYNEWPALETTE: return BrandingQueryNewPalete(hDlg); case WM_PALETTECHANGED: return BrandingPaletteChanged(hDlg, (HWND)wParam); case WM_SYSCOMMAND: if ( wParam == SC_CLOSE ) { EndDialog( hDlg, MSGINA_DLG_FAILURE ); return TRUE ; } break; case WM_COMMAND: { if (HIWORD(wParam) == CBN_SELCHANGE) { ShowBackupButton(hDlg,pPasswordData->pGlobals); return TRUE; } switch (LOWORD(wParam)) { case IDD_CHANGEPWD_NAME: switch (HIWORD(wParam)) { case EN_CHANGE: // Ensure the domain box is enabled/disabled correctly // in case of a UPN name if ( pPasswordData->Options & CHANGEPWD_OPTION_EDIT_DOMAIN ) { EnableDomainForUPN((HWND) lParam, GetDlgItem(hDlg,IDD_CHANGEPWD_DOMAIN)); ShowBackupButton(hDlg,pPasswordData->pGlobals); } return TRUE; default: break; } break; case IDC_BACKUP: { BOOL fWrongDomain = TRUE; PDOMAIN_CACHE_ENTRY Entry; HWND hwndDomain = GetDlgItem(hDlg,IDD_CHANGEPWD_DOMAIN); INT iDomainSelection = (INT)SendMessage(hwndDomain,CB_GETCURSEL,0,0); // Get the user's input. Decide if he has selected other than the local machine if (pPasswordData->Options & CHANGEPWD_OPTION_EDIT_DOMAIN) { // see if selected domain is local machine Entry = (PDOMAIN_CACHE_ENTRY)SendMessage(hwndDomain,CB_GETITEMDATA,iDomainSelection,0); // warning.... Entry can turn out to be ffffffff (CB_ERR) if (CB_ERR == (ULONG_PTR) Entry) { fWrongDomain = TRUE; } else if (NULL != Entry) { if (Entry->Type == DomainMachine) { fWrongDomain = FALSE; } } } else fWrongDomain = FALSE; // Show UI or message box if (fWrongDomain) { pGlobals = pPasswordData->pGlobals ; if (NULL == pGlobals) return TRUE; TimeoutMessageBox(hDlg, pGlobals, IDS_MBMWRONGDOMAIN, IDS_MBTWRONGDOMAIN, MB_OK | MB_ICONEXCLAMATION, TIMEOUT_CURRENT); return TRUE; } else { // standalone case // We use a single export from KEYMGR.DLL for this operation. When this operation completes, // we don't use the DLL again without unlikely user intervention. We could DELAYLOAD keymgr.dll, // but explicitly loading and unloading this DLL permits us to minimize the memory footprint of msgina. RUNDLLPROC fptr; HMODULE hDll; // hDll = LoadLibrary(L"keymgr.dll"); if (hDll) { fptr = (RUNDLLPROC) GetProcAddress(hDll,(LPCSTR)"PRShowSaveFromMsginaW"); if (fptr) { WCHAR szUser[UNLEN+1]; if (0 != SendMessage(GetDlgItem(hDlg,IDD_CHANGEPWD_NAME),WM_GETTEXT,UNLEN,(LPARAM)szUser)) fptr(hDlg,NULL,szUser,0); } FreeLibrary(hDll); } return TRUE; } // determine if this domain entered is not the local machine // if not, show a message box and bow out. } case IDOK: { pGlobals = pPasswordData->pGlobals; // // Deal with combo-box UI requirements // if (HandleComboBoxOK(hDlg, IDD_CHANGEPWD_DOMAIN)) { return(TRUE); } Result = AttemptPasswordChange(hDlg); // // Can't hurt to get the edit controls to forget their contents in // any case. It used to be done only in the failure case // SetDlgItemText(hDlg, IDD_CHANGEPWD_OLD, NULL ); SetDlgItemText(hDlg, IDD_CHANGEPWD_NEW, NULL ); SetDlgItemText(hDlg, IDD_CHANGEPWD_CONFIRM, NULL ); if (Result == MSGINA_DLG_FAILURE) { // // Let the user try again // We always make the user re-enter at least the new password. // SetPasswordFocus(hDlg); //EndDialog(hDlg, Result); return(TRUE); } // // We're finished - either success or an interrupt // EndDialog(hDlg, Result); return(TRUE); } case IDCANCEL: { SetDlgItemText(hDlg, IDD_CHANGEPWD_OLD, NULL ); SetDlgItemText(hDlg, IDD_CHANGEPWD_NEW, NULL ); SetDlgItemText(hDlg, IDD_CHANGEPWD_CONFIRM, NULL ); EndDialog(hDlg, MSGINA_DLG_FAILURE); return(TRUE); } break; } } case WLX_WM_SAS: { // Ignore it return(TRUE); } case WM_TIMER: { if (wParam == TIMER_MYLANGUAGECHECK) { LayoutCheckHandler(hDlg, LAYOUT_CUR_USER); } break; } } // We didn't process this message return FALSE; } /****************************************************************************\ * * FUNCTION: ChangePasswordDlgInit * * PURPOSE: Handles initialization of change password dialog * * RETURNS: TRUE on success, FALSE on failure * * HISTORY: * * 12-09-91 Davidc Created. * \****************************************************************************/ BOOL ChangePasswordDlgInit( HWND hDlg, LPARAM lParam ) { PCHANGE_PASSWORD_DATA pPasswordData = (PCHANGE_PASSWORD_DATA)lParam; PGLOBALS pGlobals = pPasswordData->pGlobals; // Store our structure pointer SetWindowLongPtr(hDlg, GWLP_USERDATA, lParam); // Size for the branding image we are going to add. SizeForBranding(hDlg, FALSE); // Set up the initial text field contents SetDlgItemText(hDlg, IDD_CHANGEPWD_NAME, pPasswordData->UserName); SetDlgItemText(hDlg, IDD_CHANGEPWD_OLD, pPasswordData->OldPassword); // Limit the maximum password length to 127 SendDlgItemMessage(hDlg, IDD_CHANGEPWD_OLD, EM_SETLIMITTEXT, (WPARAM) 127, 0); SendDlgItemMessage(hDlg, IDD_CHANGEPWD_NEW, EM_SETLIMITTEXT, (WPARAM) 127, 0); SendDlgItemMessage(hDlg, IDD_CHANGEPWD_CONFIRM, EM_SETLIMITTEXT, (WPARAM) 127, 0); // ShowBackupButton(hDlg,pPasswordData->pGlobals); moved to after populate domain list // If this is the domain case and we aren't force to hide the domain ui if (( pPasswordData->Options & CHANGEPWD_OPTION_SHOW_DOMAIN ) && (!ForceNoDomainUI())) { // If the user can choose their domain, fill the domain combobox // with the known domains and the local machine name. Otherwise // disable the domain combobox. if ( pPasswordData->Options & CHANGEPWD_OPTION_EDIT_DOMAIN ) { ASSERT( (pPasswordData->Options & CHANGEPWD_OPTION_KEEP_ARRAY) == 0 ); if ( !DCacheValidateCache( pGlobals->Cache ) ) { ASSERT( pGlobals->ActiveArray == NULL ); DCacheUpdateFull( pGlobals->Cache, pGlobals->Domain ); } pGlobals->ActiveArray = DCacheCopyCacheArray( pGlobals->Cache ); if ( pPasswordData->Options & CHANGEPWD_OPTION_SHOW_NETPROV ) { DCacheAddNetworkProviders( pGlobals->ActiveArray ); } if ( pGlobals->ActiveArray ) { // Fill combo box list, set domain type item data DCachePopulateListBoxFromArray( pGlobals->ActiveArray, GetDlgItem( hDlg, IDD_CHANGEPWD_DOMAIN ), NULL ); } else { EndDialog( hDlg, MSGINA_DLG_FAILURE ); } EnableDomainForUPN( GetDlgItem( hDlg, IDD_CHANGEPWD_NAME), GetDlgItem(hDlg,IDD_CHANGEPWD_DOMAIN) ); } else { SendDlgItemMessage(hDlg, IDD_CHANGEPWD_DOMAIN, CB_ADDSTRING, 0, (LPARAM)pPasswordData->Domain); SendDlgItemMessage(hDlg, IDD_CHANGEPWD_DOMAIN, CB_SETCURSEL, 0, 0); EnableDlgItem(hDlg, IDD_CHANGEPWD_DOMAIN, FALSE); } } else // workgroup case or we're forced to hide the domain UI { RECT rcDomain, rcUsername; // Hide the domain box ShowWindow(GetDlgItem(hDlg, IDD_CHANGEPWD_DOMAIN), SW_HIDE); ShowWindow(GetDlgItem(hDlg, IDD_CHANGEPWD_DOMAIN_LABEL), SW_HIDE); EnableDlgItem(hDlg, IDD_CHANGEPWD_DOMAIN, FALSE); // Shorten the window since the domain box isn't used GetWindowRect(GetDlgItem(hDlg, IDD_CHANGEPWD_NAME), &rcUsername); GetWindowRect(GetDlgItem(hDlg, IDD_CHANGEPWD_DOMAIN), &rcDomain); MoveControls(hDlg, ctrlNoDomain, ARRAYSIZE(ctrlNoDomain), 0, -(rcDomain.bottom-rcUsername.bottom), TRUE); } ShowBackupButton(hDlg,pPasswordData->pGlobals); DisplayLanguageIcon(hDlg, LAYOUT_CUR_USER, GetKeyboardLayout(0)); CentreWindow(hDlg); SetupSystemMenu(hDlg); return TRUE; } VOID UpdateWithChangedPassword( PGLOBALS pGlobals, HWND ActiveWindow, BOOL Hash, PWSTR UserName, PWSTR Domain, PWSTR Password, PWSTR NewPassword, PMSV1_0_INTERACTIVE_PROFILE NewProfile ) { WLX_MPR_NOTIFY_INFO MprInfo; int MprResult; PDOMAIN_CACHE_ENTRY Entry ; UNICODE_STRING String ; DWORD ChangeInfo = 0; HWND hwndOwner; PMSV1_0_CHANGEPASSWORD_REQUEST Request = NULL; ULONG RequestSize = 0; ULONG PackageId = 0; PVOID Response = NULL; ULONG ResponseSize; NTSTATUS SubStatus = STATUS_SUCCESS, Status = STATUS_SUCCESS; PBYTE Where; STRING Name; DWORD MaxPasswordAge ; LARGE_INTEGER Now ; LARGE_INTEGER EndOfPassword ; HANDLE ImpHandle ; BOOL InteractiveUser = FALSE; if (pGlobals->AutoAdminLogon) { if (IsAutologonUser(UserName, Domain)) { SetAutologonPassword(NewPassword); } } // // Determine if this is the interactive user // if ( (_wcsicmp( Domain, pGlobals->Domain ) == 0 ) && (_wcsicmp( UserName, pGlobals->UserName ) == 0 ) ) { InteractiveUser = TRUE ; } else if ( ( pGlobals->FlatDomain.Buffer ) && ( _wcsicmp( Domain, pGlobals->FlatDomain.Buffer ) == 0 ) && ( _wcsicmp( UserName, pGlobals->FlatUserName.Buffer ) == 0 ) ) { InteractiveUser = TRUE ; } else { // More complicated stuff for the domain\username NT4 style PWSTR BackSlash; if ((BackSlash = wcschr(pGlobals->UserName, L'\\')) != NULL) { // size of domain in domain\username ResponseSize = (ULONG)(BackSlash - pGlobals->UserName); if ((ResponseSize == (ULONG)wcslen(Domain)) && (_wcsnicmp(Domain, pGlobals->UserName, ResponseSize) == 0) && (_wcsicmp(UserName, BackSlash+1 ) == 0)) { InteractiveUser = TRUE ; } } } if ( InteractiveUser ) { // // Update the in-memory copy of the password for unlock. // RtlInitUnicodeString( &String, NewPassword ); if ( Hash ) { HashPassword( &String, pGlobals->PasswordHash ); } else { // // Don't hash the password away. This is only // set when the password is changed during logon. // (all the callers stored NewPassword in buffer of same length) // Erase the old password first as it might be shorter than the new one // It is still in cleartext at this point! ErasePassword( &pGlobals->PasswordString ); wcscpy( pGlobals->Password, NewPassword ); RtlInitUnicodeString( &pGlobals->PasswordString, pGlobals->Password); HidePassword( &pGlobals->Seed, &pGlobals->PasswordString); } // // Update password expiration time // if ( pGlobals->Profile ) { if ( NewProfile ) { pGlobals->Profile->PasswordMustChange = NewProfile->PasswordMustChange; } else { GetSystemTimeAsFileTime( (PFILETIME)&Now ); if ( GetMaxPasswordAge( Domain, &MaxPasswordAge ) == 0 ) { EndOfPassword.QuadPart = Now.QuadPart + (LONGLONG)MaxPasswordAge * (LONGLONG)10000000 ; } else { // // Compute the new expiration based on the last delta // EndOfPassword.QuadPart = pGlobals->Profile->PasswordMustChange.QuadPart - pGlobals->Profile->PasswordLastSet.QuadPart + Now.QuadPart; } // // Make sure we're not shortening the expiration time // if ( pGlobals->Profile->PasswordMustChange.QuadPart < EndOfPassword.QuadPart ) { pGlobals->Profile->PasswordMustChange.QuadPart = EndOfPassword.QuadPart; } } } // // Update the security packages: // RtlInitString( &Name, MSV1_0_PACKAGE_NAME ); Status = LsaLookupAuthenticationPackage( pGlobals->LsaHandle, &Name, &PackageId ); if ( NT_SUCCESS( Status ) ) { RequestSize = sizeof(MSV1_0_CHANGEPASSWORD_REQUEST) + (DWORD) (wcslen(UserName) + wcslen(Domain) + wcslen(NewPassword) + 3) * sizeof(WCHAR); Request = (PMSV1_0_CHANGEPASSWORD_REQUEST) LocalAlloc(LMEM_ZEROINIT,RequestSize); if ( Request ) { Where = (PBYTE) (Request + 1); Request->MessageType = MsV1_0ChangeCachedPassword; wcscpy( (LPWSTR) Where, Domain ); RtlInitUnicodeString( &Request->DomainName, (LPWSTR) Where ); Where += Request->DomainName.MaximumLength; wcscpy( (LPWSTR) Where, UserName ); RtlInitUnicodeString( &Request->AccountName, (LPWSTR) Where ); Where += Request->AccountName.MaximumLength; wcscpy( (LPWSTR) Where, NewPassword ); RtlInitUnicodeString( &Request->NewPassword, (LPWSTR) Where ); Where += Request->NewPassword.MaximumLength; // // Make the call // ImpHandle = ImpersonateUser( &pGlobals->UserProcessData, NULL ); if ( ImpHandle ) { Request->Impersonating = TRUE ; Status = LsaCallAuthenticationPackage( pGlobals->LsaHandle, PackageId, Request, RequestSize, &Response, &ResponseSize, &SubStatus ); StopImpersonating( ImpHandle ); } // this buffer contains passwords so we zeroize it before freeing it ZeroMemory(Request, RequestSize); LocalFree( Request ); if ( NT_SUCCESS( Status ) && ImpHandle ) { LsaFreeReturnBuffer( Response ); } } } } // // Let other providers know about the change // // // If the domain is one from our combo-box // then it is valid for logons. // if ( pGlobals->ActiveArray ) { RtlInitUnicodeString( &String, Domain ); Entry = DCacheSearchArray( pGlobals->ActiveArray, &String ); if ( (Entry) && (Entry->Type != DomainNetworkProvider) ) { ChangeInfo |= WN_VALID_LOGON_ACCOUNT ; } } // // Hide this dialog and pass our parent as the owner // of any provider dialogs // ShowWindow(ActiveWindow, SW_HIDE); hwndOwner = GetParent( ActiveWindow ); MprInfo.pszUserName = DupString(UserName); MprInfo.pszDomain = DupString(Domain); MprInfo.pszPassword = DupString(NewPassword); MprInfo.pszOldPassword = DupString(Password); MprResult = pWlxFuncs->WlxChangePasswordNotify( pGlobals->hGlobalWlx, &MprInfo, ChangeInfo | WN_NT_PASSWORD_CHANGED); } /****************************************************************************\ * * FUNCTION: AttemptPasswordChange * * PURPOSE: Tries to change the user's password using the current values in * the change-password dialog controls * * RETURNS: MSGINA_DLG_SUCCESS if the password was changed successfully. * MSGINA_DLG_FAILURE if the change failed * DLG_INTERRUPTED() - this is a set of possible interruptions (see winlogon.h) * * NOTES: If the password change failed, this routine displays the necessary * dialogs explaining what failed and why before returning. * This routine also clears the fields that need re-entry before * returning so the calling routine can call SetPasswordFocus on * the dialog to put the focus in the appropriate place. * * HISTORY: * * 12-09-91 Davidc Created. * \****************************************************************************/ void MyZeroMemory(PVOID lpv, SIZE_T size) { ZeroMemory(lpv, size); } INT_PTR AttemptPasswordChange( HWND hDlg ) { PCHANGE_PASSWORD_DATA pPasswordData = (PCHANGE_PASSWORD_DATA)GetWindowLongPtr(hDlg, GWLP_USERDATA); PGLOBALS pGlobals = pPasswordData->pGlobals; TCHAR UserName[MAX_STRING_BYTES]; TCHAR Domain[MAX_STRING_BYTES]; TCHAR Password[MAX_STRING_BYTES]; TCHAR NewPassword[MAX_STRING_BYTES]; TCHAR ConfirmNewPassword[MAX_STRING_BYTES]; INT_PTR Result; INT_PTR ReturnResult = MSGINA_DLG_SUCCESS; NTSTATUS Status; NTSTATUS SubStatus ; PDOMAIN_CACHE_ENTRY Entry ; PDOMAIN_CACHE_ENTRY Search ; UNICODE_STRING Domain_U ; ULONG Size ; HWND hwndDomain = GetDlgItem(hDlg, IDD_CHANGEPWD_DOMAIN); INT iDomainSelection = (INT)SendMessage(hwndDomain, CB_GETCURSEL, 0, 0); DOMAIN_PASSWORD_INFORMATION DomainInfo ; PWSTR UpnSuffix = NULL; BOOL RetryWithFlat = FALSE ; UserName[0] = TEXT('\0'); Domain[0] = TEXT('\0'); Password[0] = TEXT('\0'); NewPassword[0] = TEXT('\0'); ConfirmNewPassword[0] = TEXT('\0'); ZeroMemory( &DomainInfo, sizeof( DomainInfo ) ); GetDlgItemText(hDlg, IDD_CHANGEPWD_NAME, UserName, MAX_STRING_BYTES); if (wcschr(UserName, L'\\')) // Found a backslash { // wcscpy is OK since all buffers have the same size wcscpy(Domain, UserName); // domain\username in Domain UpnSuffix = wcschr(Domain, L'\\'); *UpnSuffix = 0; // domain in Domain UpnSuffix++; // points to username in Domain wcscpy(UserName, UpnSuffix); // username in Username // Force iDomainSelection to CB_ERR since the combo is disabled iDomainSelection = CB_ERR; // we'll use the UpnSuffix has a trigger below to remember // about the backslash } // // The selected domain may really be a special entry: the local machine // (this is also set in the logon path (expired password)) // if ( pPasswordData->Options & CHANGEPWD_OPTION_EDIT_DOMAIN ) { Entry = (PDOMAIN_CACHE_ENTRY) SendMessage( hwndDomain, CB_GETITEMDATA, iDomainSelection, 0 ); if ( CB_ERR == (ULONG_PTR) Entry ) { Entry = NULL ; } if ( Entry == NULL ) { if (NULL == UpnSuffix) { // // User typed in a new string, so there is no entry for this string. Create // an entry here, and use it later. // GetDlgItemText( hDlg, IDD_CHANGEPWD_DOMAIN, Domain, MAX_STRING_BYTES ); } //else Domain was already set above (user entered domain\username) RtlInitUnicodeString( &Domain_U, Domain ); Entry = DCacheCreateEntry( DomainNt4, &Domain_U, NULL, NULL ); } else { // // Maybe DNS, maybe not: // if ( Entry->Type == DomainNt5 ) { wcscpy( Domain, Entry->DnsName.Buffer ); RetryWithFlat = TRUE ; } else { wcscpy( Domain, Entry->FlatName.Buffer ); } // // Reference it here. The case above will create an entry with a reference // that we will need to deref when we're done. So, bump it now to make it // cleaner later. // DCacheReferenceEntry( Entry ); } } else { if (NULL == UpnSuffix) { // // Standalone case. Force the machine name entry // Size = MAX_STRING_BYTES ; GetDlgItemText( hDlg, IDD_CHANGEPWD_DOMAIN, Domain, MAX_STRING_BYTES ); // // If nothing there, use the domain from the logon: // if ( Domain[0] == L'\0' ) { wcscpy( Domain, pGlobals->Domain ); } } else { // // NT4 style name as detected above // // No need to do anything as Domain is already set. } RtlInitUnicodeString( &Domain_U, Domain ); Entry = DCacheCreateEntry( (NULL == UpnSuffix) ? DomainMachine : DomainNt4, &Domain_U, NULL, NULL ); } if ( !Entry ) { // No need to do cleanup here as we haven't read the passwords yet return DLG_FAILURE ; } GetDlgItemText(hDlg, IDD_CHANGEPWD_OLD, Password, MAX_STRING_BYTES); GetDlgItemText(hDlg, IDD_CHANGEPWD_NEW, NewPassword, MAX_STRING_BYTES); GetDlgItemText(hDlg, IDD_CHANGEPWD_CONFIRM, ConfirmNewPassword, MAX_STRING_BYTES); // If we are forcing a NoDomainUI, populate the domain with the local machine name now if ((NULL == UpnSuffix) && (ForceNoDomainUI())) { DWORD chSize = ARRAYSIZE(Domain); if (!GetComputerName(Domain, &chSize)) { *Domain = 0; } } // // If there is a at-sign in the name, assume that means that a UPN // attempt is being made. Set the domain to NULL. // if ( wcschr( UserName, L'@' ) ) { Domain[0] = TEXT('\0'); } // // Validate user entries: // // Check that new passwords match // if (lstrcmp(NewPassword, ConfirmNewPassword) != 0) { Result = TimeoutMessageBox(hDlg, pGlobals, IDS_NO_PASSWORD_CONFIRM, IDS_CHANGE_PASSWORD, MB_OK | MB_ICONEXCLAMATION, TIMEOUT_CURRENT); if (DLG_INTERRUPTED(Result)) { Result = SetInterruptFlag( MSGINA_DLG_FAILURE ); } else { Result = MSGINA_DLG_FAILURE ; } ReturnResult = Result; goto Exit; } if ( Domain[0] == L'\0' ) { UpnSuffix = wcschr( UserName, L'@' ); if ( UpnSuffix == NULL ) { Result = TimeoutMessageBox( hDlg, pGlobals, IDS_NO_DOMAIN_AND_NO_UPN, IDS_CHANGE_PASSWORD, MB_OK | MB_ICONEXCLAMATION, TIMEOUT_CURRENT ); if (DLG_INTERRUPTED(Result)) { Result = SetInterruptFlag( MSGINA_DLG_FAILURE ); } else { Result = MSGINA_DLG_FAILURE ; } ReturnResult = Result; goto Exit; } else { // // Ok, the UPN suffix is present. Check if it is part of an // MIT domain. MIT domains have the flat and DNS fields identical. // UpnSuffix++ ; Search = DCacheLocateEntry( pGlobals->Cache, UpnSuffix ); if ( Search ) { DCacheDereferenceEntry( Entry ); Entry = Search ; } } } // // Check if the password exceeds the LM limit of 14 characters. // if ( ( lstrlen( NewPassword ) > LM20_PWLEN ) && ( ( Entry->Type == DomainUPN ) || ( Entry->Type == DomainMachine ) || ( Entry->Type == DomainNt4 ) || ( Entry->Type == DomainNt5 ) ) ) { // // For long passwords, confirm with the user. // Result = TimeoutMessageBox( hDlg, pGlobals, IDS_LONG_PASSWORD_WARNING, IDS_CHANGE_PASSWORD, MB_OKCANCEL | MB_ICONEXCLAMATION, TIMEOUT_CURRENT ); if ( DLG_INTERRUPTED(Result) ) { Result = SetInterruptFlag( MSGINA_DLG_FAILURE ); } else { if ( Result == IDCANCEL ) { Result = MSGINA_DLG_FAILURE ; } } if ( ResultNoFlags( Result ) == MSGINA_DLG_FAILURE ) { ReturnResult = Result; goto Exit; } } // // Call the Appropriate Change Password Engine: // Status = ChangePasswordWorkers[ Entry->Type ]( pPasswordData, UserName, Domain, Password, NewPassword, &SubStatus, &DomainInfo ); if ( RetryWithFlat ) { // // If we just used the DNS name, restore the flat name, // since all later comparisons on the name for stored // password update will be based on this // wcscpy( Domain, Entry->FlatName.Buffer ); } if ( ( Status == STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) || ( Status == STATUS_CANT_ACCESS_DOMAIN_INFO ) ) { Status = ChangePasswordWorkers[ Entry->Type ]( pPasswordData, UserName, Domain, Password, NewPassword, &SubStatus, &DomainInfo ); } if ( NT_SUCCESS( Status ) ) { Result = TimeoutMessageBox(hDlg, pGlobals, IDS_PASSWORD_CHANGED, IDS_CHANGE_PASSWORD, MB_OK | MB_ICONINFORMATION, TIMEOUT_CURRENT); } else { ReturnResult = MSGINA_DLG_FAILURE; // // Failure, explain it to the user // Result = HandleFailedChangePassword(hDlg, pGlobals, Status, UserName, Domain, SubStatus, &DomainInfo ); } // // Only call other providers if the change password attempt succeeded. // if (NT_SUCCESS(Status)) { // // Update our own state: // UpdateWithChangedPassword( pGlobals, hDlg, (pPasswordData->Options & CHANGEPWD_OPTION_NO_UPDATE ? FALSE : TRUE ), UserName, Domain, Password, NewPassword, NULL ); } // // Find out what happened to the message box: // if ( Result != IDOK ) { // // mbox was interrupted // ReturnResult = SetInterruptFlag( ReturnResult ); } Exit: DCacheDereferenceEntry( Entry ); // Zeroize these buffers for obvious security reasons // Need to call this stub, otherwise the compiler optimize this out! MyZeroMemory(Password, sizeof(Password)); MyZeroMemory(NewPassword, sizeof(NewPassword)); MyZeroMemory(ConfirmNewPassword, sizeof(ConfirmNewPassword)); return(ReturnResult); } /****************************************************************************\ * * FUNCTION: HandleFailedChangePassword * * PURPOSE: Tells the user why their change-password attempt failed. * * RETURNS: MSGINA_DLG_FAILURE - we told them what the problem was successfully. * DLG_INTERRUPTED() - a set of return values - see winlogon.h * * HISTORY: * * 21-Sep-92 Davidc Created. * \****************************************************************************/ INT_PTR HandleFailedChangePassword( HWND hDlg, PGLOBALS pGlobals, NTSTATUS Status, PWCHAR UserName, PWCHAR Domain, NTSTATUS SubStatus, DOMAIN_PASSWORD_INFORMATION * DomainInfo ) { INT_PTR Result; DWORD Win32Error ; TCHAR* Buffer1 = NULL; TCHAR* Buffer2 = NULL; TCHAR* Buffer3 = NULL; LONGLONG OneDay; Buffer1 = (TCHAR*) Alloc(MAX_STRING_BYTES * sizeof(TCHAR)); Buffer2 = (TCHAR*) Alloc(MAX_STRING_BYTES * sizeof(TCHAR)); Buffer3 = (TCHAR*) Alloc(MAX_STRING_BYTES * sizeof(TCHAR)); if( (NULL == Buffer1) || (NULL == Buffer2) || (NULL == Buffer3) ) { return MSGINA_DLG_FAILURE; } Buffer1[ 0 ] = L'\0'; Buffer2[ 0 ] = L'\0'; Buffer3[ 0 ] = L'\0'; switch (Status) { case STATUS_CANT_ACCESS_DOMAIN_INFO: case STATUS_NO_SUCH_DOMAIN: LoadString(hDllInstance, IDS_CHANGE_PWD_NO_DOMAIN, Buffer1, MAX_STRING_BYTES); _snwprintf(Buffer2, MAX_STRING_BYTES - 1, Buffer1, Domain); Buffer2[MAX_STRING_BYTES - 1] = 0; LoadString(hDllInstance, IDS_CHANGE_PASSWORD, Buffer1, MAX_STRING_BYTES); Result = TimeoutMessageBoxlpstr(hDlg, pGlobals, Buffer2, Buffer1, MB_OK | MB_ICONEXCLAMATION, TIMEOUT_CURRENT); break; case STATUS_NO_SUCH_USER: case STATUS_WRONG_PASSWORD_CORE: case STATUS_WRONG_PASSWORD: Result = TimeoutMessageBox(hDlg, pGlobals, IDS_INCORRECT_NAME_OR_PWD_CHANGE, IDS_CHANGE_PASSWORD, MB_OK | MB_ICONEXCLAMATION, TIMEOUT_CURRENT); // Force re-entry of the old password if (GetWindowLong(GetDlgItem(hDlg, IDD_CHANGEPWD_OLD), GWL_STYLE) & WS_VISIBLE) { SetDlgItemText(hDlg, IDD_CHANGEPWD_OLD, NULL); } break; case STATUS_ACCESS_DENIED: Result = TimeoutMessageBox(hDlg, pGlobals, IDS_NO_PERMISSION_CHANGE_PWD, IDS_CHANGE_PASSWORD, MB_OK | MB_ICONEXCLAMATION, TIMEOUT_CURRENT); break; case STATUS_ACCOUNT_RESTRICTION: Result = TimeoutMessageBox(hDlg, pGlobals, IDS_ACCOUNT_RESTRICTION_CHANGE, IDS_CHANGE_PASSWORD, MB_OK | MB_ICONEXCLAMATION, TIMEOUT_CURRENT); break; case STATUS_BACKUP_CONTROLLER: Result = TimeoutMessageBox(hDlg, pGlobals, IDS_REQUIRES_PRIMARY_CONTROLLER, IDS_CHANGE_PASSWORD, MB_OK | MB_ICONEXCLAMATION, TIMEOUT_CURRENT); break; case STATUS_PASSWORD_RESTRICTION: if ( SubStatus == STATUS_UNSUCCESSFUL ) { LoadString(hDllInstance, IDS_GENERAL_PASSWORD_SPEC, Buffer2, MAX_STRING_BYTES); } else { if ( SubStatus == STATUS_ILL_FORMED_PASSWORD ) { LoadString(hDllInstance, IDS_COMPLEX_PASSWORD_SPEC, Buffer1, MAX_STRING_BYTES); } else { LoadString(hDllInstance, IDS_PASSWORD_SPEC, Buffer1, MAX_STRING_BYTES); } // this is the way filetimes are generated OneDay = (LONGLONG)(-10000000) * 60 * 60 * 24; _snwprintf(Buffer2, MAX_STRING_BYTES - 1, Buffer1, DomainInfo->MinPasswordLength, DomainInfo->PasswordHistoryLength, (LONG)(DomainInfo->MinPasswordAge.QuadPart / OneDay) ); Buffer2[MAX_STRING_BYTES - 1] = 0; } LoadString(hDllInstance, IDS_ENTER_PASSWORDS, Buffer1, MAX_STRING_BYTES); wcsncat(Buffer2, TEXT(" "), MAX_STRING_BYTES - lstrlen(Buffer2) - 1); wcsncat(Buffer2, Buffer1, MAX_STRING_BYTES - lstrlen(Buffer2) - 1); LoadString(hDllInstance, IDS_CHANGE_PASSWORD, Buffer1, MAX_STRING_BYTES ); Result = TimeoutMessageBoxlpstr(hDlg, pGlobals, Buffer2, Buffer1, MB_OK | MB_ICONEXCLAMATION, TIMEOUT_CURRENT); break; #ifdef LATER // // LATER Check for minimum password age // if ( FALSE ) { int PasswordAge = 0, RequiredAge = 0; TCHAR Buffer1[MAX_STRING_BYTES]; TCHAR Buffer2[MAX_STRING_BYTES]; LoadString(hDllInstance, IDS_PASSWORD_MINIMUM_AGE, Buffer1, sizeof(Buffer1) / sizeof( TCHAR )); _snwprintf(Buffer2, sizeof(Buffer2) / sizeof( TCHAR ), Buffer1, PasswordAge, RequiredAge); LoadString(hDllInstance, IDS_NO_PERMISSION_CHANGE_PWD, Buffer1, sizeof(Buffer1) / sizeof( TCHAR )); #_#_lstrcat(Buffer1, Buffer2); LoadString(hDllInstance, IDS_CHANGE_PASSWORD, Buffer2, sizeof(Buffer2) / sizeof( TCHAR )); Result = TimeoutMessageBoxlpstr(hDlg, pGlobals, Buffer1, Buffer2, MB_OK | MB_ICONEXCLAMATION, TIMEOUT_CURRENT); } #endif default: DebugLog((DEB_ERROR, "Change password failure status = 0x%lx\n", Status)); LoadString(hDllInstance, IDS_UNKNOWN_CHANGE_PWD_FAILURE, Buffer1, MAX_STRING_BYTES); Win32Error = RtlNtStatusToDosError( Status ); GetErrorDescription( Win32Error, Buffer3, MAX_STRING_BYTES ); _snwprintf(Buffer2, MAX_STRING_BYTES - 1, Buffer1, Win32Error, Buffer3 ); Buffer2[MAX_STRING_BYTES - 1] = 0; LoadString(hDllInstance, IDS_CHANGE_PASSWORD, Buffer1, MAX_STRING_BYTES); Result = TimeoutMessageBoxlpstr(hDlg, pGlobals, Buffer2, Buffer1, MB_OK | MB_ICONEXCLAMATION, TIMEOUT_CURRENT); break; } Free(Buffer1); Free(Buffer2); Free(Buffer3); return(Result); UNREFERENCED_PARAMETER(UserName); } BOOL IsAutologonUser(LPCTSTR szUser, LPCTSTR szDomain) { BOOL fIsUser = FALSE; HKEY hkey = NULL; TCHAR szAutologonUser[UNLEN + 1]; TCHAR szAutologonDomain[DNLEN + 1]; TCHAR szTempDomainBuffer[DNLEN + 1]; DWORD cbBuffer; DWORD dwType; *szTempDomainBuffer = 0; // Domain may be a null string. If this is the case... if (0 == *szDomain) { DWORD cchBuffer; // We really mean the local machine name // Point to our local buffer szDomain = szTempDomainBuffer; cchBuffer = ARRAYSIZE(szTempDomainBuffer); GetComputerName(szTempDomainBuffer, &cchBuffer); } // See if the domain and user name if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, WINLOGON_KEY, 0, KEY_READ, &hkey)) { // Check the user name cbBuffer = sizeof (szAutologonUser); if (ERROR_SUCCESS == RegQueryValueEx(hkey, DEFAULT_USER_NAME_KEY, 0, &dwType, (LPBYTE) szAutologonUser, &cbBuffer)) { // Does it match? #pragma prefast(suppress: 400, "PREfast noise: lstrcmpi") if (0 == lstrcmpi(szAutologonUser, szUser)) { // Yes. Now check domain cbBuffer = sizeof(szAutologonDomain); if (ERROR_SUCCESS == RegQueryValueEx(hkey, DEFAULT_DOMAIN_NAME_KEY, 0, &dwType, (LPBYTE) szAutologonDomain, &cbBuffer)) { // Make sure domain matches #pragma prefast(suppress: 400, "PREfast noise: lstrcmpi") if (0 == lstrcmpi(szAutologonDomain, szDomain)) { // Success - the users match fIsUser = TRUE; } } } } RegCloseKey(hkey); } return fIsUser; } NTSTATUS SetAutologonPassword(LPCWSTR szPassword) { NTSTATUS Status = STATUS_SUCCESS; OBJECT_ATTRIBUTES ObjectAttributes; LSA_HANDLE LsaHandle = NULL; UNICODE_STRING SecretName; UNICODE_STRING SecretValue; InitializeObjectAttributes(&ObjectAttributes, NULL, 0L, (HANDLE)NULL, NULL); Status = LsaOpenPolicy(NULL, &ObjectAttributes, POLICY_CREATE_SECRET, &LsaHandle); if (!NT_SUCCESS(Status)) return Status; RtlInitUnicodeString(&SecretName, DEFAULT_PASSWORD_KEY); RtlInitUnicodeString(&SecretValue, szPassword); Status = LsaStorePrivateData(LsaHandle, &SecretName, &SecretValue); LsaClose(LsaHandle); return Status; } NTSTATUS NtChangePassword( PCHANGE_PASSWORD_DATA pChangePasswordData, PWSTR UserName, PWSTR Domain, PWSTR OldPassword, PWSTR NewPassword, PNTSTATUS SubStatus, DOMAIN_PASSWORD_INFORMATION * DomainInfo ) { NTSTATUS Status ; NTSTATUS ProtocolStatus = STATUS_SUCCESS; PGLOBALS pGlobals = pChangePasswordData->pGlobals ; PMSV1_0_CHANGEPASSWORD_REQUEST pChangePasswordRequest = NULL; PMSV1_0_CHANGEPASSWORD_RESPONSE pChangePasswordResponse = NULL; PWCHAR DomainU; PWCHAR UserNameU; PWCHAR PasswordU; PWCHAR NewPasswordU; int Length; ULONG RequestBufferSize; ULONG ResponseBufferSize; HANDLE ImpersonationHandle = NULL; ULONG MsvPackage; STRING PackageName; // // Determine request buffer size needed, including room for // strings. Set string pointers to offsets as we step through // sizing each one. // RequestBufferSize = sizeof(*pChangePasswordRequest); UserNameU = UIntToPtr(RequestBufferSize); RequestBufferSize += (lstrlen(UserName)+1) * sizeof(WCHAR); DomainU = UIntToPtr(RequestBufferSize); RequestBufferSize += (lstrlen(Domain)+1) * sizeof(WCHAR); PasswordU = UIntToPtr(RequestBufferSize); RequestBufferSize += (lstrlen(OldPassword)+1) * sizeof(WCHAR); NewPasswordU = UIntToPtr(RequestBufferSize); RequestBufferSize += (lstrlen(NewPassword)+1) * sizeof(WCHAR); // // Allocate request buffer // pChangePasswordRequest = Alloc(RequestBufferSize); if (NULL == pChangePasswordRequest) { DebugLog((DEB_ERROR, "cannot allocate change password request buffer (%ld bytes).", RequestBufferSize)); return MSGINA_DLG_FAILURE; } // // Fixup string offsets to string pointers for request. // UserNameU = (PVOID) ((PBYTE)pChangePasswordRequest + (ULONG_PTR)UserNameU); DomainU = (PVOID) ((PBYTE)pChangePasswordRequest + (ULONG_PTR)DomainU); PasswordU = (PVOID) ((PBYTE)pChangePasswordRequest + (ULONG_PTR)PasswordU); NewPasswordU = (PVOID) ((PBYTE)pChangePasswordRequest + (ULONG_PTR)NewPasswordU); // // Setup MSV1_0ChangePassword request. // pChangePasswordRequest->MessageType = MsV1_0ChangePassword; // strings are already unicode, just copy them // lhb tracks //REVIEW lstrcpy((LPTSTR)UserNameU,UserName); lstrcpy((LPTSTR)DomainU,Domain); lstrcpy((LPTSTR)PasswordU,OldPassword); lstrcpy((LPTSTR)NewPasswordU,NewPassword); Length = lstrlen(UserName); UserNameU[Length] = 0; RtlInitUnicodeString( &pChangePasswordRequest->AccountName, UserNameU ); Length = lstrlen(Domain); DomainU[Length] = 0; RtlInitUnicodeString( &pChangePasswordRequest->DomainName, DomainU ); Length = lstrlen(OldPassword); PasswordU[Length] = 0; RtlInitUnicodeString( &pChangePasswordRequest->OldPassword, PasswordU ); Length = lstrlen(NewPassword); NewPasswordU[Length] = 0; RtlInitUnicodeString( &pChangePasswordRequest->NewPassword, NewPasswordU ); // // Make sure the passwords are short enough that we can run-encode them. // if ((pChangePasswordRequest->OldPassword.Length > 127 * sizeof( WCHAR ) ) || (pChangePasswordRequest->NewPassword.Length > 127 * sizeof( WCHAR ) )) { Status = STATUS_ILL_FORMED_PASSWORD; } else { HidePassword(NULL,&pChangePasswordRequest->OldPassword); HidePassword(NULL,&pChangePasswordRequest->NewPassword); Status = STATUS_SUCCESS ; } // // If that succeeded, try to change the password // if (NT_SUCCESS(Status)) { // // This could take some time, put up a wait cursor // SetupCursor(TRUE); // // Call off to the authentication package (MSV/NTLM) to do the work, This // is the NT change password function. The Kerb one calls the kerb package. // RtlInitString(&PackageName, MSV1_0_PACKAGE_NAME ); Status = LsaLookupAuthenticationPackage ( pGlobals->LsaHandle, &PackageName, &MsvPackage ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "Failed to find %s authentication package, status = 0x%lx", PackageName.Buffer, Status)); Status = MSGINA_DLG_FAILURE; goto Exit; } // // We want to impersonate if and only if the user is actually logged // on. Otherwise we'll be impersonating SYSTEM, which is bad. // if (pChangePasswordData->Impersonate) { ImpersonationHandle = ImpersonateUser( &pGlobals->UserProcessData, NULL ); if (NULL == ImpersonationHandle) { DebugLog((DEB_ERROR, "cannot impersonate user")); Status = MSGINA_DLG_FAILURE; goto Exit; } } // // Tell msv1_0 whether or not we're impersonating. // pChangePasswordRequest->Impersonating = (UCHAR)pChangePasswordData->Impersonate; Status = LsaCallAuthenticationPackage( pGlobals->LsaHandle, MsvPackage, pChangePasswordRequest, RequestBufferSize, (PVOID)&pChangePasswordResponse, &ResponseBufferSize, &ProtocolStatus ); if (pChangePasswordData->Impersonate) { if (!StopImpersonating(ImpersonationHandle)) { DebugLog((DEB_ERROR, "AttemptPasswordChange: Failed to revert to self")); // // Blow up // ASSERT(FALSE); } } // // Restore the normal cursor // SetupCursor(FALSE); } // // Get the most informative status code // if ( NT_SUCCESS(Status) ) { Status = ProtocolStatus; } else { DebugLog((DEB_TRACE, "FAILED in call to LsaCallAuthenticationPackage, status %x\n", Status )); } if (NT_SUCCESS(Status)) { // // Success // // // if they changed their logon password, update the // change time in their profile info so we don't keep // pestering them. // if ( (_wcsicmp( pGlobals->Domain, Domain ) == 0) && (_wcsicmp( pGlobals->UserName, UserName ) == 0 )) { // // This is code to handle the disconnected (preferred) domain. This // was to be devl-only code and removed eventually, but some customers // liked it so much, it stayed. // { HKEY Key ; int err ; PWSTR PreferredDomain ; DWORD Type ; DWORD Size ; NET_API_STATUS NetStatus ; err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Control\\Lsa\\MSV1_0"), 0, KEY_READ, &Key ); if ( err == 0 ) { Size = 0 ; err = RegQueryValueEx( Key, TEXT("PreferredDomain" ), NULL, &Type, NULL, &Size ); if ( err == 0 ) { PreferredDomain = LocalAlloc( LMEM_FIXED, Size ); if ( PreferredDomain ) { err = RegQueryValueEx( Key, TEXT("PreferredDomain"), NULL, &Type, (PBYTE) PreferredDomain, &Size ); if ( err == 0 ) { // // If we are logged on to our preferred domain, don't // do the update magic. // if ( _wcsicmp( PreferredDomain, pGlobals->Domain ) == 0 ) { err = 2 ; } } if ( err == 0 ) { NetStatus = NetUserChangePassword( PreferredDomain, UserName, OldPassword, NewPassword ); if ( NetStatus ) { DebugLog((DEB_ERROR, "Could not update password on %ws, %x\n", PreferredDomain, NetStatus )); } } LocalFree( PreferredDomain ); } } RegCloseKey( Key ); } } } } else { *SubStatus = STATUS_UNSUCCESSFUL ; if ( pChangePasswordResponse ) { if ( pChangePasswordResponse->PasswordInfoValid ) { *DomainInfo = pChangePasswordResponse->DomainPasswordInfo ; } } if ( Status == STATUS_PASSWORD_RESTRICTION ) { *SubStatus = STATUS_PASSWORD_RESTRICTION ; if ( pChangePasswordResponse->PasswordInfoValid ) { if ( pChangePasswordResponse->DomainPasswordInfo.PasswordProperties & DOMAIN_PASSWORD_COMPLEX ) { *SubStatus = STATUS_ILL_FORMED_PASSWORD ; } } } } // // Free up the return buffer // if (pChangePasswordResponse != NULL) { LsaFreeReturnBuffer(pChangePasswordResponse); } Exit: // // Free up the request buffer // if (pChangePasswordRequest) { // this buffer contains passwords so we zeroize it before freeing it ZeroMemory(pChangePasswordRequest, RequestBufferSize); Free(pChangePasswordRequest); } return Status ; } NTSTATUS MitChangePassword( PCHANGE_PASSWORD_DATA pChangePasswordData, PWSTR UserName, PWSTR DomainName, PWSTR OldPassword, PWSTR NewPassword, PNTSTATUS pSubStatus, DOMAIN_PASSWORD_INFORMATION * DomainInfo ) { PGLOBALS pGlobals = pChangePasswordData->pGlobals ; NTSTATUS Status; STRING Name; ULONG PackageId; PVOID Response = NULL ; ULONG ResponseSize; NTSTATUS SubStatus; PKERB_CHANGEPASSWORD_REQUEST ChangeRequest = NULL; ULONG ChangeSize = 0; UNICODE_STRING User,Domain,OldPass,NewPass; RtlInitString( &Name, MICROSOFT_KERBEROS_NAME_A ); Status = LsaLookupAuthenticationPackage( pGlobals->LsaHandle, &Name, &PackageId ); if (!NT_SUCCESS(Status)) { goto Cleanup; } RtlInitUnicodeString( &User, UserName ); RtlInitUnicodeString( &Domain, DomainName ); RtlInitUnicodeString( &OldPass, OldPassword ); RtlInitUnicodeString( &NewPass, NewPassword ); ChangeSize = ROUND_UP_COUNT(sizeof(KERB_CHANGEPASSWORD_REQUEST),4)+ User.Length + Domain.Length + OldPass.Length + NewPass.Length ; ChangeRequest = (PKERB_CHANGEPASSWORD_REQUEST) LocalAlloc(LMEM_ZEROINIT, ChangeSize ); if ( ChangeRequest == NULL ) { Status = STATUS_NO_MEMORY ; goto Cleanup ; } ChangeRequest->MessageType = KerbChangePasswordMessage; ChangeRequest->AccountName = User; ChangeRequest->AccountName.Buffer = (LPWSTR) ROUND_UP_POINTER(sizeof(KERB_CHANGEPASSWORD_REQUEST) + (PBYTE) ChangeRequest,4); RtlCopyMemory( ChangeRequest->AccountName.Buffer, User.Buffer, User.Length ); ChangeRequest->DomainName = Domain; ChangeRequest->DomainName.Buffer = ChangeRequest->AccountName.Buffer + ChangeRequest->AccountName.Length / sizeof(WCHAR); RtlCopyMemory( ChangeRequest->DomainName.Buffer, Domain.Buffer, Domain.Length ); ChangeRequest->OldPassword = OldPass; ChangeRequest->OldPassword.Buffer = ChangeRequest->DomainName.Buffer + ChangeRequest->DomainName.Length / sizeof(WCHAR); RtlCopyMemory( ChangeRequest->OldPassword.Buffer, OldPass.Buffer, OldPass.Length ); ChangeRequest->NewPassword = NewPass; ChangeRequest->NewPassword.Buffer = ChangeRequest->OldPassword.Buffer + ChangeRequest->OldPassword.Length / sizeof(WCHAR); RtlCopyMemory( ChangeRequest->NewPassword.Buffer, NewPass.Buffer, NewPass.Length ); // // We are running as the caller, so state we are impersonating // ChangeRequest->Impersonating = TRUE; Status = LsaCallAuthenticationPackage( pGlobals->LsaHandle, PackageId, ChangeRequest, ChangeSize, &Response, &ResponseSize, &SubStatus ); if (!NT_SUCCESS(Status) || !NT_SUCCESS(SubStatus)) { if (NT_SUCCESS(Status)) { Status = SubStatus; *pSubStatus = STATUS_UNSUCCESSFUL ; } else { *pSubStatus = SubStatus; } } Cleanup: if (Response != NULL) { LsaFreeReturnBuffer(Response); } if (ChangeRequest != NULL) { // this buffer contains passwords so we zeroize it before freeing it ZeroMemory(ChangeRequest, ChangeSize); LocalFree(ChangeRequest); } return(Status); } NTSTATUS ProviderChangePassword( PCHANGE_PASSWORD_DATA pChangePasswordData, PWSTR UserName, PWSTR Domain, PWSTR OldPassword, PWSTR NewPassword, PNTSTATUS SubStatus, DOMAIN_PASSWORD_INFORMATION * DomainInfo ) { WLX_MPR_NOTIFY_INFO MprInfo; DWORD Result ; PGLOBALS pGlobals = pChangePasswordData->pGlobals ; MprInfo.pszUserName = DupString( UserName ); MprInfo.pszDomain = DupString( Domain ); MprInfo.pszOldPassword = DupString( OldPassword ); MprInfo.pszPassword = DupString( NewPassword ); // // Hide this dialog and pass our parent as the owner // of any provider dialogs // Result = pWlxFuncs->WlxChangePasswordNotifyEx( pGlobals->hGlobalWlx, &MprInfo, 0, Domain, NULL ); return STATUS_SUCCESS ; }