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