/*++ 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 "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 SENS_SERVICE SENS_STRING("SENS") #define EVENTSYSTEM_DLL SENS_STRING("ES.DLL") // // Globals // HANDLE ghSensStartedEvent; BOOL gbIsTokenCodeInitialized; // For GetCurrentUserToken USER_LOGON_TABLE * ActiveUserList; // // Some useful Macros // #ifdef DETAIL_DEBUG #define DUMP_INFO(_EventType_) \ \ 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 - %ws\n", pInfo->UserName)); \ LogMessage((" Domain - %ws\n", pInfo->Domain)); \ LogMessage((" WinStation - %ws\n", pInfo->WindowStation)); \ 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")); #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) * * ENTRY: * nothing * * EXIT: * nothing * ****************************************************************************/ BOOL IsRemoteSession(VOID) { DWORD dwSessionId; if (ProcessIdToSessionId(GetCurrentProcessId(), &dwSessionId)) { return (dwSessionId != 0); } return FALSE; } 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 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())); // 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 the table of logged-in users. // ActiveUserList = new USER_LOGON_TABLE( &status ); if (!ActiveUserList || status) { delete ActiveUserList; 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. --*/ { RPC_STATUS status; if (!IsRemoteSession()) { status = RpcServerRegisterIfEx( _GetUserToken_ServerIfHandle, NULL, NULL, 0, RPC_C_LISTEN_MAX_CALLS_DEFAULT, AllowLocalSystem ); if (RPC_S_OK != status) { return FALSE; } } return TRUE; } DWORD WINAPI SensStartShellEvent( LPVOID lpvParam ) /*++ Routine Description: Hook to trap the StartShell Event. We treat this as Logon and notify various components (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; PWLX_NOTIFICATION_INFO pDuplicateInfo; dwLastError = 0; bRetVal = TRUE; DUMP_INFO("StartShell"); // Duplicate the parts of the notification info so we can process this // event on another thread. pTempInfo = (PWLX_NOTIFICATION_INFO) lpvParam; SIZE_T sizeUserName = wcslen(pTempInfo->UserName) + 1; SIZE_T sizeDomain = wcslen(pTempInfo->Domain) + 1; SIZE_T sizeWinsta = wcslen(pTempInfo->WindowStation) + 1; // // Allocate space for the notification block // pDuplicateInfo = new WLX_NOTIFICATION_INFO; if (pDuplicateInfo == NULL) { dwLastError = GetLastError(); LogMessage((SENSLOGN "SensStartShellEvent(): new() failed - %x.\n", dwLastError)); return dwLastError; } // // Copy simple types, safely initialize everything in case of failure. // pDuplicateInfo->Size = sizeof(WLX_NOTIFICATION_INFO); pDuplicateInfo->Flags = pTempInfo->Flags; pDuplicateInfo->hToken = NULL; // duplicated below, closed on error pDuplicateInfo->hDesktop = NULL; // not duplicated, unused pDuplicateInfo->pStatusCallback = NULL; // Allocate space for the string parameters. pDuplicateInfo->UserName = (PWSTR) new WCHAR[sizeUserName]; pDuplicateInfo->Domain = (PWSTR) new WCHAR[sizeDomain]; pDuplicateInfo->WindowStation = (PWSTR) new WCHAR[sizeWinsta]; if ( (pDuplicateInfo->UserName == NULL) || (pDuplicateInfo->Domain == NULL) || (pDuplicateInfo->WindowStation == NULL)) { dwLastError = GetLastError(); LogMessage((SENSLOGN "SensStartShellEvent(): new() of strings failed - %x.\n", dwLastError)); goto Cleanup; } // // Deep copy the parameters // StringCchCopy(pDuplicateInfo->UserName, sizeUserName, pTempInfo->UserName); StringCchCopy(pDuplicateInfo->Domain, sizeDomain, pTempInfo->Domain); StringCchCopy(pDuplicateInfo->WindowStation, sizeWinsta, pTempInfo->WindowStation); if ( !DuplicateHandle(GetCurrentProcess(), pTempInfo->hToken, GetCurrentProcess(), &pDuplicateInfo->hToken, 0, // ignored, same access flag set FALSE, DUPLICATE_SAME_ACCESS)) { dwLastError = GetLastError(); LogMessage((SENSLOGN "SensStartShellEvent(): failed to duplicate users token - %x\n", dwLastError)); goto Cleanup; } // // Create a thread to wait on the StartShell event // bRetVal = SensQueueUserWorkItem( (LPTHREAD_START_ROUTINE) SensWaitToStartRoutine, pDuplicateInfo, // Winlogon event info SENS_LONG_ITEM // Flags ); if (FALSE == bRetVal) { dwLastError = GetLastError(); LogMessage((SENSLOGN "SensStartShellEvent(): SensQueueUserWorkItem() failed with %x.\n", dwLastError)); } // Success - SensWaitToStartRoutine now owns pDuplicateInfo return 0; Cleanup: // All branches here occur after the parameter block is copied and initalized ASSERT(pDuplicateInfo); if (pDuplicateInfo->hToken) { CloseHandle(pDuplicateInfo->hToken); } delete pDuplicateInfo->UserName; delete pDuplicateInfo->Domain; delete pDuplicateInfo->WindowStation; delete pDuplicateInfo; 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)); // 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 CloseHandle(pTempInfo->hToken); delete pTempInfo->UserName; delete pTempInfo->Domain; delete pTempInfo->WindowStation; delete pTempInfo; 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; } #define MAX_WINDOWSTATION_NAME_LENGTH 1000 DWORD _SecpGetCurrentUserToken( handle_t Binding, wchar_t WindowStation[], unsigned long * pToken, unsigned long DesiredAccess ) /*++ Routine Description: RPC server manager routine. This is the only method in the GetUserToken interface. The interface registered a callback function to verify that the caller is both non-remote and is local system. Arguments: Binding - RPC server binding WindowStation - window station retrive the token from pToken - Upon return this is the handle ID of the duplicated token in the callers process DesiredAccess - Access mask passed directly to the DuplicateHandle() call. Return Value: WIN32 ERROR value or 0 --*/ { HANDLE LocalToken; HANDLE RemoteToken; HANDLE RemoteProcess; unsigned long ProcessId; 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; } if (I_RpcBindingInqLocalClientPID(0, &ProcessId)) { 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(); } // Only way to verify that the client process has not exited while waiting for // the response. (this is to avoid duplicating the handle into another process). // // This is done after the OpenProcess() call to keep the PID from being reused before // we could open the process. if (RPC_S_OK != RpcImpersonateClient(0)) { CloseHandle(RemoteProcess); return ERROR_ACCESS_DENIED; } // We have already validated that the caller is local system in the interface callback. RpcRevertToSelf(); 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; SIZE_T sizeUserName = wcslen(User->UserName) + 1; SIZE_T sizeDomain = wcslen(User->Domain) + 1; SIZE_T sizeWinsta = wcslen(User->WindowStation) + 1; WCHAR * Buffer = new WCHAR[sizeUserName + sizeDomain + sizeWinsta]; 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); StringCchCopy(Entry->Info.UserName, sizeUserName, User->UserName); StringCchCopy(Entry->Info.Domain, sizeDomain, User->Domain); StringCchCopy(Entry->Info.WindowStation, sizeWinsta, 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 ) { unsigned fLocal = FALSE; if (RPC_S_OK != I_RpcBindingIsClientLocal(NULL, &fLocal)) { return ERROR_ACCESS_DENIED; } if (!fLocal) { return ERROR_ACCESS_DENIED; } if (!IsRpcCallerLocalSystem()) { return ERROR_ACCESS_DENIED; } return RPC_S_OK; }