VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo-win.cpp@ 69496

Last change on this file since 69496 was 69496, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 54.3 KB
Line 
1/* $Id: VBoxServiceVMInfo-win.cpp 69496 2017-10-28 14:55:58Z vboxsync $ */
2/** @file
3 * VBoxService - Virtual Machine Information for the Host, Windows specifics.
4 */
5
6/*
7 * Copyright (C) 2009-2017 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
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0600
23# undef _WIN32_WINNT
24# define _WIN32_WINNT 0x0600 /* QueryFullProcessImageNameW in recent SDKs. */
25#endif
26#include <iprt/win/windows.h>
27#include <wtsapi32.h> /* For WTS* calls. */
28#include <psapi.h> /* EnumProcesses. */
29#include <Ntsecapi.h> /* Needed for process security information. */
30
31#include <iprt/assert.h>
32#include <iprt/ldr.h>
33#include <iprt/localipc.h>
34#include <iprt/mem.h>
35#include <iprt/once.h>
36#include <iprt/process.h>
37#include <iprt/string.h>
38#include <iprt/semaphore.h>
39#include <iprt/system.h>
40#include <iprt/time.h>
41#include <iprt/thread.h>
42
43#include <VBox/VBoxGuestLib.h>
44#include "VBoxServiceInternal.h"
45#include "VBoxServiceUtils.h"
46#include "VBoxServiceVMInfo.h"
47#include "../../WINNT/VBoxTray/VBoxTrayMsg.h" /* For IPC. */
48
49static uint32_t s_uDebugGuestPropClientID = 0;
50static uint32_t s_uDebugIter = 0;
51/** Whether to skip the logged-in user detection over RDP or not.
52 * See notes in this section why we might want to skip this. */
53static bool s_fSkipRDPDetection = false;
54
55
56/*********************************************************************************************************************************
57* Structures and Typedefs *
58*********************************************************************************************************************************/
59/** Structure for storing the looked up user information. */
60typedef struct VBOXSERVICEVMINFOUSER
61{
62 WCHAR wszUser[_MAX_PATH];
63 WCHAR wszAuthenticationPackage[_MAX_PATH];
64 WCHAR wszLogonDomain[_MAX_PATH];
65 /** Number of assigned user processes. */
66 ULONG ulNumProcs;
67 /** Last (highest) session ID. This
68 * is needed for distinguishing old session
69 * process counts from new (current) session
70 * ones. */
71 ULONG ulLastSession;
72} VBOXSERVICEVMINFOUSER, *PVBOXSERVICEVMINFOUSER;
73
74/** Structure for the file information lookup. */
75typedef struct VBOXSERVICEVMINFOFILE
76{
77 char *pszFilePath;
78 char *pszFileName;
79} VBOXSERVICEVMINFOFILE, *PVBOXSERVICEVMINFOFILE;
80
81/** Structure for process information lookup. */
82typedef struct VBOXSERVICEVMINFOPROC
83{
84 /** The PID. */
85 DWORD id;
86 /** The SID. */
87 PSID pSid;
88 /** The LUID. */
89 LUID luid;
90 /** Interactive process. */
91 bool fInteractive;
92} VBOXSERVICEVMINFOPROC, *PVBOXSERVICEVMINFOPROC;
93
94
95/*********************************************************************************************************************************
96* Internal Functions *
97*********************************************************************************************************************************/
98static uint32_t vgsvcVMInfoWinSessionHasProcesses(PLUID pSession, PVBOXSERVICEVMINFOPROC const paProcs, DWORD cProcs);
99static bool vgsvcVMInfoWinIsLoggedIn(PVBOXSERVICEVMINFOUSER a_pUserInfo, PLUID a_pSession);
100static int vgsvcVMInfoWinProcessesEnumerate(PVBOXSERVICEVMINFOPROC *ppProc, DWORD *pdwCount);
101static void vgsvcVMInfoWinProcessesFree(DWORD cProcs, PVBOXSERVICEVMINFOPROC paProcs);
102static int vgsvcVMInfoWinWriteLastInput(PVBOXSERVICEVEPROPCACHE pCache, const char *pszUser, const char *pszDomain);
103
104
105/*********************************************************************************************************************************
106* Global Variables *
107*********************************************************************************************************************************/
108static RTONCE g_vgsvcWinVmInitOnce = RTONCE_INITIALIZER;
109
110/** @name Secur32.dll imports are dynamically resolved because of NT4.
111 * @{ */
112static decltype(LsaGetLogonSessionData) *g_pfnLsaGetLogonSessionData = NULL;
113static decltype(LsaEnumerateLogonSessions) *g_pfnLsaEnumerateLogonSessions = NULL;
114static decltype(LsaFreeReturnBuffer) *g_pfnLsaFreeReturnBuffer = NULL;
115/** @} */
116
117/** @name WtsApi32.dll imports are dynamically resolved because of NT4.
118 * @{ */
119static decltype(WTSFreeMemory) *g_pfnWTSFreeMemory = NULL;
120static decltype(WTSQuerySessionInformationA) *g_pfnWTSQuerySessionInformationA = NULL;
121/** @} */
122
123/** @name PsApi.dll imports are dynamically resolved because of NT4.
124 * @{ */
125static decltype(EnumProcesses) *g_pfnEnumProcesses = NULL;
126static decltype(GetModuleFileNameExW) *g_pfnGetModuleFileNameExW = NULL;
127/** @} */
128
129/** @name New Kernel32.dll APIs we may use when present.
130 * @{ */
131static decltype(QueryFullProcessImageNameW) *g_pfnQueryFullProcessImageNameW = NULL;
132
133/** @} */
134
135/** Windows version. */
136static OSVERSIONINFOEXA g_WinVersion;
137
138
139/**
140 * An RTOnce callback function.
141 */
142static DECLCALLBACK(int) vgsvcWinVmInfoInitOnce(void *pvIgnored)
143{
144 RT_NOREF1(pvIgnored);
145
146 /* SECUR32 */
147 RTLDRMOD hLdrMod;
148 int rc = RTLdrLoadSystem("secur32.dll", true, &hLdrMod);
149 if (RT_SUCCESS(rc))
150 {
151 rc = RTLdrGetSymbol(hLdrMod, "LsaGetLogonSessionData", (void **)&g_pfnLsaGetLogonSessionData);
152 if (RT_SUCCESS(rc))
153 rc = RTLdrGetSymbol(hLdrMod, "LsaEnumerateLogonSessions", (void **)&g_pfnLsaEnumerateLogonSessions);
154 if (RT_SUCCESS(rc))
155 rc = RTLdrGetSymbol(hLdrMod, "LsaFreeReturnBuffer", (void **)&g_pfnLsaFreeReturnBuffer);
156 AssertRC(rc);
157 RTLdrClose(hLdrMod);
158 }
159 if (RT_FAILURE(rc))
160 {
161 VGSvcVerbose(1, "Secur32.dll APIs are not availble (%Rrc)\n", rc);
162 g_pfnLsaGetLogonSessionData = NULL;
163 g_pfnLsaEnumerateLogonSessions = NULL;
164 g_pfnLsaFreeReturnBuffer = NULL;
165 Assert(g_WinVersion.dwMajorVersion < 5);
166 }
167
168 /* WTSAPI32 */
169 rc = RTLdrLoadSystem("wtsapi32.dll", true, &hLdrMod);
170 if (RT_SUCCESS(rc))
171 {
172 rc = RTLdrGetSymbol(hLdrMod, "WTSFreeMemory", (void **)&g_pfnWTSFreeMemory);
173 if (RT_SUCCESS(rc))
174 rc = RTLdrGetSymbol(hLdrMod, "WTSQuerySessionInformationA", (void **)&g_pfnWTSQuerySessionInformationA);
175 AssertRC(rc);
176 RTLdrClose(hLdrMod);
177 }
178 if (RT_FAILURE(rc))
179 {
180 VGSvcVerbose(1, "WtsApi32.dll APIs are not availble (%Rrc)\n", rc);
181 g_pfnWTSFreeMemory = NULL;
182 g_pfnWTSQuerySessionInformationA = NULL;
183 Assert(g_WinVersion.dwMajorVersion < 5);
184 }
185
186 /* PSAPI */
187 rc = RTLdrLoadSystem("psapi.dll", true, &hLdrMod);
188 if (RT_SUCCESS(rc))
189 {
190 rc = RTLdrGetSymbol(hLdrMod, "EnumProcesses", (void **)&g_pfnEnumProcesses);
191 if (RT_SUCCESS(rc))
192 rc = RTLdrGetSymbol(hLdrMod, "GetModuleFileNameExW", (void **)&g_pfnGetModuleFileNameExW);
193 AssertRC(rc);
194 RTLdrClose(hLdrMod);
195 }
196 if (RT_FAILURE(rc))
197 {
198 VGSvcVerbose(1, "psapi.dll APIs are not availble (%Rrc)\n", rc);
199 g_pfnEnumProcesses = NULL;
200 g_pfnGetModuleFileNameExW = NULL;
201 Assert(g_WinVersion.dwMajorVersion < 5);
202 }
203
204 /* Kernel32: */
205 rc = RTLdrLoadSystem("kernel32.dll", true, &hLdrMod);
206 AssertRCReturn(rc, rc);
207 rc = RTLdrGetSymbol(hLdrMod, "QueryFullProcessImageNameW", (void **)&g_pfnQueryFullProcessImageNameW);
208 if (RT_FAILURE(rc))
209 {
210 Assert(g_WinVersion.dwMajorVersion < 6);
211 g_pfnQueryFullProcessImageNameW = NULL;
212 }
213 RTLdrClose(hLdrMod);
214
215 /*
216 * Get the extended windows version once and for all.
217 */
218 g_WinVersion.dwOSVersionInfoSize = sizeof(g_WinVersion);
219 if (!GetVersionExA((OSVERSIONINFO *)&g_WinVersion))
220 {
221 AssertFailed();
222 RT_ZERO(g_WinVersion);
223 g_WinVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
224 if (!GetVersionExA((OSVERSIONINFO *)&g_WinVersion))
225 RT_ZERO(g_WinVersion);
226 }
227
228 return VINF_SUCCESS;
229}
230
231
232static bool vgsvcVMInfoSession0Separation(void)
233{
234 return g_WinVersion.dwPlatformId == VER_PLATFORM_WIN32_NT
235 && g_WinVersion.dwMajorVersion >= 6; /* Vista = 6.0 */
236}
237
238
239/**
240 * Retrieves the module name of a given process.
241 *
242 * @return IPRT status code.
243 */
244static int vgsvcVMInfoWinProcessesGetModuleNameA(PVBOXSERVICEVMINFOPROC const pProc, PRTUTF16 *ppszName)
245{
246 AssertPtrReturn(pProc, VERR_INVALID_POINTER);
247 AssertPtrReturn(ppszName, VERR_INVALID_POINTER);
248
249 /** @todo Only do this once. Later. */
250 /* Platform other than NT (e.g. Win9x) not supported. */
251 if (g_WinVersion.dwPlatformId != VER_PLATFORM_WIN32_NT)
252 return VERR_NOT_SUPPORTED;
253
254 int rc = VINF_SUCCESS;
255
256 DWORD dwFlags = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
257 if (g_WinVersion.dwMajorVersion >= 6 /* Vista or later */)
258 dwFlags = PROCESS_QUERY_LIMITED_INFORMATION; /* possible to do on more processes */
259
260 HANDLE h = OpenProcess(dwFlags, FALSE, pProc->id);
261 if (h == NULL)
262 {
263 DWORD dwErr = GetLastError();
264 if (g_cVerbosity)
265 VGSvcError("Unable to open process with PID=%u, error=%u\n", pProc->id, dwErr);
266 rc = RTErrConvertFromWin32(dwErr);
267 }
268 else
269 {
270 /* Since GetModuleFileNameEx has trouble with cross-bitness stuff (32-bit apps cannot query 64-bit
271 apps and vice verse) we have to use a different code path for Vista and up. */
272 WCHAR wszName[_1K];
273 DWORD dwLen = sizeof(wszName); /** @todo r=bird: wrong? */
274
275 /* Use QueryFullProcessImageNameW if available (Vista+). */
276 if (g_pfnQueryFullProcessImageNameW)
277 {
278 if (!g_pfnQueryFullProcessImageNameW(h, 0 /*PROCESS_NAME_NATIVE*/, wszName, &dwLen))
279 rc = VERR_ACCESS_DENIED;
280 }
281 else if (!g_pfnGetModuleFileNameExW(h, NULL /* Get main executable */, wszName, dwLen))
282 rc = VERR_ACCESS_DENIED;
283
284 if ( RT_FAILURE(rc)
285 && g_cVerbosity > 3)
286 VGSvcError("Unable to retrieve process name for PID=%u, error=%u\n", pProc->id, GetLastError());
287 else
288 {
289 PRTUTF16 pszName = RTUtf16Dup(wszName);
290 if (pszName)
291 *ppszName = pszName;
292 else
293 rc = VERR_NO_MEMORY;
294 }
295
296 CloseHandle(h);
297 }
298
299 return rc;
300}
301
302
303/**
304 * Fills in more data for a process.
305 *
306 * @returns VBox status code.
307 * @param pProc The process structure to fill data into.
308 * @param tkClass The kind of token information to get.
309 */
310static int vgsvcVMInfoWinProcessesGetTokenInfo(PVBOXSERVICEVMINFOPROC pProc, TOKEN_INFORMATION_CLASS tkClass)
311{
312 AssertPtrReturn(pProc, VERR_INVALID_POINTER);
313
314 DWORD dwErr = ERROR_SUCCESS;
315 HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pProc->id);
316 if (h == NULL)
317 {
318 dwErr = GetLastError();
319 if (g_cVerbosity > 4)
320 VGSvcError("Unable to open process with PID=%u, error=%u\n", pProc->id, dwErr);
321 return RTErrConvertFromWin32(dwErr);
322 }
323
324 int rc = VINF_SUCCESS;
325 HANDLE hToken;
326 if (OpenProcessToken(h, TOKEN_QUERY, &hToken))
327 {
328 void *pvTokenInfo = NULL;
329 DWORD dwTokenInfoSize;
330 switch (tkClass)
331 {
332 case TokenStatistics:
333 /** @todo r=bird: Someone has been reading too many MSDN examples. You shall
334 * use RTMemAlloc here! There is absolutely not reason for
335 * complicating things uncessarily by using HeapAlloc! */
336 dwTokenInfoSize = sizeof(TOKEN_STATISTICS);
337 pvTokenInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwTokenInfoSize);
338 AssertPtr(pvTokenInfo);
339 break;
340
341 case TokenGroups:
342 dwTokenInfoSize = 0;
343 /* Allocation will follow in a second step. */
344 break;
345
346 case TokenUser:
347 dwTokenInfoSize = 0;
348 /* Allocation will follow in a second step. */
349 break;
350
351 default:
352 VGSvcError("Token class not implemented: %d\n", tkClass);
353 rc = VERR_NOT_IMPLEMENTED;
354 dwTokenInfoSize = 0; /* Shut up MSC. */
355 break;
356 }
357
358 if (RT_SUCCESS(rc))
359 {
360 DWORD dwRetLength;
361 if (!GetTokenInformation(hToken, tkClass, pvTokenInfo, dwTokenInfoSize, &dwRetLength))
362 {
363 dwErr = GetLastError();
364 if (dwErr == ERROR_INSUFFICIENT_BUFFER)
365 {
366 dwErr = ERROR_SUCCESS;
367
368 switch (tkClass)
369 {
370 case TokenGroups:
371 pvTokenInfo = (PTOKEN_GROUPS)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRetLength);
372 if (!pvTokenInfo)
373 dwErr = GetLastError();
374 dwTokenInfoSize = dwRetLength;
375 break;
376
377 case TokenUser:
378 pvTokenInfo = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRetLength);
379 if (!pvTokenInfo)
380 dwErr = GetLastError();
381 dwTokenInfoSize = dwRetLength;
382 break;
383
384 default:
385 AssertMsgFailed(("Re-allocating of token information for token class not implemented\n"));
386 break;
387 }
388
389 if (dwErr == ERROR_SUCCESS)
390 {
391 if (!GetTokenInformation(hToken, tkClass, pvTokenInfo, dwTokenInfoSize, &dwRetLength))
392 dwErr = GetLastError();
393 }
394 }
395 }
396
397 if (dwErr == ERROR_SUCCESS)
398 {
399 rc = VINF_SUCCESS;
400
401 switch (tkClass)
402 {
403 case TokenStatistics:
404 {
405 PTOKEN_STATISTICS pStats = (PTOKEN_STATISTICS)pvTokenInfo;
406 AssertPtr(pStats);
407 memcpy(&pProc->luid, &pStats->AuthenticationId, sizeof(LUID));
408 /** @todo Add more information of TOKEN_STATISTICS as needed. */
409 break;
410 }
411
412 case TokenGroups:
413 {
414 pProc->fInteractive = false;
415
416 SID_IDENTIFIER_AUTHORITY sidAuthNT = SECURITY_NT_AUTHORITY;
417 PSID pSidInteractive = NULL; /* S-1-5-4 */
418 if (!AllocateAndInitializeSid(&sidAuthNT, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pSidInteractive))
419 dwErr = GetLastError();
420
421 PSID pSidLocal = NULL; /* S-1-2-0 */
422 if (dwErr == ERROR_SUCCESS)
423 {
424 SID_IDENTIFIER_AUTHORITY sidAuthLocal = SECURITY_LOCAL_SID_AUTHORITY;
425 if (!AllocateAndInitializeSid(&sidAuthLocal, 1, 0, 0, 0, 0, 0, 0, 0, 0, &pSidLocal))
426 dwErr = GetLastError();
427 }
428
429 if (dwErr == ERROR_SUCCESS)
430 {
431 PTOKEN_GROUPS pGroups = (PTOKEN_GROUPS)pvTokenInfo;
432 AssertPtr(pGroups);
433 for (DWORD i = 0; i < pGroups->GroupCount; i++)
434 {
435 if ( EqualSid(pGroups->Groups[i].Sid, pSidInteractive)
436 || EqualSid(pGroups->Groups[i].Sid, pSidLocal)
437 || pGroups->Groups[i].Attributes & SE_GROUP_LOGON_ID)
438 {
439 pProc->fInteractive = true;
440 break;
441 }
442 }
443 }
444
445 if (pSidInteractive)
446 FreeSid(pSidInteractive);
447 if (pSidLocal)
448 FreeSid(pSidLocal);
449 break;
450 }
451
452 case TokenUser:
453 {
454 PTOKEN_USER pUser = (PTOKEN_USER)pvTokenInfo;
455 AssertPtr(pUser);
456
457 DWORD dwLength = GetLengthSid(pUser->User.Sid);
458 Assert(dwLength);
459 if (dwLength)
460 {
461 pProc->pSid = (PSID)HeapAlloc(GetProcessHeap(),
462 HEAP_ZERO_MEMORY, dwLength);
463 AssertPtr(pProc->pSid);
464 if (CopySid(dwLength, pProc->pSid, pUser->User.Sid))
465 {
466 if (!IsValidSid(pProc->pSid))
467 dwErr = ERROR_INVALID_NAME;
468 }
469 else
470 dwErr = GetLastError();
471 }
472 else
473 dwErr = ERROR_NO_DATA;
474
475 if (dwErr != ERROR_SUCCESS)
476 {
477 VGSvcError("Error retrieving SID of process PID=%u: %u\n", pProc->id, dwErr);
478 if (pProc->pSid)
479 {
480 HeapFree(GetProcessHeap(), 0 /* Flags */, pProc->pSid);
481 pProc->pSid = NULL;
482 }
483 }
484 break;
485 }
486
487 default:
488 AssertMsgFailed(("Unhandled token information class\n"));
489 break;
490 }
491 }
492
493 if (pvTokenInfo)
494 HeapFree(GetProcessHeap(), 0 /* Flags */, pvTokenInfo);
495 }
496 CloseHandle(hToken);
497 }
498 else
499 dwErr = GetLastError();
500
501 if (dwErr != ERROR_SUCCESS)
502 {
503 if (g_cVerbosity)
504 VGSvcError("Unable to query token information for PID=%u, error=%u\n", pProc->id, dwErr);
505 rc = RTErrConvertFromWin32(dwErr);
506 }
507
508 CloseHandle(h);
509 return rc;
510}
511
512
513/**
514 * Enumerate all the processes in the system and get the logon user IDs for
515 * them.
516 *
517 * @returns VBox status code.
518 * @param ppaProcs Where to return the process snapshot. This must be
519 * freed by calling vgsvcVMInfoWinProcessesFree.
520 *
521 * @param pcProcs Where to store the returned process count.
522 */
523static int vgsvcVMInfoWinProcessesEnumerate(PVBOXSERVICEVMINFOPROC *ppaProcs, PDWORD pcProcs)
524{
525 AssertPtr(ppaProcs);
526 AssertPtr(pcProcs);
527
528 if (!g_pfnEnumProcesses)
529 return VERR_NOT_SUPPORTED;
530
531 /*
532 * Call EnumProcesses with an increasingly larger buffer until it all fits
533 * or we think something is screwed up.
534 */
535 DWORD cProcesses = 64;
536 PDWORD paPID = NULL;
537 int rc = VINF_SUCCESS;
538 do
539 {
540 /* Allocate / grow the buffer first. */
541 cProcesses *= 2;
542 void *pvNew = RTMemRealloc(paPID, cProcesses * sizeof(DWORD));
543 if (!pvNew)
544 {
545 rc = VERR_NO_MEMORY;
546 break;
547 }
548 paPID = (PDWORD)pvNew;
549
550 /* Query the processes. Not the cbRet == buffer size means there could be more work to be done. */
551 DWORD cbRet;
552 if (!g_pfnEnumProcesses(paPID, cProcesses * sizeof(DWORD), &cbRet))
553 {
554 rc = RTErrConvertFromWin32(GetLastError());
555 break;
556 }
557 if (cbRet < cProcesses * sizeof(DWORD))
558 {
559 cProcesses = cbRet / sizeof(DWORD);
560 break;
561 }
562 } while (cProcesses <= _32K); /* Should be enough; see: http://blogs.technet.com/markrussinovich/archive/2009/07/08/3261309.aspx */
563
564 if (RT_SUCCESS(rc))
565 {
566 /*
567 * Allocate out process structures and fill data into them.
568 * We currently only try lookup their LUID's.
569 */
570 PVBOXSERVICEVMINFOPROC paProcs;
571 paProcs = (PVBOXSERVICEVMINFOPROC)RTMemAllocZ(cProcesses * sizeof(VBOXSERVICEVMINFOPROC));
572 if (paProcs)
573 {
574 for (DWORD i = 0; i < cProcesses; i++)
575 {
576 paProcs[i].id = paPID[i];
577 paProcs[i].pSid = NULL;
578
579 int rc2 = vgsvcVMInfoWinProcessesGetTokenInfo(&paProcs[i], TokenUser);
580 if (RT_FAILURE(rc2) && g_cVerbosity)
581 VGSvcError("Get token class 'user' for process %u failed, rc=%Rrc\n", paProcs[i].id, rc2);
582
583 rc2 = vgsvcVMInfoWinProcessesGetTokenInfo(&paProcs[i], TokenGroups);
584 if (RT_FAILURE(rc2) && g_cVerbosity)
585 VGSvcError("Get token class 'groups' for process %u failed, rc=%Rrc\n", paProcs[i].id, rc2);
586
587 rc2 = vgsvcVMInfoWinProcessesGetTokenInfo(&paProcs[i], TokenStatistics);
588 if (RT_FAILURE(rc2) && g_cVerbosity)
589 VGSvcError("Get token class 'statistics' for process %u failed, rc=%Rrc\n", paProcs[i].id, rc2);
590 }
591
592 /* Save number of processes */
593 if (RT_SUCCESS(rc))
594 {
595 *pcProcs = cProcesses;
596 *ppaProcs = paProcs;
597 }
598 else
599 vgsvcVMInfoWinProcessesFree(cProcesses, paProcs);
600 }
601 else
602 rc = VERR_NO_MEMORY;
603 }
604
605 RTMemFree(paPID);
606 return rc;
607}
608
609/**
610 * Frees the process structures returned by
611 * vgsvcVMInfoWinProcessesEnumerate() before.
612 *
613 * @param cProcs Number of processes in paProcs.
614 * @param paProcs The process array.
615 */
616static void vgsvcVMInfoWinProcessesFree(DWORD cProcs, PVBOXSERVICEVMINFOPROC paProcs)
617{
618 for (DWORD i = 0; i < cProcs; i++)
619 if (paProcs[i].pSid)
620 {
621 HeapFree(GetProcessHeap(), 0 /* Flags */, paProcs[i].pSid);
622 paProcs[i].pSid = NULL;
623 }
624 RTMemFree(paProcs);
625}
626
627/**
628 * Determines whether the specified session has processes on the system.
629 *
630 * @returns Number of processes found for a specified session.
631 * @param pSession The current user's SID.
632 * @param paProcs The process snapshot.
633 * @param cProcs The number of processes in the snaphot.
634 * @param puTerminalSession Where to return terminal session number.
635 * Optional.
636 */
637/** @todo r=bird: The 'Has' indicates a predicate function, which this is
638 * not. Predicate functions always returns bool. */
639static uint32_t vgsvcVMInfoWinSessionHasProcesses(PLUID pSession, PVBOXSERVICEVMINFOPROC const paProcs, DWORD cProcs,
640 PULONG puTerminalSession)
641{
642 if (!pSession)
643 {
644 VGSvcVerbose(1, "Session became invalid while enumerating!\n");
645 return 0;
646 }
647 if (!g_pfnLsaGetLogonSessionData)
648 return 0;
649
650 PSECURITY_LOGON_SESSION_DATA pSessionData = NULL;
651 NTSTATUS rcNt = g_pfnLsaGetLogonSessionData(pSession, &pSessionData);
652 if (rcNt != STATUS_SUCCESS)
653 {
654 VGSvcError("Could not get logon session data! rcNt=%#x\n", rcNt);
655 return 0;
656 }
657
658 if (!IsValidSid(pSessionData->Sid))
659 {
660 VGSvcError("User SID=%p is not valid\n", pSessionData->Sid);
661 if (pSessionData)
662 g_pfnLsaFreeReturnBuffer(pSessionData);
663 return 0;
664 }
665
666
667 /*
668 * Even if a user seems to be logged in, it could be a stale/orphaned logon
669 * session. So check if we have some processes bound to it by comparing the
670 * session <-> process LUIDs.
671 */
672 int rc = VINF_SUCCESS;
673 uint32_t cProcessesFound = 0;
674 for (DWORD i = 0; i < cProcs; i++)
675 {
676 PSID pProcSID = paProcs[i].pSid;
677 if ( RT_SUCCESS(rc)
678 && pProcSID
679 && IsValidSid(pProcSID))
680 {
681 if (EqualSid(pSessionData->Sid, paProcs[i].pSid))
682 {
683 if (g_cVerbosity)
684 {
685 PRTUTF16 pszName;
686 int rc2 = vgsvcVMInfoWinProcessesGetModuleNameA(&paProcs[i], &pszName);
687 VGSvcVerbose(4, "Session %RU32: PID=%u (fInt=%RTbool): %ls\n",
688 pSessionData->Session, paProcs[i].id, paProcs[i].fInteractive,
689 RT_SUCCESS(rc2) ? pszName : L"<Unknown>");
690 if (RT_SUCCESS(rc2))
691 RTUtf16Free(pszName);
692 }
693
694 if (paProcs[i].fInteractive)
695 {
696 cProcessesFound++;
697 if (!g_cVerbosity) /* We want a bit more info on higher verbosity. */
698 break;
699 }
700 }
701 }
702 }
703
704 if (puTerminalSession)
705 *puTerminalSession = pSessionData->Session;
706
707 g_pfnLsaFreeReturnBuffer(pSessionData);
708
709 return cProcessesFound;
710}
711
712
713/**
714 * Save and noisy string copy.
715 *
716 * @param pwszDst Destination buffer.
717 * @param cbDst Size in bytes - not WCHAR count!
718 * @param pSrc Source string.
719 * @param pszWhat What this is. For the log.
720 */
721static void vgsvcVMInfoWinSafeCopy(PWCHAR pwszDst, size_t cbDst, LSA_UNICODE_STRING const *pSrc, const char *pszWhat)
722{
723 Assert(RT_ALIGN(cbDst, sizeof(WCHAR)) == cbDst);
724
725 size_t cbCopy = pSrc->Length;
726 if (cbCopy + sizeof(WCHAR) > cbDst)
727 {
728 VGSvcVerbose(0, "%s is too long - %u bytes, buffer %u bytes! It will be truncated.\n", pszWhat, cbCopy, cbDst);
729 cbCopy = cbDst - sizeof(WCHAR);
730 }
731 if (cbCopy)
732 memcpy(pwszDst, pSrc->Buffer, cbCopy);
733 pwszDst[cbCopy / sizeof(WCHAR)] = '\0';
734}
735
736
737/**
738 * Detects whether a user is logged on.
739 *
740 * @returns true if logged in, false if not (or error).
741 * @param pUserInfo Where to return the user information.
742 * @param pSession The session to check.
743 */
744static bool vgsvcVMInfoWinIsLoggedIn(PVBOXSERVICEVMINFOUSER pUserInfo, PLUID pSession)
745{
746 AssertPtrReturn(pUserInfo, false);
747 if (!pSession)
748 return false;
749 if (!g_pfnLsaGetLogonSessionData)
750 return false;
751
752 PSECURITY_LOGON_SESSION_DATA pSessionData = NULL;
753 NTSTATUS rcNt = g_pfnLsaGetLogonSessionData(pSession, &pSessionData);
754 if (rcNt != STATUS_SUCCESS)
755 {
756 ULONG ulError = LsaNtStatusToWinError(rcNt);
757 switch (ulError)
758 {
759 case ERROR_NOT_ENOUGH_MEMORY:
760 /* If we don't have enough memory it's hard to judge whether the specified user
761 * is logged in or not, so just assume he/she's not. */
762 VGSvcVerbose(3, "Not enough memory to retrieve logon session data!\n");
763 break;
764
765 case ERROR_NO_SUCH_LOGON_SESSION:
766 /* Skip session data which is not valid anymore because it may have been
767 * already terminated. */
768 break;
769
770 default:
771 VGSvcError("LsaGetLogonSessionData failed with error %u\n", ulError);
772 break;
773 }
774 if (pSessionData)
775 g_pfnLsaFreeReturnBuffer(pSessionData);
776 return false;
777 }
778 if (!pSessionData)
779 {
780 VGSvcError("Invalid logon session data!\n");
781 return false;
782 }
783
784 VGSvcVerbose(3, "Session data: Name=%ls, SessionID=%RU32, LogonID=%d,%u, LogonType=%u\n",
785 pSessionData->UserName.Buffer, pSessionData->Session,
786 pSessionData->LogonId.HighPart, pSessionData->LogonId.LowPart, pSessionData->LogonType);
787
788 if (vgsvcVMInfoSession0Separation())
789 {
790 /* Starting at Windows Vista user sessions begin with session 1, so
791 * ignore (stale) session 0 users. */
792 if ( pSessionData->Session == 0
793 /* Also check the logon time. */
794 || pSessionData->LogonTime.QuadPart == 0)
795 {
796 g_pfnLsaFreeReturnBuffer(pSessionData);
797 return false;
798 }
799 }
800
801 /*
802 * Only handle users which can login interactively or logged in
803 * remotely over native RDP.
804 */
805 bool fFoundUser = false;
806 if ( IsValidSid(pSessionData->Sid)
807 && ( (SECURITY_LOGON_TYPE)pSessionData->LogonType == Interactive
808 || (SECURITY_LOGON_TYPE)pSessionData->LogonType == RemoteInteractive
809 /* Note: We also need CachedInteractive in case Windows cached the credentials
810 * or just wants to reuse them! */
811 || (SECURITY_LOGON_TYPE)pSessionData->LogonType == CachedInteractive))
812 {
813 VGSvcVerbose(3, "Session LogonType=%u is supported -- looking up SID + type ...\n", pSessionData->LogonType);
814
815 /*
816 * Copy out relevant data.
817 */
818 vgsvcVMInfoWinSafeCopy(pUserInfo->wszUser, sizeof(pUserInfo->wszUser), &pSessionData->UserName, "User name");
819 vgsvcVMInfoWinSafeCopy(pUserInfo->wszAuthenticationPackage, sizeof(pUserInfo->wszAuthenticationPackage),
820 &pSessionData->AuthenticationPackage, "Authentication pkg name");
821 vgsvcVMInfoWinSafeCopy(pUserInfo->wszLogonDomain, sizeof(pUserInfo->wszLogonDomain),
822 &pSessionData->LogonDomain, "Logon domain name");
823
824 TCHAR szOwnerName[_MAX_PATH] = { 0 };
825 DWORD dwOwnerNameSize = sizeof(szOwnerName);
826 TCHAR szDomainName[_MAX_PATH] = { 0 };
827 DWORD dwDomainNameSize = sizeof(szDomainName);
828 SID_NAME_USE enmOwnerType = SidTypeInvalid;
829 if (!LookupAccountSid(NULL,
830 pSessionData->Sid,
831 szOwnerName,
832 &dwOwnerNameSize,
833 szDomainName,
834 &dwDomainNameSize,
835 &enmOwnerType))
836 {
837 DWORD dwErr = GetLastError();
838 /*
839 * If a network time-out prevents the function from finding the name or
840 * if a SID that does not have a corresponding account name (such as a
841 * logon SID that identifies a logon session), we get ERROR_NONE_MAPPED
842 * here that we just skip.
843 */
844 if (dwErr != ERROR_NONE_MAPPED)
845 VGSvcError("Failed looking up account info for user=%ls, error=$ld!\n", pUserInfo->wszUser, dwErr);
846 }
847 else
848 {
849 if (enmOwnerType == SidTypeUser) /* Only recognize users; we don't care about the rest! */
850 {
851 VGSvcVerbose(3, "Account User=%ls, Session=%u, LogonID=%d,%u, AuthPkg=%ls, Domain=%ls\n",
852 pUserInfo->wszUser, pSessionData->Session, pSessionData->LogonId.HighPart,
853 pSessionData->LogonId.LowPart, pUserInfo->wszAuthenticationPackage, pUserInfo->wszLogonDomain);
854
855 /**
856 * Note: On certain Windows OSes WTSQuerySessionInformation leaks memory when used
857 * under a heavy stress situation. There are hotfixes available from Microsoft.
858 *
859 * See: http://support.microsoft.com/kb/970910
860 */
861 if (!s_fSkipRDPDetection)
862 {
863 /* Skip RDP detection on non-NT systems. */
864 if (g_WinVersion.dwPlatformId != VER_PLATFORM_WIN32_NT)
865 s_fSkipRDPDetection = true;
866
867 /* Skip RDP detection on Windows 2000.
868 * For Windows 2000 however we don't have any hotfixes, so just skip the
869 * RDP detection in any case. */
870 if ( g_WinVersion.dwMajorVersion == 5
871 && g_WinVersion.dwMinorVersion == 0)
872 s_fSkipRDPDetection = true;
873
874 /* Skip if we don't have the WTS API. */
875 if (!g_pfnWTSQuerySessionInformationA)
876 s_fSkipRDPDetection = true;
877
878 if (s_fSkipRDPDetection)
879 VGSvcVerbose(0, "Detection of logged-in users via RDP is disabled\n");
880 }
881
882 if (!s_fSkipRDPDetection)
883 {
884 Assert(g_pfnWTSQuerySessionInformationA);
885 Assert(g_pfnWTSFreeMemory);
886
887 /* Detect RDP sessions as well. */
888 LPTSTR pBuffer = NULL;
889 DWORD cbRet = 0;
890 int iState = -1;
891 if (g_pfnWTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE,
892 pSessionData->Session,
893 WTSConnectState,
894 &pBuffer,
895 &cbRet))
896 {
897 if (cbRet)
898 iState = *pBuffer;
899 VGSvcVerbose(3, "Account User=%ls, WTSConnectState=%d (%u)\n", pUserInfo->wszUser, iState, cbRet);
900 if ( iState == WTSActive /* User logged on to WinStation. */
901 || iState == WTSShadow /* Shadowing another WinStation. */
902 || iState == WTSDisconnected) /* WinStation logged on without client. */
903 {
904 /** @todo On Vista and W2K, always "old" user name are still
905 * there. Filter out the old one! */
906 VGSvcVerbose(3, "Account User=%ls using TCS/RDP, state=%d \n", pUserInfo->wszUser, iState);
907 fFoundUser = true;
908 }
909 if (pBuffer)
910 g_pfnWTSFreeMemory(pBuffer);
911 }
912 else
913 {
914 DWORD dwLastErr = GetLastError();
915 switch (dwLastErr)
916 {
917 /*
918 * Terminal services don't run (for example in W2K,
919 * nothing to worry about ...). ... or is on the Vista
920 * fast user switching page!
921 */
922 case ERROR_CTX_WINSTATION_NOT_FOUND:
923 VGSvcVerbose(3, "No WinStation found for user=%ls\n", pUserInfo->wszUser);
924 break;
925
926 default:
927 VGSvcVerbose(3, "Cannot query WTS connection state for user=%ls, error=%u\n",
928 pUserInfo->wszUser, dwLastErr);
929 break;
930 }
931
932 fFoundUser = true;
933 }
934 }
935 }
936 else
937 VGSvcVerbose(3, "SID owner type=%d not handled, skipping\n", enmOwnerType);
938 }
939
940 VGSvcVerbose(3, "Account User=%ls %s logged in\n", pUserInfo->wszUser, fFoundUser ? "is" : "is not");
941 }
942
943 if (fFoundUser)
944 pUserInfo->ulLastSession = pSessionData->Session;
945
946 g_pfnLsaFreeReturnBuffer(pSessionData);
947 return fFoundUser;
948}
949
950
951static int vgsvcVMInfoWinWriteLastInput(PVBOXSERVICEVEPROPCACHE pCache, const char *pszUser, const char *pszDomain)
952{
953 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
954 AssertPtrReturn(pszUser, VERR_INVALID_POINTER);
955 /* pszDomain is optional. */
956
957 char szPipeName[512 + sizeof(VBOXTRAY_IPC_PIPE_PREFIX)];
958 memcpy(szPipeName, VBOXTRAY_IPC_PIPE_PREFIX, sizeof(VBOXTRAY_IPC_PIPE_PREFIX));
959 int rc = RTStrCat(szPipeName, sizeof(szPipeName), pszUser);
960 if (RT_SUCCESS(rc))
961 {
962 bool fReportToHost = false;
963 VBoxGuestUserState userState = VBoxGuestUserState_Unknown;
964
965 RTLOCALIPCSESSION hSession;
966 rc = RTLocalIpcSessionConnect(&hSession, szPipeName, RTLOCALIPC_FLAGS_NATIVE_NAME);
967 if (RT_SUCCESS(rc))
968 {
969 VBOXTRAYIPCHEADER ipcHdr =
970 {
971 /* .uMagic = */ VBOXTRAY_IPC_HDR_MAGIC,
972 /* .uHdrVersion = */ 0,
973 /* .uMsgType = */ VBOXTRAYIPCMSGTYPE_USERLASTINPUT,
974 /* .cbMsgData = */ 0 /* No msg */
975 };
976
977 rc = RTLocalIpcSessionWrite(hSession, &ipcHdr, sizeof(ipcHdr));
978
979 if (RT_SUCCESS(rc))
980 {
981 VBOXTRAYIPCRES_USERLASTINPUT ipcRes;
982 rc = RTLocalIpcSessionRead(hSession, &ipcRes, sizeof(ipcRes), NULL /* Exact read */);
983 if ( RT_SUCCESS(rc)
984 /* If uLastInput is set to UINT32_MAX VBoxTray was not able to retrieve the
985 * user's last input time. This might happen when running on Windows NT4 or older. */
986 && ipcRes.uLastInput != UINT32_MAX)
987 {
988 userState = (ipcRes.uLastInput * 1000) < g_uVMInfoUserIdleThresholdMS
989 ? VBoxGuestUserState_InUse
990 : VBoxGuestUserState_Idle;
991
992 rc = VGSvcUserUpdateF(pCache, pszUser, pszDomain, "UsageState",
993 userState == VBoxGuestUserState_InUse ? "InUse" : "Idle");
994
995 /*
996 * Note: vboxServiceUserUpdateF can return VINF_NO_CHANGE in case there wasn't anything
997 * to update. So only report the user's status to host when we really got something
998 * new.
999 */
1000 fReportToHost = rc == VINF_SUCCESS;
1001 VGSvcVerbose(4, "User '%s' (domain '%s') is idle for %RU32, fReportToHost=%RTbool\n",
1002 pszUser, pszDomain ? pszDomain : "<None>", ipcRes.uLastInput, fReportToHost);
1003
1004#if 0 /* Do we want to write the idle time as well? */
1005 /* Also write the user's current idle time, if there is any. */
1006 if (userState == VBoxGuestUserState_Idle)
1007 rc = vgsvcUserUpdateF(pCache, pszUser, pszDomain, "IdleTimeMs", "%RU32", ipcRes.uLastInputMs);
1008 else
1009 rc = vgsvcUserUpdateF(pCache, pszUser, pszDomain, "IdleTimeMs", NULL /* Delete property */);
1010
1011 if (RT_SUCCESS(rc))
1012#endif
1013 }
1014#ifdef DEBUG
1015 else if (RT_SUCCESS(rc) && ipcRes.uLastInput == UINT32_MAX)
1016 VGSvcVerbose(4, "Last input for user '%s' is not supported, skipping\n", pszUser, rc);
1017#endif
1018 }
1019#ifdef DEBUG
1020 VGSvcVerbose(4, "Getting last input for user '%s' ended with rc=%Rrc\n", pszUser, rc);
1021#endif
1022 int rc2 = RTLocalIpcSessionClose(hSession);
1023 if (RT_SUCCESS(rc) && RT_FAILURE(rc2))
1024 rc = rc2;
1025 }
1026 else
1027 {
1028 switch (rc)
1029 {
1030 case VERR_FILE_NOT_FOUND:
1031 {
1032 /* No VBoxTray (or too old version which does not support IPC) running
1033 for the given user. Not much we can do then. */
1034 VGSvcVerbose(4, "VBoxTray for user '%s' not running (anymore), no last input available\n", pszUser);
1035
1036 /* Overwrite rc from above. */
1037 rc = VGSvcUserUpdateF(pCache, pszUser, pszDomain, "UsageState", "Idle");
1038
1039 fReportToHost = rc == VINF_SUCCESS;
1040 if (fReportToHost)
1041 userState = VBoxGuestUserState_Idle;
1042 break;
1043 }
1044
1045 default:
1046 VGSvcError("Error querying last input for user '%s', rc=%Rrc\n", pszUser, rc);
1047 break;
1048 }
1049 }
1050
1051 if (fReportToHost)
1052 {
1053 Assert(userState != VBoxGuestUserState_Unknown);
1054 int rc2 = VbglR3GuestUserReportState(pszUser, pszDomain, userState, NULL /* No details */, 0);
1055 if (RT_FAILURE(rc2))
1056 VGSvcError("Error reporting usage state %d for user '%s' to host, rc=%Rrc\n", userState, pszUser, rc2);
1057 if (RT_SUCCESS(rc))
1058 rc = rc2;
1059 }
1060 }
1061
1062 return rc;
1063}
1064
1065
1066/**
1067 * Retrieves the currently logged in users and stores their names along with the
1068 * user count.
1069 *
1070 * @returns VBox status code.
1071 * @param pCache Property cache to use for storing some of the lookup
1072 * data in between calls.
1073 * @param ppszUserList Where to store the user list (separated by commas).
1074 * Must be freed with RTStrFree().
1075 * @param pcUsersInList Where to store the number of users in the list.
1076 */
1077int VGSvcVMInfoWinWriteUsers(PVBOXSERVICEVEPROPCACHE pCache, char **ppszUserList, uint32_t *pcUsersInList)
1078{
1079 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
1080 AssertPtrReturn(ppszUserList, VERR_INVALID_POINTER);
1081 AssertPtrReturn(pcUsersInList, VERR_INVALID_POINTER);
1082
1083 int rc = RTOnce(&g_vgsvcWinVmInitOnce, vgsvcWinVmInfoInitOnce, NULL);
1084 if (RT_FAILURE(rc))
1085 return rc;
1086 if (!g_pfnLsaEnumerateLogonSessions || !g_pfnEnumProcesses)
1087 return VERR_NOT_SUPPORTED;
1088
1089 rc = VbglR3GuestPropConnect(&s_uDebugGuestPropClientID);
1090 AssertRC(rc);
1091
1092 char *pszUserList = NULL;
1093 uint32_t cUsersInList = 0;
1094
1095 /* This function can report stale or orphaned interactive logon sessions
1096 of already logged off users (especially in Windows 2000). */
1097 PLUID paSessions = NULL;
1098 ULONG cSessions = 0;
1099 NTSTATUS rcNt = g_pfnLsaEnumerateLogonSessions(&cSessions, &paSessions);
1100 if (rcNt != STATUS_SUCCESS)
1101 {
1102 ULONG uError = LsaNtStatusToWinError(rcNt);
1103 switch (uError)
1104 {
1105 case ERROR_NOT_ENOUGH_MEMORY:
1106 VGSvcError("Not enough memory to enumerate logon sessions!\n");
1107 break;
1108
1109 case ERROR_SHUTDOWN_IN_PROGRESS:
1110 /* If we're about to shutdown when we were in the middle of enumerating the logon
1111 * sessions, skip the error to not confuse the user with an unnecessary log message. */
1112 VGSvcVerbose(3, "Shutdown in progress ...\n");
1113 uError = ERROR_SUCCESS;
1114 break;
1115
1116 default:
1117 VGSvcError("LsaEnumerate failed with error %RU32\n", uError);
1118 break;
1119 }
1120
1121 if (paSessions)
1122 g_pfnLsaFreeReturnBuffer(paSessions);
1123
1124 return RTErrConvertFromWin32(uError);
1125 }
1126 VGSvcVerbose(3, "Found %u sessions\n", cSessions);
1127
1128 PVBOXSERVICEVMINFOPROC paProcs;
1129 DWORD cProcs;
1130 rc = vgsvcVMInfoWinProcessesEnumerate(&paProcs, &cProcs);
1131 if (RT_FAILURE(rc))
1132 {
1133 if (rc == VERR_NO_MEMORY)
1134 VGSvcError("Not enough memory to enumerate processes\n");
1135 else
1136 VGSvcError("Failed to enumerate processes, rc=%Rrc\n", rc);
1137 }
1138 else
1139 {
1140 PVBOXSERVICEVMINFOUSER pUserInfo;
1141 pUserInfo = (PVBOXSERVICEVMINFOUSER)RTMemAllocZ(cSessions * sizeof(VBOXSERVICEVMINFOUSER) + 1);
1142 if (!pUserInfo)
1143 VGSvcError("Not enough memory to store enumerated users!\n");
1144 else
1145 {
1146 ULONG cUniqueUsers = 0;
1147
1148 /*
1149 * Note: The cSessions loop variable does *not* correlate with
1150 * the Windows session ID!
1151 */
1152 for (ULONG i = 0; i < cSessions; i++)
1153 {
1154 VGSvcVerbose(3, "Handling session %RU32 (of %RU32)\n", i + 1, cSessions);
1155
1156 VBOXSERVICEVMINFOUSER userSession;
1157 if (vgsvcVMInfoWinIsLoggedIn(&userSession, &paSessions[i]))
1158 {
1159 VGSvcVerbose(4, "Handling user=%ls, domain=%ls, package=%ls, session=%RU32\n",
1160 userSession.wszUser, userSession.wszLogonDomain, userSession.wszAuthenticationPackage,
1161 userSession.ulLastSession);
1162
1163 /* Retrieve assigned processes of current session. */
1164 uint32_t cCurSessionProcs = vgsvcVMInfoWinSessionHasProcesses(&paSessions[i], paProcs, cProcs,
1165 NULL /* Terminal session ID */);
1166 /* Don't return here when current session does not have assigned processes
1167 * anymore -- in that case we have to search through the unique users list below
1168 * and see if got a stale user/session entry. */
1169
1170 if (g_cVerbosity > 3)
1171 {
1172 char szDebugSessionPath[255];
1173 RTStrPrintf(szDebugSessionPath, sizeof(szDebugSessionPath),
1174 "/VirtualBox/GuestInfo/Debug/LSA/Session/%RU32", userSession.ulLastSession);
1175 VGSvcWritePropF(s_uDebugGuestPropClientID, szDebugSessionPath,
1176 "#%RU32: cSessionProcs=%RU32 (of %RU32 procs total)",
1177 s_uDebugIter, cCurSessionProcs, cProcs);
1178 }
1179
1180 bool fFoundUser = false;
1181 for (ULONG a = 0; a < cUniqueUsers; a++)
1182 {
1183 PVBOXSERVICEVMINFOUSER pCurUser = &pUserInfo[a];
1184 AssertPtr(pCurUser);
1185
1186 if ( !wcscmp(userSession.wszUser, pCurUser->wszUser)
1187 && !wcscmp(userSession.wszLogonDomain, pCurUser->wszLogonDomain)
1188 && !wcscmp(userSession.wszAuthenticationPackage, pCurUser->wszAuthenticationPackage))
1189 {
1190 /*
1191 * Only respect the highest session for the current user.
1192 */
1193 if (userSession.ulLastSession > pCurUser->ulLastSession)
1194 {
1195 VGSvcVerbose(4, "Updating user=%ls to %u processes (last used session: %RU32)\n",
1196 pCurUser->wszUser, cCurSessionProcs, userSession.ulLastSession);
1197
1198 if (!cCurSessionProcs)
1199 VGSvcVerbose(3, "Stale session for user=%ls detected! Processes: %RU32 -> %RU32, Session: %RU32 -> %RU32\n",
1200 pCurUser->wszUser, pCurUser->ulNumProcs, cCurSessionProcs,
1201 pCurUser->ulLastSession, userSession.ulLastSession);
1202
1203 pCurUser->ulNumProcs = cCurSessionProcs;
1204 pCurUser->ulLastSession = userSession.ulLastSession;
1205 }
1206 /* There can be multiple session objects using the same session ID for the
1207 * current user -- so when we got the same session again just add the found
1208 * processes to it. */
1209 else if (pCurUser->ulLastSession == userSession.ulLastSession)
1210 {
1211 VGSvcVerbose(4, "Updating processes for user=%ls (old procs=%RU32, new procs=%RU32, session=%RU32)\n",
1212 pCurUser->wszUser, pCurUser->ulNumProcs, cCurSessionProcs, pCurUser->ulLastSession);
1213
1214 pCurUser->ulNumProcs = cCurSessionProcs;
1215 }
1216
1217 fFoundUser = true;
1218 break;
1219 }
1220 }
1221
1222 if (!fFoundUser)
1223 {
1224 VGSvcVerbose(4, "Adding new user=%ls (session=%RU32) with %RU32 processes\n",
1225 userSession.wszUser, userSession.ulLastSession, cCurSessionProcs);
1226
1227 memcpy(&pUserInfo[cUniqueUsers], &userSession, sizeof(VBOXSERVICEVMINFOUSER));
1228 pUserInfo[cUniqueUsers].ulNumProcs = cCurSessionProcs;
1229 cUniqueUsers++;
1230 Assert(cUniqueUsers <= cSessions);
1231 }
1232 }
1233 }
1234
1235 if (g_cVerbosity > 3)
1236 VGSvcWritePropF(s_uDebugGuestPropClientID, "/VirtualBox/GuestInfo/Debug/LSA",
1237 "#%RU32: cSessions=%RU32, cProcs=%RU32, cUniqueUsers=%RU32",
1238 s_uDebugIter, cSessions, cProcs, cUniqueUsers);
1239
1240 VGSvcVerbose(3, "Found %u unique logged-in user(s)\n", cUniqueUsers);
1241
1242 for (ULONG i = 0; i < cUniqueUsers; i++)
1243 {
1244 if (g_cVerbosity > 3)
1245 {
1246 char szDebugUserPath[255]; RTStrPrintf(szDebugUserPath, sizeof(szDebugUserPath), "/VirtualBox/GuestInfo/Debug/LSA/User/%RU32", i);
1247 VGSvcWritePropF(s_uDebugGuestPropClientID, szDebugUserPath,
1248 "#%RU32: szName=%ls, sessionID=%RU32, cProcs=%RU32",
1249 s_uDebugIter, pUserInfo[i].wszUser, pUserInfo[i].ulLastSession, pUserInfo[i].ulNumProcs);
1250 }
1251
1252 bool fAddUser = false;
1253 if (pUserInfo[i].ulNumProcs)
1254 fAddUser = true;
1255
1256 if (fAddUser)
1257 {
1258 VGSvcVerbose(3, "User '%ls' has %RU32 interactive processes (session=%RU32)\n",
1259 pUserInfo[i].wszUser, pUserInfo[i].ulNumProcs, pUserInfo[i].ulLastSession);
1260
1261 if (cUsersInList > 0)
1262 {
1263 rc = RTStrAAppend(&pszUserList, ",");
1264 AssertRCBreakStmt(rc, RTStrFree(pszUserList));
1265 }
1266
1267 cUsersInList += 1;
1268
1269 char *pszUser = NULL;
1270 char *pszDomain = NULL;
1271 rc = RTUtf16ToUtf8(pUserInfo[i].wszUser, &pszUser);
1272 if ( RT_SUCCESS(rc)
1273 && pUserInfo[i].wszLogonDomain)
1274 rc = RTUtf16ToUtf8(pUserInfo[i].wszLogonDomain, &pszDomain);
1275 if (RT_SUCCESS(rc))
1276 {
1277 /* Append user to users list. */
1278 rc = RTStrAAppend(&pszUserList, pszUser);
1279
1280 /* Do idle detection. */
1281 if (RT_SUCCESS(rc))
1282 rc = vgsvcVMInfoWinWriteLastInput(pCache, pszUser, pszDomain);
1283 }
1284 else
1285 rc = RTStrAAppend(&pszUserList, "<string-conversion-error>");
1286
1287 RTStrFree(pszUser);
1288 RTStrFree(pszDomain);
1289
1290 AssertRCBreakStmt(rc, RTStrFree(pszUserList));
1291 }
1292 }
1293
1294 RTMemFree(pUserInfo);
1295 }
1296 vgsvcVMInfoWinProcessesFree(cProcs, paProcs);
1297 }
1298 if (paSessions)
1299 g_pfnLsaFreeReturnBuffer(paSessions);
1300
1301 if (RT_SUCCESS(rc))
1302 {
1303 *ppszUserList = pszUserList;
1304 *pcUsersInList = cUsersInList;
1305 }
1306
1307 s_uDebugIter++;
1308 VbglR3GuestPropDisconnect(s_uDebugGuestPropClientID);
1309
1310 return rc;
1311}
1312
1313
1314int VGSvcVMInfoWinGetComponentVersions(uint32_t uClientID)
1315{
1316 int rc;
1317 char szSysDir[_MAX_PATH] = {0};
1318 char szWinDir[_MAX_PATH] = {0};
1319 char szDriversDir[_MAX_PATH + 32] = {0};
1320
1321 /* ASSUME: szSysDir and szWinDir and derivatives are always ASCII compatible. */
1322 GetSystemDirectory(szSysDir, _MAX_PATH);
1323 GetWindowsDirectory(szWinDir, _MAX_PATH);
1324 RTStrPrintf(szDriversDir, sizeof(szDriversDir), "%s\\drivers", szSysDir);
1325#ifdef RT_ARCH_AMD64
1326 char szSysWowDir[_MAX_PATH + 32] = {0};
1327 RTStrPrintf(szSysWowDir, sizeof(szSysWowDir), "%s\\SysWow64", szWinDir);
1328#endif
1329
1330 /* The file information table. */
1331 const VBOXSERVICEVMINFOFILE aVBoxFiles[] =
1332 {
1333 { szSysDir, "VBoxControl.exe" },
1334 { szSysDir, "VBoxHook.dll" },
1335 { szSysDir, "VBoxDisp.dll" },
1336 { szSysDir, "VBoxTray.exe" },
1337#ifdef TARGET_NT4
1338 { szSysDir, "VBoxServiceNT.exe" },
1339#else
1340 { szSysDir, "VBoxService.exe" },
1341 { szSysDir, "VBoxMRXNP.dll" },
1342 { szSysDir, "VBoxGINA.dll" },
1343 { szSysDir, "VBoxCredProv.dll" },
1344#endif
1345
1346 /* On 64-bit we don't yet have the OpenGL DLLs in native format.
1347 So just enumerate the 32-bit files in the SYSWOW directory. */
1348#ifdef RT_ARCH_AMD64
1349 { szSysWowDir, "VBoxOGLarrayspu.dll" },
1350 { szSysWowDir, "VBoxOGLcrutil.dll" },
1351 { szSysWowDir, "VBoxOGLerrorspu.dll" },
1352 { szSysWowDir, "VBoxOGLpackspu.dll" },
1353 { szSysWowDir, "VBoxOGLpassthroughspu.dll" },
1354 { szSysWowDir, "VBoxOGLfeedbackspu.dll" },
1355 { szSysWowDir, "VBoxOGL.dll" },
1356#else /* !RT_ARCH_AMD64 */
1357# ifdef TARGET_NT4
1358 { szSysDir, "VBoxOGLarrayspu.dll" },
1359 { szSysDir, "VBoxOGLcrutil.dll" },
1360 { szSysDir, "VBoxOGLerrorspu.dll" },
1361 { szSysDir, "VBoxOGLpackspu.dll" },
1362 { szSysDir, "VBoxOGLpassthroughspu.dll" },
1363 { szSysDir, "VBoxOGLfeedbackspu.dll" },
1364 { szSysDir, "VBoxOGL.dll" },
1365# endif
1366#endif /* !RT_ARCH_AMD64 */
1367
1368#ifdef TARGET_NT4
1369 { szDriversDir, "VBoxGuestNT.sys" },
1370 { szDriversDir, "VBoxMouseNT.sys" },
1371#else
1372 { szDriversDir, "VBoxGuest.sys" },
1373 { szDriversDir, "VBoxMouse.sys" },
1374 { szDriversDir, "VBoxSF.sys" },
1375#endif
1376 { szDriversDir, "VBoxVideo.sys" },
1377 };
1378
1379 for (unsigned i = 0; i < RT_ELEMENTS(aVBoxFiles); i++)
1380 {
1381 char szVer[128];
1382 VGSvcUtilWinGetFileVersionString(aVBoxFiles[i].pszFilePath, aVBoxFiles[i].pszFileName, szVer, sizeof(szVer));
1383 char szPropPath[256];
1384 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestAdd/Components/%s", aVBoxFiles[i].pszFileName);
1385 rc = VGSvcWritePropF(uClientID, szPropPath, "%s", szVer);
1386 }
1387
1388 return VINF_SUCCESS;
1389}
1390
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