VirtualBox

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

Last change on this file since 29535 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.5 KB
Line 
1/* $Id: VBoxServiceVMInfo-win.cpp 28800 2010-04-27 08:22:32Z vboxsync $ */
2/** @file
3 * VBoxService - Virtual Machine Information for the Host, Windows specifics.
4 */
5
6/*
7 * Copyright (C) 2009-2010 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#include <windows.h>
23#include <wtsapi32.h> /* For WTS* calls. */
24#include <psapi.h> /* EnumProcesses. */
25#include <Ntsecapi.h> /* Needed for process security information. */
26
27#include <iprt/assert.h>
28#include <iprt/mem.h>
29#include <iprt/thread.h>
30#include <iprt/string.h>
31#include <iprt/semaphore.h>
32#include <iprt/system.h>
33#include <iprt/time.h>
34#include <VBox/VBoxGuestLib.h>
35#include "VBoxServiceInternal.h"
36#include "VBoxServiceUtils.h"
37
38
39/*******************************************************************************
40* Global Variables *
41*******************************************************************************/
42/** Function prototypes for dynamic loading. */
43PFNWTSGETACTIVECONSOLESESSIONID g_pfnWTSGetActiveConsoleSessionId = NULL;
44
45
46#ifndef TARGET_NT4
47
48/**
49 * Fills in more data for a process.
50 *
51 * @returns VBox status code.
52 * @param pProc The process structure to fill data into.
53 * @param tkClass The kind of token information to get.
54 */
55static int VBoxServiceVMInfoWinProcessesGetTokenInfo(PVBOXSERVICEVMINFOPROC pProc,
56 TOKEN_INFORMATION_CLASS tkClass)
57{
58 AssertPtr(pProc);
59 HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pProc->id);
60 if (h == NULL)
61 return RTErrConvertFromWin32(GetLastError());
62
63 int rc = VERR_NO_MEMORY;
64 HANDLE hToken;
65 if (OpenProcessToken(h, TOKEN_QUERY, &hToken))
66 {
67 void *pvTokenInfo = NULL;
68 DWORD dwTokenInfoSize;
69 switch (tkClass)
70 {
71 case TokenStatistics:
72 dwTokenInfoSize = sizeof(TOKEN_STATISTICS);
73 pvTokenInfo = RTMemAlloc(dwTokenInfoSize);
74 break;
75
76 /** @todo Implement more token classes here. */
77
78 default:
79 VBoxServiceError("Token class not implemented: %ld", tkClass);
80 rc = VERR_NOT_IMPLEMENTED;
81 break;
82 }
83
84 if (pvTokenInfo)
85 {
86 DWORD dwRetLength;
87 if (GetTokenInformation(hToken, tkClass, pvTokenInfo, dwTokenInfoSize, &dwRetLength))
88 {
89 switch (tkClass)
90 {
91 case TokenStatistics:
92 {
93 TOKEN_STATISTICS *pStats = (TOKEN_STATISTICS*)pvTokenInfo;
94 pProc->luid = pStats->AuthenticationId;
95 /** @todo Add more information of TOKEN_STATISTICS as needed. */
96 break;
97 }
98
99 default:
100 /* Should never get here! */
101 break;
102 }
103 rc = VINF_SUCCESS;
104 }
105 else
106 rc = RTErrConvertFromWin32(GetLastError());
107 RTMemFree(pvTokenInfo);
108 }
109 CloseHandle(hToken);
110 }
111 else
112 rc = RTErrConvertFromWin32(GetLastError());
113 CloseHandle(h);
114 return rc;
115}
116
117
118/**
119 * Enumerate all the processes in the system and get the logon user IDs for
120 * them.
121 *
122 * @returns VBox status code.
123 * @param ppaProcs Where to return the process snapshot. This must be
124 * freed by calling VBoxServiceVMInfoWinProcessesFree.
125 *
126 * @param pcProcs Where to store the returned process count.
127 */
128int VBoxServiceVMInfoWinProcessesEnumerate(PVBOXSERVICEVMINFOPROC *ppaProcs, PDWORD pcProcs)
129{
130 AssertPtr(ppaProcs);
131 AssertPtr(pcProcs);
132
133 /*
134 * Call EnumProcesses with an increasingly larger buffer until it all fits
135 * or we think something is screwed up.
136 */
137 DWORD cProcesses = 64;
138 PDWORD paPids = NULL;
139 int rc = VINF_SUCCESS;
140 do
141 {
142 /* Allocate / grow the buffer first. */
143 cProcesses *= 2;
144 void *pvNew = RTMemRealloc(paPids, cProcesses * sizeof(DWORD));
145 if (!pvNew)
146 {
147 rc = VERR_NO_MEMORY;
148 break;
149 }
150 paPids = (PDWORD)pvNew;
151
152 /* Query the processes. Not the cbRet == buffer size means there could be more work to be done. */
153 DWORD cbRet;
154 if (!EnumProcesses(paPids, cProcesses * sizeof(DWORD), &cbRet))
155 {
156 rc = RTErrConvertFromWin32(GetLastError());
157 break;
158 }
159 if (cbRet < cProcesses * sizeof(DWORD))
160 {
161 cProcesses = cbRet / sizeof(DWORD);
162 break;
163 }
164 } while (cProcesses <= 32768); /* Should be enough; see: http://blogs.technet.com/markrussinovich/archive/2009/07/08/3261309.aspx */
165 if (RT_SUCCESS(rc))
166 {
167 /*
168 * Allocate out process structures and fill data into them.
169 * We currently only try lookup their LUID's.
170 */
171 PVBOXSERVICEVMINFOPROC paProcs;
172 paProcs = (PVBOXSERVICEVMINFOPROC)RTMemAllocZ(cProcesses * sizeof(VBOXSERVICEVMINFOPROC));
173 if (paProcs)
174 {
175 for (DWORD i = 0; i < cProcesses; i++)
176 {
177 paProcs[i].id = paPids[i];
178 rc = VBoxServiceVMInfoWinProcessesGetTokenInfo(&paProcs[i], TokenStatistics);
179 if (RT_FAILURE(rc))
180 {
181 /* Because some processes cannot be opened/parsed on
182 Windows, we should not consider to be this an error here. */
183 rc = VINF_SUCCESS;
184 }
185 }
186
187 /* Save number of processes */
188 if (RT_SUCCESS(rc))
189 {
190 *pcProcs = cProcesses;
191 *ppaProcs = paProcs;
192 }
193 else
194 RTMemFree(paProcs);
195 }
196 else
197 rc = VERR_NO_MEMORY;
198 }
199
200 RTMemFree(paPids);
201 return rc;
202}
203
204/**
205 * Frees the process structures returned by
206 * VBoxServiceVMInfoWinProcessesEnumerate() before.
207 *
208 * @param paProcs What
209 */
210void VBoxServiceVMInfoWinProcessesFree(PVBOXSERVICEVMINFOPROC paProcs)
211{
212 RTMemFree(paProcs);
213}
214
215/**
216 * Determins whether the specified session has processes on the system.
217 *
218 * @returns true if it has, false if it doesn't.
219 * @param pSession The session.
220 * @param paProcs The process snapshot.
221 * @param cProcs The number of processes in the snaphot.
222 */
223bool VBoxServiceVMInfoWinSessionHasProcesses(PLUID pSession, VBOXSERVICEVMINFOPROC const *paProcs, DWORD cProcs)
224{
225 AssertPtr(pSession);
226
227 if (!cProcs) /* To be on the safe side. */
228 return false;
229 AssertPtr(paProcs);
230
231 PSECURITY_LOGON_SESSION_DATA pSessionData = NULL;
232 NTSTATUS rcNt = LsaGetLogonSessionData(pSession, &pSessionData);
233 if (rcNt != STATUS_SUCCESS)
234 {
235 VBoxServiceError("Could not get logon session data! rcNt=%#x", rcNt);
236 return false;
237 }
238 AssertPtrReturn(pSessionData, false);
239
240 /*
241 * Even if a user seems to be logged in, it could be a stale/orphaned logon
242 * session. So check if we have some processes bound to it by comparing the
243 * session <-> process LUIDs.
244 */
245 for (DWORD i = 0; i < cProcs; i++)
246 {
247 /*VBoxServiceVerbose(3, "%ld:%ld <-> %ld:%ld\n",
248 paProcs[i].luid.HighPart, paProcs[i].luid.LowPart,
249 pSessionData->LogonId.HighPart, pSessionData->LogonId.LowPart);*/
250 if ( paProcs[i].luid.HighPart == pSessionData->LogonId.HighPart
251 && paProcs[i].luid.LowPart == pSessionData->LogonId.LowPart)
252 {
253 VBoxServiceVerbose(3, "Users: Session %ld:%ld has active processes\n",
254 pSessionData->LogonId.HighPart, pSessionData->LogonId.LowPart);
255 LsaFreeReturnBuffer(pSessionData);
256 return true;
257 }
258 }
259 LsaFreeReturnBuffer(pSessionData);
260 return false;
261}
262
263
264/**
265 * Save and noisy string copy.
266 *
267 * @param pwszDst Destination buffer.
268 * @param cbDst Size in bytes - not WCHAR count!
269 * @param pSrc Source string.
270 * @param pszWhat What this is. For the log.
271 */
272static void VBoxServiceVMInfoWinSafeCopy(PWCHAR pwszDst, size_t cbDst, LSA_UNICODE_STRING const *pSrc, const char *pszWhat)
273{
274 Assert(RT_ALIGN(cbDst, sizeof(WCHAR)) == cbDst);
275
276 size_t cbCopy = pSrc->Length;
277 if (cbCopy + sizeof(WCHAR) > cbDst)
278 {
279 VBoxServiceVerbose(0, "%s is too long - %u bytes, buffer %u bytes! It will be truncated.\n",
280 pszWhat, cbCopy, cbDst);
281 cbCopy = cbDst - sizeof(WCHAR);
282 }
283 if (cbCopy)
284 memcpy(pwszDst, pSrc->Buffer, cbCopy);
285 pwszDst[cbCopy / sizeof(WCHAR)] = '\0';
286}
287
288
289/**
290 * Detects whether a user is logged on based on the enumerated processes.
291 *
292 * @returns true if logged in, false if not (or error).
293 * @param a_pUserInfo Where to return the user information.
294 * @param a_pSession The session to check.
295 */
296bool VBoxServiceVMInfoWinIsLoggedIn(PVBOXSERVICEVMINFOUSER a_pUserInfo, PLUID a_pSession)
297{
298 if (!a_pSession)
299 return false;
300
301 PSECURITY_LOGON_SESSION_DATA pSessionData = NULL;
302 NTSTATUS rcNt = LsaGetLogonSessionData(a_pSession, &pSessionData);
303 if (rcNt != STATUS_SUCCESS)
304 {
305 VBoxServiceError("LsaGetLogonSessionData failed, LSA error %#x\n", LsaNtStatusToWinError(rcNt));
306 if (pSessionData)
307 LsaFreeReturnBuffer(pSessionData);
308 return false;
309 }
310 if (!pSessionData)
311 {
312 VBoxServiceError("Invalid logon session data.\n");
313 return false;
314 }
315 VBoxServiceVerbose(3, "Users: Session data: Name = %ls, Len = %d, SID = %s, LogonID = %d,%d\n",
316 pSessionData->UserName.Buffer,
317 pSessionData->UserName.Length,
318 pSessionData->Sid != NULL ? "1" : "0",
319 pSessionData->LogonId.HighPart, pSessionData->LogonId.LowPart);
320
321 bool fFoundUser = false;
322 if ( pSessionData->UserName.Buffer != NULL
323 && pSessionData->Sid != NULL
324 && pSessionData->LogonId.LowPart != 0)
325 {
326 /*
327 * Copy out the data.
328 */
329 VBoxServiceVMInfoWinSafeCopy(a_pUserInfo->wszUser, sizeof(a_pUserInfo->wszUser),
330 &pSessionData->UserName, "User name");
331 VBoxServiceVMInfoWinSafeCopy(a_pUserInfo->wszAuthenticationPackage, sizeof(a_pUserInfo->wszAuthenticationPackage),
332 &pSessionData->AuthenticationPackage, "Authentication pkg name");
333 VBoxServiceVMInfoWinSafeCopy(a_pUserInfo->wszLogonDomain, sizeof(a_pUserInfo->wszLogonDomain),
334 &pSessionData->LogonDomain, "Logon domain name");
335
336
337 /*
338 * Only handle users which can login interactively or logged in
339 * remotely over native RDP.
340 */
341 /** @todo r=bird: Whey don't we check this before copying the data? */
342 if ( ( (SECURITY_LOGON_TYPE)pSessionData->LogonType == Interactive
343 || (SECURITY_LOGON_TYPE)pSessionData->LogonType == RemoteInteractive)
344 && pSessionData->Sid != NULL)
345 {
346 TCHAR szOwnerName[_MAX_PATH] = { 0 };
347 DWORD dwOwnerNameSize = sizeof(szOwnerName);
348 TCHAR szDomainName[_MAX_PATH] = { 0 };
349 DWORD dwDomainNameSize = sizeof(szDomainName);
350 SID_NAME_USE enmOwnerType = SidTypeInvalid;
351 if (LookupAccountSid(NULL,
352 pSessionData->Sid,
353 szOwnerName,
354 &dwOwnerNameSize,
355 szDomainName,
356 &dwDomainNameSize,
357 &enmOwnerType))
358 {
359 VBoxServiceVerbose(3, "Account User=%ls, Session=%ld, LUID=%ld,%ld, AuthPkg=%ls, Domain=%ls\n",
360 a_pUserInfo->wszUser, pSessionData->Session, pSessionData->LogonId.HighPart,
361 pSessionData->LogonId.LowPart, a_pUserInfo->wszAuthenticationPackage,
362 a_pUserInfo->wszLogonDomain);
363
364#if 1 /** @todo If we don't use this, drop it? */
365 /* The session ID increments/decrements on Vista often! So don't compare
366 the session data SID with the current SID here. */
367 DWORD dwActiveSession = 0;
368 if (g_pfnWTSGetActiveConsoleSessionId != NULL) /* Check terminal session ID. */
369 dwActiveSession = g_pfnWTSGetActiveConsoleSessionId();
370 /*VBoxServiceVerbose(3, ("Users: Current active session ID: %ld\n", dwActiveSession));*/
371#endif
372
373 if (enmOwnerType == SidTypeUser)
374 {
375 /* Detect RDP sessions as well. */
376 LPTSTR pBuffer = NULL;
377 DWORD cbRet = 0;
378 int iState = 0;
379 if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE,
380 WTS_CURRENT_SESSION,
381 WTSConnectState,
382 &pBuffer,
383 &cbRet))
384 {
385 /*VBoxServiceVerbose(3, ("Users: WTSQuerySessionInformation returned %ld bytes, p=%p, state=%d\n", cbRet, pBuffer, pBuffer != NULL ? (INT)*pBuffer : -1));*/
386 if(cbRet)
387 iState = *pBuffer;
388
389 if ( iState == WTSActive /* User logged on to WinStation. */
390 || iState == WTSShadow /* Shadowing another WinStation. */
391 || iState == WTSDisconnected) /* WinStation logged on without client. */
392 {
393 /** @todo On Vista and W2K, always "old" user name are still
394 * there. Filter out the old one! */
395 VBoxServiceVerbose(3, "Users: Account User=%ls is logged in via TCS/RDP. State=%d\n",
396 a_pUserInfo->wszUser, iState);
397 fFoundUser = true;
398 }
399
400 if (pBuffer)
401 WTSFreeMemory(pBuffer);
402 }
403 else
404 {
405 /*
406 * Terminal services don't run (for example in W2K,
407 * nothing to worry about ...). ... or is on the Vista
408 * fast user switching page!
409 */
410 fFoundUser = true;
411 }
412 }
413 }
414 }
415 }
416
417 LsaFreeReturnBuffer(pSessionData);
418 return fFoundUser;
419}
420
421#endif /* TARGET_NT4 */
422
423int VBoxServiceWinGetComponentVersions(uint32_t uClientID)
424{
425 int rc;
426 char szSysDir[_MAX_PATH] = {0};
427 char szWinDir[_MAX_PATH] = {0};
428 char szDriversDir[_MAX_PATH + 32] = {0};
429
430 /* ASSUME: szSysDir and szWinDir and derivatives are always ASCII compatible. */
431 GetSystemDirectory(szSysDir, _MAX_PATH);
432 GetWindowsDirectory(szWinDir, _MAX_PATH);
433 RTStrPrintf(szDriversDir, sizeof(szDriversDir), "%s\\drivers", szSysDir);
434#ifdef RT_ARCH_AMD64
435 char szSysWowDir[_MAX_PATH + 32] = {0};
436 RTStrPrintf(szSysWowDir, sizeof(szSysWowDir), "%s\\SysWow64", szWinDir);
437#endif
438
439 /* The file information table. */
440#ifndef TARGET_NT4
441 const VBOXSERVICEVMINFOFILE aVBoxFiles[] =
442 {
443 { szSysDir, "VBoxControl.exe" },
444 { szSysDir, "VBoxHook.dll" },
445 { szSysDir, "VBoxDisp.dll" },
446 { szSysDir, "VBoxMRXNP.dll" },
447 { szSysDir, "VBoxService.exe" },
448 { szSysDir, "VBoxTray.exe" },
449 { szSysDir, "VBoxGINA.dll" },
450 { szSysDir, "VBoxCredProv.dll" },
451
452 /* On 64-bit we don't yet have the OpenGL DLLs in native format.
453 So just enumerate the 32-bit files in the SYSWOW directory. */
454# ifdef RT_ARCH_AMD64
455 { szSysWowDir, "VBoxOGLarrayspu.dll" },
456 { szSysWowDir, "VBoxOGLcrutil.dll" },
457 { szSysWowDir, "VBoxOGLerrorspu.dll" },
458 { szSysWowDir, "VBoxOGLpackspu.dll" },
459 { szSysWowDir, "VBoxOGLpassthroughspu.dll" },
460 { szSysWowDir, "VBoxOGLfeedbackspu.dll" },
461 { szSysWowDir, "VBoxOGL.dll" },
462# else /* !RT_ARCH_AMD64 */
463 { szSysDir, "VBoxOGLarrayspu.dll" },
464 { szSysDir, "VBoxOGLcrutil.dll" },
465 { szSysDir, "VBoxOGLerrorspu.dll" },
466 { szSysDir, "VBoxOGLpackspu.dll" },
467 { szSysDir, "VBoxOGLpassthroughspu.dll" },
468 { szSysDir, "VBoxOGLfeedbackspu.dll" },
469 { szSysDir, "VBoxOGL.dll" },
470# endif /* !RT_ARCH_AMD64 */
471
472 { szDriversDir, "VBoxGuest.sys" },
473 { szDriversDir, "VBoxMouse.sys" },
474 { szDriversDir, "VBoxSF.sys" },
475 { szDriversDir, "VBoxVideo.sys" },
476 };
477
478#else /* TARGET_NT4 */
479 const VBOXSERVICEVMINFOFILE aVBoxFiles[] =
480 {
481 { szSysDir, "VBoxControl.exe" },
482 { szSysDir, "VBoxHook.dll" },
483 { szSysDir, "VBoxDisp.dll" },
484 { szSysDir, "VBoxService.exe" },
485 { szSysDir, "VBoxTray.exe" },
486
487 { szDriversDir, "VBoxGuestNT.sys" },
488 { szDriversDir, "VBoxMouseNT.sys" },
489 { szDriversDir, "VBoxVideo.sys" },
490 };
491#endif /* TARGET_NT4 */
492
493 for (unsigned i = 0; i < RT_ELEMENTS(aVBoxFiles); i++)
494 {
495 char szVer[128];
496 VBoxServiceGetFileVersionString(aVBoxFiles[i].pszFilePath, aVBoxFiles[i].pszFileName, szVer, sizeof(szVer));
497 char szPropPath[256];
498 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestAdd/Components/%s", aVBoxFiles[i].pszFileName);
499 rc = VBoxServiceWritePropF(uClientID, szPropPath, "%s", szVer);
500 }
501
502 return VINF_SUCCESS;
503}
504
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