VirtualBox

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

Last change on this file since 53332 was 53332, checked in by vboxsync, 10 years ago

VBoxService/VBoxServiceVMInfo-win.cpp: Logging.

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