|
|
// SessionResolver.cpp : Implementation of CSessionResolver
#include "stdafx.h"
#include "SAFSessionResolver.h"
#include "SessionResolver.h"
#include <sddl.h>
#include <userenv.h>
#include <winerror.h>
#include <wtsapi32.h>
#include <winsta.h>
#include <wchar.h>
#include <stdarg.h>
#include <io.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#define ANSI
#include <stdarg.h>
#include <psapi.h>
#include <rderror.h>
const WCHAR c_wszHelpCtr[] = L"\\PCHealth\\HelpCtr\\Binaries\\HelpCtr.exe\""; const WCHAR c_wszCmdLine[] = L" -Mode \"hcp://system/Remote Assistance/RAHelpeeAcceptLayout.xml\" -url "; const WCHAR c_wszRdsAddin[] = L"rdsaddin.exe";
#define ARRAYSIZE(x) sizeof(x)/sizeof(x[0])
#define BUF_SZ 200
typedef struct _WTS_USER_SESSION_INFO { DWORD dwIndex; // Index into full table of these
DWORD dwSessionId; // WTS Session ID
HANDLE hUserToken; // Access token for user
HANDLE hEvent; // filled in by launchex, yank on this to say "yes"
HANDLE hProcess; // filled in by CreateProcessAsUser
HANDLE hThread; // so's this
DWORD dwProcessId; // and this
DWORD dwThreadId; // and this
} WTS_USER_SESSION_INFO, *PWTS_USER_SESSION_INFO;
// stolen from internal/ds/inc
#define RtlGenRandom SystemFunction036
extern "C" { BOOL WINAPI RtlGenRandom( OUT PVOID RandomBuffer, IN ULONG RandomBufferLength ); }
/*
* Forward declarations */ PSID GetRealSID( BSTR pTextSID); DWORD getUserName(PSID pUserSID, WCHAR **lpName, WCHAR **lpDomain);
HANDLE launchEx(PSID pUserSID, WTS_USER_SESSION_INFO *UserInfo, WCHAR *ConnectParms, WCHAR *HelpUrl, WCHAR *lpName, WCHAR *lpDomain, WCHAR *expertHelpBlob, WCHAR *userHelpBlob, SECURITY_ATTRIBUTES *sa, WCHAR *lpszMutex);
DWORD GetUserSessions(PSID pUserSID, PWTS_USER_SESSION_INFO *pUserTbl, DWORD *pEntryCnt, WCHAR *lpName, WCHAR *lpDomain); PSECURITY_DESCRIPTOR CreateSd(PSID pUserSID); BOOL SecurityCheck(PSID pUserSID); DWORD localKill(WTS_USER_SESSION_INFO *SessInfo, LPTHREAD_START_ROUTINE killThrd, LPSECURITY_ATTRIBUTES lpSA); LPTHREAD_START_ROUTINE getKillProc(void); BOOL ListFind(PSPLASHLIST pSplash, PSID user); BOOL ListInsert(PSPLASHLIST pSplash, PSID user); BOOL ListDelete(PSPLASHLIST pSplash, PSID user); BOOL GetPropertyValueFromBlob(BSTR bstrHelpBlob, WCHAR * pName, WCHAR** ppValue);
/************ things that should remain as defines ****************/ // Some environment variables used to communicate with scripts
#define ENV_USER L"USERNAME"
#define ENV_DOMAIN L"USERDOMAIN"
#define ENV_EVENT L"PCHEVENTNAME"
#define ENV_INDEX L"PCHSESSIONENUM"
#define ENV_PARMS L"PCHCONNECTPARMS"
#define EVENT_PREFIX L"Alex:PCH"
#define MODULE_NAME L"safrslv"
// I can't imagine a user having more logins than this on one server, but...
#define MAX_SESSIONS 30 // used to be 256
/************ our debug spew stuff ******************/ void DbgSpew(int DbgClass, BSTR lpFormat, va_list ap); void TrivialSpew(BSTR lpFormat, ...); void InterestingSpew(BSTR lpFormat, ...); void ImportantSpew(BSTR lpFormat, ...); void HeinousESpew(BSTR lpFormat, ...); void HeinousISpew(BSTR lpFormat, ...);
#define DBG_MSG_TRIVIAL 0x001
#define DBG_MSG_INTERESTING 0x002
#define DBG_MSG_IMPORTANT 0x003
#define DBG_MSG_HEINOUS 0x004
#define DBG_MSG_DEST_DBG 0x010
#define DBG_MSG_DEST_FILE 0x020
#define DBG_MSG_DEST_EVENT 0x040
#define DBG_MSG_TIME_MSGS 0x080
#define DBG_MSG_CLASS_ERROR 0x100
#define DBG_MSG_CLASS_SECURE 0x200
#define TRIVIAL_MSG(msg) TrivialSpew msg
#define INTERESTING_MSG(msg) InterestingSpew msg
#define IMPORTANT_MSG(msg) ImportantSpew msg
#define HEINOUS_E_MSG(msg) HeinousESpew msg
#define HEINOUS_I_MSG(msg) HeinousISpew msg
/* Strings for some error spewage. I waste the space since these friendly strings
* do make it into the Event Logs... */ WCHAR *lpszConnectState[] = { L"State_Active", L"State_Connected", L"State_ConnectQuery", L"State_Shadow", L"State_Disconnected", L"State_Idle", L"State_Listen", L"State_Reset", L"State_Down", L"State_Init" };
/*
* This global flag controls the amount of spew that we * produce. Legit values are as follows: * 1 = Trivial msgs displayed * 2 = Interesting msgs displayed * 3 = Important msgs displayed * 4 = only the most Heinous msgs displayed * The ctor actually sets this to 3 by default, but it can * be overridden by setting: * HKLM, Software/Microsoft/SAFSessionResolver, DebugSpew, DWORD */ int gDbgFlag = 0x1; int iDbgFileHandle = 0; long lSessionTag;
/////////////////////////////////////////////////////////////////////////////
// CSessionResolver Methods
/*************************************************************
* * NewResolveTSRDPSessionID(ConnectParms, userSID, *sessionID) * Returns the WTS SessionID for the one enabled and * ready to accept Remote Control. * * RETURN CODES: * WTS_Session_ID Connection accepted by user * RC_REFUSED Connection refused by user * RC_TIMEOUT User never responded * NONE_ACTIVE Found no active WTS sessions * API_FAILURE Something bad happened * *************************************************************/ STDMETHODIMP CSessionResolver::ResolveUserSessionID( /*[in]*/BSTR connectParms, /*[in]*/BSTR userSID, /*[in]*/ BSTR expertHelpBlob, /*[in]*/ BSTR userHelpBlob, /*[in]*/ ULONG_PTR hShutDown, /*[out, retval]*/long *sessionID, /*[in*/ DWORD dwPID, /*[out]*/ULONG_PTR *hHelpCtr ,/*[out, retval]*/int *userResponse ) { INTERESTING_MSG((L"CSessionResolver::ResolveUserSessionID"));
DWORD result; HANDLE hRdsAddin = NULL; HANDLE hReserveMutex = NULL; PSID pRealSID = NULL; WCHAR *pUsername=NULL, *pDomainname=NULL; PWTS_USER_SESSION_INFO pUserSessionInfo=NULL; PSECURITY_DESCRIPTOR pSD=NULL; HRESULT ret_code; int i; int TsIndex=-1; DWORD dwUserSessionCnt, dwSessionCnt; HANDLE pHandles[(MAX_SESSIONS*2)+2]; DWORD dwhIndex = 0; SECURITY_ATTRIBUTES sa; BOOL bAlreadyHelped, bRemoval=FALSE; WCHAR *pExpertId=NULL, *pUserId=NULL;
/* param validation */ if (!connectParms || !userSID || !sessionID || !hHelpCtr || !userResponse ) { IMPORTANT_MSG((L"Invalid params ConnectParms=0x%x, UserSID=0x%x, SessionID=0x%x", connectParms, userSID, sessionID)); ret_code = E_INVALIDARG; goto done; }
if( m_bCriticalError ) { IMPORTANT_MSG( (L"Possible low resources-InitCritSec failed" ) ); ret_code = E_FAIL; goto done; }
// set a default ret code
*userResponse = SAFERROR_INTERNALERROR;
if (dwPID) hRdsAddin = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);
if (hRdsAddin) { /*
* Make certain this is the correct process. If it is not * (rdsaddin.exe) then we bail out. */
DWORD dwRes; WCHAR szTmp[32];
dwRes = GetModuleBaseNameW(hRdsAddin, NULL, szTmp, ARRAYSIZE(szTmp)); if (!dwRes || StrCmpI(c_wszRdsAddin, szTmp)) { IMPORTANT_MSG((L"ERROR: the process handle for rdsaddin.exe has been recycled")); CloseHandle(hRdsAddin); hRdsAddin = 0; } }
if (!hRdsAddin) { // If we don't have a process handle, it is because
// the expert has already cancelled
ret_code = E_ACCESSDENIED; *userResponse = SAFERROR_CANTFORMLINKTOUSERSESSION; goto done; }
pRealSID = GetRealSID(userSID); if (!pRealSID) { IMPORTANT_MSG((L"GetRealSID failed"));
ret_code = E_ACCESSDENIED; *userResponse = SAFERROR_INVALIDPARAMETERSTRING; goto done; }
EnterCriticalSection(&m_CritSec); bAlreadyHelped = ListFind(m_pSplash, pRealSID); ListInsert(m_pSplash, pRealSID); // mark the SID for removal
bRemoval=TRUE; LeaveCriticalSection(&m_CritSec);
if (bAlreadyHelped) { INTERESTING_MSG((L"Helpee already has a ticket on the screen")); *sessionID = 0; *userResponse = SAFERROR_HELPEECONSIDERINGHELP; ret_code = E_ACCESSDENIED; goto done; }
TRIVIAL_MSG((L"userHelpBlob=[%s], expertHelpBlob=[%s]",userHelpBlob?userHelpBlob:L"NULL", expertHelpBlob?expertHelpBlob:L"NULL" )); /* check password: skip is UNSOLICITED=1 */ if (!GetPropertyValueFromBlob(userHelpBlob, L"UNSOLICITED", &pUserId) || !pUserId || *pUserId != L'1') { // Need to check password.
if (pUserId) { LocalFree(pUserId); pUserId = NULL; } if (GetPropertyValueFromBlob(userHelpBlob, L"PASS", &pUserId)) { if (!GetPropertyValueFromBlob(expertHelpBlob, L"PASS", &pExpertId) || wcscmp(pExpertId, pUserId) != 0) { IMPORTANT_MSG((L"Passwords don't match, x:%s, n:%s ", pExpertId, pUserId));
ret_code = E_ACCESSDENIED; *userResponse = SAFERROR_INVALIDPASSWORD; goto done; } } }
/* get the user's account strings */ if (!getUserName(pRealSID, &pUsername, &pDomainname)) { DWORD error = GetLastError(); HEINOUS_E_MSG((L"getUserName() failed, err=0x%x", error)); ret_code = E_ACCESSDENIED; *userResponse = SAFERROR_INVALIDPARAMETERSTRING; goto done; }
/*
* Get a list of all the active sessions on this WTS Server * For a specific user */ // keeps the compiler happy
dwSessionCnt = 0; result = GetUserSessions(pRealSID, &pUserSessionInfo, &dwUserSessionCnt, pUsername, pDomainname);
if (!result ) { IMPORTANT_MSG((L"GetUserSessions failed %08x", GetLastError())); ret_code = E_FAIL; goto done; }
/* If no sessions are found, then exit! */ if (dwUserSessionCnt == 0) { INTERESTING_MSG((L"no sessions found")); *sessionID = 0; *userResponse = SAFERROR_HELPEENOTFOUND; ret_code = E_ACCESSDENIED; goto done; } /* make certain we don't overflow our handle buffers */ else if (dwUserSessionCnt > MAX_SESSIONS) { HEINOUS_I_MSG((L"Found %d active sessions for %ws/%ws, limitting to %d", dwUserSessionCnt, pDomainname, pUsername, MAX_SESSIONS));
i = MAX_SESSIONS;
// free the extra WTS tokens
while (i < (int)dwSessionCnt) { if (pUserSessionInfo[i].hUserToken) CloseHandle(pUserSessionInfo[i].hUserToken); i++; } dwUserSessionCnt = MAX_SESSIONS; }
pSD = CreateSd(pRealSID); if (!pSD) { IMPORTANT_MSG((L"CreateSd failed err=%08x", GetLastError())); ret_code = E_ACCESSDENIED; goto done; }
sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = pSD; sa.bInheritHandle = FALSE;
memset(&pHandles[0], 0, sizeof(pHandles));
pHandles[0] = hRdsAddin;
lSessionTag = InterlockedIncrement(&m_lSessionTag);
WCHAR lpNameBfr[256];
wnsprintfW(lpNameBfr, ARRAYSIZE(lpNameBfr), L"Global\\AlexDont%lx", lSessionTag);
hReserveMutex = CreateMutex(&sa, FALSE, &lpNameBfr[0]); if (!hReserveMutex || ERROR_ALREADY_EXISTS == GetLastError()) { /*
* If we failed to create this event, it is most likely because one is already * in name-space, perhaps an event, or else a mutex... In any case, we will * try again, with a more random name. If that fails, then we must bail out. */ long lRand;
if (hReserveMutex) CloseHandle(hReserveMutex);
RtlGenRandom(&lRand, sizeof(lRand)); wnsprintfW(lpNameBfr, ARRAYSIZE(lpNameBfr), L"Global\\%lx%lx", lRand, lSessionTag); hReserveMutex = CreateMutex(&sa, FALSE, &lpNameBfr[0]);
if (!hReserveMutex || ERROR_ALREADY_EXISTS == GetLastError()) { if (hReserveMutex) CloseHandle(hReserveMutex); hReserveMutex = 0; HEINOUS_E_MSG((L"The named event \"%s\" was in use- potential security issue, so Remote Assistance will be denied.", lpNameBfr)); goto done; } }
/*
* Start up the HelpCtr in all the various TS sessions */
ret_code = S_OK;
for(i=0; i<(int)dwUserSessionCnt; i++) { TRIVIAL_MSG((L"calling launchEx[%d] for session %d", i, pUserSessionInfo[i].dwSessionId));
pUserSessionInfo[i].hProcess = launchEx(pRealSID, &pUserSessionInfo[i], connectParms, m_bstrResolveURL, pUsername, pDomainname, expertHelpBlob,userHelpBlob, &sa, lpNameBfr); if( pUserSessionInfo[i].hProcess == NULL || pUserSessionInfo[i].hEvent == NULL ) { ret_code = E_FAIL; break; } pHandles[i+1] = pUserSessionInfo[i].hProcess; pHandles[i+1+dwUserSessionCnt] = pUserSessionInfo[i].hEvent; }
//
// Last in list is our shutdown event
//
pHandles[dwUserSessionCnt*2+1] = (HANDLE)hShutDown;
if( ret_code == S_OK ) { /*
* Then wait for somebody to click on a "Yes", or a "No" */ // We use CoWaitForMultipleHandles otherwise, rdsaddin and sessmg
// deadlock since sessmgr might be apartment threaded
TRIVIAL_MSG((L"Waiting. m_iWaitDuration: %ld seconds", m_iWaitDuration/1000));
ret_code = CoWaitForMultipleHandles ( COWAIT_ALERTABLE, m_iWaitDuration, (dwUserSessionCnt*2)+2, pHandles, &dwhIndex ); // try to take the mutex (in case somebody asks for it later...
CoWaitForMultipleHandles (COWAIT_ALERTABLE, 0, 1, &hReserveMutex, &result); }
if( ret_code == S_OK ) {
/*
* Then wait for somebody to click on a "Yes", or a "No" */ // We use CoWaitForMultipleHandles otherwise, rdsaddin and sessmg
// deadlock since sessmgr might be apartment threaded
TRIVIAL_MSG((L"Waiting. m_iWaitDuration: %ld seconds", m_iWaitDuration/1000));
ret_code = CoWaitForMultipleHandles ( COWAIT_ALERTABLE, m_iWaitDuration, (dwUserSessionCnt*2)+1, pHandles, &dwhIndex ); // try to take the mutex (in case somebody asks for it later...
CoWaitForMultipleHandles (COWAIT_ALERTABLE, 0, 1, &hReserveMutex, &result); }
if (S_OK == ret_code) { if(dwhIndex == dwUserSessionCnt*2+1) { // shutdown event has signaled.
TsIndex = -1; ret_code = E_ACCESSDENIED; *userResponse = SAFERROR_CANTFORMLINKTOUSERSESSION; } else if (dwhIndex > dwUserSessionCnt) { /* somebody said "yes" */ long l_iWaitDuration = m_iWaitDuration/2;
TsIndex = dwhIndex-dwUserSessionCnt-1; TRIVIAL_MSG((L"User responded YES for session 0x%x, waiting for Salem setup", TsIndex));
/*
* Then we wait for the HelpCtr scripts to establish the Salem connection */ ResetEvent(pHandles[dwhIndex]); ret_code = CoWaitForMultipleHandles (COWAIT_ALERTABLE, l_iWaitDuration, 1, &pHandles[dwhIndex], &result);
if (ret_code != S_OK || result != WAIT_OBJECT_0) { IMPORTANT_MSG((L"Bad script- it didn't signal acknowledgement within %d seconds", l_iWaitDuration/1000)); bRemoval = TRUE; ret_code = E_ACCESSDENIED; *userResponse = SAFERROR_INTERNALERROR; TsIndex = -1; } else { TRIVIAL_MSG((L"HelpCtr set up Salem in time."));
*userResponse = SAFERROR_NOERROR; // mark the SID for non-removal
bRemoval=FALSE;
*hHelpCtr = NULL; // start with NULL
// return the hProc of the HelpCenter instance that wants to start RA
DuplicateHandle(GetCurrentProcess(), pUserSessionInfo[TsIndex].hProcess, hRdsAddin, (HANDLE *)hHelpCtr, SYNCHRONIZE, FALSE, 0); } } else if (dwhIndex == 0) { // we got here because the expert bailed out, or we lost the connection
INTERESTING_MSG((L"Expert killed RdsAddin")); TsIndex = -1; ret_code = E_ACCESSDENIED; *userResponse = SAFERROR_CANTFORMLINKTOUSERSESSION; } else { /*
* We get here because the novice "killed" a HelpCtr session * or else the novice just said "NO" */ INTERESTING_MSG((L"User killed session or clicked NO for session 0x%x", dwhIndex-1)); /* this keeps us from trying to kill something the user has already closed */ TsIndex = dwhIndex-1; ret_code = E_ACCESSDENIED; *userResponse = SAFERROR_HELPEESAIDNO; } } else if (RPC_S_CALLPENDING == ret_code) { TRIVIAL_MSG((L"User response timed out after %d seconds", m_iWaitDuration/1000));
TsIndex = -1; ret_code = E_PENDING; *userResponse = SAFERROR_HELPEENEVERRESPONDED; } else { IMPORTANT_MSG((L"WaitForObject failed %08x err=%08x", result, GetLastError())); TsIndex = -1; ret_code = E_FAIL; }
/*
* Then close all the windows (except the one in TsIndex) */ for(i=0; i<(int)dwUserSessionCnt; i++) { LPTHREAD_START_ROUTINE lpKill = getKillProc();
if (pUserSessionInfo[i].dwIndex != TsIndex && pUserSessionInfo[i].hProcess) { /* This has to be done for each instance, since we call into the process
* to kill itself. If we did not get "lpKill" for each seperate occurance * of HelpCtr, then Very Bad Things could happen... */ TRIVIAL_MSG((L"Killing HelpCtr in process %d", pUserSessionInfo[i].hProcess)); localKill(&pUserSessionInfo[i], lpKill, &sa); } }
if (ret_code == S_OK) *sessionID = (long) pUserSessionInfo[TsIndex].dwSessionId; done: if (bRemoval) { // remove the SID from the list
EnterCriticalSection(&m_CritSec); ListDelete(m_pSplash, pRealSID); LeaveCriticalSection(&m_CritSec); }
if (hReserveMutex) CloseHandle(hReserveMutex); if (hRdsAddin) CloseHandle(hRdsAddin);
if (pRealSID) LocalFree(pRealSID);
if (pUserSessionInfo) {
/* close all the handles */ for(i=0; i<(int)dwUserSessionCnt; i++) { if (pUserSessionInfo[i].hProcess) CloseHandle(pUserSessionInfo[i].hProcess);
if (pUserSessionInfo[i].hUserToken) CloseHandle(pUserSessionInfo[i].hUserToken);
if (pUserSessionInfo[i].hEvent) CloseHandle(pUserSessionInfo[i].hEvent); } LocalFree(pUserSessionInfo); }
if (pUsername) LocalFree(pUsername);
if (pDomainname) LocalFree(pDomainname);
if (pSD) LocalFree(pSD);
if (pUserId) LocalFree(pUserId); if (pExpertId) LocalFree(pExpertId);
INTERESTING_MSG((L"CSessionResolver::ResolveUserSessionID returns %x\n", ret_code )); return ret_code; }
/*************************************************************
* * OnDisconnect([in] BSTR connectParms, [in] BSTR userSID, [in] long sessionID) * Notifies us when an RA session ends * * NOTES: * This is called so we can maintain the state of our user prompts * * WARNING: ACHTUNG: ATTENZIONE: * This method must do a minimal amount of work before returning * and must NEVER do anything that would cause COM to pump * messages. To do so would screw Salem immensely.a * * RETURN CODES: * NONE_ACTIVE Found no active WTS sessions * API_FAILURE Something bad happened * *************************************************************/
STDMETHODIMP CSessionResolver::OnDisconnect( /*[in]*/BSTR connectParms, /*[in]*/BSTR userSID, /*[in]*/long sessionID ) { PSID pRealSID; if (!connectParms || !userSID) { HEINOUS_I_MSG((L"Invalid params in OnDisconnect- ConnectParms=0x%x, UserSID=0x%x", connectParms, userSID)); return E_INVALIDARG; }
INTERESTING_MSG((L"CSessionResolver::OnDisconnect-(%ws)", userSID));
pRealSID = GetRealSID(userSID); if (pRealSID) { EnterCriticalSection(&m_CritSec); ListDelete(m_pSplash, pRealSID); LeaveCriticalSection(&m_CritSec); LocalFree(pRealSID); }
INTERESTING_MSG((L"CSessionResolver::OnDisconnect; leaving")); return S_OK; }
/*************************************************************
* * GetRealSID([in] BSTR pTextSID) * Converts a string-based SID into a REAL usable SID * * NOTES: * This is a stub into "ConvertStringSidToSid". * * RETURN CODES: * NULL Failed for some reason * PSID Pointer to a real SID. Must be * freed with "LocalFree" * *************************************************************/ PSID GetRealSID( BSTR pTextSID) { PSID pRetSID = NULL;
if (!ConvertStringSidToSidW(pTextSID, &pRetSID)) IMPORTANT_MSG((L"ConvertStringSidToSidW(%ws) failed %08x\n", pTextSID, GetLastError()));
return pRetSID; }
/*************************************************************
* * launchEx(PSID, WTS_USER_SESSION_INFO, char * ConnectParms, char * EventName) * * * RETURN CODES: * 0 Failed to start process * <> HANDLE to started process * *************************************************************/
HANDLE launchEx(PSID pUserSID, WTS_USER_SESSION_INFO *UserInfo, WCHAR *ConnectParms, WCHAR *HelpPageURL, WCHAR *pUsername, WCHAR *pDomainname, WCHAR *expertHelpBlob, WCHAR *userHelpBlob, SECURITY_ATTRIBUTES *sa, WCHAR *lpszMutex ) { BOOL result=FALSE; HANDLE retval = 0; STARTUPINFOW StartUp; PROCESS_INFORMATION p_i; WCHAR buf1[BUF_SZ], buf2[BUF_SZ], *lpUtf8ConnectParms=NULL; static WCHAR *szEnvUser = ENV_USER; static WCHAR *szEnvDomain = ENV_DOMAIN; static WCHAR *szEnvEvent = ENV_EVENT; static WCHAR *szEnvIndex = ENV_INDEX; static WCHAR *szEnvParms = ENV_PARMS; static WCHAR *szEnvExpertBlob = L"PCHEXPERTBLOB"; static WCHAR *szEnvUserBlob = L"PCHUSERBLOB"; static WCHAR *szEnvMutex = L"PCHMutexName";
VOID *pEnvBlock = NULL; DWORD dwUsername=0, dwDomainname=0, dwStrSz; WCHAR *wszExe = NULL;
dwStrSz = 3 + GetSystemWindowsDirectory(NULL, 0); dwStrSz += wcslen(c_wszHelpCtr); dwStrSz += wcslen(c_wszCmdLine); dwStrSz += wcslen(HelpPageURL);
__try { wszExe = (LPWSTR)_alloca(dwStrSz * sizeof(*wszExe)); } __except(EXCEPTION_STACK_OVERFLOW) { wszExe = NULL; } if (wszExe == NULL)
{ SetLastError(ERROR_OUTOFMEMORY); goto done; }
ZeroMemory(wszExe, dwStrSz * sizeof(*wszExe)); wszExe[0] = L'\"'; GetSystemWindowsDirectory(wszExe+1, dwStrSz-1); wcscat(wszExe, c_wszHelpCtr); wcscat(wszExe, c_wszCmdLine); wcscat(wszExe, HelpPageURL);
/*
* Here, we must start up a Help Center script in a WTS Session * It gets a bit sticky, though as we do not have access to the * user's desktop (any desktop), and this must appear on only * one particular desktop. I am relying on the WTS-User-Token * to get the desktop for me. * * The main component is our call to CreateProcessAsUser. * Before we call that we must: * Set up Environment as follows: * PATH=%SystemPath% * WINDIR=SystemRoot% * USERNAME=(from WTS) * USERDOMAIN= * PCHEVENTNAME=EventName * PCHSESSIONENUM=UserInfo->dwIndex * PCHCONNECTPARMS=ConnectParms */ TRIVIAL_MSG((L"Launch %ws", wszExe));
/* Step on the ENVIRONMENT */ WCHAR lpNameBfr[256];
wnsprintfW(lpNameBfr, ARRAYSIZE(lpNameBfr), L"Global\\%ws%lx_%02d", EVENT_PREFIX, lSessionTag, UserInfo->dwIndex);
UserInfo->hEvent = CreateEvent(sa, TRUE, FALSE, lpNameBfr); if (!UserInfo->hEvent || ERROR_ALREADY_EXISTS == GetLastError()) { /*
* If we failed to create this event, it is most likely because one is already * in name-space, perhaps an event, or else a mutex... In any case, we will * try again, with a more random name. If that fails, then we must bail out. */ long lRand;
if (UserInfo->hEvent) CloseHandle(UserInfo->hEvent);
RtlGenRandom(&lRand, sizeof(lRand)); wnsprintfW(lpNameBfr, ARRAYSIZE(lpNameBfr), L"Global\\%lx%lx%lx_%02d", lRand, lSessionTag, UserInfo->dwIndex); UserInfo->hEvent = CreateEvent(sa, TRUE, FALSE, lpNameBfr);
if (!UserInfo->hEvent || ERROR_ALREADY_EXISTS == GetLastError()) { if (UserInfo->hEvent) CloseHandle(UserInfo->hEvent); UserInfo->hEvent = 0; HEINOUS_E_MSG((L"The named event \"%s\" was in use- potential security issue, so Remote Assistance will be denied.", lpNameBfr)); goto done; } }
SetEnvironmentVariable(szEnvEvent, lpNameBfr);
wsprintf(buf1, L"%d", UserInfo->dwIndex); SetEnvironmentVariable(szEnvIndex, buf1); SetEnvironmentVariable(szEnvParms, ConnectParms); SetEnvironmentVariable(szEnvMutex, lpszMutex); SetEnvironmentVariable(szEnvUser, pUsername ); SetEnvironmentVariable(szEnvDomain, pDomainname ); SetEnvironmentVariable(szEnvExpertBlob, expertHelpBlob); SetEnvironmentVariable(szEnvUserBlob, userHelpBlob);
if (!CreateEnvironmentBlock(&pEnvBlock, UserInfo->hUserToken, TRUE)) { IMPORTANT_MSG((L"CreateEnvironmentBlock failed in resolver:launchex, err=0x%x", GetLastError())); goto done; }
// initialize our structs
ZeroMemory(&p_i, sizeof(p_i)); ZeroMemory(&StartUp, sizeof(StartUp)); StartUp.cb = sizeof(StartUp); StartUp.dwFlags = STARTF_USESHOWWINDOW; StartUp.wShowWindow = SW_SHOWNORMAL;
result = CreateProcessAsUserW(UserInfo->hUserToken, NULL, wszExe, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS + CREATE_UNICODE_ENVIRONMENT , pEnvBlock, // Environment block (must use the CREATE_UNICODE_ENVIRONMENT flag)
NULL, &StartUp, &p_i);
if (result) { // keep a leak from happening, as we never need the hThread...
CloseHandle(p_i.hThread); UserInfo->hProcess = p_i.hProcess; UserInfo->hThread = 0; UserInfo->dwProcessId = p_i.dwProcessId; UserInfo->dwThreadId = p_i.dwThreadId;
retval = p_i.hProcess;
TRIVIAL_MSG((L"CreateProcessAsUserW started up [%ws]", wszExe)); } else { IMPORTANT_MSG((L"CreateProcessAsUserW failed, err=0x%x command line=[%ws]", GetLastError(), wszExe)); result=0; } done: if (!result) { UserInfo->hProcess = 0; UserInfo->hThread = 0; UserInfo->dwProcessId = 0; UserInfo->dwThreadId = 0; if( UserInfo->hEvent != NULL ) { CloseHandle( UserInfo->hEvent ); UserInfo->hEvent = 0; } }
// restore any memory we borrowed
if (pEnvBlock) DestroyEnvironmentBlock(pEnvBlock);
return retval; }
/*************************************************************
* * CreateSids([in] BSTR pTextSID) * Create 3 Security IDs * * NOTES: * Caller must free memory allocated to SIDs on success. * * RETURN CODES: TRUE if successfull, FALSE if not. * *************************************************************/
BOOL CreateSids( PSID *BuiltInAdministrators, PSID *PowerUsers, PSID *AuthenticatedUsers ) { //
// An SID is built from an Identifier Authority and a set of Relative IDs
// (RIDs). The Authority of interest to us SECURITY_NT_AUTHORITY.
//
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
//
// Each RID represents a sub-unit of the authority. Two of the SIDs we
// want to build, Local Administrators, and Power Users, are in the "built
// in" domain. The other SID, for Authenticated users, is based directly
// off of the authority.
//
// For examples of other useful SIDs consult the list in
// \nt\public\sdk\inc\ntseapi.h.
//
if (!AllocateAndInitializeSid(&NtAuthority, 2, // 2 sub-authorities
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0,0,0,0,0,0, BuiltInAdministrators)) { // error
HEINOUS_E_MSG((L"Could not allocate security credentials for admins.")); } else if (!AllocateAndInitializeSid(&NtAuthority, 2, // 2 sub-authorities
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0,0,0,0,0,0, PowerUsers)) { // error
HEINOUS_E_MSG((L"Could not allocate security credentials for power users.")); FreeSid(*BuiltInAdministrators); *BuiltInAdministrators = NULL;
} else if (!AllocateAndInitializeSid(&NtAuthority, 1, // 1 sub-authority
SECURITY_AUTHENTICATED_USER_RID, 0,0,0,0,0,0,0, AuthenticatedUsers)) { // error
HEINOUS_E_MSG((L"Could not allocate security credentials for users."));
FreeSid(*BuiltInAdministrators); *BuiltInAdministrators = NULL;
FreeSid(*PowerUsers); *PowerUsers = NULL;
} else { return TRUE; }
return FALSE; }
/*************************************************************
* * CreateSd(void) * Creates a SECURITY_DESCRIPTOR with specific DACLs. * * NOTES: * Caller must free the returned buffer if not NULL. * * RETURN CODES: * NULL Failed for some reason * PSECURITY_DESCRIPTOR Pointer to a SECURITY_DESCRIPTOR. * Must be freed with "LocalFree" * *************************************************************/ PSECURITY_DESCRIPTOR CreateSd(PSID pUserSID) { PSID AuthenticatedUsers; PSID BuiltInAdministrators; PSID PowerUsers; PSECURITY_DESCRIPTOR Sd = NULL; ULONG AclSize; ACL *Acl;
if (!CreateSids(&BuiltInAdministrators, &PowerUsers, &AuthenticatedUsers)) { // error
IMPORTANT_MSG((L"CreateSids failed"));
return NULL; }
//
// Calculate the size of and allocate a buffer for the DACL, we need
// this value independently of the total alloc size for ACL init.
//
//
// "- sizeof (ULONG)" represents the SidStart field of the
// ACCESS_ALLOWED_ACE. Since we're adding the entire length of the
// SID, this field is counted twice.
//
AclSize = sizeof (ACL) + (4 * (sizeof (ACCESS_ALLOWED_ACE) - sizeof (ULONG))) + GetLengthSid(AuthenticatedUsers) + GetLengthSid(BuiltInAdministrators) + GetLengthSid(PowerUsers) + GetLengthSid(pUserSID);
Sd = LocalAlloc(LMEM_FIXED + LMEM_ZEROINIT, SECURITY_DESCRIPTOR_MIN_LENGTH + AclSize);
if (!Sd) { IMPORTANT_MSG((L"Cound not allocate 0x%x bytes for Security Descriptor", SECURITY_DESCRIPTOR_MIN_LENGTH + AclSize)); goto error; }
Acl = (ACL *)((BYTE *)Sd + SECURITY_DESCRIPTOR_MIN_LENGTH);
if (!InitializeAcl(Acl, AclSize, ACL_REVISION)) { // error
IMPORTANT_MSG((L"Cound not initialize ACL err=0x%x", GetLastError())); goto error; } TRIVIAL_MSG((L"initialized Successfully"));
if (!AddAccessAllowedAce(Acl, ACL_REVISION, STANDARD_RIGHTS_ALL | GENERIC_WRITE, pUserSID)) { // Failed to build the ACE granted to OWNER
// (STANDARD_RIGHTS_ALL) access.
IMPORTANT_MSG((L"Cound not add owner rights to ACL err=0x%x", GetLastError())); goto error; }
if (!AddAccessAllowedAce(Acl, ACL_REVISION, GENERIC_READ, AuthenticatedUsers)) { // Failed to build the ACE granting "Authenticated users"
// (SYNCHRONIZE | GENERIC_READ) access.
IMPORTANT_MSG((L"Cound not add user rights to ACL err=0x%x", GetLastError())); goto error; }
if (!AddAccessAllowedAce(Acl, ACL_REVISION, GENERIC_READ | GENERIC_WRITE, PowerUsers)) { // Failed to build the ACE granting "Power users"
// (SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE) access.
IMPORTANT_MSG((L"Cound not add power user rights to ACL err=0x%x", GetLastError())); goto error; }
if (!AddAccessAllowedAce(Acl, ACL_REVISION, STANDARD_RIGHTS_ALL, BuiltInAdministrators)) { // Failed to build the ACE granting "Built-in Administrators"
// STANDARD_RIGHTS_ALL access.
IMPORTANT_MSG((L"Cound not add admin rights to ACL err=0x%x", GetLastError())); goto error; }
if (!InitializeSecurityDescriptor(Sd,SECURITY_DESCRIPTOR_REVISION)) { // error
IMPORTANT_MSG((L"Cound not initialize SD err=0x%x", GetLastError())); goto error; }
if (!SetSecurityDescriptorDacl(Sd, TRUE, Acl, FALSE)) { // error
IMPORTANT_MSG((L"SetSecurityDescriptorDacl failed err=0x%x", GetLastError())); goto error; }
FreeSid(AuthenticatedUsers); FreeSid(BuiltInAdministrators); FreeSid(PowerUsers);
// TRIVIAL_MSG((L"CreateSd succeeded."));
return Sd;
error: /* A jump of last resort */ if (Sd) LocalFree(Sd);
// error
if (AuthenticatedUsers) FreeSid(AuthenticatedUsers); if (BuiltInAdministrators) FreeSid(BuiltInAdministrators); if (PowerUsers) FreeSid(PowerUsers); return NULL; }
/*************************************************************
* * GetUserSessions(PSID, PWTS_USER_SESSION_INFOW , *DWORD) * Returns a table of all the active WTS Sessions for this * user, on this server. * * RETURN CODES: * * NOTES: * Should log NT Events for failures * *************************************************************/ DWORD GetUserSessions(PSID pUserSID, PWTS_USER_SESSION_INFO *pUserTbl, DWORD *pEntryCnt, WCHAR *pUsername, WCHAR *pDomainname) { int i,ii=0; DWORD retval=0, dwSessions=0, dwValidSessions, dwOwnedSessions; PWTS_SESSION_INFO pSessionInfo=NULL; DWORD dwRetSize = 0; WINSTATIONINFORMATION WSInfo;
/* param validation */ if (!pUserSID || !pUserTbl || !pEntryCnt) { IMPORTANT_MSG((L"GetUserSessions parameter violation"));
return 0; }
/* Initialize */
*pUserTbl = NULL;
/* Start with WTSEnumerateSessions,
* narrow it down to only active sessions, * then filter further against the logonID * then use WinStationQueryInformation to get the logged on users token */ if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &dwSessions) || !pSessionInfo || !dwSessions) { /* if we can't do this, then we must give up */ IMPORTANT_MSG((L"GetUserSessions parameter violation")); return 0; }
/*
* Narrow down to only active sessions - * a quick test */ for (i=0,dwValidSessions=0;i<(int)dwSessions;i++) { if (pSessionInfo[i].State == WTSActive) dwValidSessions++; }
/* bail out early if none found */ if (dwValidSessions == 0) { /* free the memory we asked for */ WTSFreeMemory(pSessionInfo);
INTERESTING_MSG((L"No active sessions found")); return 0; }
INTERESTING_MSG((L"%d sessions found", dwValidSessions));
/*
* Now see who owns each session. Only one problem- session details kept * by WTS have no concept of SIDs- just a user name & domain. Oh well, * it is a safe assumption that the two are just as reliable forms of * identification. One little caveat- the names can be stored using * different case variants. Example: "TomFr" and "tomfr" are equivalent, * according to NT domain rules, so I have to use a case-insensitive * check here. * * So, now that I know there is at least one possible WTS Session here, * I will compare these IDs to determine if we want to notify this particular * session. */
/* now look at all the active sessions and compare domain & user names */ for (i=0,dwOwnedSessions=0;i<(int)dwSessions;i++) { if (pSessionInfo[i].State == WTSActive) { DWORD CurSessionId = pSessionInfo[i].SessionId; BOOL bLockedState=FALSE;
memset( &WSInfo, 0, sizeof(WSInfo) );
if (!WinStationQueryInformationW( SERVERNAME_CURRENT, CurSessionId, WinStationInformation, &WSInfo, sizeof(WSInfo), &dwRetSize )) { IMPORTANT_MSG((L"WinStationQueryInformation failed err=0x%x", GetLastError()));
// SECURITY : if bad user's list is infront of good novice and
// bad user just happen to logoff at the right time, it will
// cause code to break out of loop instead of looking further
// for the intended novice.
continue; }
if (StrCmpI(WSInfo.Domain, pDomainname)) continue;
if (StrCmpI(WSInfo.UserName, pUsername)) continue;
if (!WinStationQueryInformationW( SERVERNAME_CURRENT, CurSessionId, WinStationLockedState, &bLockedState, sizeof(bLockedState), &dwRetSize )) { IMPORTANT_MSG((L"WinStationQueryInformation failed err=0x%x", GetLastError()));
break; } // if the screen is locked, don't add to the list
if (bLockedState) continue;
INTERESTING_MSG((L"WinStaQI[%d]: ConnectState=0x%x bLockedState=0x%x user=%s/%s ", i,WSInfo.ConnectState, bLockedState, WSInfo.Domain,WSInfo.UserName));
/*
* If we got this far, then we know we want this session */ dwOwnedSessions++;
// mark this as a "session of interest"
pSessionInfo[i].State = (WTS_CONNECTSTATE_CLASS)0x45; } }
/* did we find any sessions? */ if (dwOwnedSessions == 0) { TRIVIAL_MSG((L"No matching sessions found")); goto none_found; }
/* Get some memory for our session tables */ *pUserTbl = (PWTS_USER_SESSION_INFO)LocalAlloc(LMEM_FIXED, dwOwnedSessions * sizeof(WTS_USER_SESSION_INFO));
if (!*pUserTbl) { HEINOUS_E_MSG((L"Could not allocate memory for %d sessions, err=0x%x", dwOwnedSessions, GetLastError())); goto none_found; }
*pEntryCnt = dwOwnedSessions;
for (i=0,ii=0;i<(int)dwSessions; i++) { /*
* If this is one of our "sessions of interest", get the session ID * and User Token. */ if (pSessionInfo[i].State == (WTS_CONNECTSTATE_CLASS)0x45) { WINSTATIONUSERTOKEN WsUserToken; ULONG ulRet; PWTS_USER_SESSION_INFO lpi = &((*pUserTbl)[ii]);
WsUserToken.ProcessId = LongToHandle(GetCurrentProcessId()); WsUserToken.ThreadId = LongToHandle(GetCurrentThreadId());
lpi->dwIndex = (DWORD)ii; lpi->dwSessionId = pSessionInfo[i].SessionId;
if (!WinStationQueryInformationW(WTS_CURRENT_SERVER_HANDLE, pSessionInfo[i].SessionId, WinStationUserToken, &WsUserToken, sizeof(WsUserToken), &ulRet)) { goto none_found; }
lpi->hUserToken = WsUserToken.UserToken; ii++; } }
/* Yahoo! we finally built our table. Now we can leave after cleaning up */ WTSFreeMemory(pSessionInfo);
IMPORTANT_MSG((L"GetUserSessions exiting: %d sessions found", *pEntryCnt));
return 1;
none_found: /* free the memory we asked for */ if (pSessionInfo) WTSFreeMemory(pSessionInfo);
// we found no entries
*pEntryCnt =0;
if( *pUserTbl != NULL ) { LocalFree( *pUserTbl ); *pUserTbl = NULL; }
IMPORTANT_MSG((L"GetUserSessions exiting: no sessions found")); return 1; }
LPTHREAD_START_ROUTINE getKillProc(void) { LPTHREAD_START_ROUTINE lpKill = NULL;
HMODULE hKernel = LoadLibrary(L"kernel32"); if (hKernel) { lpKill = (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel, "ExitProcess"); FreeLibrary(hKernel); }
return lpKill; }
/*************************************************************
* * localKill(WTS_USER_SESSION_INFO *SessInfo, LPTHREAD_START_ROUTINE lpKill) * kills the process for us. * *************************************************************/ DWORD localKill(WTS_USER_SESSION_INFO *SessInfo, LPTHREAD_START_ROUTINE lpKill, LPSECURITY_ATTRIBUTES lpSA) { TRIVIAL_MSG((L"Entered localKill(0x%x, 0x%x)", SessInfo, lpKill));
if (lpKill) { HANDLE hKillThrd= CreateRemoteThread( SessInfo->hProcess, lpSA, NULL, lpKill, 0, 0, NULL); if (hKillThrd) { CloseHandle(hKillThrd); return 1; } IMPORTANT_MSG((L"CreateRemoteThread failed. Err: %08x", GetLastError())); // the fall-through is by design...
}
if(!TerminateProcess( SessInfo->hProcess, 0 )) { IMPORTANT_MSG((L"TerminateProcess failed. Err: %08x", GetLastError())); }
return 0; }
/*************************************************************
* * getUserName() * get the user's name & domain. * *************************************************************/ DWORD getUserName(PSID pUserSID, WCHAR **pUsername, WCHAR **pDomainname) { if (!pUsername || !pDomainname || !pUserSID) return 0;
DWORD dwUsername=0, dwDomainname=0; SID_NAME_USE eUse;
TRIVIAL_MSG((L"Entered getUserName"));
*pUsername=NULL, *pDomainname=NULL;
/* get the size of the buffers we will need */ if (!LookupAccountSid(NULL, pUserSID, NULL, &dwUsername, NULL, &dwDomainname, &eUse) && (!dwUsername || !dwDomainname)) { IMPORTANT_MSG((L"LookupAccountSid(nulls) failed err=0x%x", GetLastError())); return 0; }
/* now get enough memory for our name & domain strings */ *pUsername = (WCHAR *)LocalAlloc(LMEM_FIXED, (dwUsername+16) * sizeof(WCHAR)); *pDomainname = (WCHAR *)LocalAlloc(LMEM_FIXED, (dwDomainname+16) * sizeof(WCHAR));
if (!*pUsername || !*pDomainname || !LookupAccountSid(NULL, pUserSID, *pUsername, &dwUsername, *pDomainname, &dwDomainname, &eUse)) { /* if we get here, it is very bad!! */ DWORD error = GetLastError(); if (!*pUsername || !*pDomainname) IMPORTANT_MSG((L"LocalAlloc failed err=0x%x", error)); else IMPORTANT_MSG((L"LookupAccountSid(ptrs) failed err=0x%x", error));
return 0; }
TRIVIAL_MSG((L"Requested user=[%ws] dom=[%ws]", *pUsername, *pDomainname)); return 1; }
/*************************************************************
* * ListFind(PSPLASHLIST pSplash, PSID user) * returns TRUE if SID is found in our "splash table" * *************************************************************/ BOOL ListFind(PSPLASHLIST pSplash, PSID user) { PSPLASHLIST lpSplash;
if (!pSplash) { IMPORTANT_MSG((L"ListFind: no splash table memory")); return FALSE; }
if (!user) { IMPORTANT_MSG((L"Fatal error: no SID pointer")); return FALSE; }
lpSplash = pSplash;
while (lpSplash) { if (lpSplash->refcount && EqualSid(user, &lpSplash->Sid)) { return TRUE; } lpSplash = (PSPLASHLIST) lpSplash->next; }
return FALSE; }
/*************************************************************
* * ListInsert(PSPLASHLIST pSplash, int iSplashCnt, PSID user) * inserts SID in our "splash table" * * NOTES: * if already there, we increment the refcount for SID * *************************************************************/ BOOL ListInsert(PSPLASHLIST pSplash, PSID user) { PSPLASHLIST lpSplash;
if (!pSplash) { IMPORTANT_MSG((L"Fatal error: no splash table memory")); return FALSE; }
if (!user) { IMPORTANT_MSG((L"Fatal error: no SID pointer")); return FALSE; }
if (!IsValidSid(user)) { IMPORTANT_MSG((L"Fatal error: bad SID")); return FALSE; }
// first, look for one already in the list
lpSplash = pSplash;
while (lpSplash) { if (lpSplash->refcount && EqualSid(user, &lpSplash->Sid)) { TRIVIAL_MSG((L"Recycling SID in ListInsert")); lpSplash->refcount++; return TRUE; } lpSplash = (PSPLASHLIST) lpSplash->next; }
// since we did not find one, let's add a new one
lpSplash = pSplash;
while (lpSplash->next) lpSplash = (PSPLASHLIST) lpSplash->next;
int iSidSz = GetLengthSid(user);
lpSplash->next = LocalAlloc(LMEM_FIXED, sizeof(SPLASHLIST) + iSidSz - sizeof(SID));
if (lpSplash->next) { lpSplash = (PSPLASHLIST) lpSplash->next;
CopySid(iSidSz, &lpSplash->Sid, user); lpSplash->refcount=1; lpSplash->next = NULL; return TRUE; } else { IMPORTANT_MSG((L"Fatal error: no memory available to extend splash tables")); }
return FALSE; }
/*************************************************************
* * ListDelete(PSPLASHLIST pSplash, PSID user) * deletes SID from our "splash table" * * NOTES: * if there, we decrement the refcount for SID * when refcount hits zero, SID is removed * *************************************************************/ BOOL ListDelete(PSPLASHLIST pSplash, PSID user) { PSPLASHLIST lpSplash, lpLast;
if (!pSplash) { IMPORTANT_MSG((L"Fatal error: no splash table memory")); return FALSE; }
if (!user) { IMPORTANT_MSG((L"Fatal error: no SID pointer")); return FALSE; }
lpSplash = lpLast = pSplash;
while (lpSplash) { if (lpSplash->refcount && EqualSid(user, &lpSplash->Sid)) { lpSplash->refcount--; TRIVIAL_MSG((L"found SID in ListDelete"));
if (!lpSplash->refcount) { // blow away the SID data
TRIVIAL_MSG((L"deleting SID entry")); lpLast->next = lpSplash->next; LocalFree(lpSplash); } return TRUE; } lpLast = lpSplash; lpSplash = (PSPLASHLIST) lpSplash->next; }
IMPORTANT_MSG((L"Fatal error: attempted to delete non-existant SID from splash table memory")); return FALSE; }
/*************************************************************
* * DbgSpew(DbgClass, char *, ...) * Sends debug information. * *************************************************************/ void DbgSpew(int DbgClass, BSTR lpFormat, va_list ap) { WCHAR szMessage[2400+3]; // extra space for '\r', '\n' and NULL
DWORD bufSize = sizeof(szMessage)/sizeof(szMessage[0]) - 3; int iLen=0; DWORD dwTick; int seconds, millisec;
memset( szMessage, 0, sizeof(szMessage) );
if (gDbgFlag & DBG_MSG_TIME_MSGS) { dwTick = GetTickCount(); seconds = (int) dwTick / 1000; millisec = (int) dwTick % 1000;
_snwprintf( szMessage, bufSize, L"%06d.%03d- ", seconds, millisec); iLen = lstrlen(szMessage); }
if ((DbgClass & 0x0F) >= (gDbgFlag & 0x0F)) { // bufSize does not include three extra characters for L"\r\n\0"
if( (int)bufSize > iLen ) { _vsnwprintf(szMessage+iLen, bufSize - iLen, lpFormat, ap); }
wcscat(szMessage, L"\r\n");
// should this be sent to the debugger?
if (DbgClass & DBG_MSG_DEST_DBG) OutputDebugStringW(szMessage);
// should this go to our log file?
if (iDbgFileHandle) _write(iDbgFileHandle, szMessage, (2*lstrlen(szMessage))); }
// should this msg go to the Event Logs?
if (DbgClass & DBG_MSG_DEST_EVENT) { WORD wType; DWORD dwEvent;
if (DbgClass & DBG_MSG_CLASS_SECURE) { if (DbgClass & DBG_MSG_CLASS_ERROR) { wType = EVENTLOG_AUDIT_FAILURE; dwEvent = SESSRSLR_E_SECURE; } else { wType = EVENTLOG_AUDIT_SUCCESS; dwEvent = SESSRSLR_I_SECURE; } } else { if (DbgClass & DBG_MSG_CLASS_ERROR) { wType = EVENTLOG_ERROR_TYPE; dwEvent = SESSRSLR_E_GENERAL; } else { wType = EVENTLOG_INFORMATION_TYPE; dwEvent = SESSRSLR_I_GENERAL; } }
/* write out an NT Event */ HANDLE hEvent = RegisterEventSource(NULL, MODULE_NAME); LPCWSTR ArgsArray[1]={szMessage};
if (hEvent) { ReportEvent(hEvent, wType, 0, dwEvent, NULL, 1, 0, ArgsArray, NULL);
DeregisterEventSource(hEvent); } } }
void TrivialSpew(BSTR lpFormat, ...) { va_list vd; va_start(vd, lpFormat); DbgSpew(DBG_MSG_TRIVIAL+DBG_MSG_DEST_DBG, lpFormat, vd); va_end(vd); }
void InterestingSpew(BSTR lpFormat, ...) { va_list ap; va_start(ap, lpFormat); DbgSpew(DBG_MSG_INTERESTING+DBG_MSG_DEST_DBG, lpFormat, ap); va_end(ap); }
void ImportantSpew(BSTR lpFormat, ...) { va_list ap; va_start(ap, lpFormat); DbgSpew(DBG_MSG_IMPORTANT+DBG_MSG_DEST_DBG+DBG_MSG_DEST_FILE, lpFormat, ap); va_end(ap); }
void HeinousESpew(BSTR lpFormat, ...) { va_list ap; va_start(ap, lpFormat); DbgSpew(DBG_MSG_HEINOUS+DBG_MSG_DEST_DBG+DBG_MSG_DEST_FILE+DBG_MSG_DEST_EVENT+DBG_MSG_CLASS_ERROR, lpFormat, ap); va_end(ap); }
void HeinousISpew(BSTR lpFormat, ...) { va_list ap; va_start(ap, lpFormat); DbgSpew(DBG_MSG_HEINOUS+DBG_MSG_DEST_DBG+DBG_MSG_DEST_FILE+DBG_MSG_DEST_EVENT, lpFormat, ap); va_end(ap); }
// Blob format: propertylength;propertyName=value.
BOOL GetPropertyValueFromBlob(BSTR bstrHelpBlob, WCHAR * pName, WCHAR** ppValue) { WCHAR *p1, *p2, *pEnd; BOOL bRet = FALSE; LONG lTotal =0; size_t lProp = 0; size_t iNameLen;
if (!bstrHelpBlob || *bstrHelpBlob==L'\0' || !pName || *pName ==L'\0'|| !ppValue) return FALSE;
iNameLen = wcslen(pName);
pEnd = bstrHelpBlob + wcslen(bstrHelpBlob); p1 = p2 = bstrHelpBlob;
while (1) { // get property length
while (*p2 != L';' && *p2 != L'\0' && iswdigit(*p2) ) p2++; if (*p2 != L';') goto done;
*p2 = L'\0'; // set it to get length
lProp = _wtol(p1); *p2 = L';'; // revert it back.
// security fix: In the case where we get a malformed header for
// the HelpBlob - exit
if (lProp <= iNameLen) goto done; // get property string
p1 = ++p2; while (*p2 != L'=' && *p2 != L'\0' && p2 < p1+lProp) p2++; if (*p2 != L'=') goto done;
if (wcsncmp(p1, pName, iNameLen) == 0) { if (lProp == iNameLen+1) // A=B= case (no value)
goto done;
*ppValue = (WCHAR*)LocalAlloc(LMEM_FIXED, sizeof(WCHAR) * (lProp-iNameLen)); if (*ppValue == NULL) goto done;
wcsncpy(*ppValue, p2+1, lProp-iNameLen-1); (*ppValue)[lProp-iNameLen-1]=L'\0'; bRet = TRUE; break; }
// check next property
p2 = p1 = p1 + lProp; if (p2 > pEnd) break; }
done: return bRet; }
|