VirtualBox

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

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

IPRT/process-win.cpp: Adding RTPROC_FLAGS_AS_IMPERSONATED_TOKEN and RTPROC_FLAGS_NO_WAIT for windows. [scm fix] bugref:9341 bugref:8078

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