VirtualBox

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

Last change on this file since 103795 was 103232, checked in by vboxsync, 10 months ago

IPRT/process-win.cpp: A better fix for the r152441 regression. bugref:10261

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