windows-nt/Source/XPSP1/NT/ds/security/gina/msgina/chngepwd.c
2020-09-26 16:20:57 +08:00

2302 lines
67 KiB
C

/****************************** 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 foo@microsoft.com
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 ;
}