1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/updater/win/util.h"
6
7 #include <aclapi.h>
8 #include <shlobj.h>
9 #include <windows.h>
10 #include <wtsapi32.h>
11
12 #include "base/command_line.h"
13 #include "base/files/file_path.h"
14 #include "base/guid.h"
15 #include "base/logging.h"
16 #include "base/numerics/ranges.h"
17 #include "base/process/process_iterator.h"
18 #include "base/scoped_native_library.h"
19 #include "base/strings/strcat.h"
20 #include "base/strings/string16.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/win/registry.h"
23 #include "chrome/updater/constants.h"
24 #include "chrome/updater/win/constants.h"
25 #include "chrome/updater/win/user_info.h"
26
27 namespace updater {
28
29 namespace {
30
31 // The number of iterations to poll if a process is stopped correctly.
32 const unsigned int kMaxProcessQueryIterations = 50;
33
34 // The sleep time in ms between each poll.
35 const unsigned int kProcessQueryWaitTimeMs = 100;
36
37 } // namespace
38
39 NamedObjectAttributes::NamedObjectAttributes() = default;
40 NamedObjectAttributes::~NamedObjectAttributes() = default;
41
HRESULTFromLastError()42 HRESULT HRESULTFromLastError() {
43 const auto error_code = ::GetLastError();
44 return (error_code != NO_ERROR) ? HRESULT_FROM_WIN32(error_code) : E_FAIL;
45 }
46
IsProcessRunning(const wchar_t * executable)47 bool IsProcessRunning(const wchar_t* executable) {
48 base::NamedProcessIterator iter(executable, nullptr);
49 const base::ProcessEntry* entry = iter.NextProcessEntry();
50 return entry != nullptr;
51 }
52
WaitForProcessesStopped(const wchar_t * executable)53 bool WaitForProcessesStopped(const wchar_t* executable) {
54 DCHECK(executable);
55 VLOG(1) << "Wait for processes '" << executable << "'.";
56
57 // Wait until the process is completely stopped.
58 for (unsigned int iteration = 0; iteration < kMaxProcessQueryIterations;
59 ++iteration) {
60 if (!IsProcessRunning(executable))
61 return true;
62 ::Sleep(kProcessQueryWaitTimeMs);
63 }
64
65 // The process didn't terminate.
66 LOG(ERROR) << "Cannot stop process '" << executable << "', timeout.";
67 return false;
68 }
69
70 // This sets up COM security to allow NetworkService, LocalService, and System
71 // to call back into the process. It is largely inspired by
72 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa378987.aspx
73 // static
InitializeCOMSecurity()74 bool InitializeCOMSecurity() {
75 // Create the security descriptor explicitly as follows because
76 // CoInitializeSecurity() will not accept the relative security descriptors
77 // returned by ConvertStringSecurityDescriptorToSecurityDescriptor().
78 const size_t kSidCount = 5;
79 uint64_t* sids[kSidCount][(SECURITY_MAX_SID_SIZE + sizeof(uint64_t) - 1) /
80 sizeof(uint64_t)] = {
81 {}, {}, {}, {}, {},
82 };
83
84 // These are ordered by most interesting ones to try first.
85 WELL_KNOWN_SID_TYPE sid_types[kSidCount] = {
86 WinBuiltinAdministratorsSid, // administrator group security identifier
87 WinLocalServiceSid, // local service security identifier
88 WinNetworkServiceSid, // network service security identifier
89 WinSelfSid, // personal account security identifier
90 WinLocalSystemSid, // local system security identifier
91 };
92
93 // This creates a security descriptor that is equivalent to the following
94 // security descriptor definition language (SDDL) string:
95 // O:BAG:BAD:(A;;0x1;;;LS)(A;;0x1;;;NS)(A;;0x1;;;PS)
96 // (A;;0x1;;;SY)(A;;0x1;;;BA)
97
98 // Initialize the security descriptor.
99 SECURITY_DESCRIPTOR security_desc = {};
100 if (!::InitializeSecurityDescriptor(&security_desc,
101 SECURITY_DESCRIPTOR_REVISION))
102 return false;
103
104 DCHECK_EQ(kSidCount, base::size(sids));
105 DCHECK_EQ(kSidCount, base::size(sid_types));
106 for (size_t i = 0; i < kSidCount; ++i) {
107 DWORD sid_bytes = sizeof(sids[i]);
108 if (!::CreateWellKnownSid(sid_types[i], nullptr, sids[i], &sid_bytes))
109 return false;
110 }
111
112 // Setup the access control entries (ACE) for COM. You may need to modify
113 // the access permissions for your application. COM_RIGHTS_EXECUTE and
114 // COM_RIGHTS_EXECUTE_LOCAL are the minimum access rights required.
115 EXPLICIT_ACCESS explicit_access[kSidCount] = {};
116 DCHECK_EQ(kSidCount, base::size(sids));
117 DCHECK_EQ(kSidCount, base::size(explicit_access));
118 for (size_t i = 0; i < kSidCount; ++i) {
119 explicit_access[i].grfAccessPermissions =
120 COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL;
121 explicit_access[i].grfAccessMode = SET_ACCESS;
122 explicit_access[i].grfInheritance = NO_INHERITANCE;
123 explicit_access[i].Trustee.pMultipleTrustee = nullptr;
124 explicit_access[i].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
125 explicit_access[i].Trustee.TrusteeForm = TRUSTEE_IS_SID;
126 explicit_access[i].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
127 explicit_access[i].Trustee.ptstrName = reinterpret_cast<LPTSTR>(sids[i]);
128 }
129
130 // Create an access control list (ACL) using this ACE list, if this succeeds
131 // make sure to ::LocalFree(acl).
132 ACL* acl = nullptr;
133 DWORD acl_result = ::SetEntriesInAcl(base::size(explicit_access),
134 explicit_access, nullptr, &acl);
135 if (acl_result != ERROR_SUCCESS || acl == nullptr)
136 return false;
137
138 HRESULT hr = E_FAIL;
139
140 // Set the security descriptor owner and group to Administrators and set the
141 // discretionary access control list (DACL) to the ACL.
142 if (::SetSecurityDescriptorOwner(&security_desc, sids[0], FALSE) &&
143 ::SetSecurityDescriptorGroup(&security_desc, sids[0], FALSE) &&
144 ::SetSecurityDescriptorDacl(&security_desc, TRUE, acl, FALSE)) {
145 // Initialize COM. You may need to modify the parameters of
146 // CoInitializeSecurity() for your application. Note that an
147 // explicit security descriptor is being passed down.
148 hr = ::CoInitializeSecurity(
149 &security_desc, -1, nullptr, nullptr, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
150 RPC_C_IMP_LEVEL_IDENTIFY, nullptr,
151 EOAC_DISABLE_AAA | EOAC_NO_CUSTOM_MARSHAL, nullptr);
152 }
153
154 ::LocalFree(acl);
155 return SUCCEEDED(hr);
156 }
157
GetModuleHandleFromAddress(void * address)158 HMODULE GetModuleHandleFromAddress(void* address) {
159 MEMORY_BASIC_INFORMATION mbi = {0};
160 size_t result = ::VirtualQuery(address, &mbi, sizeof(mbi));
161 DCHECK_EQ(result, sizeof(mbi));
162 return static_cast<HMODULE>(mbi.AllocationBase);
163 }
164
GetCurrentModuleHandle()165 HMODULE GetCurrentModuleHandle() {
166 return GetModuleHandleFromAddress(
167 reinterpret_cast<void*>(&GetCurrentModuleHandle));
168 }
169
170 // The event name saved to the environment variable does not contain the
171 // decoration added by GetNamedObjectAttributes.
CreateUniqueEventInEnvironment(const base::string16 & var_name,bool is_machine,HANDLE * unique_event)172 HRESULT CreateUniqueEventInEnvironment(const base::string16& var_name,
173 bool is_machine,
174 HANDLE* unique_event) {
175 DCHECK(unique_event);
176
177 const base::string16 event_name = base::ASCIIToUTF16(base::GenerateGUID());
178 NamedObjectAttributes attr;
179 GetNamedObjectAttributes(event_name.c_str(), is_machine, &attr);
180
181 HRESULT hr = CreateEvent(&attr, unique_event);
182 if (FAILED(hr))
183 return hr;
184
185 if (!::SetEnvironmentVariable(var_name.c_str(), event_name.c_str())) {
186 DWORD error = ::GetLastError();
187 return HRESULT_FROM_WIN32(error);
188 }
189
190 return S_OK;
191 }
192
OpenUniqueEventFromEnvironment(const base::string16 & var_name,bool is_machine,HANDLE * unique_event)193 HRESULT OpenUniqueEventFromEnvironment(const base::string16& var_name,
194 bool is_machine,
195 HANDLE* unique_event) {
196 DCHECK(unique_event);
197
198 base::char16 event_name[MAX_PATH] = {0};
199 if (!::GetEnvironmentVariable(var_name.c_str(), event_name,
200 base::size(event_name))) {
201 DWORD error = ::GetLastError();
202 return HRESULT_FROM_WIN32(error);
203 }
204
205 NamedObjectAttributes attr;
206 GetNamedObjectAttributes(event_name, is_machine, &attr);
207 *unique_event = ::OpenEvent(EVENT_ALL_ACCESS, false, attr.name.c_str());
208
209 if (!*unique_event) {
210 DWORD error = ::GetLastError();
211 return HRESULT_FROM_WIN32(error);
212 }
213
214 return S_OK;
215 }
216
CreateEvent(NamedObjectAttributes * event_attr,HANDLE * event_handle)217 HRESULT CreateEvent(NamedObjectAttributes* event_attr, HANDLE* event_handle) {
218 DCHECK(event_handle);
219 DCHECK(event_attr);
220 DCHECK(!event_attr->name.empty());
221 *event_handle = ::CreateEvent(&event_attr->sa,
222 true, // manual reset
223 false, // not signaled
224 event_attr->name.c_str());
225
226 if (!*event_handle) {
227 DWORD error = ::GetLastError();
228 return HRESULT_FROM_WIN32(error);
229 }
230
231 return S_OK;
232 }
233
GetNamedObjectAttributes(const base::char16 * base_name,bool is_machine,NamedObjectAttributes * attr)234 void GetNamedObjectAttributes(const base::char16* base_name,
235 bool is_machine,
236 NamedObjectAttributes* attr) {
237 DCHECK(base_name);
238 DCHECK(attr);
239
240 attr->name = kGlobalPrefix;
241
242 if (!is_machine) {
243 base::string16 user_sid;
244 GetProcessUser(nullptr, nullptr, &user_sid);
245 attr->name += user_sid;
246 GetCurrentUserDefaultSecurityAttributes(&attr->sa);
247 } else {
248 // Grant access to administrators and system.
249 GetAdminDaclSecurityAttributes(&attr->sa, GENERIC_ALL);
250 }
251
252 attr->name += base_name;
253 }
254
GetCurrentUserDefaultSecurityAttributes(CSecurityAttributes * sec_attr)255 bool GetCurrentUserDefaultSecurityAttributes(CSecurityAttributes* sec_attr) {
256 DCHECK(sec_attr);
257
258 CAccessToken token;
259 if (!token.GetProcessToken(TOKEN_QUERY))
260 return false;
261
262 CSecurityDesc security_desc;
263 CSid sid_owner;
264 if (!token.GetOwner(&sid_owner))
265 return false;
266
267 security_desc.SetOwner(sid_owner);
268 CSid sid_group;
269 if (!token.GetPrimaryGroup(&sid_group))
270 return false;
271
272 security_desc.SetGroup(sid_group);
273
274 CDacl dacl;
275 if (!token.GetDefaultDacl(&dacl))
276 return false;
277
278 CSid sid_user;
279 if (!token.GetUser(&sid_user))
280 return false;
281 if (!dacl.AddAllowedAce(sid_user, GENERIC_ALL))
282 return false;
283
284 security_desc.SetDacl(dacl);
285 sec_attr->Set(security_desc);
286
287 return true;
288 }
289
GetAdminDaclSecurityDescriptor(CSecurityDesc * sd,ACCESS_MASK accessmask)290 void GetAdminDaclSecurityDescriptor(CSecurityDesc* sd, ACCESS_MASK accessmask) {
291 DCHECK(sd);
292
293 CDacl dacl;
294 dacl.AddAllowedAce(Sids::System(), accessmask);
295 dacl.AddAllowedAce(Sids::Admins(), accessmask);
296
297 sd->SetOwner(Sids::Admins());
298 sd->SetGroup(Sids::Admins());
299 sd->SetDacl(dacl);
300 sd->MakeAbsolute();
301 }
302
GetAdminDaclSecurityAttributes(CSecurityAttributes * sec_attr,ACCESS_MASK accessmask)303 void GetAdminDaclSecurityAttributes(CSecurityAttributes* sec_attr,
304 ACCESS_MASK accessmask) {
305 DCHECK(sec_attr);
306 CSecurityDesc sd;
307 GetAdminDaclSecurityDescriptor(&sd, accessmask);
308 sec_attr->Set(sd);
309 }
310
GetRegistryKeyClientsUpdater()311 base::string16 GetRegistryKeyClientsUpdater() {
312 return base::ASCIIToUTF16(base::StrCat({CLIENTS_KEY, kUpdaterAppId}));
313 }
314
GetRegistryKeyClientStateUpdater()315 base::string16 GetRegistryKeyClientStateUpdater() {
316 return base::ASCIIToUTF16(base::StrCat({CLIENT_STATE_KEY, kUpdaterAppId}));
317 }
318
GetDownloadProgress(int64_t downloaded_bytes,int64_t total_bytes)319 int GetDownloadProgress(int64_t downloaded_bytes, int64_t total_bytes) {
320 if (downloaded_bytes == -1 || total_bytes == -1 || total_bytes == 0)
321 return -1;
322 DCHECK_LE(downloaded_bytes, total_bytes);
323 return 100 *
324 base::ClampToRange(double{downloaded_bytes} / total_bytes, 0.0, 1.0);
325 }
326
327 // Reads the installer progress from the registry value at:
328 // {HKLM|HKCU}\Software\Google\Update\ClientState\<appid>\InstallerProgress.
GetInstallerProgress(const std::string & app_id)329 int GetInstallerProgress(const std::string& app_id) {
330 base::string16 subkey;
331 if (!base::UTF8ToUTF16(app_id.c_str(), app_id.size(), &subkey)) {
332 return -1;
333 }
334 constexpr REGSAM kRegSam = KEY_READ | KEY_WOW64_32KEY;
335 base::win::RegKey key(HKEY_CURRENT_USER,
336 base::ASCIIToUTF16(CLIENT_STATE_KEY).c_str(), kRegSam);
337 if (key.OpenKey(subkey.c_str(), kRegSam) != ERROR_SUCCESS) {
338 return -1;
339 }
340 DWORD progress = 0;
341 if (key.ReadValueDW(kRegistryValueInstallerProgress, &progress) !=
342 ERROR_SUCCESS) {
343 return -1;
344 }
345 return base::ClampToRange(progress, DWORD{0}, DWORD{100});
346 }
347
DeleteInstallerProgress(const std::string & app_id)348 bool DeleteInstallerProgress(const std::string& app_id) {
349 base::string16 subkey;
350 if (!base::UTF8ToUTF16(app_id.c_str(), app_id.size(), &subkey)) {
351 return false;
352 }
353 constexpr REGSAM kRegSam = KEY_SET_VALUE | KEY_WOW64_32KEY;
354 base::win::RegKey key(HKEY_CURRENT_USER,
355 base::ASCIIToUTF16(CLIENT_STATE_KEY).c_str(), kRegSam);
356 if (key.OpenKey(subkey.c_str(), kRegSam) != ERROR_SUCCESS) {
357 return false;
358 }
359
360 return key.DeleteValue(kRegistryValueInstallerProgress) == ERROR_SUCCESS;
361 }
362
GetUserTokenFromCurrentSessionId()363 base::win::ScopedHandle GetUserTokenFromCurrentSessionId() {
364 base::win::ScopedHandle token_handle;
365
366 DWORD bytes_returned = 0;
367 DWORD* session_id_ptr = nullptr;
368 if (!::WTSQuerySessionInformation(
369 WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSSessionId,
370 reinterpret_cast<LPTSTR*>(&session_id_ptr), &bytes_returned)) {
371 PLOG(ERROR) << "WTSQuerySessionInformation failed.";
372 return token_handle;
373 }
374
375 DCHECK_EQ(bytes_returned, sizeof(*session_id_ptr));
376 DWORD session_id = *session_id_ptr;
377 ::WTSFreeMemory(session_id_ptr);
378 DVLOG(1) << "::WTSQuerySessionInformation session id: " << session_id;
379
380 HANDLE token_handle_raw = nullptr;
381 if (!::WTSQueryUserToken(session_id, &token_handle_raw)) {
382 PLOG(ERROR) << "WTSQueryUserToken failed";
383 return token_handle;
384 }
385
386 token_handle.Set(token_handle_raw);
387 return token_handle;
388 }
389
390 } // namespace updater
391