VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxService/VBoxVMInfoUser.cpp@ 13483

Last change on this file since 13483 was 13462, checked in by vboxsync, 16 years ago

fix headers

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.4 KB
Line 
1/* $Id: VBoxVMInfoUser.cpp 13462 2008-10-22 06:46:45Z vboxsync $ */
2/** @file
3 * VBoxVMInfoUser - User information for the host.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22#include "VBoxService.h"
23#include "VBoxVMInfo.h"
24#include "VBoxVMInfoUser.h"
25
26#include <Ntsecapi.h>
27#include <wtsapi32.h> /* For WTS* calls. */
28#include <psapi.h> /* EnumProcesses. */
29
30/* Function GetLUIDsFromProcesses() written by Stefan Kuhr. */
31static DWORD GetLUIDsFromProcesses(PLUID *ppLuid)
32{
33 DWORD dwSize, dwSize2, dwIndex ;
34 LPDWORD lpdwPIDs ;
35 DWORD dwLastError = ERROR_SUCCESS;
36
37 if (!ppLuid)
38 {
39 SetLastError(ERROR_INVALID_PARAMETER);
40 return 0L;
41 }
42
43 // Call the PSAPI function EnumProcesses to get all of the
44 // ProcID's currently in the system.
45 // NOTE: In the documentation, the third parameter of
46 // EnumProcesses is named cbNeeded, which implies that you
47 // can call the function once to find out how much space to
48 // allocate for a buffer and again to fill the buffer.
49 // This is not the case. The cbNeeded parameter returns
50 // the number of PIDs returned, so if your buffer size is
51 // zero cbNeeded returns zero.
52 // NOTE: The "HeapAlloc" loop here ensures that we
53 // actually allocate a buffer large enough for all the
54 // PIDs in the system.
55 dwSize2 = 256 * sizeof( DWORD ) ;
56
57 lpdwPIDs = NULL ;
58 do
59 {
60 if( lpdwPIDs )
61 {
62 HeapFree( GetProcessHeap(), 0, lpdwPIDs ) ;
63 dwSize2 *= 2 ;
64 }
65 lpdwPIDs = (unsigned long *)HeapAlloc( GetProcessHeap(), 0, dwSize2 );
66 if( lpdwPIDs == NULL )
67 return 0L; // Last error will be that of HeapAlloc
68
69 if( !EnumProcesses( lpdwPIDs, dwSize2, &dwSize ) )
70 {
71 DWORD dw = GetLastError();
72 HeapFree( GetProcessHeap(), 0, lpdwPIDs ) ;
73 SetLastError(dw);
74 return 0L;
75 }
76 }
77 while( dwSize == dwSize2 ) ;
78
79 /* At this point we have an array of the PIDs at the
80 time of the last EnumProcesses invocation. We will
81 allocate an array of LUIDs passed back via the out
82 param ppLuid of exactly the number of PIDs. We will
83 only fill the first n values of this array, with n
84 being the number of unique LUIDs found in these PIDs. */
85
86 // How many ProcIDs did we get?
87 dwSize /= sizeof( DWORD ) ;
88 dwSize2 = 0L; /* Our return value of found luids. */
89
90 *ppLuid = (LUID *)LocalAlloc(LPTR, dwSize*sizeof(LUID));
91 if (!(*ppLuid))
92 {
93 dwLastError = GetLastError();
94 goto CLEANUP;
95 }
96 for( dwIndex = 0 ; dwIndex < dwSize ; dwIndex++ )
97 {
98 (*ppLuid)[dwIndex].LowPart =0L;
99 (*ppLuid)[dwIndex].HighPart=0;
100
101
102 // Open the process (if we can... security does not
103 // permit every process in the system).
104 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION,FALSE, lpdwPIDs[ dwIndex ] ) ;
105 if( hProcess != NULL )
106 {
107 HANDLE hAccessToken;
108 if (OpenProcessToken( hProcess, TOKEN_QUERY, &hAccessToken))
109 {
110 TOKEN_STATISTICS ts;
111 DWORD dwSize;
112 if (GetTokenInformation(hAccessToken, TokenStatistics, &ts, sizeof ts, &dwSize))
113 {
114 DWORD dwTmp = 0L;
115 BOOL bFound = FALSE;
116 for (;dwTmp<dwSize2 && !bFound;dwTmp++)
117 bFound = (*ppLuid)[dwTmp].HighPart == ts.AuthenticationId.HighPart &&
118 (*ppLuid)[dwTmp].LowPart == ts.AuthenticationId.LowPart;
119
120 if (!bFound)
121 (*ppLuid)[dwSize2++] = ts.AuthenticationId;
122 }
123 CloseHandle(hAccessToken) ;
124 }
125
126 CloseHandle( hProcess ) ;
127 }
128
129 /// we don't really care if OpenProcess or OpenProcessToken fail or succeed, because
130 /// there are quite a number of system processes we cannot open anyway, not even as SYSTEM.
131 }
132
133CLEANUP:
134
135 if (lpdwPIDs)
136 HeapFree( GetProcessHeap(), 0, lpdwPIDs ) ;
137
138 if (ERROR_SUCCESS !=dwLastError)
139 SetLastError(dwLastError);
140
141 return dwSize2;
142}
143
144BOOL isLoggedIn(VBOXINFORMATIONCONTEXT* a_pCtx,
145 VBOXUSERINFO* a_pUserInfo,
146 PLUID a_pSession,
147 PLUID a_pLuid,
148 DWORD a_dwNumOfProcLUIDs)
149{
150 BOOL bLoggedIn = FALSE;
151 BOOL bFoundUser = FALSE;
152 PSECURITY_LOGON_SESSION_DATA sessionData = NULL;
153 NTSTATUS ret = 0;
154 WCHAR szAuthPkg[256] = { 0 };
155 WCHAR szLogonDomain[256] = { 0 };
156 WCHAR *usBuffer = NULL;
157 int usLength = 0;
158
159 if (!a_pSession)
160 return FALSE;
161
162 ret = LsaGetLogonSessionData (a_pSession, &sessionData);
163 if (ret != STATUS_SUCCESS)
164 {
165 Log(("vboxVMInfoThread: LsaGetLogonSessionData failed %lu\n", LsaNtStatusToWinError(ret)));
166
167 if (sessionData)
168 LsaFreeReturnBuffer(sessionData);
169
170 return FALSE;
171 }
172
173 if (!sessionData)
174 {
175 Log(("vboxVMInfoThread: Invalid logon session data.\n"));
176 return FALSE;
177 }
178
179 if ((sessionData->UserName.Buffer != NULL) &&
180 (sessionData->Sid != NULL) &&
181 (sessionData->LogonId.LowPart != 0))
182 {
183 /* Get the user name. */
184 usBuffer = (sessionData->UserName).Buffer;
185 usLength = (sessionData->UserName).Length;
186 if (usLength > 256)
187 {
188 Log(("vboxVMInfoThread: User name too long for buffer! Length: %d, Buffer: 256\n", usLength));
189 }
190 else
191 {
192 /** @todo r=bird: Check this code for buffer overruns. the if check above is wrong as it's making assumptions about _MAX_PATH (which is 260 not 256 as stated). */
193 wcsncpy (a_pUserInfo->szUser, usBuffer, usLength);
194 wcscat (a_pUserInfo->szUser, L"");
195
196 usBuffer = (sessionData->AuthenticationPackage).Buffer;
197 usLength = (sessionData->AuthenticationPackage).Length;
198 wcsncpy (szAuthPkg, usBuffer, usLength);
199 wcscat (szAuthPkg, L"");
200
201 usBuffer = (sessionData->LogonDomain).Buffer;
202 usLength = (sessionData->LogonDomain).Length;
203 wcsncpy (szLogonDomain, usBuffer, usLength);
204 wcscat (szLogonDomain, L""); /** @todo r=bird: There is a potential buffer overrun here. */
205
206 /* Only handle users which can login interactively. */
207 if ( ((SECURITY_LOGON_TYPE)sessionData->LogonType == Interactive)
208 && (sessionData->Sid != NULL))
209 {
210 TCHAR szOwnerName [_MAX_PATH] = { 0 };
211 DWORD dwOwnerNameSize = _MAX_PATH;
212
213 TCHAR szDomainName [_MAX_PATH] = { 0 };
214 DWORD dwDomainNameSize = _MAX_PATH;
215
216 SID_NAME_USE ownerType;
217
218 if (LookupAccountSid(NULL,
219 sessionData->Sid,
220 szOwnerName,
221 &dwOwnerNameSize,
222 szDomainName,
223 &dwDomainNameSize,
224 &ownerType))
225 {
226 Log(("vboxVMInfoThread: Account User=%ls, Session=%ld, LUID=%ld,%ld, AuthPkg=%ls, Domain=%ls\n",
227 a_pUserInfo->szUser, sessionData->Session, sessionData->LogonId.HighPart, sessionData->LogonId.LowPart, szAuthPkg, szLogonDomain));
228
229 /* The session ID increments/decrements on Vista often! So don't compare
230 the session data SID with the current SID here. */
231 DWORD dwActiveSession = 0;
232 if (a_pCtx->pfnWTSGetActiveConsoleSessionId != NULL) /* Check terminal session ID. */
233 dwActiveSession = a_pCtx->pfnWTSGetActiveConsoleSessionId();
234
235 /*Log(("vboxVMInfoThread: Current active session ID: %ld\n", dwActiveSession));*/
236
237 if (SidTypeUser == ownerType)
238 {
239 LPWSTR pBuffer = NULL;
240 DWORD dwBytesRet = 0;
241 int iState = 0;
242
243 if (WTSQuerySessionInformation( /* Detect RDP sessions as well. */
244 WTS_CURRENT_SERVER_HANDLE,
245 WTS_CURRENT_SESSION,
246 WTSConnectState,
247 &pBuffer,
248 &dwBytesRet))
249 {
250 /*Log(("vboxVMInfoThread: WTSQuerySessionInformation returned %ld bytes, p=%p, state=%d\n", dwBytesRet, pBuffer, pBuffer != NULL ? (INT)*pBuffer : -1));*/
251 if(dwBytesRet)
252 iState = *pBuffer;
253
254 if ( (iState == WTSActive) /* User logged on to WinStation. */
255 || (iState == WTSShadow) /* Shadowing another WinStation. */
256 || (iState == WTSDisconnected)) /* WinStation logged on without client. */
257 {
258 /** @todo On Vista and W2K, always "old" user name are still there. Filter out the old! */
259 Log(("vboxVMInfoThread: Account User=%ls is logged in via TCS/RDP. State=%d\n", a_pUserInfo->szUser, iState));
260 bFoundUser = TRUE;
261 }
262 }
263 else
264 {
265 /* Terminal services don't run (for example in W2K, nothing to worry about ...). */
266 /* ... or is on Vista fast user switching page! */
267 bFoundUser = TRUE;
268 }
269
270 if (pBuffer)
271 WTSFreeMemory(pBuffer);
272
273 /* A user logged in, but it could be a stale/orphaned logon session. */
274 BOOL bFoundInLUIDs = FALSE;
275 for (DWORD dwIndex = 0; dwIndex < a_dwNumOfProcLUIDs; dwIndex++)
276 {
277 if ( (a_pLuid[dwIndex].HighPart == sessionData->LogonId.HighPart)
278 && (a_pLuid[dwIndex].LowPart == sessionData->LogonId.LowPart))
279 {
280 bLoggedIn = TRUE;
281 Log(("vboxVMInfoThread: User \"%ls\" is logged in!\n", a_pUserInfo->szUser));
282 break;
283 }
284 }
285 }
286 }
287 }
288 }
289 }
290
291 LsaFreeReturnBuffer(sessionData);
292 return bLoggedIn;
293}
294
295int vboxVMInfoUser(VBOXINFORMATIONCONTEXT* a_pCtx)
296{
297 PLUID pSessions = NULL;
298 ULONG ulCount = 0;
299 NTSTATUS ret = 0;
300
301 int iUserCount = 0;
302 char szUserList[4096] = {0};
303 char* pszTemp = NULL;
304
305 /* This function can report stale or orphaned interactive logon sessions of already logged
306 off users (especially in Windows 2000). */
307 ret = LsaEnumerateLogonSessions(&ulCount, &pSessions);
308 Log(("vboxVMInfoThread: Found %d users.\n", ulCount));
309
310 if (ret != STATUS_SUCCESS)
311 {
312 Log(("vboxVMInfoThread: LsaEnumerate failed %lu\n", LsaNtStatusToWinError(ret)));
313 return 1;
314 }
315
316 PLUID pLuid = NULL;
317 DWORD dwNumOfProcLUIDs = GetLUIDsFromProcesses(&pLuid);
318
319 VBOXUSERINFO userInfo;
320 ZeroMemory (&userInfo, sizeof(VBOXUSERINFO));
321
322 for (int i = 0; i<(int)ulCount; i++)
323 {
324 if (isLoggedIn(a_pCtx, &userInfo, &pSessions[i], pLuid, dwNumOfProcLUIDs))
325 {
326 if (iUserCount > 0)
327 strcat (szUserList, ",");
328
329 iUserCount++;
330
331 RTUtf16ToUtf8(userInfo.szUser, &pszTemp);
332 strcat(szUserList, pszTemp);
333 RTMemFree(pszTemp);
334 }
335 }
336
337 if (NULL != pLuid)
338 LocalFree (pLuid);
339
340 LsaFreeReturnBuffer(pSessions);
341
342 /* Write information to host. */
343 vboxVMInfoWriteProp(a_pCtx, "GuestInfo/OS/LoggedInUsersList", (iUserCount > 0) ? szUserList : NULL);
344 vboxVMInfoWritePropInt(a_pCtx, "GuestInfo/OS/LoggedInUsers", iUserCount);
345 if (a_pCtx->cUsers != iUserCount || a_pCtx->cUsers == INT32_MAX)
346 {
347 /* Update this property ONLY if there is a real change from no users to
348 * users or vice versa. The only exception is that the initialization
349 * of a_pCtx->cUsers forces an update, but only once. This ensures
350 * consistent property settings even if the VM aborted previously. */
351 if (iUserCount == 0)
352 vboxVMInfoWriteProp(a_pCtx, "GuestInfo/OS/NoLoggedInUsers", "true");
353 else if (a_pCtx->cUsers == 0 || a_pCtx->cUsers == INT32_MAX)
354 vboxVMInfoWriteProp(a_pCtx, "GuestInfo/OS/NoLoggedInUsers", "false");
355 }
356 a_pCtx->cUsers = iUserCount;
357
358 return ret;
359}
360
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