VirtualBox

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

Last change on this file since 108265 was 107892, checked in by vboxsync, 8 weeks ago

Runtime/r3/win/process-win.cpp: Fixed warnings found by Parfait (unused assignment + unused function). Removes rtProcWinParseAccountInfo() + rtProcWinFreeAccountInfo(), as unused. jiraref:VBP-1424

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette