VirtualBox

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

Last change on this file since 76071 was 76071, checked in by vboxsync, 6 years ago

VBoxSDS,VBoxSVC: Some DEBUG_bird experiments for identifying the caller. bugref:3300

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.9 KB
Line 
1/* $Id: VirtualBoxSDSImpl.cpp 76071 2018-12-08 19:11:20Z 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;
55private:
56 /** Reference count to make destruction safe wrt hung callers.
57 * (References are retain while holding the map lock in some form, but
58 * released while holding no locks.) */
59 uint32_t volatile m_cRefs;
60 /** Critical section protecting everything here. */
61 RTCRITSECT m_Lock;
62
63public:
64 VBoxSDSPerUserData(com::Utf8Str const &a_rStrUserSid, com::Utf8Str const &a_rStrUsername)
65 : m_strUserSid(a_rStrUserSid), m_strUsername(a_rStrUsername), m_cRefs(1)
66 {
67 RTCritSectInit(&m_Lock);
68 }
69
70 ~VBoxSDSPerUserData()
71 {
72 RTCritSectDelete(&m_Lock);
73 }
74
75 uint32_t i_retain()
76 {
77 uint32_t cRefs = ASMAtomicIncU32(&m_cRefs);
78 Assert(cRefs > 1);
79 return cRefs;
80 }
81
82 uint32_t i_release()
83 {
84 uint32_t cRefs = ASMAtomicDecU32(&m_cRefs);
85 Assert(cRefs < _1K);
86 if (cRefs == 0)
87 delete this;
88 return cRefs;
89 }
90
91 void i_lock()
92 {
93 RTCritSectEnter(&m_Lock);
94 }
95
96 void i_unlock()
97 {
98 RTCritSectLeave(&m_Lock);
99 }
100};
101
102
103
104
105// constructor / destructor
106/////////////////////////////////////////////////////////////////////////////
107
108DEFINE_EMPTY_CTOR_DTOR(VirtualBoxSDS)
109
110HRESULT VirtualBoxSDS::FinalConstruct()
111{
112 LogRelFlowThisFuncEnter();
113
114 int vrc = RTCritSectRwInit(&m_MapCritSect);
115 AssertLogRelRCReturn(vrc, E_FAIL);
116
117 LogRelFlowThisFuncLeave();
118 return S_OK;
119}
120
121
122void VirtualBoxSDS::FinalRelease()
123{
124 LogRelFlowThisFuncEnter();
125
126 RTCritSectRwDelete(&m_MapCritSect);
127
128 for (UserDataMap_T::iterator it = m_UserDataMap.begin(); it != m_UserDataMap.end(); ++it)
129 {
130 VBoxSDSPerUserData *pUserData = it->second;
131 if (pUserData)
132 {
133 it->second = NULL;
134 pUserData->i_release();
135 }
136 }
137
138 LogRelFlowThisFuncLeave();
139}
140
141
142
143// IVirtualBoxSDS methods
144/////////////////////////////////////////////////////////////////////////////
145
146
147/* SDS plan B interfaces: */
148STDMETHODIMP VirtualBoxSDS::RegisterVBoxSVC(IVBoxSVCRegistration *aVBoxSVC, LONG aPid, IUnknown **aExistingVirtualBox)
149{
150 LogRel(("VirtualBoxSDS::registerVBoxSVC: aVBoxSVC=%p aPid=%u (%#x)\n", (IVBoxSVCRegistration *)aVBoxSVC, aPid, aPid));
151#ifdef DEBUG_bird
152 RPC_CALL_ATTRIBUTES_V2_W CallAttribs = { RPC_CALL_ATTRIBUTES_VERSION, RPC_QUERY_CLIENT_PID | RPC_QUERY_IS_CLIENT_LOCAL};
153 RPC_STATUS rcRpc = RpcServerInqCallAttributesW(NULL, &CallAttribs);
154 LogRel(("RpcServerInqCallAttributesW -> %#x ClientPID=%#x IsClientLocal=%d ProtocolSequence=%#x CallStatus=%#x CallType=%#x OpNum=%#x InterfaceUuid=%RTuuid\n",
155 rcRpc, CallAttribs.ClientPID, CallAttribs.IsClientLocal, CallAttribs.ProtocolSequence, CallAttribs.CallStatus,
156 CallAttribs.CallType, CallAttribs.OpNum, &CallAttribs.InterfaceUuid));
157#endif
158
159 HRESULT hrc;
160 if ( RT_VALID_PTR(aVBoxSVC)
161 && RT_VALID_PTR(aExistingVirtualBox))
162 {
163 *aExistingVirtualBox = NULL;
164
165 /* Get the client user SID and name. */
166 com::Utf8Str strSid;
167 com::Utf8Str strUsername;
168 if (i_getClientUserSid(&strSid, &strUsername))
169 {
170 VBoxSDSPerUserData *pUserData = i_lookupOrCreatePerUserData(strSid, strUsername);
171 if (pUserData)
172 {
173 /*
174 * If there already is a chosen one, ask it for a IVirtualBox instance
175 * to return to the caller. Should it be dead or unresponsive, the caller
176 * takes its place.
177 */
178 if (pUserData->m_ptrTheChosenOne.isNotNull())
179 {
180 try
181 {
182 hrc = pUserData->m_ptrTheChosenOne->GetVirtualBox(aExistingVirtualBox);
183 }
184 catch (...)
185 {
186 LogRel(("VirtualBoxSDS::registerVBoxSVC: unexpected exception calling GetVirtualBox.\n"));
187 hrc = E_FAIL;
188 }
189 if (FAILED_DEAD_INTERFACE(hrc))
190 {
191 LogRel(("VirtualBoxSDS::registerVBoxSVC: Seems VBoxSVC instance died. Dropping it and letting caller take over. (hrc=%Rhrc)\n", hrc));
192 pUserData->m_ptrTheChosenOne.setNull();
193 }
194 }
195 else
196 hrc = S_OK;
197
198 /*
199 * Is the caller the chosen one?
200 */
201 if (pUserData->m_ptrTheChosenOne.isNull())
202 {
203 LogRel(("VirtualBoxSDS::registerVBoxSVC: Making aPid=%u (%#x) the chosen one for user %s (%s)!\n",
204 aPid, aPid, pUserData->m_strUserSid.c_str(), pUserData->m_strUsername.c_str()));
205 pUserData->m_ptrTheChosenOne = aVBoxSVC;
206 }
207
208 pUserData->i_unlock();
209 pUserData->i_release();
210 }
211 else
212 hrc = E_OUTOFMEMORY;
213 }
214 else
215 hrc = E_FAIL;
216 }
217 else
218 hrc = E_INVALIDARG;
219 LogRel2(("VirtualBoxSDS::registerVBoxSVC: returns %Rhrc aExistingVirtualBox=%p\n", hrc, (IUnknown *)aExistingVirtualBox));
220 return hrc;
221}
222
223
224STDMETHODIMP VirtualBoxSDS::DeregisterVBoxSVC(IVBoxSVCRegistration *aVBoxSVC, LONG aPid)
225{
226 LogRel(("VirtualBoxSDS::deregisterVBoxSVC: aVBoxSVC=%p aPid=%u (%#x)\n", (IVBoxSVCRegistration *)aVBoxSVC, aPid, aPid));
227 HRESULT hrc;
228 if (RT_VALID_PTR(aVBoxSVC))
229 {
230 /* Get the client user SID and name. */
231 com::Utf8Str strSid;
232 com::Utf8Str strUsername;
233 if (i_getClientUserSid(&strSid, &strUsername))
234 {
235 VBoxSDSPerUserData *pUserData = i_lookupPerUserData(strSid);
236 if (pUserData)
237 {
238 if (aVBoxSVC == (IVBoxSVCRegistration *)pUserData->m_ptrTheChosenOne)
239 {
240 LogRel(("VirtualBoxSDS::deregisterVBoxSVC: It's the chosen one for %s (%s)!\n",
241 pUserData->m_strUserSid.c_str(), pUserData->m_strUsername.c_str()));
242 pUserData->m_ptrTheChosenOne.setNull();
243 }
244 else
245 LogRel(("VirtualBoxSDS::deregisterVBoxSVC: not the choosen one (%p != %p)\n",
246 (IVBoxSVCRegistration *)aVBoxSVC, (IVBoxSVCRegistration *)pUserData->m_ptrTheChosenOne));
247 pUserData->i_unlock();
248 pUserData->i_release();
249
250 hrc = S_OK;
251 }
252 else
253 {
254 LogRel(("VirtualBoxSDS::deregisterVBoxSVC: Found no user data for %s (%s) (pid %u)\n",
255 strSid.c_str(), strUsername.c_str(), aPid));
256 hrc = S_OK;
257 }
258 }
259 else
260 hrc = E_FAIL;
261 }
262 else
263 hrc = E_INVALIDARG;
264 LogRel2(("VirtualBoxSDS::deregisterVBoxSVC: returns %Rhrc\n", hrc));
265 return hrc;
266}
267
268
269/*********************************************************************************************************************************
270* Internal Methods *
271*********************************************************************************************************************************/
272
273/*static*/ bool VirtualBoxSDS::i_getClientUserSid(com::Utf8Str *a_pStrSid, com::Utf8Str *a_pStrUsername)
274{
275 bool fRet = false;
276 a_pStrSid->setNull();
277 a_pStrUsername->setNull();
278
279 CoInitializeEx(NULL, COINIT_MULTITHREADED); // is this necessary?
280 HRESULT hrc = CoImpersonateClient();
281 if (SUCCEEDED(hrc))
282 {
283 HANDLE hToken = INVALID_HANDLE_VALUE;
284 if (::OpenThreadToken(GetCurrentThread(), TOKEN_READ, TRUE /*OpenAsSelf*/, &hToken))
285 {
286 CoRevertToSelf();
287
288 union
289 {
290 TOKEN_USER TokenUser;
291 uint8_t abPadding[SECURITY_MAX_SID_SIZE + 256];
292 WCHAR wszUsername[UNLEN + 1];
293 } uBuf;
294 RT_ZERO(uBuf);
295 DWORD cbActual = 0;
296 if (::GetTokenInformation(hToken, TokenUser, &uBuf, sizeof(uBuf), &cbActual))
297 {
298 WCHAR *pwszString;
299 if (ConvertSidToStringSidW(uBuf.TokenUser.User.Sid, &pwszString))
300 {
301 try
302 {
303 *a_pStrSid = pwszString;
304 a_pStrSid->toUpper(); /* (just to be on the safe side) */
305 fRet = true;
306 }
307 catch (std::bad_alloc &)
308 {
309 LogRel(("VirtualBoxSDS::i_GetClientUserSID: std::bad_alloc setting rstrSid.\n"));
310 }
311 LocalFree((HLOCAL)pwszString);
312
313 /*
314 * Get the username too. We don't care if this step fails.
315 */
316 if (fRet)
317 {
318 WCHAR wszUsername[UNLEN * 2 + 1];
319 DWORD cwcUsername = RT_ELEMENTS(wszUsername);
320 WCHAR wszDomain[UNLEN * 2 + 1];
321 DWORD cwcDomain = RT_ELEMENTS(wszDomain);
322 SID_NAME_USE enmNameUse;
323 if (LookupAccountSidW(NULL, uBuf.TokenUser.User.Sid, wszUsername, &cwcUsername,
324 wszDomain, &cwcDomain, &enmNameUse))
325 {
326 wszUsername[RT_ELEMENTS(wszUsername) - 1] = '\0';
327 wszDomain[RT_ELEMENTS(wszDomain) - 1] = '\0';
328 try
329 {
330 *a_pStrUsername = wszDomain;
331 a_pStrUsername->append('/');
332 a_pStrUsername->append(Utf8Str(wszUsername));
333 }
334 catch (std::bad_alloc &)
335 {
336 LogRel(("VirtualBoxSDS::i_GetClientUserSID: std::bad_alloc setting rStrUsername.\n"));
337 a_pStrUsername->setNull();
338 }
339 }
340 else
341 LogRel(("VirtualBoxSDS::i_GetClientUserSID: LookupAccountSidW failed: %u/%x (cwcUsername=%u, cwcDomain=%u)\n",
342 GetLastError(), cwcUsername, cwcDomain));
343 }
344 }
345 else
346 LogRel(("VirtualBoxSDS::i_GetClientUserSID: ConvertSidToStringSidW failed: %u\n", GetLastError()));
347 }
348 else
349 LogRel(("VirtualBoxSDS::i_GetClientUserSID: GetTokenInformation/TokenUser failed: %u\n", GetLastError()));
350 CloseHandle(hToken);
351 }
352 else
353 {
354 CoRevertToSelf();
355 LogRel(("VirtualBoxSDS::i_GetClientUserSID: OpenThreadToken failed: %u\n", GetLastError()));
356 }
357 }
358 else
359 LogRel(("VirtualBoxSDS::i_GetClientUserSID: CoImpersonateClient failed: %Rhrc\n", hrc));
360 CoUninitialize();
361 return fRet;
362}
363
364
365/**
366 * Looks up the given user.
367 *
368 * @returns Pointer to the LOCKED and RETAINED per user data.
369 * NULL if not found.
370 * @param a_rStrUserSid The user SID.
371 */
372VBoxSDSPerUserData *VirtualBoxSDS::i_lookupPerUserData(com::Utf8Str const &a_rStrUserSid)
373{
374 int vrc = RTCritSectRwEnterShared(&m_MapCritSect);
375 if (RT_SUCCESS(vrc))
376 {
377
378 UserDataMap_T::iterator it = m_UserDataMap.find(a_rStrUserSid);
379 if (it != m_UserDataMap.end())
380 {
381 VBoxSDSPerUserData *pUserData = it->second;
382 pUserData->i_retain();
383
384 RTCritSectRwLeaveShared(&m_MapCritSect);
385
386 pUserData->i_lock();
387 return pUserData;
388 }
389
390 RTCritSectRwLeaveShared(&m_MapCritSect);
391 }
392 return NULL;
393}
394
395
396/**
397 * Looks up the given user, creating it if not found
398 *
399 * @returns Pointer to the LOCKED and RETAINED per user data.
400 * NULL on allocation error.
401 * @param a_rStrUserSid The user SID.
402 * @param a_rStrUsername The user name if available.
403 */
404VBoxSDSPerUserData *VirtualBoxSDS::i_lookupOrCreatePerUserData(com::Utf8Str const &a_rStrUserSid,
405 com::Utf8Str const &a_rStrUsername)
406{
407 /*
408 * Try do a simple lookup first.
409 */
410 VBoxSDSPerUserData *pUserData = i_lookupPerUserData(a_rStrUserSid);
411 if (!pUserData)
412 {
413 /*
414 * SID is not in map, create a new one.
415 */
416 try
417 {
418 pUserData = new VBoxSDSPerUserData(a_rStrUserSid, a_rStrUsername);
419 }
420 catch (std::bad_alloc &)
421 {
422 pUserData = NULL;
423 }
424 if (pUserData)
425 {
426 /*
427 * Insert it. We must check if someone raced us here.
428 */
429 VBoxSDSPerUserData *pUserDataFree = pUserData;
430 pUserData->i_lock();
431
432 int vrc = RTCritSectRwEnterExcl(&m_MapCritSect);
433 if (RT_SUCCESS(vrc))
434 {
435
436 UserDataMap_T::iterator it = m_UserDataMap.find(a_rStrUserSid);
437 if (it == m_UserDataMap.end())
438 {
439 try
440 {
441 m_UserDataMap[a_rStrUserSid] = pUserData;
442 pUserData->i_retain();
443 }
444 catch (std::bad_alloc &)
445 {
446 pUserData = NULL;
447 }
448 }
449 else
450 pUserData = NULL;
451
452 RTCritSectRwLeaveExcl(&m_MapCritSect);
453
454 if (pUserData)
455 LogRel(("VirtualBoxSDS::i_lookupOrCreatePerUserData: Created new entry for %s (%s)\n",
456 pUserData->m_strUserSid.c_str(), pUserData->m_strUsername.c_str() ));
457 else
458 {
459 pUserDataFree->i_unlock();
460 delete pUserDataFree;
461 }
462 }
463 }
464 }
465
466 return pUserData;
467}
468
469/* 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