|
|
// ----------------------------------------------------------------------------
//
// UManClnt.c
//
// Utility Manager client depending code (used by UtilMan and UManDlg)
//
// Author: J. Eckhardt, ECO Kommunikation
// Copyright (c) 1997-1999 Microsoft Corporation
//
// History: created oct-98 by JE
// JE nov-15-98: removed any code related to key hook
// YX may-27-99: functions to start apps under current user account
// YX may-29-99: apps start under user account even from LogOn desktop,
// if possible
// YX jun-04-99: code to report the app processes status even if they
// have been started outside Utilman
// YX jun-23-99: IsAdmin function added (used in the dialog)
// Bug Fixes and Changes Anil Kumar 1999
// ----------------------------------------------------------------------------
#include <windows.h>
#include <TCHAR.h>
#include <WinSvc.h>
#include "_UMTool.h"
#include "w95trace.c"
#include "UtilMan.h"
#include "_UMClnt.h"
#include "ums_ctrl.h"
#include "w95trace.h"
// Handle to the utilman instance that is showing UI
HANDLE g_hUIProcess = 0; // From Terminal services
extern BOOL GetWinStationUserToken(ULONG, PHANDLE); // Private User function that returns user token for session 0 only
HANDLE GetCurrentUserTokenW( WCHAR WinSta[], DWORD desiredAccess);
#include <psapi.h>
#define MAX_NUMBER_OF_PROCESSES 2048
//
// RunningInMySession - returns TRUE if the specified process ID is
// running in the same session as UtilMan. In Whistler, with terminal
// services integrated, UtilMan is able to get information about
// processes that are not running in the same session. We must
// avoid impacting these processes.
//
BOOL RunningInMySession(DWORD dwProcessId) { DWORD dwSessionId = -1; static DWORD dwMySessionId = -1;
if (-1 == dwMySessionId) { ProcessIdToSessionId(GetCurrentProcessId(), &dwMySessionId); }
ProcessIdToSessionId(dwProcessId, &dwSessionId);
return (dwSessionId == dwMySessionId)?TRUE:FALSE; }
// These are for compiling with irnotig.lib
// To be REMOVED once that becomes an API of advapi.lib
PVOID MIDL_user_allocate(IN size_t BufferSize) { return( LocalAlloc(0, BufferSize)); }
VOID MIDL_user_free(IN PVOID Buffer) { LocalFree( Buffer ); }
BOOL StartAppAsUser( LPCTSTR appPath, LPTSTR cmdLine, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation);
BOOL GetApplicationProcessInfo(umclient_tsp tspClient, BOOL fCloseHandle); BOOL CloseAllWindowsByProcessID(DWORD procID);
// ---------------------------------
BOOL StartClient(HWND hParent,umclient_tsp client) { if (client->runCount >= client->machine.MaxRunCount || client->runCount >= MAX_APP_RUNCOUNT) { DBPRINTF(_TEXT("StartClient run count >= max run count\r\n")); return FALSE; }
switch (client->machine.ApplicationType) { case APPLICATION_TYPE_APPLICATION: { BOOL fStarted; TCHAR ApplicationPath[MAX_APPLICATION_PATH_LEN+100];
if (!GetClientApplicationPath( client->machine.ApplicationName , ApplicationPath , MAX_APPLICATION_PATH_LEN)) { return FALSE; } fStarted = StartApplication(ApplicationPath , UTILMAN_STARTCLIENT_ARG , client->user.fCanRunSecure , &client->processID[client->runCount] , &client->hProcess[client->runCount] , &client->mainThreadID[client->runCount]);
if (!fStarted) { return FALSE; }
client->runCount++; client->state = UM_CLIENT_RUNNING; break; }
case APPLICATION_TYPE_SERVICE: { DWORD i = 0; SERVICE_STATUS ssStatus; SC_HANDLE hService; TCHAR arg2[200]; LPTSTR args[2]; SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (!hSCM) return FALSE;
hService = OpenService(hSCM, client->machine.ApplicationName, SERVICE_ALL_ACCESS); CloseServiceHandle(hSCM); if (!hService) { return FALSE; } arg2[0] = 0; args[0] = UTILMAN_STARTCLIENT_ARG; args[1] = arg2; if (!StartService(hService,1,args)) { CloseServiceHandle(hService); return FALSE; }
Sleep(1000); while(QueryServiceStatus(hService, &ssStatus)) { if (ssStatus.dwCurrentState == SERVICE_RUNNING) break;
Sleep(1000); i++; if (i >= 60) break; }
if (ssStatus.dwCurrentState != SERVICE_RUNNING) { CloseServiceHandle(hService); return FALSE; }
CloseServiceHandle(hService);
client->runCount++; client->processID[0] = 0; client->mainThreadID[0] = 0; client->hProcess[0] = NULL; client->state = UM_CLIENT_RUNNING;
break; }
default: return FALSE; } return TRUE; } // ---------------------------------
// The hParent window is used to signal whether the stop is interactive
// (and thus WM_COLSE could be used) or is a reaction to the desktop
// change
BOOL StopClient(umclient_tsp client) { if (!client->runCount || client->runCount > MAX_APP_RUNCOUNT) return FALSE;
switch (client->machine.ApplicationType) { case APPLICATION_TYPE_APPLICATION: { DWORD j, runCount = client->runCount; for (j = 0; j < runCount; j++) { // If client was started outside UtilMan then try to get its process ID
if (client->mainThreadID[j] == 0) { if (!GetApplicationProcessInfo(client, FALSE)) { // could not find the client, so prevent attempts to stop it
client->hProcess[j] = NULL; } } if (client->hProcess[j]) { // Try to close the application by sending a WM_CLOSE message to
// all the windows in opened by the process. Then just kill it.
BOOL sent = CloseAllWindowsByProcessID(client->processID[j]); if (!sent) { TerminateProcess(client->hProcess[j],1); }
client->processID[j] = 0; CloseHandle(client->hProcess[j]); client->hProcess[j] = NULL; client->mainThreadID[j] = 0; client->runCount--; if (!client->runCount) client->state = UM_CLIENT_NOT_RUNNING; } } if (runCount != client->runCount) { for (j = 0; j < (runCount-1); j++) { if (!client->hProcess[j]) { memmove(&client->processID[j], &client->processID[j+1],sizeof(DWORD)*(runCount-j-1)); memmove(&client->hProcess[j], &client->hProcess[j+1],sizeof(HANDLE)*(runCount-j-1)); memmove(&client->mainThreadID[j], &client->mainThreadID[j+1],sizeof(DWORD)*(runCount-j-1)); } } } break; }
case APPLICATION_TYPE_SERVICE: { SERVICE_STATUS ssStatus; SC_HANDLE hService; SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (!hSCM) return FALSE; hService = OpenService(hSCM, client->machine.ApplicationName, SERVICE_ALL_ACCESS); CloseServiceHandle(hSCM); if (!hService) return FALSE; if (ControlService(hService, SERVICE_CONTROL_STOP, &ssStatus)) { DWORD i = 0; Sleep(1000); while(QueryServiceStatus(hService, &ssStatus)) { if (ssStatus.dwCurrentState == SERVICE_STOPPED) break; Sleep(1000); i++; if (i >= 60) break; }
if (ssStatus.dwCurrentState != SERVICE_STOPPED) { CloseServiceHandle(hService); return FALSE; } }
CloseServiceHandle(hService); client->runCount--; client->processID[0] = 0; client->hProcess[0] = NULL; client->mainThreadID[0] = 0; if (!client->runCount) client->state = UM_CLIENT_NOT_RUNNING; break; }
default: return FALSE; }
return TRUE; }//StopClient
// ---------------------------------
BOOL GetClientApplicationPath(LPTSTR ApplicationName, LPTSTR ApplicationPath,DWORD len) { HKEY hKey, sKey; DWORD ec, slen,type;
ec = RegOpenKeyEx(HKEY_LOCAL_MACHINE, UM_REGISTRY_KEY,0,KEY_READ,&hKey); if (ec != ERROR_SUCCESS) return FALSE; ec = RegOpenKeyEx(hKey,ApplicationName,0,KEY_READ,&sKey); if (ec != ERROR_SUCCESS) { RegCloseKey(hKey); return FALSE; } slen = sizeof(TCHAR)*len; ec = RegQueryValueEx(sKey,UMR_VALUE_PATH,NULL,&type,(LPBYTE)ApplicationPath,&slen); if ((ec != ERROR_SUCCESS) || (type != REG_SZ)) { ApplicationPath[0] = TEXT('\0'); RegCloseKey(sKey); RegCloseKey(hKey); return FALSE; } ApplicationPath[slen-1] = TEXT('\0'); RegCloseKey(sKey); RegCloseKey(hKey); return (slen)?TRUE:FALSE; }//GetClientApplicationPath
BOOL TestServiceClientRuns(umclient_tsp client,SERVICE_STATUS *ssStatus) { SC_HANDLE hService; SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (!hSCM) return FALSE; hService = OpenService(hSCM, client->machine.ApplicationName, SERVICE_ALL_ACCESS); CloseServiceHandle(hSCM); if (!hService) return FALSE; if (!QueryServiceStatus(hService, ssStatus) || (ssStatus->dwCurrentState == SERVICE_STOPPED)) { CloseServiceHandle(hService); return FALSE; }
CloseServiceHandle(hService); return TRUE; }
//
// CheckStatus - Called from utilman's main timer and the dialog's timer
// to detect the state of running applications and pick up
// any that are started outside of utilman.
//
// Returns: TRUE if the state of any application has changed else FALSE
//
BOOL CheckStatus(umclient_tsp c, DWORD cClients) { DWORD i; BOOL fAnyChanges = FALSE;
for (i = 0; i < cClients && cClients < MAX_NUMBER_OF_CLIENTS; i++) { // detect the client process started outside UMan
if ( (!c[i].runCount)) { if (GetApplicationProcessInfo(&c[i], TRUE)) { fAnyChanges = TRUE; } } // detect clients not running anymore or not responding
switch (c[i].machine.ApplicationType) { case APPLICATION_TYPE_APPLICATION: { DWORD j, dwRunCount = c[i].runCount; for (j = 0; j < dwRunCount && j < MAX_APP_RUNCOUNT; j++) { // step 1: test if terminated
if (!GetProcessVersion(c[i].processID[j])) { c[i].runCount--; c[i].hProcess[j] = NULL; c[i].processID[j] = 0; c[i].mainThreadID[j] = 0; c[i].state = UM_CLIENT_NOT_RUNNING; c[i].user.fRestartOnDefaultDesk = FALSE; fAnyChanges = TRUE; continue; // its not running anymore
}
// step 2: test if responding (only processes started by utilman - mainThreadID != 0)
if (c[i].mainThreadID[j] != 0) { if (!PostThreadMessage(c[i].mainThreadID[j],WM_QUERYENDSESSION,0,ENDSESSION_LOGOFF)) { c[i].state = UM_CLIENT_NOT_RESPONDING; fAnyChanges = TRUE; continue; // its not responding
} }
if (c[i].state != UM_CLIENT_RUNNING) { fAnyChanges = TRUE; } c[i].state = UM_CLIENT_RUNNING; }
if (dwRunCount != c[i].runCount) { for (j = 0; j < (dwRunCount-1) && j < (MAX_APP_RUNCOUNT-1); j++) { if (!c[i].processID[j]) { memmove(&c[i].processID[j], &c[i].processID[j+1],sizeof(DWORD)*(dwRunCount-j-1)); memmove(&c[i].hProcess[j], &c[i].hProcess[j+1],sizeof(HANDLE)*(dwRunCount-j-1)); memmove(&c[i].mainThreadID[j], &c[i].mainThreadID[j+1],sizeof(DWORD)*(dwRunCount-j-1)); } } } break; } case APPLICATION_TYPE_SERVICE: { SERVICE_STATUS ssStatus; if (!TestServiceClientRuns(&c[i],&ssStatus)) { c[i].runCount--; c[i].processID[0] = 0; c[i].mainThreadID[0] = 0; c[i].state = UM_CLIENT_NOT_RUNNING; fAnyChanges = TRUE; } break; } } }
return fAnyChanges; }
__inline DWORD GetCurrentSession() { static DWORD dwSessionId = -1; if (-1 == dwSessionId) { ProcessIdToSessionId(GetCurrentProcessId(), &dwSessionId); } return dwSessionId; }
// GetUserAccessToken - return the logged on user's access token
//
// If fNeedImpersonationToken is true the token will be an
// impersonation token otherwise it will be a primary token.
// The returned token will be 0 if security calls fail.
//
// Notes: Caller must call CloseHandle on the returned handle.
//
HANDLE GetUserAccessToken(BOOL fNeedImpersonationToken, BOOL *fError) { HANDLE hUserToken = 0; HANDLE hImpersonationToken = 0; *fError = FALSE;
if (!GetWinStationUserToken(GetCurrentSession(), &hImpersonationToken)) { // Call private API in the case where terminal services aren't running
HANDLE hPrimaryToken = 0; DWORD dwFlags = TOKEN_QUERY | TOKEN_DUPLICATE; dwFlags |= (fNeedImpersonationToken)? TOKEN_IMPERSONATE : TOKEN_ASSIGN_PRIMARY; hPrimaryToken = GetCurrentUserTokenW (L"WinSta0", dwFlags); // GetCurrentUserTokenW returns a primary token; turn
// it into an impersonation token if needed
if (hPrimaryToken && fNeedImpersonationToken) { if (!DuplicateToken(hPrimaryToken, SecurityImpersonation, &hUserToken)) { *fError = TRUE; DBPRINTF(TEXT("GetUserAccessToken: DuplicateToken returned %d\r\n"), GetLastError()); } CloseHandle(hPrimaryToken); } else { // otherwise, give out the primary token even if NULL
hUserToken = hPrimaryToken; } } else { // Terminal services are running see if we need primary token
if (hImpersonationToken && !fNeedImpersonationToken) { if (!DuplicateTokenEx(hImpersonationToken, 0, NULL , SecurityImpersonation, TokenPrimary, &hUserToken)) { *fError = TRUE; DBPRINTF(TEXT("GetUserAccessToken: DuplicateTokenEx returned %d\r\n"), GetLastError()); }
CloseHandle(hImpersonationToken);
} else { // otherwise, give out the impersonation token even if NULL
hUserToken = hImpersonationToken; } }
return hUserToken; }
// StartAppAsUser - start the app in the context of the logged on user
//
BOOL StartAppAsUser( LPCTSTR appPath, LPTSTR cmdLine, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) { HANDLE hNewToken = 0; BOOL fStarted = FALSE; BOOL fError; // Get the our process's primary token (only succeeds if we are SYSTEM)
hNewToken = GetUserAccessToken(FALSE, &fError); if (hNewToken) { // running in system context so impersonate the logged on user
fStarted = CreateProcessAsUser( hNewToken, appPath, cmdLine, 0, 0, FALSE, NORMAL_PRIORITY_CLASS , 0, 0, lpStartupInfo, lpProcessInformation );
CloseHandle( hNewToken ); DBPRINTF(TEXT("StartAppAsUser: CreateProcessAsUser(%s, %s) returns %d\r\n"), appPath, cmdLine, fStarted); } else if (IsInteractiveUser()) { TCHAR szArg[] = UTILMAN_STARTCLIENT_ARG; // Running in interactive user's context, just do normal create. Since
// we are the interactive user default security descriptors will do.
fStarted = CreateProcess(appPath, szArg , NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE , NULL, NULL, lpStartupInfo, lpProcessInformation); DBPRINTF(TEXT("StartAppAsUser: CreateProcess(%s, %s) returns %d\r\n"), appPath, UTILMAN_STARTCLIENT_ARG, fStarted); }
// caller is going to close handles
return fStarted; }
// Functions to detect running copies of Accessibility utilities
// FindProcess - Searches the running processes by application name. If found,
// returns the process id. Else returns zero. If the process
// id is returned then phProcess is the process handle. The
// caller must close the process handle.
//
// pszApplicationName [in] - application as base.ext
// phProcess [out] - pointer to memory to receive process handle
//
// returns the process Id.
//
DWORD FindProcess(LPCTSTR pszApplicationName, HANDLE *phProcess) { DWORD dwProcId = 0; DWORD adwProcess[MAX_NUMBER_OF_PROCESSES]; // array to receive the process identifiers
DWORD cProcesses; DWORD dwThisProcess = GetCurrentProcessId(); unsigned int i;
*phProcess = 0;
// Get IDs of all running processes
if (!EnumProcesses(adwProcess, sizeof(adwProcess), &cProcesses)) return 0;
// cProcesses is returned as bytes; convert to number of processes
cProcesses = cProcesses/sizeof(DWORD); if (cProcesses > MAX_NUMBER_OF_PROCESSES) cProcesses = MAX_NUMBER_OF_PROCESSES; // open each process and test against pszApplicationName
for (i = 0; i < cProcesses; i++) { HANDLE hProcess; //
// EnumProcesses returns process IDs across all sessions but
// we are only interested in processes in our session
//
if (!RunningInMySession(adwProcess[i])) continue;
// Skip this process
if (dwThisProcess == adwProcess[i]) continue;
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ , FALSE, adwProcess[i]);
if (hProcess != NULL) { HMODULE hMod; TCHAR szProcessName[MAX_PATH]; DWORD ccbProcess;
// find the module handle of exe of this process then it's base name (name.ext)
if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod), &ccbProcess) ) { DWORD ctch = GetModuleBaseName(hProcess, hMod, szProcessName, MAX_PATH); if (ctch && _wcsicmp(szProcessName, pszApplicationName) == 0) { *phProcess = hProcess; // found it
dwProcId = adwProcess[i]; break; } } CloseHandle(hProcess); } } return dwProcId; }
// GetApplicationProcessInfo - Tries to find the process running for this application
BOOL GetApplicationProcessInfo(umclient_tsp tspClient, BOOL fCloseHandle) { DWORD dwProcId; HANDLE hProcess; TCHAR ApplicationPath[MAX_PATH]; TCHAR szDrive[_MAX_DRIVE]; TCHAR szPath[_MAX_PATH]; TCHAR szName[_MAX_FNAME+_MAX_EXT]; TCHAR szExt[_MAX_EXT];
if (GetClientApplicationPath(tspClient->machine.ApplicationName, ApplicationPath, MAX_PATH)) { // ApplicationPath may include path information but we need just the base name
_wsplitpath(ApplicationPath, szDrive, szPath, szName, szExt); lstrcat(szName, szExt);
dwProcId = FindProcess(szName, &hProcess); if (dwProcId) { tspClient->processID[0] = dwProcId; // I do not know how to get main thread ID
tspClient->mainThreadID[0] = 0; tspClient->runCount = 1; tspClient->state = UM_CLIENT_RUNNING; // In order to keep the HANDLE usable, we may have to keep it open.
// So, we do not close it here, but I consider it relatively safe,
// since we cannot execute this code more than once without
// terminating the process first (and thus closing the handle)
if (fCloseHandle) { CloseHandle(hProcess); tspClient->hProcess[0] = NULL; } else { tspClient->hProcess[0] = hProcess; } return TRUE; // the application is running
} }
return FALSE; // the application is not running
}
// YX 06-15-99 [
// Code to finid the window by its Process ID
static BOOL SentClose;
BOOL CALLBACK FindWindowByID(HWND hWnd, LPARAM lParam) { DWORD procID; if (GetWindowThreadProcessId(hWnd, &procID) != 0) { if (procID == (DWORD)lParam) { // The process, We are looking for
// Send a message to close this window
// CAUTION: A SendMessage is Synchronous, So It will freeze UM if the
// message doenot return so PostMessage is safer or a SendMessageTimeout()
// PostMessage is sufficient....
PostMessage(hWnd, WM_CLOSE, 0, 0); SentClose = TRUE; } } return TRUE; }
BOOL CloseAllWindowsByProcessID(DWORD procID) { BOOL rc = FALSE; SentClose = FALSE;
rc = EnumWindows(FindWindowByID, (LPARAM)procID);
return SentClose; }
// IsAdmin - Returns TRUE if our process has admin priviliges else FALSE
//
BOOL IsAdmin() { BOOL fStatus = FALSE; BOOL fIsAdmin = FALSE; PSID AdministratorsSid = AdminSid(TRUE);
if (AdministratorsSid) { fStatus = CheckTokenMembership(NULL, AdministratorsSid, &fIsAdmin); } return (fStatus && fIsAdmin); }
// IsInteractiveUser - Returns TRUE if our process has an Interactive User SID
//
BOOL IsInteractiveUser() { BOOL fStatus = FALSE; BOOL fIsInteractiveUser = FALSE; PSID psidInteractive = InteractiveUserSid(TRUE);
if (psidInteractive) { fStatus = CheckTokenMembership(NULL, psidInteractive, &fIsInteractiveUser); }
return (fStatus && fIsInteractiveUser); }
// IsSystem - Returns TRUE if our process is running as SYSTEM
//
BOOL IsSystem() { BOOL fStatus = FALSE; BOOL fIsLocalSystem = FALSE; SID_IDENTIFIER_AUTHORITY siaLocalSystem = SECURITY_NT_AUTHORITY; PSID psidSystem = SystemSid(TRUE);
if (psidSystem) { fStatus = CheckTokenMembership(NULL, psidSystem, &fIsLocalSystem); }
return (fStatus && fIsLocalSystem); }
BOOL StartApplication( LPTSTR pszPath, // IN path + filename of application to start
LPTSTR pszArg, // IN command line argument(s)
BOOL fIsTrusted, // IN TRUE if app can run on secure desktop
DWORD *pdwProcessId, // OUT if not NULL, returned process Id
HANDLE *phProcess, // OUT if not NULL, returned process handle (caller must close)
DWORD *pdwThreadId // OUT if not NULL, returned thread Id
) { STARTUPINFO si; PROCESS_INFORMATION pi; desktop_ts desktop; BOOL fStarted;
memset(&si,0,sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); memset(&pi,0,sizeof(PROCESS_INFORMATION));
QueryCurrentDesktop(&desktop, TRUE);
DBPRINTF(TEXT("StartApplication: pszPath=%s pszArg=%s fIsTrusted=%d Utilman is SYSTEM=%d\r\n"), pszPath, pszArg, fIsTrusted, IsSystem());
// If not on the winlogon desktop, first try starting the app as the interactive
// user. If that fails (eg. the case when OOBE runs after setup when there is
// no interactive user) then, if its the winlogon desktop or utilman is running
// SYSTEM and the app is trusted then use CreateProcess (the app will be running
// as SYSTEM). The latter case (running SYSTEM and the app is trusted allows
// applets to run when OOBE is running.
fStarted = FALSE;
if (desktop.type != DESKTOP_WINLOGON) { si.lpDesktop = desktop.name; fStarted = StartAppAsUser(pszPath, pszArg, &si,&pi); }
if (!fStarted && (desktop.type == DESKTOP_WINLOGON || (IsSystem() && fIsTrusted))) { if (fIsTrusted) { si.lpDesktop = 0; // Since we only run trusted apps we can run with default security descriptor
fStarted = CreateProcess(pszPath, pszArg, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE, NULL, NULL, &si, &pi); DBPRINTF(TEXT("StartApplication: trusted CreateProcess(%s, %s) returns %d\r\n"), pszPath, pszArg, fStarted); } }
if (fStarted) { if (pdwProcessId) { *pdwProcessId = pi.dwProcessId; } if (phProcess) { *phProcess = pi.hProcess; } else { CloseHandle(pi.hProcess); } if (pdwThreadId) { *pdwThreadId = pi.dwThreadId; } CloseHandle(pi.hThread); }
return fStarted; }
|