| OLD | NEW |
| (Empty) |
| 1 // Copyright 2007-2010 Google Inc. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 // you may not use this file except in compliance with the License. | |
| 5 // You may obtain a copy of the License at | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 // ======================================================================== | |
| 15 | |
| 16 // | |
| 17 // Implementation of the Google Update recovery mechanism to be included in | |
| 18 // Google apps. | |
| 19 | |
| 20 #include "omaha/recovery/client/google_update_recovery.h" | |
| 21 #include <shellapi.h> | |
| 22 #include <wininet.h> | |
| 23 #include <atlstr.h> | |
| 24 #include "omaha/base/const_addresses.h" | |
| 25 #include "omaha/base/signaturevalidator.h" | |
| 26 #include "omaha/common/const_group_policy.h" | |
| 27 #include "omaha/third_party/smartany/scoped_any.h" | |
| 28 | |
| 29 namespace omaha { | |
| 30 | |
| 31 namespace { | |
| 32 | |
| 33 const int kRollbackWindowDays = 100; | |
| 34 | |
| 35 const TCHAR* const kMachineRepairArgs = _T("/recover /machine"); | |
| 36 const TCHAR* const kUserRepairArgs = _T("/recover"); | |
| 37 | |
| 38 // TODO(omaha): Add a Code Red lib version that is manually updated when | |
| 39 // we check in the lib. | |
| 40 const TCHAR* const kQueryStringFormat = | |
| 41 _T("?appid=%s&appversion=%s&applang=%s&machine=%u") | |
| 42 _T("&version=%s&osversion=%s&servicepack=%s"); | |
| 43 | |
| 44 // Information about where to obtain Omaha info. | |
| 45 // This must never change in Omaha. | |
| 46 const TCHAR* const kRegValueProductVersion = _T("pv"); | |
| 47 const TCHAR* const kRelativeGoopdateRegPath = _T("Software\\Google\\Update\\"); | |
| 48 const TCHAR* const kRelativeClientsGoopdateRegPath = | |
| 49 _T("Software\\Google\\Update\\Clients\\") | |
| 50 _T("{430FD4D0-B729-4F61-AA34-91526481799D}"); | |
| 51 | |
| 52 // The UpdateDev registry value to override the Code Red url. | |
| 53 const TCHAR* const kRegValueNameCodeRedUrl = _T("CodeRedUrl"); | |
| 54 | |
| 55 // Starts another process via ::CreateProcess. | |
| 56 HRESULT StartProcess(const TCHAR* process_name, TCHAR* command_line) { | |
| 57 if (!process_name && !command_line) { | |
| 58 return E_INVALIDARG; | |
| 59 } | |
| 60 | |
| 61 PROCESS_INFORMATION pi = {0}; | |
| 62 STARTUPINFO si = {sizeof(si), 0}; | |
| 63 | |
| 64 // Feedback cursor is off while the process is starting. | |
| 65 si.dwFlags = STARTF_FORCEOFFFEEDBACK; | |
| 66 | |
| 67 BOOL success = ::CreateProcess( | |
| 68 process_name, // Module name | |
| 69 command_line, // Command line | |
| 70 NULL, // Process handle not inheritable | |
| 71 NULL, // Thread handle not inheritable | |
| 72 FALSE, // Set handle inheritance to FALSE | |
| 73 0, // No creation flags | |
| 74 NULL, // Use parent's environment block | |
| 75 NULL, // Use parent's starting directory | |
| 76 &si, // Pointer to STARTUPINFO structure | |
| 77 &pi); // Pointer to PROCESS_INFORMATION structure | |
| 78 | |
| 79 if (!success) { | |
| 80 return HRESULT_FROM_WIN32(::GetLastError()); | |
| 81 } | |
| 82 | |
| 83 ::CloseHandle(pi.hProcess); | |
| 84 ::CloseHandle(pi.hThread); | |
| 85 | |
| 86 return S_OK; | |
| 87 } | |
| 88 | |
| 89 // Check if a string starts with another string. Case-sensitive. | |
| 90 bool StringStartsWith(const TCHAR *str, const TCHAR *start_str) { | |
| 91 if (!start_str || !str) { | |
| 92 return false; | |
| 93 } | |
| 94 | |
| 95 while (0 != *str) { | |
| 96 // Check for matching characters | |
| 97 TCHAR c1 = *str; | |
| 98 TCHAR c2 = *start_str; | |
| 99 | |
| 100 // Reached the end of start_str? | |
| 101 if (0 == c2) | |
| 102 return true; | |
| 103 | |
| 104 if (c1 != c2) | |
| 105 return false; | |
| 106 | |
| 107 ++str; | |
| 108 ++start_str; | |
| 109 } | |
| 110 | |
| 111 // If str is shorter than start_str, no match. If equal size, match. | |
| 112 return 0 == *start_str; | |
| 113 } | |
| 114 | |
| 115 // Escape and unescape strings (shlwapi-based implementation). | |
| 116 // The intended usage for these APIs is escaping strings to make up | |
| 117 // URLs, for example building query strings. | |
| 118 // | |
| 119 // Pass false to the flag segment_only to escape the url. This will not | |
| 120 // cause the conversion of the # (%23), ? (%3F), and / (%2F) characters. | |
| 121 | |
| 122 // Characters that must be encoded include any characters that have no | |
| 123 // corresponding graphic character in the US-ASCII coded character | |
| 124 // set (hexadecimal 80-FF, which are not used in the US-ASCII coded character | |
| 125 // set, and hexadecimal 00-1F and 7F, which are control characters), | |
| 126 // blank spaces, "%" (which is used to encode other characters), | |
| 127 // and unsafe characters (<, >, ", #, {, }, |, \, ^, ~, [, ], and '). | |
| 128 // | |
| 129 // The input and output strings can't be longer than INTERNET_MAX_URL_LENGTH | |
| 130 | |
| 131 HRESULT StringEscape(const CString& str_in, | |
| 132 bool segment_only, | |
| 133 CString* escaped_string) { | |
| 134 if (!escaped_string) { | |
| 135 return E_INVALIDARG; | |
| 136 } | |
| 137 | |
| 138 DWORD buf_len = INTERNET_MAX_URL_LENGTH + 1; | |
| 139 HRESULT hr = ::UrlEscape(str_in, | |
| 140 escaped_string->GetBufferSetLength(buf_len), | |
| 141 &buf_len, | |
| 142 segment_only ? | |
| 143 URL_ESCAPE_PERCENT | URL_ESCAPE_SEGMENT_ONLY : | |
| 144 URL_ESCAPE_PERCENT); | |
| 145 if (SUCCEEDED(hr)) { | |
| 146 escaped_string->ReleaseBuffer(); | |
| 147 } | |
| 148 return hr; | |
| 149 } | |
| 150 | |
| 151 // Gets the temporary files directory for the current user. | |
| 152 // The directory returned may not exist. | |
| 153 // The returned path ends with a '\'. | |
| 154 // Fails if the path is longer than MAX_PATH. | |
| 155 HRESULT GetTempDir(CString* temp_path) { | |
| 156 if (!temp_path) { | |
| 157 return E_INVALIDARG; | |
| 158 } | |
| 159 | |
| 160 temp_path->Empty(); | |
| 161 | |
| 162 TCHAR buffer[MAX_PATH] = {0}; | |
| 163 DWORD num_chars = ::GetTempPath(MAX_PATH, buffer); | |
| 164 if (!num_chars) { | |
| 165 return HRESULT_FROM_WIN32(::GetLastError()); | |
| 166 } else if (num_chars >= MAX_PATH) { | |
| 167 return E_FAIL; | |
| 168 } | |
| 169 | |
| 170 *temp_path = buffer; | |
| 171 return S_OK; | |
| 172 } | |
| 173 | |
| 174 // Creates the specified directory. | |
| 175 HRESULT CreateDir(const CString& dir) { | |
| 176 if (!::CreateDirectory(dir, NULL)) { | |
| 177 DWORD error = ::GetLastError(); | |
| 178 if (ERROR_FILE_EXISTS != error && ERROR_ALREADY_EXISTS != error) { | |
| 179 return HRESULT_FROM_WIN32(error); | |
| 180 } | |
| 181 } | |
| 182 return S_OK; | |
| 183 } | |
| 184 | |
| 185 HRESULT GetAndCreateTempDir(CString* temp_path) { | |
| 186 if (!temp_path) { | |
| 187 return E_INVALIDARG; | |
| 188 } | |
| 189 | |
| 190 HRESULT hr = GetTempDir(temp_path); | |
| 191 if (FAILED(hr)) { | |
| 192 return hr; | |
| 193 } | |
| 194 if (temp_path->IsEmpty()) { | |
| 195 return E_FAIL; | |
| 196 } | |
| 197 | |
| 198 // Create this dir if it doesn't already exist. | |
| 199 return CreateDir(*temp_path); | |
| 200 } | |
| 201 | |
| 202 | |
| 203 // Create a unique temporary file and returns the full path. | |
| 204 HRESULT CreateUniqueTempFile(const CString& user_temp_dir, | |
| 205 CString* unique_temp_file_path) { | |
| 206 if (user_temp_dir.IsEmpty() || !unique_temp_file_path) { | |
| 207 return E_INVALIDARG; | |
| 208 } | |
| 209 | |
| 210 TCHAR unique_temp_filename[MAX_PATH] = {0}; | |
| 211 if (!::GetTempFileName(user_temp_dir, | |
| 212 _T("GUR"), // prefix | |
| 213 0, // form a unique filename | |
| 214 unique_temp_filename)) { | |
| 215 return HRESULT_FROM_WIN32(::GetLastError()); | |
| 216 } | |
| 217 | |
| 218 *unique_temp_file_path = unique_temp_filename; | |
| 219 if (unique_temp_file_path->IsEmpty()) { | |
| 220 return E_FAIL; | |
| 221 } | |
| 222 | |
| 223 return S_OK; | |
| 224 } | |
| 225 | |
| 226 // Obtains the OS version and service pack. | |
| 227 HRESULT GetOSInfo(CString* os_version, CString* service_pack) { | |
| 228 if (!os_version || !service_pack) { | |
| 229 return E_INVALIDARG; | |
| 230 } | |
| 231 | |
| 232 OSVERSIONINFO os_version_info = { 0 }; | |
| 233 os_version_info.dwOSVersionInfoSize = sizeof(os_version_info); | |
| 234 if (!::GetVersionEx(&os_version_info)) { | |
| 235 HRESULT hr = HRESULT_FROM_WIN32(::GetLastError()); | |
| 236 return hr; | |
| 237 } else { | |
| 238 os_version->Format(_T("%d.%d"), | |
| 239 os_version_info.dwMajorVersion, | |
| 240 os_version_info.dwMinorVersion); | |
| 241 *service_pack = os_version_info.szCSDVersion; | |
| 242 } | |
| 243 return S_OK; | |
| 244 } | |
| 245 | |
| 246 // Reads the specified string value from the specified registry key. | |
| 247 // Only supports value types REG_SZ and REG_EXPAND_SZ. | |
| 248 // REG_EXPAND_SZ strings are not expanded. | |
| 249 HRESULT GetRegStringValue(bool is_machine_key, | |
| 250 const CString& relative_key_path, | |
| 251 const CString& value_name, | |
| 252 CString* value) { | |
| 253 if (!value) { | |
| 254 return E_INVALIDARG; | |
| 255 } | |
| 256 | |
| 257 value->Empty(); | |
| 258 HKEY root_key = is_machine_key ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; | |
| 259 HKEY key = NULL; | |
| 260 LONG res = ::RegOpenKeyEx(root_key, relative_key_path, 0, KEY_READ, &key); | |
| 261 if (res != ERROR_SUCCESS) { | |
| 262 return HRESULT_FROM_WIN32(res); | |
| 263 } | |
| 264 | |
| 265 // First get the size of the string buffer. | |
| 266 DWORD type = 0; | |
| 267 DWORD byte_count = 0; | |
| 268 res = ::RegQueryValueEx(key, value_name, NULL, &type, NULL, &byte_count); | |
| 269 if (ERROR_SUCCESS != res) { | |
| 270 ::RegCloseKey(key); | |
| 271 return HRESULT_FROM_WIN32(res); | |
| 272 } | |
| 273 if ((type != REG_SZ && type != REG_EXPAND_SZ) || (0 == byte_count)) { | |
| 274 ::RegCloseKey(key); | |
| 275 return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | |
| 276 } | |
| 277 | |
| 278 CString local_value; | |
| 279 // GetBuffer throws when not able to allocate the requested buffer. | |
| 280 TCHAR* buffer = local_value.GetBuffer(byte_count / sizeof(TCHAR)); | |
| 281 res = ::RegQueryValueEx(key, | |
| 282 value_name, | |
| 283 NULL, | |
| 284 NULL, | |
| 285 reinterpret_cast<BYTE*>(buffer), | |
| 286 &byte_count); | |
| 287 ::RegCloseKey(key); | |
| 288 if (ERROR_SUCCESS == res) { | |
| 289 local_value.ReleaseBufferSetLength(byte_count / sizeof(TCHAR)); | |
| 290 *value = local_value; | |
| 291 } | |
| 292 | |
| 293 return HRESULT_FROM_WIN32(res); | |
| 294 } | |
| 295 | |
| 296 // Reads the specified DWORD value from the specified registry key. | |
| 297 // Only supports value types REG_DWORD. | |
| 298 // Assumes DWORD is sufficient buffer, which must be true for valid value type. | |
| 299 HRESULT GetRegDwordValue(bool is_machine_key, | |
| 300 const CString& relative_key_path, | |
| 301 const CString& value_name, | |
| 302 DWORD* value) { | |
| 303 if (!value) { | |
| 304 return E_INVALIDARG; | |
| 305 } | |
| 306 | |
| 307 HKEY root_key = is_machine_key ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; | |
| 308 HKEY key = NULL; | |
| 309 LONG res = ::RegOpenKeyEx(root_key, relative_key_path, 0, KEY_READ, &key); | |
| 310 if (res != ERROR_SUCCESS) { | |
| 311 return HRESULT_FROM_WIN32(res); | |
| 312 } | |
| 313 | |
| 314 DWORD type = 0; | |
| 315 DWORD byte_count = sizeof(*value); | |
| 316 res = ::RegQueryValueEx(key, | |
| 317 value_name, | |
| 318 NULL, | |
| 319 &type, | |
| 320 reinterpret_cast<BYTE*>(value), | |
| 321 &byte_count); | |
| 322 ::RegCloseKey(key); | |
| 323 if (ERROR_SUCCESS != res) { | |
| 324 return HRESULT_FROM_WIN32(res); | |
| 325 } | |
| 326 if ((type != REG_DWORD) || (0 == byte_count)) { | |
| 327 return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | |
| 328 } | |
| 329 | |
| 330 return S_OK; | |
| 331 } | |
| 332 | |
| 333 // Obtains information about the current Omaha installation. | |
| 334 // Attempts to obtain as much information as possible even if errors occur. | |
| 335 // Therefore, return values of GetRegStringValue are ignored. | |
| 336 HRESULT GetOmahaInformation(bool is_machine_app, | |
| 337 CString* omaha_version) { | |
| 338 if (!omaha_version) { | |
| 339 return E_INVALIDARG; | |
| 340 } | |
| 341 | |
| 342 if (FAILED(GetRegStringValue(is_machine_app, | |
| 343 kRelativeClientsGoopdateRegPath, | |
| 344 kRegValueProductVersion, | |
| 345 omaha_version))) { | |
| 346 *omaha_version = _T("0.0.0.0"); | |
| 347 } | |
| 348 | |
| 349 return S_OK; | |
| 350 } | |
| 351 | |
| 352 // Builds the query portion of the recovery url. | |
| 353 // This method obtains values necessary to build the query that are not provided | |
| 354 // as parameters. | |
| 355 // Attempts to build with as much information as possible even if errors occur. | |
| 356 HRESULT BuildUrlQueryPortion(const CString& app_guid, | |
| 357 const CString& app_version, | |
| 358 const CString& app_language, | |
| 359 bool is_machine_app, | |
| 360 CString* query) { | |
| 361 if (!query) { | |
| 362 return E_INVALIDARG; | |
| 363 } | |
| 364 | |
| 365 CString omaha_version; | |
| 366 GetOmahaInformation(is_machine_app, &omaha_version); | |
| 367 | |
| 368 CString os_version; | |
| 369 CString os_service_pack; | |
| 370 GetOSInfo(&os_version, &os_service_pack); | |
| 371 | |
| 372 // All parameters must be escaped individually before building the query. | |
| 373 CString app_guid_escaped; | |
| 374 CString app_version_escaped; | |
| 375 CString app_language_escaped; | |
| 376 CString omaha_version_escaped; | |
| 377 CString os_version_escaped; | |
| 378 CString os_service_pack_escaped; | |
| 379 StringEscape(app_guid, true, &app_guid_escaped); | |
| 380 StringEscape(app_version, true, &app_version_escaped); | |
| 381 StringEscape(app_language, true, &app_language_escaped); | |
| 382 StringEscape(omaha_version, true, &omaha_version_escaped); | |
| 383 StringEscape(os_version, true, &os_version_escaped); | |
| 384 StringEscape(os_service_pack, true, &os_service_pack_escaped); | |
| 385 | |
| 386 query->Format(kQueryStringFormat, | |
| 387 app_guid_escaped, | |
| 388 app_version_escaped, | |
| 389 app_language_escaped, | |
| 390 is_machine_app ? 1 : 0, | |
| 391 omaha_version_escaped, | |
| 392 os_version_escaped, | |
| 393 os_service_pack_escaped); | |
| 394 | |
| 395 return S_OK; | |
| 396 } | |
| 397 | |
| 398 // Returns the full path to save the downloaded file to. | |
| 399 // The path is based on a unique temporary filename to avoid a conflict | |
| 400 // between multiple apps downloading to the same location. | |
| 401 // The path to this file is also returned. The caller is responsible for | |
| 402 // deleting the temporary file after using the download target path. | |
| 403 // If it cannot create the unique directory, it attempts to use the user's | |
| 404 // temporary directory and a constant filename. | |
| 405 HRESULT GetDownloadTargetPath(CString* download_target_path, | |
| 406 CString* temp_file_path) { | |
| 407 if (!download_target_path || !temp_file_path) { | |
| 408 return E_INVALIDARG; | |
| 409 } | |
| 410 | |
| 411 CString user_temp_dir; | |
| 412 HRESULT hr = GetAndCreateTempDir(&user_temp_dir); | |
| 413 if (FAILED(hr)) { | |
| 414 return hr; | |
| 415 } | |
| 416 | |
| 417 hr = CreateUniqueTempFile(user_temp_dir, temp_file_path); | |
| 418 if (SUCCEEDED(hr) && !temp_file_path->IsEmpty()) { | |
| 419 *download_target_path = *temp_file_path; | |
| 420 // Ignore the return value. A .tmp filename is better than none. | |
| 421 download_target_path->Replace(_T(".tmp"), _T(".exe")); | |
| 422 } else { | |
| 423 // Try a static filename in the temp directory as a fallback. | |
| 424 *download_target_path = user_temp_dir + _T("GoogleUpdateSetup.exe"); | |
| 425 *temp_file_path = _T(""); | |
| 426 } | |
| 427 | |
| 428 return S_OK; | |
| 429 } | |
| 430 | |
| 431 HRESULT DownloadRepairFile(const CString& download_target_path, | |
| 432 const CString& app_guid, | |
| 433 const CString& app_version, | |
| 434 const CString& app_language, | |
| 435 bool is_machine_app, | |
| 436 DownloadCallback download_callback, | |
| 437 void* context) { | |
| 438 CString query; | |
| 439 BuildUrlQueryPortion(app_guid, | |
| 440 app_version, | |
| 441 app_language, | |
| 442 is_machine_app, | |
| 443 &query); | |
| 444 | |
| 445 CString url; | |
| 446 HRESULT hr = GetRegStringValue(true, | |
| 447 _T("SOFTWARE\\Google\\UpdateDev"), | |
| 448 kRegValueNameCodeRedUrl, | |
| 449 &url); | |
| 450 if (FAILED(hr)) { | |
| 451 url = omaha::kUrlCodeRedCheck; | |
| 452 } | |
| 453 | |
| 454 url += query; | |
| 455 | |
| 456 return download_callback(url, download_target_path, context); | |
| 457 } | |
| 458 | |
| 459 // Makes sure the path is enclosed with double quotation marks. | |
| 460 void EnclosePath(CString* path) { | |
| 461 if (path) { | |
| 462 return; | |
| 463 } | |
| 464 | |
| 465 if (!path->IsEmpty() && path->GetAt(0) != _T('"')) { | |
| 466 path->Insert(0, _T('"')); | |
| 467 path->AppendChar(_T('"')); | |
| 468 } | |
| 469 } | |
| 470 | |
| 471 HRESULT RunRepairFile(const CString& file_path, bool is_machine_app) { | |
| 472 const TCHAR* repair_file_args = is_machine_app ? kMachineRepairArgs : | |
| 473 kUserRepairArgs; | |
| 474 | |
| 475 CString command_line(file_path); | |
| 476 EnclosePath(&command_line); | |
| 477 command_line.AppendChar(_T(' ')); | |
| 478 command_line.Append(repair_file_args); | |
| 479 | |
| 480 return StartProcess(NULL, command_line.GetBuffer()); | |
| 481 } | |
| 482 | |
| 483 } // namespace | |
| 484 | |
| 485 // Verifies the file's integrity and that it is signed by Google. | |
| 486 // We cannot prevent rollback attacks by using a version because the client | |
| 487 // may not be able to determine the current version if the files and/or | |
| 488 // registry entries have been deleted/corrupted. | |
| 489 // Therefore, we check that the file was signed recently. | |
| 490 HRESULT VerifyFileSignature(const CString& filename) { | |
| 491 // Use Authenticode/WinVerifyTrust to verify the file. | |
| 492 // Allow the revocation check to use the network. | |
| 493 HRESULT hr = VerifySignature(filename, true); | |
| 494 if (FAILED(hr)) { | |
| 495 return hr; | |
| 496 } | |
| 497 | |
| 498 // Verify that there is a Google certificate. | |
| 499 if (!VerifySigneeIsGoogle(filename)) { | |
| 500 return CERT_E_CN_NO_MATCH; | |
| 501 } | |
| 502 | |
| 503 // Check that the file was signed recently to limit the window for | |
| 504 // rollback attacks. | |
| 505 return VerifyFileSignedWithinDays(filename, kRollbackWindowDays); | |
| 506 } | |
| 507 | |
| 508 // Verifies the file contains the special markup resource for repair files. | |
| 509 HRESULT VerifyRepairFileMarkup(const CString& filename) { | |
| 510 const TCHAR* kMarkupResourceName = MAKEINTRESOURCE(1); | |
| 511 const TCHAR* kMarkupResourceType = _T("GOOGLEUPDATEREPAIR"); | |
| 512 const DWORD kMarkupResourceExpectedValue = 1; | |
| 513 | |
| 514 scoped_library module(::LoadLibraryEx(filename, 0, LOAD_LIBRARY_AS_DATAFILE)); | |
| 515 if (!module) { | |
| 516 return HRESULT_FROM_WIN32(::GetLastError()); | |
| 517 } | |
| 518 | |
| 519 HRSRC resource(::FindResource(get(module), | |
| 520 kMarkupResourceName, | |
| 521 kMarkupResourceType)); | |
| 522 if (!resource) { | |
| 523 return HRESULT_FROM_WIN32(::GetLastError()); | |
| 524 } | |
| 525 | |
| 526 if (sizeof(kMarkupResourceExpectedValue) != | |
| 527 ::SizeofResource(get(module), resource)) { | |
| 528 return E_UNEXPECTED; | |
| 529 } | |
| 530 | |
| 531 HGLOBAL loaded_resource(::LoadResource(get(module), resource)); | |
| 532 if (!loaded_resource) { | |
| 533 return HRESULT_FROM_WIN32(::GetLastError()); | |
| 534 } | |
| 535 | |
| 536 const DWORD* value = static_cast<DWORD*>(::LockResource(loaded_resource)); | |
| 537 if (!value) { | |
| 538 return E_HANDLE; | |
| 539 } | |
| 540 | |
| 541 if (kMarkupResourceExpectedValue != *value) { | |
| 542 return E_UNEXPECTED; | |
| 543 } | |
| 544 | |
| 545 return S_OK; | |
| 546 } | |
| 547 | |
| 548 // Verifies the filename is not UNC name, the file exists, has a valid signature | |
| 549 // chain, is signed by Google, and contains the special markup resource for | |
| 550 // repair files. | |
| 551 HRESULT VerifyIsValidRepairFile(const CString& filename) { | |
| 552 // Make sure file exists. | |
| 553 if (!::PathFileExists(filename)) { | |
| 554 return HRESULT_FROM_WIN32(::GetLastError()); | |
| 555 } | |
| 556 | |
| 557 HRESULT hr = VerifyFileSignature(filename); | |
| 558 if (FAILED(hr)) { | |
| 559 return hr; | |
| 560 } | |
| 561 | |
| 562 return VerifyRepairFileMarkup(filename); | |
| 563 } | |
| 564 | |
| 565 } // namespace omaha | |
| 566 | |
| 567 // If a repair file is run, the file will not be deleted until reboot. Delete | |
| 568 // after reboot will only succeed when executed by an admin or LocalSystem. | |
| 569 // Returns HRESULT_FROM_WIN32(ERROR_ACCESS_DISABLED_BY_POLICY) if automatic | |
| 570 // update checks are disabled. | |
| 571 HRESULT FixGoogleUpdate(const TCHAR* app_guid, | |
| 572 const TCHAR* app_version, | |
| 573 const TCHAR* app_language, | |
| 574 bool is_machine_app, | |
| 575 DownloadCallback download_callback, | |
| 576 void* context) { | |
| 577 if (!app_guid || !app_version || !app_language || !download_callback) { | |
| 578 return E_INVALIDARG; | |
| 579 } | |
| 580 | |
| 581 DWORD update_check_period_override_minutes(UINT_MAX); | |
| 582 HRESULT hr = omaha::GetRegDwordValue( | |
| 583 true, | |
| 584 GOOPDATE_POLICIES_RELATIVE, | |
| 585 omaha::kRegValueAutoUpdateCheckPeriodOverrideMinutes, | |
| 586 &update_check_period_override_minutes); | |
| 587 if (SUCCEEDED(hr) && (0 == update_check_period_override_minutes)) { | |
| 588 return HRESULT_FROM_WIN32(ERROR_ACCESS_DISABLED_BY_POLICY); | |
| 589 } | |
| 590 | |
| 591 CString download_target_path; | |
| 592 CString temp_file_path; | |
| 593 hr = omaha::GetDownloadTargetPath(&download_target_path, &temp_file_path); | |
| 594 if (FAILED(hr)) { | |
| 595 return hr; | |
| 596 } | |
| 597 if (download_target_path.IsEmpty()) { | |
| 598 return E_FAIL; | |
| 599 } | |
| 600 | |
| 601 // After calling DownloadRepairFile, don't return until the repair file and | |
| 602 // temp file have been deleted. | |
| 603 hr = omaha::DownloadRepairFile(download_target_path, | |
| 604 app_guid, | |
| 605 app_version, | |
| 606 app_language, | |
| 607 is_machine_app, | |
| 608 download_callback, | |
| 609 context); | |
| 610 | |
| 611 if (SUCCEEDED(hr)) { | |
| 612 hr = omaha::VerifyIsValidRepairFile(download_target_path); | |
| 613 } | |
| 614 | |
| 615 if (FAILED(hr)) { | |
| 616 ::DeleteFile(download_target_path); | |
| 617 ::DeleteFile(temp_file_path); | |
| 618 return hr; | |
| 619 } | |
| 620 | |
| 621 hr = omaha::RunRepairFile(download_target_path, is_machine_app); | |
| 622 ::MoveFileEx(download_target_path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); | |
| 623 ::DeleteFile(temp_file_path); | |
| 624 | |
| 625 return hr; | |
| 626 } | |
| OLD | NEW |