VirtualBox

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

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

VBoxServiceVMInfo-win.cpp: kernel32

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