VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/ClientToken.cpp@ 94134

Last change on this file since 94134 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.4 KB
Line 
1/* $Id: ClientToken.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 *
4 * VirtualBox API client session crash token handling
5 */
6
7/*
8 * Copyright (C) 2004-2022 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#define LOG_GROUP LOG_GROUP_MAIN
20#include <iprt/asm.h>
21#include <iprt/assert.h>
22#include <VBox/log.h>
23#include <iprt/semaphore.h>
24#include <iprt/process.h>
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include <VBox/com/defs.h>
35
36#include <vector>
37
38#include "VirtualBoxBase.h"
39#include "AutoCaller.h"
40#include "ClientToken.h"
41#include "MachineImpl.h"
42
43#ifdef RT_OS_WINDOWS
44# include <sddl.h>
45#endif
46
47Machine::ClientToken::ClientToken()
48{
49 AssertReleaseFailed();
50}
51
52Machine::ClientToken::~ClientToken()
53{
54#if defined(RT_OS_WINDOWS)
55 if (mClientToken)
56 {
57 LogFlowFunc(("Closing mClientToken=%p\n", mClientToken));
58 ::CloseHandle(mClientToken);
59 }
60#elif defined(RT_OS_OS2)
61 if (mClientToken != NULLHANDLE)
62 ::DosCloseMutexSem(mClientToken);
63#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
64 if (mClientToken >= 0)
65 ::semctl(mClientToken, 0, IPC_RMID);
66# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
67 mClientTokenId = "0";
68# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
69#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
70 /* release the token, uses reference counting */
71 if (mClientToken)
72 {
73 if (!mClientTokenPassed)
74 mClientToken->Release();
75 mClientToken = NULL;
76 }
77#else
78# error "Port me!"
79#endif
80 mClientToken = CTTOKENARG;
81}
82
83Machine::ClientToken::ClientToken(const ComObjPtr<Machine> &pMachine,
84 SessionMachine *pSessionMachine) :
85 mMachine(pMachine)
86{
87#if defined(RT_OS_WINDOWS)
88 NOREF(pSessionMachine);
89
90 /* Get user's SID to use it as part of the mutex name to distinguish shared machine instances
91 * between users
92 */
93 Utf8Str strUserSid;
94 HANDLE hProcessToken = INVALID_HANDLE_VALUE;
95 if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hProcessToken))
96 {
97 DWORD dwSize = 0;
98 BOOL fRc = ::GetTokenInformation(hProcessToken, TokenUser, NULL, 0, &dwSize);
99 DWORD dwErr = ::GetLastError();
100 if (!fRc && dwErr == ERROR_INSUFFICIENT_BUFFER && dwSize > 0)
101 {
102 PTOKEN_USER pTokenUser = (PTOKEN_USER)RTMemTmpAllocZ(dwSize);
103 if (pTokenUser)
104 {
105 if (::GetTokenInformation(hProcessToken, TokenUser, pTokenUser, dwSize, &dwSize))
106 {
107 PRTUTF16 wstrSid = NULL;
108 if (::ConvertSidToStringSid(pTokenUser->User.Sid, &wstrSid))
109 {
110 strUserSid = wstrSid;
111 ::LocalFree(wstrSid);
112 }
113 else
114 AssertMsgFailed(("Cannot convert SID to string, err=%u", ::GetLastError()));
115 }
116 else
117 AssertMsgFailed(("Cannot get thread access token information, err=%u", ::GetLastError()));
118 RTMemFree(pTokenUser);
119 }
120 else
121 AssertMsgFailed(("No memory"));
122 }
123 else
124 AssertMsgFailed(("Cannot get thread access token information, err=%u", ::GetLastError()));
125 CloseHandle(hProcessToken);
126 }
127 else
128 AssertMsgFailed(("Cannot get thread access token, err=%u", ::GetLastError()));
129
130 BstrFmt tokenId("Global\\VBoxSession-%s-VM-%RTuuid", strUserSid.c_str(), pMachine->mData->mUuid.raw());
131
132 /* create security descriptor to allow SYNCHRONIZE access from any windows sessions and users.
133 * otherwise VM can't open the mutex if VBoxSVC and VM are in different session (e.g. some VM
134 * started by autostart service)
135 *
136 * SDDL string contains following ACEs:
137 * CreateOwner : MUTEX_ALL_ACCESS
138 * System : MUTEX_ALL_ACCESS
139 * BuiltInAdministrators : MUTEX_ALL_ACCESS
140 * Everyone : SYNCHRONIZE|MUTEX_MODIFY_STATE
141 */
142
143 //static const RTUTF16 s_wszSecDesc[] = L"D:(A;;0x1F0001;;;CO)(A;;0x1F0001;;;SY)(A;;0x1F0001;;;BA)(A;;0x100001;;;WD)";
144 com::BstrFmt bstrSecDesc("D:(A;;0x1F0001;;;CO)"
145 "(A;;0x1F0001;;;SY)"
146 "(A;;0x1F0001;;;BA)"
147 "(A;;0x1F0001;;;BA)"
148 "(A;;0x1F0001;;;%s)"
149 , strUserSid.c_str());
150 PSECURITY_DESCRIPTOR pSecDesc = NULL;
151 //AssertMsgStmt(::ConvertStringSecurityDescriptorToSecurityDescriptor(s_wszSecDesc, SDDL_REVISION_1, &pSecDesc, NULL),
152 AssertMsgStmt(::ConvertStringSecurityDescriptorToSecurityDescriptor(bstrSecDesc.raw(), SDDL_REVISION_1, &pSecDesc, NULL),
153 ("Cannot create security descriptor for token '%ls', err=%u", tokenId.raw(), GetLastError()),
154 pSecDesc = NULL);
155
156 SECURITY_ATTRIBUTES SecAttr;
157 SecAttr.lpSecurityDescriptor = pSecDesc;
158 SecAttr.nLength = sizeof(SecAttr);
159 SecAttr.bInheritHandle = FALSE;
160 mClientToken = ::CreateMutex(&SecAttr, FALSE, tokenId.raw());
161 mClientTokenId = tokenId;
162 AssertMsg(mClientToken, ("Cannot create token '%s', err=%d", mClientTokenId.c_str(), ::GetLastError()));
163
164 if (pSecDesc)
165 ::LocalFree(pSecDesc);
166
167#elif defined(RT_OS_OS2)
168 NOREF(pSessionMachine);
169 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
170 pMachine->mData->mUuid.raw());
171 mClientTokenId = ipcSem;
172 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mClientToken, 0, FALSE);
173 AssertMsg(arc == NO_ERROR,
174 ("Cannot create token '%s', arc=%ld",
175 ipcSem.c_str(), arc));
176#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
177 NOREF(pSessionMachine);
178# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
179# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
180 /** @todo Check that this still works correctly. */
181 AssertCompileSize(key_t, 8);
182# else
183 AssertCompileSize(key_t, 4);
184# endif
185 key_t key;
186 mClientToken = -1;
187 mClientTokenId = "0";
188 for (uint32_t i = 0; i < 1 << 24; i++)
189 {
190 key = ((uint32_t)'V' << 24) | i;
191 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
192 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
193 {
194 mClientToken = sem;
195 if (sem >= 0)
196 mClientTokenId = BstrFmt("%u", key);
197 break;
198 }
199 }
200# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
201 Utf8Str semName = pMachine->mData->m_strConfigFileFull;
202 char *pszSemName = NULL;
203 RTStrUtf8ToCurrentCP(&pszSemName, semName);
204 key_t key = ::ftok(pszSemName, 'V');
205 RTStrFree(pszSemName);
206
207 mClientToken = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
208# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
209
210 int errnoSave = errno;
211 if (mClientToken < 0 && errnoSave == ENOSYS)
212 {
213 mMachine->setError(E_FAIL,
214 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
215 "support for SysV IPC. Check the host kernel configuration for "
216 "CONFIG_SYSVIPC=y"));
217 mClientToken = CTTOKENARG;
218 return;
219 }
220 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
221 * the token */
222 if (mClientToken < 0 && errnoSave == ENOSPC)
223 {
224#ifdef RT_OS_LINUX
225 mMachine->setError(E_FAIL,
226 tr("Cannot create IPC semaphore because the system limit for the "
227 "maximum number of semaphore sets (SEMMNI), or the system wide "
228 "maximum number of semaphores (SEMMNS) would be exceeded. The "
229 "current set of SysV IPC semaphores can be determined from "
230 "the file /proc/sysvipc/sem"));
231#else
232 mMachine->setError(E_FAIL,
233 tr("Cannot create IPC semaphore because the system-imposed limit "
234 "on the maximum number of allowed semaphores or semaphore "
235 "identifiers system-wide would be exceeded"));
236#endif
237 mClientToken = CTTOKENARG;
238 return;
239 }
240 AssertMsgReturnVoid(mClientToken >= 0, ("Cannot create token, errno=%d", errnoSave));
241 /* set the initial value to 1 */
242 int rv = ::semctl(mClientToken, 0, SETVAL, 1);
243 errnoSave = errno;
244 if (rv != 0)
245 {
246 ::semctl(mClientToken, 0, IPC_RMID);
247 mClientToken = CTTOKENARG;
248 AssertMsgFailedReturnVoid(("Cannot init token, errno=%d", errnoSave));
249 }
250#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
251 ComObjPtr<MachineToken> pToken;
252 HRESULT rc = pToken.createObject();
253 if (SUCCEEDED(rc))
254 {
255 rc = pToken->init(pSessionMachine);
256 if (SUCCEEDED(rc))
257 {
258 mClientToken = pToken;
259 if (mClientToken)
260 {
261 rc = mClientToken->AddRef();
262 if (FAILED(rc))
263 mClientToken = NULL;
264 }
265 }
266 }
267 pToken.setNull();
268 mClientTokenPassed = false;
269 /* mClientTokenId isn't really used */
270 mClientTokenId = pMachine->mData->m_strConfigFileFull;
271 AssertMsg(mClientToken,
272 ("Cannot create token '%s', rc=%Rhrc",
273 mClientTokenId.c_str(), rc));
274#else
275# error "Port me!"
276#endif
277}
278
279bool Machine::ClientToken::isReady()
280{
281 return mClientToken != CTTOKENARG;
282}
283
284void Machine::ClientToken::getId(Utf8Str &strId)
285{
286 strId = mClientTokenId;
287}
288
289CTTOKENTYPE Machine::ClientToken::getToken()
290{
291#ifdef VBOX_WITH_GENERIC_SESSION_WATCHER
292 mClientTokenPassed = true;
293#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
294 return mClientToken;
295}
296
297#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
298bool Machine::ClientToken::release()
299{
300 bool terminated = false;
301
302#if defined(RT_OS_WINDOWS)
303 AssertMsg(mClientToken, ("semaphore must be created"));
304
305 /* release the token */
306 ::ReleaseMutex(mClientToken);
307 terminated = true;
308#elif defined(RT_OS_OS2)
309 AssertMsg(mClientToken, ("semaphore must be created"));
310
311 /* release the token */
312 ::DosReleaseMutexSem(mClientToken);
313 terminated = true;
314#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
315 AssertMsg(mClientToken >= 0, ("semaphore must be created"));
316 int val = ::semctl(mClientToken, 0, GETVAL);
317 if (val > 0)
318 {
319 /* the semaphore is signaled, meaning the session is terminated */
320 terminated = true;
321 }
322#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
323 /** @todo r=klaus never tested, this code is not reached */
324 AssertMsg(mClientToken, ("token must be created"));
325 /* release the token, uses reference counting */
326 if (mClientToken)
327 {
328 if (!mClientTokenPassed)
329 mClientToken->Release();
330 mClientToken = NULL;
331 }
332 terminated = true;
333#else
334# error "Port me!"
335#endif
336 return terminated;
337}
338#endif /* !VBOX_WITH_GENERIC_SESSION_WATCHER */
339
340/* 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