//+--------------------------------------------------------------------------- // // 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 #include #include #include #include "shlwapi.h" #include "shlwapip.h" #include "winsta.h" #include "wtsapi32.h" #include #include 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; BOOL g_FirstTime = TRUE; // // 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, int nUserMax); 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; if ((ERROR_SUCCESS != RegQueryValueEx(hKey, LEGAL_NOTICE_CAPTION_KEY, 0, &dwType, (LPBYTE)lpCaption, &dwSize)) || (dwType != REG_SZ)) { lpCaption[0] = 0; } dwSize = dwMaxSize; if ((ERROR_SUCCESS != RegQueryValueEx(hKey, LEGAL_NOTICE_TEXT_KEY, 0, &dwType, (LPBYTE)lpText, &dwSize)) || (dwType != REG_SZ)) { lpText[0] = 0; } 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 ); 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 ; } } #ifdef ANNOY_AUTOLOGON_REGISTRY #define TIMER_COUNTDOWN 0 INT_PTR CALLBACK AnnoyAutologonDlgProc( HWND hDlg, UINT Message, WPARAM wParam, LPARAM lParam ) { PGLOBALS pGlobals = (PGLOBALS)GetWindowLongPtr(hDlg, GWLP_USERDATA); LARGE_INTEGER Now; WCHAR szBuild[10]; // Build number or countdown switch ( Message ) { case WM_INITDIALOG: pGlobals = (PGLOBALS) lParam ; SetWindowLongPtr(hDlg, GWLP_USERDATA, (LPARAM)pGlobals); SetTimer(hDlg, TIMER_COUNTDOWN, 1000, NULL); // 1 sec { DWORD cbBuild; HKEY hKey; DWORD dwStatus = RegOpenKeyEx( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKey ); cbBuild = sizeof(szBuild); wcscpy(szBuild, L""); // OK if (dwStatus == ERROR_SUCCESS) { DWORD dwType = REG_SZ; dwStatus = RegQueryValueEx( hKey, L"CurrentBuildNumber", 0, &dwType, (LPBYTE) szBuild, &cbBuild ); if ((dwStatus != ERROR_SUCCESS) || (dwType != REG_SZ)) { szBuild[0] = 0; } RegCloseKey(hKey); } // 10 sec per build penalty, up to 15 minutes cbBuild = _wtol(szBuild); if ((0 == cbBuild) || (cbBuild <= 3590)) { cbBuild = 3591; } if (cbBuild - 3590 > 15 * 6) { cbBuild = 3590 + 15 * 6; } GetSystemTimeAsFileTime((FILETIME*) &Now); Now.QuadPart += (cbBuild - 3590) * 10 * 10000000I64; pGlobals->LastNotification.dwHighDateTime = Now.HighPart; pGlobals->LastNotification.dwLowDateTime = Now.LowPart; cbBuild = (cbBuild - 3590) * 10; // SAFE: "00:XX:YY" is 8+1 characters (fits in 10) swprintf(szBuild, L"00:%02d:%02d", (cbBuild/60)%100, cbBuild%60); SetDlgItemText(hDlg, IDC_COUNTDOWN_STATIC, szBuild); } return TRUE ; case WM_TIMER: if (wParam == TIMER_COUNTDOWN) { LARGE_INTEGER End; End.HighPart = pGlobals->LastNotification.dwHighDateTime; End.LowPart = pGlobals->LastNotification.dwLowDateTime; GetSystemTimeAsFileTime((FILETIME*) &Now); if (Now.QuadPart >= End.QuadPart) { EndDialog(hDlg, MSGINA_DLG_SUCCESS); } else { DWORD dwMins, dwSecs; End.QuadPart -= Now.QuadPart; End.QuadPart = End.QuadPart / 10000000I64; // secs dwMins = ((DWORD)End.QuadPart) / 60 % 100; dwSecs = ((DWORD)End.QuadPart) % 60; // SAFE: "00:XX:YY" is 8+1 characters (fits in 10) swprintf(szBuild, L"00:%02d:%02d", dwMins, dwSecs); SetDlgItemText(hDlg, IDC_COUNTDOWN_STATIC, szBuild); } return TRUE; } break; case WM_DESTROY: KillTimer(hDlg, TIMER_COUNTDOWN); pGlobals->LastNotification.dwHighDateTime = 0; pGlobals->LastNotification.dwLowDateTime = 0; break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDCANCEL: // EndDialog(hDlg, MSGINA_DLG_FAILURE); return(TRUE); } break; case WLX_WM_SAS: // Swallow SAS return(TRUE); } return FALSE ; } #endif 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)); } NTSTATUS RetrieveStoredSecret(LPCWSTR pswSecretName, WCHAR *PasswordBuffer, int nBufferSize) { 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, pswSecretName ); 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 < (nBufferSize - 1) * sizeof(WCHAR)) { RtlCopyMemory( PasswordBuffer, SecretValue->Buffer, SecretValue->Length ); PasswordBuffer[SecretValue->Length/sizeof(WCHAR)] = L'\0'; } else { Status = STATUS_INVALID_PARAMETER; } ZeroMemory(SecretValue->Buffer, SecretValue->Length); } else { PasswordBuffer[0] = L'\0'; } LsaFreeMemory(SecretValue); } LsaClose(LsaHandle); } return Status; } // ========================================================================================== // 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] = TEXT(""); TCHAR szFinalMessage[MAX_STRING_BYTES] = TEXT(""); if (GetSessionZeroUser(szUser, USERNAME_LENGTH + DOMAIN_LENGTH + 2)) { LoadString(hDllInstance, IDS_LOCKED_EMAIL_NFN_MESSAGE, szMessage, MAX_STRING_BYTES); _snwprintf(szFinalMessage, sizeof(szFinalMessage)/sizeof(TCHAR), szMessage, szUser ); szFinalMessage[sizeof(szFinalMessage)/sizeof(TCHAR) - 1] = 0; // NULL terminate } 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] = TEXT(""); 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. * \***************************************************************************/ void MyZeroMemory(PVOID lpv, SIZE_T size); #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 we are going to the PIN dialog we will need a cancel button // if (GetDisableCad(pGlobals) && IsActiveConsoleSession() && (pParam->SasType != WLX_SAS_TYPE_SC_INSERT)) { 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, USERNAME_LENGTH + DOMAIN_LENGTH + 2); 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; BOOL fNoAutologon = 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 ); } // Query TermSrv if this was a Session directory redirected SmartCard autoLogon if (fResult && !pGlobals->MuGlobals.pAutoLogon->fPromptForPassword && g_FirstTime) { BOOL fSessionDirectoryRedirectedAutoLogon = FALSE; DWORD Length; if (WinStationQueryInformation( SERVERNAME_CURRENT, LOGONID_CURRENT, WinStationSDRedirectedSmartCardLogon, &fSessionDirectoryRedirectedAutoLogon, sizeof(BOOL), &Length)) { if ( fSessionDirectoryRedirectedAutoLogon ) { // // This is a TS Session directory redirected Smartcard autologon // We should not proceed with normal Autologon for this special case // This is so that Winlogon detects the SmartCard and takes the SmartCard route // fNoAutologon = TRUE; } } } if (FALSE == g_FirstTime) { // We have tried this password once, no need to retry forever... pGlobals->MuGlobals.pAutoLogon->fPromptForPassword = TRUE; } g_FirstTime = FALSE ; if ( fResult && !fNoAutologon && (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)) { _snwprintf(pszOldStyle, MAX_UPN_NAME_SIZE, TEXT("%s\\%s"), pAutoLogon->pszDomain, pAutoLogon->pszUserName); pszOldStyle[MAX_UPN_NAME_SIZE - 1] = 0; nSize = MAX_UPN_NAME_SIZE; 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) { bAutoLogon = FALSE; pGlobals->AutoAdminLogon = FALSE; if ( RetrieveStoredSecret( TEXT("DefaultPIN"), PasswordBuffer, ARRAYSIZE(PasswordBuffer)) == STATUS_SUCCESS ) { // 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) { if (_Shell_LogonDialog_UIHostActive()) { 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); #ifdef ANNOY_AUTOLOGON_REGISTRY pWlxFuncs->WlxDialogBoxParam( pGlobals->hGlobalWlx, hDllInstance, (LPTSTR) IDD_ANNOYAUTOLOGON_DIALOG, hDlg, AnnoyAutologonDlgProc, (LPARAM) pGlobals ); #endif } else { NTSTATUS Status; Status = RetrieveStoredSecret( DEFAULT_PASSWORD_KEY, PasswordBuffer, ARRAYSIZE(PasswordBuffer) ); if (NT_SUCCESS(Status)) { SetDlgItemText(hDlg, IDD_LOGON_PASSWORD, PasswordBuffer); } else { WriteProfileString( APPLICATION_NAME, AUTO_ADMIN_LOGON_KEY, TEXT("0") ); } } go_logon: // Zeroize this buffer for obvious security reasons // Need to call this stub, otherwise the compiler optimizes this out! MyZeroMemory(PasswordBuffer, sizeof(PasswordBuffer)); // 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) { // // reset autoadmin logon flag (Bug 532161) // pGlobals->AutoAdminLogon = FALSE; // // erase the stored password, that otherwise (on success) // would get erased after the dialog finishes with // MSGINA_DLG_SUCCESS in WlxLoggedOutSAS after Logon // if (!pGlobals->TransderedCredentials) { ErasePassword( &pGlobals->PasswordString ); } if (fDisconnectOnTsAutoLogonFailure || g_fHelpAssistantLogon) { // // If TermSrv Internet Connector is on // don't allow a second chance at the logon dialog // 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_EXTERNALHOST: break; case SHELL_LOGONDIALOG_NONE: default: 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); } 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); } bSmartCardInserted = FALSE; EndDialog( hDlg, Result ); break; } case WM_HANDLEFAILEDLOGON: { 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); } SECURITY_STATUS PopulateSecPackageList( PGLOBALS pGlobals ) { static UCHAR s_bDoneThat = 0; STRING Narrow; SECURITY_STATUS Status; // // Populate Security Package List: // if ( ( s_bDoneThat & 1) == 0) { RtlInitString( &Narrow, MICROSOFT_KERBEROS_NAME_A ); Status = LsaLookupAuthenticationPackage( pGlobals->LsaHandle, &Narrow, &pGlobals->SmartCardLogonPackage ); if ( NT_SUCCESS( Status ) ) { s_bDoneThat |= 1; } // // this (potential) failure is not critical. If it fails, then s/c logons later // will fail. // } Status = 0; if ( ( s_bDoneThat & 2) == 0) { RtlInitString( &Narrow, NEGOSSP_NAME_A ); Status = LsaLookupAuthenticationPackage( pGlobals->LsaHandle, &Narrow, &pGlobals->PasswordLogonPackage ); if ( NT_SUCCESS( Status ) ) { s_bDoneThat |= 2; } } return Status; } /***************************************************************************\ * 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; SECURITY_STATUS Status; RECT rc, rc2; BOOL bHasLangIcon = FALSE; ULONG CacheFlags ; // // Populate Security Package List: // Status = PopulateSecPackageList( pGlobals ); 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, FALSE ); } 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 ); } else { if ( DCacheGetCacheState( pGlobals->Cache ) != DomainCacheReady ) { DCacheUpdateFullAsync( pGlobals->Cache ); } } pGlobals->ListPopulated = FALSE ; pGlobals->ActiveArray = DCacheCopyCacheArray( pGlobals->Cache ); if ( pGlobals->ActiveArray ) { DCachePopulateListBoxFromArray( pGlobals->ActiveArray, GetDlgItem( hDlg, IDD_LOGON_DOMAIN ), NULL ); } else { return ( FALSE ); } 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() ) { if ( ( CacheFlags & DCACHE_MEMBER ) != 0 ) { pGlobals->ShowRasBox = TRUE; } else { UNICODE_STRING DnsDomain = { 0 } ; if ( GetPrimaryDomainEx( NULL, &DnsDomain, NULL, NULL ) && ( DnsDomain.Buffer != NULL) ) { // We are "joined" to a MIT realm pGlobals->ShowRasBox = TRUE; LocalFree( DnsDomain.Buffer ); } } } } } // // 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("")); LogonMsg[0] = 0; 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); if ((ERROR_SUCCESS != RegQueryValueEx (hKey, SHOW_LOGON_OPTIONS, NULL, &dwType, (LPBYTE)&ShowOptions, &dwSize)) || (REG_DWORD != dwType)) { ShowOptions = FALSE; // restore default } 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] = TEXT(""); 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); // // the logic of enabling the CANCEL button is as follows // - if a logon is in progress always disable it (not allowed to cancel) // - else if a SC logon always enable it (always allow to get to CAD) // - else gray it only when DisableCAD and is set and we're in // the active session (i.e. TS always allows Cancel) // if( pGlobals->LogonInProgress ) { EnableDlgItem(hDlg, IDCANCEL, FALSE); } else { if( pGlobals->SmartCardLogon ) { EnableDlgItem(hDlg, IDCANCEL, TRUE); } else { EnableDlgItem(hDlg, IDCANCEL, !(GetDisableCad(pGlobals) && IsActiveConsoleSession()) ); } } 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; DWORD dwAnimationTimeSlice; 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??? // If so, set local domain flag used by password UI to show/notshow password restore // pGlobals->fLocalDomain = FALSE; Entry = (PDOMAIN_CACHE_ENTRY) SendMessage( hwndDomain, CB_GETITEMDATA, (WPARAM)iDomainSel, 0); if (CB_ERR != (ULONG_PTR) Entry) { if (NULL != Entry) { if (Entry->Type == DomainMachine) { pGlobals->fLocalDomain = TRUE; } } } } else { Entry = (PDOMAIN_CACHE_ENTRY) CB_ERR; } if ( (Entry != (PDOMAIN_CACHE_ENTRY) CB_ERR) && (NULL != Entry)) { // MAX_STRING_BYTES is the size of pGlobals->Domain (WlxInitialize) // Truncation should never occur lstrcpyn( Domain, Entry->FlatName.Buffer, MAX_STRING_BYTES ); } 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; pGlobals->fLocalDomain = TRUE; 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 ( wcspbrk( 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; dwAnimationTimeSlice = GetAnimationTimeInterval(pGlobals); // setup the progress timer SetTimer(hDlg, 0, dwAnimationTimeSlice, NULL); // // 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, int nUserMax) { BOOL fReplaced; DWORD dwIndex, dwReturnedEntryCount; NET_API_STATUS nasCode; NET_DISPLAY_USER *pNDU; fReplaced = FALSE; if (*pszUsername) // Name is not empty (Admin has empty full name by default...) { 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) { lstrcpyn(pszUsername, pNDU->usri1_name, nUserMax); // Zero terminated } 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; // MAX_STRING_BYTES is the size of pGlobals->UserName (WlxInitialize) fReplaced = ReplacedPossibleDisplayName(pGlobals->UserName, MAX_STRING_BYTES); if (fReplaced) { RtlInitUnicodeString(&pGlobals->UserNameString, pGlobals->UserName); } return(fReplaced); } DWORD AttemptLogonThread( PGLOBALS pGlobals ) { STRING PackageName; PSID LogonSid; PSID DuplicatedLogonSID; 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 ); } } SetupCursor( TRUE ); FinalStatus = STATUS_SUCCESS; // // Generate a unique sid for this logon. // Duplicate the SID. It is possible that the function that called // the thread (and created the SID originally) may return causing // the SID to be freed while this thread is still running. // (see bug# 478186). // the solution was to duplicate the SID here and free it at the end // This still leaves a "small" window between starting the thread // and duplicating the token where freeing may potentially cause the // same problem (if the pGlobal->LogonSID is freed but not NULL'ed in // which case duplication will fail) // Notes: // - if the duplication fails abort this // - there is no need to actually verify the SID on free time // at this point. // LogonSid = DuplicatedLogonSID = DuplicateSID(pGlobals->LogonSid); if( NULL == LogonSid ) { FinalStatus = STATUS_NO_MEMORY ; Status = FinalStatus ; } else { if ( !wcspbrk( 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) { lstrcpyn( pGlobals->Smartcard, ScInfo->pszCard, sizeof(pGlobals->Smartcard) / sizeof(TCHAR) ); lstrcpyn( pGlobals->SmartcardReader, ScInfo->pszReader, sizeof(pGlobals->SmartcardReader) / sizeof(TCHAR) ); } #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( pGlobals->SmartCardLogon ) { // // this was a SC logon that failed because the account had // a password that expired. Put an error message and exit // TimeoutMessageBox(pGlobals->hwndLogon, pGlobals, IDS_LOGON_SMARTCARD_PWD_CHANGE, IDS_LOGON_MESSAGE, MB_OK | MB_ICONSTOP | MB_SETFOREGROUND, LOGON_TIMEOUT); Result = MSGINA_DLG_FAILURE; goto exit; } if (Status == STATUS_PASSWORD_MUST_CHANGE) { Result = TimeoutMessageBox(pGlobals->hwndLogon, pGlobals, IDS_PASSWORD_MUST_CHANGE, IDS_LOGON_MESSAGE, MB_OK | MB_ICONSTOP | MB_SETFOREGROUND, LOGON_TIMEOUT); } else { Result = TimeoutMessageBox(pGlobals->hwndLogon, pGlobals, IDS_PASSWORD_EXPIRED, IDS_LOGON_MESSAGE, MB_OK | MB_ICONSTOP | MB_SETFOREGROUND, LOGON_TIMEOUT); } if (DLG_INTERRUPTED(Result) || (WLX_DLG_INPUT_TIMEOUT == Result)) goto exit; // // Copy the old password for mpr notification later // RevealPassword( &pGlobals->PasswordString ); // pGlobals->Password has the same size as pGlobals->OldPassword (WlxInitialize) // so no need to zero terminate 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 = DuplicatedLogonSID; 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); } } } // // Force smart card logon for normal boot // if(!pGlobals->SmartCardLogon && (SafeBootMode != SAFEBOOT_MINIMAL) && (SafeBootMode != SAFEBOOT_DSREPAIR) && GetSCForceOption() ) { // // not a safe boot - 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_SC_REQUIRED, pGlobals->UserName, pGlobals->Domain); goto exit; } // // 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 ) ) { if (pGlobals->Profile) { 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.NewThreadTokenSD = CreateUserThreadTokenSD(LogonSid, pWinlogonSid); if ( NULL == pGlobals->UserProcessData.NewThreadTokenSD ) { if (pGlobals->Profile) { LsaFreeReturnBuffer(pGlobals->Profile); pGlobals->Profile = NULL; } NtClose(UserToken); Result = MSGINA_DLG_FAILEDMSGSENT; Status = STATUS_NO_MEMORY; PostFailedLogonMessage(pGlobals->hwndLogon, pGlobals, Status, 0, pGlobals->UserName, pGlobals->Domain); goto exit; } pGlobals->UserProcessData.RestrictedToken = RestrictedToken; pGlobals->UserProcessData.UserToken = UserToken; pGlobals->MprLogonScripts = NULL; // Run the dirty dialog box. if ( WinlogonDirtyDialog( NULL, pGlobals ) == WLX_SAS_ACTION_LOGOFF ) { // // If this returns logoff, it means that the dialog timed out and // we need to force the user back off. Not the best user experience, // but that's what the PMs want. // FreeSecurityDescriptor( pGlobals->UserProcessData.NewThreadTokenSD ); 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_SET_DIRTY_UI_TIMEOUT, pGlobals->UserName, pGlobals->Domain); goto exit; } // // 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)) { DWORD cb = pGlobals->Profile->FullName.Length; if (cb + sizeof(WCHAR) > MAX_STRING_LENGTH * sizeof(WCHAR)) cb = MAX_STRING_LENGTH * sizeof(WCHAR) - sizeof(WCHAR); memcpy(pGlobals->UserFullName, pGlobals->Profile->FullName.Buffer, cb); pGlobals->UserFullName[cb / sizeof(WCHAR)] = UNICODE_NULL; } 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) { if (WLX_DLG_INPUT_TIMEOUT == Result) { // // this came from a timeout when: // - we want to go back to CAD screen w/out an extra error dialog // - we want this to look like a logon failure code // the solution is to send WM_LOGONCOMPLETE / MSGINA_DLG_FAILURE // // Result = MSGINA_DLG_FAILURE; } PostMessage(pGlobals->hwndLogon, WM_LOGONCOMPLETE, 0, Result); } // // free the duplicated SID // if( DuplicatedLogonSID && RtlValidSid(DuplicatedLogonSID) ) { Free(DuplicatedLogonSID); } 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( SubStatus == IDS_LOGON_SC_REQUIRED ) { uiMsgId = IDS_LOGON_SC_REQUIRED; } else if ( SubStatus == IDS_SET_DIRTY_UI_TIMEOUT ) { uiMsgId = IDS_SET_DIRTY_UI_TIMEOUT; } 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 (pGlobals->fLocalDomain) { 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 { // Buffer1[0] = 0; unnecessary since LPTR was used LoadString(hDllInstance, IDS_STATUS_SERVER_SIDE_ERROR, Buffer1, MAX_STRING_BYTES); _snwprintf(Buffer2, MAX_STRING_BYTES, Buffer1, Status ); Buffer2[MAX_STRING_BYTES - 1] = 0; // zero terminate Buffer1[0] = 0; 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 { // Buffer1[0] = 0; unnecessary since LPTR was used LoadString(hDllInstance, IDS_LOGON_NO_DOMAIN, Buffer1, MAX_STRING_BYTES); _snwprintf(Buffer2, MAX_STRING_BYTES, Buffer1, Domain); Buffer2[MAX_STRING_BYTES - 1] = 0; // zero terminate Buffer1[0] = 0; 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 { // Buffer1[0] = 0; unnecessary since LPTR was used LoadString(hDllInstance, IDS_UNKNOWN_LOGON_FAILURE, Buffer1, MAX_STRING_BYTES); if ( NT_ERROR( Status ) ) { Win32Error = RtlNtStatusToDosError( Status ); } else { // // Probably an HRESULT: // Win32Error = Status ; } // Buffer3[0] = 0; unnecessary since LPTR was used GetErrorDescription( Win32Error, Buffer3, MAX_STRING_BYTES); _snwprintf(Buffer2, MAX_STRING_BYTES, Buffer1, Buffer3 ); Buffer2[MAX_STRING_BYTES - 1] = 0; // zero terminate Buffer1[0] = 0; 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; PACL Dacl = NULL; if (!QuerySwitchConsoleCredentials(pGlobals,&UserToken,&LogonId)) { Result = MSGINA_DLG_FAILEDMSGSENT; goto exit; } if (pGlobals->SmartCardLogon) { wcscpy(pGlobals->Password,L""); wcscpy(pGlobals->OldPassword,L""); { KERB_REFRESH_SCCRED_REQUEST PurgeRequest = {0}; PVOID Response = NULL; ULONG ResponseSize; NTSTATUS SubStatus; PopulateSecPackageList( pGlobals ); PurgeRequest.LogonId = LogonId; PurgeRequest.MessageType = KerbRefreshSmartcardCredentialsMessage; PurgeRequest.Flags = KERB_REFRESH_SCCRED_RELEASE; Status = LsaCallAuthenticationPackage( pGlobals->LsaHandle, pGlobals->SmartCardLogonPackage, &PurgeRequest, sizeof(KERB_REFRESH_SCCRED_REQUEST), &Response, &ResponseSize, &SubStatus ); if (NT_SUCCESS(Status) && NT_SUCCESS(SubStatus)) { if (Response) { LsaFreeReturnBuffer(Response); } } else { DebugLog((DEB_ERROR, "KerbRefreshSmartcardCredentials failed: (0x%x - 0x%x)\n", Status, SubStatus)); } } } 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))) { goto error_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 // { ULONG DaclLength = 0; TOKEN_DEFAULT_DACL DefaultDacl; DaclLength = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(LogonSid); Dacl = Alloc(DaclLength); // Check for memory allocation failure if (Dacl == NULL) { goto error_exit ; } Status = RtlCreateAcl(Dacl,DaclLength, ACL_REVISION); ASSERT(NT_SUCCESS(Status)); if (Status != STATUS_SUCCESS) { goto error_exit; } Status = RtlAddAccessAllowedAce( Dacl, ACL_REVISION, GENERIC_ALL, LogonSid ); ASSERT(NT_SUCCESS(Status)); if (Status != STATUS_SUCCESS) { goto error_exit; } DefaultDacl.DefaultDacl = Dacl; Status = NtSetInformationToken( RestrictedToken, TokenDefaultDacl, &DefaultDacl, sizeof(TOKEN_DEFAULT_DACL) ); ASSERT(NT_SUCCESS(Status)); if (Status != STATUS_SUCCESS) { goto error_exit; } Free(Dacl); Dacl = NULL; } } 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 ) ) { NtClose(UserToken); goto error_exit; } pGlobals->UserProcessData.NewThreadTokenSD = CreateUserThreadTokenSD(LogonSid, pWinlogonSid); if ( NULL == pGlobals->UserProcessData.NewThreadTokenSD ) { if (pGlobals->Profile) { VirtualFree(pGlobals->Profile, 0, MEM_RELEASE); pGlobals->Profile = NULL; pGlobals->ProfileLength = 0; } NtClose(UserToken); Result = MSGINA_DLG_FAILEDMSGSENT; Status = STATUS_NO_MEMORY; goto exit; } pGlobals->UserProcessData.RestrictedToken = RestrictedToken; pGlobals->UserProcessData.UserToken = UserToken; pGlobals->MprLogonScripts = 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)) { DWORD cb = pGlobals->Profile->FullName.Length; if (cb + sizeof(WCHAR) > MAX_STRING_LENGTH * sizeof(WCHAR)) cb = MAX_STRING_LENGTH * sizeof(WCHAR) - sizeof(WCHAR); memcpy(pGlobals->UserFullName, pGlobals->Profile->FullName.Buffer, cb); pGlobals->UserFullName[cb / sizeof(WCHAR)] = UNICODE_NULL; } 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; error_exit: Result = MSGINA_DLG_FAILEDMSGSENT; if (pGlobals->Profile) { VirtualFree(pGlobals->Profile, 0, MEM_RELEASE); pGlobals->Profile = NULL; pGlobals->ProfileLength = 0; } if (Dacl != NULL) { Free(Dacl); Dacl = NULL; } 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 = LocalAlloc(LMEM_FIXED, 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) { // CredInfo.UserName is a copy of pGlobals->UserName in another session (OK) wcscpy(pGlobals->UserName,CredInfo.UserName); LocalFree(CredInfo.UserName); } else { wcscpy(pGlobals->UserName,L""); } if (CredInfo.Domain) { // CredInfo.Domain is a copy of pGlobals->Domain in another session (OK) 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; PTOKEN_GROUPS ptgOldGroups = 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 // ptgOldGroups = ptgGroups; #pragma prefast(suppress: 308, "PREfast noise: LocalRealloc use is valid since old pointer was saved") if((ptgGroups=(PTOKEN_GROUPS)ReAlloc( ptgGroups, cbBuffer)) == NULL) { Free(ptgOldGroups); __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; }