• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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