VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/win/process-win.cpp@ 57845

Last change on this file since 57845 was 57845, checked in by vboxsync, 9 years ago

RTProcCreateEx/win: uninitialized rc fix and made the CreateProcessWithLogon call respect RTPROC_FLAGS_PROFILE.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 59.1 KB
Line 
1/* $Id: process-win.cpp 57845 2015-09-21 16:33:36Z vboxsync $ */
2/** @file
3 * IPRT - Process, Windows.
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_PROCESS
32#include <iprt/asm.h> /* hack */
33
34#include <Userenv.h>
35#include <Windows.h>
36#include <tlhelp32.h>
37#include <process.h>
38#include <errno.h>
39#include <Strsafe.h>
40#include <Lmcons.h>
41
42#include <iprt/process.h>
43#include "internal-r3-win.h"
44
45#include <iprt/assert.h>
46#include <iprt/critsect.h>
47#include <iprt/file.h>
48#include <iprt/err.h>
49#include <iprt/env.h>
50#include <iprt/getopt.h>
51#include <iprt/initterm.h>
52#include <iprt/ldr.h>
53#include <iprt/log.h>
54#include <iprt/mem.h>
55#include <iprt/once.h>
56#include <iprt/path.h>
57#include <iprt/pipe.h>
58#include <iprt/string.h>
59#include <iprt/socket.h>
60
61
62
63/*********************************************************************************************************************************
64* Structures and Typedefs *
65*********************************************************************************************************************************/
66/* kernel32.dll: */
67//typedef DWORD (WINAPI *PFNWTSGETACTIVECONSOLESESSIONID)(VOID);
68typedef HANDLE (WINAPI *PFNCREATETOOLHELP32SNAPSHOT)(DWORD, DWORD);
69typedef BOOL (WINAPI *PFNPROCESS32FIRST)(HANDLE, LPPROCESSENTRY32);
70typedef BOOL (WINAPI *PFNPROCESS32FIRSTW)(HANDLE, LPPROCESSENTRY32W);
71typedef BOOL (WINAPI *PFNPROCESS32NEXT)(HANDLE, LPPROCESSENTRY32);
72typedef BOOL (WINAPI *PFNPROCESS32NEXTW)(HANDLE, LPPROCESSENTRY32W);
73
74/* psapi.dll: */
75typedef BOOL (WINAPI *PFNENUMPROCESSES)(LPDWORD, DWORD, LPDWORD);
76typedef DWORD (WINAPI *PFNGETMODULEBASENAME)(HANDLE, HMODULE, LPTSTR, DWORD);
77
78/* advapi32.dll: */
79typedef BOOL (WINAPI *PFNCREATEPROCESSWITHLOGON)(LPCWSTR, LPCWSTR, LPCWSTR, DWORD, LPCWSTR, LPWSTR, DWORD,
80 LPVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION);
81
82/* userenv.dll: */
83typedef BOOL (WINAPI *PFNCREATEENVIRONMENTBLOCK)(LPVOID *, HANDLE, BOOL);
84typedef BOOL (WINAPI *PFNPFNDESTROYENVIRONMENTBLOCK)(LPVOID);
85typedef BOOL (WINAPI *PFNLOADUSERPROFILEW)(HANDLE, LPPROFILEINFOW);
86typedef BOOL (WINAPI *PFNUNLOADUSERPROFILE)(HANDLE, HANDLE);
87
88
89/*********************************************************************************************************************************
90* Global Variables *
91*********************************************************************************************************************************/
92/** Init once structure. */
93static RTONCE g_rtProcWinInitOnce = RTONCE_INITIALIZER;
94/** Critical section protecting the process array. */
95static RTCRITSECT g_CritSect;
96/** The number of processes in the array. */
97static uint32_t g_cProcesses;
98/** The current allocation size. */
99static uint32_t g_cProcessesAlloc;
100/** Array containing the live or non-reaped child processes. */
101static struct RTPROCWINENTRY
102{
103 /** The process ID. */
104 ULONG_PTR pid;
105 /** The process handle. */
106 HANDLE hProcess;
107} *g_paProcesses;
108
109/** @name userenv.dll imports (we don't unload it).
110 * They're all optional. So in addition to using g_rtProcWinResolveOnce, the
111 * caller must also check if any of the necessary APIs are NULL pointers.
112 * @{ */
113/** Init once structure for run-as-user functions we need.. */
114static RTONCE g_rtProcWinResolveOnce = RTONCE_INITIALIZER;
115/* kernel32.dll: */
116static PFNCREATETOOLHELP32SNAPSHOT g_pfnCreateToolhelp32Snapshot = NULL;
117static PFNPROCESS32FIRST g_pfnProcess32First = NULL;
118static PFNPROCESS32NEXT g_pfnProcess32Next = NULL;
119static PFNPROCESS32FIRSTW g_pfnProcess32FirstW = NULL;
120static PFNPROCESS32NEXTW g_pfnProcess32NextW = NULL;
121/* psapi.dll: */
122static PFNGETMODULEBASENAME g_pfnGetModuleBaseName = NULL;
123static PFNENUMPROCESSES g_pfnEnumProcesses = NULL;
124/* advapi32.dll: */
125static PFNCREATEPROCESSWITHLOGON g_pfnCreateProcessWithLogonW = NULL;
126/* userenv.dll: */
127static PFNCREATEENVIRONMENTBLOCK g_pfnCreateEnvironmentBlock = NULL;
128static PFNPFNDESTROYENVIRONMENTBLOCK g_pfnDestroyEnvironmentBlock = NULL;
129static PFNLOADUSERPROFILEW g_pfnLoadUserProfileW = NULL;
130static PFNUNLOADUSERPROFILE g_pfnUnloadUserProfile = NULL;
131/** @} */
132
133
134/*********************************************************************************************************************************
135* Internal Functions *
136*********************************************************************************************************************************/
137static int rtProcWinFindExe(uint32_t fFlags, RTENV hEnv, const char *pszExec, PRTUTF16 *ppwszExec);
138
139
140/**
141 * Clean up the globals.
142 *
143 * @param enmReason Ignored.
144 * @param iStatus Ignored.
145 * @param pvUser Ignored.
146 */
147static DECLCALLBACK(void) rtProcWinTerm(RTTERMREASON enmReason, int32_t iStatus, void *pvUser)
148{
149 NOREF(pvUser); NOREF(iStatus); NOREF(enmReason);
150
151 RTCritSectDelete(&g_CritSect);
152
153 size_t i = g_cProcesses;
154 while (i-- > 0)
155 {
156 CloseHandle(g_paProcesses[i].hProcess);
157 g_paProcesses[i].hProcess = NULL;
158 }
159 RTMemFree(g_paProcesses);
160
161 g_paProcesses = NULL;
162 g_cProcesses = 0;
163 g_cProcessesAlloc = 0;
164}
165
166
167/**
168 * Initialize the globals.
169 *
170 * @returns IPRT status code.
171 * @param pvUser Ignored.
172 */
173static DECLCALLBACK(int32_t) rtProcWinInitOnce(void *pvUser)
174{
175 NOREF(pvUser);
176
177 g_cProcesses = 0;
178 g_cProcessesAlloc = 0;
179 g_paProcesses = NULL;
180 int rc = RTCritSectInit(&g_CritSect);
181 if (RT_SUCCESS(rc))
182 {
183 /** @todo init once, terminate once - this is a generic thing which should
184 * have some kind of static and simpler setup! */
185 rc = RTTermRegisterCallback(rtProcWinTerm, NULL);
186 if (RT_SUCCESS(rc))
187 return rc;
188 RTCritSectDelete(&g_CritSect);
189 }
190 return rc;
191}
192
193
194/**
195 * Gets the process handle for a process from g_paProcesses.
196 *
197 * @returns Process handle if found, NULL if not.
198 * @param pid The process to remove (pid).
199 */
200static HANDLE rtProcWinFindPid(RTPROCESS pid)
201{
202 HANDLE hProcess = NULL;
203
204 RTCritSectEnter(&g_CritSect);
205 uint32_t i = g_cProcesses;
206 while (i-- > 0)
207 if (g_paProcesses[i].pid == pid)
208 {
209 hProcess = g_paProcesses[i].hProcess;
210 break;
211 }
212 RTCritSectLeave(&g_CritSect);
213
214 return hProcess;
215}
216
217
218/**
219 * Removes a process from g_paProcesses and closes the process handle.
220 *
221 * @param pid The process to remove (pid).
222 */
223static void rtProcWinRemovePid(RTPROCESS pid)
224{
225 RTCritSectEnter(&g_CritSect);
226 uint32_t i = g_cProcesses;
227 while (i-- > 0)
228 if (g_paProcesses[i].pid == pid)
229 {
230 HANDLE hProcess = g_paProcesses[i].hProcess;
231
232 g_cProcesses--;
233 uint32_t cToMove = g_cProcesses - i;
234 if (cToMove)
235 memmove(&g_paProcesses[i], &g_paProcesses[i + 1], cToMove * sizeof(g_paProcesses[0]));
236
237 RTCritSectLeave(&g_CritSect);
238 CloseHandle(hProcess);
239 return;
240 }
241 RTCritSectLeave(&g_CritSect);
242}
243
244
245/**
246 * Adds a process to g_paProcesses.
247 *
248 * @returns IPRT status code.
249 * @param pid The process id.
250 * @param hProcess The process handle.
251 */
252static int rtProcWinAddPid(RTPROCESS pid, HANDLE hProcess)
253{
254 RTCritSectEnter(&g_CritSect);
255
256 uint32_t i = g_cProcesses;
257 if (i >= g_cProcessesAlloc)
258 {
259 void *pvNew = RTMemRealloc(g_paProcesses, (i + 16) * sizeof(g_paProcesses[0]));
260 if (RT_UNLIKELY(!pvNew))
261 {
262 RTCritSectLeave(&g_CritSect);
263 return VERR_NO_MEMORY;
264 }
265 g_paProcesses = (struct RTPROCWINENTRY *)pvNew;
266 g_cProcessesAlloc = i + 16;
267 }
268
269 g_paProcesses[i].pid = pid;
270 g_paProcesses[i].hProcess = hProcess;
271 g_cProcesses = i + 1;
272
273 RTCritSectLeave(&g_CritSect);
274 return VINF_SUCCESS;
275}
276
277
278/**
279 * Initialize the import APIs for run-as-user and special environment support.
280 *
281 * @returns IPRT status code.
282 * @param pvUser Ignored.
283 */
284static DECLCALLBACK(int) rtProcWinResolveOnce(void *pvUser)
285{
286 int rc;
287 RTLDRMOD hMod;
288
289 /*
290 * kernel32.dll APIs introduced after NT4.
291 */
292 g_pfnCreateToolhelp32Snapshot = (PFNCREATETOOLHELP32SNAPSHOT)GetProcAddress(g_hModKernel32, "CreateToolhelp32Snapshot");
293 g_pfnProcess32First = (PFNPROCESS32FIRST )GetProcAddress(g_hModKernel32, "Process32First");
294 g_pfnProcess32FirstW = (PFNPROCESS32FIRSTW )GetProcAddress(g_hModKernel32, "Process32FirstW");
295 g_pfnProcess32Next = (PFNPROCESS32NEXT )GetProcAddress(g_hModKernel32, "Process32Next");
296 g_pfnProcess32NextW = (PFNPROCESS32NEXTW )GetProcAddress(g_hModKernel32, "Process32NextW");
297
298 /*
299 * psapi.dll APIs, if none of the above are available.
300 */
301 if ( !g_pfnCreateToolhelp32Snapshot
302 || !g_pfnProcess32First
303 || !g_pfnProcess32Next)
304 {
305 Assert(!g_pfnCreateToolhelp32Snapshot && !g_pfnProcess32First && !g_pfnProcess32Next);
306
307 rc = RTLdrLoadSystem("psapi.dll", true /*fNoUnload*/, &hMod);
308 if (RT_SUCCESS(rc))
309 {
310 rc = RTLdrGetSymbol(hMod, "GetModuleBaseName", (void **)&g_pfnGetModuleBaseName);
311 AssertStmt(RT_SUCCESS(rc), g_pfnGetModuleBaseName = NULL);
312
313 rc = RTLdrGetSymbol(hMod, "EnumProcesses", (void **)&g_pfnEnumProcesses);
314 AssertStmt(RT_SUCCESS(rc), g_pfnEnumProcesses = NULL);
315
316 RTLdrClose(hMod);
317 }
318 }
319
320 /*
321 * advapi32.dll APIs.
322 */
323 g_pfnCreateProcessWithLogonW = (PFNCREATEPROCESSWITHLOGON)RTLdrGetSystemSymbol("advapi32.dll", "CreateProcessWithLogonW");
324
325 /*
326 * userenv.dll APIs.
327 */
328 rc = RTLdrLoadSystem("userenv.dll", true /*fNoUnload*/, &hMod);
329 if (RT_SUCCESS(rc))
330 {
331 rc = RTLdrGetSymbol(hMod, "LoadUserProfileW", (void **)&g_pfnLoadUserProfileW);
332 AssertStmt(RT_SUCCESS(rc), g_pfnLoadUserProfileW = NULL);
333
334 rc = RTLdrGetSymbol(hMod, "UnloadUserProfile", (void **)&g_pfnUnloadUserProfile);
335 AssertStmt(RT_SUCCESS(rc), g_pfnUnloadUserProfile = NULL);
336
337 rc = RTLdrGetSymbol(hMod, "CreateEnvironmentBlock", (void **)&g_pfnCreateEnvironmentBlock);
338 AssertStmt(RT_SUCCESS(rc), g_pfnCreateEnvironmentBlock = NULL);
339
340 rc = RTLdrGetSymbol(hMod, "DestroyEnvironmentBlock", (void **)&g_pfnDestroyEnvironmentBlock);
341 AssertStmt(RT_SUCCESS(rc), g_pfnDestroyEnvironmentBlock = NULL);
342
343 RTLdrClose(hMod);
344 }
345
346 return VINF_SUCCESS;
347}
348
349
350RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)
351{
352 return RTProcCreateEx(pszExec, papszArgs, Env, fFlags,
353 NULL, NULL, NULL, /* standard handles */
354 NULL /*pszAsUser*/, NULL /* pszPassword*/,
355 pProcess);
356}
357
358
359/**
360 * Map some important or much used Windows error codes
361 * to our error codes.
362 *
363 * @return Mapped IPRT status code.
364 * @param dwError Windows error code to map to IPRT code.
365 */
366static int rtProcWinMapErrorCodes(DWORD dwError)
367{
368 int rc;
369 switch (dwError)
370 {
371 case ERROR_NOACCESS:
372 case ERROR_PRIVILEGE_NOT_HELD:
373 rc = VERR_PERMISSION_DENIED;
374 break;
375
376 case ERROR_PASSWORD_EXPIRED:
377 case ERROR_ACCOUNT_RESTRICTION: /* See: http://support.microsoft.com/kb/303846/ */
378 case ERROR_PASSWORD_RESTRICTION:
379 case ERROR_ACCOUNT_DISABLED: /* See: http://support.microsoft.com/kb/263936 */
380 rc = VERR_ACCOUNT_RESTRICTED;
381 break;
382
383 case ERROR_FILE_CORRUPT:
384 rc = VERR_BAD_EXE_FORMAT;
385 break;
386
387 case ERROR_BAD_DEVICE: /* Can happen when opening funny things like "CON". */
388 rc = VERR_INVALID_NAME;
389 break;
390
391 default:
392 /* Could trigger a debug assertion! */
393 rc = RTErrConvertFromWin32(dwError);
394 break;
395 }
396 return rc;
397}
398
399
400/**
401 * Get the process token of the process indicated by @a dwPID if the @a pSid
402 * matches.
403 *
404 * @returns IPRT status code.
405 * @param dwPid The process identifier.
406 * @param pSid The secure identifier of the user.
407 * @param phToken Where to return the a duplicate of the process token
408 * handle on success. (The caller closes it.)
409 */
410static int rtProcWinGetProcessTokenHandle(DWORD dwPid, PSID pSid, PHANDLE phToken)
411{
412 AssertPtr(pSid);
413 AssertPtr(phToken);
414
415 int rc;
416 HANDLE hProc = OpenProcess(MAXIMUM_ALLOWED, TRUE, dwPid);
417 if (hProc != NULL)
418 {
419 HANDLE hTokenProc;
420 if (OpenProcessToken(hProc,
421 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE
422 | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE,
423 &hTokenProc))
424 {
425 SetLastError(NO_ERROR);
426 DWORD dwSize = 0;
427 BOOL fRc = GetTokenInformation(hTokenProc, TokenUser, NULL, 0, &dwSize);
428 DWORD dwErr = GetLastError();
429 if ( !fRc
430 && dwErr == ERROR_INSUFFICIENT_BUFFER
431 && dwSize > 0)
432 {
433 PTOKEN_USER pTokenUser = (PTOKEN_USER)RTMemTmpAllocZ(dwSize);
434 if (pTokenUser)
435 {
436 if (GetTokenInformation(hTokenProc,
437 TokenUser,
438 pTokenUser,
439 dwSize,
440 &dwSize))
441 {
442 if ( IsValidSid(pTokenUser->User.Sid)
443 && EqualSid(pTokenUser->User.Sid, pSid))
444 {
445 if (DuplicateTokenEx(hTokenProc, MAXIMUM_ALLOWED,
446 NULL, SecurityIdentification, TokenPrimary, phToken))
447 {
448 /*
449 * So we found the process instance which belongs to the user we want to
450 * to run our new process under. This duplicated token will be used for
451 * the actual CreateProcessAsUserW() call then.
452 */
453 rc = VINF_SUCCESS;
454 }
455 else
456 rc = rtProcWinMapErrorCodes(GetLastError());
457 }
458 else
459 rc = VERR_NOT_FOUND;
460 }
461 else
462 rc = rtProcWinMapErrorCodes(GetLastError());
463 RTMemTmpFree(pTokenUser);
464 }
465 else
466 rc = VERR_NO_MEMORY;
467 }
468 else if (fRc || dwErr == NO_ERROR)
469 rc = VERR_IPE_UNEXPECTED_STATUS;
470 else
471 rc = rtProcWinMapErrorCodes(dwErr);
472 CloseHandle(hTokenProc);
473 }
474 else
475 rc = rtProcWinMapErrorCodes(GetLastError());
476 CloseHandle(hProc);
477 }
478 else
479 rc = rtProcWinMapErrorCodes(GetLastError());
480 return rc;
481}
482
483
484/**
485 * Fallback method for rtProcWinFindTokenByProcess that uses the older NT4
486 * PSAPI.DLL API.
487 *
488 * @returns Success indicator.
489 * @param papszNames The process candidates, in prioritized order.
490 * @param pSid The secure identifier of the user.
491 * @param phToken Where to return the token handle - duplicate,
492 * caller closes it on success.
493 *
494 * @remarks NT4 needs a copy of "PSAPI.dll" (redistributed by Microsoft and not
495 * part of the OS) in order to get a lookup. If we don't have this DLL
496 * we are not able to get a token and therefore no UI will be visible.
497 */
498static bool rtProcWinFindTokenByProcessAndPsApi(const char * const *papszNames, PSID pSid, PHANDLE phToken)
499{
500 /*
501 * Load PSAPI.DLL and resolve the two symbols we need.
502 */
503 if ( !g_pfnGetModuleBaseName
504 || !g_pfnEnumProcesses)
505 return false;
506
507 /*
508 * Get a list of PID. We retry if it looks like there are more PIDs
509 * to be returned than what we supplied buffer space for.
510 */
511 bool fFound = false;
512 int rc = VINF_SUCCESS;
513 DWORD cbPidsAllocated = 4096;
514 DWORD cbPidsReturned;
515 DWORD *paPids;
516 for (;;)
517 {
518 paPids = (DWORD *)RTMemTmpAlloc(cbPidsAllocated);
519 AssertBreakStmt(paPids, rc = VERR_NO_TMP_MEMORY);
520 cbPidsReturned = 0;
521 if (!g_pfnEnumProcesses(paPids, cbPidsAllocated, &cbPidsReturned))
522 {
523 rc = RTErrConvertFromWin32(GetLastError());
524 AssertMsgFailedBreak(("%Rrc\n", rc));
525 }
526 if ( cbPidsReturned < cbPidsAllocated
527 || cbPidsAllocated >= _512K)
528 break;
529 RTMemTmpFree(paPids);
530 cbPidsAllocated *= 2;
531 }
532 if (RT_SUCCESS(rc))
533 {
534 /*
535 * Search for the process.
536 *
537 * We ASSUME that the caller won't be specifying any names longer
538 * than RTPATH_MAX.
539 */
540 DWORD cbProcName = RTPATH_MAX;
541 char *pszProcName = (char *)RTMemTmpAlloc(RTPATH_MAX);
542 if (pszProcName)
543 {
544 for (size_t i = 0; papszNames[i] && !fFound; i++)
545 {
546 const DWORD cPids = cbPidsReturned / sizeof(DWORD);
547 for (DWORD iPid = 0; iPid < cPids && !fFound; iPid++)
548 {
549 HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, paPids[iPid]);
550 if (hProc)
551 {
552 *pszProcName = '\0';
553 DWORD cbRet = g_pfnGetModuleBaseName(hProc, 0 /*hModule = exe */, pszProcName, cbProcName);
554 if ( cbRet > 0
555 && _stricmp(pszProcName, papszNames[i]) == 0
556 && RT_SUCCESS(rtProcWinGetProcessTokenHandle(paPids[iPid], pSid, phToken)))
557 fFound = true;
558 CloseHandle(hProc);
559 }
560 }
561 }
562 RTMemTmpFree(pszProcName);
563 }
564 else
565 rc = VERR_NO_TMP_MEMORY;
566 }
567 RTMemTmpFree(paPids);
568
569 return fFound;
570}
571
572
573/**
574 * Finds a one of the processes in @a papszNames running with user @a pSid and
575 * returns a duplicate handle to its token.
576 *
577 * @returns Success indicator.
578 * @param papszNames The process candidates, in prioritized order.
579 * @param pSid The secure identifier of the user.
580 * @param phToken Where to return the token handle - duplicate,
581 * caller closes it on success.
582 */
583static bool rtProcWinFindTokenByProcess(const char * const *papszNames, PSID pSid, PHANDLE phToken)
584{
585 AssertPtr(papszNames);
586 AssertPtr(pSid);
587 AssertPtr(phToken);
588
589 bool fFound = false;
590
591 /*
592 * On modern systems (W2K+) try the Toolhelp32 API first; this is more stable
593 * and reliable. Fallback to EnumProcess on NT4.
594 */
595 bool fFallback = true;
596 if (g_pfnProcess32Next && g_pfnProcess32First && g_pfnCreateToolhelp32Snapshot)
597 {
598 HANDLE hSnap = g_pfnCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
599 Assert(hSnap != INVALID_HANDLE_VALUE);
600 if (hSnap != INVALID_HANDLE_VALUE)
601 {
602 fFallback = false;
603 for (size_t i = 0; papszNames[i] && !fFound; i++)
604 {
605 PROCESSENTRY32 ProcEntry;
606 ProcEntry.dwSize = sizeof(PROCESSENTRY32);
607/** @todo use W APIs here. */
608 if (g_pfnProcess32First(hSnap, &ProcEntry))
609 {
610 do
611 {
612 if (_stricmp(ProcEntry.szExeFile, papszNames[i]) == 0)
613 {
614 int rc = rtProcWinGetProcessTokenHandle(ProcEntry.th32ProcessID, pSid, phToken);
615 if (RT_SUCCESS(rc))
616 {
617 fFound = true;
618 break;
619 }
620 }
621 } while (g_pfnProcess32Next(hSnap, &ProcEntry));
622 }
623#ifdef RT_STRICT
624 else
625 {
626 DWORD dwErr = GetLastError();
627 AssertMsgFailed(("dwErr=%u (%x)\n", dwErr, dwErr));
628 }
629#endif
630 }
631 CloseHandle(hSnap);
632 }
633 }
634
635 /* If we couldn't take a process snapshot for some reason or another, fall
636 back on the NT4 compatible API. */
637 if (fFallback)
638 fFound = rtProcWinFindTokenByProcessAndPsApi(papszNames, pSid, phToken);
639 return fFound;
640}
641
642
643/**
644 * Logs on a specified user and returns its primary token.
645 *
646 * @returns IPRT status code.
647 * @param pwszUser User name.
648 * @param pwszPassword Password.
649 * @param pwszDomain Domain (not used at the moment).
650 * @param phToken Pointer to store the logon token.
651 */
652static int rtProcWinUserLogon(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszDomain, HANDLE *phToken)
653{
654 AssertPtrReturn(pwszUser, VERR_INVALID_POINTER);
655 AssertPtrReturn(pwszPassword, VERR_INVALID_POINTER);
656 NOREF(pwszDomain); /** @todo Add domain support! */
657
658 /*
659 * Because we have to deal with http://support.microsoft.com/kb/245683
660 * for NULL domain names when running on NT4 here, pass an empty string if so.
661 * However, passing FQDNs should work!
662 */
663 BOOL fRc = LogonUserW(pwszUser,
664 g_enmWinVer < kRTWinOSType_2K ? L"" /* NT4 and older */ : NULL /* Windows 2000 and up */,
665 pwszPassword,
666 LOGON32_LOGON_INTERACTIVE,
667 LOGON32_PROVIDER_DEFAULT,
668 phToken);
669 if (fRc)
670 return VINF_SUCCESS;
671
672 DWORD dwErr = GetLastError();
673 int rc = rtProcWinMapErrorCodes(dwErr);
674 if (rc == VERR_UNRESOLVED_ERROR)
675 LogRelFunc(("dwErr=%u (%#x), rc=%Rrc\n", dwErr, dwErr, rc));
676 return rc;
677}
678
679
680/**
681 * Returns the environment to use for the child process.
682 *
683 * This implements the RTPROC_FLAGS_ENV_CHANGE_RECORD and environment related
684 * parts of RTPROC_FLAGS_PROFILE.
685 *
686 * @returns IPRT status code.
687 * @param hToken The user token to use if RTPROC_FLAGS_PROFILE is given.
688 * The caller must have loaded profile for this.
689 * @param hEnv The environment passed in by the RTProcCreateEx caller.
690 * @param fFlags The process creation flags passed in by the
691 * RTProcCreateEx caller (RTPROC_FLAGS_XXX).
692 * @param phEnv Where to return the environment to use. This can either
693 * be a newly created environment block or @a hEnv. In the
694 * former case, the caller must destroy it.
695 */
696static int rtProcWinCreateEnvFromToken(HANDLE hToken, RTENV hEnv, uint32_t fFlags, PRTENV phEnv)
697{
698 int rc;
699
700 /*
701 * Query the environment from the user profile associated with the token if
702 * the caller has specified it directly or indirectly.
703 */
704 if ( (fFlags & RTPROC_FLAGS_PROFILE)
705 && ( hEnv == RTENV_DEFAULT
706 || (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)) )
707 {
708 if (g_pfnCreateEnvironmentBlock && g_pfnDestroyEnvironmentBlock)
709 {
710 LPVOID pvEnvBlockProfile = NULL;
711 if (g_pfnCreateEnvironmentBlock(&pvEnvBlockProfile, hToken, FALSE /* Don't inherit from parent. */))
712 {
713 rc = RTEnvCloneUtf16Block(phEnv, (PCRTUTF16)pvEnvBlockProfile, 0 /*fFlags*/);
714 if ( (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)
715 && RT_SUCCESS(rc)
716 && hEnv != RTENV_DEFAULT)
717 {
718 rc = RTEnvApplyChanges(*phEnv, hEnv);
719 if (RT_FAILURE(rc))
720 RTEnvDestroy(*phEnv);
721 }
722 g_pfnDestroyEnvironmentBlock(pvEnvBlockProfile);
723 }
724 else
725 rc = RTErrConvertFromWin32(GetLastError());
726 }
727 else
728 rc = VERR_SYMBOL_NOT_FOUND;
729 }
730 /*
731 * We we've got an incoming change record, combine it with the default environment.
732 */
733 else if (hEnv != RTENV_DEFAULT && (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD))
734 {
735 rc = RTEnvClone(phEnv, RTENV_DEFAULT);
736 if (RT_SUCCESS(rc))
737 {
738 rc = RTEnvApplyChanges(*phEnv, hEnv);
739 if (RT_FAILURE(rc))
740 RTEnvDestroy(*phEnv);
741 }
742 }
743 /*
744 * Otherwise we can return the incoming environment directly.
745 */
746 else
747 {
748 *phEnv = hEnv;
749 rc = VINF_SUCCESS;
750 }
751
752 return rc;
753}
754
755
756/**
757 * Method \#2.
758 */
759static int rtProcWinCreateAsUser2(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 *ppwszExec, PRTUTF16 pwszCmdLine,
760 RTENV hEnv, DWORD dwCreationFlags,
761 STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo,
762 uint32_t fFlags, const char *pszExec)
763{
764 /*
765 * So if we want to start a process from a service (RTPROC_FLAGS_SERVICE),
766 * we have to do the following:
767 * - Check the credentials supplied and get the user SID.
768 * - If valid get the correct Explorer/VBoxTray instance corresponding to that
769 * user. This of course is only possible if that user is logged in (over
770 * physical console or terminal services).
771 * - If we found the user's Explorer/VBoxTray app, use and modify the token to
772 * use it in order to allow the newly started process to access the user's
773 * desktop. If there's no Explorer/VBoxTray app we cannot display the started
774 * process (but run it without UI).
775 *
776 * The following restrictions apply:
777 * - A process only can show its UI when the user the process should run
778 * under is logged in (has a desktop).
779 * - We do not want to display a process of user A run on the desktop
780 * of user B on multi session systems.
781 *
782 * The following rights are needed in order to use LogonUserW and
783 * CreateProcessAsUserW, so the local policy has to be modified to:
784 * - SE_TCB_NAME = Act as part of the operating system
785 * - SE_ASSIGNPRIMARYTOKEN_NAME = Create/replace a token object
786 * - SE_INCREASE_QUOTA_NAME
787 *
788 * We may fail here with ERROR_PRIVILEGE_NOT_HELD.
789 */
790/** @todo r=bird: Both methods starts with rtProcWinUserLogon, so we could probably do that once in the parent function, right... */
791 DWORD dwErr = NO_ERROR;
792 HANDLE hTokenLogon = INVALID_HANDLE_VALUE;
793 int rc = rtProcWinUserLogon(pwszUser, pwszPassword, NULL /* Domain */, &hTokenLogon);
794 if (RT_SUCCESS(rc))
795 {
796 DWORD fRc;
797 bool fFound = false;
798 HANDLE hTokenUserDesktop = INVALID_HANDLE_VALUE;
799
800 if (fFlags & RTPROC_FLAGS_SERVICE)
801 {
802 /* Try query the SID and domain sizes first. */
803 DWORD cbSid = 0; /* Must be zero to query size! */
804 DWORD cwcDomain = 0;
805 SID_NAME_USE SidNameUse = SidTypeUser;
806 fRc = LookupAccountNameW(NULL, pwszUser, NULL, &cbSid, NULL, &cwcDomain, &SidNameUse);
807
808 /* Allocate memory for the LookupAccountNameW output buffers and do it for real. */
809 cbSid = fRc && cbSid != 0 ? cbSid + 16 : _1K;
810 PSID pSid = (PSID)RTMemAllocZ(cbSid);
811 if (pSid)
812 {
813 cwcDomain = fRc ? cwcDomain + 2 : _512K;
814 PRTUTF16 pwszDomain = (PRTUTF16)RTMemAllocZ(cwcDomain * sizeof(RTUTF16));
815 if (pwszDomain)
816 {
817 /* Note: Also supports FQDNs! */
818 if ( LookupAccountNameW(NULL /*lpSystemName*/, pwszUser, pSid, &cbSid, pwszDomain, &cwcDomain, &SidNameUse)
819 && IsValidSid(pSid))
820 {
821 /* Array of process names we want to look for. */
822 static const char * const s_papszProcNames[] =
823 {
824#ifdef VBOX /* The explorer entry is a fallback in case GA aren't installed. */
825 { "VBoxTray.exe" },
826#endif
827 { "explorer.exe" },
828 NULL
829 };
830 fFound = rtProcWinFindTokenByProcess(s_papszProcNames, pSid, &hTokenUserDesktop);
831 }
832 else
833 dwErr = GetLastError() != NO_ERROR ? GetLastError() : ERROR_INTERNAL_ERROR;
834 RTMemFree(pwszDomain);
835 }
836 RTMemFree(pSid);
837 }
838 }
839 /* else: !RTPROC_FLAGS_SERVICE: Nothing to do here right now. */
840
841 /** @todo Hmm, this function already is too big! We need to split
842 * it up into several small parts. */
843
844 /* If we got an error due to account lookup/loading above, don't
845 * continue here. */
846 if (dwErr == NO_ERROR)
847 {
848 /*
849 * If we didn't find a matching VBoxTray, just use the token we got
850 * above from LogonUserW(). This enables us to at least run processes
851 * with desktop interaction without UI.
852 */
853 HANDLE hTokenToUse = fFound ? hTokenUserDesktop : hTokenLogon;
854 if ( !(fFlags & RTPROC_FLAGS_PROFILE)
855 || (g_pfnUnloadUserProfile && g_pfnLoadUserProfileW) )
856 {
857 /*
858 * Load the profile, if requested. (Must be done prior to
859 * creating the enviornment.)
860 */
861 PROFILEINFOW ProfileInfo;
862 RT_ZERO(ProfileInfo);
863 if (fFlags & RTPROC_FLAGS_PROFILE)
864 {
865 ProfileInfo.dwSize = sizeof(ProfileInfo);
866 ProfileInfo.lpUserName = pwszUser;
867 ProfileInfo.dwFlags = PI_NOUI; /* Prevents the display of profile error messages. */
868
869 if (!g_pfnLoadUserProfileW(hTokenToUse, &ProfileInfo))
870 dwErr = GetLastError();
871 }
872 if (dwErr == NO_ERROR)
873 {
874 /*
875 * Create the environment.
876 */
877 RTENV hEnvFinal;
878 rc = rtProcWinCreateEnvFromToken(hTokenToUse, hEnv, fFlags, &hEnvFinal);
879 if (RT_SUCCESS(rc))
880 {
881 PRTUTF16 pwszzBlock;
882 rc = RTEnvQueryUtf16Block(hEnvFinal, &pwszzBlock);
883 if (RT_SUCCESS(rc))
884 {
885 rc = rtProcWinFindExe(fFlags, hEnv, pszExec, ppwszExec);
886 if (RT_SUCCESS(rc))
887 {
888 /*
889 * Useful KB articles:
890 * http://support.microsoft.com/kb/165194/
891 * http://support.microsoft.com/kb/184802/
892 * http://support.microsoft.com/kb/327618/
893 */
894 fRc = CreateProcessAsUserW(hTokenToUse,
895 *ppwszExec,
896 pwszCmdLine,
897 NULL, /* pProcessAttributes */
898 NULL, /* pThreadAttributes */
899 TRUE, /* fInheritHandles */
900 dwCreationFlags,
901 /** @todo Warn about exceeding 8192 bytes
902 * on XP and up. */
903 pwszzBlock, /* lpEnvironment */
904 NULL, /* pCurrentDirectory */
905 pStartupInfo,
906 pProcInfo);
907 if (fRc)
908 dwErr = NO_ERROR;
909 else
910 dwErr = GetLastError(); /* CreateProcessAsUserW() failed. */
911 }
912 RTEnvFreeUtf16Block(pwszzBlock);
913 }
914
915 if (hEnvFinal != hEnv)
916 RTEnvDestroy(hEnvFinal);
917 }
918
919 if ((fFlags & RTPROC_FLAGS_PROFILE) && ProfileInfo.hProfile)
920 {
921 fRc = g_pfnUnloadUserProfile(hTokenToUse, ProfileInfo.hProfile);
922#ifdef RT_STRICT
923 if (!fRc)
924 {
925 DWORD dwErr2 = GetLastError();
926 AssertMsgFailed(("Unloading user profile failed with error %u (%#x) - Are all handles closed? (dwErr=%u)",
927 dwErr2, dwErr2, dwErr));
928 }
929#endif
930 }
931 }
932 }
933 else
934 rc = VERR_SYMBOL_NOT_FOUND;
935 } /* Account lookup succeeded? */
936
937 if (hTokenUserDesktop != INVALID_HANDLE_VALUE)
938 CloseHandle(hTokenUserDesktop);
939 CloseHandle(hTokenLogon);
940
941 /*
942 * Do error conversion.
943 */
944 if ( RT_SUCCESS(rc)
945 && dwErr != NO_ERROR)
946 {
947 rc = rtProcWinMapErrorCodes(dwErr);
948 if (rc == VERR_UNRESOLVED_ERROR)
949 LogRelFunc(("dwErr=%u (%#x), rc=%Rrc\n", dwErr, dwErr, rc));
950 }
951 }
952 return rc;
953}
954
955
956/**
957 * Method \#1.
958 *
959 * This may fail on too old (NT4) platforms or if the calling process
960 * is running on a SYSTEM account (like a service, ERROR_ACCESS_DENIED) on newer
961 * platforms (however, this works on W2K!).
962 */
963static int rtProcWinCreateAsUser1(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 *ppwszExec, PRTUTF16 pwszCmdLine,
964 RTENV hEnv, DWORD dwCreationFlags,
965 STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo,
966 uint32_t fFlags, const char *pszExec)
967{
968 if (!g_pfnCreateProcessWithLogonW)
969 return VERR_SYMBOL_NOT_FOUND;
970
971 RTENV hEnvToUse = NIL_RTENV;
972 HANDLE hToken;
973 int rc = rtProcWinUserLogon(pwszUser, pwszPassword, NULL /* Domain */, &hToken);
974 if (RT_SUCCESS(rc))
975 {
976/** @todo r=bird: Why didn't we load the environment here? The
977 * CreateEnvironmentBlock docs indicate that USERPROFILE isn't set
978 * unless we call LoadUserProfile first. However, experiments here on W10
979 * shows it isn't really needed though. Weird. */
980#if 0
981 if (fFlags & RTPROC_FLAGS_PROFILE)
982 {
983 PROFILEINFOW ProfileInfo;
984 RT_ZERO(ProfileInfo);
985 ProfileInfo.dwSize = sizeof(ProfileInfo);
986 ProfileInfo.lpUserName = pwszUser;
987 ProfileInfo.dwFlags = PI_NOUI; /* Prevents the display of profile error messages. */
988
989 if (g_pfnLoadUserProfileW(hToken, &ProfileInfo))
990 {
991 rc = rtProcWinCreateEnvFromToken(hToken, hEnv, fFlags, &hEnvToUse);
992
993 if (!g_pfnUnloadUserProfile(hToken, ProfileInfo.hProfile))
994 AssertFailed();
995 }
996 else
997 rc = RTErrConvertFromWin32(GetLastError());
998 }
999 else
1000#endif
1001 rc = rtProcWinCreateEnvFromToken(hToken, hEnv, fFlags, &hEnvToUse);
1002 CloseHandle(hToken);
1003 }
1004 if (RT_SUCCESS(rc))
1005 {
1006 PRTUTF16 pwszzBlock;
1007 rc = RTEnvQueryUtf16Block(hEnvToUse, &pwszzBlock);
1008 if (RT_SUCCESS(rc))
1009 {
1010 rc = rtProcWinFindExe(fFlags, hEnv, pszExec, ppwszExec);
1011 if (RT_SUCCESS(rc))
1012 {
1013 BOOL fRc = g_pfnCreateProcessWithLogonW(pwszUser,
1014 NULL, /* lpDomain*/
1015 pwszPassword,
1016 fFlags & RTPROC_FLAGS_PROFILE ? 1 /*LOGON_WITH_PROFILE*/ : 0,
1017 *ppwszExec,
1018 pwszCmdLine,
1019 dwCreationFlags,
1020 pwszzBlock,
1021 NULL, /* pCurrentDirectory */
1022 pStartupInfo,
1023 pProcInfo);
1024 if (fRc)
1025 rc = VINF_SUCCESS;
1026 else
1027 {
1028 DWORD dwErr = GetLastError();
1029 rc = rtProcWinMapErrorCodes(dwErr);
1030 if (rc == VERR_UNRESOLVED_ERROR)
1031 LogRelFunc(("g_pfnCreateProcessWithLogonW (%p) failed: dwErr=%u (%#x), rc=%Rrc\n",
1032 g_pfnCreateProcessWithLogonW, dwErr, dwErr, rc));
1033 }
1034 }
1035 RTEnvFreeUtf16Block(pwszzBlock);
1036 }
1037 if (hEnvToUse != hEnv)
1038 RTEnvDestroy(hEnvToUse);
1039 }
1040 return rc;
1041}
1042
1043
1044static int rtProcWinCreateAsUser(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 *ppwszExec, PRTUTF16 pwszCmdLine,
1045 RTENV hEnv, DWORD dwCreationFlags,
1046 STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo,
1047 uint32_t fFlags, const char *pszExec)
1048{
1049 /*
1050 * If we run as a service CreateProcessWithLogon will fail, so don't even
1051 * try it (because of Local System context). This method may not work on
1052 * older OSes, it will fail and we try the next alternative.
1053 */
1054 if (!(fFlags & RTPROC_FLAGS_SERVICE))
1055 {
1056 int rc = rtProcWinCreateAsUser1(pwszUser, pwszPassword, ppwszExec, pwszCmdLine,
1057 hEnv, dwCreationFlags, pStartupInfo, pProcInfo, fFlags, pszExec);
1058 if (RT_SUCCESS(rc))
1059 return rc;
1060 }
1061 return rtProcWinCreateAsUser2(pwszUser, pwszPassword, ppwszExec, pwszCmdLine,
1062 hEnv, dwCreationFlags, pStartupInfo, pProcInfo, fFlags, pszExec);
1063}
1064
1065
1066/**
1067 * RTPathTraverseList callback used by rtProcWinFindExe to locate the
1068 * executable.
1069 */
1070static DECLCALLBACK(int) rtPathFindExec(char const *pchPath, size_t cchPath, void *pvUser1, void *pvUser2)
1071{
1072 const char *pszExec = (const char *)pvUser1;
1073 char *pszRealExec = (char *)pvUser2;
1074 int rc = RTPathJoinEx(pszRealExec, RTPATH_MAX, pchPath, cchPath, pszExec, RTSTR_MAX);
1075 if (RT_FAILURE(rc))
1076 return rc;
1077 if (RTFileExists(pszRealExec))
1078 return VINF_SUCCESS;
1079 return VERR_TRY_AGAIN;
1080}
1081
1082
1083/**
1084 * Locate the executable file if necessary.
1085 *
1086 * @returns IPRT status code.
1087 * @param pszExec The UTF-8 executable string passed in by the user.
1088 * @param fFlags The process creation flags pass in by the user.
1089 * @param hEnv The environment to get the path variabel from.
1090 * @param ppwszExec Pointer to the variable pointing to the UTF-16
1091 * converted string. If we find something, the current
1092 * pointer will be free (RTUtf16Free) and
1093 * replaced by a new one.
1094 */
1095static int rtProcWinFindExe(uint32_t fFlags, RTENV hEnv, const char *pszExec, PRTUTF16 *ppwszExec)
1096{
1097 /*
1098 * Return immediately if we're not asked to search, or if the file has a
1099 * path already or if it actually exists in the current directory.
1100 */
1101 if ( !(fFlags & RTPROC_FLAGS_SEARCH_PATH)
1102 || RTPathHavePath(pszExec)
1103 || RTPathExists(pszExec) )
1104 return VINF_SUCCESS;
1105
1106 /*
1107 * Search the Path or PATH variable for the file.
1108 */
1109 char *pszPath;
1110 if (RTEnvExistEx(hEnv, "PATH"))
1111 pszPath = RTEnvDupEx(hEnv, "PATH");
1112 else if (RTEnvExistEx(hEnv, "Path"))
1113 pszPath = RTEnvDupEx(hEnv, "Path");
1114 else
1115 return VERR_FILE_NOT_FOUND;
1116
1117 char szRealExec[RTPATH_MAX];
1118 int rc = RTPathTraverseList(pszPath, ';', rtPathFindExec, (void *)pszExec, &szRealExec[0]);
1119 RTStrFree(pszPath);
1120 if (RT_SUCCESS(rc))
1121 {
1122 /*
1123 * Replace the executable string.
1124 */
1125 RTUtf16Free(*ppwszExec);
1126 *ppwszExec = NULL;
1127 rc = RTStrToUtf16(szRealExec, ppwszExec);
1128 }
1129 else if (rc == VERR_END_OF_STRING)
1130 rc = VERR_FILE_NOT_FOUND;
1131 return rc;
1132}
1133
1134
1135/**
1136 * Creates the UTF-16 environment block and, if necessary, find the executable.
1137 *
1138 * @returns IPRT status code.
1139 * @param fFlags The process creation flags pass in by the user.
1140 * @param hEnv The environment handle passed by the user.
1141 * @param pszExec See rtProcWinFindExe.
1142 * @param ppwszzBlock Where RTEnvQueryUtf16Block returns the block.
1143 * @param ppwszExec See rtProcWinFindExe.
1144 */
1145static int rtProcWinCreateEnvBlockAndFindExe(uint32_t fFlags, RTENV hEnv, const char *pszExec,
1146 PRTUTF16 *ppwszzBlock, PRTUTF16 *ppwszExec)
1147{
1148 int rc;
1149
1150 /*
1151 * In most cases, we just need to convert the incoming enviornment to a
1152 * UTF-16 environment block.
1153 */
1154 RTENV hEnvToUse;
1155 if ( !(fFlags & (RTPROC_FLAGS_PROFILE | RTPROC_FLAGS_ENV_CHANGE_RECORD))
1156 || (hEnv == RTENV_DEFAULT && !(fFlags & RTPROC_FLAGS_PROFILE))
1157 || (hEnv != RTENV_DEFAULT && !(fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)) )
1158 {
1159 hEnvToUse = hEnv;
1160 rc = VINF_SUCCESS;
1161 }
1162 else if (fFlags & RTPROC_FLAGS_PROFILE)
1163 {
1164 /*
1165 * We need to get the profile environment for the current user.
1166 */
1167 Assert((fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) || hEnv == RTENV_DEFAULT);
1168 AssertReturn(g_pfnCreateEnvironmentBlock && g_pfnDestroyEnvironmentBlock, VERR_SYMBOL_NOT_FOUND);
1169 AssertReturn(g_pfnLoadUserProfileW && g_pfnUnloadUserProfile, VERR_SYMBOL_NOT_FOUND);
1170 HANDLE hToken;
1171 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, &hToken))
1172 {
1173 rc = rtProcWinCreateEnvFromToken(hToken, hEnv, fFlags, &hEnvToUse);
1174 CloseHandle(hToken);
1175 }
1176 else
1177 rc = RTErrConvertFromWin32(GetLastError());
1178 }
1179 else
1180 {
1181 /*
1182 * Apply hEnv as a change record on top of the default environment.
1183 */
1184 Assert(fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD);
1185 rc = RTEnvClone(&hEnvToUse, RTENV_DEFAULT);
1186 if (RT_SUCCESS(rc))
1187 {
1188 rc = RTEnvApplyChanges(hEnvToUse, hEnv);
1189 if (RT_FAILURE(rc))
1190 RTEnvDestroy(hEnvToUse);
1191 }
1192 }
1193 if (RT_SUCCESS(rc))
1194 {
1195 /*
1196 * Query the UTF-16 environment block and locate the executable (if needed).
1197 */
1198 rc = RTEnvQueryUtf16Block(hEnvToUse, ppwszzBlock);
1199 if (RT_SUCCESS(rc))
1200 rc = rtProcWinFindExe(fFlags, hEnvToUse, pszExec, ppwszExec);
1201
1202 if (hEnvToUse != hEnv)
1203 RTEnvDestroy(hEnvToUse);
1204 }
1205
1206 return rc;
1207}
1208
1209
1210RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
1211 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
1212 const char *pszPassword, PRTPROCESS phProcess)
1213{
1214 /*
1215 * Input validation
1216 */
1217 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
1218 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
1219 AssertReturn(!(fFlags & ~RTPROC_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
1220 AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
1221 AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
1222 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
1223 AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
1224 AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
1225 AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
1226 AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
1227
1228 /*
1229 * Initialize the globals.
1230 */
1231 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL);
1232 AssertRCReturn(rc, rc);
1233 if (pszAsUser || (fFlags & (RTPROC_FLAGS_PROFILE | RTPROC_FLAGS_SERVICE)))
1234 {
1235 rc = RTOnce(&g_rtProcWinResolveOnce, rtProcWinResolveOnce, NULL);
1236 AssertRCReturn(rc, rc);
1237 }
1238
1239 /*
1240 * Get the file descriptors for the handles we've been passed.
1241 *
1242 * It seems there is no point in trying to convince a child process's CRT
1243 * that any of the standard file handles is non-TEXT. So, we don't...
1244 */
1245 STARTUPINFOW StartupInfo;
1246 RT_ZERO(StartupInfo);
1247 StartupInfo.cb = sizeof(StartupInfo);
1248 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
1249#if 1 /* The CRT should keep the standard handles up to date. */
1250 StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
1251 StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
1252 StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1253#else
1254 StartupInfo.hStdInput = _get_osfhandle(0);
1255 StartupInfo.hStdOutput = _get_osfhandle(1);
1256 StartupInfo.hStdError = _get_osfhandle(2);
1257#endif
1258 /* If we want to have a hidden process (e.g. not visible to
1259 * to the user) use the STARTUPINFO flags. */
1260 if (fFlags & RTPROC_FLAGS_HIDDEN)
1261 {
1262 StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
1263 StartupInfo.wShowWindow = SW_HIDE;
1264 }
1265
1266 PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
1267 HANDLE *aphStds[3] = { &StartupInfo.hStdInput, &StartupInfo.hStdOutput, &StartupInfo.hStdError };
1268 DWORD afInhStds[3] = { 0xffffffff, 0xffffffff, 0xffffffff };
1269 for (int i = 0; i < 3; i++)
1270 {
1271 if (paHandles[i])
1272 {
1273 AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
1274 switch (paHandles[i]->enmType)
1275 {
1276 case RTHANDLETYPE_FILE:
1277 *aphStds[i] = paHandles[i]->u.hFile != NIL_RTFILE
1278 ? (HANDLE)RTFileToNative(paHandles[i]->u.hFile)
1279 : INVALID_HANDLE_VALUE;
1280 break;
1281
1282 case RTHANDLETYPE_PIPE:
1283 *aphStds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
1284 ? (HANDLE)RTPipeToNative(paHandles[i]->u.hPipe)
1285 : INVALID_HANDLE_VALUE;
1286 break;
1287
1288 case RTHANDLETYPE_SOCKET:
1289 *aphStds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
1290 ? (HANDLE)RTSocketToNative(paHandles[i]->u.hSocket)
1291 : INVALID_HANDLE_VALUE;
1292 break;
1293
1294 default:
1295 AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
1296 }
1297
1298 /* Get the inheritability of the handle. */
1299 if (*aphStds[i] != INVALID_HANDLE_VALUE)
1300 {
1301 if (!GetHandleInformation(*aphStds[i], &afInhStds[i]))
1302 {
1303 rc = RTErrConvertFromWin32(GetLastError());
1304 AssertMsgFailedReturn(("%Rrc %p\n", rc, *aphStds[i]), rc);
1305 }
1306 }
1307 }
1308 }
1309
1310 /*
1311 * Set the inheritability any handles we're handing the child.
1312 */
1313 rc = VINF_SUCCESS;
1314 for (int i = 0; i < 3; i++)
1315 if ( (afInhStds[i] != 0xffffffff)
1316 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
1317 {
1318 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
1319 {
1320 rc = RTErrConvertFromWin32(GetLastError());
1321 AssertMsgFailedBreak(("%Rrc %p\n", rc, *aphStds[i]));
1322 }
1323 }
1324
1325 /*
1326 * Create the command line and convert the executable name.
1327 */
1328 PRTUTF16 pwszCmdLine;
1329 if (RT_SUCCESS(rc))
1330 rc = RTGetOptArgvToUtf16String(&pwszCmdLine, papszArgs,
1331 !(fFlags & RTPROC_FLAGS_UNQUOTED_ARGS)
1332 ? RTGETOPTARGV_CNV_QUOTE_MS_CRT : RTGETOPTARGV_CNV_UNQUOTED);
1333 if (RT_SUCCESS(rc))
1334 {
1335 PRTUTF16 pwszExec;
1336 rc = RTStrToUtf16(pszExec, &pwszExec);
1337 if (RT_SUCCESS(rc))
1338 {
1339 /*
1340 * Get going...
1341 */
1342 PROCESS_INFORMATION ProcInfo;
1343 RT_ZERO(ProcInfo);
1344 DWORD dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
1345 if (fFlags & RTPROC_FLAGS_DETACHED)
1346 dwCreationFlags |= DETACHED_PROCESS;
1347 if (fFlags & RTPROC_FLAGS_NO_WINDOW)
1348 dwCreationFlags |= CREATE_NO_WINDOW;
1349
1350 /*
1351 * Only use the normal CreateProcess stuff if we have no user name
1352 * and we are not running from a (Windows) service. Otherwise use
1353 * the more advanced version in rtProcWinCreateAsUser().
1354 */
1355 if ( pszAsUser == NULL
1356 && !(fFlags & RTPROC_FLAGS_SERVICE))
1357 {
1358 /* Create the environment block first. */
1359 PRTUTF16 pwszzBlock;
1360 rc = rtProcWinCreateEnvBlockAndFindExe(fFlags, hEnv, pszExec, &pwszzBlock, &pwszExec);
1361 if (RT_SUCCESS(rc))
1362 {
1363 if (CreateProcessW(pwszExec,
1364 pwszCmdLine,
1365 NULL, /* pProcessAttributes */
1366 NULL, /* pThreadAttributes */
1367 TRUE, /* fInheritHandles */
1368 dwCreationFlags,
1369 pwszzBlock,
1370 NULL, /* pCurrentDirectory */
1371 &StartupInfo,
1372 &ProcInfo))
1373 rc = VINF_SUCCESS;
1374 else
1375 rc = RTErrConvertFromWin32(GetLastError());
1376 RTEnvFreeUtf16Block(pwszzBlock);
1377 }
1378 }
1379 else
1380 {
1381 /*
1382 * Convert the additional parameters and use a helper
1383 * function to do the actual work.
1384 */
1385 PRTUTF16 pwszUser;
1386 rc = RTStrToUtf16(pszAsUser, &pwszUser);
1387 if (RT_SUCCESS(rc))
1388 {
1389 PRTUTF16 pwszPassword;
1390 rc = RTStrToUtf16(pszPassword ? pszPassword : "", &pwszPassword);
1391 if (RT_SUCCESS(rc))
1392 {
1393 rc = rtProcWinCreateAsUser(pwszUser, pwszPassword,
1394 &pwszExec, pwszCmdLine, hEnv, dwCreationFlags,
1395 &StartupInfo, &ProcInfo, fFlags, pszExec);
1396
1397 if (pwszPassword && *pwszPassword)
1398 RTMemWipeThoroughly(pwszPassword, RTUtf16Len(pwszPassword), 5);
1399 RTUtf16Free(pwszPassword);
1400 }
1401 RTUtf16Free(pwszUser);
1402 }
1403 }
1404 if (RT_SUCCESS(rc))
1405 {
1406 CloseHandle(ProcInfo.hThread);
1407 if (phProcess)
1408 {
1409 /*
1410 * Add the process to the child process list so
1411 * RTProcWait can reuse and close the process handle.
1412 */
1413 rtProcWinAddPid(ProcInfo.dwProcessId, ProcInfo.hProcess);
1414 *phProcess = ProcInfo.dwProcessId;
1415 }
1416 else
1417 CloseHandle(ProcInfo.hProcess);
1418 rc = VINF_SUCCESS;
1419 }
1420 RTUtf16Free(pwszExec);
1421 }
1422 RTUtf16Free(pwszCmdLine);
1423 }
1424
1425 /* Undo any handle inherit changes. */
1426 for (int i = 0; i < 3; i++)
1427 if ( (afInhStds[i] != 0xffffffff)
1428 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
1429 {
1430 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, 0))
1431 AssertMsgFailed(("%Rrc %p\n", RTErrConvertFromWin32(GetLastError()), *aphStds[i]));
1432 }
1433
1434 return rc;
1435}
1436
1437
1438
1439RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
1440{
1441 AssertReturn(!(fFlags & ~(RTPROCWAIT_FLAGS_BLOCK | RTPROCWAIT_FLAGS_NOBLOCK)), VERR_INVALID_PARAMETER);
1442 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL);
1443 AssertRCReturn(rc, rc);
1444
1445 /*
1446 * Try find the process among the ones we've spawned, otherwise, attempt
1447 * opening the specified process.
1448 */
1449 HANDLE hOpenedProc = NULL;
1450 HANDLE hProcess = rtProcWinFindPid(Process);
1451 if (hProcess == NULL)
1452 {
1453 hProcess = hOpenedProc = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, Process);
1454 if (hProcess == NULL)
1455 {
1456 DWORD dwErr = GetLastError();
1457 if (dwErr == ERROR_INVALID_PARAMETER)
1458 return VERR_PROCESS_NOT_FOUND;
1459 return RTErrConvertFromWin32(dwErr);
1460 }
1461 }
1462
1463 /*
1464 * Wait for it to terminate.
1465 */
1466 DWORD Millies = fFlags == RTPROCWAIT_FLAGS_BLOCK ? INFINITE : 0;
1467 DWORD WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
1468 while (WaitRc == WAIT_IO_COMPLETION)
1469 WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
1470 switch (WaitRc)
1471 {
1472 /*
1473 * It has terminated.
1474 */
1475 case WAIT_OBJECT_0:
1476 {
1477 DWORD dwExitCode;
1478 if (GetExitCodeProcess(hProcess, &dwExitCode))
1479 {
1480 /** @todo the exit code can be special statuses. */
1481 if (pProcStatus)
1482 {
1483 pProcStatus->enmReason = RTPROCEXITREASON_NORMAL;
1484 pProcStatus->iStatus = (int)dwExitCode;
1485 }
1486 if (hOpenedProc == NULL)
1487 rtProcWinRemovePid(Process);
1488 rc = VINF_SUCCESS;
1489 }
1490 else
1491 rc = RTErrConvertFromWin32(GetLastError());
1492 break;
1493 }
1494
1495 /*
1496 * It hasn't terminated just yet.
1497 */
1498 case WAIT_TIMEOUT:
1499 rc = VERR_PROCESS_RUNNING;
1500 break;
1501
1502 /*
1503 * Something went wrong...
1504 */
1505 case WAIT_FAILED:
1506 rc = RTErrConvertFromWin32(GetLastError());
1507 break;
1508
1509 case WAIT_ABANDONED:
1510 AssertFailed();
1511 rc = VERR_GENERAL_FAILURE;
1512 break;
1513
1514 default:
1515 AssertMsgFailed(("WaitRc=%RU32\n", WaitRc));
1516 rc = VERR_GENERAL_FAILURE;
1517 break;
1518 }
1519
1520 if (hOpenedProc != NULL)
1521 CloseHandle(hOpenedProc);
1522 return rc;
1523}
1524
1525
1526RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
1527{
1528 /** @todo this isn't quite right. */
1529 return RTProcWait(Process, fFlags, pProcStatus);
1530}
1531
1532
1533RTR3DECL(int) RTProcTerminate(RTPROCESS Process)
1534{
1535 if (Process == NIL_RTPROCESS)
1536 return VINF_SUCCESS;
1537
1538 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL);
1539 AssertRCReturn(rc, rc);
1540
1541 /*
1542 * Try find the process among the ones we've spawned, otherwise, attempt
1543 * opening the specified process.
1544 */
1545 HANDLE hProcess = rtProcWinFindPid(Process);
1546 if (hProcess != NULL)
1547 {
1548 if (!TerminateProcess(hProcess, 127))
1549 rc = RTErrConvertFromWin32(GetLastError());
1550 }
1551 else
1552 {
1553 hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, Process);
1554 if (hProcess != NULL)
1555 {
1556 BOOL fRc = TerminateProcess(hProcess, 127);
1557 DWORD dwErr = GetLastError();
1558 CloseHandle(hProcess);
1559 if (!fRc)
1560 rc = RTErrConvertFromWin32(dwErr);
1561 }
1562 }
1563 return rc;
1564}
1565
1566
1567RTR3DECL(uint64_t) RTProcGetAffinityMask(void)
1568{
1569 DWORD_PTR dwProcessAffinityMask = 0xffffffff;
1570 DWORD_PTR dwSystemAffinityMask;
1571
1572 BOOL fRc = GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask);
1573 Assert(fRc);
1574
1575 return dwProcessAffinityMask;
1576}
1577
1578
1579RTR3DECL(int) RTProcQueryUsername(RTPROCESS hProcess, char *pszUser, size_t cbUser,
1580 size_t *pcbUser)
1581{
1582 AssertReturn( (pszUser && cbUser > 0)
1583 || (!pszUser && !cbUser), VERR_INVALID_PARAMETER);
1584
1585 if (hProcess != RTProcSelf())
1586 return VERR_NOT_SUPPORTED;
1587
1588 RTUTF16 awszUserName[UNLEN + 1];
1589 DWORD cchUserName = UNLEN + 1;
1590
1591 if (!GetUserNameW(&awszUserName[0], &cchUserName))
1592 return RTErrConvertFromWin32(GetLastError());
1593
1594 char *pszUserName = NULL;
1595 int rc = RTUtf16ToUtf8(awszUserName, &pszUserName);
1596 if (RT_SUCCESS(rc))
1597 {
1598 size_t cbUserName = strlen(pszUserName) + 1;
1599
1600 if (pcbUser)
1601 *pcbUser = cbUserName;
1602
1603 if (cbUserName > cbUser)
1604 rc = VERR_BUFFER_OVERFLOW;
1605 else
1606 {
1607 memcpy(pszUser, pszUserName, cbUserName);
1608 rc = VINF_SUCCESS;
1609 }
1610
1611 RTStrFree(pszUserName);
1612 }
1613
1614 return rc;
1615}
1616
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette