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/. */
6 
7 #include "mozilla/mscom/MainThreadRuntime.h"
8 
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/Assertions.h"
11 #include "mozilla/RefPtr.h"
12 #include "mozilla/UniquePtr.h"
13 #include "mozilla/WindowsVersion.h"
14 #include "nsDebug.h"
15 #include "nsWindowsHelpers.h"
16 
17 #include <accctrl.h>
18 #include <aclapi.h>
19 #include <objbase.h>
20 #include <objidl.h>
21 
22 namespace {
23 
24 struct LocalFreeDeleter
25 {
operator ()__anone3311c310111::LocalFreeDeleter26   void operator()(void* aPtr)
27   {
28     ::LocalFree(aPtr);
29   }
30 };
31 
32 } // anonymous namespace
33 
34 namespace mozilla {
35 namespace mscom {
36 
MainThreadRuntime()37 MainThreadRuntime::MainThreadRuntime()
38   : mInitResult(E_UNEXPECTED)
39 {
40   // We must be the outermost COM initialization on this thread. The COM runtime
41   // cannot be configured once we start manipulating objects
42   MOZ_ASSERT(mStaRegion.IsValidOutermost());
43   if (NS_WARN_IF(!mStaRegion.IsValidOutermost())) {
44     return;
45   }
46 
47   // Windows XP doesn't support setting of the COM exception policy, so we'll
48   // just stop here in that case.
49   if (!IsVistaOrLater()) {
50     mInitResult = S_OK;
51     return;
52   }
53 
54   // We are required to initialize security in order to configure global options.
55   mInitResult = InitializeSecurity();
56   MOZ_ASSERT(SUCCEEDED(mInitResult));
57   if (FAILED(mInitResult)) {
58     return;
59   }
60 
61   RefPtr<IGlobalOptions> globalOpts;
62   mInitResult = ::CoCreateInstance(CLSID_GlobalOptions, nullptr,
63                                    CLSCTX_INPROC_SERVER, IID_IGlobalOptions,
64                                    (void**)getter_AddRefs(globalOpts));
65   MOZ_ASSERT(SUCCEEDED(mInitResult));
66   if (FAILED(mInitResult)) {
67     return;
68   }
69 
70   // Windows 7 has a policy that is even more strict. We should use that one
71   // whenever possible.
72   ULONG_PTR exceptionSetting = IsWin7OrLater() ?
73                                COMGLB_EXCEPTION_DONOT_HANDLE_ANY :
74                                COMGLB_EXCEPTION_DONOT_HANDLE;
75   mInitResult = globalOpts->Set(COMGLB_EXCEPTION_HANDLING, exceptionSetting);
76   MOZ_ASSERT(SUCCEEDED(mInitResult));
77 }
78 
79 HRESULT
InitializeSecurity()80 MainThreadRuntime::InitializeSecurity()
81 {
82   HANDLE rawToken = nullptr;
83   BOOL ok = ::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &rawToken);
84   if (!ok) {
85     return HRESULT_FROM_WIN32(::GetLastError());
86   }
87   nsAutoHandle token(rawToken);
88 
89   DWORD len = 0;
90   ok = ::GetTokenInformation(token, TokenUser, nullptr, len, &len);
91   DWORD win32Error = ::GetLastError();
92   if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
93     return HRESULT_FROM_WIN32(win32Error);
94   }
95 
96   auto tokenUserBuf = MakeUnique<BYTE[]>(len);
97   TOKEN_USER& tokenUser = *reinterpret_cast<TOKEN_USER*>(tokenUserBuf.get());
98   ok = ::GetTokenInformation(token, TokenUser, tokenUserBuf.get(), len, &len);
99   if (!ok) {
100     return HRESULT_FROM_WIN32(::GetLastError());
101   }
102 
103   len = 0;
104   ok = ::GetTokenInformation(token, TokenPrimaryGroup, nullptr, len, &len);
105   win32Error = ::GetLastError();
106   if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
107     return HRESULT_FROM_WIN32(win32Error);
108   }
109 
110   auto tokenPrimaryGroupBuf = MakeUnique<BYTE[]>(len);
111   TOKEN_PRIMARY_GROUP& tokenPrimaryGroup =
112     *reinterpret_cast<TOKEN_PRIMARY_GROUP*>(tokenPrimaryGroupBuf.get());
113   ok = ::GetTokenInformation(token, TokenPrimaryGroup, tokenPrimaryGroupBuf.get(),
114                              len, &len);
115   if (!ok) {
116     return HRESULT_FROM_WIN32(::GetLastError());
117   }
118 
119   SECURITY_DESCRIPTOR sd;
120   if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
121     return HRESULT_FROM_WIN32(::GetLastError());
122   }
123 
124   BYTE systemSid[SECURITY_MAX_SID_SIZE];
125   DWORD systemSidSize = sizeof(systemSid);
126   if (!::CreateWellKnownSid(WinLocalSystemSid, nullptr, systemSid,
127                             &systemSidSize)) {
128     return HRESULT_FROM_WIN32(::GetLastError());
129   }
130 
131   BYTE adminSid[SECURITY_MAX_SID_SIZE];
132   DWORD adminSidSize = sizeof(adminSid);
133   if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, nullptr, adminSid,
134                             &adminSidSize)) {
135     return HRESULT_FROM_WIN32(::GetLastError());
136   }
137 
138   // Grant access to SYSTEM, Administrators, and the user.
139   EXPLICIT_ACCESS entries[] = {
140     {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
141       {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
142        reinterpret_cast<LPWSTR>(systemSid)}},
143     {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
144       {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_WELL_KNOWN_GROUP,
145        reinterpret_cast<LPWSTR>(adminSid)}},
146     {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
147       {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
148        reinterpret_cast<LPWSTR>(tokenUser.User.Sid)}}
149   };
150 
151   PACL rawDacl = nullptr;
152   win32Error = ::SetEntriesInAcl(ArrayLength(entries), entries, nullptr,
153                                  &rawDacl);
154   if (win32Error != ERROR_SUCCESS) {
155     return HRESULT_FROM_WIN32(win32Error);
156   }
157 
158   UniquePtr<ACL, LocalFreeDeleter> dacl(rawDacl);
159 
160   if (!::SetSecurityDescriptorDacl(&sd, TRUE, dacl.get(), FALSE)) {
161     return HRESULT_FROM_WIN32(::GetLastError());
162   }
163 
164   if (!::SetSecurityDescriptorOwner(&sd, tokenUser.User.Sid, FALSE)) {
165     return HRESULT_FROM_WIN32(::GetLastError());
166   }
167 
168   if (!::SetSecurityDescriptorGroup(&sd, tokenPrimaryGroup.PrimaryGroup, FALSE)) {
169     return HRESULT_FROM_WIN32(::GetLastError());
170   }
171 
172   return ::CoInitializeSecurity(&sd, -1, nullptr, nullptr,
173                                 RPC_C_AUTHN_LEVEL_DEFAULT,
174                                 RPC_C_IMP_LEVEL_IDENTIFY, nullptr, EOAC_NONE,
175                                 nullptr);
176 }
177 
178 } // namespace mscom
179 } // namespace mozilla
180 
181