VirtualBox

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

Last change on this file since 81106 was 80569, checked in by vboxsync, 5 years ago

Main: bugref:9341: Added VM autostart during boot support for windows host

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