VirtualBox

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

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

Export src-global and all files under it to OSE.
bugref:3300: VBoxSVC from terminal server session is not "visible" on console and vice versa on windows

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.1 KB
Line 
1/* $Id: VirtualBoxSDSImpl.cpp 70507 2018-01-10 11:07:19Z vboxsync $ */
2/** @file
3 * VBox Global COM Class implementation.
4 */
5
6/*
7 * Copyright (C) 2015-2017 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_(HRESULT) VirtualBoxSDS::RegisterVBoxSVC(IVBoxSVCRegistration *aVBoxSVC, LONG aPid, IUnknown **aExistingVirtualBox)
149{
150 LogRel(("VirtualBoxSDS::registerVBoxSVC: aVBoxSVC=%p aPid=%u\n", (IVBoxSVCRegistration *)aVBoxSVC, aPid));
151 HRESULT hrc;
152 if ( RT_VALID_PTR(aVBoxSVC)
153 && RT_VALID_PTR(aExistingVirtualBox))
154 {
155 *aExistingVirtualBox = NULL;
156
157 /* Get the client user SID and name. */
158 com::Utf8Str strSid;
159 com::Utf8Str strUsername;
160 if (i_getClientUserSid(&strSid, &strUsername))
161 {
162 VBoxSDSPerUserData *pUserData = i_lookupOrCreatePerUserData(strSid, strUsername);
163 if (pUserData)
164 {
165 if (pUserData->m_ptrTheChosenOne.isNotNull())
166 {
167 try
168 {
169 hrc = pUserData->m_ptrTheChosenOne->GetVirtualBox(aExistingVirtualBox);
170 }
171 catch (...)
172 {
173 LogRel(("VirtualBoxSDS::registerVBoxSVC: unexpected exception calling GetVirtualBox.\n"));
174 hrc = E_FAIL;
175 }
176 if (FAILED_DEAD_INTERFACE(hrc))
177 {
178 LogRel(("VirtualBoxSDS::registerVBoxSVC: Seems VBoxSVC instance died. Dropping it and letting caller take over.\n"));
179 pUserData->m_ptrTheChosenOne.setNull();
180 }
181 }
182 else
183 hrc = S_OK;
184
185 if (pUserData->m_ptrTheChosenOne.isNull())
186 {
187 LogRel(("VirtualBoxSDS::registerVBoxSVC: Making aPid=%u the chosen one for user %s (%s)!\n",
188 aPid, pUserData->m_strUserSid.c_str(), pUserData->m_strUsername.c_str()));
189 try
190 {
191 pUserData->m_ptrTheChosenOne = aVBoxSVC;
192 hrc = S_OK;
193 }
194 catch (...)
195 {
196 LogRel(("VirtualBoxSDS::registerVBoxSVC: unexpected exception setting the chosen one.\n"));
197 hrc = E_FAIL;
198 }
199 }
200
201 pUserData->i_unlock();
202 pUserData->i_release();
203 }
204 else
205 hrc = E_OUTOFMEMORY;
206 }
207 else
208 hrc = E_FAIL;
209 }
210 else
211 hrc = E_INVALIDARG;
212 LogRel2(("VirtualBoxSDS::registerVBoxSVC: returns %Rhrc aExistingVirtualBox=%p\n", hrc, (IUnknown *)aExistingVirtualBox));
213 return hrc;
214}
215
216STDMETHODIMP_(HRESULT) VirtualBoxSDS::DeregisterVBoxSVC(IVBoxSVCRegistration *aVBoxSVC, LONG aPid)
217{
218 LogRel(("VirtualBoxSDS::deregisterVBoxSVC: aVBoxSVC=%p aPid=%u\n", (IVBoxSVCRegistration *)aVBoxSVC, aPid));
219 HRESULT hrc;
220 if (RT_VALID_PTR(aVBoxSVC))
221 {
222 /* Get the client user SID and name. */
223 com::Utf8Str strSid;
224 com::Utf8Str strUsername;
225 if (i_getClientUserSid(&strSid, &strUsername))
226 {
227 VBoxSDSPerUserData *pUserData = i_lookupPerUserData(strSid);
228 if (pUserData)
229 {
230 if ( (IVBoxSVCRegistration *)aVBoxSVC
231 == (IVBoxSVCRegistration *)pUserData->m_ptrTheChosenOne)
232 {
233 LogRel(("VirtualBoxSDS::deregisterVBoxSVC: It's the chosen one for %s (%s)!\n",
234 pUserData->m_strUserSid.c_str(), pUserData->m_strUsername.c_str()));
235 pUserData->m_ptrTheChosenOne.setNull();
236 /** @todo consider evicting the user from the table... */
237 }
238 else
239 LogRel(("VirtualBoxSDS::deregisterVBoxSVC: not the choosen one (%p != %p)\n",
240 (IVBoxSVCRegistration *)aVBoxSVC, (IVBoxSVCRegistration *)pUserData->m_ptrTheChosenOne));
241 pUserData->i_unlock();
242 pUserData->i_release();
243
244 hrc = S_OK;
245 }
246 else
247 {
248 LogRel(("VirtualBoxSDS::deregisterVBoxSVC: Found no user data for %s (%s) (pid %u)\n",
249 strSid.c_str(), strUsername.c_str(), aPid));
250 hrc = S_OK;
251 }
252 }
253 else
254 hrc = E_FAIL;
255 }
256 else
257 hrc = E_INVALIDARG;
258 LogRel2(("VirtualBoxSDS::deregisterVBoxSVC: returns %Rhrc\n", hrc));
259 return hrc;
260}
261
262
263// private methods
264///////////////////////////////////////////////////////////////////////////////
265
266/*static*/ bool VirtualBoxSDS::i_getClientUserSid(com::Utf8Str *a_pStrSid, com::Utf8Str *a_pStrUsername)
267{
268 bool fRet = false;
269 a_pStrSid->setNull();
270 a_pStrUsername->setNull();
271
272 CoInitializeEx(NULL, COINIT_MULTITHREADED); // is this necessary?
273 HRESULT hrc = CoImpersonateClient();
274 if (SUCCEEDED(hrc))
275 {
276 HANDLE hToken = INVALID_HANDLE_VALUE;
277 if (::OpenThreadToken(GetCurrentThread(), TOKEN_READ, TRUE /*OpenAsSelf*/, &hToken))
278 {
279 CoRevertToSelf();
280
281 union
282 {
283 TOKEN_USER TokenUser;
284 uint8_t abPadding[SECURITY_MAX_SID_SIZE + 256];
285 WCHAR wszUsername[UNLEN + 1];
286 } uBuf;
287 RT_ZERO(uBuf);
288 DWORD cbActual = 0;
289 if (::GetTokenInformation(hToken, TokenUser, &uBuf, sizeof(uBuf), &cbActual))
290 {
291 WCHAR *pwszString;
292 if (ConvertSidToStringSidW(uBuf.TokenUser.User.Sid, &pwszString))
293 {
294 try
295 {
296 *a_pStrSid = pwszString;
297 a_pStrSid->toUpper(); /* (just to be on the safe side) */
298 fRet = true;
299 }
300 catch (std::bad_alloc)
301 {
302 LogRel(("VirtualBoxSDS::i_GetClientUserSID: std::bad_alloc setting rstrSid.\n"));
303 }
304 LocalFree((HLOCAL)pwszString);
305
306 /*
307 * Get the username too. We don't care if this step fails.
308 */
309 if (fRet)
310 {
311 WCHAR wszUsername[UNLEN * 2 + 1];
312 DWORD cwcUsername = RT_ELEMENTS(wszUsername);
313 WCHAR wszDomain[UNLEN * 2 + 1];
314 DWORD cwcDomain = RT_ELEMENTS(wszDomain);
315 SID_NAME_USE enmNameUse;
316 if (LookupAccountSidW(NULL, uBuf.TokenUser.User.Sid, wszUsername, &cwcUsername,
317 wszDomain, &cwcDomain, &enmNameUse))
318 {
319 wszUsername[RT_ELEMENTS(wszUsername) - 1] = '\0';
320 wszDomain[RT_ELEMENTS(wszDomain) - 1] = '\0';
321 try
322 {
323 *a_pStrUsername = wszDomain;
324 a_pStrUsername->append('/');
325 a_pStrUsername->append(Utf8Str(wszUsername));
326 }
327 catch (std::bad_alloc)
328 {
329 LogRel(("VirtualBoxSDS::i_GetClientUserSID: std::bad_alloc setting rStrUsername.\n"));
330 a_pStrUsername->setNull();
331 }
332 }
333 else
334 LogRel(("VirtualBoxSDS::i_GetClientUserSID: LookupAccountSidW failed: %u/%x (cwcUsername=%u, cwcDomain=%u)\n",
335 GetLastError(), cwcUsername, cwcDomain));
336 }
337 }
338 else
339 LogRel(("VirtualBoxSDS::i_GetClientUserSID: ConvertSidToStringSidW failed: %u\n", GetLastError()));
340 }
341 else
342 LogRel(("VirtualBoxSDS::i_GetClientUserSID: GetTokenInformation/TokenUser failed: %u\n", GetLastError()));
343 CloseHandle(hToken);
344 }
345 else
346 {
347 CoRevertToSelf();
348 LogRel(("VirtualBoxSDS::i_GetClientUserSID: OpenThreadToken failed: %u\n", GetLastError()));
349 }
350 }
351 else
352 LogRel(("VirtualBoxSDS::i_GetClientUserSID: CoImpersonateClient failed: %Rhrc\n", hrc));
353 CoUninitialize();
354 return fRet;
355}
356
357
358/**
359 * Looks up the given user.
360 *
361 * @returns Pointer to the LOCKED and RETAINED per user data.
362 * NULL if not found.
363 * @param a_rStrUserSid The user SID.
364 */
365VBoxSDSPerUserData *VirtualBoxSDS::i_lookupPerUserData(com::Utf8Str const &a_rStrUserSid)
366{
367 int vrc = RTCritSectRwEnterShared(&m_MapCritSect);
368 if (RT_SUCCESS(vrc))
369 {
370
371 UserDataMap_T::iterator it = m_UserDataMap.find(a_rStrUserSid);
372 if (it != m_UserDataMap.end())
373 {
374 VBoxSDSPerUserData *pUserData = it->second;
375 pUserData->i_retain();
376
377 RTCritSectRwLeaveShared(&m_MapCritSect);
378
379 pUserData->i_lock();
380 return pUserData;
381 }
382
383 RTCritSectRwLeaveShared(&m_MapCritSect);
384 }
385 return NULL;
386}
387
388
389/**
390 * Looks up the given user, creating it if not found
391 *
392 * @returns Pointer to the LOCKED and RETAINED per user data.
393 * NULL on allocation error.
394 * @param a_rStrUserSid The user SID.
395 * @param a_rStrUsername The user name if available.
396 */
397VBoxSDSPerUserData *VirtualBoxSDS::i_lookupOrCreatePerUserData(com::Utf8Str const &a_rStrUserSid,
398 com::Utf8Str const &a_rStrUsername)
399{
400 /*
401 * Try do a simple lookup first.
402 */
403 VBoxSDSPerUserData *pUserData = i_lookupPerUserData(a_rStrUserSid);
404 if (!pUserData)
405 {
406 /*
407 * SID is not in map, create a new one.
408 */
409 try
410 {
411 pUserData = new VBoxSDSPerUserData(a_rStrUserSid, a_rStrUsername);
412 }
413 catch (std::bad_alloc)
414 {
415 pUserData = NULL;
416 }
417 if (pUserData)
418 {
419 /*
420 * Insert it. We must check if someone raced us here.
421 */
422 VBoxSDSPerUserData *pUserDataFree = pUserData;
423 pUserData->i_lock();
424
425 int vrc = RTCritSectRwEnterExcl(&m_MapCritSect);
426 if (RT_SUCCESS(vrc))
427 {
428
429 UserDataMap_T::iterator it = m_UserDataMap.find(a_rStrUserSid);
430 if (it == m_UserDataMap.end())
431 {
432 try
433 {
434 m_UserDataMap[a_rStrUserSid] = pUserData;
435 pUserData->i_retain();
436 }
437 catch (std::bad_alloc)
438 {
439 pUserData = NULL;
440 }
441 }
442 else
443 pUserData = NULL;
444
445 RTCritSectRwLeaveExcl(&m_MapCritSect);
446
447 if (pUserData)
448 LogRel(("VirtualBoxSDS::i_lookupOrCreatePerUserData: Created new entry for %s (%s)\n",
449 pUserData->m_strUserSid.c_str(), pUserData->m_strUsername.c_str() ));
450 else
451 {
452 pUserDataFree->i_unlock();
453 delete pUserDataFree;
454 }
455 }
456 }
457 }
458
459 return pUserData;
460}
461
462/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use