VirtualBox

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

Last change on this file since 45547 was 44884, checked in by vboxsync, 12 years ago

VBoxServiceVMInfo-win: Include MMR file information.

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

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