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/memory/scoped_ptr.h" 18 #include "base/path_service.h" 19 #include "base/process/kill.h" 20 #include "base/process/launch.h" 21 #include "base/strings/string16.h" 22 #include "base/strings/string_split.h" 23 #include "base/time/time.h" 24 #include "base/win/registry.h" 25 #include "base/win/scoped_handle.h" 26 #include "base/win/win_util.h" 27 #include "base/win/windows_version.h" 28 #include "chrome/installer/launcher_support/chrome_launcher_support.h" 29 #include "chrome/installer/util/browser_distribution.h" 30 #include "chrome/installer/util/google_update_constants.h" 31 #include "chrome/installer/util/google_update_settings.h" 32 #include "chrome/installer/util/install_util.h" 33 #include "chrome/installer/util/installation_state.h" 34 #include "chrome/installer/util/product.h" 35 36 using base::win::RegKey; 37 38 namespace google_update { 39 40 namespace { 41 42 const int kGoogleUpdateTimeoutMs = 20 * 1000; 43 44 const char kEnvVariableUntrustedData[] = "GoogleUpdateUntrustedData"; 45 const int kUntrustedDataMaxLength = 4096; 46 47 // Returns true if Google Update is present at the given level. 48 bool IsGoogleUpdatePresent(bool system_install) { 49 // Using the existence of version key in the registry to decide. 50 return GoogleUpdateSettings::GetGoogleUpdateVersion(system_install).IsValid(); 51 } 52 53 // Returns GoogleUpdateSetup.exe's executable path at specified level. 54 // or an empty path if none is found. 55 base::FilePath GetGoogleUpdateSetupExe(bool system_install) { 56 const HKEY root_key = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; 57 RegKey update_key; 58 59 if (update_key.Open(root_key, kRegPathGoogleUpdate, KEY_QUERY_VALUE) == 60 ERROR_SUCCESS) { 61 base::string16 path_str; 62 base::string16 version_str; 63 if ((update_key.ReadValue(kRegPathField, &path_str) == ERROR_SUCCESS) && 64 (update_key.ReadValue(kRegGoogleUpdateVersion, &version_str) == 65 ERROR_SUCCESS)) { 66 return base::FilePath(path_str).DirName().Append(version_str). 67 Append(kGoogleUpdateSetupExe); 68 } 69 } 70 return base::FilePath(); 71 } 72 73 // If Google Update is present at system-level, sets |cmd_string| to the command 74 // line to install Google Update at user-level and returns true. 75 // Otherwise, clears |cmd_string| and returns false. 76 bool GetUserLevelGoogleUpdateInstallCommandLine(base::string16* cmd_string) { 77 cmd_string->clear(); 78 base::FilePath google_update_setup( 79 GetGoogleUpdateSetupExe(true)); // system-level. 80 if (!google_update_setup.empty()) { 81 CommandLine cmd(google_update_setup); 82 // Appends "/install runtime=true&needsadmin=false /silent /nomitag". 83 // NB: /nomitag needs to be at the end. 84 // Constants are found in code.google.com/p/omaha/common/const_cmd_line.h. 85 cmd.AppendArg("/install"); 86 // The "&" can be used in base::LaunchProcess() without quotation 87 // (this is problematic only if run from command prompt). 88 cmd.AppendArg("runtime=true&needsadmin=false"); 89 cmd.AppendArg("/silent"); 90 cmd.AppendArg("/nomitag"); 91 *cmd_string = cmd.GetCommandLineString(); 92 } 93 return !cmd_string->empty(); 94 } 95 96 // Launches command |cmd_string|, and waits for |timeout| milliseconds before 97 // timing out. To wait indefinitely, one can set 98 // |timeout| to be base::TimeDelta::FromMilliseconds(INFINITE). 99 // Returns true if this executes successfully. 100 // Returns false if command execution fails to execute, or times out. 101 bool LaunchProcessAndWaitWithTimeout(const base::string16& cmd_string, 102 base::TimeDelta timeout) { 103 bool success = false; 104 base::win::ScopedHandle process; 105 int exit_code = 0; 106 VLOG(0) << "Launching: " << cmd_string; 107 if (!base::LaunchProcess(cmd_string, base::LaunchOptions(), 108 &process)) { 109 PLOG(ERROR) << "Failed to launch (" << cmd_string << ")"; 110 } else if (!base::WaitForExitCodeWithTimeout(process, &exit_code, timeout)) { 111 // The GetExitCodeProcess failed or timed-out. 112 LOG(ERROR) <<"Command (" << cmd_string << ") is taking more than " 113 << timeout.InMilliseconds() << " milliseconds to complete."; 114 } else if (exit_code != 0) { 115 LOG(ERROR) << "Command (" << cmd_string << ") exited with code " 116 << exit_code; 117 } else { 118 success = true; 119 } 120 return success; 121 } 122 123 bool IsNotPrintable(unsigned char c) { 124 return c < 32 || c >= 127; 125 } 126 127 // Returns whether or not |s| consists of printable characters. 128 bool IsStringPrintable(const std::string& s) { 129 return std::find_if(s.begin(), s.end(), IsNotPrintable) == s.end(); 130 } 131 132 bool IsIllegalUntrustedDataKeyChar(unsigned char c) { 133 return !(c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || 134 c >= '0' && c <= '9' || c == '-' || c == '_' || c == '$'); 135 } 136 137 // Returns true if |key| from untrusted data is valid. 138 bool IsUntrustedDataKeyValid(const std::string& key) { 139 return std::find_if(key.begin(), key.end(), IsIllegalUntrustedDataKeyChar) 140 == key.end(); 141 } 142 143 // Parses |data_string| as key-value pairs and overwrites |untrusted_data| with 144 // the result. Returns true if the data could be parsed. 145 bool ParseUntrustedData( 146 const std::string& data_string, 147 std::map<std::string, std::string>* untrusted_data) { 148 DCHECK(untrusted_data); 149 if (data_string.length() > kUntrustedDataMaxLength || 150 !IsStringPrintable(data_string)) { 151 LOG(ERROR) << "Invalid value in untrusted data string."; 152 return false; 153 } 154 155 VLOG(1) << "Untrusted data string: " << data_string; 156 157 std::vector<std::pair<std::string, std::string> > kv_pairs; 158 if (!base::SplitStringIntoKeyValuePairs(data_string, '=', '&', &kv_pairs)) { 159 LOG(ERROR) << "Failed to parse untrusted data: " << data_string; 160 return false; 161 } 162 163 untrusted_data->clear(); 164 std::vector<std::pair<std::string, std::string> >::const_iterator it; 165 for (it = kv_pairs.begin(); it != kv_pairs.end(); ++it) { 166 const std::string& key(it->first); 167 // TODO(huangs): URL unescape |value|. 168 const std::string& value(it->second); 169 if (IsUntrustedDataKeyValid(key) && IsStringPrintable(value)) 170 (*untrusted_data)[key] = value; 171 else 172 LOG(ERROR) << "Illegal character found in untrusted data."; 173 } 174 return true; 175 } 176 177 // Reads and parses untrusted data passed from Google Update as key-value 178 // pairs, then overwrites |untrusted_data_map| with the result. 179 // Returns true if data are successfully read. 180 bool GetGoogleUpdateUntrustedData( 181 std::map<std::string, std::string>* untrusted_data) { 182 scoped_ptr<base::Environment> env(base::Environment::Create()); 183 std::string data_string; 184 if (!env || !env->GetVar(kEnvVariableUntrustedData, &data_string)) 185 return false; 186 187 return ParseUntrustedData(data_string, untrusted_data); 188 } 189 190 } // namespace 191 192 bool EnsureUserLevelGoogleUpdatePresent() { 193 VLOG(0) << "Ensuring Google Update is present at user-level."; 194 195 bool success = false; 196 if (IsGoogleUpdatePresent(false)) { 197 success = true; 198 } else { 199 base::string16 cmd_string; 200 if (!GetUserLevelGoogleUpdateInstallCommandLine(&cmd_string)) { 201 LOG(ERROR) << "Cannot find Google Update at system-level."; 202 // Ideally we should return false. However, this case should not be 203 // encountered by regular users, and developers (who often installs 204 // Chrome without Google Update) may be unduly impeded by this case. 205 // Therefore we return true. 206 success = true; 207 } else { 208 success = LaunchProcessAndWaitWithTimeout(cmd_string, 209 base::TimeDelta::FromMilliseconds(INFINITE)); 210 } 211 } 212 return success; 213 } 214 215 bool UninstallGoogleUpdate(bool system_install) { 216 bool success = false; 217 base::string16 cmd_string( 218 GoogleUpdateSettings::GetUninstallCommandLine(system_install)); 219 if (cmd_string.empty()) { 220 success = true; // Nothing to; vacuous success. 221 } else { 222 success = LaunchProcessAndWaitWithTimeout(cmd_string, 223 base::TimeDelta::FromMilliseconds(kGoogleUpdateTimeoutMs)); 224 } 225 return success; 226 } 227 228 void ElevateIfNeededToReenableUpdates() { 229 base::FilePath chrome_exe; 230 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { 231 NOTREACHED(); 232 return; 233 } 234 installer::ProductState product_state; 235 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 236 const bool system_install = !InstallUtil::IsPerUserInstall( 237 chrome_exe.value().c_str()); 238 if (!product_state.Initialize(system_install, dist)) 239 return; 240 base::FilePath exe_path(product_state.GetSetupPath()); 241 if (exe_path.empty() || !base::PathExists(exe_path)) { 242 LOG(ERROR) << "Could not find setup.exe to reenable updates."; 243 return; 244 } 245 246 CommandLine cmd(exe_path); 247 cmd.AppendSwitch(installer::switches::kReenableAutoupdates); 248 installer::Product product(dist); 249 product.InitializeFromUninstallCommand(product_state.uninstall_command()); 250 product.AppendProductFlags(&cmd); 251 if (system_install) 252 cmd.AppendSwitch(installer::switches::kSystemLevel); 253 if (product_state.uninstall_command().HasSwitch( 254 installer::switches::kVerboseLogging)) { 255 cmd.AppendSwitch(installer::switches::kVerboseLogging); 256 } 257 258 base::LaunchOptions launch_options; 259 launch_options.force_breakaway_from_job_ = true; 260 261 if (base::win::GetVersion() >= base::win::VERSION_VISTA && 262 base::win::UserAccountControlIsEnabled()) { 263 base::LaunchElevatedProcess(cmd, launch_options, NULL); 264 } else { 265 base::LaunchProcess(cmd, launch_options, NULL); 266 } 267 } 268 269 std::string GetUntrustedDataValue(const std::string& key) { 270 std::map<std::string, std::string> untrusted_data; 271 if (GetGoogleUpdateUntrustedData(&untrusted_data)) { 272 std::map<std::string, std::string>::const_iterator data_it( 273 untrusted_data.find(key)); 274 if (data_it != untrusted_data.end()) 275 return data_it->second; 276 } 277 278 return std::string(); 279 } 280 281 std::string GetUntrustedDataValueFromTag(const std::string& tag, 282 const std::string& key) { 283 std::map<std::string, std::string> untrusted_data; 284 if (ParseUntrustedData(tag, &untrusted_data)) 285 return untrusted_data[key]; 286 287 return std::string(); 288 } 289 290 } // namespace google_update 291