VirtualBox

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

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

RTLocalIpc fixes.

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