google_update_util.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
1// Copyright (c) 2012 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/installer/util/google_update_util.h" 6 7#include <algorithm> 8#include <map> 9#include <utility> 10#include <vector> 11 12#include "base/command_line.h" 13#include "base/environment.h" 14#include "base/file_util.h" 15#include "base/files/file_path.h" 16#include "base/logging.h" 17#include "base/process_util.h" 18#include "base/string16.h" 19#include "base/strings/string_split.h" 20#include "base/time.h" 21#include "base/win/registry.h" 22#include "base/win/scoped_handle.h" 23#include "chrome/installer/launcher_support/chrome_launcher_support.h" 24#include "chrome/installer/util/google_update_constants.h" 25#include "chrome/installer/util/google_update_settings.h" 26 27using base::win::RegKey; 28 29namespace google_update { 30 31namespace { 32 33const int kGoogleUpdateTimeoutMs = 20 * 1000; 34 35const char kEnvVariableUntrustedData[] = "GoogleUpdateUntrustedData"; 36const int kEnvVariableUntrustedDataMaxLength = 4096; 37 38// Returns true if Google Update is present at the given level. 39bool IsGoogleUpdatePresent(bool system_install) { 40 // Using the existence of version key in the registry to decide. 41 return GoogleUpdateSettings::GetGoogleUpdateVersion(system_install).IsValid(); 42} 43 44// Returns GoogleUpdateSetup.exe's executable path at specified level. 45// or an empty path if none is found. 46base::FilePath GetGoogleUpdateSetupExe(bool system_install) { 47 const HKEY root_key = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; 48 RegKey update_key; 49 50 if (update_key.Open(root_key, kRegPathGoogleUpdate, KEY_QUERY_VALUE) == 51 ERROR_SUCCESS) { 52 string16 path_str; 53 string16 version_str; 54 if ((update_key.ReadValue(kRegPathField, &path_str) == ERROR_SUCCESS) && 55 (update_key.ReadValue(kRegGoogleUpdateVersion, &version_str) == 56 ERROR_SUCCESS)) { 57 return base::FilePath(path_str).DirName().Append(version_str). 58 Append(kGoogleUpdateSetupExe); 59 } 60 } 61 return base::FilePath(); 62} 63 64// If Google Update is present at system-level, sets |cmd_string| to the command 65// line to install Google Update at user-level and returns true. 66// Otherwise, clears |cmd_string| and returns false. 67bool GetUserLevelGoogleUpdateInstallCommandLine(string16* cmd_string) { 68 cmd_string->clear(); 69 base::FilePath google_update_setup( 70 GetGoogleUpdateSetupExe(true)); // system-level. 71 if (!google_update_setup.empty()) { 72 CommandLine cmd(google_update_setup); 73 // Appends "/install runtime=true&needsadmin=false /silent /nomitag". 74 // NB: /nomitag needs to be at the end. 75 // Constants are found in code.google.com/p/omaha/common/const_cmd_line.h. 76 cmd.AppendArg("/install"); 77 // The "&" can be used in base::LaunchProcess() without quotation 78 // (this is problematic only if run from command prompt). 79 cmd.AppendArg("runtime=true&needsadmin=false"); 80 cmd.AppendArg("/silent"); 81 cmd.AppendArg("/nomitag"); 82 *cmd_string = cmd.GetCommandLineString(); 83 } 84 return !cmd_string->empty(); 85} 86 87// Launches command |cmd_string|, and waits for |timeout| milliseconds before 88// timing out. To wait indefinitely, one can set 89// |timeout| to be base::TimeDelta::FromMilliseconds(INFINITE). 90// Returns true if this executes successfully. 91// Returns false if command execution fails to execute, or times out. 92bool LaunchProcessAndWaitWithTimeout(const string16& cmd_string, 93 base::TimeDelta timeout) { 94 bool success = false; 95 base::win::ScopedHandle process; 96 int exit_code = 0; 97 LOG(INFO) << "Launching: " << cmd_string; 98 if (!base::LaunchProcess(cmd_string, base::LaunchOptions(), 99 process.Receive())) { 100 PLOG(ERROR) << "Failed to launch (" << cmd_string << ")"; 101 } else if (!base::WaitForExitCodeWithTimeout(process, &exit_code, timeout)) { 102 // The GetExitCodeProcess failed or timed-out. 103 LOG(ERROR) <<"Command (" << cmd_string << ") is taking more than " 104 << timeout.InMilliseconds() << " milliseconds to complete."; 105 } else if (exit_code != 0) { 106 LOG(ERROR) << "Command (" << cmd_string << ") exited with code " 107 << exit_code; 108 } else { 109 success = true; 110 } 111 return success; 112} 113 114bool IsNotPrintable(unsigned char c) { 115 return c < 32 || c >= 127; 116} 117 118// Returns whether or not |s| consists of printable characters. 119bool IsStringPrintable(const std::string& s) { 120 return std::find_if(s.begin(), s.end(), IsNotPrintable) == s.end(); 121} 122 123bool IsIllegalUntrustedDataKeyChar(unsigned char c) { 124 return !(c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || 125 c >= '0' && c <= '9' || c == '-' || c == '_' || c == '$'); 126} 127 128// Returns true if |key| from untrusted data is valid. 129bool IsUntrustedDataKeyValid(const std::string& key) { 130 return std::find_if(key.begin(), key.end(), IsIllegalUntrustedDataKeyChar) 131 == key.end(); 132} 133 134// Reads and parses untrusted data passed from Google Update as key-value 135// pairs, then overwrites |untrusted_data_map| with the result. 136// Returns true if data are successfully read. 137bool GetGoogleUpdateUntrustedData( 138 std::map<std::string, std::string>* untrusted_data) { 139 DCHECK(untrusted_data); 140 scoped_ptr<base::Environment> env(base::Environment::Create()); 141 std::string data_string; 142 if (env == NULL || !env->GetVar(kEnvVariableUntrustedData, &data_string)) 143 return false; 144 145 if (data_string.length() > kEnvVariableUntrustedDataMaxLength || 146 !IsStringPrintable(data_string)) { 147 LOG(ERROR) << "Invalid value in " << kEnvVariableUntrustedData; 148 return false; 149 } 150 151 VLOG(1) << kEnvVariableUntrustedData << ": " << data_string; 152 153 std::vector<std::pair<std::string, std::string> > kv_pairs; 154 if (!base::SplitStringIntoKeyValuePairs(data_string, '=', '&', &kv_pairs)) { 155 LOG(ERROR) << "Failed to parse untrusted data: " << data_string; 156 return false; 157 } 158 159 untrusted_data->clear(); 160 std::vector<std::pair<std::string, std::string> >::const_iterator it; 161 for (it = kv_pairs.begin(); it != kv_pairs.end(); ++it) { 162 const std::string& key(it->first); 163 // TODO(huangs): URL unescape |value|. 164 const std::string& value(it->second); 165 if (IsUntrustedDataKeyValid(key) && IsStringPrintable(value)) 166 (*untrusted_data)[key] = value; 167 else 168 LOG(ERROR) << "Illegal character found in untrusted data."; 169 } 170 return true; 171} 172 173} // namespace 174 175bool EnsureUserLevelGoogleUpdatePresent() { 176 LOG(INFO) << "Ensuring Google Update is present at user-level."; 177 178 bool success = false; 179 if (IsGoogleUpdatePresent(false)) { 180 success = true; 181 } else { 182 string16 cmd_string; 183 if (!GetUserLevelGoogleUpdateInstallCommandLine(&cmd_string)) { 184 LOG(ERROR) << "Cannot find Google Update at system-level."; 185 // Ideally we should return false. However, this case should not be 186 // encountered by regular users, and developers (who often installs 187 // Chrome without Google Update) may be unduly impeded by this case. 188 // Therefore we return true. 189 success = true; 190 } else { 191 success = LaunchProcessAndWaitWithTimeout(cmd_string, 192 base::TimeDelta::FromMilliseconds(INFINITE)); 193 } 194 } 195 return success; 196} 197 198bool UninstallGoogleUpdate(bool system_install) { 199 bool success = false; 200 string16 cmd_string( 201 GoogleUpdateSettings::GetUninstallCommandLine(system_install)); 202 if (cmd_string.empty()) { 203 success = true; // Nothing to; vacuous success. 204 } else { 205 success = LaunchProcessAndWaitWithTimeout(cmd_string, 206 base::TimeDelta::FromMilliseconds(kGoogleUpdateTimeoutMs)); 207 } 208 return success; 209} 210 211std::string GetUntrustedDataValue(const std::string& key) { 212 std::map<std::string, std::string> untrusted_data; 213 if (GetGoogleUpdateUntrustedData(&untrusted_data)) { 214 std::map<std::string, std::string>::const_iterator data_it( 215 untrusted_data.find(key)); 216 if (data_it != untrusted_data.end()) 217 return data_it->second; 218 } 219 220 return std::string(); 221} 222 223} // namespace google_update 224