Bug 1277075: Add MainThreadRuntime class to mscom glue, enabling safe initialization of COM security and exception handlers; r?jimm
MozReview-Commit-ID: 9Ig9wb4KVps
--- a/ipc/mscom/COMApartmentRegion.h
+++ b/ipc/mscom/COMApartmentRegion.h
@@ -11,17 +11,17 @@
#include "mozilla/Attributes.h"
#include <objbase.h>
namespace mozilla {
namespace mscom {
template<COINIT T>
-class MOZ_RAII COMApartmentRegion
+class MOZ_NON_TEMPORARY_CLASS COMApartmentRegion
{
public:
COMApartmentRegion()
: mInitResult(::CoInitializeEx(nullptr, T))
{
// If this fires then we're probably mixing apartments on the same thread
MOZ_ASSERT(IsValid());
}
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/MainThreadRuntime.cpp
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/mscom/MainThreadRuntime.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/WindowsVersion.h"
+#include "nsDebug.h"
+#include "nsWindowsHelpers.h"
+
+#include <accctrl.h>
+#include <aclapi.h>
+#include <objbase.h>
+#include <objidl.h>
+
+namespace {
+
+struct LocalFreeDeleter
+{
+ void operator()(void* aPtr)
+ {
+ ::LocalFree(aPtr);
+ }
+};
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace mscom {
+
+MainThreadRuntime::MainThreadRuntime()
+ : mInitResult(E_UNEXPECTED)
+{
+ // We must be the outermost COM initialization on this thread. The COM runtime
+ // cannot be configured once we start manipulating objects
+ MOZ_ASSERT(mStaRegion.IsValidOutermost());
+ if (NS_WARN_IF(!mStaRegion.IsValidOutermost())) {
+ return;
+ }
+
+ // Windows XP doesn't support setting of the COM exception policy, so we'll
+ // just stop here in that case.
+ if (!IsVistaOrLater()) {
+ mInitResult = S_OK;
+ return;
+ }
+
+ // We are required to initialize security in order to configure global options.
+ mInitResult = InitializeSecurity();
+ MOZ_ASSERT(SUCCEEDED(mInitResult));
+ if (FAILED(mInitResult)) {
+ return;
+ }
+
+ RefPtr<IGlobalOptions> globalOpts;
+ mInitResult = ::CoCreateInstance(CLSID_GlobalOptions, nullptr,
+ CLSCTX_INPROC_SERVER, IID_IGlobalOptions,
+ (void**)getter_AddRefs(globalOpts));
+ MOZ_ASSERT(SUCCEEDED(mInitResult));
+ if (FAILED(mInitResult)) {
+ return;
+ }
+
+ // Windows 7 has a policy that is even more strict. We should use that one
+ // whenever possible.
+ ULONG_PTR exceptionSetting = IsWin7OrLater() ?
+ COMGLB_EXCEPTION_DONOT_HANDLE_ANY :
+ COMGLB_EXCEPTION_DONOT_HANDLE;
+ mInitResult = globalOpts->Set(COMGLB_EXCEPTION_HANDLING, exceptionSetting);
+ MOZ_ASSERT(SUCCEEDED(mInitResult));
+}
+
+HRESULT
+MainThreadRuntime::InitializeSecurity()
+{
+ HANDLE rawToken = nullptr;
+ BOOL ok = ::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &rawToken);
+ if (!ok) {
+ return HRESULT_FROM_WIN32(::GetLastError());
+ }
+ nsAutoHandle token(rawToken);
+
+ DWORD len = 0;
+ ok = ::GetTokenInformation(token, TokenUser, nullptr, len, &len);
+ DWORD win32Error = ::GetLastError();
+ if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
+ return HRESULT_FROM_WIN32(win32Error);
+ }
+
+ auto tokenUserBuf = MakeUnique<BYTE[]>(len);
+ TOKEN_USER& tokenUser = *reinterpret_cast<TOKEN_USER*>(tokenUserBuf.get());
+ ok = ::GetTokenInformation(token, TokenUser, tokenUserBuf.get(), len, &len);
+ if (!ok) {
+ return HRESULT_FROM_WIN32(::GetLastError());
+ }
+
+ len = 0;
+ ok = ::GetTokenInformation(token, TokenPrimaryGroup, nullptr, len, &len);
+ win32Error = ::GetLastError();
+ if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
+ return HRESULT_FROM_WIN32(win32Error);
+ }
+
+ auto tokenPrimaryGroupBuf = MakeUnique<BYTE[]>(len);
+ TOKEN_PRIMARY_GROUP& tokenPrimaryGroup =
+ *reinterpret_cast<TOKEN_PRIMARY_GROUP*>(tokenPrimaryGroupBuf.get());
+ ok = ::GetTokenInformation(token, TokenPrimaryGroup, tokenPrimaryGroupBuf.get(),
+ len, &len);
+ if (!ok) {
+ return HRESULT_FROM_WIN32(::GetLastError());
+ }
+
+ SECURITY_DESCRIPTOR sd;
+ if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
+ return HRESULT_FROM_WIN32(::GetLastError());
+ }
+
+ // Grant access to SYSTEM, Administrators, and the user.
+ EXPLICIT_ACCESS entries[] = {
+ {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
+ {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_NAME, TRUSTEE_IS_USER,
+ L"SYSTEM"}},
+ {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
+ {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_NAME, TRUSTEE_IS_WELL_KNOWN_GROUP,
+ L"ADMINISTRATORS"}},
+ {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
+ {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
+ reinterpret_cast<LPWSTR>(tokenUser.User.Sid)}}
+ };
+
+ PACL rawDacl = nullptr;
+ win32Error = ::SetEntriesInAcl(ArrayLength(entries), entries, nullptr,
+ &rawDacl);
+ if (win32Error != ERROR_SUCCESS) {
+ return HRESULT_FROM_WIN32(win32Error);
+ }
+
+ UniquePtr<ACL, LocalFreeDeleter> dacl(rawDacl);
+
+ if (!::SetSecurityDescriptorDacl(&sd, TRUE, dacl.get(), FALSE)) {
+ return HRESULT_FROM_WIN32(::GetLastError());
+ }
+
+ if (!::SetSecurityDescriptorOwner(&sd, tokenUser.User.Sid, FALSE)) {
+ return HRESULT_FROM_WIN32(::GetLastError());
+ }
+
+ if (!::SetSecurityDescriptorGroup(&sd, tokenPrimaryGroup.PrimaryGroup, FALSE)) {
+ return HRESULT_FROM_WIN32(::GetLastError());
+ }
+
+ return ::CoInitializeSecurity(&sd, -1, nullptr, nullptr,
+ RPC_C_AUTHN_LEVEL_DEFAULT,
+ RPC_C_IMP_LEVEL_IDENTIFY, nullptr, EOAC_NONE,
+ nullptr);
+}
+
+} // namespace mscom
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/MainThreadRuntime.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_mscom_MainThreadRuntime_h
+#define mozilla_mscom_MainThreadRuntime_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/mscom/COMApartmentRegion.h"
+
+namespace mozilla {
+namespace mscom {
+
+class MOZ_NON_TEMPORARY_CLASS MainThreadRuntime
+{
+public:
+ MainThreadRuntime();
+
+ explicit operator bool() const
+ {
+ return mStaRegion.IsValidOutermost() && SUCCEEDED(mInitResult);
+ }
+
+ MainThreadRuntime(MainThreadRuntime&) = delete;
+ MainThreadRuntime(MainThreadRuntime&&) = delete;
+ MainThreadRuntime& operator=(MainThreadRuntime&) = delete;
+ MainThreadRuntime& operator=(MainThreadRuntime&&) = delete;
+
+private:
+ HRESULT InitializeSecurity();
+
+ STARegion mStaRegion;
+ HRESULT mInitResult;
+};
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_MainThreadRuntime_h
+
--- a/ipc/mscom/moz.build
+++ b/ipc/mscom/moz.build
@@ -1,18 +1,20 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS.mozilla.mscom += [
'COMApartmentRegion.h',
+ 'MainThreadRuntime.h',
'Utils.h',
]
UNIFIED_SOURCES += [
+ 'MainThreadRuntime.cpp',
'Utils.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'