|
|
/****************************** 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 <stdio.h>
#include <wchar.h>
#include <align.h>
#include <keymgr.h>
#include <netlib.h>
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 [email protected]
fEnable = (NULL == wcschr(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.
//
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);
if (Result == MSGINA_DLG_FAILURE) { //
// Let the user try again
// We always make the user re-enter at least the new password.
//
SetDlgItemText(hDlg, IDD_CHANGEPWD_OLD, NULL ); SetDlgItemText(hDlg, IDD_CHANGEPWD_NEW, NULL ); SetDlgItemText(hDlg, IDD_CHANGEPWD_CONFIRM, NULL );
SetPasswordFocus(hDlg);
//EndDialog(hDlg, Result);
return(TRUE); }
//
// We're finished - either success or an interrupt
//
EndDialog(hDlg, Result); return(TRUE); }
case IDCANCEL: { 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 ;
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 { InteractiveUser = FALSE ; }
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.
//
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 { if ( GetMaxPasswordAge( Domain, &MaxPasswordAge ) == 0 ) { GetSystemTimeAsFileTime( (PFILETIME)&Now ); EndOfPassword.QuadPart = Now.QuadPart + (LONGLONG)MaxPasswordAge * (LONGLONG)10000000 ;
//
// 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 ); }
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. * \****************************************************************************/
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 ; 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);
//
// The selected domain may really be a special entry: the local machine
//
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 ) { //
// 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 );
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 { //
// 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 ); }
RtlInitUnicodeString( &Domain_U, Domain );
Entry = DCacheCreateEntry( DomainMachine, &Domain_U, NULL, NULL ); }
if ( !Entry ) { 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 (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 ; }
DCacheDereferenceEntry( Entry );
return(Result); }
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 ; } return(Result); } 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 ) { DCacheDereferenceEntry( Entry );
return(Result); }
}
//
// 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 ); }
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[MAX_STRING_BYTES]; TCHAR Buffer2[MAX_STRING_BYTES]; TCHAR Buffer3[MAX_STRING_BYTES];
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, sizeof(Buffer1) / sizeof(TCHAR));
_snwprintf(Buffer2, sizeof(Buffer2) / sizeof(TCHAR), Buffer1, Domain);
LoadString(hDllInstance, IDS_CHANGE_PASSWORD, Buffer1, sizeof(Buffer1) / sizeof(TCHAR));
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, sizeof(Buffer2) / sizeof( TCHAR )); } else {
if ( SubStatus == STATUS_ILL_FORMED_PASSWORD ) { LoadString(hDllInstance, IDS_COMPLEX_PASSWORD_SPEC, Buffer1, sizeof(Buffer1) / sizeof( TCHAR )); } else { LoadString(hDllInstance, IDS_PASSWORD_SPEC, Buffer1, sizeof(Buffer1) / sizeof( TCHAR )); } _snwprintf(Buffer2, sizeof(Buffer2) / sizeof( TCHAR ), Buffer1, DomainInfo->MinPasswordLength, DomainInfo->PasswordHistoryLength ); }
LoadString(hDllInstance, IDS_ENTER_PASSWORDS, Buffer1, sizeof(Buffer1) / sizeof( TCHAR )); wcsncat(Buffer2, TEXT(" "), sizeof(Buffer2) - sizeof(TCHAR)*(lstrlen(Buffer2) - 1)); wcsncat(Buffer2, Buffer1, sizeof(Buffer2) - sizeof(TCHAR)*(lstrlen(Buffer2) - 1));
LoadString(hDllInstance, IDS_CHANGE_PASSWORD, Buffer1, sizeof(Buffer1) / sizeof( TCHAR ) );
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, sizeof(Buffer1) / sizeof( TCHAR ));
Win32Error = RtlNtStatusToDosError( Status );
GetErrorDescription( Win32Error, Buffer3, sizeof( Buffer3 ) / sizeof(TCHAR) );
_snwprintf(Buffer2, sizeof(Buffer2) / sizeof( TCHAR ), Buffer1, Win32Error, Buffer3 );
LoadString(hDllInstance, IDS_CHANGE_PASSWORD, Buffer1, sizeof(Buffer1) / sizeof( TCHAR ));
Result = TimeoutMessageBoxlpstr(hDlg, pGlobals, Buffer2, Buffer1, MB_OK | MB_ICONEXCLAMATION, TIMEOUT_CURRENT); break; }
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?
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
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);
//
// 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")); Free(pChangePasswordRequest); return MSGINA_DLG_FAILURE; } }
//
// Tell msv1_0 whether or not we're impersonating.
//
pChangePasswordRequest->Impersonating = (UCHAR)pChangePasswordData->Impersonate;
//
// 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));
return( MSGINA_DLG_FAILURE ); }
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); }
//
// Free up the request buffer
//
Free(pChangePasswordRequest);
//
// 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); }
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; 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) { 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 ;
}
|