google_update_util.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
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
36using base::win::RegKey;
37
38namespace google_update {
39
40namespace {
41
42const int kGoogleUpdateTimeoutMs = 20 * 1000;
43
44const char kEnvVariableUntrustedData[] = "GoogleUpdateUntrustedData";
45const int kUntrustedDataMaxLength = 4096;
46
47// Returns true if Google Update is present at the given level.
48bool 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.
55base::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.
76bool 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.
101bool 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
123bool IsNotPrintable(unsigned char c) {
124  return c < 32 || c >= 127;
125}
126
127// Returns whether or not |s| consists of printable characters.
128bool IsStringPrintable(const std::string& s) {
129  return std::find_if(s.begin(), s.end(), IsNotPrintable) == s.end();
130}
131
132bool 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.
138bool 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.
145bool 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.
180bool 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
192bool 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
215bool 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
228void 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
269std::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
281std::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