Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(249)

Side by Side Diff: recovery/client/google_update_recovery.cc

Issue 624713003: Keep only base/extractor.[cc|h]. (Closed) Base URL: https://chromium.googlesource.com/external/omaha.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 }
OLDNEW
« no previous file with comments | « recovery/client/google_update_recovery.h ('k') | recovery/client/google_update_recovery_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698