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
SourceToString(ProxyConfigServiceImpl::ProxyConfig::Source source)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
operator <<(std::ostream & out,const ProxyConfigServiceImpl::ProxyConfig::ManualProxy & proxy)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
operator <<(std::ostream & out,const ProxyConfigServiceImpl::ProxyConfig & config)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
ProxyConfigToString(const ProxyConfigServiceImpl::ProxyConfig & proxy_config)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
CanBeWrittenByUser(bool user_is_owner)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
ToNetProxyConfig(net::ProxyConfig * net_config)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
CanBeWrittenByUser(bool user_is_owner,const std::string & scheme)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*
MapSchemeToProxy(const std::string & scheme)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
Serialize(std::string * output)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
Deserialize(const std::string & input)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
ToString() const290 std::string ProxyConfigServiceImpl::ProxyConfig::ToString() const {
291 return ProxyConfigToString(*this);
292 }
293
294 //----------- ProxyConfigServiceImpl::ProxyConfig: private methods -------------
295
296 // static
EncodeAndAppendProxyServer(const std::string & scheme,const net::ProxyServer & server,std::string * spec)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
ProxyConfigServiceImpl()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
ProxyConfigServiceImpl(const ProxyConfig & init_config)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
~ProxyConfigServiceImpl()350 ProxyConfigServiceImpl::~ProxyConfigServiceImpl() {
351 }
352
UIGetProxyConfig(ProxyConfig * config)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
UISetProxyConfigToDirect()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
UISetProxyConfigToAutoDetect()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
UISetProxyConfigToPACScript(const GURL & pac_url)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
UISetProxyConfigToSingleProxy(const net::ProxyServer & server)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
UISetProxyConfigToProxyPerScheme(const std::string & scheme,const net::ProxyServer & server)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
UISetProxyConfigBypassRules(const net::ProxyBypassRules & bypass_rules)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
AddObserver(net::ProxyConfigService::Observer * observer)427 void ProxyConfigServiceImpl::AddObserver(
428 net::ProxyConfigService::Observer* observer) {
429 // Should be called from IO thread.
430 CheckCurrentlyOnIOThread();
431 observers_.AddObserver(observer);
432 }
433
RemoveObserver(net::ProxyConfigService::Observer * observer)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
IOGetProxyConfig(net::ProxyConfig * net_config)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
OnSettingsOpCompleted(SignedSettings::ReturnCode code,bool value)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
OnSettingsOpCompleted(SignedSettings::ReturnCode code,std::string value)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
PersistConfigToDevice()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
OnUISetProxyConfig(bool persist_to_device)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
IOSetProxyConfig(const ProxyConfig & new_config,net::ProxyConfigService::ConfigAvailability new_availability)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
CheckCurrentlyOnIOThread()538 void ProxyConfigServiceImpl::CheckCurrentlyOnIOThread() {
539 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
540 }
541
CheckCurrentlyOnUIThread()542 void ProxyConfigServiceImpl::CheckCurrentlyOnUIThread() {
543 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
544 }
545
546 } // namespace chromeos
547