|
|
/*************************************************************************
* * execsvr.c * * Remote CreateProcess server to allow programs to be started on a given * Session. Needed for OLE2 support. * * NOTE: Maybe this should be converted to RPC in the future when we * have more time so that it can be a more general facility. * * copyright notice: Microsoft, 1997 * * Author: * * *************************************************************************/
#define UNICODE 1
#include "precomp.h"
#pragma hdrstop
#include <execsrv.h>
#include <wincrypt.h>
HANDLE ExecThreadHandle = NULL; HANDLE ExecServerPipe = NULL;
static HANDLE ghUserToken = NULL; extern CRITICAL_SECTION GlobalsLock;
DWORD ExecServerThread( LPVOID lpThreadParameter );
BOOLEAN ProcessExecRequest( HANDLE hPipe, PCHAR pBuf, DWORD AmountRead );
HANDLE ImpersonateUser( HANDLE UserToken, HANDLE ThreadHandle );
BOOL StopImpersonating( HANDLE ThreadHandle );
HANDLE CreateExecSrvPipe( LPCTSTR lpPipeName );
/*****************************************************************************
* * CtxExecServerLogon * * Notify the Exec Server service that a user has logged on * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/
VOID CtxExecServerLogon( HANDLE hToken ) {
EnterCriticalSection( &GlobalsLock );
//
// Store information about the current user
// so we can create processes under their account
// as needed.
//
ghUserToken = hToken;
LeaveCriticalSection( &GlobalsLock ); }
/*****************************************************************************
* * CtxExecServerLogoff * * Notify the Exec Server service that a user has logged off * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/
VOID CtxExecServerLogoff() {
EnterCriticalSection( &GlobalsLock );
//
// Release information stored about the current logged
// on user.
//
ghUserToken = NULL;
LeaveCriticalSection( &GlobalsLock ); }
//-----------------------------------------------------
// Helper functions copied from SALEM
// (nt\termsrv\remdsk\server\sessmgr\helper.cpp)
//-----------------------------------------------------
DWORD GenerateRandomBytes( IN DWORD dwSize, IN OUT LPBYTE pbBuffer ) /*++
Description:
Generate fill buffer with random bytes.
Parameters:
dwSize : Size of buffer pbBuffer point to. pbBuffer : Pointer to buffer to hold the random bytes.
Returns:
TRUE/FALSE
--*/ { HCRYPTPROV hProv = (HCRYPTPROV)NULL; DWORD dwStatus = ERROR_SUCCESS;
//
// Create a Crypto Provider to generate random number
//
if( !CryptAcquireContext( &hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ) ) { dwStatus = GetLastError(); goto CLEANUPANDEXIT; }
if( !CryptGenRandom(hProv, dwSize, pbBuffer) ) { dwStatus = GetLastError(); }
CLEANUPANDEXIT:
if( (HCRYPTPROV)NULL != hProv ) { CryptReleaseContext( hProv, 0 ); }
return dwStatus; }
DWORD GenerateRandomString( IN DWORD dwSizeRandomSeed, IN OUT LPTSTR* pszRandomString ) /*++
--*/ { PBYTE lpBuffer = NULL; DWORD dwStatus = ERROR_SUCCESS; BOOL bSuccess; DWORD cbConvertString = 0;
if( 0 == dwSizeRandomSeed || NULL == pszRandomString ) { dwStatus = ERROR_INVALID_PARAMETER; ASSERT(FALSE); goto CLEANUPANDEXIT; }
*pszRandomString = NULL;
lpBuffer = (PBYTE)LocalAlloc( LPTR, dwSizeRandomSeed ); if( NULL == lpBuffer ) { dwStatus = GetLastError(); goto CLEANUPANDEXIT; }
dwStatus = GenerateRandomBytes( dwSizeRandomSeed, lpBuffer );
if( ERROR_SUCCESS != dwStatus ) { goto CLEANUPANDEXIT; }
// Convert to string
// cbConvertString will include the NULL character
bSuccess = CryptBinaryToString( lpBuffer, dwSizeRandomSeed, CRYPT_STRING_BASE64, NULL, &cbConvertString ); if( FALSE == bSuccess ) { dwStatus = GetLastError(); goto CLEANUPANDEXIT; }
*pszRandomString = (LPTSTR)LocalAlloc( LPTR, cbConvertString*sizeof(TCHAR) ); if( NULL == *pszRandomString ) { dwStatus = GetLastError(); goto CLEANUPANDEXIT; }
bSuccess = CryptBinaryToString( lpBuffer, dwSizeRandomSeed, CRYPT_STRING_BASE64, *pszRandomString, &cbConvertString ); if( FALSE == bSuccess ) { dwStatus = GetLastError(); } else { if( (*pszRandomString)[cbConvertString - 1] == '\n' && (*pszRandomString)[cbConvertString - 2] == '\r' ) { (*pszRandomString)[cbConvertString - 2] = 0; } }
CLEANUPANDEXIT:
if( ERROR_SUCCESS != dwStatus ) { if( NULL != *pszRandomString ) { LocalFree(*pszRandomString); *pszRandomString = NULL; } }
if( NULL != lpBuffer ) { LocalFree(lpBuffer); }
return dwStatus; }
/*****************************************************************************
* * StartExecServerThread * * Start the remote exec server thread. * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/
BOOLEAN StartExecServerThread() { DWORD ThreadId; BOOL Result; WCHAR szPipeName[EXECSRVPIPENAMELEN]; SECURITY_ATTRIBUTES SecurityAttributes; PSECURITY_ATTRIBUTES pSecurityAttributes = NULL; PSECURITY_DESCRIPTOR lpSecurityDescriptor = NULL; LPTSTR pszRandomString = NULL; ULONG RandomLen; DWORD dwStatus = ERROR_SUCCESS;
#if DBG
OutputDebugString (TEXT("EXECSERVERSYSTEM: Starting ExecServerThread\n")); #endif
RandomLen = sizeof(szPipeName)/sizeof(WCHAR) - 30;
dwStatus = GenerateRandomString( RandomLen, &pszRandomString ); if( ERROR_SUCCESS != dwStatus ) { return FALSE; }
// the string generated is always greater than what we ask
pszRandomString[RandomLen] = L'\0';
_snwprintf(&szPipeName[0], EXECSRVPIPENAMELEN, L"\\\\.\\Pipe\\TerminalServer\\%ws\\%d", pszRandomString, NtCurrentPeb()->SessionId); szPipeName[EXECSRVPIPENAMELEN-1] = L'\0';
ExecServerPipe = CreateExecSrvPipe( &szPipeName[0] ); if( ExecServerPipe == (HANDLE)-1 ) { OutputDebugString (TEXT("EXECSRV: Could not get pipe for ExecSrvr\n")); return( FALSE ); }
WinStationSetInformation( SERVERNAME_CURRENT, NtCurrentPeb()->SessionId, WinStationExecSrvSystemPipe, &szPipeName[0], sizeof(szPipeName) ); ExecThreadHandle = CreateThread( NULL, // Use default ACL
0, // Same stack size
ExecServerThread, // Start address
(LPVOID)ExecServerPipe, // Parameter
0, // Creation flags
&ThreadId // Get the id back here
);
if( ExecThreadHandle == NULL ) { OutputDebugString (TEXT("WLEXECSERVER: Could not create server thread Error\n")); return(FALSE); }
return(TRUE); }
/*****************************************************************************
* * ExecServerThread * * Thread that listens on the named pipe for remote exec service * requests and executes them. Passes the results back to the caller. * * ENTRY: * lpThreadParameter (input) * Handle to exec server pipe * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/
DWORD ExecServerThread( LPVOID lpThreadParameter ) { BOOL Result; DWORD AmountRead; CHAR *pBuf; HANDLE hPipe = (HANDLE)lpThreadParameter;
pBuf = LocalAlloc(LMEM_FIXED, EXECSRV_BUFFER_SIZE );
if (pBuf == NULL) { OutputDebugString (TEXT("WLEXECSERVER: ExecServerThread : nomemory \n")); return(STATUS_NO_MEMORY); }
while( 1 ) {
// read the pipe for a request (pipe is in message mode)
Result = ConnectNamedPipe( hPipe, NULL ); if( !Result ) { OutputDebugString (TEXT("WLEXECSERVER: ConnectNamePipe failed\n")); LocalFree( pBuf ); return(FALSE); }
// read the request from the pipe
Result = ReadFile( hPipe, pBuf, EXECSRV_BUFFER_SIZE, &AmountRead, NULL );
if( Result ) { ProcessExecRequest( hPipe, pBuf, AmountRead ); } else { OutputDebugString (TEXT("WLEXECSERVER: Error reading pipe after connect\n")); // Could handle the to big error, but this means mismatched client
}
// wait until the client reads out the reply
Result = FlushFileBuffers( hPipe ); #if DBG
if( Result == 0 ) { OutputDebugString (TEXT("EXECSRV: FlushFileBuffers failed! \n")); } #endif
// disconnect the name pipe
Result = DisconnectNamedPipe( hPipe ); #if DBG
if( Result == 0 ) { OutputDebugString (TEXT("EXECSRV: Disconnect Named Pipe failed! Error \n")); } #endif
} }
/*****************************************************************************
* * ProcessExecRequest * * Do the work of processing a remote exec request * * ENTRY: * hPipe (input) * Pipe handle for reply * * pBuf (input) * Request buffer * * AmountRead (input) * Amount in request buffer * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/
BOOLEAN ProcessExecRequest( HANDLE hPipe, PCHAR pBuf, DWORD AmountRead ) { DWORD AmountWrote; BOOL Result; HANDLE ImpersonationHandle = NULL; SECURITY_ATTRIBUTES saProcess; EXECSRV_REPLY Rep; HANDLE LocalProc = NULL; HANDLE RemoteProc = NULL; HANDLE LocalhProcess = NULL; HANDLE LocalhThread = NULL; PEXECSRV_REQUEST p = (PEXECSRV_REQUEST)pBuf; LPVOID lpEnvironment = NULL; HANDLE hUserToken=NULL; BOOL bEnvBlockCreatedLocally = FALSE;
#if DBG
KdPrint(("WLEXECSERVER: AmountRead = %d, pBuf->Size= %d \n", AmountRead, p->Size )); #endif
RtlZeroMemory(&Rep,sizeof(EXECSRV_REPLY));
if( AmountRead < sizeof(EXECSRV_REQUEST) ) { // drop the request
OutputDebugString (TEXT("WLEXECSERVER: BAD EXECSRV Request size (WinLogon)\n")); return(FALSE); }
// normalize the pointers
if( p->lpszImageName ) { p->lpszImageName = (PWCHAR)(((ULONG_PTR)p->lpszImageName) + pBuf); if( ( (PCHAR)p->lpszImageName > (PCHAR)(pBuf+AmountRead)) || ((PCHAR)p->lpszImageName < (PCHAR)(pBuf + sizeof(EXECSRV_REQUEST))) ) { OutputDebugString (TEXT("WLEXECSERVER: Invalid image name pointer\n")); // drop the request
return(FALSE); } }
if( p->lpszCommandLine ) { p->lpszCommandLine = (PWCHAR)(((ULONG_PTR)p->lpszCommandLine) + pBuf); if( ((PCHAR)p->lpszCommandLine > (PCHAR)(pBuf+AmountRead)) || ((PCHAR)p->lpszCommandLine < (PCHAR)(pBuf + sizeof(EXECSRV_REQUEST)))) { OutputDebugString (TEXT("WLEXECSERVER: Invalid command line pointer\n")); // drop the request
return(FALSE); } }
if( p->lpszCurDir ) { p->lpszCurDir = (PWCHAR)(((ULONG_PTR)p->lpszCurDir) + pBuf); if( ((PCHAR)p->lpszCurDir > (PCHAR)(pBuf+AmountRead)) || ((PCHAR)p->lpszCurDir < (PCHAR)(pBuf + sizeof(EXECSRV_REQUEST))) ) { OutputDebugString (TEXT("WLEXECSERVER: Invalid CurDir pointer\n")); // drop the request
return(FALSE); } }
if( p->StartInfo.lpDesktop ) { p->StartInfo.lpDesktop = (PWCHAR)(((ULONG_PTR)p->StartInfo.lpDesktop) + pBuf); if( ((PCHAR)p->StartInfo.lpDesktop > (PCHAR)(pBuf+AmountRead)) || ((PCHAR)p->StartInfo.lpDesktop < (PCHAR)(pBuf + sizeof(EXECSRV_REQUEST))) ) { OutputDebugString (TEXT("WLEXECSERVER: Invalid StartInfo.lpDesktop pointer\n")); // drop the request
return(FALSE); } }
if( p->StartInfo.lpTitle ) { p->StartInfo.lpTitle = (PWCHAR)(((ULONG_PTR)p->StartInfo.lpTitle) + pBuf); if( ((PCHAR)p->StartInfo.lpTitle > (PCHAR)(pBuf+AmountRead)) || ((PCHAR)p->StartInfo.lpTitle < (PCHAR)(pBuf + sizeof(EXECSRV_REQUEST))) ) { OutputDebugString (TEXT("WLEXECSERVER: Invalid StartInfo.lpTitle pointer\n")); // drop the request
return(FALSE); } }
if (p->lpvEnvironment ) { p->lpvEnvironment = (PWCHAR)(((ULONG_PTR)p->lpvEnvironment) + pBuf); if( ((PCHAR)p->lpvEnvironment > (PCHAR)(pBuf+AmountRead)) || ((PCHAR)p->lpvEnvironment < (PCHAR)(pBuf + sizeof(EXECSRV_REQUEST))) ) { OutputDebugString (TEXT("WLEXECSERVER: Invalid env pointer\n")); // drop the request
return(FALSE); } }
// We do not know what the reserved is, so make sure it is NULL
p->StartInfo.lpReserved = NULL;
//if( p->lpszImageName )
//OutputDebugString (TEXT("WLEXECSERVER: Got request ImageName :%ws:\n", p->lpszImageName));
//if( p->lpszCommandLine )
//OutputDebugString (TEXT("WLEXECSERVER: Got request command line :%ws:\n", p->lpszCommandLine));
//OutputDebugString (TEXT("WLEXECSERVER: CreateFlags 0x%x\n",p->fdwCreate));
//OutputDebugString (TEXT("System Flag 0x%x\n",p->System));
//
// Can only service user security level requests when a user is logged on.
//
if( !p->System ) {
EnterCriticalSection( &GlobalsLock );
if (ghUserToken == NULL) { #if DBG
OutputDebugString (TEXT("WLEXECSERVER: No USER Logged On for USER CreateProcess Request!\n")); #endif
LeaveCriticalSection( &GlobalsLock ); return( FALSE ); }
//
// We need to open the remote process in order to duplicate the user token.
// But for that, we need to impersonate the named pipe client.
//
if ( ImpersonateNamedPipeClient( hPipe ) == 0 ) { LeaveCriticalSection( &GlobalsLock ); return( FALSE ); }
//
// Get the handle to remote process
//
RemoteProc = OpenProcess( PROCESS_DUP_HANDLE|PROCESS_QUERY_INFORMATION, FALSE, // no inherit
p->RequestingProcessId );
if( RemoteProc == NULL ) { OutputDebugString (TEXT("WLEXECSERVER: Could not get handle to remote process \n")); //
// on retail builds we can not duplicate a handle into
// service controller process
//
// The handles are not used by SCM right now, we must
// have another way to pass handles if this function
// is used by other services.
//
ASSERT( FALSE ); // Should not happen in WinLogon
RevertToSelf(); // Imperonation of named pipe client had succeeded.
LeaveCriticalSection( &GlobalsLock ); goto ReturnError; }
if ( !RevertToSelf() ) { ASSERT( FALSE ); // This RevertToSelf should not fail.
LeaveCriticalSection( &GlobalsLock ); return( FALSE ); }
//
// Get the handle to current process
//
LocalProc = OpenProcess( PROCESS_DUP_HANDLE|PROCESS_QUERY_INFORMATION, FALSE, // no inherit
GetCurrentProcessId() );
if( LocalProc == NULL ) { OutputDebugString (TEXT("WLEXECSERVER: Could not get handle to local process\n")); LeaveCriticalSection( &GlobalsLock ); goto ReturnError; }
// decide if we are creating the new process for the currntly logged in user, or a
// new user
if (p->hToken) { //
// we are dealing with a new user, for which we have a token comming from
// services.exe (for SecLogon)
//
Result = DuplicateHandle( RemoteProc, // Source of the handle (us)
p->hToken, // Source handle
LocalProc, // Target of the handle
&hUserToken, // Target handle
0, // ignored since DUPLICATE_SAME_ACCESS is set
FALSE, // no inherit on the handle
DUPLICATE_SAME_ACCESS );
if( !Result ) { OutputDebugString (TEXT("WLEXECSERVER: Error duping process handle to target process\n")); LeaveCriticalSection( &GlobalsLock ); goto ReturnError; }
} else { hUserToken=ghUserToken; // currently logged in user
}
lpEnvironment = p->lpvEnvironment ;
//
// Create Environment Block if we have none
//
if ( !lpEnvironment ) { if (!CreateEnvironmentBlock (&lpEnvironment, hUserToken, FALSE)) { KdPrint(("WLEXECSERVER: CreateEnvironmentBlock() Failed\n")); } else { bEnvBlockCreatedLocally = TRUE; } }
//
// If we are to run the process under USER security, impersonate
// the user.
//
// This will also access check the users access to the exe image as well.
//
ImpersonationHandle = ImpersonateUser(hUserToken, NULL ); if (ImpersonationHandle == NULL) { OutputDebugString (TEXT("WLEXECSERVER: failed to impersonate user\n")); LeaveCriticalSection( &GlobalsLock ); goto ReturnError; }
LeaveCriticalSection( &GlobalsLock );
// this environment block is UNICODE
p->fdwCreate |= CREATE_UNICODE_ENVIRONMENT;
Result = CreateProcessAsUserW( hUserToken, p->lpszImageName, p->lpszCommandLine, NULL, // &saProcess,
NULL, // &p->saThread
p->fInheritHandles, p->fdwCreate, lpEnvironment, p->lpszCurDir, &p->StartInfo, &Rep.ProcInfo );
if ( bEnvBlockCreatedLocally ) { DestroyEnvironmentBlock (lpEnvironment); } } else { // If creating system, force separate WOW
p->fdwCreate |= CREATE_SEPARATE_WOW_VDM;
// CreateProcessAsUser() does not take a NULL token for SYSTEM
Result = CreateProcessW( p->lpszImageName, p->lpszCommandLine, NULL, // &saProcess,
NULL, // &p->saThread
p->fInheritHandles, p->fdwCreate, NULL, //p->lpvEnvironment
p->lpszCurDir, &p->StartInfo, &Rep.ProcInfo ); }
if( !Result ){ if( ImpersonationHandle ) { StopImpersonating(ImpersonationHandle); } //
// Rep.Result = FALSE;
// Rep.LastError = GetLastError();
// Result = WriteFile( hPipe, &Rep, sizeof(Rep), &AmountWrote, NULL );
// OutputDebugString (TEXT("WLEXECSERVER: Error in CreateProcess\n"));
// return(FALSE);
goto ReturnError; }
// Stop impersonating the process
if( ImpersonationHandle ) { StopImpersonating(ImpersonationHandle); }
if (!Result) { OutputDebugString (TEXT("ExecServer: failed to resume new process thread\n")); CloseHandle(Rep.ProcInfo.hProcess); CloseHandle(Rep.ProcInfo.hThread); goto ReturnError; }
//
// do any tricky handle DUP stuff
//
LocalhProcess = Rep.ProcInfo.hProcess; LocalhThread = Rep.ProcInfo.hThread;
Result = DuplicateHandle( LocalProc, // Source of the handle (us)
Rep.ProcInfo.hProcess, // Source handle
RemoteProc, // Target of the handle
&Rep.ProcInfo.hProcess, // Target handle
0, // ignored since DUPLICATE_SAME_ACCESS is set
FALSE, // no inherit on the handle
DUPLICATE_SAME_ACCESS );
if( !Result ) { OutputDebugString (TEXT("WLEXECSERVER: Error duping process handle to target process\n")); }
//
// If the program got launched into the shared WOW virtual machine,
// then the hThread will be NULL.
//
if( Rep.ProcInfo.hThread != NULL ) { Result = DuplicateHandle( LocalProc, // Source of the handle (us)
Rep.ProcInfo.hThread, // Source handle
RemoteProc, // Target of the handle
&Rep.ProcInfo.hThread, // Target handle
0, // ignored since DUPLICATE_SAME_ACCESS is set
FALSE, // no inherit on the handle
DUPLICATE_SAME_ACCESS );
if( !Result ) { //OutputDebugString (TEXT("WLEXECSERVER: Error %d duping thread handle to target process, Handle 0x%x, ThreadId 0x%x\n",GetLastError(),Rep.ProcInfo.hThread,Rep.ProcInfo.dwThreadId));
} }
//OutputDebugString (TEXT("WLXEXECSERVER: Success for %d type exec\n",p->System));
//
// build the reply packet with the handle valid in the context
// of the requesting process
//
Rep.Result = TRUE; Rep.LastError = 0; Result = WriteFile( hPipe, &Rep, sizeof(Rep), &AmountWrote, NULL );
if( !Result ) { OutputDebugString (TEXT("WLEXECSERVER: Error sending reply \n")); }
//
// close our versions of the handles. The requestors references
// are now the main ones
//
if( LocalProc != NULL ) CloseHandle( LocalProc );
if( RemoteProc != NULL ) CloseHandle( RemoteProc );
if( LocalhProcess != NULL ) CloseHandle( LocalhProcess );
if( LocalhThread != NULL ) CloseHandle( LocalhThread );
if (hUserToken && (hUserToken != ghUserToken)) { CloseHandle( hUserToken ); } return (BOOLEAN)Result;
ReturnError: Rep.Result = FALSE; Rep.LastError = GetLastError();
//OutputDebugString (TEXT("WLXEXECSERVER: Error %d for %d type exec\n",Rep.LastError,p->System));
Result = WriteFile( hPipe, &Rep, sizeof(Rep), &AmountWrote, NULL );
if( LocalProc != NULL ) CloseHandle( LocalProc );
if( RemoteProc != NULL ) CloseHandle( RemoteProc );
if( LocalhProcess != NULL ) CloseHandle( LocalhProcess );
if( LocalhThread != NULL ) CloseHandle( LocalhThread );
if (hUserToken && (hUserToken != ghUserToken) ) { CloseHandle( hUserToken ); } return (BOOLEAN)Result; }
/***************************************************************************\
* FUNCTION: ImpersonateUser * * PURPOSE: Impersonates the user by setting the users token * on the specified thread. If no thread is specified the token * is set on the current thread. * * RETURNS: Handle to be used on call to StopImpersonating() or NULL on failure * If a non-null thread handle was passed in, the handle returned will * be the one passed in. (See note) * * NOTES: Take care when passing in a thread handle and then calling * StopImpersonating() with the handle returned by this routine. * StopImpersonating() will close any thread handle passed to it - * even yours ! * * \***************************************************************************/
HANDLE ImpersonateUser( HANDLE UserToken, HANDLE ThreadHandle ) { NTSTATUS Status, IgnoreStatus; SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE ImpersonationToken; BOOL ThreadHandleOpened = FALSE;
if (ThreadHandle == NULL) {
//
// Get a handle to the current thread.
// Once we have this handle, we can set the user's impersonation
// token into the thread and remove it later even though we ARE
// the user for the removal operation. This is because the handle
// contains the access rights - the access is not re-evaluated
// at token removal time.
//
Status = NtDuplicateObject( NtCurrentProcess(), // Source process
NtCurrentThread(), // Source handle
NtCurrentProcess(), // Target process
&ThreadHandle, // Target handle
THREAD_SET_THREAD_TOKEN,// Access
0L, // Attributes
DUPLICATE_SAME_ATTRIBUTES ); if (!NT_SUCCESS(Status)) { KdPrint(("ImpersonateUser : Failed to duplicate thread handle, status = 0x%lx", Status)); return(NULL); }
ThreadHandleOpened = TRUE; }
//
// If the usertoken is NULL, there's nothing to do
//
if (UserToken != NULL) {
//
// UserToken is a primary token - create an impersonation token version
// of it so we can set it on our thread
//
InitializeObjectAttributes( &ObjectAttributes, NULL, 0L, NULL, //UserProcessData->NewThreadTokenSD);
NULL);
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation; SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; SecurityQualityOfService.EffectiveOnly = FALSE;
ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;
Status = NtDuplicateToken( UserToken, TOKEN_IMPERSONATE | TOKEN_READ, &ObjectAttributes, FALSE, TokenImpersonation, &ImpersonationToken ); if (!NT_SUCCESS(Status)) {
KdPrint(("Failed to duplicate users token to create" " impersonation thread, status = 0x%lx\n", Status));
if (ThreadHandleOpened) { IgnoreStatus = NtClose(ThreadHandle); ASSERT(NT_SUCCESS(IgnoreStatus)); }
return(NULL); }
//
// Set the impersonation token on this thread so we 'are' the user
//
Status = NtSetInformationThread( ThreadHandle, ThreadImpersonationToken, (PVOID)&ImpersonationToken, sizeof(ImpersonationToken) ); //
// We're finished with our handle to the impersonation token
//
IgnoreStatus = NtClose(ImpersonationToken); ASSERT(NT_SUCCESS(IgnoreStatus));
//
// Check we set the token on our thread ok
//
if (!NT_SUCCESS(Status)) {
KdPrint(( "Failed to set user impersonation token on winlogon thread, status = 0x%lx", Status));
if (ThreadHandleOpened) { IgnoreStatus = NtClose(ThreadHandle); ASSERT(NT_SUCCESS(IgnoreStatus)); }
return(NULL); }
}
return(ThreadHandle);
}
/***************************************************************************\
* FUNCTION: StopImpersonating * * PURPOSE: Stops impersonating the client by removing the token on the * current thread. * * PARAMETERS: ThreadHandle - handle returned by ImpersonateUser() call. * * RETURNS: TRUE on success, FALSE on failure * * NOTES: If a thread handle was passed in to ImpersonateUser() then the * handle returned was one and the same. If this is passed to * StopImpersonating() the handle will be closed. Take care ! * * HISTORY: * * 04-21-92 Davidc Created. * \***************************************************************************/
BOOL StopImpersonating( HANDLE ThreadHandle ) { NTSTATUS Status, IgnoreStatus; HANDLE ImpersonationToken;
//
// Remove the user's token from our thread so we are 'ourself' again
//
ImpersonationToken = NULL;
Status = NtSetInformationThread( ThreadHandle, ThreadImpersonationToken, (PVOID)&ImpersonationToken, sizeof(ImpersonationToken) ); //
// We're finished with the thread handle
//
IgnoreStatus = NtClose(ThreadHandle); ASSERT(NT_SUCCESS(IgnoreStatus));
if (!NT_SUCCESS(Status)) { KdPrint(("Failed to remove user impersonation token from winlogon thread, status = 0x%lx", Status)); }
return(NT_SUCCESS(Status)); }
//---------------------------------------------------------------------------------------
//
// CreateExecSrvPipe
// Creates exec server named pipe with appropriate DACL. It allows access only to local
// system, local service and network service.
// It return handle to the newly created pipe. If the operation fails, it returns
// INVALID_HANDLE_VALUE.
//
//---------------------------------------------------------------------------------------
HANDLE CreateExecSrvPipe( LPCTSTR lpPipeName ) { HANDLE hPipe = INVALID_HANDLE_VALUE; NTSTATUS Status; SECURITY_ATTRIBUTES SecAttr; PSID pSystemSid = NULL; PSID pLocalServiceSid = NULL; PSID pNetworkServiceSid = NULL; PSECURITY_DESCRIPTOR pSd = NULL; PACL pDacl; ULONG AclLength; SID_IDENTIFIER_AUTHORITY SystemAuth = SECURITY_NT_AUTHORITY;
// Allocate and Initialize the "System" Sid.
Status = RtlAllocateAndInitializeSid( &SystemAuth, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &pSystemSid ); if (!NT_SUCCESS(Status)) { goto CreatePipeErr; }
// Allocate and Initialize the "Local Service" Sid.
Status = RtlAllocateAndInitializeSid( &SystemAuth, 1, SECURITY_LOCAL_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, &pLocalServiceSid ); if (!NT_SUCCESS(Status)) { goto CreatePipeErr; }
// Allocate and Initialize the "Network Service" Sid.
Status = RtlAllocateAndInitializeSid( &SystemAuth, 1, SECURITY_NETWORK_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, &pNetworkServiceSid ); if (!NT_SUCCESS(Status)) { goto CreatePipeErr; }
// Allocate space for the security descriptor.
AclLength = (ULONG)sizeof(ACL) + 3 * sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid( pSystemSid ) + RtlLengthSid( pLocalServiceSid ) + RtlLengthSid( pNetworkServiceSid ) - 3 * sizeof( ULONG );
pSd = (PSECURITY_DESCRIPTOR) LocalAlloc( LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH + AclLength ); if (pSd == NULL) { goto CreatePipeErr; }
pDacl = (PACL) ((BYTE*)(pSd) + SECURITY_DESCRIPTOR_MIN_LENGTH);
// Set up a new ACL with no ACE.
Status = RtlCreateAcl( pDacl, AclLength, ACL_REVISION2 ); if ( !NT_SUCCESS(Status) ) { goto CreatePipeErr; }
// System access
Status = RtlAddAccessAllowedAce( pDacl, ACL_REVISION2, GENERIC_READ | GENERIC_WRITE, pSystemSid ); if (!NT_SUCCESS(Status)) { goto CreatePipeErr; }
// Local Service access
Status = RtlAddAccessAllowedAce( pDacl, ACL_REVISION2, GENERIC_READ | GENERIC_WRITE, pLocalServiceSid ); if (!NT_SUCCESS(Status)) { goto CreatePipeErr; }
// Network Service access
Status = RtlAddAccessAllowedAce( pDacl, ACL_REVISION2, GENERIC_READ | GENERIC_WRITE, pNetworkServiceSid ); if (!NT_SUCCESS(Status)) { goto CreatePipeErr; }
// Now initialize security descriptors that export this protection
Status = RtlCreateSecurityDescriptor(pSd, SECURITY_DESCRIPTOR_REVISION1); if (!NT_SUCCESS(Status)) { goto CreatePipeErr; }
Status = RtlSetDaclSecurityDescriptor(pSd, TRUE, pDacl, FALSE); if (!NT_SUCCESS(Status)) { goto CreatePipeErr; }
// Fill the Security Attributes
ZeroMemory(&SecAttr, sizeof(SecAttr)); SecAttr.nLength = sizeof(SECURITY_ATTRIBUTES); SecAttr.lpSecurityDescriptor = pSd; SecAttr.bInheritHandle = FALSE;
hPipe = CreateNamedPipeW( lpPipeName, PIPE_ACCESS_DUPLEX, PIPE_WAIT | PIPE_READMODE_MESSAGE | PIPE_TYPE_MESSAGE, PIPE_UNLIMITED_INSTANCES, EXECSRV_BUFFER_SIZE, EXECSRV_BUFFER_SIZE, 0, &SecAttr );
// It's very unlikely, but still make sure that pipe with this name does not exist.
if (GetLastError() == ERROR_ALREADY_EXISTS) { NtClose( hPipe ); hPipe = INVALID_HANDLE_VALUE; }
CreatePipeErr:
// Free the SIDs
if ( pSystemSid ) { RtlFreeSid( pSystemSid ); }
if ( pLocalServiceSid ) { RtlFreeSid( pLocalServiceSid ); }
if ( pNetworkServiceSid ) { RtlFreeSid( pNetworkServiceSid ); }
if (pSd) { LocalFree(pSd); }
return hPipe; }
|