VirtualBox

source: vbox/trunk/src/VBox/Main/src-global/VirtualBoxSDSImpl.cpp@ 75267

Last change on this file since 75267 was 71159, checked in by vboxsync, 7 years ago

Main/VBoxSVC,VBoxSDS: fix for ​​​​​​​​​bugref:8161: added svn:sync-process export to RpcChannelHook.h and VirtualBoxClientListImpl.h. Copyright changed to open source.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.1 KB
Line 
1/* $Id: VirtualBoxSDSImpl.cpp 71159 2018-02-28 16:06:10Z vboxsync $ */
2/** @file
3 * VBox Global COM Class implementation.
4 */
5
6/*
7 * Copyright (C) 2015-2018 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 <VBox/com/VirtualBox.h>
23#include "VirtualBoxSDSImpl.h"
24
25#include "AutoCaller.h"
26#include "Logging.h"
27
28#include <VBox/err.h>
29#include <iprt/asm.h>
30#include <iprt/critsect.h>
31
32#include <rpcasync.h>
33#include <rpcdcep.h>
34#include <sddl.h>
35#include <lmcons.h> /* UNLEN */
36
37
38/**
39 * Per user data.
40 *
41 * @note We never delete instances of this class, except in case of an insertion
42 * race. This allows us to separate the map lock from the user data lock
43 * and avoid DoS issues.
44 */
45class VBoxSDSPerUserData
46{
47public:
48 /** The SID (secure identifier) for the user. This is the key. */
49 com::Utf8Str m_strUserSid;
50 /** The user name (if we could get it). */
51 com::Utf8Str m_strUsername;
52 /** The VBoxSVC chosen to instantiate CLSID_VirtualBox.
53 * This is NULL if not set. */
54 ComPtr<IVBoxSVCRegistration> m_ptrTheChosenOne;
55 ComPtr<IVirtualBoxClientList> m_ptrClientList;
56private:
57 /** Reference count to make destruction safe wrt hung callers.
58 * (References are retain while holding the map lock in some form, but
59 * released while holding no locks.) */
60 uint32_t volatile m_cRefs;
61 /** Critical section protecting everything here. */
62 RTCRITSECT m_Lock;
63
64public:
65 VBoxSDSPerUserData(com::Utf8Str const &a_rStrUserSid, com::Utf8Str const &a_rStrUsername)
66 : m_strUserSid(a_rStrUserSid), m_strUsername(a_rStrUsername), m_cRefs(1)
67 {
68 RTCritSectInit(&m_Lock);
69 }
70
71 ~VBoxSDSPerUserData()
72 {
73 RTCritSectDelete(&m_Lock);
74 }
75
76 uint32_t i_retain()
77 {
78 uint32_t cRefs = ASMAtomicIncU32(&m_cRefs);
79 Assert(cRefs > 1);
80 return cRefs;
81 }
82
83 uint32_t i_release()
84 {
85 uint32_t cRefs = ASMAtomicDecU32(&m_cRefs);
86 Assert(cRefs < _1K);
87 if (cRefs == 0)
88 delete this;
89 return cRefs;
90 }
91
92 void i_lock()
93 {
94 RTCritSectEnter(&m_Lock);
95 }
96
97 void i_unlock()
98 {
99 RTCritSectLeave(&m_Lock);
100 }
101};
102
103
104
105
106// constructor / destructor
107/////////////////////////////////////////////////////////////////////////////
108
109DEFINE_EMPTY_CTOR_DTOR(VirtualBoxSDS)
110
111HRESULT VirtualBoxSDS::FinalConstruct()
112{
113 LogRelFlowThisFuncEnter();
114
115 int vrc = RTCritSectRwInit(&m_MapCritSect);
116 AssertLogRelRCReturn(vrc, E_FAIL);
117
118 LogRelFlowThisFuncLeave();
119 return S_OK;
120}
121
122
123void VirtualBoxSDS::FinalRelease()
124{
125 LogRelFlowThisFuncEnter();
126
127 RTCritSectRwDelete(&m_MapCritSect);
128
129 for (UserDataMap_T::iterator it = m_UserDataMap.begin(); it != m_UserDataMap.end(); ++it)
130 {
131 VBoxSDSPerUserData *pUserData = it->second;
132 if (pUserData)
133 {
134 it->second = NULL;
135 pUserData->i_release();
136 }
137 }
138
139 LogRelFlowThisFuncLeave();
140}
141
142
143
144// IVirtualBoxSDS methods
145/////////////////////////////////////////////////////////////////////////////
146
147
148/* SDS plan B interfaces: */
149STDMETHODIMP_(HRESULT) VirtualBoxSDS::RegisterVBoxSVC(IVBoxSVCRegistration *aVBoxSVC, LONG aPid, IUnknown **aExistingVirtualBox)
150{
151 LogRel(("VirtualBoxSDS::registerVBoxSVC: aVBoxSVC=%p aPid=%u\n", (IVBoxSVCRegistration *)aVBoxSVC, aPid));
152 HRESULT hrc;
153 if ( RT_VALID_PTR(aVBoxSVC)
154 && RT_VALID_PTR(aExistingVirtualBox))
155 {
156 *aExistingVirtualBox = NULL;
157
158 /* Get the client user SID and name. */
159 com::Utf8Str strSid;
160 com::Utf8Str strUsername;
161 if (i_getClientUserSid(&strSid, &strUsername))
162 {
163 VBoxSDSPerUserData *pUserData = i_lookupOrCreatePerUserData(strSid, strUsername);
164 if (pUserData)
165 {
166 if (pUserData->m_ptrTheChosenOne.isNotNull())
167 {
168 try
169 {
170 hrc = pUserData->m_ptrTheChosenOne->GetVirtualBox(aExistingVirtualBox);
171 }
172 catch (...)
173 {
174 LogRel(("VirtualBoxSDS::registerVBoxSVC: unexpected exception calling GetVirtualBox.\n"));
175 hrc = E_FAIL;
176 }
177 if (FAILED_DEAD_INTERFACE(hrc))
178 {
179 LogRel(("VirtualBoxSDS::registerVBoxSVC: Seems VBoxSVC instance died. Dropping it and letting caller take over.\n"));
180 pUserData->m_ptrTheChosenOne.setNull();
181 /* Release the client list and stop client list watcher thread*/
182 pUserData->m_ptrClientList.setNull();
183 }
184 }
185 else
186 hrc = S_OK;
187
188 if (pUserData->m_ptrTheChosenOne.isNull())
189 {
190 LogRel(("VirtualBoxSDS::registerVBoxSVC: Making aPid=%u the chosen one for user %s (%s)!\n",
191 aPid, pUserData->m_strUserSid.c_str(), pUserData->m_strUsername.c_str()));
192 try
193 {
194 pUserData->m_ptrTheChosenOne = aVBoxSVC;
195 /*
196 * Create instance of ClientList
197 */
198 HRESULT hrc = CoCreateInstance(CLSID_VirtualBoxClientList, NULL, CLSCTX_LOCAL_SERVER,
199 IID_IVirtualBoxClientList,
200 (void **)pUserData->m_ptrClientList.asOutParam());
201 if (SUCCEEDED(hrc))
202 {
203 LogFunc(("Created API client list instance in VBoxSDS : hr=%Rhrf\n", hrc));
204 }
205 else
206 {
207 LogFunc(("Error in creating API client list instance: hr=%Rhrf\n", hrc));
208 }
209
210 hrc = S_OK;
211 }
212 catch (...)
213 {
214 LogRel(("VirtualBoxSDS::registerVBoxSVC: unexpected exception setting the chosen one.\n"));
215 hrc = E_FAIL;
216 }
217 }
218
219 pUserData->i_unlock();
220 pUserData->i_release();
221 }
222 else
223 hrc = E_OUTOFMEMORY;
224 }
225 else
226 hrc = E_FAIL;
227 }
228 else
229 hrc = E_INVALIDARG;
230 LogRel2(("VirtualBoxSDS::registerVBoxSVC: returns %Rhrc aExistingVirtualBox=%p\n", hrc, (IUnknown *)aExistingVirtualBox));
231 return hrc;
232}
233
234STDMETHODIMP_(HRESULT) VirtualBoxSDS::DeregisterVBoxSVC(IVBoxSVCRegistration *aVBoxSVC, LONG aPid)
235{
236 LogRel(("VirtualBoxSDS::deregisterVBoxSVC: aVBoxSVC=%p aPid=%u\n", (IVBoxSVCRegistration *)aVBoxSVC, aPid));
237 HRESULT hrc;
238 if (RT_VALID_PTR(aVBoxSVC))
239 {
240 /* Get the client user SID and name. */
241 com::Utf8Str strSid;
242 com::Utf8Str strUsername;
243 if (i_getClientUserSid(&strSid, &strUsername))
244 {
245 VBoxSDSPerUserData *pUserData = i_lookupPerUserData(strSid);
246 if (pUserData)
247 {
248 if ( (IVBoxSVCRegistration *)aVBoxSVC
249 == (IVBoxSVCRegistration *)pUserData->m_ptrTheChosenOne)
250 {
251 LogRel(("VirtualBoxSDS::deregisterVBoxSVC: It's the chosen one for %s (%s)!\n",
252 pUserData->m_strUserSid.c_str(), pUserData->m_strUsername.c_str()));
253 pUserData->m_ptrTheChosenOne.setNull();
254 /** @todo consider evicting the user from the table... */
255 /* Release the client list and stop client list watcher thread*/
256 pUserData->m_ptrClientList.setNull();
257 }
258 else
259 LogRel(("VirtualBoxSDS::deregisterVBoxSVC: not the choosen one (%p != %p)\n",
260 (IVBoxSVCRegistration *)aVBoxSVC, (IVBoxSVCRegistration *)pUserData->m_ptrTheChosenOne));
261 pUserData->i_unlock();
262 pUserData->i_release();
263
264 hrc = S_OK;
265 }
266 else
267 {
268 LogRel(("VirtualBoxSDS::deregisterVBoxSVC: Found no user data for %s (%s) (pid %u)\n",
269 strSid.c_str(), strUsername.c_str(), aPid));
270 hrc = S_OK;
271 }
272 }
273 else
274 hrc = E_FAIL;
275 }
276 else
277 hrc = E_INVALIDARG;
278 LogRel2(("VirtualBoxSDS::deregisterVBoxSVC: returns %Rhrc\n", hrc));
279 return hrc;
280}
281
282
283STDMETHODIMP_(HRESULT) VirtualBoxSDS::NotifyClientsFinished()
284{
285 LogRelFlowThisFuncEnter();
286
287 int vrc = RTCritSectRwEnterShared(&m_MapCritSect);
288 if (RT_SUCCESS(vrc))
289 {
290 for (UserDataMap_T::iterator it = m_UserDataMap.begin(); it != m_UserDataMap.end(); ++it)
291 {
292 VBoxSDSPerUserData *pUserData = it->second;
293 if (pUserData && pUserData->m_ptrTheChosenOne)
294 {
295 LogRelFunc(("Notify VBoxSVC that all clients finished\n"));
296 /* Notify VBoxSVC about finishing all API clients it should free references to VBoxSDS
297 and clean up itself */
298 if (pUserData->m_ptrClientList)
299 pUserData->m_ptrClientList.setNull();
300 pUserData->m_ptrTheChosenOne->NotifyClientsFinished();
301 }
302 }
303 RTCritSectRwLeaveShared(&m_MapCritSect);
304 }
305
306 LogRelFlowThisFuncLeave();
307 return S_OK;
308}
309
310// private methods
311///////////////////////////////////////////////////////////////////////////////
312
313/*static*/ bool VirtualBoxSDS::i_getClientUserSid(com::Utf8Str *a_pStrSid, com::Utf8Str *a_pStrUsername)
314{
315 bool fRet = false;
316 a_pStrSid->setNull();
317 a_pStrUsername->setNull();
318
319 CoInitializeEx(NULL, COINIT_MULTITHREADED); // is this necessary?
320 HRESULT hrc = CoImpersonateClient();
321 if (SUCCEEDED(hrc))
322 {
323 HANDLE hToken = INVALID_HANDLE_VALUE;
324 if (::OpenThreadToken(GetCurrentThread(), TOKEN_READ, TRUE /*OpenAsSelf*/, &hToken))
325 {
326 CoRevertToSelf();
327
328 union
329 {
330 TOKEN_USER TokenUser;
331 uint8_t abPadding[SECURITY_MAX_SID_SIZE + 256];
332 WCHAR wszUsername[UNLEN + 1];
333 } uBuf;
334 RT_ZERO(uBuf);
335 DWORD cbActual = 0;
336 if (::GetTokenInformation(hToken, TokenUser, &uBuf, sizeof(uBuf), &cbActual))
337 {
338 WCHAR *pwszString;
339 if (ConvertSidToStringSidW(uBuf.TokenUser.User.Sid, &pwszString))
340 {
341 try
342 {
343 *a_pStrSid = pwszString;
344 a_pStrSid->toUpper(); /* (just to be on the safe side) */
345 fRet = true;
346 }
347 catch (std::bad_alloc)
348 {
349 LogRel(("VirtualBoxSDS::i_GetClientUserSID: std::bad_alloc setting rstrSid.\n"));
350 }
351 LocalFree((HLOCAL)pwszString);
352
353 /*
354 * Get the username too. We don't care if this step fails.
355 */
356 if (fRet)
357 {
358 WCHAR wszUsername[UNLEN * 2 + 1];
359 DWORD cwcUsername = RT_ELEMENTS(wszUsername);
360 WCHAR wszDomain[UNLEN * 2 + 1];
361 DWORD cwcDomain = RT_ELEMENTS(wszDomain);
362 SID_NAME_USE enmNameUse;
363 if (LookupAccountSidW(NULL, uBuf.TokenUser.User.Sid, wszUsername, &cwcUsername,
364 wszDomain, &cwcDomain, &enmNameUse))
365 {
366 wszUsername[RT_ELEMENTS(wszUsername) - 1] = '\0';
367 wszDomain[RT_ELEMENTS(wszDomain) - 1] = '\0';
368 try
369 {
370 *a_pStrUsername = wszDomain;
371 a_pStrUsername->append('/');
372 a_pStrUsername->append(Utf8Str(wszUsername));
373 }
374 catch (std::bad_alloc)
375 {
376 LogRel(("VirtualBoxSDS::i_GetClientUserSID: std::bad_alloc setting rStrUsername.\n"));
377 a_pStrUsername->setNull();
378 }
379 }
380 else
381 LogRel(("VirtualBoxSDS::i_GetClientUserSID: LookupAccountSidW failed: %u/%x (cwcUsername=%u, cwcDomain=%u)\n",
382 GetLastError(), cwcUsername, cwcDomain));
383 }
384 }
385 else
386 LogRel(("VirtualBoxSDS::i_GetClientUserSID: ConvertSidToStringSidW failed: %u\n", GetLastError()));
387 }
388 else
389 LogRel(("VirtualBoxSDS::i_GetClientUserSID: GetTokenInformation/TokenUser failed: %u\n", GetLastError()));
390 CloseHandle(hToken);
391 }
392 else
393 {
394 CoRevertToSelf();
395 LogRel(("VirtualBoxSDS::i_GetClientUserSID: OpenThreadToken failed: %u\n", GetLastError()));
396 }
397 }
398 else
399 LogRel(("VirtualBoxSDS::i_GetClientUserSID: CoImpersonateClient failed: %Rhrc\n", hrc));
400 CoUninitialize();
401 return fRet;
402}
403
404
405/**
406 * Looks up the given user.
407 *
408 * @returns Pointer to the LOCKED and RETAINED per user data.
409 * NULL if not found.
410 * @param a_rStrUserSid The user SID.
411 */
412VBoxSDSPerUserData *VirtualBoxSDS::i_lookupPerUserData(com::Utf8Str const &a_rStrUserSid)
413{
414 int vrc = RTCritSectRwEnterShared(&m_MapCritSect);
415 if (RT_SUCCESS(vrc))
416 {
417
418 UserDataMap_T::iterator it = m_UserDataMap.find(a_rStrUserSid);
419 if (it != m_UserDataMap.end())
420 {
421 VBoxSDSPerUserData *pUserData = it->second;
422 pUserData->i_retain();
423
424 RTCritSectRwLeaveShared(&m_MapCritSect);
425
426 pUserData->i_lock();
427 return pUserData;
428 }
429
430 RTCritSectRwLeaveShared(&m_MapCritSect);
431 }
432 return NULL;
433}
434
435
436/**
437 * Looks up the given user, creating it if not found
438 *
439 * @returns Pointer to the LOCKED and RETAINED per user data.
440 * NULL on allocation error.
441 * @param a_rStrUserSid The user SID.
442 * @param a_rStrUsername The user name if available.
443 */
444VBoxSDSPerUserData *VirtualBoxSDS::i_lookupOrCreatePerUserData(com::Utf8Str const &a_rStrUserSid,
445 com::Utf8Str const &a_rStrUsername)
446{
447 /*
448 * Try do a simple lookup first.
449 */
450 VBoxSDSPerUserData *pUserData = i_lookupPerUserData(a_rStrUserSid);
451 if (!pUserData)
452 {
453 /*
454 * SID is not in map, create a new one.
455 */
456 try
457 {
458 pUserData = new VBoxSDSPerUserData(a_rStrUserSid, a_rStrUsername);
459 }
460 catch (std::bad_alloc)
461 {
462 pUserData = NULL;
463 }
464 if (pUserData)
465 {
466 /*
467 * Insert it. We must check if someone raced us here.
468 */
469 VBoxSDSPerUserData *pUserDataFree = pUserData;
470 pUserData->i_lock();
471
472 int vrc = RTCritSectRwEnterExcl(&m_MapCritSect);
473 if (RT_SUCCESS(vrc))
474 {
475
476 UserDataMap_T::iterator it = m_UserDataMap.find(a_rStrUserSid);
477 if (it == m_UserDataMap.end())
478 {
479 try
480 {
481 m_UserDataMap[a_rStrUserSid] = pUserData;
482 pUserData->i_retain();
483 }
484 catch (std::bad_alloc)
485 {
486 pUserData = NULL;
487 }
488 }
489 else
490 pUserData = NULL;
491
492 RTCritSectRwLeaveExcl(&m_MapCritSect);
493
494 if (pUserData)
495 LogRel(("VirtualBoxSDS::i_lookupOrCreatePerUserData: Created new entry for %s (%s)\n",
496 pUserData->m_strUserSid.c_str(), pUserData->m_strUsername.c_str() ));
497 else
498 {
499 pUserDataFree->i_unlock();
500 delete pUserDataFree;
501 }
502 }
503 }
504 }
505
506 return pUserData;
507}
508
509/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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