VirtualBox

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

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

IPRT: Doxygen warning fixes (last ones, hopefully).

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