VirtualBox

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

Last change on this file since 59544 was 59544, checked in by vboxsync, 9 years ago

IPRT: r3/win/process-win.cpp: Removed debug output.

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