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