VirtualBox

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

Last change on this file since 70172 was 70172, checked in by vboxsync, 7 years ago

r3/win/process-win.cpp: Drop the IPRT_TARGET_NT4.

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