Bug 1651162 [wpt PR 24490] - Origin isolation: add WPTs for different ports, a=testonly
[gecko.git] / ipc / mscom / ProcessRuntime.cpp
blob6c549c406af8db02cb94d023f9251681d5b5b875
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/mscom/ProcessRuntime.h"
9 #if defined(ACCESSIBILITY) && defined(MOZILLA_INTERNAL_API)
10 # include "mozilla/a11y/Compatibility.h"
11 #endif // defined(ACCESSIBILITY) && defined(MOZILLA_INTERNAL_API)
12 #include "mozilla/Assertions.h"
13 #include "mozilla/DynamicallyLinkedFunctionPtr.h"
14 #include "mozilla/mscom/ProcessRuntimeShared.h"
15 #include "mozilla/RefPtr.h"
16 #include "mozilla/UniquePtr.h"
17 #include "mozilla/Unused.h"
18 #include "mozilla/Vector.h"
19 #include "mozilla/WindowsVersion.h"
20 #include "nsWindowsHelpers.h"
22 #if defined(MOZILLA_INTERNAL_API) && defined(MOZ_SANDBOX)
23 # include "mozilla/mscom/EnsureMTA.h"
24 # include "mozilla/sandboxTarget.h"
25 # include "nsThreadManager.h"
26 #endif // defined(MOZILLA_INTERNAL_API) && defined(MOZ_SANDBOX)
28 #include <accctrl.h>
29 #include <aclapi.h>
30 #include <objbase.h>
31 #include <objidl.h>
33 // This API from oleaut32.dll is not declared in Windows SDK headers
34 extern "C" void __cdecl SetOaNoCache(void);
36 namespace mozilla {
37 namespace mscom {
39 ProcessRuntime::ProcessRuntime(GeckoProcessType aProcessType)
40 : ProcessRuntime(aProcessType == GeckoProcessType_Default
41 ? ProcessCategory::GeckoBrowserParent
42 : ProcessCategory::GeckoChild) {}
44 ProcessRuntime::ProcessRuntime(ProcessRuntime::ProcessCategory aProcessCategory)
45 : mInitResult(CO_E_NOTINITIALIZED),
46 mProcessCategory(aProcessCategory)
47 #if defined(ACCESSIBILITY) && defined(MOZILLA_INTERNAL_API)
49 mActCtxRgn(a11y::Compatibility::GetActCtxResourceId())
50 #endif // defined(ACCESSIBILITY) && defined(MOZILLA_INTERNAL_API)
52 #if defined(MOZILLA_INTERNAL_API) && defined(MOZ_SANDBOX)
53 // If our process is running under Win32k lockdown, we cannot initialize
54 // COM with single-threaded apartments. This is because STAs create a hidden
55 // window, which implicitly requires user32 and Win32k, which are blocked.
56 // Instead we start a multi-threaded apartment and conduct our process-wide
57 // COM initialization on that MTA background thread.
58 if (mProcessCategory == ProcessCategory::GeckoChild && IsWin32kLockedDown()) {
59 // It is possible that we're running so early that we might need to start
60 // the thread manager ourselves.
61 nsresult rv = nsThreadManager::get().Init();
62 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
63 if (NS_FAILED(rv)) {
64 return;
67 // Use the current thread's impersonation token to initialize COM, as
68 // it might fail otherwise (depending on sandbox policy).
69 HANDLE rawCurThreadImpToken;
70 if (!::OpenThreadToken(::GetCurrentThread(), TOKEN_DUPLICATE | TOKEN_QUERY,
71 FALSE, &rawCurThreadImpToken)) {
72 mInitResult = HRESULT_FROM_WIN32(::GetLastError());
73 return;
75 nsAutoHandle curThreadImpToken(rawCurThreadImpToken);
77 # if defined(DEBUG)
78 // Ensure that our current token is still an impersonation token (ie, we
79 // have not yet called RevertToSelf() on this thread).
80 DWORD len;
81 TOKEN_TYPE tokenType;
82 MOZ_ASSERT(::GetTokenInformation(rawCurThreadImpToken, TokenType,
83 &tokenType, sizeof(tokenType), &len) &&
84 len == sizeof(tokenType) && tokenType == TokenImpersonation);
85 # endif // defined(DEBUG)
87 // Create an impersonation token based on the current thread's token
88 HANDLE rawMtaThreadImpToken = nullptr;
89 if (!::DuplicateToken(rawCurThreadImpToken, SecurityImpersonation,
90 &rawMtaThreadImpToken)) {
91 mInitResult = HRESULT_FROM_WIN32(::GetLastError());
92 return;
94 nsAutoHandle mtaThreadImpToken(rawMtaThreadImpToken);
96 SandboxTarget::Instance()->RegisterSandboxStartCallback([]() -> void {
97 EnsureMTA(
98 []() -> void {
99 // This is a security risk if it fails, so we release assert
100 MOZ_RELEASE_ASSERT(::RevertToSelf(),
101 "mscom::ProcessRuntime RevertToSelf failed");
103 EnsureMTA::Option::ForceDispatch);
106 // Impersonate and initialize.
107 EnsureMTA(
108 [this, rawMtaThreadImpToken]() -> void {
109 if (!::SetThreadToken(nullptr, rawMtaThreadImpToken)) {
110 mInitResult = HRESULT_FROM_WIN32(::GetLastError());
111 return;
114 InitInsideApartment();
116 EnsureMTA::Option::ForceDispatch);
118 return;
121 #endif // defined(MOZILLA_INTERNAL_API)
123 mAptRegion.Init(GetDesiredApartmentType(mProcessCategory));
125 // We must be the outermost COM initialization on this thread. The COM runtime
126 // cannot be configured once we start manipulating objects
127 MOZ_ASSERT(mAptRegion.IsValidOutermost());
128 if (!mAptRegion.IsValidOutermost()) {
129 mInitResult = mAptRegion.GetHResult();
130 return;
133 InitInsideApartment();
136 /* static */
137 COINIT ProcessRuntime::GetDesiredApartmentType(
138 ProcessRuntime::ProcessCategory aProcessCategory) {
139 // Gecko processes get single-threaded apartments, others get multithreaded
140 // apartments. We should revisit the GeckoChild case as soon as we deploy
141 // Win32k lockdown.
142 switch (aProcessCategory) {
143 case ProcessCategory::GeckoBrowserParent:
144 case ProcessCategory::GeckoChild:
145 return COINIT_APARTMENTTHREADED;
146 default:
147 return COINIT_MULTITHREADED;
151 void ProcessRuntime::InitInsideApartment() {
152 ProcessInitLock lock;
153 if (lock.IsInitialized()) {
154 // COM has already been initialized by a previous ProcessRuntime instance
155 mInitResult = S_OK;
156 return;
159 // We are required to initialize security prior to configuring global options.
160 mInitResult = InitializeSecurity();
161 MOZ_ASSERT(SUCCEEDED(mInitResult));
162 if (FAILED(mInitResult)) {
163 return;
166 RefPtr<IGlobalOptions> globalOpts;
167 mInitResult = ::CoCreateInstance(CLSID_GlobalOptions, nullptr,
168 CLSCTX_INPROC_SERVER, IID_IGlobalOptions,
169 (void**)getter_AddRefs(globalOpts));
170 MOZ_ASSERT(SUCCEEDED(mInitResult));
171 if (FAILED(mInitResult)) {
172 return;
175 // Disable COM's catch-all exception handler
176 mInitResult = globalOpts->Set(COMGLB_EXCEPTION_HANDLING,
177 COMGLB_EXCEPTION_DONOT_HANDLE_ANY);
178 MOZ_ASSERT(SUCCEEDED(mInitResult));
180 // Disable the BSTR cache (as it never invalidates, thus leaking memory)
181 ::SetOaNoCache();
183 if (FAILED(mInitResult)) {
184 return;
187 lock.SetInitialized();
190 /* static */
191 DWORD
192 ProcessRuntime::GetClientThreadId() {
193 DWORD callerTid;
194 HRESULT hr = ::CoGetCallerTID(&callerTid);
195 // Don't return callerTid unless the call succeeded and returned S_FALSE,
196 // indicating that the caller originates from a different process.
197 if (hr != S_FALSE) {
198 return 0;
201 return callerTid;
204 HRESULT
205 ProcessRuntime::InitializeSecurity() {
206 HANDLE rawToken = nullptr;
207 BOOL ok = ::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &rawToken);
208 if (!ok) {
209 return HRESULT_FROM_WIN32(::GetLastError());
211 nsAutoHandle token(rawToken);
213 DWORD len = 0;
214 ok = ::GetTokenInformation(token, TokenUser, nullptr, len, &len);
215 DWORD win32Error = ::GetLastError();
216 if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
217 return HRESULT_FROM_WIN32(win32Error);
220 auto tokenUserBuf = MakeUnique<BYTE[]>(len);
221 TOKEN_USER& tokenUser = *reinterpret_cast<TOKEN_USER*>(tokenUserBuf.get());
222 ok = ::GetTokenInformation(token, TokenUser, tokenUserBuf.get(), len, &len);
223 if (!ok) {
224 return HRESULT_FROM_WIN32(::GetLastError());
227 len = 0;
228 ok = ::GetTokenInformation(token, TokenPrimaryGroup, nullptr, len, &len);
229 win32Error = ::GetLastError();
230 if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
231 return HRESULT_FROM_WIN32(win32Error);
234 auto tokenPrimaryGroupBuf = MakeUnique<BYTE[]>(len);
235 TOKEN_PRIMARY_GROUP& tokenPrimaryGroup =
236 *reinterpret_cast<TOKEN_PRIMARY_GROUP*>(tokenPrimaryGroupBuf.get());
237 ok = ::GetTokenInformation(token, TokenPrimaryGroup,
238 tokenPrimaryGroupBuf.get(), len, &len);
239 if (!ok) {
240 return HRESULT_FROM_WIN32(::GetLastError());
243 SECURITY_DESCRIPTOR sd;
244 if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
245 return HRESULT_FROM_WIN32(::GetLastError());
248 BYTE systemSid[SECURITY_MAX_SID_SIZE];
249 DWORD systemSidSize = sizeof(systemSid);
250 if (!::CreateWellKnownSid(WinLocalSystemSid, nullptr, systemSid,
251 &systemSidSize)) {
252 return HRESULT_FROM_WIN32(::GetLastError());
255 BYTE adminSid[SECURITY_MAX_SID_SIZE];
256 DWORD adminSidSize = sizeof(adminSid);
257 if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, nullptr, adminSid,
258 &adminSidSize)) {
259 return HRESULT_FROM_WIN32(::GetLastError());
262 BYTE appContainersSid[SECURITY_MAX_SID_SIZE];
263 DWORD appContainersSidSize = sizeof(appContainersSid);
264 if (mProcessCategory == ProcessCategory::GeckoBrowserParent &&
265 IsWin8OrLater()) {
266 if (!::CreateWellKnownSid(WinBuiltinAnyPackageSid, nullptr,
267 appContainersSid, &appContainersSidSize)) {
268 return HRESULT_FROM_WIN32(::GetLastError());
272 // Grant access to SYSTEM, Administrators, the user, and when running as the
273 // browser process on Windows 8+, all app containers.
274 const size_t kMaxInlineEntries = 4;
275 mozilla::Vector<EXPLICIT_ACCESS_W, kMaxInlineEntries> entries;
277 Unused << entries.append(EXPLICIT_ACCESS_W{
278 COM_RIGHTS_EXECUTE,
279 GRANT_ACCESS,
280 NO_INHERITANCE,
281 {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
282 reinterpret_cast<LPWSTR>(systemSid)}});
284 Unused << entries.append(EXPLICIT_ACCESS_W{
285 COM_RIGHTS_EXECUTE,
286 GRANT_ACCESS,
287 NO_INHERITANCE,
288 {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID,
289 TRUSTEE_IS_WELL_KNOWN_GROUP, reinterpret_cast<LPWSTR>(adminSid)}});
291 Unused << entries.append(EXPLICIT_ACCESS_W{
292 COM_RIGHTS_EXECUTE,
293 GRANT_ACCESS,
294 NO_INHERITANCE,
295 {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
296 reinterpret_cast<LPWSTR>(tokenUser.User.Sid)}});
298 if (mProcessCategory == ProcessCategory::GeckoBrowserParent &&
299 IsWin8OrLater()) {
300 Unused << entries.append(
301 EXPLICIT_ACCESS_W{COM_RIGHTS_EXECUTE,
302 GRANT_ACCESS,
303 NO_INHERITANCE,
304 {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID,
305 TRUSTEE_IS_WELL_KNOWN_GROUP,
306 reinterpret_cast<LPWSTR>(appContainersSid)}});
309 PACL rawDacl = nullptr;
310 win32Error =
311 ::SetEntriesInAclW(entries.length(), entries.begin(), nullptr, &rawDacl);
312 if (win32Error != ERROR_SUCCESS) {
313 return HRESULT_FROM_WIN32(win32Error);
316 UniquePtr<ACL, LocalFreeDeleter> dacl(rawDacl);
318 if (!::SetSecurityDescriptorDacl(&sd, TRUE, dacl.get(), FALSE)) {
319 return HRESULT_FROM_WIN32(::GetLastError());
322 if (!::SetSecurityDescriptorOwner(&sd, tokenUser.User.Sid, FALSE)) {
323 return HRESULT_FROM_WIN32(::GetLastError());
326 if (!::SetSecurityDescriptorGroup(&sd, tokenPrimaryGroup.PrimaryGroup,
327 FALSE)) {
328 return HRESULT_FROM_WIN32(::GetLastError());
331 return ::CoInitializeSecurity(
332 &sd, -1, nullptr, nullptr, RPC_C_AUTHN_LEVEL_DEFAULT,
333 RPC_C_IMP_LEVEL_IDENTIFY, nullptr, EOAC_NONE, nullptr);
336 } // namespace mscom
337 } // namespace mozilla