VirtualBox

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

Last change on this file since 102673 was 98288, checked in by vboxsync, 2 years ago

Main/src-server: rc -> hrc/vrc (partial). bugref:10223

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