Path: blob/main/crypto/krb5/src/windows/kfwlogon/kfwlogon.c
34907 views
/*1Copyright 2005,2006 by the Massachusetts Institute of Technology2Copyright 2007 by Secure Endpoints Inc.34All rights reserved.56Permission to use, copy, modify, and distribute this software and its7documentation for any purpose and without fee is hereby granted,8provided that the above copyright notice appear in all copies and that9both that copyright notice and this permission notice appear in10supporting documentation, and that the name of the Massachusetts11Institute of Technology (M.I.T.) not be used in advertising or publicity12pertaining to distribution of the software without specific, written13prior permission.1415M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING16ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL17M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR18ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,19WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,20ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS21SOFTWARE.2223*/2425#include "kfwlogon.h"2627#include <io.h>28#include <stdio.h>29#include <sys/stat.h>30#include <sys/types.h>31#include <fcntl.h>3233#include <winsock2.h>34#include <lm.h>35#include <nb30.h>3637static HANDLE hDLL;3839static HANDLE hInitMutex = NULL;40static BOOL bInit = FALSE;414243BOOLEAN APIENTRY DllMain(HANDLE dll, DWORD reason, PVOID reserved)44{45hDLL = dll;46switch (reason) {47case DLL_PROCESS_ATTACH:48/* Initialization Mutex */49hInitMutex = CreateMutex(NULL, FALSE, NULL);50break;5152case DLL_PROCESS_DETACH:53CloseHandle(hInitMutex);54break;5556case DLL_THREAD_ATTACH:57case DLL_THREAD_DETACH:58default:59/* Everything else succeeds but does nothing. */60break;61}6263return TRUE;64}6566DWORD APIENTRY NPGetCaps(DWORD index)67{68switch (index) {69case WNNC_NET_TYPE:70/* We aren't a file system; We don't have our own type; use somebody else's. */71return WNNC_NET_SUN_PC_NFS;72case WNNC_START:73/* Say we are already started, even though we might wait after we receive NPLogonNotify */74return 1;7576default:77return 0;78}79}808182static BOOL83WINAPI84UnicodeStringToANSI(UNICODE_STRING uInputString, LPSTR lpszOutputString, int nOutStringLen)85{86CPINFO CodePageInfo;8788GetCPInfo(CP_ACP, &CodePageInfo);8990if (CodePageInfo.MaxCharSize > 1)91// Only supporting non-Unicode strings92return FALSE;9394if (uInputString.Buffer && ((LPBYTE) uInputString.Buffer)[1] == '\0')95{96// Looks like unicode, better translate it97// UNICODE_STRING specifies the length of the buffer string in Bytes not WCHARS98WideCharToMultiByte(CP_ACP, 0, (LPCWSTR) uInputString.Buffer, uInputString.Length/2,99lpszOutputString, nOutStringLen-1, NULL, NULL);100lpszOutputString[min(uInputString.Length/2,nOutStringLen-1)] = '\0';101return TRUE;102}103104lpszOutputString[0] = '\0';105return FALSE;106} // UnicodeStringToANSI107108109static BOOL110is_windows_vista(void)111{112static BOOL fChecked = FALSE;113static BOOL fIsWinVista = FALSE;114115if (!fChecked)116{117OSVERSIONINFO Version;118119memset (&Version, 0x00, sizeof(Version));120Version.dwOSVersionInfoSize = sizeof(Version);121122if (GetVersionEx (&Version))123{124if (Version.dwPlatformId == VER_PLATFORM_WIN32_NT &&125Version.dwMajorVersion >= 6)126fIsWinVista = TRUE;127}128fChecked = TRUE;129}130131return fIsWinVista;132}133134135/* Construct a Logon Script that will cause the LogonEventHandler to be executed136* under in the logon session137*/138139#define RUNDLL32_CMDLINE "rundll32.exe kfwlogon.dll,LogonEventHandler "140VOID141ConfigureLogonScript(LPWSTR *lpLogonScript, char * filename) {142DWORD dwLogonScriptLen;143LPWSTR lpScript;144LPSTR lpTemp;145146if (!lpLogonScript)147return;148*lpLogonScript = NULL;149150if (!filename)151return;152153dwLogonScriptLen = strlen(RUNDLL32_CMDLINE) + strlen(filename) + 2;154lpTemp = (LPSTR) malloc(dwLogonScriptLen);155if (!lpTemp)156return;157158_snprintf(lpTemp, dwLogonScriptLen, "%s%s", RUNDLL32_CMDLINE, filename);159160SetLastError(0);161dwLogonScriptLen = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, lpTemp, -1, NULL, 0);162DebugEvent("ConfigureLogonScript %s requires %d bytes gle=0x%x", lpTemp, dwLogonScriptLen, GetLastError());163164lpScript = LocalAlloc(LMEM_ZEROINIT, dwLogonScriptLen * 2);165if (lpScript) {166if (MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, lpTemp, -1, lpScript, 2 * dwLogonScriptLen))167*lpLogonScript = lpScript;168else {169DebugEvent("ConfigureLogonScript - MultiByteToWideChar failed gle = 0x%x", GetLastError());170LocalFree(lpScript);171}172} else {173DebugEvent("LocalAlloc failed gle=0x%x", GetLastError());174}175free(lpTemp);176}177178179DWORD APIENTRY NPLogonNotify(180PLUID lpLogonId,181LPCWSTR lpAuthentInfoType,182LPVOID lpAuthentInfo,183LPCWSTR lpPreviousAuthentInfoType,184LPVOID lpPreviousAuthentInfo,185LPWSTR lpStationName,186LPVOID StationHandle,187LPWSTR *lpLogonScript)188{189char uname[MAX_USERNAME_LENGTH+1]="";190char password[MAX_PASSWORD_LENGTH+1]="";191char logonDomain[MAX_DOMAIN_LENGTH+1]="";192193MSV1_0_INTERACTIVE_LOGON *IL;194195DWORD code = 0;196197char *reason;198char *ctemp;199200BOOLEAN interactive = TRUE;201HWND hwndOwner = (HWND)StationHandle;202BOOLEAN lowercased_name = TRUE;203204/* Can we load KFW binaries? */205if ( !KFW_is_available() )206return 0;207208DebugEvent0("NPLogonNotify start");209210/* Remote Desktop / Terminal Server connections to existing sessions211* are interactive logons. Unfortunately, because the session already212* exists the logon script does not get executed and this prevents213* us from being able to execute the rundll32 entrypoint214* LogonEventHandlerA which would process the credential cache this215* routine will produce. Therefore, we must cleanup orphaned cache216* files from this routine. We will take care of it before doing217* anything else.218*/219KFW_cleanup_orphaned_caches();220221/* Are we interactive? */222if (lpStationName)223interactive = (wcsicmp(lpStationName, L"WinSta0") == 0);224225if ( !interactive ) {226char station[64]="station";227DWORD rv;228229SetLastError(0);230rv = WideCharToMultiByte(CP_UTF8, 0, lpStationName, -1,231station, sizeof(station), NULL, NULL);232DebugEvent("Skipping NPLogonNotify- LoginId(%d,%d) - Interactive(%d:%s) - gle %d",233lpLogonId->HighPart, lpLogonId->LowPart, interactive, rv != 0 ? station : "failure", GetLastError());234return 0;235} else236DebugEvent("NPLogonNotify - LoginId(%d,%d)", lpLogonId->HighPart, lpLogonId->LowPart);237238/* Initialize Logon Script to none */239*lpLogonScript=NULL;240241/* MSV1_0_INTERACTIVE_LOGON and KERB_INTERACTIVE_LOGON are equivalent for242* our purposes */243244if ( wcsicmp(lpAuthentInfoType,L"MSV1_0:Interactive") &&245wcsicmp(lpAuthentInfoType,L"Kerberos:Interactive") )246{247char msg[64];248WideCharToMultiByte(CP_ACP, 0, lpAuthentInfoType, -1,249msg, sizeof(msg), NULL, NULL);250msg[sizeof(msg)-1]='\0';251DebugEvent("NPLogonNotify - Unsupported Authentication Info Type: %s", msg);252return 0;253}254255IL = (MSV1_0_INTERACTIVE_LOGON *) lpAuthentInfo;256257/* Convert from Unicode to ANSI */258259/*TODO: Use SecureZeroMemory to erase passwords */260if (!UnicodeStringToANSI(IL->UserName, uname, MAX_USERNAME_LENGTH) ||261!UnicodeStringToANSI(IL->Password, password, MAX_PASSWORD_LENGTH) ||262!UnicodeStringToANSI(IL->LogonDomainName, logonDomain, MAX_DOMAIN_LENGTH))263return 0;264265/* Make sure AD-DOMAINS sent from login that is sent to us is stripped */266ctemp = strchr(uname, '@');267if (ctemp) *ctemp = 0;268269/* is the name all lowercase? */270for ( ctemp = uname; *ctemp ; ctemp++) {271if ( !islower(*ctemp) ) {272lowercased_name = FALSE;273break;274}275}276277code = KFW_get_cred(uname, password, 0, &reason);278DebugEvent("NPLogonNotify - KFW_get_cred uname=[%s] code=[%d]",uname, code);279280/* remove any kerberos 5 tickets currently held by the SYSTEM account281* for this user282*/283if (!code) {284char filename[MAX_PATH+1] = "";285char acctname[MAX_USERNAME_LENGTH+MAX_DOMAIN_LENGTH+3]="";286PSID pUserSid = NULL;287LPTSTR pReferencedDomainName = NULL;288DWORD dwSidLen = 0, dwDomainLen = 0, count;289SID_NAME_USE eUse;290291if (_snprintf(acctname, sizeof(acctname), "%s\\%s", logonDomain, uname) < 0) {292code = -1;293goto cleanup;294}295296count = GetTempPath(sizeof(filename), filename);297if (count == 0 || count > (sizeof(filename)-1)) {298code = -1;299goto cleanup;300}301302if (_snprintf(filename, sizeof(filename), "%s\\kfwlogon-%x.%x",303filename, lpLogonId->HighPart, lpLogonId->LowPart) < 0)304{305code = -1;306goto cleanup;307}308309KFW_copy_cache_to_system_file(uname, filename);310311/* Need to determine the SID */312313/* First get the size of the required buffers */314LookupAccountName (NULL,315acctname,316pUserSid,317&dwSidLen,318pReferencedDomainName,319&dwDomainLen,320&eUse);321if(dwSidLen){322pUserSid = (PSID) malloc (dwSidLen);323memset(pUserSid,0,dwSidLen);324}325326if(dwDomainLen){327pReferencedDomainName = (LPTSTR) malloc (dwDomainLen * sizeof(TCHAR));328memset(pReferencedDomainName,0,dwDomainLen * sizeof(TCHAR));329}330331//Now get the SID and the domain name332if (pUserSid && LookupAccountName( NULL,333acctname,334pUserSid,335&dwSidLen,336pReferencedDomainName,337&dwDomainLen,338&eUse))339{340DebugEvent("LookupAccountName obtained user %s sid in domain %s", acctname, pReferencedDomainName);341code = KFW_set_ccache_dacl_with_user_sid(filename, pUserSid);342343#ifdef USE_WINLOGON_EVENT344/* If we are on Vista, setup a LogonScript345* that will execute the LogonEventHandler entry point via rundll32.exe346*/347if (is_windows_vista()) {348ConfigureLogonScript(lpLogonScript, filename);349if (*lpLogonScript)350DebugEvent0("LogonScript assigned");351else352DebugEvent0("No Logon Script");353}354#else355ConfigureLogonScript(lpLogonScript, filename);356if (*lpLogonScript)357DebugEvent0("LogonScript assigned");358else359DebugEvent0("No Logon Script");360#endif361} else {362DebugEvent0("LookupAccountName failed");363DeleteFile(filename);364code = -1;365}366367cleanup:368if (pUserSid)369free(pUserSid);370if (pReferencedDomainName)371free(pReferencedDomainName);372}373374KFW_destroy_tickets_for_principal(uname);375376if (code) {377char msg[128];378HANDLE h;379char *ptbuf[1];380381StringCbPrintf(msg, sizeof(msg), "Kerberos ticket acquisition failed: %s", reason);382383h = RegisterEventSource(NULL, KFW_LOGON_EVENT_NAME);384ptbuf[0] = msg;385ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1008, NULL, 1, 0, ptbuf, NULL);386DeregisterEventSource(h);387SetLastError(code);388}389390if (code)391DebugEvent0("NPLogonNotify failure");392else393DebugEvent0("NPLogonNotify success");394395return code;396}397398399DWORD APIENTRY NPPasswordChangeNotify(400LPCWSTR lpAuthentInfoType,401LPVOID lpAuthentInfo,402LPCWSTR lpPreviousAuthentInfoType,403LPVOID lpPreviousAuthentInfo,404LPWSTR lpStationName,405LPVOID StationHandle,406DWORD dwChangeInfo)407{408return 0;409}410411#include <userenv.h>412#include <Winwlx.h>413414#ifdef COMMENT415typedef struct _WLX_NOTIFICATION_INFO {416ULONG Size;417ULONG Flags;418PWSTR UserName;419PWSTR Domain;420PWSTR WindowStation;421HANDLE hToken;422HDESK hDesktop;423PFNMSGECALLBACK pStatusCallback;424} WLX_NOTIFICATION_INFO, *PWLX_NOTIFICATION_INFO;425#endif426427VOID KFW_Startup_Event( PWLX_NOTIFICATION_INFO pInfo )428{429DebugEvent0("KFW_Startup_Event");430}431432static BOOL433GetSecurityLogonSessionData(HANDLE hToken, PSECURITY_LOGON_SESSION_DATA * ppSessionData)434{435NTSTATUS Status = 0;436TOKEN_STATISTICS Stats;437DWORD ReqLen;438BOOL Success;439440if (!ppSessionData)441return FALSE;442*ppSessionData = NULL;443444445Success = GetTokenInformation( hToken, TokenStatistics, &Stats, sizeof(TOKEN_STATISTICS), &ReqLen );446if ( !Success )447return FALSE;448449Status = LsaGetLogonSessionData( &Stats.AuthenticationId, ppSessionData );450if ( FAILED(Status) || !ppSessionData )451return FALSE;452453return TRUE;454}455456VOID KFW_Logon_Event( PWLX_NOTIFICATION_INFO pInfo )457{458#ifdef USE_WINLOGON_EVENT459WCHAR szUserW[128] = L"";460char szUserA[128] = "";461char szPath[MAX_PATH] = "";462char szLogonId[128] = "";463DWORD count;464char filename[MAX_PATH] = "";465char newfilename[MAX_PATH] = "";466char commandline[MAX_PATH+256] = "";467STARTUPINFO startupinfo;468PROCESS_INFORMATION procinfo;469HANDLE hf = NULL;470471LUID LogonId = {0, 0};472PSECURITY_LOGON_SESSION_DATA pLogonSessionData = NULL;473474HKEY hKey1 = NULL, hKey2 = NULL;475476DebugEvent0("KFW_Logon_Event - Start");477478GetSecurityLogonSessionData( pInfo->hToken, &pLogonSessionData );479480if ( pLogonSessionData ) {481LogonId = pLogonSessionData->LogonId;482DebugEvent("KFW_Logon_Event - LogonId(%d,%d)", LogonId.HighPart, LogonId.LowPart);483484_snprintf(szLogonId, sizeof(szLogonId), "kfwlogon-%d.%d",LogonId.HighPart, LogonId.LowPart);485LsaFreeReturnBuffer( pLogonSessionData );486} else {487DebugEvent0("KFW_Logon_Event - Unable to determine LogonId");488return;489}490491count = GetEnvironmentVariable("TEMP", filename, sizeof(filename));492if ( count > sizeof(filename) || count == 0 ) {493GetWindowsDirectory(filename, sizeof(filename));494}495496if ( strlen(filename) + strlen(szLogonId) + 2 > sizeof(filename) ) {497DebugEvent0("KFW_Logon_Event - filename too long");498return;499}500501strcat(filename, "\\");502strcat(filename, szLogonId);503504hf = CreateFile(filename, FILE_ALL_ACCESS, 0, NULL, OPEN_EXISTING,505FILE_ATTRIBUTE_NORMAL, NULL);506if (hf == INVALID_HANDLE_VALUE) {507DebugEvent0("KFW_Logon_Event - file cannot be opened");508return;509}510CloseHandle(hf);511512if (KFW_set_ccache_dacl(filename, pInfo->hToken)) {513DebugEvent0("KFW_Logon_Event - unable to set dacl");514DeleteFile(filename);515return;516}517518if (KFW_obtain_user_temp_directory(pInfo->hToken, newfilename, sizeof(newfilename))) {519DebugEvent0("KFW_Logon_Event - unable to obtain temp directory");520return;521}522523if ( strlen(newfilename) + strlen(szLogonId) + 2 > sizeof(newfilename) ) {524DebugEvent0("KFW_Logon_Event - new filename too long");525return;526}527528strcat(newfilename, "\\");529strcat(newfilename, szLogonId);530531if (!MoveFileEx(filename, newfilename,532MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) {533DebugEvent("KFW_Logon_Event - MoveFileEx failed GLE = 0x%x", GetLastError());534return;535}536537_snprintf(commandline, sizeof(commandline), "kfwcpcc.exe \"%s\"", newfilename);538539GetStartupInfo(&startupinfo);540if (CreateProcessAsUser( pInfo->hToken,541"kfwcpcc.exe",542commandline,543NULL,544NULL,545FALSE,546CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS,547NULL,548NULL,549&startupinfo,550&procinfo))551{552DebugEvent("KFW_Logon_Event - CommandLine %s", commandline);553554WaitForSingleObject(procinfo.hProcess, 30000);555556CloseHandle(procinfo.hThread);557CloseHandle(procinfo.hProcess);558} else {559DebugEvent0("KFW_Logon_Event - CreateProcessFailed");560}561562DeleteFile(newfilename);563564DebugEvent0("KFW_Logon_Event - End");565#endif /* USE_WINLOGON_EVENT */566}567568569/* Documentation on the use of RunDll32 entrypoints can be found570* at https://support.microsoft.com/kb/164787571*/572void CALLBACK573LogonEventHandlerA(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow)574{575HANDLE hf = NULL;576char commandline[MAX_PATH+256] = "";577STARTUPINFO startupinfo;578PROCESS_INFORMATION procinfo;579580DebugEvent0("LogonEventHandler - Start");581582/* Validate lpszCmdLine as a file */583hf = CreateFile(lpszCmdLine, GENERIC_READ | DELETE, 0, NULL, OPEN_EXISTING,584FILE_ATTRIBUTE_NORMAL, NULL);585if (hf == INVALID_HANDLE_VALUE) {586DebugEvent("LogonEventHandler - \"%s\" cannot be opened", lpszCmdLine);587return;588}589CloseHandle(hf);590591592_snprintf(commandline, sizeof(commandline), "kfwcpcc.exe \"%s\"", lpszCmdLine);593594GetStartupInfo(&startupinfo);595SetLastError(0);596if (CreateProcess( NULL,597commandline,598NULL,599NULL,600FALSE,601CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS,602NULL,603NULL,604&startupinfo,605&procinfo))606{607DebugEvent("KFW_Logon_Event - CommandLine %s", commandline);608609WaitForSingleObject(procinfo.hProcess, 30000);610611CloseHandle(procinfo.hThread);612CloseHandle(procinfo.hProcess);613} else {614DebugEvent("KFW_Logon_Event - CreateProcessFailed \"%s\" GLE 0x%x",615commandline, GetLastError());616DebugEvent("KFW_Logon_Event PATH %s", getenv("PATH"));617}618619DeleteFile(lpszCmdLine);620621DebugEvent0("KFW_Logon_Event - End");622}623624625