Home | History | Annotate | Download | only in util
      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.
     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.
     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.
     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.
    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 
    123 bool IsNotPrintable(unsigned char c) {
    124   return c < 32 || c >= 127;
    125 }
    126 
    127 // Returns whether or not |s| consists of printable characters.
    128 bool IsStringPrintable(const std::string& s) {
    129   return std::find_if(s.begin(), s.end(), IsNotPrintable) == s.end();
    130 }
    131 
    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.
    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.
    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.
    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 
    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 
    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 
    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 
    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 
    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