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.
IsGoogleUpdatePresent(bool system_install)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.
GetGoogleUpdateSetupExe(bool system_install)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.
GetUserLevelGoogleUpdateInstallCommandLine(base::string16 * cmd_string)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.
LaunchProcessAndWaitWithTimeout(const base::string16 & cmd_string,base::TimeDelta timeout)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
IsNotPrintable(unsigned char c)123 bool IsNotPrintable(unsigned char c) {
124 return c < 32 || c >= 127;
125 }
126
127 // Returns whether or not |s| consists of printable characters.
IsStringPrintable(const std::string & s)128 bool IsStringPrintable(const std::string& s) {
129 return std::find_if(s.begin(), s.end(), IsNotPrintable) == s.end();
130 }
131
IsIllegalUntrustedDataKeyChar(unsigned char c)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.
IsUntrustedDataKeyValid(const std::string & key)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.
ParseUntrustedData(const std::string & data_string,std::map<std::string,std::string> * untrusted_data)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.
GetGoogleUpdateUntrustedData(std::map<std::string,std::string> * untrusted_data)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
EnsureUserLevelGoogleUpdatePresent()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
UninstallGoogleUpdate(bool system_install)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
ElevateIfNeededToReenableUpdates()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
GetUntrustedDataValue(const std::string & key)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
GetUntrustedDataValueFromTag(const std::string & tag,const std::string & key)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