You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2409 lines
73 KiB
2409 lines
73 KiB
/****************************** 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] 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 ;
|
|
|
|
}
|