VirtualBox

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

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

updates

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