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