Home | History | Annotate | Download | only in chromeos
      1 // Copyright (c) 2011 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/browser/chromeos/proxy_config_service_impl.h"
      6 
      7 #include <ostream>
      8 
      9 #include "base/logging.h"
     10 #include "base/string_util.h"
     11 #include "base/task.h"
     12 #include "chrome/browser/chromeos/cros/cros_library.h"
     13 #include "chrome/browser/chromeos/cros_settings_names.h"
     14 #include "chrome/browser/policy/proto/chrome_device_policy.pb.h"
     15 #include "chrome/browser/prefs/proxy_prefs.h"
     16 #include "content/browser/browser_thread.h"
     17 
     18 namespace em = enterprise_management;
     19 
     20 namespace chromeos {
     21 
     22 namespace {
     23 
     24 const char* SourceToString(ProxyConfigServiceImpl::ProxyConfig::Source source) {
     25   switch (source) {
     26     case ProxyConfigServiceImpl::ProxyConfig::SOURCE_NONE:
     27       return "SOURCE_NONE";
     28     case ProxyConfigServiceImpl::ProxyConfig::SOURCE_POLICY:
     29       return "SOURCE_POLICY";
     30     case ProxyConfigServiceImpl::ProxyConfig::SOURCE_OWNER:
     31       return "SOURCE_OWNER";
     32   }
     33   NOTREACHED() << "Unrecognized source type";
     34   return "";
     35 }
     36 
     37 std::ostream& operator<<(std::ostream& out,
     38     const ProxyConfigServiceImpl::ProxyConfig::ManualProxy& proxy) {
     39   out << "  " << SourceToString(proxy.source) << "\n"
     40       << "  server: " << (proxy.server.is_valid() ? proxy.server.ToURI() : "")
     41       << "\n";
     42   return out;
     43 }
     44 
     45 std::ostream& operator<<(std::ostream& out,
     46     const ProxyConfigServiceImpl::ProxyConfig& config) {
     47   switch (config.mode) {
     48     case ProxyConfigServiceImpl::ProxyConfig::MODE_DIRECT:
     49       out << "Direct connection:\n  "
     50           << SourceToString(config.automatic_proxy.source) << "\n";
     51       break;
     52     case ProxyConfigServiceImpl::ProxyConfig::MODE_AUTO_DETECT:
     53       out << "Auto detection:\n  "
     54           << SourceToString(config.automatic_proxy.source) << "\n";
     55       break;
     56     case ProxyConfigServiceImpl::ProxyConfig::MODE_PAC_SCRIPT:
     57       out << "Custom PAC script:\n  "
     58           << SourceToString(config.automatic_proxy.source)
     59           << "\n  PAC: " << config.automatic_proxy.pac_url << "\n";
     60       break;
     61     case ProxyConfigServiceImpl::ProxyConfig::MODE_SINGLE_PROXY:
     62       out << "Single proxy:\n" << config.single_proxy;
     63       break;
     64     case ProxyConfigServiceImpl::ProxyConfig::MODE_PROXY_PER_SCHEME:
     65       out << "HTTP proxy: " << config.http_proxy;
     66       out << "HTTPS proxy: " << config.https_proxy;
     67       out << "FTP proxy: " << config.ftp_proxy;
     68       out << "SOCKS proxy: " << config.socks_proxy;
     69       break;
     70     default:
     71       NOTREACHED() << "Unrecognized proxy config mode";
     72       break;
     73   }
     74   if (config.mode == ProxyConfigServiceImpl::ProxyConfig::MODE_SINGLE_PROXY ||
     75       config.mode ==
     76           ProxyConfigServiceImpl::ProxyConfig::MODE_PROXY_PER_SCHEME) {
     77     out << "Bypass list: ";
     78     if (config.bypass_rules.rules().empty()) {
     79       out << "[None]";
     80     } else {
     81       const net::ProxyBypassRules& bypass_rules = config.bypass_rules;
     82       net::ProxyBypassRules::RuleList::const_iterator it;
     83       for (it = bypass_rules.rules().begin();
     84            it != bypass_rules.rules().end(); ++it) {
     85         out << "\n    " << (*it)->ToString();
     86       }
     87     }
     88   }
     89   return out;
     90 }
     91 
     92 std::string ProxyConfigToString(
     93     const ProxyConfigServiceImpl::ProxyConfig& proxy_config) {
     94   std::ostringstream stream;
     95   stream << proxy_config;
     96   return stream.str();
     97 }
     98 
     99 }  // namespace
    100 
    101 //---------- ProxyConfigServiceImpl::ProxyConfig::Setting methods --------------
    102 
    103 bool ProxyConfigServiceImpl::ProxyConfig::Setting::CanBeWrittenByUser(
    104     bool user_is_owner) {
    105   // Setting can only be written by user if user is owner and setting is not
    106   // from policy.
    107   return user_is_owner && source != ProxyConfig::SOURCE_POLICY;
    108 }
    109 
    110 //----------- ProxyConfigServiceImpl::ProxyConfig: public methods --------------
    111 
    112 void ProxyConfigServiceImpl::ProxyConfig::ToNetProxyConfig(
    113     net::ProxyConfig* net_config) {
    114   switch (mode) {
    115     case MODE_DIRECT:
    116       *net_config = net::ProxyConfig::CreateDirect();
    117       break;
    118     case MODE_AUTO_DETECT:
    119       *net_config = net::ProxyConfig::CreateAutoDetect();
    120       break;
    121     case MODE_PAC_SCRIPT:
    122       *net_config = net::ProxyConfig::CreateFromCustomPacURL(
    123           automatic_proxy.pac_url);
    124       break;
    125     case MODE_SINGLE_PROXY:
    126       *net_config = net::ProxyConfig();
    127       net_config->proxy_rules().type =
    128              net::ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
    129       net_config->proxy_rules().single_proxy = single_proxy.server;
    130       net_config->proxy_rules().bypass_rules = bypass_rules;
    131       break;
    132     case MODE_PROXY_PER_SCHEME:
    133       *net_config = net::ProxyConfig();
    134       net_config->proxy_rules().type =
    135           net::ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
    136       net_config->proxy_rules().proxy_for_http = http_proxy.server;
    137       net_config->proxy_rules().proxy_for_https = https_proxy.server;
    138       net_config->proxy_rules().proxy_for_ftp = ftp_proxy.server;
    139       net_config->proxy_rules().fallback_proxy = socks_proxy.server;
    140       net_config->proxy_rules().bypass_rules = bypass_rules;
    141       break;
    142     default:
    143       NOTREACHED() << "Unrecognized proxy config mode";
    144       break;
    145   }
    146 }
    147 
    148 bool ProxyConfigServiceImpl::ProxyConfig::CanBeWrittenByUser(
    149     bool user_is_owner, const std::string& scheme) {
    150   // Setting can only be written by user if user is owner and setting is not
    151   // from policy.
    152   Setting* setting = NULL;
    153   switch (mode) {
    154     case MODE_DIRECT:
    155     case MODE_AUTO_DETECT:
    156     case MODE_PAC_SCRIPT:
    157       setting = &automatic_proxy;
    158       break;
    159     case MODE_SINGLE_PROXY:
    160       setting = &single_proxy;
    161       break;
    162     case MODE_PROXY_PER_SCHEME:
    163       setting = MapSchemeToProxy(scheme);
    164       break;
    165     default:
    166       break;
    167   }
    168   if (!setting) {
    169     NOTREACHED() << "Unrecognized proxy config mode";
    170     return false;
    171   }
    172   return setting->CanBeWrittenByUser(user_is_owner);
    173 }
    174 
    175 ProxyConfigServiceImpl::ProxyConfig::ManualProxy*
    176     ProxyConfigServiceImpl::ProxyConfig::MapSchemeToProxy(
    177         const std::string& scheme) {
    178   if (scheme == "http")
    179     return &http_proxy;
    180   if (scheme == "https")
    181     return &https_proxy;
    182   if (scheme == "ftp")
    183     return &ftp_proxy;
    184   if (scheme == "socks")
    185     return &socks_proxy;
    186   NOTREACHED() << "Invalid scheme: " << scheme;
    187   return NULL;
    188 }
    189 
    190 bool ProxyConfigServiceImpl::ProxyConfig::Serialize(std::string* output) {
    191   em::DeviceProxySettingsProto proxy_proto;
    192   switch (mode) {
    193     case MODE_DIRECT: {
    194       proxy_proto.set_proxy_mode(ProxyPrefs::kDirectProxyModeName);
    195       break;
    196     }
    197     case MODE_AUTO_DETECT: {
    198       proxy_proto.set_proxy_mode(ProxyPrefs::kAutoDetectProxyModeName);
    199       break;
    200     }
    201     case MODE_PAC_SCRIPT: {
    202       proxy_proto.set_proxy_mode(ProxyPrefs::kPacScriptProxyModeName);
    203       if (!automatic_proxy.pac_url.is_empty())
    204         proxy_proto.set_proxy_pac_url(automatic_proxy.pac_url.spec());
    205       break;
    206     }
    207     case MODE_SINGLE_PROXY: {
    208       proxy_proto.set_proxy_mode(ProxyPrefs::kFixedServersProxyModeName);
    209       if (single_proxy.server.is_valid())
    210         proxy_proto.set_proxy_server(single_proxy.server.ToURI());
    211       break;
    212     }
    213     case MODE_PROXY_PER_SCHEME: {
    214       proxy_proto.set_proxy_mode(ProxyPrefs::kFixedServersProxyModeName);
    215       std::string spec;
    216       EncodeAndAppendProxyServer("http", http_proxy.server, &spec);
    217       EncodeAndAppendProxyServer("https", https_proxy.server, &spec);
    218       EncodeAndAppendProxyServer("ftp", ftp_proxy.server, &spec);
    219       EncodeAndAppendProxyServer("socks", socks_proxy.server, &spec);
    220       if (!spec.empty())
    221         proxy_proto.set_proxy_server(spec);
    222       break;
    223     }
    224     default: {
    225       NOTREACHED() << "Unrecognized proxy config mode";
    226       break;
    227     }
    228   }
    229   proxy_proto.set_proxy_bypass_list(bypass_rules.ToString());
    230   return proxy_proto.SerializeToString(output);
    231 }
    232 
    233 bool ProxyConfigServiceImpl::ProxyConfig::Deserialize(
    234     const std::string& input) {
    235   em::DeviceProxySettingsProto proxy_proto;
    236   if (!proxy_proto.ParseFromString(input))
    237     return false;
    238 
    239   const std::string& mode_string(proxy_proto.proxy_mode());
    240   if (mode_string == ProxyPrefs::kDirectProxyModeName) {
    241     mode = MODE_DIRECT;
    242   } else if (mode_string == ProxyPrefs::kAutoDetectProxyModeName) {
    243     mode = MODE_AUTO_DETECT;
    244   } else if (mode_string == ProxyPrefs::kPacScriptProxyModeName) {
    245     mode = MODE_PAC_SCRIPT;
    246     if (proxy_proto.has_proxy_pac_url())
    247       automatic_proxy.pac_url = GURL(proxy_proto.proxy_pac_url());
    248   } else if (mode_string == ProxyPrefs::kFixedServersProxyModeName) {
    249     net::ProxyConfig::ProxyRules rules;
    250     rules.ParseFromString(proxy_proto.proxy_server());
    251     switch (rules.type) {
    252       case net::ProxyConfig::ProxyRules::TYPE_NO_RULES:
    253         return false;
    254       case net::ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY:
    255         if (!rules.single_proxy.is_valid())
    256           return false;
    257         mode = MODE_SINGLE_PROXY;
    258         single_proxy.server = rules.single_proxy;
    259         break;
    260       case net::ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME:
    261         // Make sure we have valid server for at least one of the protocols.
    262         if (!rules.proxy_for_http.is_valid() &&
    263             !rules.proxy_for_https.is_valid() &&
    264             !rules.proxy_for_ftp.is_valid() &&
    265             !rules.fallback_proxy.is_valid()) {
    266           return false;
    267         }
    268         mode = MODE_PROXY_PER_SCHEME;
    269         if (rules.proxy_for_http.is_valid())
    270           http_proxy.server = rules.proxy_for_http;
    271         if (rules.proxy_for_https.is_valid())
    272           https_proxy.server = rules.proxy_for_https;
    273         if (rules.proxy_for_ftp.is_valid())
    274           ftp_proxy.server = rules.proxy_for_ftp;
    275         if (rules.fallback_proxy.is_valid())
    276           socks_proxy.server = rules.fallback_proxy;
    277         break;
    278     }
    279   } else {
    280     NOTREACHED() << "Unrecognized proxy config mode";
    281     return false;
    282   }
    283 
    284   if (proxy_proto.has_proxy_bypass_list())
    285     bypass_rules.ParseFromString(proxy_proto.proxy_bypass_list());
    286 
    287   return true;
    288 }
    289 
    290 std::string ProxyConfigServiceImpl::ProxyConfig::ToString() const {
    291   return ProxyConfigToString(*this);
    292 }
    293 
    294 //----------- ProxyConfigServiceImpl::ProxyConfig: private methods -------------
    295 
    296 // static
    297 void ProxyConfigServiceImpl::ProxyConfig::EncodeAndAppendProxyServer(
    298     const std::string& scheme,
    299     const net::ProxyServer& server,
    300     std::string* spec) {
    301   if (!server.is_valid())
    302     return;
    303 
    304   if (!spec->empty())
    305     *spec += ';';
    306 
    307   if (!scheme.empty()) {
    308     *spec += scheme;
    309     *spec += "=";
    310   }
    311   *spec += server.ToURI();
    312 }
    313 
    314 //------------------- ProxyConfigServiceImpl: public methods -------------------
    315 
    316 ProxyConfigServiceImpl::ProxyConfigServiceImpl()
    317     : can_post_task_(false),
    318       config_availability_(net::ProxyConfigService::CONFIG_PENDING),
    319       persist_to_device_(true),
    320       persist_to_device_pending_(false) {
    321   // Start async fetch of proxy config from settings persisted on device.
    322   // TODO(kuan): retrieve config from policy and owner and merge them
    323   bool use_default = true;
    324   if (CrosLibrary::Get()->EnsureLoaded()) {
    325     retrieve_property_op_ = SignedSettings::CreateRetrievePropertyOp(
    326         kSettingProxyEverywhere, this);
    327     if (retrieve_property_op_) {
    328       retrieve_property_op_->Execute();
    329       VLOG(1) << "Start retrieving proxy setting from device";
    330       use_default = false;
    331     } else {
    332       VLOG(1) << "Fail to retrieve proxy setting from device";
    333     }
    334   }
    335   if (use_default)
    336     config_availability_ = net::ProxyConfigService::CONFIG_UNSET;
    337   can_post_task_ = true;
    338 }
    339 
    340 ProxyConfigServiceImpl::ProxyConfigServiceImpl(const ProxyConfig& init_config)
    341     : can_post_task_(true),
    342       config_availability_(net::ProxyConfigService::CONFIG_VALID),
    343       persist_to_device_(false),
    344       persist_to_device_pending_(false) {
    345   reference_config_ = init_config;
    346   // Update the IO-accessible copy in |cached_config_| as well.
    347   cached_config_ = reference_config_;
    348 }
    349 
    350 ProxyConfigServiceImpl::~ProxyConfigServiceImpl() {
    351 }
    352 
    353 void ProxyConfigServiceImpl::UIGetProxyConfig(ProxyConfig* config) {
    354   // Should be called from UI thread.
    355   CheckCurrentlyOnUIThread();
    356   // Simply returns the copy on the UI thread.
    357   *config = reference_config_;
    358 }
    359 
    360 bool ProxyConfigServiceImpl::UISetProxyConfigToDirect() {
    361   // Should be called from UI thread.
    362   CheckCurrentlyOnUIThread();
    363   reference_config_.mode = ProxyConfig::MODE_DIRECT;
    364   OnUISetProxyConfig(persist_to_device_);
    365   return true;
    366 }
    367 
    368 bool ProxyConfigServiceImpl::UISetProxyConfigToAutoDetect() {
    369   // Should be called from UI thread.
    370   CheckCurrentlyOnUIThread();
    371   reference_config_.mode = ProxyConfig::MODE_AUTO_DETECT;
    372   OnUISetProxyConfig(persist_to_device_);
    373   return true;
    374 }
    375 
    376 bool ProxyConfigServiceImpl::UISetProxyConfigToPACScript(const GURL& pac_url) {
    377   // Should be called from UI thread.
    378   CheckCurrentlyOnUIThread();
    379   reference_config_.mode = ProxyConfig::MODE_PAC_SCRIPT;
    380   reference_config_.automatic_proxy.pac_url = pac_url;
    381   OnUISetProxyConfig(persist_to_device_);
    382   return true;
    383 }
    384 
    385 bool ProxyConfigServiceImpl::UISetProxyConfigToSingleProxy(
    386     const net::ProxyServer& server) {
    387   // Should be called from UI thread.
    388   CheckCurrentlyOnUIThread();
    389   reference_config_.mode = ProxyConfig::MODE_SINGLE_PROXY;
    390   reference_config_.single_proxy.server = server;
    391   OnUISetProxyConfig(persist_to_device_);
    392   return true;
    393 }
    394 
    395 bool ProxyConfigServiceImpl::UISetProxyConfigToProxyPerScheme(
    396     const std::string& scheme, const net::ProxyServer& server) {
    397   // Should be called from UI thread.
    398   CheckCurrentlyOnUIThread();
    399   ProxyConfig::ManualProxy* proxy = reference_config_.MapSchemeToProxy(scheme);
    400   if (!proxy) {
    401     NOTREACHED() << "Cannot set proxy: invalid scheme [" << scheme << "]";
    402     return false;
    403   }
    404   reference_config_.mode = ProxyConfig::MODE_PROXY_PER_SCHEME;
    405   proxy->server = server;
    406   OnUISetProxyConfig(persist_to_device_);
    407   return true;
    408 }
    409 
    410 bool ProxyConfigServiceImpl::UISetProxyConfigBypassRules(
    411     const net::ProxyBypassRules& bypass_rules) {
    412   // Should be called from UI thread.
    413   CheckCurrentlyOnUIThread();
    414   DCHECK(reference_config_.mode == ProxyConfig::MODE_SINGLE_PROXY ||
    415          reference_config_.mode == ProxyConfig::MODE_PROXY_PER_SCHEME);
    416   if (reference_config_.mode != ProxyConfig::MODE_SINGLE_PROXY &&
    417       reference_config_.mode != ProxyConfig::MODE_PROXY_PER_SCHEME) {
    418     VLOG(1) << "Cannot set bypass rules for proxy mode ["
    419              << reference_config_.mode << "]";
    420     return false;
    421   }
    422   reference_config_.bypass_rules = bypass_rules;
    423   OnUISetProxyConfig(persist_to_device_);
    424   return true;
    425 }
    426 
    427 void ProxyConfigServiceImpl::AddObserver(
    428     net::ProxyConfigService::Observer* observer) {
    429   // Should be called from IO thread.
    430   CheckCurrentlyOnIOThread();
    431   observers_.AddObserver(observer);
    432 }
    433 
    434 void ProxyConfigServiceImpl::RemoveObserver(
    435     net::ProxyConfigService::Observer* observer) {
    436   // Should be called from IO thread.
    437   CheckCurrentlyOnIOThread();
    438   observers_.RemoveObserver(observer);
    439 }
    440 
    441 net::ProxyConfigService::ConfigAvailability
    442     ProxyConfigServiceImpl::IOGetProxyConfig(net::ProxyConfig* net_config) {
    443   // Should be called from IO thread.
    444   CheckCurrentlyOnIOThread();
    445   if (config_availability_ == net::ProxyConfigService::CONFIG_VALID)
    446     cached_config_.ToNetProxyConfig(net_config);
    447 
    448   return config_availability_;
    449 }
    450 
    451 void ProxyConfigServiceImpl::OnSettingsOpCompleted(
    452     SignedSettings::ReturnCode code,
    453     bool value) {
    454   if (SignedSettings::SUCCESS == code)
    455     VLOG(1) << "Stored proxy setting to device";
    456   else
    457     LOG(WARNING) << "Error storing proxy setting to device";
    458   store_property_op_ = NULL;
    459   if (persist_to_device_pending_)
    460     PersistConfigToDevice();
    461 }
    462 
    463 void ProxyConfigServiceImpl::OnSettingsOpCompleted(
    464     SignedSettings::ReturnCode code,
    465     std::string value) {
    466   retrieve_property_op_ = NULL;
    467   if (SignedSettings::SUCCESS == code) {
    468     VLOG(1) << "Retrieved proxy setting from device, value=[" << value << "]";
    469     if (reference_config_.Deserialize(value)) {
    470       IOSetProxyConfig(reference_config_,
    471                        net::ProxyConfigService::CONFIG_VALID);
    472       return;
    473     } else {
    474       LOG(WARNING) << "Error deserializing device's proxy setting";
    475     }
    476   } else {
    477     LOG(WARNING) << "Error retrieving proxy setting from device";
    478   }
    479 
    480   // Update the configuration state on the IO thread.
    481   IOSetProxyConfig(reference_config_, net::ProxyConfigService::CONFIG_UNSET);
    482 }
    483 
    484 //------------------ ProxyConfigServiceImpl: private methods -------------------
    485 
    486 void ProxyConfigServiceImpl::PersistConfigToDevice() {
    487   DCHECK(!store_property_op_);
    488   persist_to_device_pending_ = false;
    489   std::string value;
    490   if (!reference_config_.Serialize(&value)) {
    491     LOG(WARNING) << "Error serializing proxy config";
    492     return;
    493   }
    494   store_property_op_ = SignedSettings::CreateStorePropertyOp(
    495       kSettingProxyEverywhere, value, this);
    496   store_property_op_->Execute();
    497   VLOG(1) << "Start storing proxy setting to device, value=" << value;
    498 }
    499 
    500 void ProxyConfigServiceImpl::OnUISetProxyConfig(bool persist_to_device) {
    501   IOSetProxyConfig(reference_config_, net::ProxyConfigService::CONFIG_VALID);
    502   if (persist_to_device && CrosLibrary::Get()->EnsureLoaded()) {
    503     if (store_property_op_) {
    504       persist_to_device_pending_ = true;
    505       VLOG(1) << "Pending persisting proxy setting to device";
    506     } else {
    507       PersistConfigToDevice();
    508     }
    509   }
    510 }
    511 
    512 void ProxyConfigServiceImpl::IOSetProxyConfig(
    513     const ProxyConfig& new_config,
    514     net::ProxyConfigService::ConfigAvailability new_availability) {
    515   if (!BrowserThread::CurrentlyOn(BrowserThread::IO) && can_post_task_) {
    516     // Posts a task to IO thread with the new config, so it can update
    517     // |cached_config_|.
    518     Task* task = NewRunnableMethod(this,
    519                                    &ProxyConfigServiceImpl::IOSetProxyConfig,
    520                                    new_config,
    521                                    new_availability);
    522     if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task))
    523       VLOG(1) << "Couldn't post task to IO thread to set new proxy config";
    524     return;
    525   }
    526 
    527   // Now guaranteed to be on the correct thread.
    528   VLOG(1) << "Proxy configuration changed";
    529   cached_config_ = new_config;
    530   config_availability_ = new_availability;
    531   // Notify observers of new proxy config.
    532   net::ProxyConfig net_config;
    533   cached_config_.ToNetProxyConfig(&net_config);
    534   FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_,
    535                     OnProxyConfigChanged(net_config, config_availability_));
    536 }
    537 
    538 void ProxyConfigServiceImpl::CheckCurrentlyOnIOThread() {
    539   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    540 }
    541 
    542 void ProxyConfigServiceImpl::CheckCurrentlyOnUIThread() {
    543   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    544 }
    545 
    546 }  // namespace chromeos
    547