VirtualBox

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

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

IPRT/r3/win/process-win.cpp: re-applied the process-win.cpp changes which were backed-out at r105657 and resolve LsaLookupNames2() dynamically as it's not supported on systems < Windows XP

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