|
|
/*+
* * Microsoft Windows * Copyright (C) Microsoft Corporation, 1997 - 1998. * * Name : seclogon.cxx * Author:Jeffrey Richter (v-jeffrr) * * Abstract: * This is the service DLL for Secondary Logon Service * This service supports the CreateProcessWithLogon API implemented * in advapi32.dll * * Revision History: * PraeritG 10/8/97 To integrate this in to services.exe * -*/
#define STRICT
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntlsa.h>
#include <Windows.h>
#define SECURITY_WIN32
#define SECURITY_KERBEROS
#include <security.h>
#include <secint.h>
#include <winsafer.h>
#include <shellapi.h>
#include <svcs.h>
#include <userenv.h>
#include <sddl.h>
#include "seclogon.h"
#include <stdio.h>
#include "stringid.h"
#include "dbgdef.h"
//
// must move to winbase.h soon!
#define LOGON_WITH_PROFILE 0x00000001
#define LOGON_NETCREDENTIALS_ONLY 0x00000002
#define MAXIMUM_SECLOGON_PROCESSES MAXIMUM_WAIT_OBJECTS*4
#define DESKTOP_ALL (DESKTOP_READOBJECTS | DESKTOP_CREATEWINDOW | \
DESKTOP_CREATEMENU | DESKTOP_HOOKCONTROL | \ DESKTOP_JOURNALRECORD | DESKTOP_JOURNALPLAYBACK | \ DESKTOP_ENUMERATE | DESKTOP_WRITEOBJECTS | \ DESKTOP_SWITCHDESKTOP | STANDARD_RIGHTS_REQUIRED)
#define WINSTA_ALL (WINSTA_ENUMDESKTOPS | WINSTA_READATTRIBUTES | \
WINSTA_ACCESSCLIPBOARD | WINSTA_CREATEDESKTOP | \ WINSTA_WRITEATTRIBUTES | WINSTA_ACCESSGLOBALATOMS | \ WINSTA_EXITWINDOWS | WINSTA_ENUMERATE | \ WINSTA_READSCREEN | \ STANDARD_RIGHTS_REQUIRED)
struct SECL_STATE { SERVICE_STATUS serviceStatus; SERVICE_STATUS_HANDLE hServiceStatus; } g_state;
typedef struct _SECONDARYLOGONINFOW { // First fields should all be quad-word types to avoid alignment errors:
LPSTARTUPINFO lpStartupInfo; LPWSTR lpUsername; LPWSTR lpDomain; LPWSTR lpPassword; LPWSTR lpApplicationName; LPWSTR lpCommandLine; LPVOID lpEnvironment; LPCWSTR lpCurrentDirectory;
// Next group of fields are double-word types:
DWORD dwProcessId; ULONG LogonIdLowPart; LONG LogonIdHighPart; DWORD dwLogonFlags; DWORD dwCreationFlags;
DWORD dwSeclogonFlags; HANDLE hWinsta; HANDLE hDesk; // Insert smaller types below:
BOOL fFreeWinsta; BOOL fFreeDesk; } SECONDARYLOGONINFOW, *PSECONDARYLOGONINFOW;
typedef struct _SECONDARYLOGONRETINFO { PROCESS_INFORMATION pi; DWORD dwErrorCode; } SECONDARYLOGONRETINFO, *PSECONDARYLOGONRETINFO;
typedef struct _SECONDARYLOGINWATCHINFO { HANDLE hProcess; HANDLE hToken; HANDLE hProfile; LUID LogonId; PSECONDARYLOGONINFOW psli; } SECONDARYLOGONWATCHINFO, *PSECONDARYLOGONWATCHINFO;
typedef struct _JOBINFO { HANDLE Job; LUID LogonId; } JOBINFO, *PJOBINFO;
#define _JumpCondition(condition, label) \
if (condition) \ { \ goto label; \ } \ else { }
#define _JumpConditionWithExpr(condition, label, expr) \
if (condition) \ { \ expr; \ goto label; \ } \ else { }
#define ARRAYSIZE(array) ((sizeof(array)) / (sizeof(array[0])))
#define FIELDOFFSET(s,m) ((size_t)(ULONG_PTR)&(((s *)0)->m))
HANDLE g_hThreadWatchdog; JOBINFO g_Jobs[MAXIMUM_SECLOGON_PROCESSES]; HANDLE g_hProcess[MAXIMUM_SECLOGON_PROCESSES]; HANDLE g_hToken[MAXIMUM_SECLOGON_PROCESSES]; HANDLE g_hProfile[MAXIMUM_SECLOGON_PROCESSES]; LUID g_LogonId[MAXIMUM_SECLOGON_PROCESSES]; PSECONDARYLOGONINFOW g_psli[MAXIMUM_SECLOGON_PROCESSES]; int g_nNumSecondaryLogonProcesses = 0; CRITICAL_SECTION csForProcessCount; CRITICAL_SECTION csForDesktop; BOOL g_fIsCsInitialized = FALSE; BOOL g_fTerminateSecondaryLogonService = FALSE; PSVCHOST_GLOBAL_DATA GlobalData; HANDLE g_hIOCP = NULL; BOOL g_fCleanupThreadActive = FALSE;
//
// function prototypes
//
void Free_SECONDARYLOGONINFOW(PSECONDARYLOGONINFOW psli); void FreeGlobalState(); DWORD InitGlobalState(); DWORD MySetServiceStatus(DWORD dwCurrentState, DWORD dwCheckPoint, DWORD dwWaitHint, DWORD dwExitCode); DWORD MySetServiceStopped(DWORD dwExitCode); DWORD SeclStartRpcServer(); DWORD SeclStopRpcServer(); VOID SecondaryLogonCleanupJob(LPVOID pvJobIndex, BOOL *pfLastJob); BOOL SlpLoadUserProfile(HANDLE hToken, PHANDLE hProfile); DWORD To_SECONDARYLOGONINFOW(PSECL_SLI pSeclSli, PSECONDARYLOGONINFOW *ppsli); DWORD To_SECL_SLRI(SECONDARYLOGONRETINFO *pslri, PSECL_SLRI pSeclSlri);
void DbgPrintf( DWORD dwSubSysId, LPCSTR pszFormat , ...) { va_list args; CHAR pszBuffer[1024]; va_start(args, pszFormat); _vsnprintf(pszBuffer, 1024, pszFormat, args); va_end(args); }
BOOL IsSystemProcess( VOID ) { PTOKEN_USER User; HANDLE Token; DWORD RetLen; PSID SystemSid = NULL; SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY; BYTE Buffer[100];
if(AllocateAndInitializeSid(&SidAuthority,1,SECURITY_LOCAL_SYSTEM_RID, 0,0,0,0,0,0,0,&SystemSid)) { if(OpenThreadToken(GetCurrentThread(), MAXIMUM_ALLOWED, FALSE, &Token)) { if(GetTokenInformation(Token, TokenUser, Buffer, 100, &RetLen)) { User = (PTOKEN_USER)Buffer;
CloseHandle(Token);
if(EqualSid(User->User.Sid, SystemSid)) { FreeSid(SystemSid); return TRUE; } } else CloseHandle(Token); } FreeSid(SystemSid); } return FALSE; }
DWORD SlpGetClientLogonId( HANDLE Process, PLUID LogonId )
{ HANDLE Token; TOKEN_STATISTICS TokenStats; DWORD ReturnLength;
//
// Get handle to the process token.
//
if(OpenProcessToken(Process, MAXIMUM_ALLOWED, &Token)) { if(GetTokenInformation ( Token, TokenStatistics, (PVOID)&TokenStats, sizeof( TOKEN_STATISTICS ), &ReturnLength )) {
*LogonId = TokenStats.AuthenticationId; CloseHandle(Token); return ERROR_SUCCESS;
} CloseHandle(Token); } return GetLastError(); }
DWORD ModifyUserAccessToObject (IN HANDLE hObject, IN PSID pUserSid, IN DWORD dwAccessMask, // access mask to apply IF granting access
IN BYTE bAceFlags, // flags to supply with Ace IF granting access
IN BOOL fAdd // true if we're granting access
) { ACL_SIZE_INFORMATION asiSize; PACCESS_ALLOWED_ACE pAce = NULL; PACCESS_ALLOWED_ACE pAceNew = NULL; BOOL fRemovedAccess = FALSE; BOOL fDaclDefaulted; BOOL fDaclPresent; DWORD dwIndex; DWORD dwNeeded; DWORD dwNewAclSize; DWORD dwResult; PACL pDaclNew = NULL; PACL pDaclReadOnly = NULL; SECURITY_DESCRIPTOR SdNew; PSECURITY_DESCRIPTOR pSdReadOnly = NULL; SECURITY_INFORMATION siRequested;
// Initialize non-pointer data:
ZeroMemory(&SdNew, sizeof(SdNew));
// Query the security descriptor
siRequested = DACL_SECURITY_INFORMATION; if (!GetUserObjectSecurity(hObject, &siRequested, pSdReadOnly, 0, &dwNeeded)) { // allocate buffer large for returned SD and another ACE.
pSdReadOnly = (PSECURITY_DESCRIPTOR) HeapAlloc (GetProcessHeap(), 0, dwNeeded + 100); if (NULL == pSdReadOnly) goto MemoryError; if (!GetUserObjectSecurity(hObject, &siRequested, pSdReadOnly, dwNeeded, &dwNeeded)) goto GetUserObjectSecurityError; } else { // There's no security descriptor, not much we can do here!
goto SuccessReturn; }
if (!GetSecurityDescriptorDacl(pSdReadOnly, &fDaclPresent, &pDaclReadOnly, &fDaclDefaulted)) goto GetSecurityDescriptorDaclError;
// if Dacl is null, we don't need to do anything
// because it gives WORLD full control...
if (!fDaclPresent || NULL == pDaclReadOnly) goto SuccessReturn;
// Compute the size for the new ACL, based on the size of the current
// ACL, and the operation to be performed on it.
if (!GetAclInformation(pDaclReadOnly, (PVOID)&asiSize, sizeof(asiSize), AclSizeInformation)) goto GetAclInformationError;
if (fAdd) dwNewAclSize = asiSize.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(pUserSid) + sizeof(DWORD); else dwNewAclSize = asiSize.AclBytesInUse - sizeof(ACCESS_ALLOWED_ACE) - GetLengthSid(pUserSid) + sizeof(DWORD); pDaclNew = (PACL)HeapAlloc(GetProcessHeap(), 0, dwNewAclSize); if (NULL == pDaclNew) goto MemoryError;
if (!InitializeAcl(pDaclNew, dwNewAclSize, ACL_REVISION)) goto InitializeAclError;
if (fAdd) // We're granting the user access
{ if (!AddAccessAllowedAce(pDaclNew, ACL_REVISION, dwAccessMask, pUserSid)) goto AddAccessAllowedAceError;
if (!GetAce(pDaclNew, 0, &pAceNew)) goto GetAceError;
pAceNew->Header.AceFlags = bAceFlags; }
// Copy over the ACEs that are still valid
// (all if adding, all but the user's SID if removing).
for (dwIndex = 0; dwIndex < asiSize.AceCount; dwIndex++) { if (!GetAce(pDaclReadOnly, dwIndex, (PVOID*)(&pAce))) goto GetAceError;
// We're removing -- check if this ACE contains the user's SID
if (!fAdd && !fRemovedAccess) { if ((((PACE_HEADER)pAce)->AceType) == ACCESS_ALLOWED_ACE_TYPE) { if (EqualSid(pUserSid, (PSID)(&(pAce->SidStart)))) { // Don't add this ACE to the new ACL we're constructing.
// NOTE: we only want to remove one ACCESS_ALLOWED_ACE. Otherwise, if the
// user already had access to the desktop, we could
// be removing the access they previously had!
fRemovedAccess = TRUE; continue; } } } if (!AddAce(pDaclNew, ACL_REVISION, 0xFFFFFFFF, pAce, ((PACE_HEADER)pAce)->AceSize)) goto AddAceError; } // Create the new security descriptor to assign to the object:
if (!InitializeSecurityDescriptor(&SdNew, SECURITY_DESCRIPTOR_REVISION)) goto InitializeSecurityDescriptorError;
// Add the new DACL to the descriptor:
if (!SetSecurityDescriptorDacl(&SdNew, TRUE, pDaclNew, fDaclDefaulted)) goto SetSecurityDescriptorDaclError;
// Finally, set the object security using our new security descriptor.
siRequested = DACL_SECURITY_INFORMATION; if (!SetUserObjectSecurity(hObject, &siRequested, &SdNew)) goto SetUserObjectSecurityError;
SuccessReturn: dwResult = ERROR_SUCCESS; ErrorReturn: if (NULL != pSdReadOnly) { HeapFree(GetProcessHeap(), 0, pSdReadOnly); } if (NULL != pDaclNew) { HeapFree(GetProcessHeap(), 0, pDaclNew); } return dwResult; SET_DWRESULT(AddAceError, GetLastError()); SET_DWRESULT(AddAccessAllowedAceError, GetLastError()); SET_DWRESULT(GetAceError, GetLastError()); SET_DWRESULT(GetAclInformationError, GetLastError()); SET_DWRESULT(GetSecurityDescriptorDaclError, GetLastError()); SET_DWRESULT(GetUserObjectSecurityError, GetLastError()); SET_DWRESULT(InitializeAclError, GetLastError()); SET_DWRESULT(InitializeSecurityDescriptorError, GetLastError()); SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY); SET_DWRESULT(SetSecurityDescriptorDaclError, GetLastError()); SET_DWRESULT(SetUserObjectSecurityError, GetLastError()); }
DWORD ModifyUserAccessToDesktop (IN HANDLE hWinsta, IN HANDLE hDesk, IN HANDLE hToken, IN BOOL fAdd) { BOOL fEnteredCriticalSection = FALSE; BYTE rgbSidBuff[256]; DWORD dwResult = ERROR_SUCCESS; DWORD dwReturnedLen; PTOKEN_USER pTokenUser = NULL;
// Get the SID from the token:
pTokenUser = (PSID)&rgbSidBuff[0]; if (!GetTokenInformation(hToken, TokenUser, pTokenUser, sizeof(rgbSidBuff), &dwReturnedLen)) goto GetTokenInformationError;
// We don't want any other threads messing with the desktop ACL
EnterCriticalSection(&csForDesktop); fEnteredCriticalSection = TRUE;
if (NULL != hWinsta) { dwResult = ModifyUserAccessToObject(hWinsta, pTokenUser->User.Sid, WINSTA_ALL, NO_PROPAGATE_INHERIT_ACE, fAdd); if (ERROR_SUCCESS != dwResult) goto ModifyUserAccessToObjectError; }
if (NULL != hDesk) { dwResult = ModifyUserAccessToObject(hDesk, pTokenUser->User.Sid, DESKTOP_ALL, 0, fAdd); if (ERROR_SUCCESS != dwResult) goto ModifyUserAccessToObjectError; }
dwResult = ERROR_SUCCESS; ErrorReturn: if (fEnteredCriticalSection) { LeaveCriticalSection(&csForDesktop); } return dwResult;
SET_DWRESULT(GetTokenInformationError, GetLastError()); TRACE_ERROR (ModifyUserAccessToObjectError); }
DWORD WINAPI WaitForNextJobTermination(PVOID pvIgnored) { BOOL fResult; DWORD dwNumberOfBytes; DWORD dwResult; OVERLAPPED *po; ULONG_PTR ulptrCompletionKey;
while (TRUE) { fResult = GetQueuedCompletionStatus(g_hIOCP, &dwNumberOfBytes, &ulptrCompletionKey, &po, INFINITE); if (!fResult) { // We've encountered an error. Shutdown our cleanup thread -- the next runas will queue another one.
EnterCriticalSection(&csForProcessCount); g_fCleanupThreadActive = FALSE; LeaveCriticalSection(&csForProcessCount);
goto GetQueuedCompletionStatusError; }
// When waiting on job objects, the dwNumberOfBytes contains a message ID, indicating
// the event which just occured.
switch (dwNumberOfBytes) { case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: { BOOL fLastJob;
// All of our processes have terminated. Call our cleanup function.
SecondaryLogonCleanupJob((LPVOID)ulptrCompletionKey /*job index*/, &fLastJob); if (fLastJob) { // There are no more jobs -- we're done processing notification.
goto CommonReturn; } else { // More jobs left to clean up. Keep processing...
} } default:; // some message we don't care about. Try again.
} } CommonReturn: dwResult = ERROR_SUCCESS; ErrorReturn: return dwResult;
SET_DWRESULT(GetQueuedCompletionStatusError, GetLastError()); }
VOID SecondaryLogonCleanupJob( LPVOID pvJobIndex, BOOL *pfLastJob ) /*++
Routine Description: This routine is a process cleanup handler when one of the secondary logon process goes away.
Arguments: dwProcessIndex -- the actual index to the process, the pointer is cast back to dword. THIS IS SAFE IN SUNDOWN. fWaitStatus -- status of the wait done by one of services.exe threads.
Return Value: always 0.
--*/ { DWORD dwJobIndex = PtrToUlong(pvJobIndex); DWORD ProcessNum = dwJobIndex; // 1-to-1 mapping between jobs and runas'd processes
DWORD dwResult;
EnterCriticalSection(&csForProcessCount);
// We've found another process in this job.
if(g_psli[ProcessNum]) { // we don't care about return value.
ModifyUserAccessToDesktop(g_psli[ProcessNum]->hWinsta, g_psli[ProcessNum]->hDesk, g_hToken[ProcessNum], FALSE /*remove*/); Free_SECONDARYLOGONINFOW(g_psli[ProcessNum]); g_psli[ProcessNum] = NULL; } if(g_hProcess[ProcessNum]) { CloseHandle(g_hProcess[ProcessNum]); g_hProcess[ProcessNum] = NULL; } if(g_hProfile[ProcessNum] != NULL) { UnloadUserProfile(g_hToken[ProcessNum], g_hProfile[ProcessNum]); g_hProfile[ProcessNum] = NULL; } if(g_hToken[ProcessNum]) { CloseHandle(g_hToken[ProcessNum]); g_hToken[ProcessNum] = NULL; } // Close off this job:
CloseHandle(g_Jobs[dwJobIndex].Job); g_Jobs[dwJobIndex].Job = NULL;
*pfLastJob = --g_nNumSecondaryLogonProcesses == 0;
// If it's the last job, the cleanup thread terminates:
g_fCleanupThreadActive = !(*pfLastJob); // Update the service status to reflect whether there is a runas'd process alive.
MySetServiceStatus(SERVICE_RUNNING, 0, 0, 0);
LeaveCriticalSection(&csForProcessCount);
return; }
VOID APIENTRY SecondaryLogonProcessWatchdogNewProcess( PSECONDARYLOGONWATCHINFO dwParam )
/*++
Routine Description: This routine puts the secondary logon process created on the wait queue such that cleanup can be done after the process dies.
Arguments: dwParam -- the pointer to the process information.
Return Value: none.
--*/ { DWORD j, FirstFreeJob; unsigned __int3264 i; BOOL JobFound; JOBOBJECT_ASSOCIATE_COMPLETION_PORT joacp; LUID ProcessLogonId; ULONG_PTR ulptrJobIndex;
if (dwParam != NULL) { PSECONDARYLOGONWATCHINFO pslwi = (PSECONDARYLOGONWATCHINFO) dwParam; EnterCriticalSection(&csForProcessCount); for(i=0;i<MAXIMUM_SECLOGON_PROCESSES;i++) { // if(g_hProcess[i] == LongToHandle(0xDEADBEEF)) break;
if(g_hProcess[i] == NULL) break; }
g_hProcess[i] = pslwi->hProcess; g_hToken[i] = pslwi->hToken; g_hProfile[i] = pslwi->hProfile; g_LogonId[i].LowPart = pslwi->LogonId.LowPart; g_LogonId[i].HighPart = pslwi->LogonId.HighPart; g_psli[i] = pslwi->psli; // Initialize this job with the logon ID of the client process.
// If this is a recursive runas, we'll override this value in the following loop
g_Jobs[i].LogonId.LowPart = g_LogonId[i].LowPart; g_Jobs[i].LogonId.HighPart = g_LogonId[i].HighPart; // Determine which logon session the new process should be associated with.
for(j=0;j<MAXIMUM_SECLOGON_PROCESSES;j++) { if(g_Jobs[j].Job != NULL) { SlpGetClientLogonId(g_hProcess[j], &ProcessLogonId); if(ProcessLogonId.LowPart == g_LogonId[i].LowPart && ProcessLogonId.HighPart == g_LogonId[i].HighPart) { JobFound = TRUE; g_Jobs[i].LogonId.LowPart = g_Jobs[j].LogonId.LowPart; g_Jobs[i].LogonId.HighPart = g_Jobs[j].LogonId.HighPart; break; } } } // Increment the number of runas'd processes
g_nNumSecondaryLogonProcesses++;
// BUGBUG: we currently have no means of recovering from failures
// in the functions below.
// we have to create a new one;
g_Jobs[i].Job = CreateJobObject(NULL, NULL); if (NULL != g_Jobs[i].Job) { if (AssignProcessToJobObject(g_Jobs[i].Job, g_hProcess[i])) { ulptrJobIndex = i; // Register our IO completion port to wait for events from this job:
joacp.CompletionKey = (LPVOID)ulptrJobIndex; joacp.CompletionPort = g_hIOCP;
if (SetInformationJobObject(g_Jobs[i].Job, JobObjectAssociateCompletionPortInformation, &joacp, sizeof(joacp))) { // If we don't already have a cleanup thread running, start one now:
if (!g_fCleanupThreadActive) { g_fCleanupThreadActive = QueueUserWorkItem(WaitForNextJobTermination, NULL, WT_EXECUTELONGFUNCTION); } } } }
// Update the service status to reflect that there is a runas'd process
// This prevents the service from receiving SERVICE_STOP controls
// while runas'd processes are alive.
MySetServiceStatus(SERVICE_RUNNING, 0, 0, 0); LeaveCriticalSection(&csForProcessCount); } else { //
// We were just awakened in order to terminate the service (nothing to do)
//
} }
DWORD ServiceStop(BOOL fShutdown, DWORD dwExitCode) { DWORD dwCheckPoint = 0; DWORD dwIndex; DWORD dwResult;
// Don't want the process count to change while we're shutting down the service!
EnterCriticalSection(&csForProcessCount); // Only stop if we have no runas'd processes, or if we're shutting down
if (fShutdown || 0 == g_nNumSecondaryLogonProcesses) { dwResult = MySetServiceStatus(SERVICE_STOP_PENDING, dwCheckPoint++, 0, 0); _JumpCondition(ERROR_SUCCESS != dwResult && !fShutdown, MySetServiceStatusError);
// We shouldn't hold the critical section while we're shutting down the RPC server,
// because RPC threads may be trying to acquire it.
LeaveCriticalSection(&csForProcessCount);
dwResult = SeclStopRpcServer(); _JumpCondition(ERROR_SUCCESS != dwResult && !fShutdown, SeclStopRpcServerError);
dwResult = MySetServiceStatus(SERVICE_STOP_PENDING, dwCheckPoint++, 0, 0); _JumpCondition(ERROR_SUCCESS != dwResult && !fShutdown, MySetServiceStatusError);
g_fTerminateSecondaryLogonService = TRUE;
if (g_fIsCsInitialized) { DeleteCriticalSection(&csForProcessCount); DeleteCriticalSection(&csForDesktop); g_fIsCsInitialized = FALSE; }
if (NULL != g_hIOCP) { CloseHandle(g_hIOCP); g_hIOCP = NULL; }
// Unlike MySetServiceStatus, this routine doesn't access any
// global state which could have been freed:
dwResult = MySetServiceStopped(dwExitCode); _JumpCondition(ERROR_SUCCESS != dwResult && !fShutdown, MySetServiceStopped); }
dwResult = ERROR_SUCCESS; ErrorReturn: return dwResult;
SET_DWRESULT(MySetServiceStatusError, dwResult); SET_DWRESULT(MySetServiceStopped, dwResult); SET_DWRESULT(SeclStopRpcServerError, dwResult); }
void WINAPI ServiceHandler( DWORD fdwControl ) /*++
Routine Description:
Service handler which wakes up the main service thread when ever service controller needs to send a message.
Arguments:
fdwControl -- the control from the service controller.
Return Value: none.
--*/ { BOOL fResult; BOOL fCanStopService = TRUE; DWORD dwNextState = g_state.serviceStatus.dwCurrentState; DWORD dwResult;
switch (fdwControl) { case SERVICE_CONTROL_CONTINUE: dwResult = MySetServiceStatus(SERVICE_CONTINUE_PENDING, 0, 0, 0); _JumpCondition(ERROR_SUCCESS != dwResult, MySetServiceStatusError); dwResult = SeclStartRpcServer(); _JumpCondition(ERROR_SUCCESS != dwResult, StartRpcServerError); dwNextState = SERVICE_RUNNING; break;
case SERVICE_CONTROL_INTERROGATE: break;
case SERVICE_CONTROL_PAUSE: dwResult = MySetServiceStatus(SERVICE_PAUSE_PENDING, 0, 0, 0); _JumpCondition(ERROR_SUCCESS != dwResult, MySetServiceStatusError); dwResult = SeclStopRpcServer(); _JumpCondition(ERROR_SUCCESS != dwResult, StopRpcServerError); dwNextState = SERVICE_PAUSED; break;
case SERVICE_CONTROL_STOP: dwResult = ServiceStop(FALSE /*fShutdown*/, ERROR_SUCCESS); _JumpCondition(ERROR_SUCCESS != dwResult, ServiceStopError); return ; // All global state has been freed, just exit.
case SERVICE_CONTROL_SHUTDOWN: dwResult = ServiceStop(TRUE /*fShutdown*/, ERROR_SUCCESS); _JumpCondition(ERROR_SUCCESS != dwResult, ServiceStopError); return ; // All global state has been freed, just exit.
default: // Unhandled service control!
goto ErrorReturn; }
CommonReturn: // Restore the original state on error, set the new state on success.
dwResult = MySetServiceStatus(dwNextState, 0, 0, 0); return;
ErrorReturn: goto CommonReturn;
SET_ERROR(MySetServiceStatusError, dwResult); SET_ERROR(ServiceStopError, dwResult); SET_ERROR(StartRpcServerError, dwResult); SET_ERROR(StopRpcServerError, dwResult); }
VOID SlrCreateProcessWithLogon (IN RPC_BINDING_HANDLE hRPCBinding, IN PSECONDARYLOGONINFOW psli, OUT PSECONDARYLOGONRETINFO pslri)
/*++
Routine Description: The core routine -- it handles a client request to start a secondary logon process.
Arguments: psli -- the input structure with client request information pslri -- the output structure with response back to the client.
Return Value: none.
--*/ { BOOL fAccessWasAllowed = FALSE; HANDLE hCurrentThread = NULL; HANDLE hCurrentThreadToken = NULL; HANDLE hToken = NULL; HANDLE hProfile = NULL; HANDLE hProcessClient = NULL; PVOID pvEnvBlock = NULL, pvUserProfile = NULL; BOOL fCreatedEnvironmentBlock = FALSE; BOOL fIsImpersonatingRpcClient = FALSE; BOOL fIsImpersonatingClient = FALSE; BOOL fInheritHandles = FALSE; BOOL fOpenedSTDIN = FALSE; BOOL fOpenedSTDOUT = FALSE; BOOL fOpenedSTDERR = FALSE; PROFILEINFO pi; SECURITY_ATTRIBUTES sa; SECONDARYLOGONWATCHINFO slwi; DWORD dwResult; DWORD SessionId; DWORD dwLogonProvider; SECURITY_LOGON_TYPE LogonType;
__try {
//
// Do some security checks:
//
// 1) We should impersonate the client and then try to open
// the process so that we are assured that they didn't
// give us some fake id.
//
dwResult = RpcImpersonateClient(hRPCBinding); _JumpCondition(RPC_S_OK != dwResult, leave_with_last_error); fIsImpersonatingRpcClient = TRUE;
hProcessClient = OpenProcess(MAXIMUM_ALLOWED, FALSE, psli->dwProcessId); _JumpCondition(hProcessClient == NULL, leave_with_last_error);
#if 0
//
// 2) Check that the client is not running from a restricted account.
//
hCurrentThread = GetCurrentThread(); // Doesn't need to be freed with CloseHandle().
_JumpCondition(NULL == hCurrentThread, leave_with_last_error); _JumpCondition(FALSE == OpenThreadToken(hCurrentThread, TOKEN_QUERY | TOKEN_DUPLICATE, TRUE, &hCurrentThreadToken), leave_with_last_error);
#endif
dwResult = RpcRevertToSelfEx(hRPCBinding); if (RPC_S_OK != dwResult) { __leave; } fIsImpersonatingRpcClient = FALSE;
#if 0
if (TRUE == IsTokenUntrusted(hCurrentThreadToken)) { dwResult = ERROR_ACCESS_DENIED; __leave; } #endif
//
// We should get the session id from process id
// we will set this up in the token so that create process
// happens on the correct session.
//
_JumpCondition(!ProcessIdToSessionId(psli->dwProcessId, &SessionId), leave_with_last_error);
//
// Get the unique logonId.
// we will use this to cleanup any running processes
// when the logoff happens.
//
dwResult = SlpGetClientLogonId(hProcessClient, &slwi.LogonId); if(dwResult != ERROR_SUCCESS) { __leave; }
if ((psli->lpStartupInfo->dwFlags & STARTF_USESTDHANDLES) != 0) { _JumpCondition(!DuplicateHandle (hProcessClient, psli->lpStartupInfo->hStdInput, GetCurrentProcess(), &psli->lpStartupInfo->hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS), leave_with_last_error); fOpenedSTDIN = TRUE;
_JumpCondition(!DuplicateHandle (hProcessClient, psli->lpStartupInfo->hStdOutput, GetCurrentProcess(), &psli->lpStartupInfo->hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS), leave_with_last_error); fOpenedSTDOUT = TRUE;
_JumpCondition(!DuplicateHandle (hProcessClient, psli->lpStartupInfo->hStdError, GetCurrentProcess(), &psli->lpStartupInfo->hStdError, 0, TRUE, DUPLICATE_SAME_ACCESS), leave_with_last_error); fOpenedSTDERR = TRUE;
fInheritHandles = TRUE; } else { psli->lpStartupInfo->hStdInput = INVALID_HANDLE_VALUE; psli->lpStartupInfo->hStdOutput = INVALID_HANDLE_VALUE; psli->lpStartupInfo->hStdError = INVALID_HANDLE_VALUE; }
if(psli->dwLogonFlags & LOGON_NETCREDENTIALS_ONLY) { LogonType = (SECURITY_LOGON_TYPE)LOGON32_LOGON_NEW_CREDENTIALS; dwLogonProvider = LOGON32_PROVIDER_WINNT50; } else { LogonType = (SECURITY_LOGON_TYPE) LOGON32_LOGON_INTERACTIVE; dwLogonProvider = LOGON32_PROVIDER_DEFAULT; }
// Duplicate windowstation handles received from the client process
// so that they're valid in this process.
if (NULL != psli->hWinsta) { _JumpCondition(!DuplicateHandle(hProcessClient, psli->hWinsta, GetCurrentProcess(), &psli->hWinsta, 0, TRUE, DUPLICATE_SAME_ACCESS), leave_with_last_error); psli->fFreeWinsta = TRUE; }
if (NULL != psli->hDesk) { _JumpCondition(!DuplicateHandle(hProcessClient, psli->hDesk, GetCurrentProcess(), &psli->hDesk, 0, TRUE, DUPLICATE_SAME_ACCESS), leave_with_last_error); psli->fFreeDesk = TRUE; }
// LogonUser does not return profile information, we need to grab
// that out of band after the logon has completed.
//
dwResult = RpcImpersonateClient(hRPCBinding); _JumpCondition(RPC_S_OK != dwResult, leave_with_last_error); fIsImpersonatingRpcClient = TRUE;
_JumpCondition(!LogonUser(psli->lpUsername, psli->lpDomain, psli->lpPassword, LogonType, dwLogonProvider, &hToken), leave_with_last_error);
if (0 == (SECLOGON_CALLER_SPECIFIED_DESKTOP & psli->dwSeclogonFlags)) { // If the caller did not specify their own desktop, it is our responsibility
// to grant the user access to the default desktop:
dwResult = ModifyUserAccessToDesktop(psli->hWinsta, psli->hDesk, hToken, TRUE /*grant*/); if (ERROR_SUCCESS != dwResult) __leave; fAccessWasAllowed = TRUE; }
dwResult = RpcRevertToSelfEx(hRPCBinding); if (RPC_S_OK != dwResult) { __leave; }
fIsImpersonatingRpcClient = FALSE; // Let us set the SessionId in the Token.
_JumpCondition(!SetTokenInformation(hToken, TokenSessionId, &SessionId, sizeof(DWORD)), leave_with_last_error);
// Load the user's profile.
// if this fails, we will not continue
//
if(psli->dwLogonFlags & LOGON_WITH_PROFILE) { _JumpCondition(!SlpLoadUserProfile(hToken, &hProfile), leave_with_last_error); }
// we should now impersonate the user.
//
_JumpCondition(!ImpersonateLoggedOnUser(hToken), leave_with_last_error); fIsImpersonatingClient = TRUE;
// Query Default Owner/ACL from token. Make SD with this stuff, pass for
sa.nLength = sizeof(sa); sa.bInheritHandle = FALSE; sa.lpSecurityDescriptor = NULL;
//
// We should set the console control handler so CtrlC is correctly
// handled by the new process.
//
// SetConsoleCtrlHandler(NULL, FALSE);
//
// if lpEnvironment is NULL, we create new one for this user
// using CreateEnvironmentBlock
//
if(NULL == (psli->lpEnvironment)) { if(FALSE == CreateEnvironmentBlock( &(psli->lpEnvironment), hToken, FALSE )) { psli->lpEnvironment = NULL; } else { // Successfully created environment block.
fCreatedEnvironmentBlock = TRUE; } }
_JumpCondition(!CreateProcessAsUser(hToken, psli->lpApplicationName, psli->lpCommandLine, &sa, &sa, fInheritHandles, psli->dwCreationFlags | CREATE_UNICODE_ENVIRONMENT, psli->lpEnvironment, psli->lpCurrentDirectory, psli->lpStartupInfo, &pslri->pi), leave_with_last_error);
SetLastError(NO_ERROR); leave_with_last_error: dwResult = GetLastError(); __leave; } __finally { pslri->dwErrorCode = dwResult;
if (fCreatedEnvironmentBlock) { DestroyEnvironmentBlock(psli->lpEnvironment); } if (fIsImpersonatingClient) { RevertToSelf(); /* Ignore retval: nothing we can do on failure! */ } if (fIsImpersonatingRpcClient) { RpcRevertToSelfEx(hRPCBinding); /* Ignore retval: nothing we can do on failure! */ } if (fOpenedSTDIN) { CloseHandle(psli->lpStartupInfo->hStdInput); } if (fOpenedSTDOUT) { CloseHandle(psli->lpStartupInfo->hStdOutput); } if (fOpenedSTDERR) { CloseHandle(psli->lpStartupInfo->hStdError); }
if(pslri->dwErrorCode != NO_ERROR) { if (NULL != hProfile) { UnloadUserProfile(hToken, hProfile); } if (fAccessWasAllowed) { ModifyUserAccessToDesktop(psli->hWinsta, psli->hDesk, hToken, FALSE /*remove*/); } if (NULL != hToken) { CloseHandle(hToken); } } else { // Start the watchdog process last so it won't delete psli before we're done with it.
slwi.hProcess = pslri->pi.hProcess; slwi.hToken = hToken; slwi.hProfile = hProfile; // LogonId was already filled up.. right at the begining.
slwi.psli = psli; SecondaryLogonProcessWatchdogNewProcess(&slwi); // SetConsoleCtrlHandler(NULL, TRUE);
//
// Have the watchdog watch this newly added process so that
// cleanup will occur correctly when the process terminates.
//
// Set up the windowstation and desktop for the process
DuplicateHandle(GetCurrentProcess(), pslri->pi.hProcess, hProcessClient, &pslri->pi.hProcess, 0, FALSE, DUPLICATE_SAME_ACCESS); DuplicateHandle(GetCurrentProcess(), pslri->pi.hThread, hProcessClient, &pslri->pi.hThread, 0, FALSE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); }
if (NULL != hProcessClient) { CloseHandle(hProcessClient); } if (NULL != hCurrentThreadToken) { CloseHandle(hCurrentThreadToken); } } }
void WINAPI ServiceMain (IN DWORD dwArgc, IN WCHAR ** lpszArgv) /*++
Routine Description: The main service handler thread routine.
Arguments:
Return Value: none.
--*/ { DWORD i, dwResult, dwWaitResult; HANDLE rghWait[4];
for (i = 0; i < MAXIMUM_SECLOGON_PROCESSES; i++) { // g_hProcess[i] = LongToHandle(0xDEADBEEF);
g_Jobs[i].Job = NULL; g_hProcess[i] = NULL; g_hProfile[i] = NULL; g_hToken[i] = NULL; }
__try { InitializeCriticalSection(&csForProcessCount); InitializeCriticalSection(&csForDesktop); g_fIsCsInitialized = TRUE; } __except (EXCEPTION_EXECUTE_HANDLER) { return; // We can't do anything if we can't initialize this critsec
}
g_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0,0); _JumpCondition(NULL == g_hIOCP, CreateIoCompletionPortError);
dwResult = InitGlobalState(); _JumpCondition(ERROR_SUCCESS != dwResult, InitGlobalStateError);
// NOTE: hSS does not have to be closed.
g_state.hServiceStatus = RegisterServiceCtrlHandler(wszSvcName, ServiceHandler); _JumpCondition(NULL == g_state.hServiceStatus, RegisterServiceCtrlHandlerError);
dwResult = SeclStartRpcServer(); _JumpCondition(ERROR_SUCCESS != dwResult, StartRpcServerError);
// Tell the SCM we're up and running:
dwResult = MySetServiceStatus(SERVICE_RUNNING, 0, 0, ERROR_SUCCESS); _JumpCondition(ERROR_SUCCESS != dwResult, MySetServiceStatusError);
SetLastError(ERROR_SUCCESS); ErrorReturn: // Shut down the service if we couldn't fully start:
if (ERROR_SUCCESS != GetLastError()) { ServiceStop(TRUE /*fShutdown*/, GetLastError()); } return;
SET_ERROR(InitGlobalStateError, dwResult) SET_ERROR(MySetServiceStatusError, dwResult); SET_ERROR(RegisterServiceCtrlHandlerError, dwResult); SET_ERROR(StartRpcServerError, dwResult); TRACE_ERROR(CreateIoCompletionPortError); }
DWORD InstallService() /*++
Routine Description: It installs the service with service controller, basically creating the service object.
Arguments: none.
Return Value: several - as returned by the service controller.
--*/ { // TCHAR *szModulePathname;
TCHAR AppName[MAX_PATH]; LPTSTR ptszAppName = NULL; SC_HANDLE hService; DWORD dw; HANDLE hMod;
//
// Open the SCM on this machine.
//
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); if(hSCM == NULL) { dw = GetLastError(); return dw; }
//
// Let us give the service a useful description
// This is not earth shattering... if it works fine, if it
// doesn't it is just too bad :-)
//
hMod = GetModuleHandle(L"seclogon.dll");
//
// we'll try to get the localized name for the service,
// if it fails, we'll just put an english string...
//
if(hMod != NULL) { LoadString(hMod, SECLOGON_STRING_NAME, AppName, MAX_PATH ); ptszAppName = AppName; } else ptszAppName = L"RunAs Service";
//
// Add this service to the SCM's database.
//
hService = CreateService (hSCM, wszSvcName, ptszAppName, SERVICE_ALL_ACCESS, SERVICE_WIN32_SHARE_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, L"%SystemRoot%\\system32\\svchost.exe -k netsvcs", NULL, NULL, NULL, NULL, NULL); if(hService == NULL) { dw = GetLastError(); CloseServiceHandle(hSCM); return dw; }
if(hMod != NULL) { WCHAR DescString[500]; SERVICE_DESCRIPTION SvcDesc; LoadString( hMod, SECLOGON_STRING_DESCRIPTION, DescString, 500 ); SvcDesc.lpDescription = DescString; ChangeServiceConfig2( hService, SERVICE_CONFIG_DESCRIPTION, &SvcDesc ); }
//
// Close the service and the SCM
//
CloseServiceHandle(hService); CloseServiceHandle(hSCM); return S_OK; }
DWORD RemoveService() /*++
Routine Description: deinstalls the service.
Arguments: none.
Return Value: as returned by service controller apis.
--*/ { DWORD dw; SC_HANDLE hService; //
// Open the SCM on this machine.
//
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if(hSCM == NULL) { dw = GetLastError(); return dw; }
//
// Open this service for DELETE access
//
hService = OpenService(hSCM, wszSvcName, DELETE); if(hService == NULL) { dw = GetLastError(); CloseServiceHandle(hSCM); return dw; }
//
// Remove this service from the SCM's database.
//
DeleteService(hService);
//
// Close the service and the SCM
//
CloseServiceHandle(hService); CloseServiceHandle(hSCM); return S_OK; }
void SvchostPushServiceGlobals(PSVCHOST_GLOBAL_DATA pGlobalData) { // this entry point is called by svchost.exe
GlobalData=pGlobalData; }
void SvcEntry_Seclogon (IN DWORD argc, IN WCHAR **argv) /*++
Routine Description: Entry point for the service dll when running in svchost.exe
Arguments:
Return Value:
--*/ { ServiceMain(0,NULL);
UNREFERENCED_PARAMETER(argc); UNREFERENCED_PARAMETER(argv); }
STDAPI DllRegisterServer(void) /*++
Routine Description:
Arguments:
Return Value:
--*/ { return InstallService(); }
STDAPI DllUnregisterServer(void) /*++
Routine Description:
Arguments:
Return Value:
--*/ { return RemoveService(); }
DWORD InitGlobalState() { ZeroMemory(&g_state, sizeof(g_state)); return ERROR_SUCCESS; }
DWORD MySetServiceStatus(DWORD dwCurrentState, DWORD dwCheckPoint, DWORD dwWaitHint, DWORD dwExitCode) { BOOL fResult; DWORD dwResult; DWORD dwAcceptStop; int nNumProcesses;
EnterCriticalSection(&csForProcessCount); nNumProcesses = g_nNumSecondaryLogonProcesses; dwAcceptStop = 0 == nNumProcesses ? SERVICE_ACCEPT_STOP : 0;
g_state.serviceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS; g_state.serviceStatus.dwCurrentState = dwCurrentState;
switch (dwCurrentState) { case SERVICE_STOPPED: case SERVICE_STOP_PENDING: g_state.serviceStatus.dwControlsAccepted = 0; break; case SERVICE_RUNNING: case SERVICE_PAUSED: g_state.serviceStatus.dwControlsAccepted = // SERVICE_ACCEPT_SHUTDOWN
SERVICE_ACCEPT_PAUSE_CONTINUE | dwAcceptStop; break; case SERVICE_START_PENDING: case SERVICE_CONTINUE_PENDING: case SERVICE_PAUSE_PENDING: g_state.serviceStatus.dwControlsAccepted = // SERVICE_ACCEPT_SHUTDOWN
dwAcceptStop; break; } g_state.serviceStatus.dwWin32ExitCode = dwExitCode; g_state.serviceStatus.dwCheckPoint = dwCheckPoint; g_state.serviceStatus.dwWaitHint = dwWaitHint;
fResult = SetServiceStatus(g_state.hServiceStatus, &g_state.serviceStatus); _JumpCondition(FALSE == fResult, SetServiceStatusError);
dwResult = ERROR_SUCCESS; CommonReturn: LeaveCriticalSection(&csForProcessCount); return dwResult;
ErrorReturn: goto CommonReturn;
SET_DWRESULT(SetServiceStatusError, GetLastError()); }
DWORD MySetServiceStopped(DWORD dwExitCode) { BOOL fResult; DWORD dwResult;
g_state.serviceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS; g_state.serviceStatus.dwCurrentState = SERVICE_STOPPED; g_state.serviceStatus.dwControlsAccepted = 0; g_state.serviceStatus.dwWin32ExitCode = dwExitCode; g_state.serviceStatus.dwCheckPoint = 0; g_state.serviceStatus.dwWaitHint = 0;
fResult = SetServiceStatus(g_state.hServiceStatus, &g_state.serviceStatus); _JumpCondition(FALSE == fResult, SetServiceStatusError);
dwResult = ERROR_SUCCESS; CommonReturn: return dwResult;
ErrorReturn: goto CommonReturn;
SET_DWRESULT(SetServiceStatusError, GetLastError()); }
////////////////////////////////////////////////////////////////////////
//
// Implementation of RPC interface:
//
////////////////////////////////////////////////////////////////////////
void WINAPI SeclCreateProcessWithLogonW (IN handle_t hRPCBinding, IN SECL_SLI *pSeclSli, OUT SECL_SLRI *pSeclSlri) { BOOL fIsImpersonatingClient = FALSE; DWORD dwResult; HANDLE hHeap = NULL; PSECONDARYLOGONINFOW psli = NULL; SECL_SLRI SeclSlri; SECONDARYLOGONRETINFO slri;
ZeroMemory(&SeclSlri, sizeof(SeclSlri)); ZeroMemory(&slri, sizeof(slri));
// We don't want the service to be stopped while we're creating a process.
EnterCriticalSection(&csForProcessCount); // Service isn't running anymore ... don't create the process.
_JumpCondition(SERVICE_RUNNING != g_state.serviceStatus.dwCurrentState, ServiceStoppedError);
hHeap = GetProcessHeap(); _JumpCondition(NULL == hHeap, MemoryError);
__try { dwResult = To_SECONDARYLOGONINFOW(pSeclSli, &psli); _JumpCondition(ERROR_SUCCESS != dwResult, To_SECONDARYLOGONINFOW_Error);
if (psli->LogonIdHighPart != 0 || psli->LogonIdLowPart != 0) { // This is probably a notification from winlogon.exe that
// a client is logging off. If so, we must clean up all processes
// they've left running.
int i; LUID LogonId;
//
// We should impersonate the client,
// check it is LocalSystem and only then proceed.
//
fIsImpersonatingClient = RPC_S_OK == RpcImpersonateClient((RPC_BINDING_HANDLE)hRPCBinding); if(FALSE == fIsImpersonatingClient || FALSE == IsSystemProcess()) { slri.dwErrorCode = ERROR_INVALID_PARAMETER; ZeroMemory(&slri.pi, sizeof(slri.pi)); } else { LogonId.HighPart = psli->LogonIdHighPart; LogonId.LowPart = psli->LogonIdLowPart; for(i=0;i<MAXIMUM_SECLOGON_PROCESSES;i++) { //
// Let us destroy all jobs associated with
// this user. There could be more than one.
//
if(g_Jobs[i].Job == NULL) continue; if(g_Jobs[i].LogonId.HighPart == LogonId.HighPart && g_Jobs[i].LogonId.LowPart == LogonId.LowPart) { TerminateJobObject(g_Jobs[i].Job, 0); } } slri.dwErrorCode = ERROR_SUCCESS; ZeroMemory(&slri.pi, sizeof(slri.pi)); } if (fIsImpersonatingClient) { // Ignore error: nothing we can do on failure!
if (RPC_S_OK == RpcRevertToSelfEx((RPC_BINDING_HANDLE)hRPCBinding)) { fIsImpersonatingClient = FALSE; } } } else { // Ok, this isn't notification from winlogon, it's really a user
// trying to use the service. Create a process for them.
//
DWORD currentNumber; currentNumber = g_nNumSecondaryLogonProcesses; if(currentNumber == MAXIMUM_SECLOGON_PROCESSES) { slri.dwErrorCode = ERROR_NO_SYSTEM_RESOURCES; ZeroMemory(&slri.pi, sizeof(slri.pi)); } else { SlrCreateProcessWithLogon((RPC_BINDING_HANDLE)hRPCBinding, psli, &slri); } }
// If we've errored out, jump to the error handler.
_JumpCondition(NO_ERROR != slri.dwErrorCode, UnspecifiedSeclogonError); } __except (EXCEPTION_EXECUTE_HANDLER) { // If anything goes wrong, return the exception code to the client
dwResult = GetExceptionCode(); goto ExceptionError; }
CommonReturn: // Do not free slri: this will be freed by the watchdog!
SeclSlri.hProcess = (unsigned __int64)slri.pi.hProcess; SeclSlri.hThread = (unsigned __int64)slri.pi.hThread; SeclSlri.ulProcessId = slri.pi.dwProcessId; SeclSlri.ulThreadId = slri.pi.dwThreadId; SeclSlri.ulErrorCode = slri.dwErrorCode;
LeaveCriticalSection(&csForProcessCount); // Assign the OUT parameter:
*pSeclSlri = SeclSlri; return;
ErrorReturn: ZeroMemory(&slri.pi, sizeof(slri.pi)); if (NULL != psli) { Free_SECONDARYLOGONINFOW(psli); }
slri.dwErrorCode = dwResult; goto CommonReturn;
SET_DWRESULT(ExceptionError, dwResult); SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY); SET_DWRESULT(ServiceStoppedError, ERROR_SERVICE_NOT_ACTIVE); SET_DWRESULT(To_SECONDARYLOGONINFOW_Error, dwResult); SET_DWRESULT(UnspecifiedSeclogonError, slri.dwErrorCode); }
////////////////////////////////////////////////////////////////////////
//
// RPC Utility methods:
//
////////////////////////////////////////////////////////////////////////
DWORD SeclStartRpcServer() { DWORD dwResult; RPC_STATUS RpcStatus;
EnterCriticalSection(&csForProcessCount);
if (NULL != GlobalData) { // we are running under services.exe - we have to play nicely and share
// the process's RPC server. (The other services wouldn't be happy if we
// shut it down while they were still using it.)
RpcStatus = GlobalData->StartRpcServer(wszSeclogonSharedProcEndpointName, ISeclogon_v1_0_s_ifspec); _JumpCondition(RPC_S_OK != RpcStatus, StartRpcServerError); }
dwResult = ERROR_SUCCESS;
CommonReturn: LeaveCriticalSection(&csForProcessCount); return dwResult; ErrorReturn: goto CommonReturn; SET_DWRESULT(StartRpcServerError, RpcStatus); }
DWORD SeclStopRpcServer() { DWORD dwResult; RPC_STATUS RpcStatus;
EnterCriticalSection(&csForProcessCount);
if (NULL != GlobalData) { // we are running under services.exe - we have to play nicely and share
// the process's RPC server. (The other services wouldn't be happy if we
// shut it down while they were still using it.)
RpcStatus = GlobalData->StopRpcServer(ISeclogon_v1_0_s_ifspec); _JumpCondition(RPC_S_OK != RpcStatus, StopRpcServerError); }
dwResult = ERROR_SUCCESS; CommonReturn: LeaveCriticalSection(&csForProcessCount); return dwResult;
ErrorReturn: goto CommonReturn;
SET_DWRESULT(StopRpcServerError, RpcStatus); }
HRESULT To_LPWSTR(IN SECL_STRING *pss, OUT LPWSTR *ppwsz) { DWORD dwResult; HANDLE hHeap = NULL; LPWSTR pwsz = NULL; hHeap = GetProcessHeap(); _JumpCondition(NULL == hHeap, GetProcessHeapError);
__try { if (pss->ccLength > 0) { // Prevent large heap allocs:
_JumpCondition(pss->ccLength > 4096, InvalidArgError);
pwsz = (LPWSTR)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, pss->ccLength * sizeof(WCHAR)); _JumpCondition(NULL == pwsz, MemoryError); CopyMemory(pwsz, pss->pwsz, pss->ccLength * sizeof(WCHAR)); } } __except (EXCEPTION_EXECUTE_HANDLER) { dwResult = GetExceptionCode(); goto ExceptionError; }
*ppwsz = pwsz; dwResult = ERROR_SUCCESS;
CommonReturn: return dwResult;
ErrorReturn: if (NULL != pwsz) { HeapFree(hHeap, 0, pwsz); } goto CommonReturn;
SET_DWRESULT(ExceptionError, dwResult); SET_DWRESULT(GetProcessHeapError, GetLastError()); SET_DWRESULT(InvalidArgError, ERROR_INVALID_PARAMETER); SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY); }
void Free_SECONDARYLOGONINFOW(IN PSECONDARYLOGONINFOW psli) { HANDLE hHeap = GetProcessHeap();
if (NULL == hHeap) return;
if (NULL != psli) { if (NULL != psli->lpStartupInfo) { if (NULL != psli->lpStartupInfo->lpDesktop) { HeapFree(hHeap, 0, psli->lpStartupInfo->lpDesktop); } if (NULL != psli->lpStartupInfo->lpTitle) { HeapFree(hHeap, 0, psli->lpStartupInfo->lpTitle); } HeapFree(hHeap, 0, psli->lpStartupInfo); }
if (NULL != psli->lpUsername) { HeapFree(hHeap, 0, psli->lpUsername); } if (NULL != psli->lpDomain) { HeapFree(hHeap, 0, psli->lpDomain); } if (NULL != psli->lpPassword) { HeapFree(hHeap, 0, psli->lpPassword); } if (NULL != psli->lpApplicationName) { HeapFree(hHeap, 0, psli->lpApplicationName); } if (NULL != psli->lpCommandLine) { HeapFree(hHeap, 0, psli->lpCommandLine); } if (NULL != psli->lpCurrentDirectory) { HeapFree(hHeap, 0, (LPVOID)psli->lpCurrentDirectory); } if (NULL != psli->hWinsta && psli->fFreeWinsta) { CloseHandle(psli->hWinsta); } if (NULL != psli->hDesk && psli->fFreeDesk) { CloseHandle(psli->hDesk); } HeapFree(hHeap, 0, psli); } }
DWORD To_SECONDARYLOGONINFOW(IN PSECL_SLI pSeclSli, OUT PSECONDARYLOGONINFOW *ppsli) { DWORD dwAllocFlags = HEAP_ZERO_MEMORY; DWORD dwIndex; DWORD dwResult; HANDLE hHeap = NULL; PSECONDARYLOGONINFOW psli = NULL;
hHeap = GetProcessHeap(); _JumpCondition(NULL == hHeap, GetProcessHeapError);
psli = (PSECONDARYLOGONINFOW)HeapAlloc(hHeap, dwAllocFlags, sizeof(SECONDARYLOGONINFOW)); _JumpCondition(NULL == psli, MemoryError);
psli->lpStartupInfo = (LPSTARTUPINFO)HeapAlloc(hHeap, dwAllocFlags, sizeof(STARTUPINFO)); _JumpCondition(NULL == psli->lpStartupInfo, MemoryError);
__try { { struct { SECL_STRING *pss; LPWSTR *ppwsz; } rg_StringsToMap[] = { { &(pSeclSli->ssDesktop), /* Is mapped to ----> */ &(psli->lpStartupInfo->lpDesktop) }, { &(pSeclSli->ssTitle), /* Is mapped to ----> */ &(psli->lpStartupInfo->lpTitle) }, { &(pSeclSli->ssUsername), /* Is mapped to ----> */ &(psli->lpUsername) }, { &(pSeclSli->ssDomain), /* Is mapped to ----> */ &(psli->lpDomain) }, { &(pSeclSli->ssPassword), /* Is mapped to ----> */ &(psli->lpPassword) }, { &(pSeclSli->ssApplicationName), /* Is mapped to ----> */ &(psli->lpApplicationName) }, { &(pSeclSli->ssCommandLine), /* Is mapped to ----> */ &(psli->lpCommandLine) }, { &(pSeclSli->ssCurrentDirectory), /* Is mapped to ----> */ (LPWSTR *)&(psli->lpCurrentDirectory) } };
for (dwIndex = 0; dwIndex < ARRAYSIZE(rg_StringsToMap); dwIndex++) { dwResult = To_LPWSTR(rg_StringsToMap[dwIndex].pss, rg_StringsToMap[dwIndex].ppwsz); _JumpCondition(ERROR_SUCCESS != dwResult, To_LPWSTR_Error); } }
if (pSeclSli->sbEnvironment.cb > 0) { psli->lpEnvironment = HeapAlloc(hHeap, dwAllocFlags, pSeclSli->sbEnvironment.cb); _JumpCondition(NULL == psli->lpEnvironment, MemoryError); CopyMemory(psli->lpEnvironment, pSeclSli->sbEnvironment.pb, pSeclSli->sbEnvironment.cb); } } __except (EXCEPTION_EXECUTE_HANDLER) { dwResult = GetExceptionCode(); goto ExceptionError; }
psli->dwProcessId = pSeclSli->ulProcessId; psli->LogonIdLowPart = pSeclSli->ulLogonIdLowPart; psli->LogonIdHighPart = pSeclSli->lLogonIdHighPart; psli->dwLogonFlags = pSeclSli->ulLogonFlags; psli->dwCreationFlags = pSeclSli->ulCreationFlags; psli->dwSeclogonFlags = pSeclSli->ulSeclogonFlags; psli->hWinsta = (HANDLE)pSeclSli->hWinsta; psli->hDesk = (HANDLE)pSeclSli->hDesk;
*ppsli = psli; dwResult = ERROR_SUCCESS; CommonReturn: return dwResult;
ErrorReturn: Free_SECONDARYLOGONINFOW(psli); goto CommonReturn;
SET_DWRESULT(ExceptionError, dwResult); SET_DWRESULT(GetProcessHeapError, GetLastError()); SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY); SET_DWRESULT(To_LPWSTR_Error, dwResult); }
//////////////////////////////// End Of File /////////////////////////////////
|