VirtualBox

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

最後變更 在這個檔案從13127是 13127,由 vboxsync 提交於 16 年 前

Additions/Windows: guest property fix for LoggedInUsersList and new NoLoggedInUsers property which only gets updated when transitioning from/to no users. Important for timestamp.

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

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