/************************************************************************* * * 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 #include 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; }