VirtualBox

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

最後變更 在這個檔案從80569是 80569,由 vboxsync 提交於 6 年 前

Main: bugref:9341: Added VM autostart during boot support for windows host

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 11.2 KB
 
1/* $Id: ClientToken.cpp 80569 2019-09-03 14:34:21Z vboxsync $ */
2/** @file
3 *
4 * VirtualBox API client session crash token handling
5 */
6
7/*
8 * Copyright (C) 2004-2019 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.alldomusa.eu.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 Bstr tokenId = Bstr(Utf8Str("Global\\") + strUserSid + "/" + pMachine->mData->mUuid.toString());
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("O:%sD:(A;;0x1F0001;;;CO)(A;;0x1F0001;;;SY)(A;;0x1F0001;;;BA)", strUserSid.c_str());
145 PSECURITY_DESCRIPTOR pSecDesc = NULL;
146 //AssertMsgStmt(::ConvertStringSecurityDescriptorToSecurityDescriptor(s_wszSecDesc, SDDL_REVISION_1, &pSecDesc, NULL),
147 AssertMsgStmt(::ConvertStringSecurityDescriptorToSecurityDescriptor(bstrSecDesc.raw(), SDDL_REVISION_1, &pSecDesc, NULL),
148 ("Cannot create security descriptor for token '%ls', err=%u", tokenId.raw(), GetLastError()),
149 pSecDesc = NULL);
150
151 SECURITY_ATTRIBUTES SecAttr;
152 SecAttr.lpSecurityDescriptor = pSecDesc;
153 SecAttr.nLength = sizeof(SecAttr);
154 SecAttr.bInheritHandle = FALSE;
155 mClientToken = ::CreateMutex(&SecAttr, FALSE, tokenId.raw());
156 mClientTokenId = tokenId;
157 AssertMsg(mClientToken, ("Cannot create token '%s', err=%d", mClientTokenId.c_str(), ::GetLastError()));
158
159 if (pSecDesc)
160 ::LocalFree(pSecDesc);
161
162#elif defined(RT_OS_OS2)
163 NOREF(pSessionMachine);
164 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
165 pMachine->mData->mUuid.raw());
166 mClientTokenId = ipcSem;
167 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mClientToken, 0, FALSE);
168 AssertMsg(arc == NO_ERROR,
169 ("Cannot create token '%s', arc=%ld",
170 ipcSem.c_str(), arc));
171#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
172 NOREF(pSessionMachine);
173# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
174# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
175 /** @todo Check that this still works correctly. */
176 AssertCompileSize(key_t, 8);
177# else
178 AssertCompileSize(key_t, 4);
179# endif
180 key_t key;
181 mClientToken = -1;
182 mClientTokenId = "0";
183 for (uint32_t i = 0; i < 1 << 24; i++)
184 {
185 key = ((uint32_t)'V' << 24) | i;
186 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
187 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
188 {
189 mClientToken = sem;
190 if (sem >= 0)
191 mClientTokenId = BstrFmt("%u", key);
192 break;
193 }
194 }
195# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
196 Utf8Str semName = pMachine->mData->m_strConfigFileFull;
197 char *pszSemName = NULL;
198 RTStrUtf8ToCurrentCP(&pszSemName, semName);
199 key_t key = ::ftok(pszSemName, 'V');
200 RTStrFree(pszSemName);
201
202 mClientToken = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
203# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
204
205 int errnoSave = errno;
206 if (mClientToken < 0 && errnoSave == ENOSYS)
207 {
208 mMachine->setError(E_FAIL,
209 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
210 "support for SysV IPC. Check the host kernel configuration for "
211 "CONFIG_SYSVIPC=y"));
212 mClientToken = CTTOKENARG;
213 return;
214 }
215 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
216 * the token */
217 if (mClientToken < 0 && errnoSave == ENOSPC)
218 {
219#ifdef RT_OS_LINUX
220 mMachine->setError(E_FAIL,
221 tr("Cannot create IPC semaphore because the system limit for the "
222 "maximum number of semaphore sets (SEMMNI), or the system wide "
223 "maximum number of semaphores (SEMMNS) would be exceeded. The "
224 "current set of SysV IPC semaphores can be determined from "
225 "the file /proc/sysvipc/sem"));
226#else
227 mMachine->setError(E_FAIL,
228 tr("Cannot create IPC semaphore because the system-imposed limit "
229 "on the maximum number of allowed semaphores or semaphore "
230 "identifiers system-wide would be exceeded"));
231#endif
232 mClientToken = CTTOKENARG;
233 return;
234 }
235 AssertMsgReturnVoid(mClientToken >= 0, ("Cannot create token, errno=%d", errnoSave));
236 /* set the initial value to 1 */
237 int rv = ::semctl(mClientToken, 0, SETVAL, 1);
238 errnoSave = errno;
239 if (rv != 0)
240 {
241 ::semctl(mClientToken, 0, IPC_RMID);
242 mClientToken = CTTOKENARG;
243 AssertMsgFailedReturnVoid(("Cannot init token, errno=%d", errnoSave));
244 }
245#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
246 ComObjPtr<MachineToken> pToken;
247 HRESULT rc = pToken.createObject();
248 if (SUCCEEDED(rc))
249 {
250 rc = pToken->init(pSessionMachine);
251 if (SUCCEEDED(rc))
252 {
253 mClientToken = pToken;
254 if (mClientToken)
255 {
256 rc = mClientToken->AddRef();
257 if (FAILED(rc))
258 mClientToken = NULL;
259 }
260 }
261 }
262 pToken.setNull();
263 mClientTokenPassed = false;
264 /* mClientTokenId isn't really used */
265 mClientTokenId = pMachine->mData->m_strConfigFileFull;
266 AssertMsg(mClientToken,
267 ("Cannot create token '%s', rc=%Rhrc",
268 mClientTokenId.c_str(), rc));
269#else
270# error "Port me!"
271#endif
272}
273
274bool Machine::ClientToken::isReady()
275{
276 return mClientToken != CTTOKENARG;
277}
278
279void Machine::ClientToken::getId(Utf8Str &strId)
280{
281 strId = mClientTokenId;
282}
283
284CTTOKENTYPE Machine::ClientToken::getToken()
285{
286#ifdef VBOX_WITH_GENERIC_SESSION_WATCHER
287 mClientTokenPassed = true;
288#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
289 return mClientToken;
290}
291
292#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
293bool Machine::ClientToken::release()
294{
295 bool terminated = false;
296
297#if defined(RT_OS_WINDOWS)
298 AssertMsg(mClientToken, ("semaphore must be created"));
299
300 /* release the token */
301 ::ReleaseMutex(mClientToken);
302 terminated = true;
303#elif defined(RT_OS_OS2)
304 AssertMsg(mClientToken, ("semaphore must be created"));
305
306 /* release the token */
307 ::DosReleaseMutexSem(mClientToken);
308 terminated = true;
309#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
310 AssertMsg(mClientToken >= 0, ("semaphore must be created"));
311 int val = ::semctl(mClientToken, 0, GETVAL);
312 if (val > 0)
313 {
314 /* the semaphore is signaled, meaning the session is terminated */
315 terminated = true;
316 }
317#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
318 /** @todo r=klaus never tested, this code is not reached */
319 AssertMsg(mClientToken, ("token must be created"));
320 /* release the token, uses reference counting */
321 if (mClientToken)
322 {
323 if (!mClientTokenPassed)
324 mClientToken->Release();
325 mClientToken = NULL;
326 }
327 terminated = true;
328#else
329# error "Port me!"
330#endif
331 return terminated;
332}
333#endif /* !VBOX_WITH_GENERIC_SESSION_WATCHER */
334
335/* vi: set tabstop=4 shiftwidth=4 expandtab: */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette