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/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 
     28 using base::win::RegKey;
     29 
     30 namespace google_update {
     31 
     32 namespace {
     33 
     34 const int kGoogleUpdateTimeoutMs = 20 * 1000;
     35 
     36 const char kEnvVariableUntrustedData[] = "GoogleUpdateUntrustedData";
     37 const int kEnvVariableUntrustedDataMaxLength = 4096;
     38 
     39 // Returns true if Google Update is present at the given level.
     40 bool 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.
     47 base::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     string16 path_str;
     54     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.
     68 bool GetUserLevelGoogleUpdateInstallCommandLine(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.
     93 bool LaunchProcessAndWaitWithTimeout(const 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 
    115 bool IsNotPrintable(unsigned char c) {
    116   return c < 32 || c >= 127;
    117 }
    118 
    119 // Returns whether or not |s| consists of printable characters.
    120 bool IsStringPrintable(const std::string& s) {
    121   return std::find_if(s.begin(), s.end(), IsNotPrintable) == s.end();
    122 }
    123 
    124 bool 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.
    130 bool IsUntrustedDataKeyValid(const std::string& key) {
    131   return std::find_if(key.begin(), key.end(), IsIllegalUntrustedDataKeyChar)
    132       == key.end();
    133 }
    134 
    135 // Reads and parses untrusted data passed from Google Update as key-value
    136 // pairs, then overwrites |untrusted_data_map| with the result.
    137 // Returns true if data are successfully read.
    138 bool GetGoogleUpdateUntrustedData(
    139     std::map<std::string, std::string>* untrusted_data) {
    140   DCHECK(untrusted_data);
    141   scoped_ptr<base::Environment> env(base::Environment::Create());
    142   std::string data_string;
    143   if (env == NULL || !env->GetVar(kEnvVariableUntrustedData, &data_string))
    144     return false;
    145 
    146   if (data_string.length() > kEnvVariableUntrustedDataMaxLength ||
    147       !IsStringPrintable(data_string)) {
    148     LOG(ERROR) << "Invalid value in " << kEnvVariableUntrustedData;
    149     return false;
    150   }
    151 
    152   VLOG(1) << kEnvVariableUntrustedData << ": " << data_string;
    153 
    154   std::vector<std::pair<std::string, std::string> > kv_pairs;
    155   if (!base::SplitStringIntoKeyValuePairs(data_string, '=', '&', &kv_pairs)) {
    156     LOG(ERROR) << "Failed to parse untrusted data: " << data_string;
    157     return false;
    158   }
    159 
    160   untrusted_data->clear();
    161   std::vector<std::pair<std::string, std::string> >::const_iterator it;
    162   for (it = kv_pairs.begin(); it != kv_pairs.end(); ++it) {
    163     const std::string& key(it->first);
    164     // TODO(huangs): URL unescape |value|.
    165     const std::string& value(it->second);
    166     if (IsUntrustedDataKeyValid(key) && IsStringPrintable(value))
    167       (*untrusted_data)[key] = value;
    168     else
    169       LOG(ERROR) << "Illegal character found in untrusted data.";
    170   }
    171   return true;
    172 }
    173 
    174 }  // namespace
    175 
    176 bool EnsureUserLevelGoogleUpdatePresent() {
    177   VLOG(0) << "Ensuring Google Update is present at user-level.";
    178 
    179   bool success = false;
    180   if (IsGoogleUpdatePresent(false)) {
    181     success = true;
    182   } else {
    183     string16 cmd_string;
    184     if (!GetUserLevelGoogleUpdateInstallCommandLine(&cmd_string)) {
    185       LOG(ERROR) << "Cannot find Google Update at system-level.";
    186       // Ideally we should return false. However, this case should not be
    187       // encountered by regular users, and developers (who often installs
    188       // Chrome without Google Update) may be unduly impeded by this case.
    189       // Therefore we return true.
    190       success = true;
    191     } else {
    192       success = LaunchProcessAndWaitWithTimeout(cmd_string,
    193           base::TimeDelta::FromMilliseconds(INFINITE));
    194     }
    195   }
    196   return success;
    197 }
    198 
    199 bool UninstallGoogleUpdate(bool system_install) {
    200   bool success = false;
    201   string16 cmd_string(
    202       GoogleUpdateSettings::GetUninstallCommandLine(system_install));
    203   if (cmd_string.empty()) {
    204     success = true;  // Nothing to; vacuous success.
    205   } else {
    206     success = LaunchProcessAndWaitWithTimeout(cmd_string,
    207         base::TimeDelta::FromMilliseconds(kGoogleUpdateTimeoutMs));
    208   }
    209   return success;
    210 }
    211 
    212 std::string GetUntrustedDataValue(const std::string& key) {
    213   std::map<std::string, std::string> untrusted_data;
    214   if (GetGoogleUpdateUntrustedData(&untrusted_data)) {
    215     std::map<std::string, std::string>::const_iterator data_it(
    216         untrusted_data.find(key));
    217     if (data_it != untrusted_data.end())
    218       return data_it->second;
    219   }
    220 
    221   return std::string();
    222 }
    223 
    224 }  // namespace google_update
    225