VirtualBox

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

Last change on this file since 62584 was 62584, checked in by vboxsync, 8 years ago

IPRT: Unused parameters on windows.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 96.2 KB
Line 
1/* $Id: process-win.cpp 62584 2016-07-27 11:46:03Z vboxsync $ */
2/** @file
3 * IPRT - Process, Windows.
4 */
5
6/*
7 * Copyright (C) 2006-2016 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 <iprt/nt/nt-and-windows.h>
35#include <Userenv.h>
36#include <tlhelp32.h>
37#include <process.h>
38#include <errno.h>
39#include <Strsafe.h>
40#ifndef IPRT_TARGET_NT4
41# include <LsaLookup.h>
42#endif
43#include <Lmcons.h>
44
45#ifndef IPRT_TARGET_NT4
46# define _NTDEF_ /* Prevents redefining (P)UNICODE_STRING. */
47# include <Ntsecapi.h>
48#endif
49
50#include <iprt/process.h>
51#include "internal-r3-win.h"
52
53#include <iprt/assert.h>
54#include <iprt/critsect.h>
55#include <iprt/file.h>
56#include <iprt/err.h>
57#include <iprt/env.h>
58#include <iprt/getopt.h>
59#include <iprt/initterm.h>
60#include <iprt/ldr.h>
61#include <iprt/log.h>
62#include <iprt/mem.h>
63#include <iprt/once.h>
64#include <iprt/path.h>
65#include <iprt/pipe.h>
66#include <iprt/string.h>
67#include <iprt/socket.h>
68
69
70
71/*********************************************************************************************************************************
72* Structures and Typedefs *
73*********************************************************************************************************************************/
74/* kernel32.dll: */
75//typedef DWORD (WINAPI *PFNWTSGETACTIVECONSOLESESSIONID)(VOID);
76typedef HANDLE (WINAPI *PFNCREATETOOLHELP32SNAPSHOT)(DWORD, DWORD);
77typedef BOOL (WINAPI *PFNPROCESS32FIRST)(HANDLE, LPPROCESSENTRY32);
78typedef BOOL (WINAPI *PFNPROCESS32FIRSTW)(HANDLE, LPPROCESSENTRY32W);
79typedef BOOL (WINAPI *PFNPROCESS32NEXT)(HANDLE, LPPROCESSENTRY32);
80typedef BOOL (WINAPI *PFNPROCESS32NEXTW)(HANDLE, LPPROCESSENTRY32W);
81
82/* psapi.dll: */
83typedef BOOL (WINAPI *PFNENUMPROCESSES)(LPDWORD, DWORD, LPDWORD);
84typedef DWORD (WINAPI *PFNGETMODULEBASENAME)(HANDLE, HMODULE, LPTSTR, DWORD);
85
86/* advapi32.dll: */
87typedef BOOL (WINAPI *PFNCREATEPROCESSWITHLOGON)(LPCWSTR, LPCWSTR, LPCWSTR, DWORD, LPCWSTR, LPWSTR, DWORD,
88 LPVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION);
89typedef NTSTATUS (NTAPI *PFNLSALOOKUPNAMES2)(LSA_HANDLE, ULONG, ULONG, PLSA_UNICODE_STRING,
90 PLSA_REFERENCED_DOMAIN_LIST*, PLSA_TRANSLATED_SID2*);
91
92/* userenv.dll: */
93typedef BOOL (WINAPI *PFNCREATEENVIRONMENTBLOCK)(LPVOID *, HANDLE, BOOL);
94typedef BOOL (WINAPI *PFNPFNDESTROYENVIRONMENTBLOCK)(LPVOID);
95typedef BOOL (WINAPI *PFNLOADUSERPROFILEW)(HANDLE, LPPROFILEINFOW);
96typedef BOOL (WINAPI *PFNUNLOADUSERPROFILE)(HANDLE, HANDLE);
97
98
99/*********************************************************************************************************************************
100* Global Variables *
101*********************************************************************************************************************************/
102/** Init once structure. */
103static RTONCE g_rtProcWinInitOnce = RTONCE_INITIALIZER;
104/** Critical section protecting the process array. */
105static RTCRITSECT g_CritSect;
106/** The number of processes in the array. */
107static uint32_t g_cProcesses;
108/** The current allocation size. */
109static uint32_t g_cProcessesAlloc;
110/** Array containing the live or non-reaped child processes. */
111static struct RTPROCWINENTRY
112{
113 /** The process ID. */
114 ULONG_PTR pid;
115 /** The process handle. */
116 HANDLE hProcess;
117} *g_paProcesses;
118
119/** Structure for storing a user's account info.
120 * Must be free'd with rtProcWinFreeAccountInfo(). */
121typedef struct RTPROCWINACCOUNTINFO
122{
123 /** User name. */
124 PRTUTF16 pwszUserName;
125 /** Domain this account is tied to. Can be NULL if no domain is being used. */
126 PRTUTF16 pwszDomain;
127} RTPROCWINACCOUNTINFO, *PRTPROCWINACCOUNTINFO;
128
129/** @name userenv.dll imports (we don't unload it).
130 * They're all optional. So in addition to using g_rtProcWinResolveOnce, the
131 * caller must also check if any of the necessary APIs are NULL pointers.
132 * @{ */
133/** Init once structure for run-as-user functions we need. */
134static RTONCE g_rtProcWinResolveOnce = RTONCE_INITIALIZER;
135/* kernel32.dll: */
136static PFNCREATETOOLHELP32SNAPSHOT g_pfnCreateToolhelp32Snapshot = NULL;
137static PFNPROCESS32FIRST g_pfnProcess32First = NULL;
138static PFNPROCESS32NEXT g_pfnProcess32Next = NULL;
139static PFNPROCESS32FIRSTW g_pfnProcess32FirstW = NULL;
140static PFNPROCESS32NEXTW g_pfnProcess32NextW = NULL;
141/* psapi.dll: */
142static PFNGETMODULEBASENAME g_pfnGetModuleBaseName = NULL;
143static PFNENUMPROCESSES g_pfnEnumProcesses = NULL;
144/* advapi32.dll: */
145static PFNCREATEPROCESSWITHLOGON g_pfnCreateProcessWithLogonW = NULL;
146static PFNLSALOOKUPNAMES2 g_pfnLsaLookupNames2 = NULL;
147/* userenv.dll: */
148static PFNCREATEENVIRONMENTBLOCK g_pfnCreateEnvironmentBlock = NULL;
149static PFNPFNDESTROYENVIRONMENTBLOCK g_pfnDestroyEnvironmentBlock = NULL;
150static PFNLOADUSERPROFILEW g_pfnLoadUserProfileW = NULL;
151static PFNUNLOADUSERPROFILE g_pfnUnloadUserProfile = NULL;
152/** @} */
153
154
155/*********************************************************************************************************************************
156* Internal Functions *
157*********************************************************************************************************************************/
158static int rtProcWinFindExe(uint32_t fFlags, RTENV hEnv, const char *pszExec, PRTUTF16 *ppwszExec);
159static int rtProcWinCreateEnvBlockAndFindExe(uint32_t fFlags, RTENV hEnv, const char *pszExec,
160 PRTUTF16 *ppwszzBlock, PRTUTF16 *ppwszExec);
161
162
163/**
164 * Clean up the globals.
165 *
166 * @param enmReason Ignored.
167 * @param iStatus Ignored.
168 * @param pvUser Ignored.
169 */
170static DECLCALLBACK(void) rtProcWinTerm(RTTERMREASON enmReason, int32_t iStatus, void *pvUser)
171{
172 NOREF(pvUser); NOREF(iStatus); NOREF(enmReason);
173
174 RTCritSectDelete(&g_CritSect);
175
176 size_t i = g_cProcesses;
177 while (i-- > 0)
178 {
179 CloseHandle(g_paProcesses[i].hProcess);
180 g_paProcesses[i].hProcess = NULL;
181 }
182 RTMemFree(g_paProcesses);
183
184 g_paProcesses = NULL;
185 g_cProcesses = 0;
186 g_cProcessesAlloc = 0;
187}
188
189
190/**
191 * Initialize the globals.
192 *
193 * @returns IPRT status code.
194 * @param pvUser Ignored.
195 */
196static DECLCALLBACK(int32_t) rtProcWinInitOnce(void *pvUser)
197{
198 NOREF(pvUser);
199
200 g_cProcesses = 0;
201 g_cProcessesAlloc = 0;
202 g_paProcesses = NULL;
203 int rc = RTCritSectInit(&g_CritSect);
204 if (RT_SUCCESS(rc))
205 {
206 /** @todo init once, terminate once - this is a generic thing which should
207 * have some kind of static and simpler setup! */
208 rc = RTTermRegisterCallback(rtProcWinTerm, NULL);
209 if (RT_SUCCESS(rc))
210 return rc;
211 RTCritSectDelete(&g_CritSect);
212 }
213 return rc;
214}
215
216
217/**
218 * Gets the process handle for a process from g_paProcesses.
219 *
220 * @returns Process handle if found, NULL if not.
221 * @param pid The process to remove (pid).
222 */
223static HANDLE rtProcWinFindPid(RTPROCESS pid)
224{
225 HANDLE hProcess = NULL;
226
227 RTCritSectEnter(&g_CritSect);
228 uint32_t i = g_cProcesses;
229 while (i-- > 0)
230 if (g_paProcesses[i].pid == pid)
231 {
232 hProcess = g_paProcesses[i].hProcess;
233 break;
234 }
235 RTCritSectLeave(&g_CritSect);
236
237 return hProcess;
238}
239
240
241/**
242 * Removes a process from g_paProcesses and closes the process handle.
243 *
244 * @param pid The process to remove (pid).
245 */
246static void rtProcWinRemovePid(RTPROCESS pid)
247{
248 RTCritSectEnter(&g_CritSect);
249 uint32_t i = g_cProcesses;
250 while (i-- > 0)
251 if (g_paProcesses[i].pid == pid)
252 {
253 HANDLE hProcess = g_paProcesses[i].hProcess;
254
255 g_cProcesses--;
256 uint32_t cToMove = g_cProcesses - i;
257 if (cToMove)
258 memmove(&g_paProcesses[i], &g_paProcesses[i + 1], cToMove * sizeof(g_paProcesses[0]));
259
260 RTCritSectLeave(&g_CritSect);
261 CloseHandle(hProcess);
262 return;
263 }
264 RTCritSectLeave(&g_CritSect);
265}
266
267
268/**
269 * Adds a process to g_paProcesses.
270 *
271 * @returns IPRT status code.
272 * @param pid The process id.
273 * @param hProcess The process handle.
274 */
275static int rtProcWinAddPid(RTPROCESS pid, HANDLE hProcess)
276{
277 RTCritSectEnter(&g_CritSect);
278
279 uint32_t i = g_cProcesses;
280 if (i >= g_cProcessesAlloc)
281 {
282 void *pvNew = RTMemRealloc(g_paProcesses, (i + 16) * sizeof(g_paProcesses[0]));
283 if (RT_UNLIKELY(!pvNew))
284 {
285 RTCritSectLeave(&g_CritSect);
286 return VERR_NO_MEMORY;
287 }
288 g_paProcesses = (struct RTPROCWINENTRY *)pvNew;
289 g_cProcessesAlloc = i + 16;
290 }
291
292 g_paProcesses[i].pid = pid;
293 g_paProcesses[i].hProcess = hProcess;
294 g_cProcesses = i + 1;
295
296 RTCritSectLeave(&g_CritSect);
297 return VINF_SUCCESS;
298}
299
300
301/**
302 * Initialize the import APIs for run-as-user and special environment support.
303 *
304 * @returns IPRT status code.
305 * @param pvUser Ignored.
306 */
307static DECLCALLBACK(int) rtProcWinResolveOnce(void *pvUser)
308{
309 int rc;
310 RTLDRMOD hMod;
311 RT_NOREF_PV(pvUser);
312
313 /*
314 * kernel32.dll APIs introduced after NT4.
315 */
316 g_pfnCreateToolhelp32Snapshot = (PFNCREATETOOLHELP32SNAPSHOT)GetProcAddress(g_hModKernel32, "CreateToolhelp32Snapshot");
317 g_pfnProcess32First = (PFNPROCESS32FIRST )GetProcAddress(g_hModKernel32, "Process32First");
318 g_pfnProcess32FirstW = (PFNPROCESS32FIRSTW )GetProcAddress(g_hModKernel32, "Process32FirstW");
319 g_pfnProcess32Next = (PFNPROCESS32NEXT )GetProcAddress(g_hModKernel32, "Process32Next");
320 g_pfnProcess32NextW = (PFNPROCESS32NEXTW )GetProcAddress(g_hModKernel32, "Process32NextW");
321
322 /*
323 * psapi.dll APIs, if none of the above are available.
324 */
325 if ( !g_pfnCreateToolhelp32Snapshot
326 || !g_pfnProcess32First
327 || !g_pfnProcess32Next)
328 {
329 Assert(!g_pfnCreateToolhelp32Snapshot && !g_pfnProcess32First && !g_pfnProcess32Next);
330
331 rc = RTLdrLoadSystem("psapi.dll", true /*fNoUnload*/, &hMod);
332 if (RT_SUCCESS(rc))
333 {
334 rc = RTLdrGetSymbol(hMod, "GetModuleBaseName", (void **)&g_pfnGetModuleBaseName);
335 AssertStmt(RT_SUCCESS(rc), g_pfnGetModuleBaseName = NULL);
336
337 rc = RTLdrGetSymbol(hMod, "EnumProcesses", (void **)&g_pfnEnumProcesses);
338 AssertStmt(RT_SUCCESS(rc), g_pfnEnumProcesses = NULL);
339
340 RTLdrClose(hMod);
341 }
342 }
343
344 /*
345 * advapi32.dll APIs.
346 */
347 rc = RTLdrLoadSystem("advapi32.dll", true /*fNoUnload*/, &hMod);
348 if (RT_SUCCESS(rc))
349 {
350 rc = RTLdrGetSymbol(hMod, "CreateProcessWithLogonW", (void **)&g_pfnCreateProcessWithLogonW);
351 AssertStmt(RT_SUCCESS(rc), g_pfnCreateProcessWithLogonW = NULL);
352
353 rc = RTLdrGetSymbol(hMod, "LsaLookupNames2", (void **)&g_pfnLsaLookupNames2);
354 AssertStmt(RT_SUCCESS(rc), g_pfnLsaLookupNames2 = NULL);
355
356 RTLdrClose(hMod);
357 }
358
359 /*
360 * userenv.dll APIs.
361 */
362 rc = RTLdrLoadSystem("userenv.dll", true /*fNoUnload*/, &hMod);
363 if (RT_SUCCESS(rc))
364 {
365 rc = RTLdrGetSymbol(hMod, "LoadUserProfileW", (void **)&g_pfnLoadUserProfileW);
366 AssertStmt(RT_SUCCESS(rc), g_pfnLoadUserProfileW = NULL);
367
368 rc = RTLdrGetSymbol(hMod, "UnloadUserProfile", (void **)&g_pfnUnloadUserProfile);
369 AssertStmt(RT_SUCCESS(rc), g_pfnUnloadUserProfile = NULL);
370
371 rc = RTLdrGetSymbol(hMod, "CreateEnvironmentBlock", (void **)&g_pfnCreateEnvironmentBlock);
372 AssertStmt(RT_SUCCESS(rc), g_pfnCreateEnvironmentBlock = NULL);
373
374 rc = RTLdrGetSymbol(hMod, "DestroyEnvironmentBlock", (void **)&g_pfnDestroyEnvironmentBlock);
375 AssertStmt(RT_SUCCESS(rc), g_pfnDestroyEnvironmentBlock = NULL);
376
377 RTLdrClose(hMod);
378 }
379
380 return VINF_SUCCESS;
381}
382
383
384RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)
385{
386 return RTProcCreateEx(pszExec, papszArgs, Env, fFlags,
387 NULL, NULL, NULL, /* standard handles */
388 NULL /*pszAsUser*/, NULL /* pszPassword*/,
389 pProcess);
390}
391
392
393/**
394 * Get the process token of the process indicated by @a dwPID if the @a pSid
395 * matches.
396 *
397 * @returns IPRT status code.
398 * @param dwPid The process identifier.
399 * @param pSid The secure identifier of the user.
400 * @param phToken Where to return the a duplicate of the process token
401 * handle on success. (The caller closes it.)
402 */
403static int rtProcWinGetProcessTokenHandle(DWORD dwPid, PSID pSid, PHANDLE phToken)
404{
405 AssertPtr(pSid);
406 AssertPtr(phToken);
407
408 int rc;
409 HANDLE hProc = OpenProcess(MAXIMUM_ALLOWED, TRUE, dwPid);
410 if (hProc != NULL)
411 {
412 HANDLE hTokenProc;
413 if (OpenProcessToken(hProc,
414 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE
415 | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE,
416 &hTokenProc))
417 {
418 SetLastError(NO_ERROR);
419 DWORD dwSize = 0;
420 BOOL fRc = GetTokenInformation(hTokenProc, TokenUser, NULL, 0, &dwSize);
421 DWORD dwErr = GetLastError();
422 if ( !fRc
423 && dwErr == ERROR_INSUFFICIENT_BUFFER
424 && dwSize > 0)
425 {
426 PTOKEN_USER pTokenUser = (PTOKEN_USER)RTMemTmpAllocZ(dwSize);
427 if (pTokenUser)
428 {
429 if (GetTokenInformation(hTokenProc, TokenUser, pTokenUser, dwSize, &dwSize))
430 {
431 if ( IsValidSid(pTokenUser->User.Sid)
432 && EqualSid(pTokenUser->User.Sid, pSid))
433 {
434 if (DuplicateTokenEx(hTokenProc, MAXIMUM_ALLOWED,
435 NULL, SecurityIdentification, TokenPrimary, phToken))
436 {
437 /*
438 * So we found the process instance which belongs to the user we want to
439 * to run our new process under. This duplicated token will be used for
440 * the actual CreateProcessAsUserW() call then.
441 */
442 rc = VINF_SUCCESS;
443 }
444 else
445 rc = RTErrConvertFromWin32(GetLastError());
446 }
447 else
448 rc = VERR_NOT_FOUND;
449 }
450 else
451 rc = RTErrConvertFromWin32(GetLastError());
452 RTMemTmpFree(pTokenUser);
453 }
454 else
455 rc = VERR_NO_MEMORY;
456 }
457 else if (fRc || dwErr == NO_ERROR)
458 rc = VERR_IPE_UNEXPECTED_STATUS;
459 else
460 rc = RTErrConvertFromWin32(dwErr);
461 CloseHandle(hTokenProc);
462 }
463 else
464 rc = RTErrConvertFromWin32(GetLastError());
465 CloseHandle(hProc);
466 }
467 else
468 rc = RTErrConvertFromWin32(GetLastError());
469 return rc;
470}
471
472
473/**
474 * Fallback method for rtProcWinFindTokenByProcess that uses the older NT4
475 * PSAPI.DLL API.
476 *
477 * @returns Success indicator.
478 * @param papszNames The process candidates, in prioritized order.
479 * @param pSid The secure identifier of the user.
480 * @param phToken Where to return the token handle - duplicate,
481 * caller closes it on success.
482 *
483 * @remarks NT4 needs a copy of "PSAPI.dll" (redistributed by Microsoft and not
484 * part of the OS) in order to get a lookup. If we don't have this DLL
485 * we are not able to get a token and therefore no UI will be visible.
486 */
487static bool rtProcWinFindTokenByProcessAndPsApi(const char * const *papszNames, PSID pSid, PHANDLE phToken)
488{
489 /*
490 * Load PSAPI.DLL and resolve the two symbols we need.
491 */
492 if ( !g_pfnGetModuleBaseName
493 || !g_pfnEnumProcesses)
494 return false;
495
496 /*
497 * Get a list of PID. We retry if it looks like there are more PIDs
498 * to be returned than what we supplied buffer space for.
499 */
500 bool fFound = false;
501 int rc = VINF_SUCCESS;
502 DWORD cbPidsAllocated = 4096;
503 DWORD cbPidsReturned = 0; /* (MSC maybe used uninitialized) */
504 DWORD *paPids;
505 for (;;)
506 {
507 paPids = (DWORD *)RTMemTmpAlloc(cbPidsAllocated);
508 AssertBreakStmt(paPids, rc = VERR_NO_TMP_MEMORY);
509 cbPidsReturned = 0;
510 if (!g_pfnEnumProcesses(paPids, cbPidsAllocated, &cbPidsReturned))
511 {
512 rc = RTErrConvertFromWin32(GetLastError());
513 AssertMsgFailedBreak(("%Rrc\n", rc));
514 }
515 if ( cbPidsReturned < cbPidsAllocated
516 || cbPidsAllocated >= _512K)
517 break;
518 RTMemTmpFree(paPids);
519 cbPidsAllocated *= 2;
520 }
521 if (RT_SUCCESS(rc))
522 {
523 /*
524 * Search for the process.
525 *
526 * We ASSUME that the caller won't be specifying any names longer
527 * than RTPATH_MAX.
528 */
529 DWORD cbProcName = RTPATH_MAX;
530 char *pszProcName = (char *)RTMemTmpAlloc(RTPATH_MAX);
531 if (pszProcName)
532 {
533 for (size_t i = 0; papszNames[i] && !fFound; i++)
534 {
535 const DWORD cPids = cbPidsReturned / sizeof(DWORD);
536 for (DWORD iPid = 0; iPid < cPids && !fFound; iPid++)
537 {
538 HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, paPids[iPid]);
539 if (hProc)
540 {
541 *pszProcName = '\0';
542 DWORD cbRet = g_pfnGetModuleBaseName(hProc, 0 /*hModule = exe */, pszProcName, cbProcName);
543 if ( cbRet > 0
544 && _stricmp(pszProcName, papszNames[i]) == 0
545 && RT_SUCCESS(rtProcWinGetProcessTokenHandle(paPids[iPid], pSid, phToken)))
546 fFound = true;
547 CloseHandle(hProc);
548 }
549 }
550 }
551 RTMemTmpFree(pszProcName);
552 }
553 else
554 rc = VERR_NO_TMP_MEMORY;
555 }
556 RTMemTmpFree(paPids);
557
558 return fFound;
559}
560
561
562/**
563 * Finds a one of the processes in @a papszNames running with user @a pSid and
564 * returns a duplicate handle to its token.
565 *
566 * @returns Success indicator.
567 * @param papszNames The process candidates, in prioritized order.
568 * @param pSid The secure identifier of the user.
569 * @param phToken Where to return the token handle - duplicate,
570 * caller closes it on success.
571 */
572static bool rtProcWinFindTokenByProcess(const char * const *papszNames, PSID pSid, PHANDLE phToken)
573{
574 AssertPtr(papszNames);
575 AssertPtr(pSid);
576 AssertPtr(phToken);
577
578 bool fFound = false;
579
580 /*
581 * On modern systems (W2K+) try the Toolhelp32 API first; this is more stable
582 * and reliable. Fallback to EnumProcess on NT4.
583 */
584 bool fFallback = true;
585 if (g_pfnProcess32Next && g_pfnProcess32First && g_pfnCreateToolhelp32Snapshot)
586 {
587 HANDLE hSnap = g_pfnCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
588 Assert(hSnap != INVALID_HANDLE_VALUE);
589 if (hSnap != INVALID_HANDLE_VALUE)
590 {
591 fFallback = false;
592 for (size_t i = 0; papszNames[i] && !fFound; i++)
593 {
594 PROCESSENTRY32 ProcEntry;
595 ProcEntry.dwSize = sizeof(PROCESSENTRY32);
596/** @todo use W APIs here. */
597 if (g_pfnProcess32First(hSnap, &ProcEntry))
598 {
599 do
600 {
601 if (_stricmp(ProcEntry.szExeFile, papszNames[i]) == 0)
602 {
603 int rc = rtProcWinGetProcessTokenHandle(ProcEntry.th32ProcessID, pSid, phToken);
604 if (RT_SUCCESS(rc))
605 {
606 fFound = true;
607 break;
608 }
609 }
610 } while (g_pfnProcess32Next(hSnap, &ProcEntry));
611 }
612#ifdef RT_STRICT
613 else
614 {
615 DWORD dwErr = GetLastError();
616 AssertMsgFailed(("dwErr=%u (%x)\n", dwErr, dwErr));
617 }
618#endif
619 }
620 CloseHandle(hSnap);
621 }
622 }
623
624 /* If we couldn't take a process snapshot for some reason or another, fall
625 back on the NT4 compatible API. */
626 if (fFallback)
627 fFound = rtProcWinFindTokenByProcessAndPsApi(papszNames, pSid, phToken);
628 return fFound;
629}
630
631
632/**
633 * Logs on a specified user and returns its primary token.
634 *
635 * @returns IPRT status code.
636 * @param pwszUser User name. A domain name can be specified (as part of a UPN, User Principal Name),
637 * e.g. "joedoe@example.com".
638 * @param pwszPassword Password.
639 * @param phToken Pointer to store the logon token.
640 */
641static int rtProcWinUserLogon(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, HANDLE *phToken)
642{
643 AssertPtrReturn(pwszUser, VERR_INVALID_POINTER);
644 AssertPtrReturn(pwszPassword, VERR_INVALID_POINTER);
645 AssertPtrReturn(phToken, VERR_INVALID_POINTER);
646
647 /*
648 * Because we have to deal with http://support.microsoft.com/kb/245683
649 * for NULL domain names when running on NT4 here, pass an empty string if so.
650 * However, passing FQDNs should work!
651 *
652 * The SE_TCB_NAME (Policy: Act as part of the operating system) right
653 * is required on older windows versions (NT4, W2K, possibly XP).
654 */
655 PCRTUTF16 pwszDomainNone = g_enmWinVer < kRTWinOSType_2K ? L"" /* NT4 and older */ : NULL /* Windows 2000 and up */;
656 BOOL fRc = LogonUserW(pwszUser,
657 /* The domain always is passed as part of the UPN (user name). */
658 pwszDomainNone,
659 pwszPassword,
660 LOGON32_LOGON_INTERACTIVE,
661 LOGON32_PROVIDER_DEFAULT,
662 phToken);
663 if (fRc)
664 return VINF_SUCCESS;
665
666 DWORD dwErr = GetLastError();
667 int rc = dwErr == ERROR_PRIVILEGE_NOT_HELD ? VERR_PROC_TCB_PRIV_NOT_HELD : RTErrConvertFromWin32(dwErr);
668 if (rc == VERR_UNRESOLVED_ERROR)
669 LogRelFunc(("dwErr=%u (%#x), rc=%Rrc\n", dwErr, dwErr, rc));
670 return rc;
671}
672
673
674/**
675 * Returns the environment to use for the child process.
676 *
677 * This implements the RTPROC_FLAGS_ENV_CHANGE_RECORD and environment related
678 * parts of RTPROC_FLAGS_PROFILE.
679 *
680 * @returns IPRT status code.
681 * @param hToken The user token to use if RTPROC_FLAGS_PROFILE is given.
682 * The caller must have loaded profile for this.
683 * @param hEnv The environment passed in by the RTProcCreateEx caller.
684 * @param fFlags The process creation flags passed in by the
685 * RTProcCreateEx caller (RTPROC_FLAGS_XXX).
686 * @param phEnv Where to return the environment to use. This can either
687 * be a newly created environment block or @a hEnv. In the
688 * former case, the caller must destroy it.
689 */
690static int rtProcWinCreateEnvFromToken(HANDLE hToken, RTENV hEnv, uint32_t fFlags, PRTENV phEnv)
691{
692 int rc;
693
694 /*
695 * Query the environment from the user profile associated with the token if
696 * the caller has specified it directly or indirectly.
697 */
698 if ( (fFlags & RTPROC_FLAGS_PROFILE)
699 && ( hEnv == RTENV_DEFAULT
700 || (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)) )
701 {
702 if (g_pfnCreateEnvironmentBlock && g_pfnDestroyEnvironmentBlock)
703 {
704 LPVOID pvEnvBlockProfile = NULL;
705 if (g_pfnCreateEnvironmentBlock(&pvEnvBlockProfile, hToken, FALSE /* Don't inherit from parent. */))
706 {
707 rc = RTEnvCloneUtf16Block(phEnv, (PCRTUTF16)pvEnvBlockProfile, 0 /*fFlags*/);
708 if ( (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)
709 && RT_SUCCESS(rc)
710 && hEnv != RTENV_DEFAULT)
711 {
712 rc = RTEnvApplyChanges(*phEnv, hEnv);
713 if (RT_FAILURE(rc))
714 RTEnvDestroy(*phEnv);
715 }
716 g_pfnDestroyEnvironmentBlock(pvEnvBlockProfile);
717 }
718 else
719 rc = RTErrConvertFromWin32(GetLastError());
720 }
721 else
722 rc = VERR_SYMBOL_NOT_FOUND;
723 }
724 /*
725 * We we've got an incoming change record, combine it with the default environment.
726 */
727 else if (hEnv != RTENV_DEFAULT && (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD))
728 {
729 rc = RTEnvClone(phEnv, RTENV_DEFAULT);
730 if (RT_SUCCESS(rc))
731 {
732 rc = RTEnvApplyChanges(*phEnv, hEnv);
733 if (RT_FAILURE(rc))
734 RTEnvDestroy(*phEnv);
735 }
736 }
737 /*
738 * Otherwise we can return the incoming environment directly.
739 */
740 else
741 {
742 *phEnv = hEnv;
743 rc = VINF_SUCCESS;
744 }
745
746 return rc;
747}
748
749
750/**
751 * Figures which privilege we're missing for success application of
752 * CreateProcessAsUserW.
753 *
754 * @returns IPRT error status.
755 */
756static int rtProcWinFigureWhichPrivilegeNotHeld2(void)
757{
758 HANDLE hToken;
759 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken))
760 {
761 static struct
762 {
763 const char *pszName;
764 int rc;
765 } const s_aPrivileges[] =
766 {
767 { SE_TCB_NAME, VERR_PROC_TCB_PRIV_NOT_HELD },
768 { SE_ASSIGNPRIMARYTOKEN_NAME, VERR_PROC_APT_PRIV_NOT_HELD },
769 { SE_INCREASE_QUOTA_NAME, VERR_PROC_IQ_PRIV_NOT_HELD },
770 };
771 for (uint32_t i = 0; i < RT_ELEMENTS(s_aPrivileges); i++)
772 {
773 union
774 {
775 TOKEN_PRIVILEGES TokPriv;
776 char abAlloced[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)];
777 } uNew, uOld;
778 uNew.TokPriv.PrivilegeCount = 1;
779 uNew.TokPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
780 AssertContinue(LookupPrivilegeValue(NULL, s_aPrivileges[i].pszName, &uNew.TokPriv.Privileges[0].Luid));
781 uOld = uNew;
782 SetLastError(NO_ERROR);
783 DWORD cbActual = RT_OFFSETOF(TOKEN_PRIVILEGES, Privileges[1]);
784 AdjustTokenPrivileges(hToken, FALSE /*fDisableAllPrivileges*/, &uNew.TokPriv, cbActual, &uOld.TokPriv, &cbActual);
785 if (GetLastError() != NO_ERROR)
786 {
787 CloseHandle(hToken);
788 return s_aPrivileges[i].rc;
789 }
790 if (uOld.TokPriv.Privileges[0].Attributes == 0)
791 AdjustTokenPrivileges(hToken, FALSE /*fDisableAllPrivileges*/, &uOld.TokPriv, 0, NULL, NULL);
792 }
793 AssertFailed();
794 CloseHandle(hToken);
795 }
796 else
797 AssertFailed();
798 return VERR_PRIVILEGE_NOT_HELD;
799}
800
801#if 0 /* debug code */
802
803static char *rtProcWinSidToString(char *psz, PSID pSid)
804{
805 char *pszRet = psz;
806
807 *psz++ = 'S';
808 *psz++ = '-';
809 *psz++ = '1';
810 *psz++ = '-';
811
812 PISID pISid = (PISID)pSid;
813
814 psz += RTStrFormatU32(psz, 32, RT_MAKE_U32_FROM_U8(pISid->IdentifierAuthority.Value[5],
815 pISid->IdentifierAuthority.Value[4],
816 pISid->IdentifierAuthority.Value[3],
817 pISid->IdentifierAuthority.Value[2]),
818 10, 0, 0, 0);
819 for (unsigned i = 0; i < pISid->SubAuthorityCount; i++)
820 {
821 *psz++ = '-';
822 psz += RTStrFormatU32(psz, 32, pISid->SubAuthority[i], 10, 0, 0, 0);
823 }
824 *psz++ = '\0';
825 return pszRet;
826}
827
828static void rtProcWinLogAcl(PACL pAcl)
829{
830 if (!pAcl)
831 RTAssertMsg2("ACL is NULL\n");
832 else
833 {
834 RTAssertMsg2("AceCount=%d AclSize=%#x AclRevision=%d\n", pAcl->AceCount, pAcl->AclSize, pAcl->AclRevision);
835 for (uint32_t i = 0; i < pAcl->AceCount; i++)
836 {
837 PACE_HEADER pAceHdr = NULL;
838 if (GetAce(pAcl, i, (PVOID *)&pAceHdr))
839 {
840 RTAssertMsg2(" ACE[%u]: Flags=%#x Type=%#x Size=%#x", i, pAceHdr->AceFlags, pAceHdr->AceType, pAceHdr->AceSize);
841 char szTmp[256];
842 if (pAceHdr->AceType == ACCESS_ALLOWED_ACE_TYPE)
843 RTAssertMsg2(" Mask=%#x %s\n", ((ACCESS_ALLOWED_ACE *)pAceHdr)->Mask,
844 rtProcWinSidToString(szTmp, &((ACCESS_ALLOWED_ACE *)pAceHdr)->SidStart));
845 else
846 RTAssertMsg2(" ACE[%u]: Flags=%#x Type=%#x Size=%#x\n", i, pAceHdr->AceFlags, pAceHdr->AceType, pAceHdr->AceSize);
847 }
848 }
849 }
850}
851
852static bool rtProcWinLogSecAttr(HANDLE hUserObj)
853{
854 /*
855 * Get the security descriptor for the user interface object.
856 */
857 uint32_t cbSecDesc = _64K;
858 PSECURITY_DESCRIPTOR pSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc);
859 SECURITY_INFORMATION SecInfo = DACL_SECURITY_INFORMATION;
860 DWORD cbNeeded;
861 AssertReturn(pSecDesc, false);
862 if (!GetUserObjectSecurity(hUserObj, &SecInfo, pSecDesc, cbSecDesc, &cbNeeded))
863 {
864 RTMemTmpFree(pSecDesc);
865 AssertReturn(GetLastError() == ERROR_INSUFFICIENT_BUFFER, false);
866 cbSecDesc = cbNeeded + 128;
867 pSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc);
868 AssertReturn(pSecDesc, false);
869 if (!GetUserObjectSecurity(hUserObj, &SecInfo, pSecDesc, cbSecDesc, &cbNeeded))
870 {
871 RTMemTmpFree(pSecDesc);
872 AssertFailedReturn(false);
873 }
874 }
875
876 /*
877 * Get the discretionary access control list (if we have one).
878 */
879 BOOL fDaclDefaulted;
880 BOOL fDaclPresent;
881 PACL pDacl;
882 if (GetSecurityDescriptorDacl(pSecDesc, &fDaclPresent, &pDacl, &fDaclDefaulted))
883 rtProcWinLogAcl(pDacl);
884 else
885 RTAssertMsg2("GetSecurityDescriptorDacl failed\n");
886
887 RTMemFree(pSecDesc);
888 return true;
889}
890
891#endif /* debug */
892
893/**
894 * Get the user SID from a token.
895 *
896 * @returns Pointer to the SID on success. Free by calling RTMemFree.
897 * @param hToken The token..
898 */
899static PSID rtProcWinGetTokenUserSid(HANDLE hToken)
900{
901 /*
902 * Get the groups associated with the token. We just try a size first then
903 * reallocates if it's insufficient.
904 */
905 DWORD cbUser = _1K;
906 PTOKEN_USER pUser = (PTOKEN_USER)RTMemTmpAlloc(cbUser);
907 AssertReturn(pUser, NULL);
908 DWORD cbNeeded = 0;
909 if (!GetTokenInformation(hToken, TokenUser, pUser, cbUser, &cbNeeded))
910 {
911 RTMemTmpFree(pUser);
912 AssertReturn(GetLastError() == ERROR_INSUFFICIENT_BUFFER, NULL);
913 cbUser = cbNeeded + 128;
914 pUser = (PTOKEN_USER)RTMemTmpAlloc(cbUser);
915 AssertReturn(pUser, NULL);
916 if (!GetTokenInformation(hToken, TokenUser, pUser, cbUser, &cbNeeded))
917 {
918 RTMemTmpFree(pUser);
919 AssertFailedReturn(NULL);
920 }
921 }
922
923 DWORD cbSid = GetLengthSid(pUser->User.Sid);
924 PSID pSidRet = RTMemDup(pUser->User.Sid, cbSid);
925 Assert(pSidRet);
926 RTMemTmpFree(pUser);
927 return pSidRet;
928}
929
930
931#if 0 /* not used */
932/**
933 * Get the login SID from a token.
934 *
935 * @returns Pointer to the SID on success. Free by calling RTMemFree.
936 * @param hToken The token..
937 */
938static PSID rtProcWinGetTokenLogonSid(HANDLE hToken)
939{
940 /*
941 * Get the groups associated with the token. We just try a size first then
942 * reallocates if it's insufficient.
943 */
944 DWORD cbGroups = _1K;
945 PTOKEN_GROUPS pGroups = (PTOKEN_GROUPS)RTMemTmpAlloc(cbGroups);
946 AssertReturn(pGroups, NULL);
947 DWORD cbNeeded = 0;
948 if (!GetTokenInformation(hToken, TokenGroups, pGroups, cbGroups, &cbNeeded))
949 {
950 RTMemTmpFree(pGroups);
951 AssertReturn(GetLastError() == ERROR_INSUFFICIENT_BUFFER, NULL);
952 cbGroups = cbNeeded + 128;
953 pGroups = (PTOKEN_GROUPS)RTMemTmpAlloc(cbGroups);
954 AssertReturn(pGroups, NULL);
955 if (!GetTokenInformation(hToken, TokenGroups, pGroups, cbGroups, &cbNeeded))
956 {
957 RTMemTmpFree(pGroups);
958 AssertFailedReturn(NULL);
959 }
960 }
961
962 /*
963 * Locate the logon sid.
964 */
965 PSID pSidRet = NULL;
966 uint32_t i = pGroups->GroupCount;
967 while (i-- > 0)
968 if ((pGroups->Groups[i].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID)
969 {
970 DWORD cbSid = GetLengthSid(pGroups->Groups[i].Sid);
971 pSidRet = RTMemDup(pGroups->Groups[i].Sid, cbSid);
972 break;
973 }
974
975 RTMemTmpFree(pGroups);
976 Assert(pSidRet);
977 return pSidRet;
978}
979#endif /* unused */
980
981
982/**
983 * Retrieves the DACL security descriptor of the give GUI object.
984 *
985 * @returns Pointer to the security descriptor.
986 * @param hUserObj The GUI object handle.
987 * @param pcbSecDesc Where to return the size of the security descriptor.
988 * @param ppDacl Where to return the DACL pointer.
989 * @param pfDaclPresent Where to return the DACL-present indicator.
990 * @param pDaclSizeInfo Where to return the DACL size information.
991 */
992static PSECURITY_DESCRIPTOR rtProcWinGetUserObjDacl(HANDLE hUserObj, uint32_t *pcbSecDesc, PACL *ppDacl,
993 BOOL *pfDaclPresent, ACL_SIZE_INFORMATION *pDaclSizeInfo)
994{
995 /*
996 * Get the security descriptor for the user interface object.
997 */
998 uint32_t cbSecDesc = _1K;
999 PSECURITY_DESCRIPTOR pSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc);
1000 SECURITY_INFORMATION SecInfo = DACL_SECURITY_INFORMATION;
1001 DWORD cbNeeded;
1002 AssertReturn(pSecDesc, NULL);
1003 if (!GetUserObjectSecurity(hUserObj, &SecInfo, pSecDesc, cbSecDesc, &cbNeeded))
1004 {
1005 RTMemTmpFree(pSecDesc);
1006 AssertReturn(GetLastError() == ERROR_INSUFFICIENT_BUFFER, NULL);
1007 cbSecDesc = cbNeeded + 128;
1008 pSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc);
1009 AssertReturn(pSecDesc, NULL);
1010 if (!GetUserObjectSecurity(hUserObj, &SecInfo, pSecDesc, cbSecDesc, &cbNeeded))
1011 {
1012 RTMemTmpFree(pSecDesc);
1013 AssertFailedReturn(NULL);
1014 }
1015 }
1016 *pcbSecDesc = cbNeeded;
1017
1018 /*
1019 * Get the discretionary access control list (if we have one).
1020 */
1021 BOOL fDaclDefaulted;
1022 if (GetSecurityDescriptorDacl(pSecDesc, pfDaclPresent, ppDacl, &fDaclDefaulted))
1023 {
1024 RT_ZERO(*pDaclSizeInfo);
1025 pDaclSizeInfo->AclBytesInUse = sizeof(ACL);
1026 if ( !*ppDacl
1027 || GetAclInformation(*ppDacl, pDaclSizeInfo, sizeof(*pDaclSizeInfo), AclSizeInformation))
1028 return pSecDesc;
1029 AssertFailed();
1030 }
1031 else
1032 AssertFailed();
1033 RTMemTmpFree(pSecDesc);
1034 return NULL;
1035}
1036
1037
1038/**
1039 * Copy ACEs from one ACL to another.
1040 *
1041 * @returns true on success, false on failure.
1042 * @param pDst The destination ACL.
1043 * @param pSrc The source ACL.
1044 * @param cAces The number of ACEs to copy.
1045 */
1046static bool rtProcWinCopyAces(PACL pDst, PACL pSrc, uint32_t cAces)
1047{
1048 for (uint32_t i = 0; i < cAces; i++)
1049 {
1050 PACE_HEADER pAceHdr;
1051 AssertReturn(GetAce(pSrc, i, (PVOID *)&pAceHdr), false);
1052 AssertReturn(AddAce(pDst, ACL_REVISION, MAXDWORD, pAceHdr, pAceHdr->AceSize), false);
1053 }
1054 return true;
1055}
1056
1057
1058/**
1059 * Adds an access-allowed access control entry to an ACL.
1060 *
1061 * @returns true on success, false on failure.
1062 * @param pDstAcl The ACL.
1063 * @param fAceFlags The ACE flags.
1064 * @param fMask The ACE access mask.
1065 * @param pSid The SID to go with the ACE.
1066 * @param cbSid The size of the SID.
1067 */
1068static bool rtProcWinAddAccessAllowedAce(PACL pDstAcl, uint32_t fAceFlags, uint32_t fMask, PSID pSid, uint32_t cbSid)
1069{
1070 struct
1071 {
1072 ACCESS_ALLOWED_ACE Core;
1073 DWORD abPadding[128]; /* More than enough, AFAIK. */
1074 } AceBuf;
1075 RT_ZERO(AceBuf);
1076 uint32_t const cbAllowedAce = RT_OFFSETOF(ACCESS_ALLOWED_ACE, SidStart) + cbSid;
1077 AssertReturn(cbAllowedAce <= sizeof(AceBuf), false);
1078
1079 AceBuf.Core.Header.AceSize = cbAllowedAce;
1080 AceBuf.Core.Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
1081 AceBuf.Core.Header.AceFlags = fAceFlags;
1082 AceBuf.Core.Mask = fMask;
1083 AssertReturn(CopySid(cbSid, &AceBuf.Core.SidStart, pSid), false);
1084
1085 uint32_t i = pDstAcl->AceCount;
1086 while (i-- > 0)
1087 {
1088 PACE_HEADER pAceHdr;
1089 AssertContinue(GetAce(pDstAcl, i, (PVOID *)&pAceHdr));
1090 if ( pAceHdr->AceSize == cbAllowedAce
1091 && memcmp(pAceHdr, &AceBuf.Core, cbAllowedAce) == 0)
1092 return true;
1093
1094 }
1095 AssertMsgReturn(AddAce(pDstAcl, ACL_REVISION, MAXDWORD, &AceBuf.Core, cbAllowedAce), ("%u\n", GetLastError()), false);
1096 return true;
1097}
1098
1099
1100/** All window station rights we know about */
1101#define MY_WINSTATION_ALL_RIGHTS ( WINSTA_ACCESSCLIPBOARD | WINSTA_ACCESSGLOBALATOMS | WINSTA_CREATEDESKTOP \
1102 | WINSTA_ENUMDESKTOPS | WINSTA_ENUMERATE | WINSTA_EXITWINDOWS | WINSTA_READATTRIBUTES \
1103 | WINSTA_READSCREEN | WINSTA_WRITEATTRIBUTES | DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER )
1104/** All desktop rights we know about */
1105#define MY_DESKTOP_ALL_RIGHTS ( DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL \
1106 | DESKTOP_JOURNALPLAYBACK | DESKTOP_JOURNALRECORD | DESKTOP_READOBJECTS \
1107 | DESKTOP_SWITCHDESKTOP | DESKTOP_WRITEOBJECTS | DELETE | READ_CONTROL | WRITE_DAC \
1108 | WRITE_OWNER )
1109/** Generic rights. */
1110#define MY_GENERIC_ALL_RIGHTS ( GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL )
1111
1112
1113/**
1114 * Grants the given SID full access to the given window station.
1115 *
1116 * @returns true on success, false on failure.
1117 * @param hWinStation The window station.
1118 * @param pSid The SID.
1119 */
1120static bool rtProcWinAddSidToWinStation(HWINSTA hWinStation, PSID pSid)
1121{
1122 bool fRet = false;
1123
1124 /*
1125 * Get the current DACL.
1126 */
1127 uint32_t cbSecDesc;
1128 PACL pDacl;
1129 ACL_SIZE_INFORMATION DaclSizeInfo;
1130 BOOL fDaclPresent;
1131 PSECURITY_DESCRIPTOR pSecDesc = rtProcWinGetUserObjDacl(hWinStation, &cbSecDesc, &pDacl, &fDaclPresent, &DaclSizeInfo);
1132 if (pSecDesc)
1133 {
1134 /*
1135 * Create a new DACL. This will contain two extra ACEs.
1136 */
1137 PSECURITY_DESCRIPTOR pNewSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc);
1138 if ( pNewSecDesc
1139 && InitializeSecurityDescriptor(pNewSecDesc, SECURITY_DESCRIPTOR_REVISION))
1140 {
1141 uint32_t const cbSid = GetLengthSid(pSid);
1142 uint32_t const cbNewDacl = DaclSizeInfo.AclBytesInUse + (sizeof(ACCESS_ALLOWED_ACE) + cbSid) * 2;
1143 PACL pNewDacl = (PACL)RTMemTmpAlloc(cbNewDacl);
1144 if ( pNewDacl
1145 && InitializeAcl(pNewDacl, cbNewDacl, ACL_REVISION)
1146 && rtProcWinCopyAces(pNewDacl, pDacl, fDaclPresent ? DaclSizeInfo.AceCount : 0))
1147 {
1148 /*
1149 * Add the two new SID ACEs.
1150 */
1151 if ( rtProcWinAddAccessAllowedAce(pNewDacl, CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE,
1152 MY_GENERIC_ALL_RIGHTS, pSid, cbSid)
1153 && rtProcWinAddAccessAllowedAce(pNewDacl, NO_PROPAGATE_INHERIT_ACE, MY_WINSTATION_ALL_RIGHTS, pSid, cbSid))
1154 {
1155 /*
1156 * Now mate the new DECL with the security descriptor and set it.
1157 */
1158 if (SetSecurityDescriptorDacl(pNewSecDesc, TRUE /*fDaclPresent*/, pNewDacl, FALSE /*fDaclDefaulted*/))
1159 {
1160 SECURITY_INFORMATION SecInfo = DACL_SECURITY_INFORMATION;
1161 if (SetUserObjectSecurity(hWinStation, &SecInfo, pNewSecDesc))
1162 fRet = true;
1163 else
1164 AssertFailed();
1165 }
1166 else
1167 AssertFailed();
1168 }
1169 else
1170 AssertFailed();
1171 }
1172 else
1173 AssertFailed();
1174 RTMemTmpFree(pNewDacl);
1175 }
1176 else
1177 AssertFailed();
1178 RTMemTmpFree(pNewSecDesc);
1179 RTMemTmpFree(pSecDesc);
1180 }
1181 return fRet;
1182}
1183
1184
1185/**
1186 * Grants the given SID full access to the given desktop.
1187 *
1188 * @returns true on success, false on failure.
1189 * @param hDesktop The desktop handle.
1190 * @param pSid The SID.
1191 */
1192static bool rtProcWinAddSidToDesktop(HDESK hDesktop, PSID pSid)
1193{
1194 bool fRet = false;
1195
1196 /*
1197 * Get the current DACL.
1198 */
1199 uint32_t cbSecDesc;
1200 PACL pDacl;
1201 ACL_SIZE_INFORMATION DaclSizeInfo;
1202 BOOL fDaclPresent;
1203 PSECURITY_DESCRIPTOR pSecDesc = rtProcWinGetUserObjDacl(hDesktop, &cbSecDesc, &pDacl, &fDaclPresent, &DaclSizeInfo);
1204 if (pSecDesc)
1205 {
1206 /*
1207 * Create a new DACL. This will contain one extra ACE.
1208 */
1209 PSECURITY_DESCRIPTOR pNewSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc);
1210 if ( pNewSecDesc
1211 && InitializeSecurityDescriptor(pNewSecDesc, SECURITY_DESCRIPTOR_REVISION))
1212 {
1213 uint32_t const cbSid = GetLengthSid(pSid);
1214 uint32_t const cbNewDacl = DaclSizeInfo.AclBytesInUse + (sizeof(ACCESS_ALLOWED_ACE) + cbSid) * 1;
1215 PACL pNewDacl = (PACL)RTMemTmpAlloc(cbNewDacl);
1216 if ( pNewDacl
1217 && InitializeAcl(pNewDacl, cbNewDacl, ACL_REVISION)
1218 && rtProcWinCopyAces(pNewDacl, pDacl, fDaclPresent ? DaclSizeInfo.AceCount : 0))
1219 {
1220 /*
1221 * Add the new SID ACE.
1222 */
1223 if (rtProcWinAddAccessAllowedAce(pNewDacl, 0 /*fAceFlags*/, MY_DESKTOP_ALL_RIGHTS, pSid, cbSid))
1224 {
1225 /*
1226 * Now mate the new DECL with the security descriptor and set it.
1227 */
1228 if (SetSecurityDescriptorDacl(pNewSecDesc, TRUE /*fDaclPresent*/, pNewDacl, FALSE /*fDaclDefaulted*/))
1229 {
1230 SECURITY_INFORMATION SecInfo = DACL_SECURITY_INFORMATION;
1231 if (SetUserObjectSecurity(hDesktop, &SecInfo, pNewSecDesc))
1232 fRet = true;
1233 else
1234 AssertFailed();
1235 }
1236 else
1237 AssertFailed();
1238 }
1239 else
1240 AssertFailed();
1241 }
1242 else
1243 AssertFailed();
1244 RTMemTmpFree(pNewDacl);
1245 }
1246 else
1247 AssertFailed();
1248 RTMemTmpFree(pNewSecDesc);
1249 RTMemTmpFree(pSecDesc);
1250 }
1251 return fRet;
1252}
1253
1254
1255/**
1256 * Preps the window station and desktop for the new app.
1257 *
1258 * EXPERIMENTAL. Thus no return code.
1259 *
1260 * @param hTokenToUse The access token of the new process.
1261 * @param pStartupInfo The startup info (we'll change lpDesktop, maybe).
1262 * @param phWinStationOld Where to return an window station handle to restore.
1263 * Pass this to SetProcessWindowStation if not NULL.
1264 */
1265static void rtProcWinStationPrep(HANDLE hTokenToUse, STARTUPINFOW *pStartupInfo, HWINSTA *phWinStationOld)
1266{
1267 /** @todo Always mess with the interactive one? Maybe it's not there... */
1268 *phWinStationOld = GetProcessWindowStation();
1269 HWINSTA hWinStation0 = OpenWindowStationW(L"winsta0", FALSE /*fInherit*/, READ_CONTROL | WRITE_DAC);
1270 if (hWinStation0)
1271 {
1272 if (SetProcessWindowStation(hWinStation0))
1273 {
1274 HDESK hDesktop = OpenDesktop("default", 0 /*fFlags*/, FALSE /*fInherit*/,
1275 READ_CONTROL | WRITE_DAC | DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS);
1276 if (hDesktop)
1277 {
1278 /*PSID pSid = rtProcWinGetTokenLogonSid(hTokenToUse); - Better to use the user SID. Avoid overflowing the ACL. */
1279 PSID pSid = rtProcWinGetTokenUserSid(hTokenToUse);
1280 if (pSid)
1281 {
1282 if ( rtProcWinAddSidToWinStation(hWinStation0, pSid)
1283 && rtProcWinAddSidToDesktop(hDesktop, pSid))
1284 {
1285 pStartupInfo->lpDesktop = L"winsta0\\default";
1286 }
1287 RTMemFree(pSid);
1288 }
1289 CloseDesktop(hDesktop);
1290 }
1291 else
1292 AssertFailed();
1293 }
1294 else
1295 AssertFailed();
1296 CloseWindowStation(hWinStation0);
1297 }
1298 else
1299 AssertFailed();
1300}
1301
1302
1303/**
1304 * Extracts the user name + domain from a given UPN (User Principal Name, "joedoe@example.com") or
1305 * Down-Level Logon Name format ("example.com\\joedoe") string.
1306 *
1307 * @return IPRT status code.
1308 * @param pwszString Pointer to string to extract the account info from.
1309 * @param pAccountInfo Where to store the parsed account info.
1310 * Must be free'd with rtProcWinFreeAccountInfo().
1311 */
1312static int rtProcWinParseAccountInfo(PRTUTF16 pwszString, PRTPROCWINACCOUNTINFO pAccountInfo)
1313{
1314 AssertPtrReturn(pwszString, VERR_INVALID_POINTER);
1315 AssertPtrReturn(pAccountInfo, VERR_INVALID_POINTER);
1316
1317 /*
1318 * Note: UPN handling is defined in RFC 822. We only implement very rudimentary parsing for the user
1319 * name and domain fields though.
1320 */
1321 char *pszString;
1322 int rc = RTUtf16ToUtf8(pwszString, &pszString);
1323 if (RT_SUCCESS(rc))
1324 {
1325 do
1326 {
1327 /* UPN or FQDN handling needed? */
1328 /** @todo Add more validation here as needed. Regular expressions would be nice. */
1329 char *pszDelim = strchr(pszString, '@');
1330 if (pszDelim) /* UPN name? */
1331 {
1332 rc = RTStrToUtf16Ex(pszString, pszDelim - pszString, &pAccountInfo->pwszUserName, 0, NULL);
1333 if (RT_FAILURE(rc))
1334 break;
1335
1336 rc = RTStrToUtf16Ex(pszDelim + 1, RTSTR_MAX, &pAccountInfo->pwszDomain, 0, NULL);
1337 if (RT_FAILURE(rc))
1338 break;
1339 }
1340 else if (pszDelim = strchr(pszString, '\\')) /* FQDN name? */
1341 {
1342 rc = RTStrToUtf16Ex(pszString, pszDelim - pszString, &pAccountInfo->pwszDomain, 0, NULL);
1343 if (RT_FAILURE(rc))
1344 break;
1345
1346 rc = RTStrToUtf16Ex(pszDelim + 1, RTSTR_MAX, &pAccountInfo->pwszUserName, 0, NULL);
1347 if (RT_FAILURE(rc))
1348 break;
1349 }
1350 else
1351 rc = VERR_NOT_SUPPORTED;
1352
1353 } while (0);
1354
1355 RTStrFree(pszString);
1356 }
1357
1358#ifdef DEBUG
1359 LogRelFunc(("Name : %ls\n", pAccountInfo->pwszUserName));
1360 LogRelFunc(("Domain: %ls\n", pAccountInfo->pwszDomain));
1361#endif
1362
1363 if (RT_FAILURE(rc))
1364 LogRelFunc(("Parsing \"%ls\" failed with rc=%Rrc\n", pwszString, rc));
1365 return rc;
1366}
1367
1368
1369static void rtProcWinFreeAccountInfo(PRTPROCWINACCOUNTINFO pAccountInfo)
1370{
1371 if (!pAccountInfo)
1372 return;
1373
1374 if (pAccountInfo->pwszUserName)
1375 {
1376 RTUtf16Free(pAccountInfo->pwszUserName);
1377 pAccountInfo->pwszUserName = NULL;
1378 }
1379
1380 if (pAccountInfo->pwszDomain)
1381 {
1382 RTUtf16Free(pAccountInfo->pwszDomain);
1383 pAccountInfo->pwszDomain = NULL;
1384 }
1385}
1386
1387
1388/**
1389 * Method \#2.
1390 */
1391static int rtProcWinCreateAsUser2(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 *ppwszExec, PRTUTF16 pwszCmdLine,
1392 RTENV hEnv, DWORD dwCreationFlags,
1393 STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo,
1394 uint32_t fFlags, const char *pszExec)
1395{
1396 /*
1397 * So if we want to start a process from a service (RTPROC_FLAGS_SERVICE),
1398 * we have to do the following:
1399 * - Check the credentials supplied and get the user SID.
1400 * - If valid get the correct Explorer/VBoxTray instance corresponding to that
1401 * user. This of course is only possible if that user is logged in (over
1402 * physical console or terminal services).
1403 * - If we found the user's Explorer/VBoxTray app, use and modify the token to
1404 * use it in order to allow the newly started process to access the user's
1405 * desktop. If there's no Explorer/VBoxTray app we cannot display the started
1406 * process (but run it without UI).
1407 *
1408 * The following restrictions apply:
1409 * - A process only can show its UI when the user the process should run
1410 * under is logged in (has a desktop).
1411 * - We do not want to display a process of user A run on the desktop
1412 * of user B on multi session systems.
1413 *
1414 * The following rights are needed in order to use LogonUserW and
1415 * CreateProcessAsUserW, so the local policy has to be modified to:
1416 * - SE_TCB_NAME = Act as part of the operating system
1417 * - SE_ASSIGNPRIMARYTOKEN_NAME = Create/replace a (process) token object
1418 * - SE_INCREASE_QUOTA_NAME = Increase quotas
1419 *
1420 * We may fail here with ERROR_PRIVILEGE_NOT_HELD.
1421 */
1422 DWORD dwErr = NO_ERROR;
1423 HANDLE hTokenLogon = INVALID_HANDLE_VALUE;
1424 int rc = rtProcWinUserLogon(pwszUser, pwszPassword, &hTokenLogon);
1425 if (RT_SUCCESS(rc))
1426 {
1427 DWORD fRc;
1428 bool fFound = false;
1429 HANDLE hTokenUserDesktop = INVALID_HANDLE_VALUE;
1430
1431 /*
1432 * If the SERVICE flag is specified, we do something rather ugly to
1433 * make things work at all. We search for a known desktop process
1434 * belonging to the user, grab its token and use it for launching
1435 * the new process. That way the process will have desktop access.
1436 */
1437 if (fFlags & RTPROC_FLAGS_SERVICE)
1438 {
1439 PSID pSid = NULL;
1440
1441 /* Try query the SID and domain sizes first. */
1442 DWORD cbSid = 0; /* Must be zero to query size! */
1443 DWORD cwcDomain = 0;
1444 SID_NAME_USE SidNameUse = SidTypeUser;
1445 fRc = LookupAccountNameW(NULL, pwszUser, NULL, &cbSid, NULL, &cwcDomain, &SidNameUse);
1446 if (!fRc)
1447 {
1448 dwErr = GetLastError();
1449
1450#ifndef IPRT_TARGET_NT4
1451 /*
1452 * The errors ERROR_TRUSTED_DOMAIN_FAILURE and ERROR_TRUSTED_RELATIONSHIP_FAILURE
1453 * can happen if an ADC (Active Domain Controller) is offline or not reachable.
1454 *
1455 * Try to handle these errors gracefully by asking the local LSA cache of the
1456 * client OS instead then. For this to work, the desired user must have at
1457 * least logged in once at that client -- otherwise there will be no cached
1458 * authentication available and this fallback will fail.
1459 */
1460 if ( g_pfnLsaLookupNames2 /* >= Windows XP */
1461 && ( dwErr == ERROR_TRUSTED_DOMAIN_FAILURE
1462 || dwErr == ERROR_TRUSTED_RELATIONSHIP_FAILURE))
1463 {
1464 LSA_OBJECT_ATTRIBUTES objAttr;
1465 RT_ZERO(objAttr);
1466 objAttr.Length = sizeof(LSA_OBJECT_ATTRIBUTES);
1467
1468 LSA_HANDLE lsahPolicy;
1469 NTSTATUS ntSts = LsaOpenPolicy(NULL, &objAttr, POLICY_LOOKUP_NAMES, &lsahPolicy);
1470 if (ntSts == STATUS_SUCCESS)
1471 {
1472 RTPROCWINACCOUNTINFO accountInfo;
1473 RT_ZERO(accountInfo);
1474 rc = rtProcWinParseAccountInfo(pwszUser, &accountInfo);
1475 AssertRC(rc);
1476 AssertPtr(accountInfo.pwszUserName);
1477
1478 LSA_UNICODE_STRING lsaUser;
1479 lsaUser.Buffer = accountInfo.pwszUserName;
1480 lsaUser.Length = (USHORT)(RTUtf16Len(accountInfo.pwszUserName) * sizeof(WCHAR));
1481 lsaUser.MaximumLength = lsaUser.Length;
1482
1483 PLSA_REFERENCED_DOMAIN_LIST pDomainList = NULL;
1484 PLSA_TRANSLATED_SID2 pTranslatedSids = NULL;
1485 ntSts = g_pfnLsaLookupNames2(lsahPolicy, 0 /* Flags */,
1486 1 /* Number of users to lookup */,
1487 &lsaUser, &pDomainList, &pTranslatedSids);
1488 if (ntSts == STATUS_SUCCESS)
1489 {
1490 AssertPtr(pDomainList);
1491 AssertPtr(pTranslatedSids);
1492# ifdef DEBUG
1493 LogRelFunc(("LsaLookupNames2: cDomains=%u, DomainIndex=%ld, SidUse=%ld\n",
1494 pDomainList->Entries, pTranslatedSids[0].DomainIndex, pTranslatedSids[0].Use));
1495# endif
1496 Assert(pTranslatedSids[0].Use == SidTypeUser);
1497
1498 if (pDomainList->Entries)
1499 {
1500 AssertPtr(pDomainList->Domains);
1501 LogRelFunc(("LsaLookupNames2: Domain=%ls\n",
1502 pDomainList->Domains[pTranslatedSids[0].DomainIndex].Name.Buffer));
1503 }
1504
1505 cbSid = GetLengthSid(pTranslatedSids->Sid) + 16;
1506 Assert(cbSid);
1507 pSid = (PSID)RTMemAllocZ(cbSid);
1508 if (!CopySid(cbSid, pSid, pTranslatedSids->Sid))
1509 {
1510 dwErr = GetLastError();
1511 LogRelFunc(("CopySid failed with: %ld\n", dwErr));
1512 rc = dwErr != NO_ERROR ? RTErrConvertFromWin32(dwErr) : VERR_INTERNAL_ERROR_2;
1513 }
1514 }
1515 else
1516 {
1517 dwErr = LsaNtStatusToWinError(ntSts);
1518 LogRelFunc(("LsaLookupNames2 failed with: %ld\n", dwErr));
1519 rc = dwErr != NO_ERROR ? RTErrConvertFromWin32(dwErr) : VERR_INTERNAL_ERROR_2;
1520 }
1521
1522 if (pDomainList)
1523 {
1524 LsaFreeMemory(pDomainList);
1525 pDomainList = NULL;
1526 }
1527 if (pTranslatedSids)
1528 {
1529 LsaFreeMemory(pTranslatedSids);
1530 pTranslatedSids = NULL;
1531 }
1532
1533 rtProcWinFreeAccountInfo(&accountInfo);
1534 LsaClose(lsahPolicy);
1535 }
1536 else
1537 {
1538 dwErr = LsaNtStatusToWinError(ntSts);
1539 LogRelFunc(("LsaOpenPolicy failed with: %ld\n", dwErr));
1540 rc = dwErr != NO_ERROR ? RTErrConvertFromWin32(dwErr) : VERR_INTERNAL_ERROR_3;
1541 }
1542
1543 /* Note: pSid will be free'd down below. */
1544 }
1545 else
1546#endif /* !IPRT_TARGET_NT4 */
1547 if (dwErr == ERROR_INSUFFICIENT_BUFFER)
1548 {
1549 /* Allocate memory for the LookupAccountNameW output buffers and do it for real. */
1550 cbSid = fRc && cbSid != 0 ? cbSid + 16 : _1K;
1551 pSid = (PSID)RTMemAllocZ(cbSid);
1552 if (pSid)
1553 {
1554 cwcDomain = fRc ? cwcDomain + 2 : _4K;
1555 PRTUTF16 pwszDomain = (PRTUTF16)RTMemAllocZ(cwcDomain * sizeof(RTUTF16));
1556 if (pwszDomain)
1557 {
1558 /* Note: Just pass in the UPN (User Principal Name), e.g. someone@example.com */
1559 if (!LookupAccountNameW(NULL /*lpSystemName*/, pwszUser, pSid, &cbSid, pwszDomain, &cwcDomain,
1560 &SidNameUse))
1561 {
1562 dwErr = GetLastError();
1563 LogRelFunc(("LookupAccountNameW(2) failed with: %ld\n", dwErr));
1564 rc = dwErr != NO_ERROR ? RTErrConvertFromWin32(dwErr) : VERR_INTERNAL_ERROR_4;
1565 }
1566
1567 RTMemFree(pwszDomain);
1568 }
1569 else
1570 rc = VERR_NO_MEMORY;
1571
1572 /* Note: pSid will be free'd down below. */
1573 }
1574 else
1575 rc = VERR_NO_MEMORY;
1576 }
1577 else
1578 {
1579 LogRelFunc(("LookupAccountNameW(1) failed with: %ld\n", dwErr));
1580 rc = dwErr != NO_ERROR ? RTErrConvertFromWin32(dwErr) : VERR_INTERNAL_ERROR_2;
1581 }
1582 }
1583
1584 if (pSid)
1585 {
1586 if (IsValidSid(pSid))
1587 {
1588 /* Array of process names we want to look for. */
1589 static const char * const s_papszProcNames[] =
1590 {
1591#ifdef VBOX /* The explorer entry is a fallback in case GA aren't installed. */
1592 { "VBoxTray.exe" },
1593#endif
1594 { "explorer.exe" },
1595 NULL
1596 };
1597 fFound = rtProcWinFindTokenByProcess(s_papszProcNames, pSid, &hTokenUserDesktop);
1598 dwErr = 0;
1599 }
1600 else
1601 {
1602 dwErr = GetLastError();
1603 LogRelFunc(("SID is invalid: %ld\n", dwErr));
1604 rc = dwErr != NO_ERROR ? RTErrConvertFromWin32(dwErr) : VERR_INTERNAL_ERROR_3;
1605 }
1606
1607 RTMemFree(pSid);
1608 }
1609 }
1610 /* else: !RTPROC_FLAGS_SERVICE: Nothing to do here right now. */
1611
1612#if 0
1613 /*
1614 * If we make LogonUserW to return an impersonation token, enable this
1615 * to convert it into a primary token.
1616 */
1617 if (!fFound && detect-impersonation-token)
1618 {
1619 HANDLE hNewToken;
1620 if (DuplicateTokenEx(hTokenLogon, MAXIMUM_ALLOWED, NULL /*SecurityAttribs*/,
1621 SecurityIdentification, TokenPrimary, &hNewToken))
1622 {
1623 CloseHandle(hTokenLogon);
1624 hTokenLogon = hNewToken;
1625 }
1626 else
1627 AssertMsgFailed(("%d\n", GetLastError()));
1628 }
1629#endif
1630
1631 if (RT_SUCCESS(rc))
1632 {
1633 /*
1634 * If we didn't find a matching VBoxTray, just use the token we got
1635 * above from LogonUserW(). This enables us to at least run processes
1636 * with desktop interaction without UI.
1637 */
1638 HANDLE hTokenToUse = fFound ? hTokenUserDesktop : hTokenLogon;
1639 if ( !(fFlags & RTPROC_FLAGS_PROFILE)
1640 || (g_pfnUnloadUserProfile && g_pfnLoadUserProfileW) )
1641 {
1642 /*
1643 * Load the profile, if requested. (Must be done prior to
1644 * creating the enviornment.)
1645 */
1646 PROFILEINFOW ProfileInfo;
1647 RT_ZERO(ProfileInfo);
1648 if (fFlags & RTPROC_FLAGS_PROFILE)
1649 {
1650 ProfileInfo.dwSize = sizeof(ProfileInfo);
1651 ProfileInfo.lpUserName = pwszUser;
1652 ProfileInfo.dwFlags = PI_NOUI; /* Prevents the display of profile error messages. */
1653
1654 if (!g_pfnLoadUserProfileW(hTokenToUse, &ProfileInfo))
1655 rc = RTErrConvertFromWin32(GetLastError());
1656 }
1657 if (RT_SUCCESS(rc))
1658 {
1659 /*
1660 * Create the environment.
1661 */
1662 RTENV hEnvFinal;
1663 rc = rtProcWinCreateEnvFromToken(hTokenToUse, hEnv, fFlags, &hEnvFinal);
1664 if (RT_SUCCESS(rc))
1665 {
1666 PRTUTF16 pwszzBlock;
1667 rc = RTEnvQueryUtf16Block(hEnvFinal, &pwszzBlock);
1668 if (RT_SUCCESS(rc))
1669 {
1670 rc = rtProcWinFindExe(fFlags, hEnv, pszExec, ppwszExec);
1671 if (RT_SUCCESS(rc))
1672 {
1673 HWINSTA hOldWinStation = NULL;
1674 if (!fFound && g_enmWinVer <= kRTWinOSType_NT4) /** @todo test newer versions... */
1675 rtProcWinStationPrep(hTokenToUse, pStartupInfo, &hOldWinStation);
1676
1677 /*
1678 * Useful KB articles:
1679 * http://support.microsoft.com/kb/165194/
1680 * http://support.microsoft.com/kb/184802/
1681 * http://support.microsoft.com/kb/327618/
1682 */
1683 fRc = CreateProcessAsUserW(hTokenToUse,
1684 *ppwszExec,
1685 pwszCmdLine,
1686 NULL, /* pProcessAttributes */
1687 NULL, /* pThreadAttributes */
1688 TRUE, /* fInheritHandles */
1689 dwCreationFlags,
1690 /** @todo Warn about exceeding 8192 bytes
1691 * on XP and up. */
1692 pwszzBlock, /* lpEnvironment */
1693 NULL, /* pCurrentDirectory */
1694 pStartupInfo,
1695 pProcInfo);
1696 if (fRc)
1697 rc = VINF_SUCCESS;
1698 else
1699 {
1700 dwErr = GetLastError();
1701 if (dwErr == ERROR_PRIVILEGE_NOT_HELD)
1702 rc = rtProcWinFigureWhichPrivilegeNotHeld2();
1703 else
1704 rc = RTErrConvertFromWin32(dwErr);
1705 }
1706
1707 if (hOldWinStation)
1708 SetProcessWindowStation(hOldWinStation);
1709 }
1710 RTEnvFreeUtf16Block(pwszzBlock);
1711 }
1712
1713 if (hEnvFinal != hEnv)
1714 RTEnvDestroy(hEnvFinal);
1715 }
1716
1717 if ((fFlags & RTPROC_FLAGS_PROFILE) && ProfileInfo.hProfile)
1718 {
1719 fRc = g_pfnUnloadUserProfile(hTokenToUse, ProfileInfo.hProfile);
1720#ifdef RT_STRICT
1721 if (!fRc)
1722 {
1723 DWORD dwErr2 = GetLastError();
1724 AssertMsgFailed(("Unloading user profile failed with error %u (%#x) - Are all handles closed? (dwErr=%u)",
1725 dwErr2, dwErr2, dwErr));
1726 }
1727#endif
1728 }
1729 }
1730 }
1731 else
1732 rc = VERR_SYMBOL_NOT_FOUND;
1733 } /* Account lookup succeeded? */
1734
1735 if (hTokenUserDesktop != INVALID_HANDLE_VALUE)
1736 CloseHandle(hTokenUserDesktop);
1737 if (hTokenLogon != INVALID_HANDLE_VALUE)
1738 CloseHandle(hTokenLogon);
1739
1740 if (rc == VERR_UNRESOLVED_ERROR)
1741 LogRelFunc(("dwErr=%u (%#x), rc=%Rrc\n", dwErr, dwErr, rc));
1742 }
1743
1744 return rc;
1745}
1746
1747
1748/**
1749 * Plants a standard handle into a child process on older windows versions.
1750 *
1751 * This is only needed when using CreateProcessWithLogonW on older windows
1752 * versions. It would appear that newer versions of windows does this for us.
1753 *
1754 * @param hSrcHandle The source handle.
1755 * @param hDstProcess The child process handle.
1756 * @param offProcParamMember The offset to RTL_USER_PROCESS_PARAMETERS.
1757 * @param ppvDstProcParamCache Where where cached the address of
1758 * RTL_USER_PROCESS_PARAMETERS in the child.
1759 */
1760static void rtProcWinDupStdHandleIntoChild(HANDLE hSrcHandle, HANDLE hDstProcess, uint32_t offProcParamMember,
1761 PVOID *ppvDstProcParamCache)
1762{
1763 if (hSrcHandle != NULL && hSrcHandle != INVALID_HANDLE_VALUE)
1764 {
1765 HANDLE hDstHandle;
1766 if (DuplicateHandle(GetCurrentProcess(), hSrcHandle, hDstProcess, &hDstHandle,
1767 0 /*IgnoredDesiredAccess*/, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
1768 {
1769 if (hSrcHandle == hDstHandle)
1770 return;
1771
1772 if (!*ppvDstProcParamCache)
1773 {
1774 PROCESS_BASIC_INFORMATION BasicInfo;
1775 ULONG cbIgn;
1776 NTSTATUS rcNt = NtQueryInformationProcess(hDstProcess, ProcessBasicInformation,
1777 &BasicInfo, sizeof(BasicInfo), &cbIgn);
1778 if (NT_SUCCESS(rcNt))
1779 {
1780 SIZE_T cbCopied = 0;
1781 if (!ReadProcessMemory(hDstProcess,
1782 (char *)BasicInfo.PebBaseAddress + RT_OFFSETOF(PEB_COMMON, ProcessParameters),
1783 ppvDstProcParamCache, sizeof(*ppvDstProcParamCache), &cbCopied))
1784 {
1785 AssertMsgFailed(("PebBaseAddress=%p %d\n", BasicInfo.PebBaseAddress, GetLastError()));
1786 *ppvDstProcParamCache = NULL;
1787 }
1788 }
1789 else
1790 AssertMsgFailed(("rcNt=%#x\n", rcNt));
1791 }
1792 if (*ppvDstProcParamCache)
1793 {
1794 if (WriteProcessMemory(hDstProcess, (char *)*ppvDstProcParamCache + offProcParamMember,
1795 &hDstHandle, sizeof(hDstHandle), NULL))
1796 return;
1797 }
1798
1799 /*
1800 * Close the handle.
1801 */
1802 HANDLE hSrcHandle2;
1803 if (DuplicateHandle(hDstProcess, hDstHandle, GetCurrentProcess(), &hSrcHandle2,
1804 0 /*IgnoredDesiredAccess*/, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
1805 CloseHandle(hSrcHandle2);
1806 else
1807 AssertMsgFailed(("hDstHandle=%p %u\n", hDstHandle, GetLastError()));
1808 }
1809 else
1810 AssertMsg(GetLastError() == ERROR_INVALID_PARAMETER, ("%u\n", GetLastError()));
1811 }
1812}
1813
1814
1815/**
1816 * Method \#1.
1817 *
1818 * This method requires Windows 2000 or later. It may fail if the process is
1819 * running under the SYSTEM account (like a service, ERROR_ACCESS_DENIED) on
1820 * newer platforms (however, this works on W2K!).
1821 */
1822static int rtProcWinCreateAsUser1(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 *ppwszExec, PRTUTF16 pwszCmdLine,
1823 RTENV hEnv, DWORD dwCreationFlags,
1824 STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo,
1825 uint32_t fFlags, const char *pszExec)
1826{
1827 /* The CreateProcessWithLogonW API was introduced with W2K and later. It uses a service
1828 for launching the process. */
1829 if (!g_pfnCreateProcessWithLogonW)
1830 return VERR_SYMBOL_NOT_FOUND;
1831
1832 /*
1833 * Create the environment block and find the executable first.
1834 *
1835 * We try to skip this when RTPROC_FLAGS_PROFILE is set so we can sidestep
1836 * potential missing TCB privilege issues when calling UserLogonW. At least
1837 * NT4 and W2K requires the trusted code base (TCB) privilege for logon use.
1838 * Passing pwszzBlock=NULL and LOGON_WITH_PROFILE means the child process
1839 * gets the environment specified by the user profile.
1840 */
1841 int rc;
1842 PRTUTF16 pwszzBlock = NULL;
1843
1844 /* Eliminating the path search flags simplifies things a little. */
1845 if ( (fFlags & RTPROC_FLAGS_SEARCH_PATH)
1846 && (RTPathHasPath(pszExec) || RTPathExists(pszExec)))
1847 fFlags &= ~RTPROC_FLAGS_SEARCH_PATH;
1848
1849 /*
1850 * No profile is simple, as is a user specified environment (no change record).
1851 */
1852 if ( !(fFlags & RTPROC_FLAGS_PROFILE)
1853 || ( !(fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)
1854 && hEnv != RTENV_DEFAULT))
1855 rc = rtProcWinCreateEnvBlockAndFindExe(fFlags, hEnv, pszExec, &pwszzBlock, ppwszExec);
1856 /*
1857 * Default profile environment without changes or path searching we leave
1858 * to the service that implements the API.
1859 */
1860 else if ( hEnv == RTENV_DEFAULT
1861 && !(fFlags & (RTPROC_FLAGS_ENV_CHANGE_RECORD | RTPROC_FLAGS_SEARCH_PATH)))
1862 {
1863 pwszzBlock = NULL;
1864 rc = VINF_SUCCESS;
1865 }
1866 /*
1867 * Otherwise, we need to get the user profile environment.
1868 */
1869 else
1870 {
1871 RTENV hEnvToUse = NIL_RTENV;
1872 HANDLE hTokenLogon = INVALID_HANDLE_VALUE;
1873 rc = rtProcWinUserLogon(pwszUser, pwszPassword, &hTokenLogon);
1874 if (RT_SUCCESS(rc))
1875 {
1876 /* CreateEnvFromToken docs says we should load the profile, though
1877 we haven't observed any difference when not doing it. Maybe it's
1878 only an issue with roaming profiles or something similar... */
1879 PROFILEINFOW ProfileInfo;
1880 RT_ZERO(ProfileInfo);
1881 ProfileInfo.dwSize = sizeof(ProfileInfo);
1882 ProfileInfo.lpUserName = pwszUser;
1883 ProfileInfo.dwFlags = PI_NOUI; /* Prevents the display of profile error messages. */
1884
1885 if (g_pfnLoadUserProfileW(hTokenLogon, &ProfileInfo))
1886 {
1887 /*
1888 * Do what we need to do. Don't keep any temp environment object.
1889 */
1890 rc = rtProcWinCreateEnvFromToken(hTokenLogon, hEnv, fFlags, &hEnvToUse);
1891 if (RT_SUCCESS(rc))
1892 {
1893 rc = rtProcWinFindExe(fFlags, hEnv, pszExec, ppwszExec);
1894 if (RT_SUCCESS(rc))
1895 rc = RTEnvQueryUtf16Block(hEnvToUse, &pwszzBlock);
1896 if (hEnvToUse != hEnv)
1897 RTEnvDestroy(hEnvToUse);
1898 }
1899
1900 if (!g_pfnUnloadUserProfile(hTokenLogon, ProfileInfo.hProfile))
1901 AssertFailed();
1902 }
1903 else
1904 rc = RTErrConvertFromWin32(GetLastError());
1905
1906 if (hTokenLogon != INVALID_HANDLE_VALUE)
1907 CloseHandle(hTokenLogon);
1908 }
1909 }
1910 if (RT_SUCCESS(rc))
1911 {
1912 /*
1913 * Create the process.
1914 */
1915 Assert(!(dwCreationFlags & CREATE_SUSPENDED));
1916 bool const fCreatedSuspended = g_enmWinVer < kRTWinOSType_XP;
1917 BOOL fRc = g_pfnCreateProcessWithLogonW(pwszUser,
1918 NULL, /* lpDomain*/
1919 pwszPassword,
1920 fFlags & RTPROC_FLAGS_PROFILE ? 1 /*LOGON_WITH_ PROFILE*/ : 0,
1921 *ppwszExec,
1922 pwszCmdLine,
1923 dwCreationFlags | (fCreatedSuspended ? CREATE_SUSPENDED : 0),
1924 pwszzBlock,
1925 NULL, /* pCurrentDirectory */
1926 pStartupInfo,
1927 pProcInfo);
1928 if (fRc)
1929 {
1930 if (!fCreatedSuspended)
1931 rc = VINF_SUCCESS;
1932 else
1933 {
1934 /*
1935 * Duplicate standard handles into the child process, we ignore failures here as it's
1936 * legal to have bad standard handle values and we cannot dup console I/O handles.*
1937 */
1938 PVOID pvDstProcParamCache = NULL;
1939 rtProcWinDupStdHandleIntoChild(pStartupInfo->hStdInput, pProcInfo->hProcess,
1940 RT_OFFSETOF(RTL_USER_PROCESS_PARAMETERS, StandardInput), &pvDstProcParamCache);
1941 rtProcWinDupStdHandleIntoChild(pStartupInfo->hStdOutput, pProcInfo->hProcess,
1942 RT_OFFSETOF(RTL_USER_PROCESS_PARAMETERS, StandardOutput), &pvDstProcParamCache);
1943 rtProcWinDupStdHandleIntoChild(pStartupInfo->hStdError, pProcInfo->hProcess,
1944 RT_OFFSETOF(RTL_USER_PROCESS_PARAMETERS, StandardError), &pvDstProcParamCache);
1945
1946 if (ResumeThread(pProcInfo->hThread) != ~(DWORD)0)
1947 rc = VINF_SUCCESS;
1948 else
1949 rc = RTErrConvertFromWin32(GetLastError());
1950 if (RT_FAILURE(rc))
1951 {
1952 TerminateProcess(pProcInfo->hProcess, 127);
1953 CloseHandle(pProcInfo->hThread);
1954 CloseHandle(pProcInfo->hProcess);
1955 }
1956 }
1957 }
1958 else
1959 {
1960 DWORD dwErr = GetLastError();
1961 rc = RTErrConvertFromWin32(dwErr);
1962 if (rc == VERR_UNRESOLVED_ERROR)
1963 LogRelFunc(("CreateProcessWithLogonW (%p) failed: dwErr=%u (%#x), rc=%Rrc\n",
1964 g_pfnCreateProcessWithLogonW, dwErr, dwErr, rc));
1965 }
1966 if (pwszzBlock)
1967 RTEnvFreeUtf16Block(pwszzBlock);
1968 }
1969 return rc;
1970}
1971
1972
1973static int rtProcWinCreateAsUser(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 *ppwszExec, PRTUTF16 pwszCmdLine,
1974 RTENV hEnv, DWORD dwCreationFlags,
1975 STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo,
1976 uint32_t fFlags, const char *pszExec)
1977{
1978 /*
1979 * If we run as a service CreateProcessWithLogon will fail, so don't even
1980 * try it (because of Local System context). This method is very slow on W2K.
1981 */
1982 if (!(fFlags & RTPROC_FLAGS_SERVICE))
1983 {
1984 int rc = rtProcWinCreateAsUser1(pwszUser, pwszPassword, ppwszExec, pwszCmdLine,
1985 hEnv, dwCreationFlags, pStartupInfo, pProcInfo, fFlags, pszExec);
1986 if (RT_SUCCESS(rc))
1987 return rc;
1988 }
1989 return rtProcWinCreateAsUser2(pwszUser, pwszPassword, ppwszExec, pwszCmdLine,
1990 hEnv, dwCreationFlags, pStartupInfo, pProcInfo, fFlags, pszExec);
1991}
1992
1993
1994/**
1995 * RTPathTraverseList callback used by rtProcWinFindExe to locate the
1996 * executable.
1997 */
1998static DECLCALLBACK(int) rtPathFindExec(char const *pchPath, size_t cchPath, void *pvUser1, void *pvUser2)
1999{
2000 const char *pszExec = (const char *)pvUser1;
2001 char *pszRealExec = (char *)pvUser2;
2002 int rc = RTPathJoinEx(pszRealExec, RTPATH_MAX, pchPath, cchPath, pszExec, RTSTR_MAX);
2003 if (RT_FAILURE(rc))
2004 return rc;
2005 if (RTFileExists(pszRealExec))
2006 return VINF_SUCCESS;
2007 return VERR_TRY_AGAIN;
2008}
2009
2010
2011/**
2012 * Locate the executable file if necessary.
2013 *
2014 * @returns IPRT status code.
2015 * @param pszExec The UTF-8 executable string passed in by the user.
2016 * @param fFlags The process creation flags pass in by the user.
2017 * @param hEnv The environment to get the path variabel from.
2018 * @param ppwszExec Pointer to the variable pointing to the UTF-16
2019 * converted string. If we find something, the current
2020 * pointer will be free (RTUtf16Free) and
2021 * replaced by a new one.
2022 */
2023static int rtProcWinFindExe(uint32_t fFlags, RTENV hEnv, const char *pszExec, PRTUTF16 *ppwszExec)
2024{
2025 /*
2026 * Return immediately if we're not asked to search, or if the file has a
2027 * path already or if it actually exists in the current directory.
2028 */
2029 if ( !(fFlags & RTPROC_FLAGS_SEARCH_PATH)
2030 || RTPathHavePath(pszExec)
2031 || RTPathExists(pszExec) )
2032 return VINF_SUCCESS;
2033
2034 /*
2035 * Search the Path or PATH variable for the file.
2036 */
2037 char *pszPath;
2038 if (RTEnvExistEx(hEnv, "PATH"))
2039 pszPath = RTEnvDupEx(hEnv, "PATH");
2040 else if (RTEnvExistEx(hEnv, "Path"))
2041 pszPath = RTEnvDupEx(hEnv, "Path");
2042 else
2043 return VERR_FILE_NOT_FOUND;
2044
2045 char szRealExec[RTPATH_MAX];
2046 int rc = RTPathTraverseList(pszPath, ';', rtPathFindExec, (void *)pszExec, &szRealExec[0]);
2047 RTStrFree(pszPath);
2048 if (RT_SUCCESS(rc))
2049 {
2050 /*
2051 * Replace the executable string.
2052 */
2053 RTUtf16Free(*ppwszExec);
2054 *ppwszExec = NULL;
2055 rc = RTStrToUtf16(szRealExec, ppwszExec);
2056 }
2057 else if (rc == VERR_END_OF_STRING)
2058 rc = VERR_FILE_NOT_FOUND;
2059 return rc;
2060}
2061
2062
2063/**
2064 * Creates the UTF-16 environment block and, if necessary, find the executable.
2065 *
2066 * @returns IPRT status code.
2067 * @param fFlags The process creation flags pass in by the user.
2068 * @param hEnv The environment handle passed by the user.
2069 * @param pszExec See rtProcWinFindExe.
2070 * @param ppwszzBlock Where RTEnvQueryUtf16Block returns the block.
2071 * @param ppwszExec See rtProcWinFindExe.
2072 */
2073static int rtProcWinCreateEnvBlockAndFindExe(uint32_t fFlags, RTENV hEnv, const char *pszExec,
2074 PRTUTF16 *ppwszzBlock, PRTUTF16 *ppwszExec)
2075{
2076 int rc;
2077
2078 /*
2079 * In most cases, we just need to convert the incoming enviornment to a
2080 * UTF-16 environment block.
2081 */
2082 RTENV hEnvToUse = NIL_RTENV; /* (MSC maybe used uninitialized) */
2083 if ( !(fFlags & (RTPROC_FLAGS_PROFILE | RTPROC_FLAGS_ENV_CHANGE_RECORD))
2084 || (hEnv == RTENV_DEFAULT && !(fFlags & RTPROC_FLAGS_PROFILE))
2085 || (hEnv != RTENV_DEFAULT && !(fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)) )
2086 {
2087 hEnvToUse = hEnv;
2088 rc = VINF_SUCCESS;
2089 }
2090 else if (fFlags & RTPROC_FLAGS_PROFILE)
2091 {
2092 /*
2093 * We need to get the profile environment for the current user.
2094 */
2095 Assert((fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) || hEnv == RTENV_DEFAULT);
2096 AssertReturn(g_pfnCreateEnvironmentBlock && g_pfnDestroyEnvironmentBlock, VERR_SYMBOL_NOT_FOUND);
2097 AssertReturn(g_pfnLoadUserProfileW && g_pfnUnloadUserProfile, VERR_SYMBOL_NOT_FOUND);
2098 HANDLE hToken;
2099 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, &hToken))
2100 {
2101 rc = rtProcWinCreateEnvFromToken(hToken, hEnv, fFlags, &hEnvToUse);
2102 CloseHandle(hToken);
2103 }
2104 else
2105 rc = RTErrConvertFromWin32(GetLastError());
2106 }
2107 else
2108 {
2109 /*
2110 * Apply hEnv as a change record on top of the default environment.
2111 */
2112 Assert(fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD);
2113 rc = RTEnvClone(&hEnvToUse, RTENV_DEFAULT);
2114 if (RT_SUCCESS(rc))
2115 {
2116 rc = RTEnvApplyChanges(hEnvToUse, hEnv);
2117 if (RT_FAILURE(rc))
2118 RTEnvDestroy(hEnvToUse);
2119 }
2120 }
2121 if (RT_SUCCESS(rc))
2122 {
2123 /*
2124 * Query the UTF-16 environment block and locate the executable (if needed).
2125 */
2126 rc = RTEnvQueryUtf16Block(hEnvToUse, ppwszzBlock);
2127 if (RT_SUCCESS(rc))
2128 rc = rtProcWinFindExe(fFlags, hEnvToUse, pszExec, ppwszExec);
2129
2130 if (hEnvToUse != hEnv)
2131 RTEnvDestroy(hEnvToUse);
2132 }
2133
2134 return rc;
2135}
2136
2137
2138RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
2139 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
2140 const char *pszPassword, PRTPROCESS phProcess)
2141{
2142 /*
2143 * Input validation
2144 */
2145 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
2146 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
2147 AssertReturn(!(fFlags & ~RTPROC_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
2148 AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
2149 AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
2150 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
2151 AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
2152 AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
2153 AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
2154 AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
2155
2156 /*
2157 * Initialize the globals.
2158 */
2159 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL);
2160 AssertRCReturn(rc, rc);
2161 if (pszAsUser || (fFlags & (RTPROC_FLAGS_PROFILE | RTPROC_FLAGS_SERVICE)))
2162 {
2163 rc = RTOnce(&g_rtProcWinResolveOnce, rtProcWinResolveOnce, NULL);
2164 AssertRCReturn(rc, rc);
2165 }
2166
2167 /*
2168 * Get the file descriptors for the handles we've been passed.
2169 *
2170 * It seems there is no point in trying to convince a child process's CRT
2171 * that any of the standard file handles is non-TEXT. So, we don't...
2172 */
2173 STARTUPINFOW StartupInfo;
2174 RT_ZERO(StartupInfo);
2175 StartupInfo.cb = sizeof(StartupInfo);
2176 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
2177#if 1 /* The CRT should keep the standard handles up to date. */
2178 StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
2179 StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
2180 StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
2181#else
2182 StartupInfo.hStdInput = _get_osfhandle(0);
2183 StartupInfo.hStdOutput = _get_osfhandle(1);
2184 StartupInfo.hStdError = _get_osfhandle(2);
2185#endif
2186 /* If we want to have a hidden process (e.g. not visible to
2187 * to the user) use the STARTUPINFO flags. */
2188 if (fFlags & RTPROC_FLAGS_HIDDEN)
2189 {
2190 StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
2191 StartupInfo.wShowWindow = SW_HIDE;
2192 }
2193
2194 PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
2195 HANDLE *aphStds[3] = { &StartupInfo.hStdInput, &StartupInfo.hStdOutput, &StartupInfo.hStdError };
2196 DWORD afInhStds[3] = { 0xffffffff, 0xffffffff, 0xffffffff };
2197 for (int i = 0; i < 3; i++)
2198 {
2199 if (paHandles[i])
2200 {
2201 AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
2202 switch (paHandles[i]->enmType)
2203 {
2204 case RTHANDLETYPE_FILE:
2205 *aphStds[i] = paHandles[i]->u.hFile != NIL_RTFILE
2206 ? (HANDLE)RTFileToNative(paHandles[i]->u.hFile)
2207 : INVALID_HANDLE_VALUE;
2208 break;
2209
2210 case RTHANDLETYPE_PIPE:
2211 *aphStds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
2212 ? (HANDLE)RTPipeToNative(paHandles[i]->u.hPipe)
2213 : INVALID_HANDLE_VALUE;
2214 break;
2215
2216 case RTHANDLETYPE_SOCKET:
2217 *aphStds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
2218 ? (HANDLE)RTSocketToNative(paHandles[i]->u.hSocket)
2219 : INVALID_HANDLE_VALUE;
2220 break;
2221
2222 default:
2223 AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
2224 }
2225
2226 /* Get the inheritability of the handle. */
2227 if (*aphStds[i] != INVALID_HANDLE_VALUE)
2228 {
2229 if (!GetHandleInformation(*aphStds[i], &afInhStds[i]))
2230 {
2231 rc = RTErrConvertFromWin32(GetLastError());
2232 AssertMsgFailedReturn(("%Rrc %p\n", rc, *aphStds[i]), rc);
2233 }
2234 }
2235 }
2236 }
2237
2238 /*
2239 * Set the inheritability any handles we're handing the child.
2240 */
2241 rc = VINF_SUCCESS;
2242 for (int i = 0; i < 3; i++)
2243 if ( (afInhStds[i] != 0xffffffff)
2244 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
2245 {
2246 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
2247 {
2248 rc = RTErrConvertFromWin32(GetLastError());
2249 AssertMsgFailedBreak(("%Rrc %p\n", rc, *aphStds[i]));
2250 }
2251 }
2252
2253 /*
2254 * Create the command line and convert the executable name.
2255 */
2256 PRTUTF16 pwszCmdLine = NULL; /* Shut up, MSC! */
2257 if (RT_SUCCESS(rc))
2258 rc = RTGetOptArgvToUtf16String(&pwszCmdLine, papszArgs,
2259 !(fFlags & RTPROC_FLAGS_UNQUOTED_ARGS)
2260 ? RTGETOPTARGV_CNV_QUOTE_MS_CRT : RTGETOPTARGV_CNV_UNQUOTED);
2261 if (RT_SUCCESS(rc))
2262 {
2263 PRTUTF16 pwszExec;
2264 rc = RTStrToUtf16(pszExec, &pwszExec);
2265 if (RT_SUCCESS(rc))
2266 {
2267 /*
2268 * Get going...
2269 */
2270 PROCESS_INFORMATION ProcInfo;
2271 RT_ZERO(ProcInfo);
2272 DWORD dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
2273 if (fFlags & RTPROC_FLAGS_DETACHED)
2274 dwCreationFlags |= DETACHED_PROCESS;
2275 if (fFlags & RTPROC_FLAGS_NO_WINDOW)
2276 dwCreationFlags |= CREATE_NO_WINDOW;
2277
2278 /*
2279 * Only use the normal CreateProcess stuff if we have no user name
2280 * and we are not running from a (Windows) service. Otherwise use
2281 * the more advanced version in rtProcWinCreateAsUser().
2282 */
2283 if ( pszAsUser == NULL
2284 && !(fFlags & RTPROC_FLAGS_SERVICE))
2285 {
2286 /* Create the environment block first. */
2287 PRTUTF16 pwszzBlock;
2288 rc = rtProcWinCreateEnvBlockAndFindExe(fFlags, hEnv, pszExec, &pwszzBlock, &pwszExec);
2289 if (RT_SUCCESS(rc))
2290 {
2291 if (CreateProcessW(pwszExec,
2292 pwszCmdLine,
2293 NULL, /* pProcessAttributes */
2294 NULL, /* pThreadAttributes */
2295 TRUE, /* fInheritHandles */
2296 dwCreationFlags,
2297 pwszzBlock,
2298 NULL, /* pCurrentDirectory */
2299 &StartupInfo,
2300 &ProcInfo))
2301 rc = VINF_SUCCESS;
2302 else
2303 rc = RTErrConvertFromWin32(GetLastError());
2304 RTEnvFreeUtf16Block(pwszzBlock);
2305 }
2306 }
2307 else
2308 {
2309 /*
2310 * Convert the additional parameters and use a helper
2311 * function to do the actual work.
2312 */
2313 PRTUTF16 pwszUser;
2314 rc = RTStrToUtf16(pszAsUser, &pwszUser);
2315 if (RT_SUCCESS(rc))
2316 {
2317 PRTUTF16 pwszPassword;
2318 rc = RTStrToUtf16(pszPassword ? pszPassword : "", &pwszPassword);
2319 if (RT_SUCCESS(rc))
2320 {
2321 rc = rtProcWinCreateAsUser(pwszUser, pwszPassword,
2322 &pwszExec, pwszCmdLine, hEnv, dwCreationFlags,
2323 &StartupInfo, &ProcInfo, fFlags, pszExec);
2324
2325 if (pwszPassword && *pwszPassword)
2326 RTMemWipeThoroughly(pwszPassword, RTUtf16Len(pwszPassword), 5);
2327 RTUtf16Free(pwszPassword);
2328 }
2329 RTUtf16Free(pwszUser);
2330 }
2331 }
2332 if (RT_SUCCESS(rc))
2333 {
2334 CloseHandle(ProcInfo.hThread);
2335 if (phProcess)
2336 {
2337 /*
2338 * Add the process to the child process list so
2339 * RTProcWait can reuse and close the process handle.
2340 */
2341 rtProcWinAddPid(ProcInfo.dwProcessId, ProcInfo.hProcess);
2342 *phProcess = ProcInfo.dwProcessId;
2343 }
2344 else
2345 CloseHandle(ProcInfo.hProcess);
2346 rc = VINF_SUCCESS;
2347 }
2348 RTUtf16Free(pwszExec);
2349 }
2350 RTUtf16Free(pwszCmdLine);
2351 }
2352
2353 /* Undo any handle inherit changes. */
2354 for (int i = 0; i < 3; i++)
2355 if ( (afInhStds[i] != 0xffffffff)
2356 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
2357 {
2358 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, 0))
2359 AssertMsgFailed(("%Rrc %p\n", RTErrConvertFromWin32(GetLastError()), *aphStds[i]));
2360 }
2361
2362 return rc;
2363}
2364
2365
2366
2367RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
2368{
2369 AssertReturn(!(fFlags & ~(RTPROCWAIT_FLAGS_BLOCK | RTPROCWAIT_FLAGS_NOBLOCK)), VERR_INVALID_PARAMETER);
2370 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL);
2371 AssertRCReturn(rc, rc);
2372
2373 /*
2374 * Try find the process among the ones we've spawned, otherwise, attempt
2375 * opening the specified process.
2376 */
2377 HANDLE hOpenedProc = NULL;
2378 HANDLE hProcess = rtProcWinFindPid(Process);
2379 if (hProcess == NULL)
2380 {
2381 hProcess = hOpenedProc = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, Process);
2382 if (hProcess == NULL)
2383 {
2384 DWORD dwErr = GetLastError();
2385 if (dwErr == ERROR_INVALID_PARAMETER)
2386 return VERR_PROCESS_NOT_FOUND;
2387 return RTErrConvertFromWin32(dwErr);
2388 }
2389 }
2390
2391 /*
2392 * Wait for it to terminate.
2393 */
2394 DWORD Millies = fFlags == RTPROCWAIT_FLAGS_BLOCK ? INFINITE : 0;
2395 DWORD WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
2396 while (WaitRc == WAIT_IO_COMPLETION)
2397 WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
2398 switch (WaitRc)
2399 {
2400 /*
2401 * It has terminated.
2402 */
2403 case WAIT_OBJECT_0:
2404 {
2405 DWORD dwExitCode;
2406 if (GetExitCodeProcess(hProcess, &dwExitCode))
2407 {
2408 /** @todo the exit code can be special statuses. */
2409 if (pProcStatus)
2410 {
2411 pProcStatus->enmReason = RTPROCEXITREASON_NORMAL;
2412 pProcStatus->iStatus = (int)dwExitCode;
2413 }
2414 if (hOpenedProc == NULL)
2415 rtProcWinRemovePid(Process);
2416 rc = VINF_SUCCESS;
2417 }
2418 else
2419 rc = RTErrConvertFromWin32(GetLastError());
2420 break;
2421 }
2422
2423 /*
2424 * It hasn't terminated just yet.
2425 */
2426 case WAIT_TIMEOUT:
2427 rc = VERR_PROCESS_RUNNING;
2428 break;
2429
2430 /*
2431 * Something went wrong...
2432 */
2433 case WAIT_FAILED:
2434 rc = RTErrConvertFromWin32(GetLastError());
2435 break;
2436
2437 case WAIT_ABANDONED:
2438 AssertFailed();
2439 rc = VERR_GENERAL_FAILURE;
2440 break;
2441
2442 default:
2443 AssertMsgFailed(("WaitRc=%RU32\n", WaitRc));
2444 rc = VERR_GENERAL_FAILURE;
2445 break;
2446 }
2447
2448 if (hOpenedProc != NULL)
2449 CloseHandle(hOpenedProc);
2450 return rc;
2451}
2452
2453
2454RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
2455{
2456 /** @todo this isn't quite right. */
2457 return RTProcWait(Process, fFlags, pProcStatus);
2458}
2459
2460
2461RTR3DECL(int) RTProcTerminate(RTPROCESS Process)
2462{
2463 if (Process == NIL_RTPROCESS)
2464 return VINF_SUCCESS;
2465
2466 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL);
2467 AssertRCReturn(rc, rc);
2468
2469 /*
2470 * Try find the process among the ones we've spawned, otherwise, attempt
2471 * opening the specified process.
2472 */
2473 HANDLE hProcess = rtProcWinFindPid(Process);
2474 if (hProcess != NULL)
2475 {
2476 if (!TerminateProcess(hProcess, 127))
2477 rc = RTErrConvertFromWin32(GetLastError());
2478 }
2479 else
2480 {
2481 hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, Process);
2482 if (hProcess != NULL)
2483 {
2484 BOOL fRc = TerminateProcess(hProcess, 127);
2485 DWORD dwErr = GetLastError();
2486 CloseHandle(hProcess);
2487 if (!fRc)
2488 rc = RTErrConvertFromWin32(dwErr);
2489 }
2490 }
2491 return rc;
2492}
2493
2494
2495RTR3DECL(uint64_t) RTProcGetAffinityMask(void)
2496{
2497 DWORD_PTR dwProcessAffinityMask = 0xffffffff;
2498 DWORD_PTR dwSystemAffinityMask;
2499
2500 BOOL fRc = GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask);
2501 Assert(fRc); NOREF(fRc);
2502
2503 return dwProcessAffinityMask;
2504}
2505
2506
2507RTR3DECL(int) RTProcQueryUsername(RTPROCESS hProcess, char *pszUser, size_t cbUser, size_t *pcbUser)
2508{
2509 AssertReturn( (pszUser && cbUser > 0)
2510 || (!pszUser && !cbUser), VERR_INVALID_PARAMETER);
2511 AssertReturn(pcbUser || pszUser, VERR_INVALID_PARAMETER);
2512
2513 int rc;
2514 if ( hProcess == NIL_RTPROCESS
2515 || hProcess == RTProcSelf())
2516 {
2517 RTUTF16 wszUsername[UNLEN + 1];
2518 DWORD cwcUsername = RT_ELEMENTS(wszUsername);
2519 if (GetUserNameW(&wszUsername[0], &cwcUsername))
2520 {
2521 if (pszUser)
2522 {
2523 rc = RTUtf16ToUtf8Ex(wszUsername, cwcUsername, &pszUser, cbUser, pcbUser);
2524 if (pcbUser)
2525 *pcbUser += 1;
2526 }
2527 else
2528 {
2529 *pcbUser = RTUtf16CalcUtf8Len(wszUsername) + 1;
2530 rc = VERR_BUFFER_OVERFLOW;
2531 }
2532 }
2533 else
2534 rc = RTErrConvertFromWin32(GetLastError());
2535 }
2536 else
2537 rc = VERR_NOT_SUPPORTED;
2538 return rc;
2539}
2540
2541
2542RTR3DECL(int) RTProcQueryUsernameA(RTPROCESS hProcess, char **ppszUser)
2543{
2544 AssertPtrReturn(ppszUser, VERR_INVALID_POINTER);
2545 int rc;
2546 if ( hProcess == NIL_RTPROCESS
2547 || hProcess == RTProcSelf())
2548 {
2549 RTUTF16 wszUsername[UNLEN + 1];
2550 DWORD cwcUsername = RT_ELEMENTS(wszUsername);
2551 if (GetUserNameW(&wszUsername[0], &cwcUsername))
2552 rc = RTUtf16ToUtf8(wszUsername, ppszUser);
2553 else
2554 rc = RTErrConvertFromWin32(GetLastError());
2555 }
2556 else
2557 rc = VERR_NOT_SUPPORTED;
2558 return rc;
2559}
2560
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