Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4915 lines
149 KiB

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1993.
//
// File: mslogon.c
//
// Contents: Microsoft Logon GUI DLL
//
// History: 7-14-94 RichardW Created
//
//----------------------------------------------------------------------------
#include "msgina.h"
#include "shtdnp.h"
#include "authmon.h"
#include <stdio.h>
#include <wchar.h>
#include <wincrypt.h>
#include <sclogon.h>
#include "shlwapi.h"
#include "shlwapip.h"
#include "winsta.h"
#include "wtsapi32.h"
#include <keymgr.h>
#include <passrec.h>
typedef void (WINAPI *RUNDLLPROC)(HWND hWndStub,HINSTANCE hInstance,LPWSTR szCommandLine,int nShow);
typedef struct _MSGINA_LOGON_PARAMETERS {
PGLOBALS pGlobals;
DWORD SasType;
} MSGINA_LOGON_PARAMETERS, * PMSGINA_LOGON_PARAMETERS ;
#define WINSTATIONS_DISABLED TEXT("WinStationsDisabled")
//
// Number of seconds we will display the legal notices
// before timing out.
//
#define LEGAL_NOTICE_TIMEOUT 120
#define LOGON_SLEEP_PERIOD 750
#define WM_LOGONPROMPT WM_USER + 257
#define WM_LOGONCOMPLETE WM_USER + 258
#define WM_HANDLEFAILEDLOGON WM_USER + 259
#define WM_DCACHE_COMPLETE WM_USER + 260
#define MAX_CAPTION_LENGTH 256
// Maximum size of a UPN name we allow at present
#define MAX_UPN_NAME_SIZE 520
typedef struct FAILEDLOGONINFO_t
{
PGLOBALS pGlobals;
NTSTATUS Status;
NTSTATUS SubStatus;
TCHAR UserName[UNLEN + DNLEN + 2];
TCHAR Domain[DNLEN + 1];
} FAILEDLOGONINFO, *PFAILEDLOGONINFO;
typedef struct _LEGALINFO
{
LPTSTR NoticeText;
LPTSTR CaptionText;
} LEGALINFO, *PLEGALINFO;
// Also defined in wstrpc.c
#define INET_CONNECTOR_EVENT_NAME L"Global\\TermSrvInetConnectorEvent"
#define TERMSERV_EVENTSOURCE L"TermService"
// Also defined in icaevent.mc
#define EVENT_BAD_TSINTERNET_USER 1007
//
// Globals:
//
static WNDPROC OldCBWndProc;
HICON hSteadyFlag;
HICON hWavingFlag;
HICON hAuditFull;
extern HICON hLockedIcon;
BOOL IsPswBackupAvailable;
BOOL s_fAttemptedAutoLogon;
BOOL g_fHelpAssistantLogon = FALSE;
//
// Prototypes:
//
INT_PTR
DisplayLegalNotices(
PGLOBALS pGlobals
);
BOOL
GetLegalNotices(
LPTSTR lpSubKey,
LPTSTR *NoticeText,
LPTSTR *CaptionText
);
INT_PTR WINAPI
LogonDlgCBProc(
HWND hDlg,
UINT message,
WPARAM wParam,
LPARAM lParam
);
INT_PTR WINAPI
LogonDlgUsernameProc(
HWND hDlg,
UINT message,
WPARAM wParam,
LPARAM lParam
);
INT_PTR WINAPI
LogonDlgProc(
HWND hDlg,
UINT message,
WPARAM wParam,
LPARAM lParam
);
BOOL
LogonDlgInit(
HWND hDlg,
BOOL bAutoLogon,
DWORD SasType
);
NTSTATUS
UpnFromCert(
IN PCCERT_CONTEXT pCert,
IN OUT DWORD *pcUpn,
IN OUT LPWSTR pUPN
);
BOOL
WINAPI
QuerySwitchConsoleCredentials(
PGLOBALS pGlobals,
HANDLE * phUserToken,
PLUID pLogonId);
// Global structure for a failed logon filled in by the worker thread to be consumed
// by the ui thread.
FAILEDLOGONINFO g_failinfo;
void PostFailedLogonMessage(HWND hDlg,
PGLOBALS pGlobals,
NTSTATUS Status,
NTSTATUS SubStatus,
PWCHAR UserName,
PWCHAR Domain
);
INT_PTR
HandleFailedLogon(
HWND hDlg
);
VOID
ReportBootGood(
PGLOBALS pGlobals
);
VOID LogonShowOptions(
PGLOBALS pGlobals,
HWND hDlg,
BOOL fShow,
BOOL fSticky);
VOID AttemptLogonSetControls(
PGLOBALS pGlobals,
HWND hDlg
);
INT_PTR
AttemptLogon(
HWND hDlg
);
DWORD
AttemptLogonThread(
PGLOBALS pGlobals
);
BOOL
GetAndAllocateLogonSid(
HANDLE hToken,
PSID *pLogonSid
);
BOOL GetSessionZeroUser(LPTSTR szUser);
BOOL FastUserSwitchingEnabled();
//
// control tables for showing/hiding options
//
static UINT ctrlNoShutdown[] =
{
IDOK,
IDCANCEL,
};
static UINT ctrlNoCancel[] =
{
IDOK,
};
static UINT ctrlNoDomain[] =
{
IDOK,
IDCANCEL,
IDD_LOGON_SHUTDOWN,
IDD_LOGON_OPTIONS,
IDD_LOGON_RASBOX,
IDD_KBLAYOUT_ICON,
};
static UINT ctrlNoRAS[] =
{
IDOK,
IDCANCEL,
IDD_LOGON_SHUTDOWN,
IDD_LOGON_OPTIONS,
IDD_KBLAYOUT_ICON,
};
static UINT ctrlNoOptions[] =
{
IDOK,
IDCANCEL,
IDD_LOGON_SHUTDOWN,
IDD_KBLAYOUT_ICON,
};
static UINT ctrlNoLegalBanner[] =
{
IDD_LOGON_NAME_LABEL,
IDD_LOGON_NAME,
IDD_LOGON_PASSWORD_LABEL,
IDD_LOGON_PASSWORD,
IDD_LOGON_DOMAIN_LABEL,
IDD_LOGON_DOMAIN,
IDD_LOGON_RASBOX,
IDD_KBLAYOUT_ICON,
IDOK,
IDCANCEL,
IDD_LOGON_SHUTDOWN,
IDD_LOGON_OPTIONS,
};
static UINT ctrlNoUserName[] =
{
IDD_LOGON_PASSWORD_LABEL,
IDD_LOGON_PASSWORD,
IDD_LOGON_DOMAIN_LABEL,
IDD_LOGON_DOMAIN,
IDD_LOGON_RASBOX,
IDD_KBLAYOUT_ICON,
IDOK,
IDCANCEL,
IDD_LOGON_SHUTDOWN,
IDD_LOGON_OPTIONS,
};
// --------------------------------------------------------------------------
// ::DisableEditSubClassProc
//
// Arguments: hwnd = See the platform SDK under WindowProc.
// uMsg = See the platform SDK under WindowProc.
// wParam = See the platform SDK under WindowProc.
// lParam = See the platform SDK under WindowProc.
// uiID = ID assigned at subclass time.
// dwRefData = reference data assigned at subclass time.
//
// Returns: LRESULT
//
// Purpose: comctl32 subclass callback function. This allows us to not
// process WM_CUT/WM_COPY/WM_PASTE/WM_CLEAR/WM_UNDO and any
// other messages to be discarded.
//
// History: 2001-02-18 vtan created
// --------------------------------------------------------------------------
LRESULT CALLBACK DisableEditSubClassProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uiID, DWORD_PTR dwRefData)
{
LRESULT lResult;
switch (uMsg)
{
case WM_CUT:
case WM_COPY:
case WM_PASTE:
case WM_CLEAR:
case WM_UNDO:
case WM_CONTEXTMENU:
lResult = FALSE;
break;
default:
lResult = DefSubclassProc(hwnd, uMsg, wParam, lParam);
break;
}
return(lResult);
}
INT_PTR WINAPI
LegalDlgProc(
HWND hDlg,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
switch (message)
{
case WM_INITDIALOG:
{
PLEGALINFO pLegalInfo;
pLegalInfo = (PLEGALINFO) lParam;
SetWindowText (hDlg, pLegalInfo->CaptionText);
SetWindowText (GetDlgItem(hDlg, IDD_LEGALTEXT), pLegalInfo->NoticeText);
CentreWindow(hDlg);
// Ensure the window is topmost so it's not obscured by the welcome screen.
SetWindowPos(hDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
return( TRUE );
}
case WM_COMMAND:
{
if (LOWORD(wParam) == IDOK)
{
EndDialog(hDlg, IDOK);
}
}
break;
}
return FALSE;
}
/***************************************************************************\
* FUNCTION: DisplayLegalNotices
*
* PURPOSE: Puts up a dialog box containing legal notices, if any.
*
* RETURNS: MSGINA_DLG_SUCCESS - the dialog was shown and dismissed successfully.
* MSGINA_DLG_FAILURE - the dialog could not be shown
* DLG_INTERRUPTED() - a set defined in winlogon.h
*
* HISTORY:
*
* Robertre 6-30-93 Created
*
\***************************************************************************/
INT_PTR
DisplayLegalNotices(
PGLOBALS pGlobals
)
{
INT_PTR Result = MSGINA_DLG_SUCCESS;
LPTSTR NoticeText;
LPTSTR CaptionText;
LEGALINFO LegalInfo;
if (GetLegalNotices( WINLOGON_POLICY_KEY, &NoticeText, &CaptionText ))
{
LegalInfo.NoticeText = NoticeText;
LegalInfo.CaptionText = CaptionText;
_Shell_LogonStatus_Hide();
pWlxFuncs->WlxSetTimeout(pGlobals->hGlobalWlx, LEGAL_NOTICE_TIMEOUT);
Result = pWlxFuncs->WlxDialogBoxParam( pGlobals->hGlobalWlx,
hDllInstance,
(LPTSTR) IDD_LEGALMSG,
NULL,
LegalDlgProc,
(LPARAM) &LegalInfo );
_Shell_LogonStatus_Show();
Free( NoticeText );
Free( CaptionText );
}
else if (GetLegalNotices( WINLOGON_KEY, &NoticeText, &CaptionText ))
{
LegalInfo.NoticeText = NoticeText;
LegalInfo.CaptionText = CaptionText;
_Shell_LogonStatus_Hide();
pWlxFuncs->WlxSetTimeout(pGlobals->hGlobalWlx, LEGAL_NOTICE_TIMEOUT);
Result = pWlxFuncs->WlxDialogBoxParam( pGlobals->hGlobalWlx,
hDllInstance,
(LPTSTR) IDD_LEGALMSG,
NULL,
LegalDlgProc,
(LPARAM) &LegalInfo );
_Shell_LogonStatus_Show();
Free( NoticeText );
Free( CaptionText );
}
return( Result );
}
/***************************************************************************\
* FUNCTION: GetLegalNotices
*
* PURPOSE: Get legal notice information out of the registry.
*
* RETURNS: TRUE - Output parameters contain valid data
* FALSE - No data returned.
*
* HISTORY:
*
* Robertre 6-30-93 Created
*
\***************************************************************************/
BOOL
GetLegalNotices(
LPTSTR lpSubKey,
LPTSTR *NoticeText,
LPTSTR *CaptionText
)
{
LPTSTR lpCaption, lpText;
HKEY hKey;
DWORD dwSize, dwType, dwMaxSize = 0;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpSubKey,
0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
RegQueryInfoKey (hKey, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, &dwMaxSize,
NULL, NULL);
lpCaption = Alloc (dwMaxSize);
if (!lpCaption) {
RegCloseKey(hKey);
return FALSE;
}
lpText = Alloc (dwMaxSize);
if (!lpText) {
Free(lpCaption);
RegCloseKey(hKey);
return FALSE;
}
dwSize = dwMaxSize;
RegQueryValueEx(hKey, LEGAL_NOTICE_CAPTION_KEY,
0, &dwType, (LPBYTE)lpCaption, &dwSize);
dwSize = dwMaxSize;
RegQueryValueEx(hKey, LEGAL_NOTICE_TEXT_KEY,
0, &dwType, (LPBYTE)lpText, &dwSize);
RegCloseKey(hKey);
if (*lpCaption && *lpText) {
*CaptionText = lpCaption;
*NoticeText = lpText;
return TRUE;
}
Free(lpCaption);
Free(lpText);
}
return FALSE;
}
/***************************************************************************\
* FUNCTION: Logon
*
* PURPOSE: Display the logon UI depending on the SAS type.
*
* RETURNS: -
*
* NOTES: If the logon is successful, the global structure is filled in
* with the logon information.
*
* HISTORY:
* 12-09-91 daviddv Comments.
*
\***************************************************************************/
INT_PTR
Logon(
PGLOBALS pGlobals,
DWORD SasType
)
{
INT_PTR Result;
MSGINA_LOGON_PARAMETERS Parm ;
if ( SasType == WLX_SAS_TYPE_SC_REMOVE )
{
return WLX_SAS_ACTION_NONE ;
}
if( !g_Console )
{
//
// Check if current session is HelpAssistant Session, HelpAssisant
// session can not be console session.
//
g_fHelpAssistantLogon = WinStationIsHelpAssistantSession(
SERVERNAME_CURRENT,
LOGONID_CURRENT
);
}
if ( SasType == WLX_SAS_TYPE_SC_INSERT )
{
PWLX_SC_NOTIFICATION_INFO ScInfo = NULL ;
pWlxFuncs->WlxGetOption( pGlobals->hGlobalWlx,
WLX_OPTION_SMART_CARD_INFO,
(ULONG_PTR *) &ScInfo );
//
// Validate the SC info against some common user
// errors before the PIN dialog appears
//
if ( ScInfo )
{
if ( ( ScInfo->pszReader ) &&
( ScInfo->pszCard == NULL ) )
{
//
// The card could not be read. Might not be
// inserted correctly.
//
LocalFree(ScInfo);
TimeoutMessageBox( NULL, pGlobals, IDS_CARD_NOT_RECOGNIZED,
IDS_LOGON_MESSAGE,
MB_OK | MB_ICONEXCLAMATION,
LOGON_TIMEOUT );
return WLX_SAS_ACTION_NONE;
}
if ( ( ScInfo->pszReader ) &&
( ScInfo->pszCryptoProvider == NULL ) )
{
//
// Got a card, but the CSP for it could not be
// found.
//
LocalFree(ScInfo);
TimeoutMessageBox( NULL, pGlobals, IDS_CARD_CSP_NOT_RECOGNIZED,
IDS_LOGON_MESSAGE,
MB_OK | MB_ICONEXCLAMATION,
LOGON_TIMEOUT );
return WLX_SAS_ACTION_NONE;
}
LocalFree(ScInfo);
}
}
//
// Asynchronously update domain cache if necessary.
// We won't ask to wait so this routine will do no UI.
// i.e. we can ignore the result.
//
// Result = UpdateDomainCache(pGlobals, NULL, FALSE);
// ASSERT(!DLG_INTERRUPTED(Result));
if( !g_fHelpAssistantLogon ) {
//
// See if there are legal notices in the registry.
// If so, put them up in a message box
//
Result = DisplayLegalNotices( pGlobals );
if ( Result != MSGINA_DLG_SUCCESS ) {
return(WLX_SAS_ACTION_NONE);
}
//
// Get the latest audit log status and store in our globals
// If the audit log is full we show a different logon dialog.
//
GetAuditLogStatus(pGlobals);
} else {
//
// fake it so audit log is not full, setting is from GetAuditLogStatus()
//
pGlobals->AuditLogFull = FALSE;
pGlobals->AuditLogNearFull = FALSE;
}
Parm.pGlobals = pGlobals ;
Parm.SasType = SasType ;
//
// Take their username and password and try to log them on
//
pWlxFuncs->WlxSetTimeout(pGlobals->hGlobalWlx,
( (GetDisableCad(pGlobals) && IsActiveConsoleSession()) ? TIMEOUT_NONE : LOGON_TIMEOUT));
Result = pWlxFuncs->WlxDialogBoxParam(pGlobals->hGlobalWlx,
hDllInstance,
MAKEINTRESOURCE(IDD_LOGON_DIALOG),
NULL,
LogonDlgProc,
(LPARAM) &Parm );
if ( Result == WLX_SAS_ACTION_LOGON )
{
if ( (pGlobals->SmartCardOption == 0) || (!pGlobals->SmartCardLogon) )
{
// As no action will be taken on SC removal, we can filter these events
pWlxFuncs->WlxSetOption( pGlobals->hGlobalWlx,
WLX_OPTION_USE_SMART_CARD,
0,
NULL );
}
else
{
//
// Continue to monitor the s/c device
//
NOTHING ;
}
}
return(Result);
}
/***************************************************************************\
* FUNCTION: LogonDlgCBProc
*
* PURPOSE: Processes messages for Logon dialog combo box
*
* RETURNS: Return value depends on message being sent.
*
* HISTORY:
*
* 05-21-93 RobertRe Created.
*
\***************************************************************************/
INT_PTR WINAPI
LogonDlgCBProc(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
TCHAR KeyPressed;
// DbgPrint("message = %X\n",message);
switch (message) {
case WM_CHAR:
{
KeyPressed = (TCHAR) wParam;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG)KeyPressed);
//
// This fake CBN_SELCHANGE message will cause the
// "Please wait..." dialog box to appear even if
// the character pressed doesn't exist in the combobox yet.
//
PostMessage (GetParent(hwnd), WM_COMMAND,
MAKELONG(0, CBN_SELCHANGE), 0);
break;
}
}
return CallWindowProc(OldCBWndProc,hwnd,message,wParam,lParam);
}
INT_PTR
CALLBACK
DomainCacheDlgProc(
HWND hDlg,
UINT Message,
WPARAM wParam,
LPARAM lParam
)
{
PGLOBALS pGlobals ;
DebugLog(( DEB_TRACE_DOMAIN, "DomainCacheDlgProc( %p, %x, %p, %p )\n",
hDlg, Message, wParam, lParam ));
switch ( Message )
{
case WM_INITDIALOG:
pGlobals = (PGLOBALS) lParam ;
if ( DCacheSetNotifyWindowIfNotReady(
pGlobals->Cache,
hDlg,
WM_DCACHE_COMPLETE ) )
{
EndDialog( hDlg, TRUE );
}
return TRUE ;
case WM_DCACHE_COMPLETE:
EndDialog( hDlg, TRUE );
return TRUE ;
default:
return FALSE ;
}
}
BOOL IsAutoLogonUserInteractiveLogonRestricted (HWND hDlg)
{
WCHAR szUsername[UNLEN + 1]; // sizeof('\0')
return((GetDlgItemText(hDlg, IDD_LOGON_NAME, szUsername, ARRAYSIZE(szUsername)) != 0) &&
!ShellIsUserInteractiveLogonAllowed(szUsername));
}
BOOL HasDefaultPassword (TCHAR *pszPassword, int cchPassword)
{
DWORD dwType, dwPasswordSize;
dwType = REG_NONE;
dwPasswordSize = cchPassword * sizeof(TCHAR);
return(ERROR_SUCCESS == RegQueryValueEx(WinlogonKey,
DEFAULT_PASSWORD_KEY,
NULL,
&dwType,
(LPBYTE)pszPassword,
&dwPasswordSize) &&
(REG_SZ == dwType));
}
// ==========================================================================================
// Logon dialog has 2 formats, one that looks like logon dialog box, another that looks like
// unlock desktop dialogbox. When user connects to session 0 from remote (tsclient) the
// dialog that appears at console // need to change to unlock computer. so if session 0 is in
// use, and if this session is created at active console. we change logon dialog to look like
// "unlock computer" dialog.
// This function SwitchLogonLocked does most of the stuff related to switching these
// dialog controls.
// Parameters:
// HWND hDlg - dialog window handle,
// BOOL bShowLocked - if true show locked dialog, if false show normal logon dialog.
// BOOL bInit - TRUE when this function is called for the first time.
// ==========================================================================================
static bLocked = TRUE;
BOOL IsthisUnlockWindowsDialog ()
{
return bLocked;
}
BOOL SwitchLogonLocked(HWND hDlg, BOOL bShowLocked, BOOL bInit)
{
UINT rgidLockControls[] = {IDC_GROUP_UNLOCK, IDD_UNLOCK_ICON, IDD_UNLOCK_MESSAGE, IDD_UNLOCK_NAME_INFO};
static LockedControlHeight = 0;
BOOL bShutdownWithoutLogon;
int i;
if (bShowLocked == bLocked && !bInit)
{
// nothing to do.
return TRUE;
}
if (bInit)
{
{
//
// remember the reference rectangle height (groupbox) for control movements.
//
RECT rectLockedControls;
HWND hWnd = GetDlgItem(hDlg, rgidLockControls[0]);
GetWindowRect(hWnd, &rectLockedControls);
LockedControlHeight = rectLockedControls.bottom - rectLockedControls.top;
//
// this group box was only for reference, now hide it forever.
//
ShowWindow(hWnd, SW_HIDE);
}
bLocked = TRUE;
if ( !hLockedIcon )
{
hLockedIcon = LoadImage( hDllInstance,
MAKEINTRESOURCE( IDI_LOCKED),
IMAGE_ICON,
0, 0,
LR_DEFAULTCOLOR );
}
SendMessage( GetDlgItem( hDlg, IDD_UNLOCK_ICON),
STM_SETICON,
(WPARAM)hLockedIcon,
0 );
}
// lets move controls arround, depending upon if lock controls are to be shown or not.
if (bLocked != bShowLocked)
{
if (bShowLocked)
{
MoveChildren(hDlg, 0, LockedControlHeight);
for ( i = 1; i < sizeof(rgidLockControls)/sizeof(rgidLockControls[0]); i++)
{
HWND hWnd = GetDlgItem(hDlg, rgidLockControls[i]);
ASSERT(hWnd);
EnableWindow(hWnd, TRUE);
ShowWindow(hWnd, SW_SHOW);
}
}
else
{
for ( i = 1; i < sizeof(rgidLockControls)/sizeof(rgidLockControls[0]); i++)
{
HWND hWnd = GetDlgItem(hDlg, rgidLockControls[i]);
ASSERT(hWnd);
ShowWindow(hWnd, SW_HIDE);
EnableWindow(hWnd, FALSE);
}
MoveChildren(hDlg, 0, -LockedControlHeight);
}
}
// some more processing
{
if (bShowLocked)
{
TCHAR szUser[USERNAME_LENGTH + DOMAIN_LENGTH + 2];
TCHAR szMessage[MAX_STRING_BYTES];
TCHAR szFinalMessage[MAX_STRING_BYTES];
if (GetSessionZeroUser(szUser))
{
LoadString(hDllInstance, IDS_LOCKED_EMAIL_NFN_MESSAGE, szMessage, MAX_STRING_BYTES);
_snwprintf(szFinalMessage, sizeof(szFinalMessage)/sizeof(TCHAR), szMessage, szUser );
}
else
{
//
// for some reason we could not get the current session zero user.
//
LoadString(hDllInstance, IDS_LOCKED_NO_USER_MESSAGE, szFinalMessage, MAX_STRING_BYTES);
}
SetDlgItemText(hDlg, IDD_UNLOCK_NAME_INFO, szFinalMessage);
}
//
// update the dialog box caption, accordingly
//
{
TCHAR szCaption[MAX_CAPTION_LENGTH];
LoadString(hDllInstance, bShowLocked ? IDS_CAPTION_UNLOCK_DIALOG : IDS_CAPTION_LOGON_DIALOG, szCaption, ARRAYSIZE(szCaption));
if ( szCaption[0] != TEXT('\0') )
SetWindowText( hDlg, szCaption );
}
}
bLocked = bShowLocked;
if ( SafeBootMode == SAFEBOOT_MINIMAL )
{
bShutdownWithoutLogon = TRUE ;
}
else if (IsthisUnlockWindowsDialog() || !IsActiveConsoleSession())
{
bShutdownWithoutLogon = FALSE ;
}
else
{
bShutdownWithoutLogon = ReadWinlogonBoolValue(SHUTDOWN_WITHOUT_LOGON_KEY, TRUE);
}
EnableDlgItem(hDlg, IDD_LOGON_SHUTDOWN, bShutdownWithoutLogon);
InvalidateRect(hDlg, NULL, TRUE);
return TRUE;
}
/***************************************************************************\
* FUNCTION: LogonDlgProc
*
* PURPOSE: Processes messages for Logon dialog
*
* RETURNS: MSGINA_DLG_SUCCESS - the user was logged on successfully
* MSGINA_DLG_FAILURE - the logon failed,
* DLG_INTERRUPTED() - a set defined in winlogon.h
*
* HISTORY:
*
* 12-09-91 Davidc Created.
*
\***************************************************************************/
#define WM_HIDEOURSELVES (WM_USER + 1000)
INT_PTR WINAPI
LogonDlgProc(
HWND hDlg,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
PGLOBALS pGlobals = (PGLOBALS)GetWindowLongPtr(hDlg, GWLP_USERDATA);
INT_PTR Result;
HWND CBHandle;
BOOL fDisconnectOnTsAutoLogonFailure = FALSE;
static BOOL bSessionZeroInUse = FALSE;
static int iSessionRegistrationCount = 0;
static BOOL bSmartCardInserted = FALSE;
switch (message)
{
case WM_INITDIALOG:
{
TCHAR PasswordBuffer[127];
BOOL bAutoLogon;
PMSGINA_LOGON_PARAMETERS pParam ;
pParam = (PMSGINA_LOGON_PARAMETERS) lParam ;
pGlobals = pParam->pGlobals ;
SetWindowLongPtr(hDlg, GWLP_USERDATA, (LPARAM)pGlobals);
// Hide the keyboard accelerator keys to start
SendMessage(hDlg, WM_CHANGEUISTATE, MAKELONG(UIS_SET, UISF_HIDEACCEL | UISF_HIDEFOCUS), 0);
// Limit the maximum password length to 127
SendDlgItemMessage(hDlg, IDD_LOGON_PASSWORD, EM_SETLIMITTEXT, (WPARAM) 127, 0);
s_fAttemptedAutoLogon = FALSE;
//
// Check if auto logon is enabled.
//
pGlobals->AutoAdminLogon = GetProfileInt( APPLICATION_NAME, AUTO_ADMIN_LOGON_KEY, 0 ) != 0;
bAutoLogon = !pGlobals->IgnoreAutoAdminLogon;
if ( !pGlobals->AutoAdminLogon || (!g_Console) ||
((GetAsyncKeyState(VK_SHIFT) < 0) && (GetProfileInt( APPLICATION_NAME, IGNORE_SHIFT_OVERRIDE_KEY, 0 ) == 0)) )
{
bAutoLogon = FALSE;
}
KdPrint(("AutoAdminLogon = %d, IgnoreAutoAdminLogon = %d, bAutoLogon = %d\n",
pGlobals->AutoAdminLogon,
pGlobals->IgnoreAutoAdminLogon,
bAutoLogon ));
//
// Subclass the domain list control so we can filter messages
//
CBHandle = GetDlgItem(hDlg,IDD_LOGON_DOMAIN);
SetWindowLongPtr(CBHandle, GWLP_USERDATA, 0);
OldCBWndProc = (WNDPROC) SetWindowLongPtr(CBHandle, GWLP_WNDPROC, (LONG_PTR)LogonDlgCBProc);
//
// Subclass the user name and password edit also so we can disable edits
//
SetWindowSubclass(GetDlgItem(hDlg, IDD_LOGON_NAME) , DisableEditSubClassProc, IDD_LOGON_NAME , 0);
SetWindowSubclass(GetDlgItem(hDlg, IDD_LOGON_PASSWORD), DisableEditSubClassProc, IDD_LOGON_PASSWORD, 0);
ShellReleaseLogonMutex(FALSE);
if (!LogonDlgInit(hDlg, bAutoLogon, pParam->SasType ))
{
bSmartCardInserted = FALSE;
EndDialog(hDlg, MSGINA_DLG_FAILURE);
return(TRUE);
}
//
// If the default user for auto logon is present and the user is
// restricted (interactive logon denied) then disable auto logon.
//
if (bAutoLogon && IsAutoLogonUserInteractiveLogonRestricted(hDlg))
{
bAutoLogon = FALSE;
}
//
// If CAD is disabled, then gray out the Cancel button
//
if (GetDisableCad(pGlobals) && IsActiveConsoleSession())
EnableDlgItem(hDlg, IDCANCEL, FALSE);
//
// this dialog has 2 formats, one that looks like logon dialog box,
// another that looks like unlock desktop dialogbox.
// we choose locked one, if session 0 is in use, and if this session is created at
// active console.
//
if (g_IsTerminalServer &&
IsActiveConsoleSession() &&
NtCurrentPeb()->SessionId != 0 &&
!FastUserSwitchingEnabled() &&
!_ShellIsFriendlyUIActive())
{
TCHAR szUser[USERNAME_LENGTH + DOMAIN_LENGTH + 2];
//
// we are at temporary session created at console...
//
// check if a user is logged on at console session
bSessionZeroInUse = GetSessionZeroUser(szUser);
if (WinStationRegisterConsoleNotification(SERVERNAME_CURRENT, hDlg, NOTIFY_FOR_ALL_SESSIONS))
{
iSessionRegistrationCount++;
}
}
else
{
//
// this is not active console nonzero session.
//
bSessionZeroInUse = FALSE;
}
//
//
// now switch the control, accordigly to show logon or unlock dialog
//
SwitchLogonLocked(hDlg, bSessionZeroInUse, TRUE);
if (g_IsTerminalServer) {
BOOL fForceUPN;
BOOL fPopulateFields = TRUE;
BOOL fResult = FALSE;
PWLX_CLIENT_CREDENTIALS_INFO_V2_0 pAutoLogon;
//
// Query network WinStation client credentials for
// auto logon
//
pGlobals->MuGlobals.pAutoLogon =
LocalAlloc( LPTR, sizeof(WLX_CLIENT_CREDENTIALS_INFO_V2_0) );
if (pGlobals->MuGlobals.pAutoLogon) {
pGlobals->MuGlobals.pAutoLogon->dwType = WLX_CREDENTIAL_TYPE_V2_0;
if (NtCurrentPeb()->SessionId != 0) {
fResult = pWlxFuncs->WlxQueryTsLogonCredentials(
pGlobals->MuGlobals.pAutoLogon
);
}
if ( fResult &&
(pGlobals->MuGlobals.pAutoLogon->pszUserName[0] || pGlobals->MuGlobals.pAutoLogon->pszDomain[0] )) {
pAutoLogon = pGlobals->MuGlobals.pAutoLogon;
fDisconnectOnTsAutoLogonFailure = pAutoLogon->fDisconnectOnLogonFailure;
SetupCursor(TRUE); // hourglass cursor
fForceUPN = GetProfileInt( APPLICATION_NAME, TEXT("TSForceUPN"), FALSE );
if (fForceUPN)
{
fPopulateFields = FALSE; // never show old SAM style is UPN is forced
}
if (pAutoLogon->pszDomain[0] == TEXT('\0') && fForceUPN)
{
fForceUPN = FALSE; // domain name not provided, can't apply policy
}
if (fForceUPN && pGlobals->MuGlobals.pAutoLogon->pszUserName[0] )
{
LRESULT iDomain;
HWND hwndDomain;
PDOMAIN_CACHE_ENTRY Entry ;
ULONG nSize;
// Performance issue. We don't want to perform a UPN conversion
// for local machine accounts (or unknown domains). When this
// happens, the lookup will take a LONG time.
hwndDomain = GetDlgItem( hDlg, IDD_LOGON_DOMAIN );
iDomain = SendMessage( hwndDomain,
CB_FINDSTRING,
(WPARAM) -1,
(LPARAM) pAutoLogon->pszDomain );
fForceUPN = FALSE; // don't do the conversion
if (iDomain != CB_ERR)
{
Entry = (PDOMAIN_CACHE_ENTRY) SendMessage( hwndDomain, CB_GETITEMDATA, (WPARAM)iDomain, 0);
if ( Entry != (PDOMAIN_CACHE_ENTRY) CB_ERR && Entry != NULL)
{
switch (Entry->Type)
{
case DomainNt5:
fForceUPN = TRUE; // Attempt the conversion
break;
}
}
}
// Convert the domain\username into UPN format.
// and make sure the dialog is in UPN form.
// 2000/10/09 vtan: this function used to have two stack variables
// szOldStyle and szUPNName that were TCHARs of MAX_UPN_NAME_SIZE size. The
// fix for this makes these dynamically allocated to save stack space
{
TCHAR *pszOldStyle;
TCHAR *pszUPNName;
pszOldStyle = (TCHAR*)LocalAlloc(LMEM_FIXED, MAX_UPN_NAME_SIZE * sizeof(TCHAR));
pszUPNName = (TCHAR*)LocalAlloc(LMEM_FIXED, MAX_UPN_NAME_SIZE * sizeof(TCHAR));
if ((pszOldStyle != NULL) && (pszUPNName != NULL))
{
wsprintf(pszOldStyle, TEXT("%s\\%s"), pAutoLogon->pszDomain, pAutoLogon->pszUserName);
nSize = MAX_PATH;
fResult = TranslateName(
pszOldStyle,
NameSamCompatible,
NameUserPrincipal,
pszUPNName,
&nSize
);
if (fResult)
{
// We now have the UPN form of the user account.
SetDlgItemText( hDlg, IDD_LOGON_NAME, pszUPNName);
}
}
if (pszOldStyle != NULL)
{
LocalFree(pszOldStyle);
}
if (pszUPNName != NULL)
{
LocalFree(pszUPNName);
}
}
}
if (fPopulateFields)
{
// display the old SAM style
SetDlgItemText( hDlg, IDD_LOGON_NAME, pAutoLogon->pszUserName );
SendMessage( GetDlgItem( hDlg, IDD_LOGON_DOMAIN ),
CB_SELECTSTRING,
(WPARAM) -1,
(LPARAM) pAutoLogon->pszDomain );
}
else
{
// Enable or disable the domain box depending on whether a UPN name has been typed
EnableDomainForUPN(GetDlgItem(hDlg, IDD_LOGON_NAME), GetDlgItem(hDlg, IDD_LOGON_DOMAIN));
// Since we're forcing UPN, hide the options dialog, but don't make it sticky
LogonShowOptions(pGlobals, hDlg, FALSE, FALSE);
}
// See if the administrator always wants password prompting
if ( TRUE == g_fHelpAssistantLogon || !pAutoLogon->fPromptForPassword ) {
SetDlgItemText( hDlg, IDD_LOGON_PASSWORD, pAutoLogon->pszPassword );
}
DCacheSetDefaultEntry(
pGlobals->Cache,
pAutoLogon->pszDomain,
NULL );
if( TRUE == g_fHelpAssistantLogon || !pGlobals->MuGlobals.pAutoLogon->fPromptForPassword )
{
FreeAutoLogonInfo( pGlobals );
// Drop through as if Enter had been pressed...
wParam = IDOK;
goto go_logon;
}
else
{
FreeAutoLogonInfo( pGlobals );
}
}
else
{
FreeAutoLogonInfo( pGlobals );
}
}
}
if (pGlobals->SmartCardLogon) {
if ( GetProfileString(APPLICATION_NAME, TEXT("DefaultPIN"), TEXT(""), PasswordBuffer, ARRAYSIZE(PasswordBuffer)) != 0 )
{
// Ensure we never write more than 127 chars into the password box
PasswordBuffer[126] = 0;
SetDlgItemText(hDlg, IDD_LOGON_PASSWORD, PasswordBuffer);
goto go_logon;
}
}
// save off the auto logon attempt.
s_fAttemptedAutoLogon = (bAutoLogon != FALSE);
if (bAutoLogon)
{
GetWindowRect(hDlg, &pGlobals->rcDialog);
SetWindowPos(hDlg, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
PostMessage(hDlg, WM_HIDEOURSELVES, 0, 0);
}
else
{
switch (_Shell_LogonDialog_Init(hDlg, SHELL_LOGONDIALOG_LOGGEDOFF))
{
case SHELL_LOGONDIALOG_NONE:
default:
{
//
// If auto logon isn't enabled, set the focus to the
// password edit control and leave.
//
return(SetPasswordFocus(hDlg));
}
case SHELL_LOGONDIALOG_LOGON:
{
GetWindowRect(hDlg, &pGlobals->rcDialog);
SetWindowPos(hDlg, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
PostMessage(hDlg, WM_HIDEOURSELVES, 0, 0);
goto go_logon;
}
case SHELL_LOGONDIALOG_EXTERNALHOST:
{
return(TRUE);
}
}
}
//
// Attempt to auto logon. If no default password
// specified, then this is a one shot attempt, which handles
// the case when auto logging on as Administrator.
//
if (HasDefaultPassword(PasswordBuffer, ARRAYSIZE(PasswordBuffer)) != FALSE)
{
// Ensure we never write more than 127 chars into the password box
PasswordBuffer[126] = 0;
SetDlgItemText(hDlg, IDD_LOGON_PASSWORD, PasswordBuffer);
}
else
{
NTSTATUS Status = STATUS_SUCCESS;
OBJECT_ATTRIBUTES ObjectAttributes;
LSA_HANDLE LsaHandle = NULL;
UNICODE_STRING SecretName;
PUNICODE_STRING SecretValue = NULL;
//
// Set up the object attributes to open the Lsa policy object
//
InitializeObjectAttributes(&ObjectAttributes,
NULL,
0L,
(HANDLE)NULL,
NULL);
//
// Open the local LSA policy object
//
Status = LsaOpenPolicy( NULL,
&ObjectAttributes,
POLICY_VIEW_LOCAL_INFORMATION,
&LsaHandle
);
if (NT_SUCCESS(Status)) {
RtlInitUnicodeString(
&SecretName,
DEFAULT_PASSWORD_KEY
);
Status = LsaRetrievePrivateData(
LsaHandle,
&SecretName,
&SecretValue
);
if (NT_SUCCESS(Status)) {
if ( SecretValue->Length > 0 ) {
//
// If the password fits in the buffer, copy it there
// and null terminate
//
if (SecretValue->Length < sizeof(PasswordBuffer) - sizeof(WCHAR)) {
RtlCopyMemory(
PasswordBuffer,
SecretValue->Buffer,
SecretValue->Length
);
PasswordBuffer[SecretValue->Length/sizeof(WCHAR)] = L'\0';
SetDlgItemText(hDlg, IDD_LOGON_PASSWORD, PasswordBuffer);
} else {
Status = STATUS_INVALID_PARAMETER;
}
}
LsaFreeMemory(SecretValue);
}
LsaClose(LsaHandle);
}
if (!NT_SUCCESS(Status))
{
WriteProfileString( APPLICATION_NAME, AUTO_ADMIN_LOGON_KEY, TEXT("0") );
}
}
go_logon:
// Drop through as if Enter had been pressed...
wParam = IDOK;
}
// nb: deliberate drop through from above
case WM_COMMAND:
switch (HIWORD(wParam))
{
case CBN_DROPDOWN:
case CBN_SELCHANGE:
DebugLog((DEB_TRACE, "Got CBN_DROPDOWN\n"));
if ( !pGlobals->ListPopulated )
{
WCHAR Buffer[ 2 ];
if ( DCacheGetCacheState( pGlobals->Cache ) < DomainCacheRegistryCache )
{
pWlxFuncs->WlxDialogBoxParam(
pGlobals->hGlobalWlx,
hDllInstance,
(LPTSTR) IDD_WAITDOMAINCACHEVALID_DIALOG,
hDlg,
DomainCacheDlgProc,
(LPARAM) pGlobals );
}
if ( DCacheGetCacheState( pGlobals->Cache ) == DomainCacheReady )
{
PDOMAIN_CACHE_ARRAY ActiveArrayBackup ;
ActiveArrayBackup = pGlobals->ActiveArray;
pGlobals->ActiveArray = DCacheCopyCacheArray( pGlobals->Cache );
if ( pGlobals->ActiveArray )
{
DCacheFreeArray( ActiveArrayBackup ); // Not needed anymore
Buffer[ 0 ] = (WCHAR) GetWindowLongPtr( GetDlgItem( hDlg, IDD_LOGON_DOMAIN ),
GWLP_USERDATA );
Buffer[ 1 ] = L'\0';
DCachePopulateListBoxFromArray(
pGlobals->ActiveArray,
GetDlgItem( hDlg, IDD_LOGON_DOMAIN ),
Buffer );
pGlobals->ListPopulated = TRUE ;
}
else
{
//
// Restore the old array, otherwise the pointers in the
// combo items will point to freed memory
//
pGlobals->ActiveArray = ActiveArrayBackup ;
}
}
}
break;
default:
switch (LOWORD(wParam))
{
case IDD_LOGON_NAME:
{
switch(HIWORD(wParam))
{
case EN_CHANGE:
{
EnableDomainForUPN((HWND) lParam, GetDlgItem(hDlg, IDD_LOGON_DOMAIN));
return TRUE;
}
}
}
break;
case IDOK:
//
// Deal with combo-box UI requirements
//
if (HandleComboBoxOK(hDlg, IDD_LOGON_DOMAIN))
{
return(TRUE);
}
Result = AttemptLogon( hDlg );
if (Result == MSGINA_DLG_FAILURE)
{
if (!fDisconnectOnTsAutoLogonFailure &&
!g_fHelpAssistantLogon ) {
// Let the user try again
// Clear the password field and set focus to it
SetDlgItemText(hDlg, IDD_LOGON_PASSWORD, NULL);
SetPasswordFocus(hDlg);
} else {
bSmartCardInserted = FALSE;
EndDialog(hDlg, MSGINA_DLG_USER_LOGOFF);
}
return(TRUE);
}
return(TRUE);
case IDCANCEL:
{
if (!_Shell_LogonDialog_Cancel())
{
// If this is TS and the user hit ESC at the smart card pin prompt
// we want to switch to the password dialog
if (/*!g_Console && !IsActiveConsoleSession() && */pGlobals->SmartCardLogon) {
EndDialog(hDlg, bSmartCardInserted ? MSGINA_DLG_SMARTCARD_REMOVED : MSGINA_DLG_FAILURE);
bSmartCardInserted = FALSE;
return TRUE;
}
//
// Allow logon screen to go away if not at console
//
bSmartCardInserted = FALSE;
EndDialog(hDlg, !g_Console ? MSGINA_DLG_USER_LOGOFF
: MSGINA_DLG_FAILURE);
if (g_Console && !IsActiveConsoleSession()) {
pWlxFuncs->WlxDisconnect();
}
}
return(TRUE);
}
case IDD_LOGON_SHUTDOWN:
//
// This is a normal shutdown request
//
// Check they know what they're doing and find
// out if they want to reboot too.
//
// Note that we definitely don't want disconnect or logofff
// here since no one is logged on
Result = WinlogonShutdownDialog(hDlg, pGlobals, (SHTDN_DISCONNECT | SHTDN_LOGOFF));
if (DLG_SHUTDOWN(Result))
{
_Shell_LogonDialog_ShuttingDown();
bSmartCardInserted = FALSE;
EndDialog(hDlg, Result);
}
return(TRUE);
case IDD_LOGON_OPTIONS:
LogonShowOptions(pGlobals, hDlg, !pGlobals->LogonOptionsShown, TRUE);
return(TRUE);
}
break;
}
break;
case WM_TIMER:
{
switch (wParam)
{
case 0:
{
HDC hDC;
RtlEnterCriticalSection(&pGlobals->csGlobals);
if ( pGlobals->LogonInProgress )
{
if (pGlobals->cxBand != 0)
{
pGlobals->xBandOffset = (pGlobals->xBandOffset+5) % pGlobals->cxBand;
}
}
else
{
pGlobals->xBandOffset = 0;
KillTimer(hDlg, 0);
}
RtlLeaveCriticalSection(&pGlobals->csGlobals);
hDC = GetDC(hDlg);
if ( hDC )
{
PaintBranding(hDlg, hDC, pGlobals->xBandOffset, TRUE, TRUE, COLOR_BTNFACE);
ReleaseDC(hDlg, hDC);
}
return FALSE;
}
case TIMER_MYLANGUAGECHECK:
{
LayoutCheckHandler(hDlg, LAYOUT_DEF_USER);
break;
}
}
break;
}
case WM_ERASEBKGND:
return PaintBranding(hDlg, (HDC)wParam, 0, FALSE, TRUE, COLOR_BTNFACE);
case WM_QUERYNEWPALETTE:
return BrandingQueryNewPalete(hDlg);
case WM_PALETTECHANGED:
return BrandingPaletteChanged(hDlg, (HWND)wParam);
case WM_LOGONCOMPLETE:
{
_Shell_LogonDialog_LogonCompleted(lParam, pGlobals->UserName, pGlobals->Domain);
Result = lParam;
//
// Discard the logon in progress dialog if one is displayed
//
RtlEnterCriticalSection(&pGlobals->csGlobals);
pGlobals->LogonInProgress = FALSE;
RtlLeaveCriticalSection(&pGlobals->csGlobals);
AttemptLogonSetControls(pGlobals, hDlg);
if (Result == MSGINA_DLG_FAILURE)
{
if (fDisconnectOnTsAutoLogonFailure || g_fHelpAssistantLogon)
{
//
// If TermSrv Internet Connector is on
// don't allow a second chance at the logon dialog
//
//
// disconnect immediatly when fail to logon as helpassisant
//
bSmartCardInserted = FALSE;
EndDialog(hDlg, MSGINA_DLG_USER_LOGOFF);
break;
}
if (s_fAttemptedAutoLogon != FALSE)
{
s_fAttemptedAutoLogon = FALSE;
switch (_Shell_LogonDialog_Init(hDlg, SHELL_LOGONDIALOG_LOGGEDOFF))
{
case SHELL_LOGONDIALOG_LOGON:
goto go_logon;
break;
case SHELL_LOGONDIALOG_NONE:
case SHELL_LOGONDIALOG_EXTERNALHOST:
default:
break;
}
}
if (!_Shell_LogonDialog_UIHostActive())
{
// Let the user try again - clear the password
SetDlgItemText(hDlg, IDD_LOGON_PASSWORD, NULL);
SetPasswordFocus(hDlg);
// the logon failed, so lets ensure we show the options pane so they can update
// their domain selection if needed.
if ( !pGlobals->LogonOptionsShown )
LogonShowOptions(pGlobals, hDlg, TRUE, FALSE);
}
return(TRUE);
}
//
// Initialize the TS Profile path and Home dir globals. Also get
// all TS specific user-config data which is used in winlogon
//
if (!g_Console) //only do this for non-console sessions
{
HANDLE ImpersonationHandle;
BOOL rc;
// DbgPrint("Calling WlxQueryTerminalServicesData() for %ws %ws\n",pGlobals->UserName,pGlobals->Domain);
ImpersonationHandle = ImpersonateUser(&pGlobals->UserProcessData, NULL);
ASSERT(ImpersonationHandle);
if (ImpersonationHandle != NULL)
{
//For WlxQueryTerminalServicesData() we need NT type names rather than
//UPN names, if we pass a UPN name it will try to resolve it
//(through ADS API) to NT name anyway. Besides, ADS API cannot
//resolve some UPN names and takes a long time to execute.
//So let's pass in an NT user and domain names.
PUNICODE_STRING pFlatUser ;
PUNICODE_STRING pFlatDomain ;
LPWSTR wszFlatUserName, wszFlatDomainName;
if(NT_SUCCESS(LsaGetUserName( &pFlatUser, &pFlatDomain )))
{
wszFlatUserName = (LPWSTR)LocalAlloc(LPTR,
pFlatUser->Length+sizeof(WCHAR));
wszFlatDomainName = (LPWSTR)LocalAlloc(LPTR,
pFlatDomain->Length+sizeof(WCHAR));
if(wszFlatUserName && wszFlatDomainName)
{
memcpy(wszFlatUserName, pFlatUser->Buffer,
pFlatUser->Length);
memcpy(wszFlatDomainName, pFlatDomain->Buffer,
pFlatDomain->Length);
pWlxFuncs->WlxQueryTerminalServicesData(pGlobals->hGlobalWlx,
&pGlobals->MuGlobals.TSData, wszFlatUserName, wszFlatDomainName);
}
else
{
pWlxFuncs->WlxQueryTerminalServicesData(pGlobals->hGlobalWlx,
&pGlobals->MuGlobals.TSData,pGlobals->UserName , pGlobals->Domain);
}
if(wszFlatUserName)
{
LocalFree(wszFlatUserName);
}
if(wszFlatDomainName)
{
LocalFree(wszFlatDomainName);
}
LsaFreeMemory( pFlatUser->Buffer );
LsaFreeMemory( pFlatUser );
LsaFreeMemory( pFlatDomain->Buffer );
LsaFreeMemory( pFlatDomain );
}
else
{
pWlxFuncs->WlxQueryTerminalServicesData(pGlobals->hGlobalWlx,
&pGlobals->MuGlobals.TSData,pGlobals->UserName , pGlobals->Domain);
}
rc = StopImpersonating(ImpersonationHandle);
ASSERT(rc);
}
}
bSmartCardInserted = FALSE;
EndDialog( hDlg, Result );
break;
}
case WM_HANDLEFAILEDLOGON:
{
INT_PTR Result;
if (_Shell_LogonDialog_LogonDisplayError(g_failinfo.Status, g_failinfo.SubStatus))
{
if (!IsWindowVisible(hDlg))
{
//
// The dialog was hidden for automatic logon. An error occurred.
// Show the dialog so the error can be seen and the problem corrected.
//
SetWindowPos(hDlg, NULL, 0, 0, pGlobals->rcDialog.right - pGlobals->rcDialog.left, pGlobals->rcDialog.bottom - pGlobals->rcDialog.top, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
ShowWindow(hDlg, SW_SHOW);
}
Result = HandleFailedLogon(hDlg);
}
else
{
Result = MSGINA_DLG_FAILURE;
}
SendMessage(hDlg, WM_LOGONCOMPLETE, 0, (LPARAM) Result);
return TRUE;
}
case WLX_WM_SAS:
// Give the consumer logon part a chance to handle the SAS
// or to key off the fact that a SAS has occurred.
(BOOL)_Shell_LogonDialog_DlgProc(hDlg, message, wParam, lParam);
if ((wParam == WLX_SAS_TYPE_TIMEOUT) ||
(wParam == WLX_SAS_TYPE_SCRNSVR_TIMEOUT) )
{
//
// If this was a timeout, return false, and let winlogon
// kill us later
//
bSmartCardInserted = FALSE;
return(FALSE);
}
if ( wParam == WLX_SAS_TYPE_SC_INSERT ) {
//
// If a password logon is already taking place then ignore this sas
//
if (pGlobals->LogonInProgress && !pGlobals->SmartCardLogon)
{
return(TRUE);
}
bSmartCardInserted = TRUE;
EndDialog( hDlg, MSGINA_DLG_SMARTCARD_INSERTED );
} else if ( wParam == WLX_SAS_TYPE_SC_REMOVE ) {
//
// If a password logon is already taking place then ignore this sas
//
if (pGlobals->LogonInProgress && !pGlobals->SmartCardLogon)
{
return(TRUE);
}
if ( bSmartCardInserted ) {
bSmartCardInserted = FALSE;
EndDialog( hDlg, MSGINA_DLG_SMARTCARD_REMOVED );
} else if ( pGlobals->SmartCardLogon ) {
// If this was a s/c initiated logon, then cancel
// the dialog. Otherwise, ignore it.
bSmartCardInserted = FALSE;
EndDialog( hDlg, MSGINA_DLG_FAILURE );
}
} else if ( wParam == WLX_SAS_TYPE_AUTHENTICATED )
{
bSmartCardInserted = FALSE;
_Shell_LogonDialog_LogonCompleted(MSGINA_DLG_SWITCH_CONSOLE, pGlobals->UserName, pGlobals->Domain);
EndDialog( hDlg, MSGINA_DLG_SWITCH_CONSOLE );
}
return(TRUE);
case WM_WTSSESSION_CHANGE:
ASSERT(iSessionRegistrationCount < 2);
//
// its possible, that we unregister for notification in wm_destroy and still receive this notification,
// as the notification may already have been sent.
//
if (iSessionRegistrationCount == 1)
{
if (lParam == 0)
{
//
// we are interested only in logon/logoff messages from session 0.
//
if (wParam == WTS_SESSION_LOGON || wParam == WTS_SESSION_LOGOFF)
{
bSessionZeroInUse = (wParam == WTS_SESSION_LOGON);
SwitchLogonLocked(hDlg, bSessionZeroInUse, FALSE);
}
}
}
break;
case WM_DESTROY:
// if registered for notification unregister now.
if (iSessionRegistrationCount)
{
WinStationUnRegisterConsoleNotification (SERVERNAME_CURRENT, hDlg);
iSessionRegistrationCount--;
ASSERT(iSessionRegistrationCount == 0);
}
_Shell_LogonDialog_Destroy();
FreeLayoutInfo(LAYOUT_DEF_USER);
if ( pGlobals->ActiveArray )
{
DCacheFreeArray( pGlobals->ActiveArray );
pGlobals->ActiveArray = NULL ;
}
RemoveWindowSubclass(GetDlgItem(hDlg, IDD_LOGON_NAME), DisableEditSubClassProc, IDD_LOGON_NAME);
RemoveWindowSubclass(GetDlgItem(hDlg, IDD_LOGON_PASSWORD), DisableEditSubClassProc, IDD_LOGON_PASSWORD);
break;
case WM_HIDEOURSELVES:
ShowWindow(hDlg, SW_HIDE);
break;
default:
if (_Shell_LogonDialog_DlgProc(hDlg, message, wParam, lParam) != FALSE)
{
return(TRUE);
}
}
return(FALSE);
}
/***************************************************************************\
* FUNCTION: LogonDlgInit
*
* PURPOSE: Handles initialization of logon dialog
*
* RETURNS: TRUE on success, FALSE on failure
*
* HISTORY:
*
* 12-09-91 Davidc Created.
*
\***************************************************************************/
BOOL
LogonDlgInit(
HWND hDlg,
BOOL bAutoLogon,
DWORD SasType
)
{
PGLOBALS pGlobals = (PGLOBALS)GetWindowLongPtr(hDlg, GWLP_USERDATA);
LPTSTR String = NULL;
TCHAR LogonMsg[MAX_PATH];
BOOL RemoveLegalBanner;
BOOL ShowOptions = FALSE;
HKEY hKey;
int err;
DWORD RasDisable;
DWORD RasForce;
STRING Narrow;
SECURITY_STATUS Status;
RECT rc, rc2;
BOOL bHasLangIcon = FALSE;
ULONG CacheFlags ;
//
// Populate Security Package List:
//
RtlInitString( &Narrow, MICROSOFT_KERBEROS_NAME_A );
Status = LsaLookupAuthenticationPackage(
pGlobals->LsaHandle,
&Narrow,
&pGlobals->SmartCardLogonPackage );
//
// this (potential) failure is not critical. If it fails, then s/c logons later
// will fail.
//
RtlInitString( &Narrow, NEGOSSP_NAME_A );
Status = LsaLookupAuthenticationPackage(
pGlobals->LsaHandle,
&Narrow,
&pGlobals->PasswordLogonPackage );
if ( !NT_SUCCESS( Status ) )
{
return FALSE ;
}
//
// Update the caption for certain banks
//
SetWelcomeCaption(hDlg);
//
// Get username and domain last used to login
//
//
// Ignore the default user name unless on the active console
//
if (IsActiveConsoleSession())
{
String = NULL;
if ( pGlobals->AutoAdminLogon && pGlobals->IgnoreAutoAdminLogon)
{
String = AllocAndGetProfileString(APPLICATION_NAME, TEMP_DEFAULT_USER_NAME_KEY, TEXT(""));
}
if ( (!String) || (!String[0]) )
{
if ( String )
{
Free(String);
}
String = AllocAndGetProfileString(APPLICATION_NAME, DEFAULT_USER_NAME_KEY, TEXT(""));
}
if ( String )
{
if (!bAutoLogon && (ReadWinlogonBoolValue(DONT_DISPLAY_LAST_USER_KEY, FALSE) == TRUE))
{
String[0] = 0;
}
SetDlgItemText(hDlg, IDD_LOGON_NAME, String);
Free(String);
}
}
GetProfileString( APPLICATION_NAME,
DEFAULT_DOMAIN_NAME_KEY,
TEXT(""),
pGlobals->Domain,
MAX_STRING_BYTES );
if ( !DCacheValidateCache( pGlobals->Cache ) )
{
ASSERT( pGlobals->ActiveArray == NULL );
DCacheUpdateMinimal( pGlobals->Cache, pGlobals->Domain, TRUE );
}
else
{
//
// Set the current default:
//
DCacheSetDefaultEntry( pGlobals->Cache,
pGlobals->Domain,
NULL );
}
CacheFlags = DCacheGetFlags( pGlobals->Cache );
if ( ( CacheFlags & DCACHE_NO_CACHE ) &&
( SafeBootMode != SAFEBOOT_MINIMAL ) &&
( ( pGlobals->AutoAdminLogon ) ||
( CacheFlags & DCACHE_DEF_UNKNOWN ) ) )
{
//
// Must wait for the cache to be populated
//
DCacheUpdateFull( pGlobals->Cache,
pGlobals->Domain );
CacheFlags = DCacheGetFlags( pGlobals->Cache );
}
pGlobals->ListPopulated = FALSE ;
pGlobals->ActiveArray = DCacheCopyCacheArray( pGlobals->Cache );
if ( pGlobals->ActiveArray )
{
DCachePopulateListBoxFromArray( pGlobals->ActiveArray,
GetDlgItem( hDlg, IDD_LOGON_DOMAIN ),
NULL );
}
else
{
EndDialog( hDlg, MSGINA_DLG_FAILURE );
}
pGlobals->ShowRasBox = FALSE;
if (g_Console) {
err = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
TEXT("Software\\Microsoft\\RAS"),
0,
KEY_READ,
& hKey );
if ( err == 0 )
{
RegCloseKey( hKey );
if ( GetRasDialOutProtocols() &&
( ( CacheFlags & DCACHE_MEMBER ) != 0 ) )
{
pGlobals->ShowRasBox = TRUE;
}
}
}
//
// If the audit log is full then display the banner, otherwise
// load the text from the resource if that gives us a string
// then set the control.
//
// Should neither of these apply then remove the control.
// The log full info is only displayed at the console so we
// don't disclose too much info in TS sessions.
//
RemoveLegalBanner = FALSE;
if ( pGlobals->AuditLogFull && !GetSystemMetrics(SM_REMOTESESSION))
{
if ( LoadString( hDllInstance, IDS_LOGON_LOG_FULL, LogonMsg, MAX_PATH ) )
{
SetDlgItemText( hDlg, IDD_LOGON_ANNOUNCE, LogonMsg );
}
else
{
RemoveLegalBanner = TRUE;
}
}
else
{
String = AllocAndGetProfileString( APPLICATION_NAME,
LOGON_MSG_KEY, TEXT("") );
if ( String )
{
if ( *String )
{
SetDlgItemText( hDlg, IDD_LOGON_ANNOUNCE, String );
}
else
{
RemoveLegalBanner = TRUE;
}
Free( String );
}
else
{
RemoveLegalBanner = TRUE;
}
}
if ( RemoveLegalBanner )
{
GetWindowRect(GetDlgItem(hDlg, IDD_LOGON_ANNOUNCE), &rc);
MoveControls(hDlg, ctrlNoLegalBanner,
sizeof(ctrlNoLegalBanner)/sizeof(ctrlNoLegalBanner[0]),
0, rc.top-rc.bottom,
TRUE);
ShowDlgItem(hDlg, IDD_LOGON_ANNOUNCE, FALSE);
}
//
// Smart Card Specific Stuff:
//
if ( SasType == WLX_SAS_TYPE_SC_INSERT )
{
//
// remove the user name fields
//
GetWindowRect(GetDlgItem(hDlg, IDD_LOGON_NAME), &rc);
GetWindowRect(GetDlgItem(hDlg, IDD_LOGON_PASSWORD), &rc2);
MoveControls(hDlg, ctrlNoUserName,
sizeof(ctrlNoUserName)/sizeof(ctrlNoUserName[0]),
0, -(rc2.top-rc.top),
TRUE);
ShowDlgItem(hDlg, IDD_LOGON_NAME_LABEL, FALSE);
EnableDlgItem(hDlg, IDD_LOGON_NAME_LABEL, FALSE);
ShowDlgItem(hDlg, IDD_LOGON_NAME, FALSE);
EnableDlgItem(hDlg, IDD_LOGON_NAME, FALSE);
SetDlgItemText( hDlg, IDD_LOGON_NAME, TEXT(""));
LoadString(hDllInstance, IDS_PIN, LogonMsg, MAX_PATH);
SetDlgItemText( hDlg, IDD_LOGON_PASSWORD_LABEL, LogonMsg );
pGlobals->SmartCardLogon = TRUE;
}
else
{
pGlobals->SmartCardLogon = FALSE;
}
//
// If this is safe boot and/or we are not part of a domain then lets
// remove the domain and nix out the RAS box.
//
if ((SafeBootMode == SAFEBOOT_MINIMAL)
|| (!IsMachineDomainMember())
|| (SasType == WLX_SAS_TYPE_SC_INSERT)
|| (ForceNoDomainUI()))
{
ShowDlgItem(hDlg, IDD_LOGON_DOMAIN_LABEL, FALSE);
EnableDlgItem(hDlg, IDD_LOGON_DOMAIN_LABEL, FALSE);
ShowDlgItem(hDlg, IDD_LOGON_DOMAIN, FALSE);
EnableDlgItem(hDlg, IDD_LOGON_DOMAIN, FALSE);
pGlobals->ShowDomainBox = FALSE;
// Shorten the window since the domain box isn't used
GetWindowRect(GetDlgItem(hDlg, IDD_LOGON_PASSWORD), &rc);
GetWindowRect(GetDlgItem(hDlg, IDD_LOGON_DOMAIN), &rc2);
MoveControls(hDlg, ctrlNoDomain,
ARRAYSIZE(ctrlNoDomain),
0, -(rc2.bottom-rc.bottom),
TRUE);
}
else
{
pGlobals->ShowDomainBox = TRUE;
}
bHasLangIcon = DisplayLanguageIcon(hDlg, LAYOUT_DEF_USER, GetKeyboardLayout(0));
//
// Handle showing the RAS box if needed
//
if ( pGlobals->ShowRasBox )
{
RasDisable = GetProfileInt( APPLICATION_NAME, RAS_DISABLE, 0 );
RasForce = GetProfileInt( APPLICATION_NAME, RAS_FORCE, 0 );
if (RasForce)
{
CheckDlgButton( hDlg, IDD_LOGON_RASBOX, 1 );
}
else
{
CheckDlgButton( hDlg, IDD_LOGON_RASBOX, 0 );
}
// SM_CLEANBOOT tells us we are in safe mode. In this case, disable since tapisrv isn't started
if (RasDisable || RasForce || GetSystemMetrics(SM_CLEANBOOT))
{
EnableDlgItem(hDlg, IDD_LOGON_RASBOX, FALSE);
}
else
{
EnableDlgItem(hDlg, IDD_LOGON_RASBOX, TRUE);
}
}
else
{
// If the domain box is hidden, then we'll have to shorten the dialog by the distance
// between the RAS box and the password box instead of the distance between the
// RAS box and the domain box since the RAS and Domain boxes will be on top of each other
BOOL fUsePassword = !pGlobals->ShowDomainBox;
CheckDlgButton( hDlg, IDD_LOGON_RASBOX, 0 );
EnableDlgItem(hDlg, IDD_LOGON_RASBOX, FALSE);
ShowDlgItem(hDlg, IDD_LOGON_RASBOX, FALSE);
GetWindowRect(GetDlgItem(hDlg, fUsePassword ? IDD_LOGON_PASSWORD : IDD_LOGON_DOMAIN), &rc);
GetWindowRect(GetDlgItem(hDlg, IDD_LOGON_RASBOX), &rc2);
if (!bHasLangIcon)
{
MoveControls(hDlg, ctrlNoRAS,
sizeof(ctrlNoRAS)/sizeof(ctrlNoRAS[0]),
0, -(rc2.bottom-rc.bottom),
TRUE);
}
}
// Centre the window on the screen and bring it to the front
pGlobals->xBandOffset = 0; // band is not animated yet
SizeForBranding(hDlg, TRUE);
// Position the window at the same coords as the welcome window
if ((pGlobals->rcWelcome.right - pGlobals->rcWelcome.left) != 0)
{
SetWindowPos(hDlg, NULL, pGlobals->rcWelcome.left, pGlobals->rcWelcome.top,
0, 0, SWP_NOZORDER | SWP_NOSIZE);
}
else
{
CentreWindow(hDlg);
}
//
// Handle showing and hiding the logon bits
//
if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, WINLOGON_KEY, 0, KEY_READ,
&hKey) == ERROR_SUCCESS)
{
DWORD dwType, dwSize = sizeof(ShowOptions);
RegQueryValueEx (hKey, SHOW_LOGON_OPTIONS, NULL, &dwType,
(LPBYTE)&ShowOptions, &dwSize);
RegCloseKey (hKey);
}
pGlobals->LogonOptionsShown = TRUE;
LogonShowOptions(pGlobals, hDlg, ShowOptions, TRUE);
// Success
return TRUE;
}
/****************************************************************************\
*
* FUNCTION: LogonShowOptions
*
* PURPOSE: Hide the options part of the logon dialog
*
* RETURNS: Nothing
*
* HISTORY:
*
* 15-dec-97 daviddv - Created
*
\****************************************************************************/
VOID LogonShowOptions(PGLOBALS pGlobals, HWND hDlg, BOOL fShow, BOOL fSticky)
{
HKEY hKey;
RECT rc, rc2;
INT dy = 0;
INT dx = 0;
TCHAR szBuffer[32];
BOOL bHasLangIcon = TRUE;
DWORD RasDisable;
DWORD RasForce;
if ( pGlobals->LogonOptionsShown != fShow )
{
BOOL bShutdownWithoutLogon;
//
// Show/hide domain if it is present in the dialog
//
if (pGlobals->ShowDomainBox)
{
GetWindowRect(GetDlgItem(hDlg, IDD_LOGON_PASSWORD), &rc);
GetWindowRect(GetDlgItem(hDlg, IDD_LOGON_DOMAIN), &rc2);
dy += rc2.bottom-rc.bottom;
}
//
// If RAS is present then lets ensure that we remove that.
//
if (GetKeyboardLayoutList(0,NULL) < 2)
{
bHasLangIcon = FALSE;
}
if ( pGlobals->ShowRasBox || bHasLangIcon)
{
// Since the domain box may be hidden with the RAS box directly over
// top of it, we may need to use the space between the RAS box and the password
// box
BOOL fUsePassword = !pGlobals->ShowDomainBox;
GetWindowRect(GetDlgItem(hDlg, fUsePassword ? IDD_LOGON_PASSWORD : IDD_LOGON_DOMAIN), &rc);
GetWindowRect(GetDlgItem(hDlg, IDD_LOGON_RASBOX), &rc2);
dy += rc2.bottom-rc.bottom;
}
MoveControls(hDlg, ctrlNoRAS,
sizeof(ctrlNoRAS)/sizeof(ctrlNoRAS[0]),
0, fShow ? dy:-dy,
TRUE);
// Handle showing or hiding the shutdown button
// and moving other controls.
ShowDlgItem(hDlg, IDD_KBLAYOUT_ICON, fShow);
EnableWindow(GetDlgItem(hDlg, IDD_KBLAYOUT_ICON), fShow);
ShowDlgItem(hDlg, IDD_LOGON_SHUTDOWN, fShow);
// Move the OK and Cancel buttons over if we are hiding shutdown
// ..Calculate one "button space". Assumes shutdown will always be on the left of options
GetWindowRect(GetDlgItem(hDlg, IDD_LOGON_SHUTDOWN), &rc);
GetWindowRect(GetDlgItem(hDlg, IDD_LOGON_OPTIONS), &rc2);
dx = rc2.left - rc.left;
// Move OK and Cancel left or right 1 button space
MoveControls(hDlg, ctrlNoShutdown,
sizeof(ctrlNoShutdown)/sizeof(ctrlNoShutdown[0]),
fShow ? -dx:dx, 0,
FALSE);
//
// if ShutdownWithoutLogon, use the proper 3 buttons: OK, Shutdown and Cancel
// instead of the 2 buttons OK and Cancel
//
if ( SafeBootMode == SAFEBOOT_MINIMAL )
{
bShutdownWithoutLogon = TRUE ;
}
else if (IsthisUnlockWindowsDialog() || !IsActiveConsoleSession())
{
bShutdownWithoutLogon = FALSE ;
}
else
{
bShutdownWithoutLogon = ReadWinlogonBoolValue(SHUTDOWN_WITHOUT_LOGON_KEY, TRUE);
}
EnableDlgItem(hDlg, IDD_LOGON_SHUTDOWN, (fShow) &&
(bShutdownWithoutLogon));
if ( pGlobals->ShowRasBox )
{
ShowDlgItem(hDlg, IDD_LOGON_RASBOX, fShow);
RasDisable = GetProfileInt(APPLICATION_NAME, RAS_DISABLE,0);
RasForce = GetProfileInt(APPLICATION_NAME, RAS_FORCE, 0);
// Never enable RAS for cleanboot
if (!GetSystemMetrics(SM_CLEANBOOT) && !RasForce && !RasDisable)
{
EnableWindow(GetDlgItem(hDlg, IDD_LOGON_RASBOX), fShow);
}
}
if ( pGlobals->ShowDomainBox )
{
ShowDlgItem(hDlg, IDD_LOGON_DOMAIN_LABEL, fShow);
EnableWindow(GetDlgItem(hDlg, IDD_LOGON_DOMAIN_LABEL), fShow);
ShowDlgItem(hDlg, IDD_LOGON_DOMAIN, fShow);
EnableWindow(GetDlgItem(hDlg, IDD_LOGON_DOMAIN), fShow);
}
if ( fSticky )
{
if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, WINLOGON_KEY, 0, KEY_WRITE, &hKey) == ERROR_SUCCESS)
{
RegSetValueEx(hKey, SHOW_LOGON_OPTIONS, 0, REG_DWORD,
(LPBYTE)&fShow, sizeof(fShow));
RegCloseKey (hKey);
}
}
}
//
// Change the options button to reflect the open/close state
//
LoadString(hDllInstance, fShow ? IDS_LESSOPTIONS:IDS_MOREOPTIONS,
szBuffer, sizeof(szBuffer)/sizeof(szBuffer[0]));
SetDlgItemText(hDlg, IDD_LOGON_OPTIONS, szBuffer);
pGlobals->LogonOptionsShown = fShow;
// Enable or disable the domain box depending on whether a UPN name has been typed
EnableDomainForUPN(GetDlgItem(hDlg, IDD_LOGON_NAME), GetDlgItem(hDlg, IDD_LOGON_DOMAIN));
}
/***************************************************************************\
* FUNCTION: AttemptLogonSetControls
*
* PURPOSE: Sets up the logon UI to animating and controls to the
* correct state.
*
* HISTORY:
*
* 02-05-98 diz Created
*
\***************************************************************************/
VOID AttemptLogonSetControls(
PGLOBALS pGlobals,
HWND hDlg
)
{
DWORD RasDisable;
static BOOL sbRasBoxOriginalyEnabled;
static BOOL sbShutDownOriginallyEnabled;
RtlEnterCriticalSection( &pGlobals->csGlobals );
EnableDlgItem(hDlg, IDD_LOGON_NAME_LABEL, !pGlobals->LogonInProgress);
EnableDlgItem(hDlg, IDD_LOGON_NAME, !pGlobals->LogonInProgress);
EnableDlgItem(hDlg, IDD_LOGON_PASSWORD_LABEL, !pGlobals->LogonInProgress);
EnableDlgItem(hDlg, IDD_LOGON_PASSWORD, !pGlobals->LogonInProgress);
EnableDlgItem(hDlg, IDD_LOGON_DOMAIN_LABEL, !pGlobals->LogonInProgress);
EnableDlgItem(hDlg, IDD_LOGON_DOMAIN, !pGlobals->LogonInProgress);
// If no logon is in progress, we want to enable domain box based on whether
// a UPN has been typed
if (!pGlobals->LogonInProgress)
{
EnableDomainForUPN(GetDlgItem(hDlg, IDD_LOGON_NAME), GetDlgItem(hDlg, IDD_LOGON_DOMAIN));
}
//
// MakarP: we should not enable all these control when !pGlobals->LogonInProgress, they should really be reverted back to their original state.
// but for now I am just looking after IDD_LOGON_RASBOX in remote connection cases to fix bug #267270
//
if (pGlobals->LogonInProgress)
{
sbRasBoxOriginalyEnabled = IsWindowEnabled(GetDlgItem(hDlg, IDD_LOGON_RASBOX));
RasDisable = GetProfileInt(APPLICATION_NAME, RAS_DISABLE, 0);
EnableDlgItem(hDlg, IDD_LOGON_RASBOX, !RasDisable && !pGlobals->LogonInProgress);
sbShutDownOriginallyEnabled = IsWindowEnabled(GetDlgItem(hDlg, IDD_LOGON_SHUTDOWN));
EnableDlgItem(hDlg, IDD_LOGON_SHUTDOWN, !pGlobals->LogonInProgress);
}
else
{
EnableDlgItem(hDlg, IDD_LOGON_RASBOX, sbRasBoxOriginalyEnabled);
EnableDlgItem(hDlg, IDD_LOGON_SHUTDOWN, sbShutDownOriginallyEnabled);
}
EnableDlgItem(hDlg, IDD_KBLAYOUT_ICON, !pGlobals->LogonInProgress);
EnableDlgItem(hDlg, IDD_LOGON_OPTIONS, !pGlobals->LogonInProgress);
//
// if ShutdownWithoutLogon, use the proper 3 buttons: OK, Shutdown and Cancel
// instead of the 2 buttons OK and Cancel
//
EnableDlgItem(hDlg, IDOK, !pGlobals->LogonInProgress);
if ( !GetDisableCad(pGlobals) )
EnableDlgItem(hDlg, IDCANCEL, !pGlobals->LogonInProgress);
RtlLeaveCriticalSection( &pGlobals->csGlobals );
}
/***************************************************************************\
* FUNCTION: AttemptLogon
*
* PURPOSE: Tries to the log the user on using the current values in the
* logon dialog controls
*
* RETURNS: MSGINA_DLG_SUCCESS - the user was logged on successfully
* MSGINA_DLG_FAILURE - the logon failed,
* DLG_INTERRUPTED() - a set defined in winlogon.h
*
* NOTES: If the logon is successful, the global structure is filled in
* with the logon information.
*
* HISTORY:
*
* 12-09-91 Davidc Created.
*
\***************************************************************************/
INT_PTR
AttemptLogon(
HWND hDlg
)
{
PGLOBALS pGlobals = (PGLOBALS)GetWindowLongPtr(hDlg, GWLP_USERDATA);
PWCHAR UserName = pGlobals->UserName;
PWCHAR Domain = pGlobals->Domain;
PWCHAR Password = pGlobals->Password;
PDOMAIN_CACHE_ENTRY Entry ;
RECT rc;
HANDLE hThread;
DWORD tid;
BOOL timeout;
PUCHAR Dummy;
BOOL RasBox;
UserName[0] = TEXT('\0');
Domain[0] = TEXT('\0');
Password[0] = TEXT('\0');
//
// Hide the password so it doesn't make it to the pagefile in
// cleartext. Do this before getting the username and password
// so that it can't easily be identified (by association with
// the username and password) if we should crash or be rebooted
// before getting a chance to encode it.
//
GetDlgItemText(hDlg, IDD_LOGON_PASSWORD, Password, MAX_STRING_BYTES);
RtlInitUnicodeString(&pGlobals->PasswordString, Password);
pGlobals->Seed = 0; // Causes the encode routine to assign a seed
HidePassword( &pGlobals->Seed, &pGlobals->PasswordString );
//
// Now get the username and domain
//
if ( pGlobals->SmartCardLogon == FALSE )
{
HWND hwndDomain = GetDlgItem(hDlg, IDD_LOGON_DOMAIN);
if (hwndDomain != NULL)
{
INT iDomainSel = (INT)SendMessage(hwndDomain, CB_GETCURSEL, 0, 0);
GetDlgItemText(hDlg, IDD_LOGON_NAME, UserName, MAX_STRING_BYTES);
//
// is this the magical "this computer" entry???
//
Entry = (PDOMAIN_CACHE_ENTRY) SendMessage( hwndDomain, CB_GETITEMDATA, (WPARAM)iDomainSel, 0);
}
else
{
Entry = (PDOMAIN_CACHE_ENTRY) CB_ERR;
}
if ( Entry != (PDOMAIN_CACHE_ENTRY) CB_ERR )
{
wcscpy( Domain, Entry->FlatName.Buffer );
}
else
{
Domain[0] = L'\0';
}
}
else
{
UserName[0] = TEXT('\0');
Domain[0] = TEXT('\0') ;
}
// If we are forcing a NoDomainUI, populate the domain with the local machine name now
if (ForceNoDomainUI())
{
DWORD chSize = MAX_STRING_BYTES;
if (GetComputerName(Domain, &chSize))
{
NOTHING;
}
else
{
*Domain = 0;
}
}
//
// If there is a at-sign in the name, assume that means that a UPN logon
// attempt is being made. Set the domain to NULL.
//
if ( wcschr( UserName, L'@' ) )
{
Domain[0] = TEXT('\0');
}
RtlInitUnicodeString(&pGlobals->UserNameString, UserName);
RtlInitUnicodeString(&pGlobals->DomainString, Domain);
//
// Ok, is the RASbox checked?
//
RasBox = IsDlgButtonChecked( hDlg, IDD_LOGON_RASBOX );
pGlobals->RasUsed = FALSE;
if ( RasBox == BST_CHECKED )
{
//
// Reset the current timeout so that they neatly clean up before
// winlogon up and blows them away.
//
pWlxFuncs->WlxSetTimeout( pGlobals->hGlobalWlx, 5 * 60 );
if ( !PopupRasPhonebookDlg( hDlg, pGlobals, &timeout) )
{
return( MSGINA_DLG_FAILURE );
}
pGlobals->RasUsed = TRUE;
//
// Reinitialize strings in case they've changed
//
RtlInitUnicodeString( &pGlobals->UserNameString, UserName );
//
// Ping Netlogon to allow us to go out on the net again...
//
I_NetLogonControl2(NULL,
NETLOGON_CONTROL_TRANSPORT_NOTIFY,
1, (LPBYTE) &Dummy, &Dummy );
Sleep ((DWORD) ReadWinlogonBoolValue(TEXT("RASSleepTime"), 3000));
RefreshPolicy(TRUE);
}
//
// Process arguments before kicking off the thread
//
pGlobals->hwndLogon = hDlg;
RtlEnterCriticalSection( &pGlobals->csGlobals );
pGlobals->LogonInProgress = TRUE ;
RtlLeaveCriticalSection( &pGlobals->csGlobals );
GetClientRect(hDlg, &rc);
pGlobals->cxBand = rc.right-rc.left;
SetTimer(hDlg, 0, 20, NULL); // setup the progress timer
//
// Kick off real logon thread
//
// Set timeout to infinite while attempting to logon
pWlxFuncs->WlxSetTimeout( pGlobals->hGlobalWlx, TIMEOUT_NONE );
hThread = CreateThread( NULL, 0,
AttemptLogonThread,
pGlobals,
0, &tid );
if (hThread)
{
CloseHandle( hThread );
}
else
{
//
// CreateThread failed, likely because of low memory.
// Inform the user.
//
PostFailedLogonMessage(pGlobals->hwndLogon,
pGlobals,
GetLastError(),
0,
NULL,
NULL);
RtlEnterCriticalSection( &pGlobals->csGlobals );
pGlobals->LogonInProgress = FALSE ;
RtlLeaveCriticalSection( &pGlobals->csGlobals );
return MSGINA_DLG_FAILURE ;
}
AttemptLogonSetControls(pGlobals, hDlg);
return MSGINA_DLG_SUCCESS;
}
BOOL ReplacedPossibleDisplayName (WCHAR *pszUsername)
{
BOOL fReplaced;
DWORD dwIndex, dwReturnedEntryCount;
NET_API_STATUS nasCode;
NET_DISPLAY_USER *pNDU;
fReplaced = FALSE;
dwIndex = 0;
nasCode = NetQueryDisplayInformation(NULL,
1,
dwIndex,
1,
sizeof(NET_DISPLAY_USER),
&dwReturnedEntryCount,
(void**)&pNDU);
while (!fReplaced &&
(dwReturnedEntryCount > 0) &&
(NERR_Success == nasCode) || (ERROR_MORE_DATA == nasCode))
{
fReplaced = (lstrcmpiW(pNDU->usri1_full_name, pszUsername) == 0);
if (fReplaced)
{
lstrcpyW(pszUsername, pNDU->usri1_name);
}
nasCode = NetApiBufferFree(pNDU);
if (!fReplaced)
{
nasCode = NetQueryDisplayInformation(NULL,
1,
++dwIndex,
1,
sizeof(NET_DISPLAY_USER),
&dwReturnedEntryCount,
(void**)&pNDU);
}
}
return(fReplaced);
}
BOOL ReplacedLogonName (PGLOBALS pGlobals)
{
BOOL fReplaced;
fReplaced = ReplacedPossibleDisplayName(pGlobals->UserName);
if (fReplaced)
{
RtlInitUnicodeString(&pGlobals->UserNameString, pGlobals->UserName);
}
return(fReplaced);
}
DWORD
AttemptLogonThread(
PGLOBALS pGlobals
)
{
STRING PackageName;
PSID LogonSid;
LUID LogonId = { 0, 0 };
HANDLE UserToken = NULL;
HANDLE RestrictedToken;
BOOL PasswordExpired, ChangedLogonName;
NTSTATUS FinalStatus;
NTSTATUS Status = STATUS_SUCCESS;
NTSTATUS SubStatus = STATUS_SUCCESS;
INT_PTR Result = MSGINA_DLG_FAILURE;
ULONG LogonPackage;
BYTE GroupsBuffer[sizeof(TOKEN_GROUPS)+sizeof(SID_AND_ATTRIBUTES)];
PTOKEN_GROUPS TokenGroups = (PTOKEN_GROUPS) GroupsBuffer;
PVOID AuthInfo ;
ULONG AuthInfoSize ;
UCHAR UserBuffer[ SID_MAX_SUB_AUTHORITIES * sizeof( DWORD ) + 8 + sizeof( TOKEN_USER ) ];
PTOKEN_USER pTokenUser ;
ULONG TokenInfoSize ;
PUCHAR SmartCardInfo ;
SECURITY_LOGON_TYPE logonType;
PWLX_SC_NOTIFICATION_INFO ScInfo = NULL ;
#ifdef SMARTCARD_DOGFOOD
DWORD StartTime = 0, EndTime = 0;
#endif
//
// Store the logon time
// Do this before calling Lsa so we know if logon is successful that
// the password-must-change time will be greater than this time.
// If we grabbed this time after calling the lsa, this might not be true.
//
if ( IsActiveConsoleSession() )
{
// this is the console logon;
logonType = Interactive;
}
else
{
// remote sessions user must have the SeRemoteInteractiveLogonRight right which
// is granted to a user due to their membership in the new Remote-Desktop Users group.
logonType = RemoteInteractive;
}
GetSystemTimeAsFileTime( (LPFILETIME) &pGlobals->LogonTime );
DebugLog((DEB_TRACE, "In Attempt Logon!\n"));
if ( pGlobals->RasUsed )
{
if ( DCacheGetCacheState( pGlobals->Cache ) < DomainCacheRegistryCache )
{
//
// We are using really stale data. Poke the cache to get it to use the
// now made RAS connection
//
DCacheUpdateMinimal( pGlobals->Cache, NULL, TRUE );
}
}
//
// Generate a unique sid for this logon
//
LogonSid = pGlobals->LogonSid;
SetupCursor( TRUE );
FinalStatus = STATUS_SUCCESS;
if ( wcschr( pGlobals->UserName, L'\\' ) ||
wcschr( pGlobals->UserName, L'/' ) )
{
FinalStatus = STATUS_LOGON_FAILURE ;
Status = FinalStatus ;
}
// clear card and reader name
pGlobals->Smartcard[0] = TEXT('\0');
pGlobals->SmartcardReader[0] = TEXT('\0');
if ( NT_SUCCESS( FinalStatus ) )
{
if ( pGlobals->SmartCardLogon )
{
pGlobals->AuthenticationPackage = pGlobals->SmartCardLogonPackage ;
}
else
{
pGlobals->AuthenticationPackage = pGlobals->PasswordLogonPackage ;
}
if ( pGlobals->SmartCardLogon )
{
pWlxFuncs->WlxGetOption( pGlobals->hGlobalWlx,
WLX_OPTION_SMART_CARD_INFO,
(ULONG_PTR *) &ScInfo );
if ( !ScInfo )
{
goto exit;
}
SmartCardInfo = ScBuildLogonInfo(
ScInfo->pszCard,
ScInfo->pszReader,
ScInfo->pszContainer,
ScInfo->pszCryptoProvider );
if(ScInfo->pszCard && ScInfo->pszReader) {
wcsncpy(
pGlobals->Smartcard,
ScInfo->pszCard,
sizeof(pGlobals->Smartcard) / sizeof(TCHAR) - 1
);
wcsncpy(
pGlobals->SmartcardReader,
ScInfo->pszReader,
sizeof(pGlobals->SmartcardReader) / sizeof(TCHAR) - 1
);
}
#ifndef SMARTCARD_DOGFOOD
LocalFree( ScInfo );
#endif
AuthInfo = FormatSmartCardCredentials(
&pGlobals->PasswordString,
SmartCardInfo,
FALSE,
NULL,
&AuthInfoSize );
LocalFree( SmartCardInfo );
}
else
{
AuthInfo = FormatPasswordCredentials(
&pGlobals->UserNameString,
&pGlobals->DomainString,
&pGlobals->PasswordString,
FALSE,
NULL,
&AuthInfoSize );
}
//
// Actually try to logon the user
//
#ifdef SMARTCARD_DOGFOOD
StartTime = GetTickCount();
#endif
FinalStatus = WinLogonUser(
pGlobals->LsaHandle,
pGlobals->AuthenticationPackage,
logonType,
AuthInfo,
AuthInfoSize,
LogonSid,
&LogonId,
&UserToken,
&pGlobals->UserProcessData.Quotas,
(PVOID *)&pGlobals->Profile,
&pGlobals->ProfileLength,
&SubStatus,
&pGlobals->OptimizedLogonStatus);
#ifdef SMARTCARD_DOGFOOD
EndTime = GetTickCount();
#endif
Status = FinalStatus;
}
SetupCursor( FALSE );
RtlEnterCriticalSection( &pGlobals->csGlobals );
pGlobals->LogonInProgress = FALSE;
RtlLeaveCriticalSection( &pGlobals->csGlobals );
DebugLog((DEB_TRACE, "WinLogonUser returned %#x\n", Status));
PasswordExpired = (((Status == STATUS_ACCOUNT_RESTRICTION) && (SubStatus == STATUS_PASSWORD_EXPIRED)) ||
(Status == STATUS_PASSWORD_MUST_CHANGE));
//
// If the account has expired we let them change their password and
// automatically retry the logon with the new password.
//
if (PasswordExpired)
{
_Shell_LogonDialog_HideUIHost();
if (Status == STATUS_PASSWORD_MUST_CHANGE)
{
Result = TimeoutMessageBox(pGlobals->hwndLogon, pGlobals, IDS_PASSWORD_MUST_CHANGE,
IDS_LOGON_MESSAGE,
MB_OK | MB_ICONSTOP | MB_SETFOREGROUND,
TIMEOUT_CURRENT);
}
else
{
Result = TimeoutMessageBox(pGlobals->hwndLogon, pGlobals, IDS_PASSWORD_EXPIRED,
IDS_LOGON_MESSAGE,
MB_OK | MB_ICONSTOP | MB_SETFOREGROUND,
TIMEOUT_CURRENT);
}
if (DLG_INTERRUPTED(Result))
goto exit;
//
// Copy the old password for mpr notification later
//
RevealPassword( &pGlobals->PasswordString );
wcsncpy(pGlobals->OldPassword, pGlobals->Password, MAX_STRING_BYTES);
pGlobals->OldSeed = 0;
RtlInitUnicodeString(&pGlobals->OldPasswordString, pGlobals->OldPassword);
HidePassword( &pGlobals->OldSeed, &pGlobals->OldPasswordString);
pGlobals->OldPasswordPresent = 1;
//
// Let the user change their password
//
LogonPackage = pGlobals->AuthenticationPackage ;
RtlInitString(&PackageName, MSV1_0_PACKAGE_NAME );
Status = LsaLookupAuthenticationPackage (
pGlobals->LsaHandle,
&PackageName,
&pGlobals->AuthenticationPackage
);
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "Failed to find %s authentication package, status = 0x%lx",
MSV1_0_PACKAGE_NAME, Status));
Result = MSGINA_DLG_FAILURE;
goto exit;
}
Result = ChangePasswordLogon(pGlobals->hwndLogon, pGlobals,
pGlobals->UserName,
pGlobals->Domain,
pGlobals->Password);
pGlobals->AuthenticationPackage = LogonPackage ;
if (DLG_INTERRUPTED(Result))
goto exit;
if (Result == MSGINA_DLG_FAILURE)
{
// The user doesn't want to, or failed to change their password.
goto exit;
}
}
// Special handling for failed logon on personal or professional
// machines that are NOT joined to a domain. In this case it's
// probably a user who disabled friendly UI and only knows of
// their "display name" not their real "logon name". This
// transparently maps one to the other to allow logons using
// the "display name".
ChangedLogonName = ((FinalStatus == STATUS_LOGON_FAILURE) &&
(IsOS(OS_PERSONAL) || IsOS(OS_PROFESSIONAL)) &&
!IsMachineDomainMember() &&
ReplacedLogonName(pGlobals));
if (PasswordExpired || ChangedLogonName)
{
//
// Retry the logon with the changed password
//
//
// Generate a unique sid for this logon
//
LogonSid = pGlobals->LogonSid;
AuthInfo = FormatPasswordCredentials(
&pGlobals->UserNameString,
&pGlobals->DomainString,
&pGlobals->PasswordString,
FALSE,
NULL,
&AuthInfoSize );
Status = WinLogonUser(
pGlobals->LsaHandle,
pGlobals->AuthenticationPackage,
logonType,
AuthInfo,
AuthInfoSize,
LogonSid,
&LogonId,
&UserToken,
&pGlobals->UserProcessData.Quotas,
(PVOID *)&pGlobals->Profile,
&pGlobals->ProfileLength,
&SubStatus,
&pGlobals->OptimizedLogonStatus);
}
//
// Deal with a terminally failed logon attempt
//
if (!NT_SUCCESS(Status))
{
//
// Do lockout processing
//
LockoutHandleFailedLogon(pGlobals);
Result = MSGINA_DLG_FAILEDMSGSENT;
PostFailedLogonMessage(pGlobals->hwndLogon, pGlobals, Status, SubStatus, pGlobals->UserName, pGlobals->Domain);
goto exit;
}
//
// The user logged on successfully
//
//
// Do lockout processing
//
LockoutHandleSuccessfulLogon(pGlobals);
//
// If the audit log is full, check they're an admin
//
if (pGlobals->AuditLogFull)
{
//
// The audit log is full, so only administrators are allowed to logon.
//
if (!UserToken || !TestTokenForAdmin(UserToken))
{
//
// The user is not an administrator, boot 'em.
//
LsaFreeReturnBuffer(pGlobals->Profile);
pGlobals->Profile = NULL;
NtClose(UserToken);
Result = MSGINA_DLG_FAILEDMSGSENT;
// Post a specific substatus so we can display a meaningful error message
PostFailedLogonMessage(pGlobals->hwndLogon, pGlobals, STATUS_LOGON_FAILURE, IDS_LOGON_LOG_FULL, pGlobals->UserName, pGlobals->Domain);
goto exit;
}
else
{
//
// If we are in a session, we didn't display the log full onfo on the welcome
// screen, so tell the admin
//
if (GetSystemMetrics(SM_REMOTESESSION))
{
TimeoutMessageBox(
pGlobals->hwndLogon,
pGlobals,
IDS_LOGON_LOG_FULL_ADMIN,
IDS_LOGON_MESSAGE,
MB_OK | MB_ICONSTOP | MB_SETFOREGROUND,
TIMEOUT_CURRENT);
}
}
}
//
// Hide ourselves before letting other credential managers put
// up dialogs
//
#if 0
ShowWindow(hDlg, SW_HIDE);
#endif
//
// Create a filtered version of the token for running normal applications
// if so indicated by a registry setting
//
if (GetProfileInt( APPLICATION_NAME, RESTRICT_SHELL, 0) != 0) {
TokenGroups->Groups[0].Attributes = 0;
TokenGroups->Groups[0].Sid = gAdminSid;
TokenGroups->GroupCount = 1;
Status = NtFilterToken(
UserToken,
DISABLE_MAX_PRIVILEGE,
TokenGroups, // disable the administrators sid
NULL, // no privileges
NULL,
&RestrictedToken
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "Failed to filter token: 0x%%x\n", Status));
RestrictedToken = NULL;
}
//
// Now set the default dacl for the token
//
{
PACL Dacl = NULL;
ULONG DaclLength = 0;
TOKEN_DEFAULT_DACL DefaultDacl;
DaclLength = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(LogonSid);
Dacl = Alloc(DaclLength);
Status = RtlCreateAcl(Dacl,DaclLength, ACL_REVISION);
ASSERT(NT_SUCCESS(Status));
Status = RtlAddAccessAllowedAce(
Dacl,
ACL_REVISION,
GENERIC_ALL,
LogonSid
);
ASSERT(NT_SUCCESS(Status));
DefaultDacl.DefaultDacl = Dacl;
Status = NtSetInformationToken(
RestrictedToken,
TokenDefaultDacl,
&DefaultDacl,
sizeof(TOKEN_DEFAULT_DACL)
);
ASSERT(NT_SUCCESS(Status));
Free(Dacl);
}
} else {
RestrictedToken = NULL;
}
//
// Notify credential managers of the successful logon
//
pTokenUser = (PTOKEN_USER) UserBuffer ;
Status = NtQueryInformationToken( UserToken,
TokenUser,
pTokenUser,
sizeof( UserBuffer ),
&TokenInfoSize );
if ( NT_SUCCESS( Status ) )
{
pGlobals->UserProcessData.UserSid = LocalAlloc( LMEM_FIXED,
RtlLengthSid( pTokenUser->User.Sid ) );
if ( pGlobals->UserProcessData.UserSid )
{
RtlCopyMemory( pGlobals->UserProcessData.UserSid,
pTokenUser->User.Sid,
RtlLengthSid( pTokenUser->User.Sid ) );
}
else
{
Status = STATUS_NO_MEMORY ;
}
}
if ( !NT_SUCCESS( Status ) )
{
LsaFreeReturnBuffer(pGlobals->Profile);
pGlobals->Profile = NULL;
NtClose(UserToken);
Result = MSGINA_DLG_FAILEDMSGSENT;
PostFailedLogonMessage(pGlobals->hwndLogon, pGlobals, Status, 0, pGlobals->UserName, pGlobals->Domain);
goto exit;
}
pGlobals->UserProcessData.RestrictedToken = RestrictedToken;
pGlobals->UserProcessData.UserToken = UserToken;
pGlobals->UserProcessData.NewThreadTokenSD = CreateUserThreadTokenSD(LogonSid, pWinlogonSid);
pGlobals->MprLogonScripts = NULL;
pGlobals->ExtraApps = NULL ;
//
// If we get here, the system works well enough for the user to have
// actually logged on. Profile failures aren't fixable by last known
// good anyway. Therefore, declare the boot good.
//
ReportBootGood(pGlobals);
//
// Set up the system for the new user
//
pGlobals->LogonId = LogonId;
if ((pGlobals->Profile != NULL) && (pGlobals->Profile->FullName.Length > 0)) {
if (pGlobals->Profile->FullName.Length > MAX_STRING_LENGTH) {
wcsncpy(pGlobals->UserFullName, pGlobals->Profile->FullName.Buffer, MAX_STRING_LENGTH);
*(pGlobals->UserFullName + MAX_STRING_LENGTH) = UNICODE_NULL;
}
else {
lstrcpy(pGlobals->UserFullName, pGlobals->Profile->FullName.Buffer);
}
} else {
//
// No profile - set full name = NULL
pGlobals->UserFullName[0] = 0;
ASSERT( lstrlen(pGlobals->UserFullName) == 0);
}
if ( pGlobals->SmartCardLogon )
{
PCCERT_CONTEXT Cert ;
PKERB_SMART_CARD_PROFILE ScProfile ;
//
// Need to fix up the user name with the name (UPN) from the
// certificate, so that unlock, etc. work correctly.
//
ScProfile = (PKERB_SMART_CARD_PROFILE) pGlobals->Profile ;
pGlobals->UserName[0] = 0 ;
try
{
Cert = CertCreateCertificateContext( X509_ASN_ENCODING,
ScProfile->CertificateData,
ScProfile->CertificateSize );
if ( Cert )
{
// Even though the name is MAX_STRING_BYTES, the way it is used
// throughout the code, it is used as a character counter
// (Grrr, crappy gina code)
//
DWORD dwLen = MAX_STRING_BYTES;
if(STATUS_SUCCESS == UpnFromCert(Cert, &dwLen, pGlobals->UserName))
{
RtlInitUnicodeString( &pGlobals->UserNameString,
pGlobals->UserName );
}
CertFreeCertificateContext( Cert );
}
}
except( EXCEPTION_EXECUTE_HANDLER )
{
pGlobals->UserName[0] = L'\0';
}
//
// If this is still 0 on exit, the code that sets up the flat name
// will copy the flat name into UserName, so the failure case is
// easy.
//
}
pGlobals->SmartCardOption = GetProfileInt( APPLICATION_NAME, SC_REMOVE_OPTION, 0 );
//
// WE SHOULD NOT WRITE INTO THE REGISTRY.
// CLupu
//
//
// Update our default username and domain ready for the next logon
//
//
// Update the default username & domain only if on the console. Otherwise
// we'll break AutoAdminLogon by changing the user name.
//
if ( g_Console )
{
if ( (!pGlobals->AutoAdminLogon) &&
(SafeBootMode != SAFEBOOT_MINIMAL ) )
{
WriteProfileString(APPLICATION_NAME, DEFAULT_USER_NAME_KEY, pGlobals->UserName);
WriteProfileString(APPLICATION_NAME, DEFAULT_DOMAIN_NAME_KEY, pGlobals->Domain);
}
WriteProfileString(APPLICATION_NAME, TEMP_DEFAULT_USER_NAME_KEY, pGlobals->UserName);
WriteProfileString(APPLICATION_NAME, TEMP_DEFAULT_DOMAIN_NAME_KEY, pGlobals->Domain);
}
if ( pGlobals->Domain[0] )
{
DCacheSetDefaultEntry( pGlobals->Cache,
pGlobals->Domain,
NULL );
}
Result = MSGINA_DLG_SUCCESS;
exit:
#ifdef SMARTCARD_DOGFOOD
if (pGlobals->SmartCardLogon) {
switch (SubStatus)
{
case STATUS_SMARTCARD_WRONG_PIN:
case STATUS_SMARTCARD_CARD_BLOCKED:
case STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED:
case STATUS_SMARTCARD_NO_CARD:
case STATUS_SMARTCARD_NO_KEY_CONTAINER:
case STATUS_SMARTCARD_NO_CERTIFICATE:
case STATUS_SMARTCARD_NO_KEYSET:
case STATUS_SMARTCARD_IO_ERROR:
case STATUS_SMARTCARD_SUBSYSTEM_FAILURE:
case STATUS_SMARTCARD_CERT_EXPIRED:
case STATUS_SMARTCARD_CERT_REVOKED:
case STATUS_ISSUING_CA_UNTRUSTED:
case STATUS_REVOCATION_OFFLINE_C:
case STATUS_PKINIT_CLIENT_FAILURE:
FinalStatus = SubStatus;
break;
default:
break; // do NOTHING
}
// write logon data to database
AuthMonitor(
AuthOperLogon,
g_Console,
&pGlobals->UserNameString,
&pGlobals->DomainString,
(ScInfo ? ScInfo->pszCard : NULL),
(ScInfo ? ScInfo->pszReader : NULL),
(PKERB_SMART_CARD_PROFILE) pGlobals->Profile,
EndTime - StartTime,
FinalStatus
);
}
if (ScInfo)
{
LocalFree( ScInfo );
}
#endif
// Only send a logon complete message if we haven't sent a failed
// message. The failed message will send a logon complete message
// when its done.
if (Result != MSGINA_DLG_FAILEDMSGSENT)
{
PostMessage(pGlobals->hwndLogon, WM_LOGONCOMPLETE, 0, Result);
}
return 0L;
}
/****************************************************************************\
*
* FUNCTION: PostFailedLogonMessage
*
* PURPOSE: Posts a message to the UI thread telling it to display a dialog that
* tells the user why their logon attempt failed.
*
* The window on the UI thread must correctly handle WM_HANDLEFAILEDLOGON
* by calling HandleFailedLogon and the Free'ing the structure
*
* RETURNS: void
*
* HISTORY:
*
* 12-09-91 Davidc Created.
*
\****************************************************************************/
void PostFailedLogonMessage(HWND hDlg,
PGLOBALS pGlobals,
NTSTATUS Status,
NTSTATUS SubStatus,
PWCHAR UserName,
PWCHAR Domain
)
{
g_failinfo.pGlobals = pGlobals;
g_failinfo.Status = Status;
g_failinfo.SubStatus = SubStatus;
if ( UserName )
{
lstrcpyn(g_failinfo.UserName, UserName, ARRAYSIZE(g_failinfo.UserName));
}
else
{
g_failinfo.UserName[0] = L'\0';
}
if ( Domain )
{
lstrcpyn(g_failinfo.Domain, Domain, ARRAYSIZE(g_failinfo.Domain));
}
else
{
g_failinfo.Domain[0] = L'\0' ;
}
PostMessage(hDlg, WM_HANDLEFAILEDLOGON, 0 , 0);
}
INT_PTR
CALLBACK
FailDlgProc(
HWND hDlg,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
RUNDLLPROC fptr;
HMODULE hDll;
switch (message)
{
case WM_INITDIALOG:
{
CentreWindow(hDlg);
return( TRUE );
}
case WM_COMMAND:
{
if (LOWORD(wParam) == IDOK)
{
EndDialog(hDlg, IDOK);
}
if (LOWORD(wParam) == IDC_RECOVER)
{
// Will eventually supply username to the recover wizard
// 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.
hDll = LoadLibraryW(L"keymgr.dll");
if (hDll)
{
fptr = (RUNDLLPROC) GetProcAddress(hDll,(LPCSTR)"PRShowRestoreFromMsginaW");
// next stmt will be removed eventually when we pass the username
if (fptr)
{
fptr(hDlg,NULL,g_failinfo.UserName,0);
}
FreeLibrary(hDll);
EndDialog(hDlg,IDOK);
}
}
}
break;
}
return FALSE;
}
/****************************************************************************\
*
* FUNCTION: HandleFailedLogon
*
* PURPOSE: Tells the user why their logon 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:
*
* 12-09-91 Davidc Created.
*
\****************************************************************************/
INT_PTR
HandleFailedLogon(
HWND hDlg
)
{
INT_PTR Result = 0xffffffff;
DWORD Win32Error ;
TCHAR *Buffer1 = NULL;
TCHAR *Buffer2 = NULL;
TCHAR *Buffer3 = NULL;
PGLOBALS pGlobals = g_failinfo.pGlobals;
NTSTATUS Status = g_failinfo.Status;
NTSTATUS SubStatus = g_failinfo.SubStatus;
PWCHAR Domain = g_failinfo.Domain;
DWORD BUStatus = 0xffffffff;
UINT uiMsgId = 0xabab; // abab is out of range value for default handler at the bottom of this
// routine. 0 indicates that the user has a psw reset disk
// -1 means that Buffer1 & 2 contain the message
// otherwise there is a corresponding resource message
//
// for remote sessions, we must set finite timeout value for messagebox.
// so that the session does not remain there forever
//
DWORD TimeOut = IsActiveConsoleSession() ? TIMEOUT_CURRENT : 20;
switch (Status)
{
case STATUS_LOGON_FAILURE:
case STATUS_NAME_TOO_LONG: // Returned if username is too long
if (SubStatus == IDS_LOGON_LOG_FULL)
{
uiMsgId = IDS_LOGON_LOG_FULL;
}
else if (pGlobals->SmartCardLogon)
{
switch(SubStatus)
{
case STATUS_SMARTCARD_WRONG_PIN:
uiMsgId = IDS_STATUS_SMARTCARD_WRONG_PIN;
break;
case STATUS_SMARTCARD_CARD_BLOCKED:
uiMsgId = IDS_STATUS_SMARTCARD_CARD_BLOCKED;
break;
case STATUS_SMARTCARD_NO_CARD:
uiMsgId = IDS_STATUS_SMARTCARD_NO_CARD;
break;
case STATUS_SMARTCARD_NO_KEY_CONTAINER:
uiMsgId = IDS_STATUS_SMARTCARD_NO_KEY_CONTAINER;
break;
case STATUS_SMARTCARD_NO_CERTIFICATE:
uiMsgId = IDS_STATUS_SMARTCARD_NO_CERTIFICATE;
break;
case STATUS_SMARTCARD_NO_KEYSET:
uiMsgId = IDS_STATUS_SMARTCARD_NO_KEYSET;
break;
case STATUS_SMARTCARD_IO_ERROR:
uiMsgId = IDS_STATUS_SMARTCARD_IO_ERROR;
break;
case STATUS_SMARTCARD_SUBSYSTEM_FAILURE:
uiMsgId = IDS_STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
break;
case STATUS_SMARTCARD_CERT_REVOKED:
uiMsgId = IDS_STATUS_SMARTCARD_CERT_REVOKED;
break;
case STATUS_ISSUING_CA_UNTRUSTED:
uiMsgId = IDS_STATUS_ISSUING_CA_UNTRUSTED;
break;
case STATUS_REVOCATION_OFFLINE_C:
uiMsgId = IDS_STATUS_REVOCATION_OFFLINE_C;
break;
case STATUS_PKINIT_CLIENT_FAILURE:
uiMsgId = IDS_STATUS_PKINIT_CLIENT_FAILURE;
break;
case STATUS_SMARTCARD_CERT_EXPIRED:
uiMsgId = IDS_STATUS_SMARTCARD_CERT_EXPIRED;
break;
default:
uiMsgId = IDS_INCORRECT_NAME_OR_PWD_SC;
}
}
else
{
// Non-smartcard logon case:
// Find out if the user who attempted logon has a password backup disk
// that could be used to reset the password. If so, present a dialog that
// offers that possibility. Else simple message box. (see passrec.h)
if ((0 == PRQueryStatus(NULL,g_failinfo.UserName,&BUStatus)) && (0 == GetSystemMetrics(SM_REMOTESESSION)))
{
if (BUStatus == 0)
{
uiMsgId = 0;
break;
}
}
// Else UI message is generic one
uiMsgId = IDS_INCORRECT_NAME_OR_PWD;
}
break;
case STATUS_NOT_SUPPORTED:
case STATUS_PKINIT_NAME_MISMATCH:
case STATUS_PKINIT_FAILURE:
Buffer1 = LocalAlloc(LPTR, MAX_STRING_BYTES * sizeof(TCHAR));
Buffer2 = LocalAlloc(LPTR, MAX_STRING_BYTES * sizeof(TCHAR));
if ((Buffer1 == NULL) || (Buffer2 == NULL))
{
uiMsgId = IDS_STATUS_SERVER_SIDE_ERROR_NOINSERT;
}
else
{
LoadString(hDllInstance,
IDS_STATUS_SERVER_SIDE_ERROR,
Buffer1,
MAX_STRING_BYTES);
_snwprintf(Buffer2, MAX_STRING_BYTES, Buffer1, Status );
LoadString(hDllInstance,
IDS_LOGON_MESSAGE,
Buffer1,
MAX_STRING_BYTES);
uiMsgId = (DWORD)-1;
}
break;
case STATUS_ACCOUNT_RESTRICTION:
switch (SubStatus)
{
case STATUS_INVALID_LOGON_HOURS:
uiMsgId = IDS_INVALID_LOGON_HOURS;
break;
case STATUS_INVALID_WORKSTATION:
uiMsgId = IDS_INVALID_WORKSTATION;
break;
case STATUS_ACCOUNT_DISABLED:
uiMsgId = IDS_ACCOUNT_DISABLED;
break;
case STATUS_ACCOUNT_EXPIRED:
uiMsgId = IDS_ACCOUNT_EXPIRED2;
break;
case STATUS_SMARTCARD_LOGON_REQUIRED:
uiMsgId = IDS_SMARTCARD_REQUIRED;
break;
default:
uiMsgId = IDS_ACCOUNT_RESTRICTION;
break;
}
break;
case STATUS_NO_LOGON_SERVERS:
Buffer1 = LocalAlloc(LPTR, MAX_STRING_BYTES * sizeof(TCHAR));
Buffer2 = LocalAlloc(LPTR, MAX_STRING_BYTES * sizeof(TCHAR));
if ((Buffer1 == NULL) || (Buffer2 == NULL))
{
uiMsgId = IDS_LOGON_NO_DOMAIN_NOINSERT;
}
else
{
LoadString(hDllInstance, IDS_LOGON_NO_DOMAIN, Buffer1, MAX_STRING_BYTES);
_snwprintf(Buffer2, MAX_STRING_BYTES, Buffer1, Domain);
LoadString(hDllInstance, IDS_LOGON_MESSAGE, Buffer1, MAX_STRING_BYTES);
uiMsgId = (DWORD)-1;
}
break;
case STATUS_LOGON_TYPE_NOT_GRANTED:
uiMsgId = IDS_LOGON_TYPE_NOT_GRANTED;
break;
case STATUS_NO_TRUST_LSA_SECRET:
uiMsgId = IDS_NO_TRUST_LSA_SECRET;
break;
case STATUS_TRUSTED_DOMAIN_FAILURE:
uiMsgId = IDS_TRUSTED_DOMAIN_FAILURE;
break;
case STATUS_TRUSTED_RELATIONSHIP_FAILURE:
uiMsgId = IDS_TRUSTED_RELATIONSHIP_FAILURE;
break;
case STATUS_ACCOUNT_EXPIRED:
uiMsgId = IDS_ACCOUNT_EXPIRED;
break;
case STATUS_NETLOGON_NOT_STARTED:
uiMsgId = IDS_NETLOGON_NOT_STARTED;
break;
case STATUS_ACCOUNT_LOCKED_OUT:
uiMsgId = IDS_ACCOUNT_LOCKED;
break;
case ERROR_CTX_LOGON_DISABLED:
uiMsgId = IDS_MULTIUSER_LOGON_DISABLED;
break;
case ERROR_CTX_WINSTATION_ACCESS_DENIED:
uiMsgId = IDS_MULTIUSER_WINSTATION_ACCESS_DENIED;
break;
case SCARD_E_NO_SMARTCARD:
case SCARD_E_UNKNOWN_CARD:
//
// Card not recognized (although we should never get this far)
//
uiMsgId = IDS_CARD_NOT_RECOGNIZED;
break;
case NTE_PROV_DLL_NOT_FOUND:
//
// Card's CSP not found (although we should never get this far)
//
uiMsgId = IDS_CARD_CSP_NOT_RECOGNIZED;
break;
case STATUS_TIME_DIFFERENCE_AT_DC:
uiMsgId = IDS_TIME_DIFFERENCE_AT_DC;
break;
default:
WLPrint(("Logon failure status = 0x%lx, sub-status = 0x%lx", Status, SubStatus));
Buffer1 = LocalAlloc(LPTR, MAX_STRING_BYTES * sizeof(TCHAR));
Buffer2 = LocalAlloc(LPTR, MAX_STRING_BYTES * sizeof(TCHAR));
Buffer3 = LocalAlloc(LPTR, MAX_STRING_BYTES * sizeof(TCHAR));
if ((Buffer1 == NULL) || (Buffer2 == NULL) || (Buffer3 == NULL))
{
uiMsgId = IDS_UNKNOWN_LOGON_FAILURE_NOINSERT;
}
else
{
LoadString(hDllInstance,
IDS_UNKNOWN_LOGON_FAILURE,
Buffer1,
MAX_STRING_BYTES);
if ( NT_ERROR( Status ) )
{
Win32Error = RtlNtStatusToDosError( Status );
}
else
{
//
// Probably an HRESULT:
//
Win32Error = Status ;
}
GetErrorDescription( Win32Error, Buffer3, MAX_STRING_BYTES);
_snwprintf(Buffer2, MAX_STRING_BYTES, Buffer1, Buffer3 );
LoadString(hDllInstance,
IDS_LOGON_MESSAGE,
Buffer1,
MAX_STRING_BYTES);
uiMsgId = (DWORD)-1;
}
break;
}
_Shell_LogonDialog_HideUIHost();
switch (uiMsgId)
{
case 0:
// User has a password reset disk - present the option to use it along with the usual
// help message
pWlxFuncs->WlxSetTimeout(pGlobals->hGlobalWlx,LOGON_TIMEOUT);
Result = pWlxFuncs->WlxDialogBoxParam(pGlobals->hGlobalWlx,
hDllInstance,
(LPTSTR) IDD_FAILLOGONHELP_DIALOG,
hDlg,
FailDlgProc,
0);
break;
case (DWORD)-1:
Result = TimeoutMessageBoxlpstr(hDlg, pGlobals,
Buffer2,
Buffer1,
MB_OK | MB_ICONEXCLAMATION,
TimeOut);
break;
default:
Result = TimeoutMessageBox(hDlg, pGlobals,
uiMsgId,
IDS_LOGON_MESSAGE,
MB_OK | MB_ICONEXCLAMATION,
TimeOut);
}
if (Buffer1 != NULL)
LocalFree(Buffer1);
if (Buffer2 != NULL)
LocalFree(Buffer2);
if (Buffer3 != NULL)
LocalFree(Buffer3);
if (!DLG_INTERRUPTED(Result))
{
Result = MSGINA_DLG_FAILURE;
}
return(Result);
}
VOID
ReportBootGoodThread (LPVOID lpDummy)
{
HANDLE hInstDll;
// PGLOBALS pGlobals = (PGLOBALS)lpDummy;
// SetThreadDesktop(pGlobals->hdeskParent);
hInstDll = LoadLibrary (TEXT("msgina.dll"));
NotifyBootConfigStatus(TRUE);
if (hInstDll) {
FreeLibraryAndExitThread(hInstDll, TRUE);
} else {
ExitThread (TRUE);
}
}
/****************************************************************************\
*
* FUNCTION: ReportBootGood
*
* PURPOSE: Discover if reporting boot success is responsibility of
* winlogon or not.
* If it is, report boot success.
* Otherwise, do nothing.
*
* RETURNS: Nothing
*
* HISTORY:
*
* 02-Feb-1993 bryanwi - created
*
\****************************************************************************/
VOID
ReportBootGood(PGLOBALS pGlobals)
{
static DWORD fDoIt = (DWORD) -1; // -1 == uninited
// 0 == don't do it, or done
// 1 == do it
PWCH pchData;
DWORD cb, cbCopied;
HANDLE hThread;
DWORD dwThreadID;
if (fDoIt == -1) {
if ((pchData = Alloc(cb = sizeof(TCHAR)*128)) == NULL) {
return;
}
pchData[0] = TEXT('0');
cbCopied = GetProfileString(APPLICATION_NAME, REPORT_BOOT_OK_KEY, TEXT("0"),
(LPTSTR)pchData, 128);
fDoIt = 0;
if (pchData[0] != TEXT('0')) {
//
// "ReportBootGood" is present, and has some value other than
// '0', so report success.
//
fDoIt = 1;
}
Free((TCHAR *)pchData);
}
if (fDoIt == 1) {
hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)ReportBootGoodThread,
pGlobals, CREATE_SUSPENDED, &dwThreadID);
if (hThread) {
SetThreadPriority (hThread, THREAD_PRIORITY_LOWEST);
ResumeThread (hThread);
CloseHandle (hThread);
} else {
NotifyBootConfigStatus(TRUE);
}
fDoIt = 0;
}
return;
}
//+---------------------------------------------------------------------------
//
// Function: UpnFromCert
//
// Notes:
//
//----------------------------------------------------------------------------
NTSTATUS
UpnFromCert(
IN PCCERT_CONTEXT pCert,
IN OUT DWORD *pcUpn,
IN OUT LPWSTR pUPN
)
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG ExtensionIndex = 0;
PCERT_ALT_NAME_INFO AltName=NULL;
PCERT_NAME_VALUE PrincipalNameBlob = NULL;
//
// Get the client name from the cert
//
// See if cert has UPN in AltSubjectName->otherName
for(ExtensionIndex = 0;
ExtensionIndex < pCert->pCertInfo->cExtension;
ExtensionIndex++)
{
if(strcmp(pCert->pCertInfo->rgExtension[ExtensionIndex].pszObjId,
szOID_SUBJECT_ALT_NAME2) == 0)
{
DWORD AltNameStructSize = 0;
ULONG CertAltNameIndex = 0;
if(CryptDecodeObjectEx(pCert->dwCertEncodingType,
X509_ALTERNATE_NAME,
pCert->pCertInfo->rgExtension[ExtensionIndex].Value.pbData,
pCert->pCertInfo->rgExtension[ExtensionIndex].Value.cbData,
CRYPT_DECODE_ALLOC_FLAG,
NULL,
(PVOID)&AltName,
&AltNameStructSize))
{
for(CertAltNameIndex = 0; CertAltNameIndex < AltName->cAltEntry; CertAltNameIndex++)
{
PCERT_ALT_NAME_ENTRY AltNameEntry = &AltName->rgAltEntry[CertAltNameIndex];
if((CERT_ALT_NAME_OTHER_NAME == AltNameEntry->dwAltNameChoice) &&
(NULL != AltNameEntry->pOtherName) &&
(0 == strcmp(szOID_NT_PRINCIPAL_NAME, AltNameEntry->pOtherName->pszObjId)))
{
DWORD PrincipalNameBlobSize = 0;
// We found a UPN!
if(CryptDecodeObjectEx(pCert->dwCertEncodingType,
X509_UNICODE_ANY_STRING,
AltNameEntry->pOtherName->Value.pbData,
AltNameEntry->pOtherName->Value.cbData,
CRYPT_DECODE_ALLOC_FLAG,
NULL,
(PVOID)&PrincipalNameBlob,
&PrincipalNameBlobSize))
{
if(PrincipalNameBlob->Value.cbData + sizeof(WCHAR) > *pcUpn)
{
Status = STATUS_BUFFER_OVERFLOW;
}
else
{
*pcUpn = PrincipalNameBlob->Value.cbData + sizeof(WCHAR);
CopyMemory(pUPN, PrincipalNameBlob->Value.pbData, PrincipalNameBlob->Value.cbData);
*(WCHAR *)((PBYTE)pUPN+PrincipalNameBlob->Value.cbData) = 0;
}
LocalFree(PrincipalNameBlob);
PrincipalNameBlob = NULL;
LocalFree(AltName);
AltName = NULL;
goto Finished;
}
}
}
LocalFree(AltName);
AltName = NULL;
}
}
}
//
// If the name was not found in the UPN, then
// we grab it the old way.
if ( !CertGetNameString( pCert,
CERT_NAME_ATTR_TYPE,
0,
szOID_COMMON_NAME,
pUPN,
*pcUpn ) )
{
Status = GetLastError();
}
Finished:
return Status ;
}
//+---------------------------------------------------------------------------
//
// Function:TSAuthenticatedLogon
//
// Notes: This routine gets called in response to WLX_SAS_TYPE_AUTHENTICATED
// in the context of the console session (sessionid 0) winlogon.
// This type of logon is for Single Session Terminal Server. When a user
// logs on from a remote TS session, we pass the credentials from the remote session
// to the console session and do an auto-logon. This routine queries the credentials
// logs on the user on the console sesion
//
//
//----------------------------------------------------------------------------
INT_PTR TSAuthenticatedLogon(PGLOBALS pGlobals)
{
PSID LogonSid;
LUID LogonId;
HANDLE UserToken;
HANDLE RestrictedToken;
INT_PTR Result = MSGINA_DLG_SUCCESS;
UCHAR UserBuffer[ SID_MAX_SUB_AUTHORITIES * sizeof( DWORD ) + 8 + sizeof( TOKEN_USER ) ];
PTOKEN_USER pTokenUser ;
ULONG TokenInfoSize ;
NTSTATUS Status;
BYTE GroupsBuffer[sizeof(TOKEN_GROUPS)+sizeof(SID_AND_ATTRIBUTES)];
PTOKEN_GROUPS TokenGroups = (PTOKEN_GROUPS) GroupsBuffer;
if (!QuerySwitchConsoleCredentials(pGlobals,&UserToken,&LogonId)) {
Result = MSGINA_DLG_FAILEDMSGSENT;
goto exit;
}
if (pGlobals->SmartCardLogon) {
wcscpy(pGlobals->Password,L"");
wcscpy(pGlobals->OldPassword,L"");
}
else
{
wcscpy(pGlobals->Password,L"");
RtlInitUnicodeString(&pGlobals->PasswordString,pGlobals->Password);
wcscpy(pGlobals->OldPassword,L"");
RtlInitUnicodeString(&pGlobals->OldPasswordString,pGlobals->OldPassword);
}
RtlInitUnicodeString(&pGlobals->UserNameString, pGlobals->UserName);
RtlInitUnicodeString(&pGlobals->DomainString, pGlobals->Domain);
pGlobals->RasUsed = FALSE;
pGlobals->hwndLogon = NULL;
//
// Generate a unique sid for this logon
//
if (!GetAndAllocateLogonSid(UserToken,&(pGlobals->LogonSid))) {
Result = MSGINA_DLG_FAILEDMSGSENT;
if (pGlobals->Profile) {
VirtualFree(pGlobals->Profile, 0, MEM_RELEASE);
pGlobals->Profile = NULL;
pGlobals->ProfileLength = 0;
}
goto exit;
}
LogonSid = pGlobals->LogonSid;
//
// The user logged on successfully
//
//
// Create a filtered version of the token for running normal applications
// if so indicated by a registry setting
//
if (GetProfileInt( APPLICATION_NAME, RESTRICT_SHELL, 0) != 0) {
TokenGroups->Groups[0].Attributes = 0;
TokenGroups->Groups[0].Sid = gAdminSid;
TokenGroups->GroupCount = 1;
Status = NtFilterToken(
UserToken,
DISABLE_MAX_PRIVILEGE,
TokenGroups, // disable the administrators sid
NULL, // no privileges
NULL,
&RestrictedToken
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "Failed to filter token: 0x%%x\n", Status));
RestrictedToken = NULL;
}
//
// Now set the default dacl for the token
//
{
PACL Dacl = NULL;
ULONG DaclLength = 0;
TOKEN_DEFAULT_DACL DefaultDacl;
DaclLength = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(LogonSid);
Dacl = Alloc(DaclLength);
Status = RtlCreateAcl(Dacl,DaclLength, ACL_REVISION);
ASSERT(NT_SUCCESS(Status));
Status = RtlAddAccessAllowedAce(
Dacl,
ACL_REVISION,
GENERIC_ALL,
LogonSid
);
ASSERT(NT_SUCCESS(Status));
DefaultDacl.DefaultDacl = Dacl;
Status = NtSetInformationToken(
RestrictedToken,
TokenDefaultDacl,
&DefaultDacl,
sizeof(TOKEN_DEFAULT_DACL)
);
ASSERT(NT_SUCCESS(Status));
Free(Dacl);
}
} else {
RestrictedToken = NULL;
}
//
// Notify credential managers of the successful logon
//
pTokenUser = (PTOKEN_USER) UserBuffer ;
Status = NtQueryInformationToken( UserToken,
TokenUser,
pTokenUser,
sizeof( UserBuffer ),
&TokenInfoSize );
if ( NT_SUCCESS( Status ) )
{
pGlobals->UserProcessData.UserSid = LocalAlloc( LMEM_FIXED,
RtlLengthSid( pTokenUser->User.Sid ) );
if ( pGlobals->UserProcessData.UserSid )
{
RtlCopyMemory( pGlobals->UserProcessData.UserSid,
pTokenUser->User.Sid,
RtlLengthSid( pTokenUser->User.Sid ) );
}
else
{
Status = STATUS_NO_MEMORY ;
}
}
if ( !NT_SUCCESS( Status ) )
{
if (pGlobals->Profile) {
VirtualFree(pGlobals->Profile, 0, MEM_RELEASE);
pGlobals->Profile = NULL;
pGlobals->ProfileLength = 0;
}
NtClose(UserToken);
Result = MSGINA_DLG_FAILEDMSGSENT;
goto exit;
}
pGlobals->UserProcessData.RestrictedToken = RestrictedToken;
pGlobals->UserProcessData.UserToken = UserToken;
pGlobals->UserProcessData.NewThreadTokenSD = CreateUserThreadTokenSD(LogonSid, pWinlogonSid);
pGlobals->MprLogonScripts = NULL;
pGlobals->ExtraApps = NULL ;
//
// If we get here, the system works well enough for the user to have
// actually logged on. Profile failures aren't fixable by last known
// good anyway. Therefore, declare the boot good.
//
ReportBootGood(pGlobals);
//
// Set up the system for the new user
//
pGlobals->LogonId = LogonId;
if ((pGlobals->Profile != NULL) && (pGlobals->Profile->FullName.Length > 0)) {
if (pGlobals->Profile->FullName.Length > MAX_STRING_LENGTH) {
wcsncpy(pGlobals->UserFullName, pGlobals->Profile->FullName.Buffer, MAX_STRING_LENGTH);
*(pGlobals->UserFullName + MAX_STRING_LENGTH) = UNICODE_NULL;
}
else {
lstrcpy(pGlobals->UserFullName, pGlobals->Profile->FullName.Buffer);
}
} else {
//
// No profile - set full name = NULL
pGlobals->UserFullName[0] = 0;
ASSERT( lstrlen(pGlobals->UserFullName) == 0);
}
//
// Update our default username and domain ready for the next logon
//
//
// Update the default username & domain only if on the console. Otherwise
// we'll break AutoAdminLogon by changing the user name.
//
if ( g_Console )
{
if ( (!pGlobals->AutoAdminLogon) &&
(SafeBootMode != SAFEBOOT_MINIMAL ) )
{
WriteProfileString(APPLICATION_NAME, DEFAULT_USER_NAME_KEY, pGlobals->UserName);
WriteProfileString(APPLICATION_NAME, DEFAULT_DOMAIN_NAME_KEY, pGlobals->Domain);
}
WriteProfileString(APPLICATION_NAME, TEMP_DEFAULT_USER_NAME_KEY, pGlobals->UserName);
WriteProfileString(APPLICATION_NAME, TEMP_DEFAULT_DOMAIN_NAME_KEY, pGlobals->Domain);
}
if ( pGlobals->Domain[0] == '\0' )
{
GetProfileString( APPLICATION_NAME,
DEFAULT_DOMAIN_NAME_KEY,
TEXT(""),
pGlobals->Domain,
MAX_STRING_BYTES );
}
if ( !DCacheValidateCache( pGlobals->Cache ) )
{
ASSERT( pGlobals->ActiveArray == NULL );
DCacheUpdateMinimal( pGlobals->Cache, pGlobals->Domain, TRUE );
}
else
{
//
// Set the current default:
//
DCacheSetDefaultEntry( pGlobals->Cache,
pGlobals->Domain,
NULL );
}
Result = MSGINA_DLG_SUCCESS;
exit:
return Result;
}
PWSTR
AllocAndDuplicateString(
PWSTR pszString,
int len)
{
PWSTR pszNewString;
if (!pszString || !len)
{
return(NULL);
}
pszNewString = LocalAlloc(LMEM_FIXED, (len + 2)*sizeof(WCHAR));
if (pszNewString)
{
wcsncpy(pszNewString, pszString, len);
pszNewString[len] = UNICODE_NULL;
}
return(pszNewString);
}
BOOL
WINAPI
WlxGetConsoleSwitchCredentials (
PVOID pWlxContext,
PVOID pInfo
)
{
PGLOBALS pGlobals = (PGLOBALS) pWlxContext;
PWLX_CONSOLESWITCH_CREDENTIALS_INFO_V1_0 pReq = (PWLX_CONSOLESWITCH_CREDENTIALS_INFO_V1_0)pInfo;
BOOL bReturn = FALSE;
if (pReq->dwType != WLX_CONSOLESWITCHCREDENTIAL_TYPE_V1_0) {
return FALSE;
}
//
// Initialize allocated pointers.
//
pReq->UserName = NULL;
pReq->Domain = NULL;
pReq->LogonScript = NULL;
pReq->HomeDirectory = NULL;
pReq->FullName = NULL;
pReq->ProfilePath = NULL;
pReq->HomeDirectoryDrive = NULL;
pReq->LogonServer = NULL;
pReq->PrivateData = NULL;
pReq->LogonId = pGlobals->LogonId;
pReq->UserToken = pGlobals->UserProcessData.UserToken;
pReq->LogonTime = pGlobals->LogonTime;
pReq->SmartCardLogon = pGlobals->SmartCardLogon;
pReq->UserName = AllocAndDuplicateString(pGlobals->UserName,
(DWORD) wcslen(pGlobals->UserName));
pReq->Domain = AllocAndDuplicateString(pGlobals->Domain,
(DWORD) wcslen(pGlobals->Domain));
//
// Quota Information
//
pReq->Quotas.PagedPoolLimit = pGlobals->UserProcessData.Quotas.PagedPoolLimit;
pReq->Quotas.NonPagedPoolLimit = pGlobals->UserProcessData.Quotas.NonPagedPoolLimit;
pReq->Quotas.MinimumWorkingSetSize = pGlobals->UserProcessData.Quotas.MinimumWorkingSetSize;
pReq->Quotas.MaximumWorkingSetSize = pGlobals->UserProcessData.Quotas.MaximumWorkingSetSize;
pReq->Quotas.PagefileLimit = pGlobals->UserProcessData.Quotas.PagefileLimit;
pReq->Quotas.TimeLimit = pGlobals->UserProcessData.Quotas.TimeLimit;
//
// Profile Information
//
pReq->ProfileLength = pGlobals->ProfileLength;
pReq->UserFlags = pGlobals->Profile->UserFlags;
pReq->MessageType = pGlobals->Profile->MessageType;
pReq->LogonCount = pGlobals->Profile->LogonCount;
pReq->BadPasswordCount = pGlobals->Profile->BadPasswordCount;
pReq->ProfileLogonTime = pGlobals->Profile->LogonTime;
pReq->LogoffTime = pGlobals->Profile->LogoffTime;
pReq->KickOffTime = pGlobals->Profile->KickOffTime;
pReq->PasswordLastSet = pGlobals->Profile->PasswordLastSet;
pReq->PasswordCanChange = pGlobals->Profile->PasswordCanChange;
pReq->PasswordMustChange = pGlobals->Profile->PasswordMustChange;
pReq->LogonScript = AllocAndDuplicateString(pGlobals->Profile->LogonScript.Buffer, pGlobals->Profile->LogonScript.Length/sizeof(WCHAR));
pReq->HomeDirectory = AllocAndDuplicateString(pGlobals->Profile->HomeDirectory.Buffer, pGlobals->Profile->HomeDirectory.Length/sizeof(WCHAR));
pReq->FullName = AllocAndDuplicateString(pGlobals->Profile->FullName.Buffer, pGlobals->Profile->FullName.Length/sizeof(WCHAR));
pReq->ProfilePath = AllocAndDuplicateString(pGlobals->Profile->ProfilePath.Buffer, pGlobals->Profile->ProfilePath.Length/sizeof(WCHAR));
pReq->HomeDirectoryDrive = AllocAndDuplicateString(pGlobals->Profile->HomeDirectoryDrive.Buffer, pGlobals->Profile->HomeDirectoryDrive.Length/sizeof(WCHAR));
pReq->LogonServer = AllocAndDuplicateString(pGlobals->Profile->LogonServer.Buffer, pGlobals->Profile->LogonServer.Length/sizeof(WCHAR));
pReq->PrivateDataLen = PASSWORD_HASH_SIZE;
pReq->PrivateData = (PBYTE)AllocAndDuplicateString((PWSTR)pGlobals->PasswordHash, PASSWORD_HASH_SIZE );
if (pReq->PrivateData == NULL) {
goto done;
}
memcpy(pReq->PrivateData, pGlobals->PasswordHash, PASSWORD_HASH_SIZE );
bReturn = TRUE;
done:
if (!bReturn) {
if (pReq->UserName != NULL) {
LocalFree(pReq->UserName);
}
if (pReq->Domain != NULL) {
LocalFree(pReq->Domain);
}
if (pReq->LogonScript != NULL) {
LocalFree(pReq->LogonScript);
}
if (pReq->HomeDirectory != NULL) {
LocalFree(pReq->HomeDirectory);
}
if (pReq->FullName != NULL) {
LocalFree(pReq->FullName);
}
if (pReq->ProfilePath != NULL) {
LocalFree(pReq->ProfilePath);
}
if (pReq->HomeDirectoryDrive != NULL) {
LocalFree(pReq->HomeDirectoryDrive);
}
if (pReq->LogonServer != NULL) {
LocalFree(pReq->LogonServer);
}
if (pReq->PrivateData != NULL) {
LocalFree(pReq->PrivateData);
}
}
return bReturn;
}
//+---------------------------------------------------------------------------
//
// Function: QuerySwitchConsoleCredentials
//
// Notes:
//
// Query credentials from session connecting to console to do switch console
// This routine gets called in response to WLX_SAS_TYPE_AUTHENTICATED
// in the context of the console session (sessionid 0) winlogon.
// This type of logon is for Single Session Terminal Server. When a user
// logs on from a remote TS session, we pass the credentials from the remote session
// to the console session and do an auto-logon. This routine queries the credentials,
// logs on the user on the console sesion
//
//
//----------------------------------------------------------------------------
BOOL
WINAPI
QuerySwitchConsoleCredentials(PGLOBALS pGlobals, HANDLE * phUserToken, PLUID pLogonId)
{
WLX_CONSOLESWITCH_CREDENTIALS_INFO_V1_0 CredInfo;
RtlZeroMemory(&CredInfo,sizeof(CredInfo));
CredInfo.dwType = WLX_CONSOLESWITCHCREDENTIAL_TYPE_V1_0;
if (!pWlxFuncs->WlxQueryConsoleSwitchCredentials(&CredInfo)){
return FALSE;
}
if (!CredInfo.UserToken || !CredInfo.UserName) {
//return false if any of the critical information is missing
return FALSE;
}
pGlobals->Profile = (PMSV1_0_INTERACTIVE_PROFILE) VirtualAlloc(NULL,
sizeof(MSV1_0_INTERACTIVE_PROFILE),
MEM_COMMIT,
PAGE_READWRITE);
if (pGlobals->Profile == NULL) {
goto returnerror;
}
//
// Token, LUID
//
*pLogonId = CredInfo.LogonId;
*phUserToken = CredInfo.UserToken;
pGlobals->LogonTime = CredInfo.LogonTime;
pGlobals->SmartCardLogon = CredInfo.SmartCardLogon;
pGlobals->SmartCardOption = GetProfileInt( APPLICATION_NAME, SC_REMOVE_OPTION, 0 );
//
// Quota Information
//
pGlobals->UserProcessData.Quotas.PagedPoolLimit = CredInfo.Quotas.PagedPoolLimit ;
pGlobals->UserProcessData.Quotas.NonPagedPoolLimit = CredInfo.Quotas.NonPagedPoolLimit;
pGlobals->UserProcessData.Quotas.MinimumWorkingSetSize = CredInfo.Quotas.MinimumWorkingSetSize;
pGlobals->UserProcessData.Quotas.MaximumWorkingSetSize = CredInfo.Quotas.MaximumWorkingSetSize;
pGlobals->UserProcessData.Quotas.PagefileLimit = CredInfo.Quotas.PagefileLimit;
pGlobals->UserProcessData.Quotas.TimeLimit = CredInfo.Quotas.TimeLimit;
//
// Profile Information
//
pGlobals->ProfileLength = CredInfo.ProfileLength;
pGlobals->Profile->UserFlags = CredInfo.UserFlags;
pGlobals->Profile->MessageType = CredInfo.MessageType;
pGlobals->Profile->LogonCount = CredInfo.LogonCount;
pGlobals->Profile->BadPasswordCount = CredInfo.BadPasswordCount;
pGlobals->Profile->LogonTime = CredInfo.ProfileLogonTime;
pGlobals->Profile->LogoffTime = CredInfo.LogoffTime;
pGlobals->Profile->KickOffTime = CredInfo.KickOffTime;
pGlobals->Profile->PasswordLastSet = CredInfo.PasswordLastSet;
pGlobals->Profile->PasswordCanChange = CredInfo.PasswordCanChange;
pGlobals->Profile->PasswordMustChange = CredInfo.PasswordMustChange;
RtlInitUnicodeString(&pGlobals->Profile->LogonScript, CredInfo.LogonScript);
RtlInitUnicodeString(&pGlobals->Profile->HomeDirectory, CredInfo.HomeDirectory);
RtlInitUnicodeString(&pGlobals->Profile->FullName, CredInfo.FullName);
RtlInitUnicodeString(&pGlobals->Profile->ProfilePath, CredInfo.ProfilePath);
RtlInitUnicodeString(&pGlobals->Profile->HomeDirectoryDrive, CredInfo.HomeDirectoryDrive);
RtlInitUnicodeString(&pGlobals->Profile->LogonServer, CredInfo.LogonServer);
if (CredInfo.UserName) {
wcscpy(pGlobals->UserName,CredInfo.UserName);
LocalFree(CredInfo.UserName);
} else {
wcscpy(pGlobals->UserName,L"");
}
if (CredInfo.Domain) {
wcscpy(pGlobals->Domain,CredInfo.Domain);
LocalFree(CredInfo.Domain);
} else {
wcscpy(pGlobals->Domain,L"");
}
if (CredInfo.PrivateDataLen) {
RtlCopyMemory(pGlobals->PasswordHash,CredInfo.PrivateData, CredInfo.PrivateDataLen );
LocalFree(CredInfo.PrivateData);
} else {
RtlZeroMemory(pGlobals->PasswordHash,PASSWORD_HASH_SIZE);
}
pGlobals->TransderedCredentials = TRUE;
return TRUE;
returnerror:
if (CredInfo.UserName) {
LocalFree(CredInfo.UserName);
}
if (CredInfo.Domain) {
LocalFree(CredInfo.Domain);
}
if (CredInfo.LogonScript) {
LocalFree(CredInfo.LogonScript);
}
if (CredInfo.HomeDirectory) {
LocalFree(CredInfo.HomeDirectory);
}
if (CredInfo.FullName) {
LocalFree(CredInfo.FullName);
}
if (CredInfo.ProfilePath) {
LocalFree(CredInfo.ProfilePath);
}
if (CredInfo.HomeDirectoryDrive) {
LocalFree(CredInfo.HomeDirectoryDrive);
}
if (CredInfo.LogonServer) {
LocalFree(CredInfo.LogonServer);
}
if (CredInfo.UserToken) {
CloseHandle(CredInfo.UserToken);
}
if (pGlobals->Profile) {
VirtualFree(pGlobals->Profile, 0, MEM_RELEASE);
pGlobals->Profile = NULL;
pGlobals->ProfileLength = 0;
}
return FALSE;
}
BOOL
GetAndAllocateLogonSid(
HANDLE hToken,
PSID *pLogonSid
)
{
PTOKEN_GROUPS ptgGroups = NULL;
DWORD cbBuffer = 512; // allocation size
DWORD dwSidLength; // required size to hold Sid
UINT i; // Sid index counter
BOOL bSuccess = FALSE; // assume this function will fail
*pLogonSid = NULL; // invalidate pointer
//
// initial allocation attempts
//
ptgGroups=(PTOKEN_GROUPS)Alloc(cbBuffer);
if(ptgGroups == NULL) return FALSE;
__try {
//
// obtain token information. reallocate memory if necessary
//
while(!GetTokenInformation(
hToken, TokenGroups, ptgGroups, cbBuffer, &cbBuffer)) {
//
// if appropriate, reallocate memory, otherwise bail
//
if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
//
// attempt to reallocate buffer
//
if((ptgGroups=(PTOKEN_GROUPS)ReAlloc(
ptgGroups, cbBuffer)) == NULL) __leave;
}
else __leave;
}
//
// Get the logon Sid by looping through the Sids in the token
//
for(i = 0 ; i < ptgGroups->GroupCount ; i++) {
if(ptgGroups->Groups[i].Attributes & SE_GROUP_LOGON_ID) {
//
// insure we are dealing with a valid Sid
//
if(!IsValidSid(ptgGroups->Groups[i].Sid)) __leave;
//
// get required allocation size to copy the Sid
//
dwSidLength=GetLengthSid(ptgGroups->Groups[i].Sid);
//
// allocate storage for the Logon Sid
//
if((*pLogonSid=(PSID *)Alloc(
dwSidLength)) == NULL) __leave;
//
// copy the Logon Sid to the storage we just allocated
//
if(!CopySid(dwSidLength, *pLogonSid, ptgGroups->Groups[i].Sid)) __leave;
bSuccess=TRUE; // indicate success...
break; // ...and get out
}
}
} // try
__finally {
//
// free allocated resources
//
if(ptgGroups != NULL) Free(ptgGroups);
if(!bSuccess) {
if(*pLogonSid != NULL) {
Free(*pLogonSid);
*pLogonSid = NULL;
}
}
} // finally
return bSuccess;
}