/*++ Copyright (C) Microsoft Corporation, 1997 - 1999 Module Name: senslogn.cxx Abstract: This file contains the implementation of a Stub DLL to notify SENS of events generated by Winlogon. Author: Gopal Parupudi Notes: a. This DLL notifies the following components: o EventSystem (contact DMcCrady) o IR service (contact JRoberts) o SENS service (contact GopalP) b. This DLL also maintains tokens of the currently logged on user. This is used by COM for activation. Revision History: GopalP 12/7/1997 Start. --*/ #include #include #include #include #include #include #include #include "mutex.hxx" #include "irnotify.h" #include "usertok.h" #include "senslogn.hxx" #include "onestop.cxx" // // Constants // #define NOTIFY_LCE_STARTSHELL 0x00000003 #define NOTIFY_LCE_LOGOFF 0x00000004 #define SENS_START_WAIT_TIMEOUT 180*1000 // 3 minutes #define NOTIFY_LCE_LOGONUSER "NotifyLogonUser" #define NOTIFY_LCE_LOGOFFUSER "NotifyLogoffUser" #define NOTIFY_IR_LOGONUSER "OnUserLogon" #define NOTIFY_IR_LOGOFFUSER "OnUserLogoff" #define NOTIFY_IR_INIT "InitializeDll" #define SENS_SERVICE SENS_STRING("SENS") #define EVENTSYSTEM_DLL SENS_STRING("ES.DLL") #define IR_DLL SENS_STRING("IRNOTIFY.DLL") #define SENS_STARTED_EVENT SENS_STRING("SENS Started Event") // // Globals // PWLX_NOTIFICATION_INFO gpStartShellInfo; HANDLE ghSensStartedEvent; MUTEX * SetupMutex; BOOL gbIsTokenCodeInitialized; // For GetCurrentUserToken PSID LocalSystemSid; USER_LOGON_TABLE * ActiveUserList; // For IR notification of logon/logoff RPC_BINDING_HANDLE g_hIrxfer; // // Some useful Macros // #ifdef DETAIL_DEBUG #define DUMP_INFO(_EventType_) \ \ char buf[512]; \ PWLX_NOTIFICATION_INFO pInfo = (PWLX_NOTIFICATION_INFO) lpvParam; \ \ LogMessage(("------------------------------------------------------\n")); \ LogMessage((SENSLOGN " Received a %s Event.\n", _EventType_)); \ LogMessage((" Size - %d\n", pInfo->Size)); \ LogMessage((" Flags - 0x%x\n", pInfo->Flags)); \ LogMessage((" UserName - %s\n", UnicodeToAnsi(pInfo->UserName, buf))); \ LogMessage((" Domain - %s\n", UnicodeToAnsi(pInfo->Domain, buf))); \ LogMessage((" WinStation - %s\n", UnicodeToAnsi(pInfo->WindowStation, buf))); \ LogMessage((" hToken - 0x%x\n", pInfo->hToken)); \ LogMessage((" hDesktop - 0x%x\n", pInfo->hDesktop)); \ LogMessage((" pCallback - 0x%x\n", pInfo->pStatusCallback)); \ LogMessage((" dwSessionId - 0x%x\n", NtCurrentPeb()->SessionId)); \ LogMessage(("------------------------------------------------------\n")); // // Functions // PCHAR UnicodeToAnsi( PWSTR in, PCHAR out ) { PCHAR pSave = out; if (in == NULL) { return ""; } if (*in == (WCHAR)'\0') { return ""; } while( *out++ = (CHAR)*in++) ; return pSave; } #else // ! DETAIL_DEBUG #define DUMP_INFO(_EventType_) #endif // DETAIL_DEBUG #define FIRE_EVENT(_EventType_) \ { \ \ SENS_NOTIFY_WINLOGON Data; \ \ Data.eType = _EventType_; \ Data.Info.Size = sizeof(SENS_NOTIFY_WINLOGON); \ Data.Info.Flags = ((PWLX_NOTIFICATION_INFO)lpvParam)->Flags; \ Data.Info.UserName = ((PWLX_NOTIFICATION_INFO)lpvParam)->UserName; \ Data.Info.Domain = ((PWLX_NOTIFICATION_INFO)lpvParam)->Domain; \ Data.Info.WindowStation = ((PWLX_NOTIFICATION_INFO)lpvParam)->WindowStation; \ Data.Info.hToken = HandleToUlong(((PWLX_NOTIFICATION_INFO)lpvParam)->hToken); \ Data.Info.hDesktop = HandleToUlong(((PWLX_NOTIFICATION_INFO)lpvParam)->hDesktop); \ Data.Info.dwSessionId = NtCurrentPeb()->SessionId; \ \ status = SensNotifyWinlogonEvent(&Data); \ \ if (status) {SensPrintToDebugger(SENS_DBG, (SENSLOGN "SensNotifyWinlogonEvent(0x%x) returned %d\n", _EventType_, status));} \ } /***************************************************************************** * * IsRemoteSession * * On a Terminal Server: returns TRUE if current Session is the not the physical * console , FALSE if it is the console session (SessionId == 0) * On non-Hydra NT: always returns FALSE * * ENTRY: * nothing * * EXIT: * nothing * ****************************************************************************/ typedef BOOL (__stdcall * PFNPROCESSIDTOSESSIONID)(DWORD, PDWORD); #define PFN_FIRSTTIME (PFNPROCESSIDTOSESSIONID(-1)) BOOL IsRemoteSession(VOID) { static PFNPROCESSIDTOSESSIONID s_pfn = PFN_FIRSTTIME; static BOOL bCachedIsRemoteSession = FALSE; DWORD dwSessionId; if (PFN_FIRSTTIME == s_pfn) { HINSTANCE hinst = GetModuleHandle(L"KERNEL32.DLL"); if (hinst) { s_pfn = (PFNPROCESSIDTOSESSIONID)GetProcAddress(hinst, "ProcessIdToSessionId"); } else { s_pfn = NULL; } } if (s_pfn && s_pfn(GetCurrentProcessId(), &dwSessionId)) { bCachedIsRemoteSession = (dwSessionId != 0); } // we set s_pfn = NULL to guarntee we only ever call ProcessIdToSessionId // once, and after that we just use the bCachedIsRemoteSession value s_pfn = NULL; return bCachedIsRemoteSession; } DWORD WINAPI SensLogonEvent( LPVOID lpvParam ) /*++ Routine Description: Hook to trap the Logon Event. In addition, we do the following: a. Populate our token table with the current token. b. Initialize The token RPC interface, if necessary. Arguments: lpvParam - Winlogon notification info. Return Value: RPC status from SensNotifyWinlogonEvent() --*/ { DWORD status; PWLX_NOTIFICATION_INFO pTempInfo = (PWLX_NOTIFICATION_INFO) lpvParam; DUMP_INFO("Logon"); ActiveUserList->Add( pTempInfo ); FIRE_EVENT(SENS_NOTIFY_WINLOGON_LOGON); return status; } DWORD WINAPI SensLogoffEvent( LPVOID lpvParam ) /*++ Routine Description: Hook to trap the Logoff Event. We notify various components (like IR, OneStop, EventSystem, SENS) of the Logoff event. Arguments: lpvParam - Winlogon notification info. Notes: a. The system logoff will block till this call returns. Be very careful in adding code here. Return Value: RPC status from SensNotifyWinlogonEvent() --*/ { DWORD status; DWORD dwError; HRESULT hr; PWLX_NOTIFICATION_INFO pTempInfo; hr = S_OK; dwError = 0x0; pTempInfo = (PWLX_NOTIFICATION_INFO) lpvParam; LogMessage((SENSLOGN "[%d] Entered Logoff.\n", GetTickCount())); DUMP_INFO("Logoff"); // Try to fire SENS Event LogMessage((SENSLOGN "[%d] Notifying SENS...\n", GetTickCount())); FIRE_EVENT(SENS_NOTIFY_WINLOGON_LOGOFF); LogMessage((SENSLOGN "[%d] SensNotifyWinlogonEvent(LOGOFF) succeeded.\n", GetTickCount())); // Notify IR LogMessage((SENSLOGN "[%d] Notifying IR...\n", GetTickCount())); OnUserLogoff( pTempInfo ); LogMessage((SENSLOGN "[%d] OnUserLogoff() succeeded, notified IR.\n", GetTickCount())); // Start OneStop if necessary, except on Restart. if ( ((pTempInfo->Flags & EWX_REBOOT) == 0) && (IsAutoSyncEnabled(pTempInfo->hToken, AUTOSYNC_ON_LOGOFF))) { // // NOTE: If SENS ever becomes demandstarted on NT5, there are a couple // of things that can be done: // o Call StartSensIfNecessary() here. (OR) // o Make Sens APIs call StartSensIfNecessary(). // LogMessage((SENSLOGN "[%d] Notifying OneStop...\n", GetTickCount())); hr = SensNotifyOneStop(pTempInfo->hToken, SYNC_MANAGER_LOGOFF, TRUE); LogMessage((SENSLOGN "[%d] SensNotifyOneStop() returned 0x%x\n", GetTickCount(), hr)); } // Notify EventSystem LogMessage((SENSLOGN "[%d] Notifying EventSystem...\n", GetTickCount())); hr = SensNotifyEventSystem(NOTIFY_LCE_LOGOFF, lpvParam); LogMessage((SENSLOGN "[%d] SensNotifyEventSystem() returned 0x%x\n", GetTickCount(), hr)); // // Remove token handle from the list at the end so that COM activation // works till SENS gets done with event firings. // ActiveUserList->Remove( pTempInfo ); LogMessage((SENSLOGN "Removed current user's token!\n")); return status; } DWORD WINAPI SensStartupEvent( LPVOID lpvParam ) /*++ Routine Description: Hook to trap the Startup Event. Do some token management related initialization. Arguments: lpvParam - Winlogon notification info. Notes: a. This occurs very early in the bootup sequence. At this time, SENS service hasn't yet started up. Return Value: RPC status from SensNotifyWinlogonEvent() --*/ { DWORD status; DWORD dwLastError; status = 0x0; gbIsTokenCodeInitialized = FALSE; // // Create a SID representing the Local System account. // SID_IDENTIFIER_AUTHORITY Authority = SECURITY_NT_AUTHORITY; status = RtlAllocateAndInitializeSid( &Authority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &LocalSystemSid ); if (!NT_SUCCESS(status)) { return RtlNtStatusToDosError(status); } // // Create the table of logged-in users. // ActiveUserList = new USER_LOGON_TABLE( &status ); if (!ActiveUserList || status) { delete ActiveUserList; return ERROR_NOT_ENOUGH_MEMORY; } SetupMutex = new MUTEX( &status ); if (!SetupMutex || status) { delete SetupMutex; return ERROR_NOT_ENOUGH_MEMORY; } InitializeNotifyInterface(); // // We are ready to use Token Code. // gbIsTokenCodeInitialized = TRUE; LogMessage((SENSLOGN "**** Token code initialized successfully ****\n")); // // Create an Event to indicate the starting of SENS. // ghSensStartedEvent = CreateEvent( NULL, // Specific Security Attributes TRUE, // Event is ManualReset FALSE, // Initial state is not Signalled SENS_STARTED_EVENT ); if (ghSensStartedEvent == NULL) { dwLastError = GetLastError(); LogMessage((SENSLOGN "SensStartupEvent() - CreateEvent() failed - %x.\n", dwLastError)); } else { LogMessage((SENSLOGN "SensStartedEvent created successfully\n")); } DUMP_INFO("Startup"); return status; } BOOLEAN InitializeNotifyInterface( void ) /*++ Routine Description: Initialize RPC interface for accepting calls to GetCurrentUserToken() API. Arguments: None. Return Value: TRUE, if successful FALSE, otherwise. --*/ { unsigned i; DWORD status; SetupMutex->Enter(); // // Register the interface for GetCurrentUserToken(). // status = RpcServerRegisterAuthInfo( NULL, RPC_C_AUTHN_WINNT, NULL, NULL ); if (status) { SetupMutex->Leave(); return FALSE; } status = RpcServerUseAllProtseqsIf( RPC_C_PROTSEQ_MAX_REQS_DEFAULT, _GetUserToken_ServerIfHandle, NULL ); if (status) { SetupMutex->Leave(); return FALSE; } // // We expect another part of the process has already called RpcServerListen. // status = RpcServerRegisterIfEx( _GetUserToken_ServerIfHandle, NULL, NULL, 0, RPC_C_LISTEN_MAX_CALLS_DEFAULT, AllowLocalSystem ); if (status) { SetupMutex->Leave(); return FALSE; } if (!IsRemoteSession()) { // // Make a handle to the Infrared Monitor app. // RPC_BINDING_HANDLE hServer; status = RpcBindingFromStringBinding(L"ncalrpc:[,security=impersonation dynamic false]", &hServer); if (status) { SetupMutex->Leave(); return FALSE; } RPC_SECURITY_QOS RpcSecQos; RpcSecQos.Version= RPC_C_SECURITY_QOS_VERSION_1; RpcSecQos.ImpersonationType= RPC_C_IMP_LEVEL_IMPERSONATE; RpcSecQos.IdentityTracking= RPC_C_QOS_IDENTITY_DYNAMIC; RpcSecQos.Capabilities= RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH; status= RpcBindingSetAuthInfoEx(hServer, L"NT Authority\\System", RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_WINNT, NULL, RPC_C_AUTHZ_NONE, (RPC_SECURITY_QOS *)&RpcSecQos); if (RPC_S_OK != status) { RpcBindingFree(&hServer); SetupMutex->Leave(); return FALSE; } g_hIrxfer = hServer; } SetupMutex->Leave(); return TRUE; } DWORD WINAPI SensStartShellEvent( LPVOID lpvParam ) /*++ Routine Description: Hook to trap the StartShell Event. We treat this as Logon and notify various components (IR, OneStop, SENS, EventSystem) of Logon. Arguments: lpvParam - Winlogon notification info. Notes: a. When this function is called by Winlogon, the shell has begun starting. There is no guarantee that shell has started completely and is up and running. b. We create a thread to do bulk of the work and allow the function to return. Return Value: RPC status from SensNotifyWinlogonEvent() --*/ { BOOL bRetVal; DWORD status; DWORD dwLastError; PWLX_NOTIFICATION_INFO pTempInfo; dwLastError = 0; bRetVal = TRUE; DUMP_INFO("StartShell"); // // Allocate space for the parameters // pTempInfo = (PWLX_NOTIFICATION_INFO) lpvParam; gpStartShellInfo = (PWLX_NOTIFICATION_INFO) new char[(sizeof(WLX_NOTIFICATION_INFO))]; if (gpStartShellInfo == NULL) { dwLastError = GetLastError(); LogMessage((SENSLOGN "SensStartShellEvent(): new() failed - %x.\n", dwLastError)); return dwLastError; } memcpy(gpStartShellInfo, pTempInfo, sizeof(WLX_NOTIFICATION_INFO)); // String in WLX_NOTIFICATION_INFO are Unicode gpStartShellInfo->UserName = (PWSTR) new WCHAR[(wcslen(pTempInfo->UserName) + 1)]; gpStartShellInfo->Domain = (PWSTR) new WCHAR[(wcslen(pTempInfo->Domain) + 1)]; gpStartShellInfo->WindowStation = (PWSTR) new WCHAR[(wcslen(pTempInfo->WindowStation) + 1)]; if ( (gpStartShellInfo->UserName == NULL) || (gpStartShellInfo->Domain == NULL) || (gpStartShellInfo->WindowStation == NULL)) { dwLastError = GetLastError(); LogMessage((SENSLOGN "SensStartShellEvent(): new() of strings failed - %x.\n", dwLastError)); delete gpStartShellInfo->UserName; delete gpStartShellInfo->Domain; delete gpStartShellInfo->WindowStation; delete gpStartShellInfo; gpStartShellInfo = NULL; return dwLastError; } // // Copy the parameters // wcscpy(gpStartShellInfo->UserName, pTempInfo->UserName); wcscpy(gpStartShellInfo->Domain, pTempInfo->Domain); wcscpy(gpStartShellInfo->WindowStation, pTempInfo->WindowStation); // // Create a thread to wait on the StartShell event // bRetVal = SensQueueUserWorkItem( (LPTHREAD_START_ROUTINE) SensWaitToStartRoutine, gpStartShellInfo, // Winlogon event info SENS_LONG_ITEM // Flags ); if (FALSE == bRetVal) { dwLastError = GetLastError(); LogMessage((SENSLOGN "SensStartShellEvent(): SensQueueUserWorkItem() failed with %x.\n", dwLastError)); // Cleanup delete gpStartShellInfo->UserName; delete gpStartShellInfo->Domain; delete gpStartShellInfo->WindowStation; delete gpStartShellInfo; gpStartShellInfo = NULL; } return dwLastError; } DWORD WINAPI SensPostShellEvent( LPVOID lpvParam ) /*++ Routine Description: Hook to trap the Post Shell Event. Arguments: lpvParam - Winlogon notification info. Return Value: RPC status from SensNotifyWinlogonEvent() --*/ { DWORD status; DUMP_INFO("Post Shell"); FIRE_EVENT(SENS_NOTIFY_WINLOGON_POSTSHELL); return status; } DWORD WINAPI SensDisconnectEvent( LPVOID lpvParam ) /*++ Routine Description: Hook to trap the Session Disconnect Event. Arguments: lpvParam - Winlogon notification info. Return Value: RPC status from SensNotifyWinlogonEvent() --*/ { DWORD status; DUMP_INFO("Session Disconnect"); FIRE_EVENT(SENS_NOTIFY_WINLOGON_SESSION_DISCONNECT); return status; } DWORD WINAPI SensReconnectEvent( LPVOID lpvParam ) /*++ Routine Description: Hook to trap the Session Reconnect Event. Arguments: lpvParam - Winlogon notification info. Return Value: RPC status from SensNotifyWinlogonEvent() --*/ { DWORD status; DUMP_INFO("Session Reconnect"); FIRE_EVENT(SENS_NOTIFY_WINLOGON_SESSION_RECONNECT); return status; } DWORD WINAPI SensShutdownEvent( LPVOID lpvParam ) /*++ Routine Description: Hook to trap the Shutdown Event. Do some cleanup. Arguments: lpvParam - Winlogon notification info. Notes: a. It is guaranteed that COM activation will not work when this event is received. Return Value: RPC status from SensNotifyWinlogonEvent() --*/ { DWORD status; status = ERROR_SUCCESS; DUMP_INFO("Shutdown"); return status; } DWORD WINAPI SensLockEvent( LPVOID lpvParam ) /*++ Routine Description: Hook to trap the Display Lock Event. Arguments: lpvParam - Winlogon notification info. Return Value: RPC status from SensNotifyWinlogonEvent() --*/ { DWORD status; DUMP_INFO("Display Lock"); FIRE_EVENT(SENS_NOTIFY_WINLOGON_LOCK); return status; } DWORD WINAPI SensUnlockEvent( LPVOID lpvParam ) /*++ Routine Description: Hook to trap the Display unlock Event. Arguments: lpvParam - Winlogon notification info. Return Value: RPC status from SensNotifyWinlogonEvent() --*/ { DWORD status; DUMP_INFO("Display Unlock"); FIRE_EVENT(SENS_NOTIFY_WINLOGON_UNLOCK); return status; } DWORD WINAPI SensStartScreenSaverEvent( LPVOID lpvParam ) /*++ Routine Description: Hook to trap the ScreenSaver Start Event. Arguments: lpvParam - Winlogon notification info. Return Value: RPC status from SensNotifyWinlogonEvent() --*/ { DWORD status; DUMP_INFO("StartScreenSaver"); FIRE_EVENT(SENS_NOTIFY_WINLOGON_STARTSCREENSAVER); return status; } DWORD WINAPI SensStopScreenSaverEvent( LPVOID lpvParam ) /*++ Routine Description: Hook to trap the Screen Saver Stop Event. Arguments: lpvParam - Winlogon notification info. Return Value: RPC status from SensNotifyWinlogonEvent() --*/ { DWORD status; DUMP_INFO("StopScreenSaver"); FIRE_EVENT(SENS_NOTIFY_WINLOGON_STOPSCREENSAVER); return status; } DWORD WINAPI SensWaitToStartRoutine( LPVOID lpvParam ) /*++ Routine Description: This routine implements the work item that is queued when the StartShell event is received. Arguments: lpvParam - Winlogon notification info. Return Value: ERROR_SUCCESS, always --*/ { DWORD dwError; DWORD dwWaitStatus; DWORD status; HRESULT hr; PWLX_NOTIFICATION_INFO pTempInfo; dwError = ERROR_SUCCESS; dwWaitStatus = 0x0; pTempInfo = (PWLX_NOTIFICATION_INFO) lpvParam; // Give SENS a chance to start if it has not already stated. // Give up after a short time in case sens is has been set to manual start // We could check the service configuration but the thread as already been // created so we save won't very much. ASSERT(ghSensStartedEvent); dwError = WaitForSingleObject(ghSensStartedEvent, 20*1000); if (dwError != STATUS_WAIT_0) { LogMessage((SENSLOGN "[%d] Wait for sens start event timed out...\n", GetTickCount())); } // Notify EventSystem LogMessage((SENSLOGN "[%d] Notifying EventSystem...\n", GetTickCount())); hr = SensNotifyEventSystem(NOTIFY_LCE_STARTSHELL, lpvParam); LogMessage((SENSLOGN "[%d] SensNotifyEventSystem() returned 0x%x\n", GetTickCount(), hr)); // Notify IR LogMessage((SENSLOGN "[%d] Notifying IR...\n", GetTickCount())); OnUserLogon( pTempInfo ); LogMessage((SENSLOGN "[%d] OnUserLogon succeeded.\n", GetTickCount())); // Try to fire SENS Event LogMessage((SENSLOGN "[%d] Notifying SENS...\n", GetTickCount())); FIRE_EVENT(SENS_NOTIFY_WINLOGON_STARTSHELL); LogMessage((SENSLOGN "[%d] SensNotifyWinlogonEvent(STARTSHELL) succeeded.\n", GetTickCount())); // Cleanup delete pTempInfo->UserName; delete pTempInfo->Domain; delete pTempInfo->WindowStation; delete pTempInfo; gpStartShellInfo = NULL; return ERROR_SUCCESS; } HRESULT SensNotifyEventSystem( DWORD dwEvent, LPVOID lpvParam ) /*++ Routine Description: This routine notifies EventSystem of the Logon/Logoff events. Arguments: dwEvent - Tells if the event is Logon or Logoff. lpvParam - Winlogon notification info. Notes: a. EventSystem's notify routine has been observed sometimes to take a long time causing Logoff to take longer time to complete. Return Value: HRESULT from EventSystem's Notify routine. --*/ { HRESULT hr; hr = S_OK; PWLX_NOTIFICATION_INFO pTempInfo = (PWLX_NOTIFICATION_INFO)lpvParam; // // Notify the COM+ Event System that a user has just logged on. // Per-user subscriptions will be activated. // typedef HRESULT (__stdcall *LPFN_NOTIFICATION)(HANDLE); HMODULE hDLL; LPFN_NOTIFICATION lpfnNotify = NULL; hDLL = (HMODULE) LoadLibrary(EVENTSYSTEM_DLL); if (hDLL == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); } else { if (dwEvent == NOTIFY_LCE_STARTSHELL) { lpfnNotify = (LPFN_NOTIFICATION) GetProcAddress(hDLL, NOTIFY_LCE_LOGONUSER); } else if (dwEvent == NOTIFY_LCE_LOGOFF) { lpfnNotify = (LPFN_NOTIFICATION) GetProcAddress(hDLL, NOTIFY_LCE_LOGOFFUSER); } hr = (lpfnNotify == NULL) ? HRESULT_FROM_WIN32(GetLastError()) : (*lpfnNotify)(pTempInfo->hToken); FreeLibrary(hDLL); } return hr; } void OnUserLogon( WLX_NOTIFICATION_INFO * User ) { DWORD status = 0; if (FALSE == gbIsTokenCodeInitialized) { return; } if (IsRemoteSession()) { return; } if (!ImpersonateLoggedOnUser( User->hToken )) { return; } UserLoggedOn( g_hIrxfer, &status ); RevertToSelf(); } void OnUserLogoff( WLX_NOTIFICATION_INFO * User ) { DWORD status; if (FALSE == gbIsTokenCodeInitialized) { return; } if (IsRemoteSession()) { return; } if (!ImpersonateLoggedOnUser( User->hToken )) { return; } UserLoggedOff( g_hIrxfer, &status ); RevertToSelf(); } #define MAX_WINDOWSTATION_NAME_LENGTH 1000 DWORD _SecpGetCurrentUserToken( handle_t Binding, wchar_t WindowStation[], unsigned long ProcessId, unsigned long * pToken, unsigned long DesiredAccess ) { HANDLE LocalToken; HANDLE RemoteToken; HANDLE RemoteProcess; if (FALSE == gbIsTokenCodeInitialized) { return ERROR_OUTOFMEMORY; } // // Validate arguments. The only one that can cause us harm is the window station. // if (IsBadStringPtr( WindowStation, MAX_WINDOWSTATION_NAME_LENGTH)) { return ERROR_ACCESS_DENIED; } // // Clone the token into the requested process. // LocalToken = ActiveUserList->CurrentUserTokenFromWindowStation( WindowStation ); if (!LocalToken) { LogMessage((SENSLOGN "GetCurrentUserToken(): User not logged on!\n")); return ERROR_NOT_LOGGED_ON; } RemoteProcess = OpenProcess( PROCESS_DUP_HANDLE, FALSE, // not inheritable ProcessId ); if (!RemoteProcess) { LogMessage((SENSLOGN "GetCurrentUserToken(): OpenProcess() failed!\n")); return GetLastError(); } if (!DuplicateHandle( GetCurrentProcess(), LocalToken, RemoteProcess, &RemoteToken, DesiredAccess, FALSE, // not inheritable 0 // no funny options )) { LogMessage((SENSLOGN "GetCurrentUserToken(): DuplicateHandle() failed!\n")); CloseHandle( RemoteProcess ); return GetLastError(); } CloseHandle( RemoteProcess ); *pToken = HandleToUlong(RemoteToken); LogMessage((SENSLOGN "GetCurrentUserToken(): Succeeded. Returning 0x%x.\n", *pToken)); return ERROR_SUCCESS; } BOOL USER_LOGON_TABLE::Add( WLX_NOTIFICATION_INFO * User ) { USER_INFO_NODE * Entry; if (FALSE == gbIsTokenCodeInitialized) { return FALSE; } CLAIM_MUTEX Lock( Mutex ); Entry = FindInactiveEntry(); if (!Entry) { Entry = new USER_INFO_NODE; if (!Entry) { return FALSE; } Entry->fActive = FALSE; InsertTailList( &List, &Entry->Links ); } Entry->Info.Size = sizeof(WLX_NOTIFICATION_INFO); Entry->Info.Flags = User->Flags; Entry->Info.hToken = User->hToken; WCHAR * Buffer = new WCHAR[ 1+wcslen(User->UserName) + 1+wcslen(User->Domain) + 1+wcslen(User->WindowStation) ]; if (!Buffer) { Entry->Info.UserName = 0; Entry->Info.Domain = 0; Entry->Info.WindowStation = 0; return FALSE; } // // This must match the code in Remove(). // Entry->Info.UserName = Buffer; Entry->Info.Domain = Entry->Info.UserName+1+wcslen(User->UserName); Entry->Info.WindowStation = Entry->Info.Domain +1+wcslen(User->Domain); wcscpy(Entry->Info.UserName, User->UserName); wcscpy(Entry->Info.Domain, User->Domain); wcscpy(Entry->Info.WindowStation, User->WindowStation); Entry->fActive = TRUE; return TRUE; } BOOL USER_LOGON_TABLE::Remove( WLX_NOTIFICATION_INFO * User ) { if (FALSE == gbIsTokenCodeInitialized) { return FALSE; } USER_INFO_NODE * Entry = FromWindowStation( User->WindowStation ); if (Entry) { // // This must match the code in Add(). // delete Entry->Info.UserName; Entry->fActive = FALSE; Mutex.Leave(); return TRUE; } Mutex.Leave(); return FALSE; } HANDLE USER_LOGON_TABLE::CurrentUserTokenFromWindowStation( wchar_t WindowStation[] ) { HANDLE Token = NULL; USER_INFO_NODE * Node = FromWindowStation(WindowStation); if (Node) { Token = Node->Info.hToken; } Mutex.Leave(); return Token; } USER_INFO_NODE * USER_LOGON_TABLE::FromWindowStation( wchar_t WindowStation[] ) /*++ Note that the mutex is held on exit, to avoid race conditions. --*/ { USER_INFO_NODE * Node; LIST_ENTRY * Link; Mutex.Enter(); for (Link = List.Flink, Node = CONTAINING_RECORD(Link, USER_INFO_NODE, Links); Link != &List; Link = Link->Flink, Node = CONTAINING_RECORD(Link, USER_INFO_NODE, Links)) { if (Node->fActive && 0 == wcscmp(Node->Info.WindowStation, WindowStation)) { return Node; } } return 0; } USER_INFO_NODE * USER_LOGON_TABLE::FindInactiveEntry( void ) { USER_INFO_NODE * Node; LIST_ENTRY * Link; for (Link = List.Flink, Node = CONTAINING_RECORD(Link, USER_INFO_NODE, Links); Link != &List; Link = Link->Flink, Node = CONTAINING_RECORD(Link, USER_INFO_NODE, Links)) { if (!Node->fActive) { return Node; } } return 0; } RPC_STATUS RPC_ENTRY AllowLocalSystem ( IN RPC_IF_HANDLE InterfaceUuid, IN void *Context ) { if (RpcImpersonateClient(Context)) { return ERROR_ACCESS_DENIED; } // // Clone the user's token so we can launch apps on the desktop. // HANDLE ImpersonationToken; if (!OpenThreadToken( GetCurrentThread(), TOKEN_QUERY, TRUE, // use process token for access check &ImpersonationToken )) { return GetLastError(); } // // Get the SID from the token. // DWORD SizeNeeded = 0; TOKEN_USER * TokenData = NULL; // Get the size first. if (!GetTokenInformation( ImpersonationToken, TokenUser, 0, 0, &SizeNeeded )) { DWORD dwLastError = GetLastError(); if (ERROR_INSUFFICIENT_BUFFER != dwLastError) { CloseHandle( ImpersonationToken ); return dwLastError; } } __try { TokenData = (TOKEN_USER *) _alloca( SizeNeeded ); } __except(EXCEPTION_EXECUTE_HANDLER) { // Nothing } if (!TokenData) { CloseHandle( ImpersonationToken ); return ERROR_NOT_ENOUGH_MEMORY; } if (!GetTokenInformation( ImpersonationToken, TokenUser, TokenData, SizeNeeded, &SizeNeeded )) { CloseHandle( ImpersonationToken ); return GetLastError(); } CloseHandle(ImpersonationToken); if (!RtlEqualSid( TokenData->User.Sid, LocalSystemSid )) { return ERROR_ACCESS_DENIED; } return 0; }