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.
5 #include "chromeos/network/onc/onc_utils.h"
7 #include "base/base64.h"
8 #include "base/json/json_reader.h"
9 #include "base/logging.h"
10 #include "base/metrics/histogram.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "base/values.h"
14 #include "chromeos/network/network_event_log.h"
15 #include "chromeos/network/onc/onc_mapper.h"
16 #include "chromeos/network/onc/onc_signature.h"
17 #include "chromeos/network/onc/onc_utils.h"
18 #include "chromeos/network/onc/onc_validator.h"
19 #include "components/device_event_log/device_event_log.h"
20 #include "components/proxy_config/proxy_config_dictionary.h"
21 #include "crypto/encryptor.h"
22 #include "crypto/hmac.h"
23 #include "crypto/symmetric_key.h"
24 #include "net/base/host_port_pair.h"
25 #include "net/cert/pem_tokenizer.h"
26 #include "net/cert/x509_certificate.h"
27 #include "net/proxy/proxy_bypass_rules.h"
28 #include "net/proxy/proxy_config.h"
29 #include "net/proxy/proxy_server.h"
30 #include "url/url_constants.h"
32 using namespace ::onc
;
39 const char kUnableToDecrypt
[] = "Unable to decrypt encrypted ONC";
40 const char kUnableToDecode
[] = "Unable to decode encrypted ONC";
44 const char kEmptyUnencryptedConfiguration
[] =
45 "{\"Type\":\"UnencryptedConfiguration\",\"NetworkConfigurations\":[],"
46 "\"Certificates\":[]}";
48 scoped_ptr
<base::DictionaryValue
> ReadDictionaryFromJson(
49 const std::string
& json
) {
51 base::Value
* root
= base::JSONReader::DeprecatedReadAndReturnError(
52 json
, base::JSON_ALLOW_TRAILING_COMMAS
, nullptr, &error
);
54 base::DictionaryValue
* dict_ptr
= nullptr;
55 if (!root
|| !root
->GetAsDictionary(&dict_ptr
)) {
56 NET_LOG(ERROR
) << "Invalid JSON Dictionary: " << error
;
60 return make_scoped_ptr(dict_ptr
);
63 scoped_ptr
<base::DictionaryValue
> Decrypt(const std::string
& passphrase
,
64 const base::DictionaryValue
& root
) {
65 const int kKeySizeInBits
= 256;
66 const int kMaxIterationCount
= 500000;
68 std::string initial_vector
;
71 std::string stretch_method
;
72 std::string hmac_method
;
75 std::string ciphertext
;
77 if (!root
.GetString(encrypted::kCiphertext
, &ciphertext
) ||
78 !root
.GetString(encrypted::kCipher
, &cipher
) ||
79 !root
.GetString(encrypted::kHMAC
, &hmac
) ||
80 !root
.GetString(encrypted::kHMACMethod
, &hmac_method
) ||
81 !root
.GetString(encrypted::kIV
, &initial_vector
) ||
82 !root
.GetInteger(encrypted::kIterations
, &iterations
) ||
83 !root
.GetString(encrypted::kSalt
, &salt
) ||
84 !root
.GetString(encrypted::kStretch
, &stretch_method
) ||
85 !root
.GetString(toplevel_config::kType
, &onc_type
) ||
86 onc_type
!= toplevel_config::kEncryptedConfiguration
) {
87 NET_LOG(ERROR
) << "Encrypted ONC malformed.";
91 if (hmac_method
!= encrypted::kSHA1
||
92 cipher
!= encrypted::kAES256
||
93 stretch_method
!= encrypted::kPBKDF2
) {
94 NET_LOG(ERROR
) << "Encrypted ONC unsupported encryption scheme.";
98 // Make sure iterations != 0, since that's not valid.
99 if (iterations
== 0) {
100 NET_LOG(ERROR
) << kUnableToDecrypt
;
104 // Simply a sanity check to make sure we can't lock up the machine
105 // for too long with a huge number (or a negative number).
106 if (iterations
< 0 || iterations
> kMaxIterationCount
) {
107 NET_LOG(ERROR
) << "Too many iterations in encrypted ONC";
111 if (!base::Base64Decode(salt
, &salt
)) {
112 NET_LOG(ERROR
) << kUnableToDecode
;
116 scoped_ptr
<crypto::SymmetricKey
> key(
117 crypto::SymmetricKey::DeriveKeyFromPassword(crypto::SymmetricKey::AES
,
123 if (!base::Base64Decode(initial_vector
, &initial_vector
)) {
124 NET_LOG(ERROR
) << kUnableToDecode
;
127 if (!base::Base64Decode(ciphertext
, &ciphertext
)) {
128 NET_LOG(ERROR
) << kUnableToDecode
;
131 if (!base::Base64Decode(hmac
, &hmac
)) {
132 NET_LOG(ERROR
) << kUnableToDecode
;
136 crypto::HMAC
hmac_verifier(crypto::HMAC::SHA1
);
137 if (!hmac_verifier
.Init(key
.get()) ||
138 !hmac_verifier
.Verify(ciphertext
, hmac
)) {
139 NET_LOG(ERROR
) << kUnableToDecrypt
;
143 crypto::Encryptor decryptor
;
144 if (!decryptor
.Init(key
.get(), crypto::Encryptor::CBC
, initial_vector
)) {
145 NET_LOG(ERROR
) << kUnableToDecrypt
;
149 std::string plaintext
;
150 if (!decryptor
.Decrypt(ciphertext
, &plaintext
)) {
151 NET_LOG(ERROR
) << kUnableToDecrypt
;
155 scoped_ptr
<base::DictionaryValue
> new_root
=
156 ReadDictionaryFromJson(plaintext
);
158 NET_LOG(ERROR
) << "Property dictionary malformed.";
162 return new_root
.Pass();
165 std::string
GetSourceAsString(ONCSource source
) {
167 case ONC_SOURCE_UNKNOWN
:
169 case ONC_SOURCE_NONE
:
171 case ONC_SOURCE_DEVICE_POLICY
:
172 return "device policy";
173 case ONC_SOURCE_USER_POLICY
:
174 return "user policy";
175 case ONC_SOURCE_USER_IMPORT
:
176 return "user import";
178 NOTREACHED() << "unknown ONC source " << source
;
182 void ExpandField(const std::string
& fieldname
,
183 const StringSubstitution
& substitution
,
184 base::DictionaryValue
* onc_object
) {
185 std::string user_string
;
186 if (!onc_object
->GetStringWithoutPathExpansion(fieldname
, &user_string
))
189 std::string login_id
;
190 if (substitution
.GetSubstitute(substitutes::kLoginIDField
, &login_id
)) {
191 base::ReplaceSubstringsAfterOffset(&user_string
, 0,
192 substitutes::kLoginIDField
,
197 if (substitution
.GetSubstitute(substitutes::kEmailField
, &email
)) {
198 base::ReplaceSubstringsAfterOffset(&user_string
, 0,
199 substitutes::kEmailField
,
203 onc_object
->SetStringWithoutPathExpansion(fieldname
, user_string
);
206 void ExpandStringsInOncObject(
207 const OncValueSignature
& signature
,
208 const StringSubstitution
& substitution
,
209 base::DictionaryValue
* onc_object
) {
210 if (&signature
== &kEAPSignature
) {
211 ExpandField(eap::kAnonymousIdentity
, substitution
, onc_object
);
212 ExpandField(eap::kIdentity
, substitution
, onc_object
);
213 } else if (&signature
== &kL2TPSignature
||
214 &signature
== &kOpenVPNSignature
) {
215 ExpandField(vpn::kUsername
, substitution
, onc_object
);
218 // Recurse into nested objects.
219 for (base::DictionaryValue::Iterator
it(*onc_object
); !it
.IsAtEnd();
221 base::DictionaryValue
* inner_object
= nullptr;
222 if (!onc_object
->GetDictionaryWithoutPathExpansion(it
.key(), &inner_object
))
225 const OncFieldSignature
* field_signature
=
226 GetFieldSignature(signature
, it
.key());
227 if (!field_signature
)
230 ExpandStringsInOncObject(*field_signature
->value_signature
,
231 substitution
, inner_object
);
235 void ExpandStringsInNetworks(const StringSubstitution
& substitution
,
236 base::ListValue
* network_configs
) {
237 for (base::Value
* entry
: *network_configs
) {
238 base::DictionaryValue
* network
= nullptr;
239 entry
->GetAsDictionary(&network
);
241 ExpandStringsInOncObject(
242 kNetworkConfigurationSignature
, substitution
, network
);
246 void FillInHexSSIDFieldsInOncObject(const OncValueSignature
& signature
,
247 base::DictionaryValue
* onc_object
) {
248 if (&signature
== &kWiFiSignature
)
249 FillInHexSSIDField(onc_object
);
251 // Recurse into nested objects.
252 for (base::DictionaryValue::Iterator
it(*onc_object
); !it
.IsAtEnd();
254 base::DictionaryValue
* inner_object
= nullptr;
255 if (!onc_object
->GetDictionaryWithoutPathExpansion(it
.key(), &inner_object
))
258 const OncFieldSignature
* field_signature
=
259 GetFieldSignature(signature
, it
.key());
260 if (!field_signature
)
263 FillInHexSSIDFieldsInOncObject(*field_signature
->value_signature
,
268 void FillInHexSSIDField(base::DictionaryValue
* wifi_fields
) {
269 std::string ssid_string
;
270 if (wifi_fields
->HasKey(::onc::wifi::kHexSSID
) ||
271 !wifi_fields
->GetStringWithoutPathExpansion(::onc::wifi::kSSID
,
275 if (ssid_string
.empty()) {
276 NET_LOG(ERROR
) << "Found empty SSID field.";
279 wifi_fields
->SetStringWithoutPathExpansion(
280 ::onc::wifi::kHexSSID
,
281 base::HexEncode(ssid_string
.c_str(), ssid_string
.size()));
286 class OncMaskValues
: public Mapper
{
288 static scoped_ptr
<base::DictionaryValue
> Mask(
289 const OncValueSignature
& signature
,
290 const base::DictionaryValue
& onc_object
,
291 const std::string
& mask
) {
292 OncMaskValues
masker(mask
);
294 return masker
.MapObject(signature
, onc_object
, &unused_error
);
298 explicit OncMaskValues(const std::string
& mask
)
302 scoped_ptr
<base::Value
> MapField(const std::string
& field_name
,
303 const OncValueSignature
& object_signature
,
304 const base::Value
& onc_value
,
305 bool* found_unknown_field
,
306 bool* error
) override
{
307 if (FieldIsCredential(object_signature
, field_name
)) {
308 return scoped_ptr
<base::Value
>(new base::StringValue(mask_
));
310 return Mapper::MapField(field_name
, object_signature
, onc_value
,
311 found_unknown_field
, error
);
315 // Mask to insert in place of the sensitive values.
321 scoped_ptr
<base::DictionaryValue
> MaskCredentialsInOncObject(
322 const OncValueSignature
& signature
,
323 const base::DictionaryValue
& onc_object
,
324 const std::string
& mask
) {
325 return OncMaskValues::Mask(signature
, onc_object
, mask
);
330 std::string
DecodePEM(const std::string
& pem_encoded
) {
331 // The PEM block header used for DER certificates
332 const char kCertificateHeader
[] = "CERTIFICATE";
334 // This is an older PEM marker for DER certificates.
335 const char kX509CertificateHeader
[] = "X509 CERTIFICATE";
337 std::vector
<std::string
> pem_headers
;
338 pem_headers
.push_back(kCertificateHeader
);
339 pem_headers
.push_back(kX509CertificateHeader
);
341 net::PEMTokenizer
pem_tokenizer(pem_encoded
, pem_headers
);
343 if (pem_tokenizer
.GetNext()) {
344 decoded
= pem_tokenizer
.data();
346 // If we failed to read the data as a PEM file, then try plain base64 decode
347 // in case the PEM marker strings are missing. For this to work, there has
348 // to be no white space, and it has to only contain the base64-encoded data.
349 if (!base::Base64Decode(pem_encoded
, &decoded
)) {
350 LOG(ERROR
) << "Unable to base64 decode X509 data: " << pem_encoded
;
351 return std::string();
357 CertPEMsByGUIDMap
GetServerAndCACertsByGUID(
358 const base::ListValue
& certificates
) {
359 CertPEMsByGUIDMap certs_by_guid
;
360 for (const base::Value
* entry
: certificates
) {
361 const base::DictionaryValue
* cert
= nullptr;
362 entry
->GetAsDictionary(&cert
);
365 cert
->GetStringWithoutPathExpansion(certificate::kGUID
, &guid
);
366 std::string cert_type
;
367 cert
->GetStringWithoutPathExpansion(certificate::kType
, &cert_type
);
368 if (cert_type
!= certificate::kServer
&&
369 cert_type
!= certificate::kAuthority
) {
372 std::string x509_data
;
373 cert
->GetStringWithoutPathExpansion(certificate::kX509
, &x509_data
);
375 std::string der
= DecodePEM(x509_data
);
377 if (der
.empty() || !net::X509Certificate::GetPEMEncodedFromDER(der
, &pem
)) {
378 LOG(ERROR
) << "Certificate with GUID " << guid
379 << " is not in PEM encoding.";
382 certs_by_guid
[guid
] = pem
;
385 return certs_by_guid
;
388 void FillInHexSSIDFieldsInNetworks(base::ListValue
* network_configs
) {
389 for (base::Value
* entry
: *network_configs
) {
390 base::DictionaryValue
* network
= nullptr;
391 entry
->GetAsDictionary(&network
);
393 FillInHexSSIDFieldsInOncObject(kNetworkConfigurationSignature
, network
);
399 bool ParseAndValidateOncForImport(const std::string
& onc_blob
,
400 ONCSource onc_source
,
401 const std::string
& passphrase
,
402 base::ListValue
* network_configs
,
403 base::DictionaryValue
* global_network_config
,
404 base::ListValue
* certificates
) {
405 network_configs
->Clear();
406 global_network_config
->Clear();
407 certificates
->Clear();
408 if (onc_blob
.empty())
411 scoped_ptr
<base::DictionaryValue
> toplevel_onc
=
412 ReadDictionaryFromJson(onc_blob
);
414 LOG(ERROR
) << "ONC loaded from " << GetSourceAsString(onc_source
)
415 << " is not a valid JSON dictionary.";
419 // Check and see if this is an encrypted ONC file. If so, decrypt it.
420 std::string onc_type
;
421 toplevel_onc
->GetStringWithoutPathExpansion(toplevel_config::kType
,
423 if (onc_type
== toplevel_config::kEncryptedConfiguration
) {
424 toplevel_onc
= Decrypt(passphrase
, *toplevel_onc
);
426 LOG(ERROR
) << "Couldn't decrypt the ONC from "
427 << GetSourceAsString(onc_source
);
432 bool from_policy
= (onc_source
== ONC_SOURCE_USER_POLICY
||
433 onc_source
== ONC_SOURCE_DEVICE_POLICY
);
435 // Validate the ONC dictionary. We are liberal and ignore unknown field
436 // names and ignore invalid field names in kRecommended arrays.
437 Validator
validator(false, // Ignore unknown fields.
438 false, // Ignore invalid recommended field names.
439 true, // Fail on missing fields.
441 validator
.SetOncSource(onc_source
);
443 Validator::Result validation_result
;
444 toplevel_onc
= validator
.ValidateAndRepairObject(
445 &kToplevelConfigurationSignature
,
450 UMA_HISTOGRAM_BOOLEAN("Enterprise.ONC.PolicyValidation",
451 validation_result
== Validator::VALID
);
455 if (validation_result
== Validator::VALID_WITH_WARNINGS
) {
456 LOG(WARNING
) << "ONC from " << GetSourceAsString(onc_source
)
457 << " produced warnings.";
459 } else if (validation_result
== Validator::INVALID
|| !toplevel_onc
) {
460 LOG(ERROR
) << "ONC from " << GetSourceAsString(onc_source
)
461 << " is invalid and couldn't be repaired.";
465 base::ListValue
* validated_certs
= nullptr;
466 if (toplevel_onc
->GetListWithoutPathExpansion(toplevel_config::kCertificates
,
468 certificates
->Swap(validated_certs
);
471 base::ListValue
* validated_networks
= nullptr;
472 if (toplevel_onc
->GetListWithoutPathExpansion(
473 toplevel_config::kNetworkConfigurations
, &validated_networks
)) {
474 FillInHexSSIDFieldsInNetworks(validated_networks
);
476 CertPEMsByGUIDMap server_and_ca_certs
=
477 GetServerAndCACertsByGUID(*certificates
);
479 if (!ResolveServerCertRefsInNetworks(server_and_ca_certs
,
480 validated_networks
)) {
481 LOG(ERROR
) << "Some certificate references in the ONC policy for source "
482 << GetSourceAsString(onc_source
) << " could not be resolved.";
486 network_configs
->Swap(validated_networks
);
489 base::DictionaryValue
* validated_global_config
= nullptr;
490 if (toplevel_onc
->GetDictionaryWithoutPathExpansion(
491 toplevel_config::kGlobalNetworkConfiguration
,
492 &validated_global_config
)) {
493 global_network_config
->Swap(validated_global_config
);
499 scoped_refptr
<net::X509Certificate
> DecodePEMCertificate(
500 const std::string
& pem_encoded
) {
501 std::string decoded
= DecodePEM(pem_encoded
);
502 scoped_refptr
<net::X509Certificate
> cert
=
503 net::X509Certificate::CreateFromBytes(decoded
.data(), decoded
.size());
504 LOG_IF(ERROR
, !cert
.get()) << "Couldn't create certificate from X509 data: "
511 bool GUIDRefToPEMEncoding(const CertPEMsByGUIDMap
& certs_by_guid
,
512 const std::string
& guid_ref
,
513 std::string
* pem_encoded
) {
514 CertPEMsByGUIDMap::const_iterator it
= certs_by_guid
.find(guid_ref
);
515 if (it
== certs_by_guid
.end()) {
516 LOG(ERROR
) << "Couldn't resolve certificate reference " << guid_ref
;
519 *pem_encoded
= it
->second
;
520 if (pem_encoded
->empty()) {
521 LOG(ERROR
) << "Couldn't PEM-encode certificate with GUID " << guid_ref
;
527 bool ResolveSingleCertRef(const CertPEMsByGUIDMap
& certs_by_guid
,
528 const std::string
& key_guid_ref
,
529 const std::string
& key_pem
,
530 base::DictionaryValue
* onc_object
) {
531 std::string guid_ref
;
532 if (!onc_object
->GetStringWithoutPathExpansion(key_guid_ref
, &guid_ref
))
535 std::string pem_encoded
;
536 if (!GUIDRefToPEMEncoding(certs_by_guid
, guid_ref
, &pem_encoded
))
539 onc_object
->RemoveWithoutPathExpansion(key_guid_ref
, nullptr);
540 onc_object
->SetStringWithoutPathExpansion(key_pem
, pem_encoded
);
544 bool ResolveCertRefList(const CertPEMsByGUIDMap
& certs_by_guid
,
545 const std::string
& key_guid_ref_list
,
546 const std::string
& key_pem_list
,
547 base::DictionaryValue
* onc_object
) {
548 const base::ListValue
* guid_ref_list
= nullptr;
549 if (!onc_object
->GetListWithoutPathExpansion(key_guid_ref_list
,
554 scoped_ptr
<base::ListValue
> pem_list(new base::ListValue
);
555 for (const base::Value
* entry
: *guid_ref_list
) {
556 std::string guid_ref
;
557 entry
->GetAsString(&guid_ref
);
559 std::string pem_encoded
;
560 if (!GUIDRefToPEMEncoding(certs_by_guid
, guid_ref
, &pem_encoded
))
563 pem_list
->AppendString(pem_encoded
);
566 onc_object
->RemoveWithoutPathExpansion(key_guid_ref_list
, nullptr);
567 onc_object
->SetWithoutPathExpansion(key_pem_list
, pem_list
.release());
571 bool ResolveSingleCertRefToList(const CertPEMsByGUIDMap
& certs_by_guid
,
572 const std::string
& key_guid_ref
,
573 const std::string
& key_pem_list
,
574 base::DictionaryValue
* onc_object
) {
575 std::string guid_ref
;
576 if (!onc_object
->GetStringWithoutPathExpansion(key_guid_ref
, &guid_ref
))
579 std::string pem_encoded
;
580 if (!GUIDRefToPEMEncoding(certs_by_guid
, guid_ref
, &pem_encoded
))
583 scoped_ptr
<base::ListValue
> pem_list(new base::ListValue
);
584 pem_list
->AppendString(pem_encoded
);
585 onc_object
->RemoveWithoutPathExpansion(key_guid_ref
, nullptr);
586 onc_object
->SetWithoutPathExpansion(key_pem_list
, pem_list
.release());
590 // Resolves the reference list at |key_guid_refs| if present and otherwise the
591 // single reference at |key_guid_ref|. Returns whether the respective resolving
593 bool ResolveCertRefsOrRefToList(const CertPEMsByGUIDMap
& certs_by_guid
,
594 const std::string
& key_guid_refs
,
595 const std::string
& key_guid_ref
,
596 const std::string
& key_pem_list
,
597 base::DictionaryValue
* onc_object
) {
598 if (onc_object
->HasKey(key_guid_refs
)) {
599 if (onc_object
->HasKey(key_guid_ref
)) {
600 LOG(ERROR
) << "Found both " << key_guid_refs
<< " and " << key_guid_ref
601 << ". Ignoring and removing the latter.";
602 onc_object
->RemoveWithoutPathExpansion(key_guid_ref
, nullptr);
604 return ResolveCertRefList(
605 certs_by_guid
, key_guid_refs
, key_pem_list
, onc_object
);
608 // Only resolve |key_guid_ref| if |key_guid_refs| isn't present.
609 return ResolveSingleCertRefToList(
610 certs_by_guid
, key_guid_ref
, key_pem_list
, onc_object
);
613 bool ResolveServerCertRefsInObject(const CertPEMsByGUIDMap
& certs_by_guid
,
614 const OncValueSignature
& signature
,
615 base::DictionaryValue
* onc_object
) {
616 if (&signature
== &kCertificatePatternSignature
) {
617 if (!ResolveCertRefList(certs_by_guid
,
618 client_cert::kIssuerCARef
,
619 client_cert::kIssuerCAPEMs
,
623 } else if (&signature
== &kEAPSignature
) {
624 if (!ResolveCertRefsOrRefToList(certs_by_guid
,
631 } else if (&signature
== &kIPsecSignature
) {
632 if (!ResolveCertRefsOrRefToList(certs_by_guid
,
633 ipsec::kServerCARefs
,
635 ipsec::kServerCAPEMs
,
639 } else if (&signature
== &kIPsecSignature
||
640 &signature
== &kOpenVPNSignature
) {
641 if (!ResolveSingleCertRef(certs_by_guid
,
642 openvpn::kServerCertRef
,
643 openvpn::kServerCertPEM
,
645 !ResolveCertRefsOrRefToList(certs_by_guid
,
646 openvpn::kServerCARefs
,
647 openvpn::kServerCARef
,
648 openvpn::kServerCAPEMs
,
654 // Recurse into nested objects.
655 for (base::DictionaryValue::Iterator
it(*onc_object
); !it
.IsAtEnd();
657 base::DictionaryValue
* inner_object
= nullptr;
658 if (!onc_object
->GetDictionaryWithoutPathExpansion(it
.key(), &inner_object
))
661 const OncFieldSignature
* field_signature
=
662 GetFieldSignature(signature
, it
.key());
663 if (!field_signature
)
666 if (!ResolveServerCertRefsInObject(certs_by_guid
,
667 *field_signature
->value_signature
,
677 bool ResolveServerCertRefsInNetworks(const CertPEMsByGUIDMap
& certs_by_guid
,
678 base::ListValue
* network_configs
) {
680 for (base::ListValue::iterator it
= network_configs
->begin();
681 it
!= network_configs
->end(); ) {
682 base::DictionaryValue
* network
= nullptr;
683 (*it
)->GetAsDictionary(&network
);
684 if (!ResolveServerCertRefsInNetwork(certs_by_guid
, network
)) {
686 network
->GetStringWithoutPathExpansion(network_config::kGUID
, &guid
);
687 // This might happen even with correct validation, if the referenced
688 // certificate couldn't be imported.
689 LOG(ERROR
) << "Couldn't resolve some certificate reference of network "
691 it
= network_configs
->Erase(it
, nullptr);
700 bool ResolveServerCertRefsInNetwork(const CertPEMsByGUIDMap
& certs_by_guid
,
701 base::DictionaryValue
* network_config
) {
702 return ResolveServerCertRefsInObject(certs_by_guid
,
703 kNetworkConfigurationSignature
,
707 NetworkTypePattern
NetworkTypePatternFromOncType(const std::string
& type
) {
708 if (type
== ::onc::network_type::kAllTypes
)
709 return NetworkTypePattern::Default();
710 if (type
== ::onc::network_type::kCellular
)
711 return NetworkTypePattern::Cellular();
712 if (type
== ::onc::network_type::kEthernet
)
713 return NetworkTypePattern::Ethernet();
714 if (type
== ::onc::network_type::kVPN
)
715 return NetworkTypePattern::VPN();
716 if (type
== ::onc::network_type::kWiFi
)
717 return NetworkTypePattern::WiFi();
718 if (type
== ::onc::network_type::kWimax
)
719 return NetworkTypePattern::Wimax();
720 if (type
== ::onc::network_type::kWireless
)
721 return NetworkTypePattern::Wireless();
722 NOTREACHED() << "Unrecognized ONC type: " << type
;
723 return NetworkTypePattern::Default();
726 bool IsRecommendedValue(const base::DictionaryValue
* onc
,
727 const std::string
& property_key
) {
728 std::string property_basename
, recommended_property_key
;
729 size_t pos
= property_key
.find_last_of('.');
730 if (pos
!= std::string::npos
) {
731 // 'WiFi.AutoConnect' -> 'AutoConnect', 'WiFi.Recommended'
732 property_basename
= property_key
.substr(pos
+ 1);
733 recommended_property_key
=
734 property_key
.substr(0, pos
+ 1) + ::onc::kRecommended
;
736 // 'Name' -> 'Name', 'Recommended'
737 property_basename
= property_key
;
738 recommended_property_key
= ::onc::kRecommended
;
741 const base::ListValue
* recommended_keys
= nullptr;
742 return (onc
->GetList(recommended_property_key
, &recommended_keys
) &&
743 recommended_keys
->Find(base::StringValue(property_basename
)) !=
744 recommended_keys
->end());
749 const char kDirectScheme
[] = "direct";
750 const char kQuicScheme
[] = "quic";
751 const char kSocksScheme
[] = "socks";
752 const char kSocks4Scheme
[] = "socks4";
753 const char kSocks5Scheme
[] = "socks5";
755 net::ProxyServer
ConvertOncProxyLocationToHostPort(
756 net::ProxyServer::Scheme default_proxy_scheme
,
757 const base::DictionaryValue
& onc_proxy_location
) {
759 onc_proxy_location
.GetStringWithoutPathExpansion(::onc::proxy::kHost
, &host
);
760 // Parse |host| according to the format [<scheme>"://"]<server>[":"<port>].
761 net::ProxyServer proxy_server
=
762 net::ProxyServer::FromURI(host
, default_proxy_scheme
);
764 onc_proxy_location
.GetIntegerWithoutPathExpansion(::onc::proxy::kPort
, &port
);
766 // Replace the port parsed from |host| by the provided |port|.
767 return net::ProxyServer(
768 proxy_server
.scheme(),
769 net::HostPortPair(proxy_server
.host_port_pair().host(),
770 static_cast<uint16
>(port
)));
773 void AppendProxyServerForScheme(const base::DictionaryValue
& onc_manual
,
774 const std::string
& onc_scheme
,
776 const base::DictionaryValue
* onc_proxy_location
= nullptr;
777 if (!onc_manual
.GetDictionaryWithoutPathExpansion(onc_scheme
,
778 &onc_proxy_location
)) {
782 net::ProxyServer::Scheme default_proxy_scheme
= net::ProxyServer::SCHEME_HTTP
;
783 std::string url_scheme
;
784 if (onc_scheme
== ::onc::proxy::kFtp
) {
785 url_scheme
= url::kFtpScheme
;
786 } else if (onc_scheme
== ::onc::proxy::kHttp
) {
787 url_scheme
= url::kHttpScheme
;
788 } else if (onc_scheme
== ::onc::proxy::kHttps
) {
789 url_scheme
= url::kHttpsScheme
;
790 } else if (onc_scheme
== ::onc::proxy::kSocks
) {
791 default_proxy_scheme
= net::ProxyServer::SCHEME_SOCKS4
;
792 url_scheme
= kSocksScheme
;
797 net::ProxyServer proxy_server
= ConvertOncProxyLocationToHostPort(
798 default_proxy_scheme
, *onc_proxy_location
);
800 ProxyConfigDictionary::EncodeAndAppendProxyServer(url_scheme
, proxy_server
,
804 net::ProxyBypassRules
ConvertOncExcludeDomainsToBypassRules(
805 const base::ListValue
& onc_exclude_domains
) {
806 net::ProxyBypassRules rules
;
807 for (base::ListValue::const_iterator it
= onc_exclude_domains
.begin();
808 it
!= onc_exclude_domains
.end(); ++it
) {
810 (*it
)->GetAsString(&rule
);
811 rules
.AddRuleFromString(rule
);
816 std::string
SchemeToString(net::ProxyServer::Scheme scheme
) {
818 case net::ProxyServer::SCHEME_DIRECT
:
819 return kDirectScheme
;
820 case net::ProxyServer::SCHEME_HTTP
:
821 return url::kHttpScheme
;
822 case net::ProxyServer::SCHEME_SOCKS4
:
823 return kSocks4Scheme
;
824 case net::ProxyServer::SCHEME_SOCKS5
:
825 return kSocks5Scheme
;
826 case net::ProxyServer::SCHEME_HTTPS
:
827 return url::kHttpsScheme
;
828 case net::ProxyServer::SCHEME_QUIC
:
830 case net::ProxyServer::SCHEME_INVALID
:
837 void SetProxyForScheme(const net::ProxyConfig::ProxyRules
& proxy_rules
,
838 const std::string
& scheme
,
839 const std::string
& onc_scheme
,
840 base::DictionaryValue
* dict
) {
841 const net::ProxyList
* proxy_list
= nullptr;
842 if (proxy_rules
.type
== net::ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY
) {
843 proxy_list
= &proxy_rules
.single_proxies
;
844 } else if (proxy_rules
.type
==
845 net::ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME
) {
846 proxy_list
= proxy_rules
.MapUrlSchemeToProxyList(scheme
);
848 if (!proxy_list
|| proxy_list
->IsEmpty())
850 const net::ProxyServer
& server
= proxy_list
->Get();
851 scoped_ptr
<base::DictionaryValue
> url_dict(new base::DictionaryValue
);
852 std::string host
= server
.host_port_pair().host();
854 // For all proxy types except SOCKS, the default scheme of the proxy host is
856 net::ProxyServer::Scheme default_scheme
=
857 (onc_scheme
== ::onc::proxy::kSocks
) ? net::ProxyServer::SCHEME_SOCKS4
858 : net::ProxyServer::SCHEME_HTTP
;
859 // Only prefix the host with a non-default scheme.
860 if (server
.scheme() != default_scheme
)
861 host
= SchemeToString(server
.scheme()) + "://" + host
;
862 url_dict
->SetStringWithoutPathExpansion(::onc::proxy::kHost
, host
);
863 url_dict
->SetIntegerWithoutPathExpansion(::onc::proxy::kPort
,
864 server
.host_port_pair().port());
865 dict
->SetWithoutPathExpansion(onc_scheme
, url_dict
.release());
870 scoped_ptr
<base::DictionaryValue
> ConvertOncProxySettingsToProxyConfig(
871 const base::DictionaryValue
& onc_proxy_settings
) {
873 onc_proxy_settings
.GetStringWithoutPathExpansion(::onc::proxy::kType
, &type
);
874 scoped_ptr
<base::DictionaryValue
> proxy_dict
;
876 if (type
== ::onc::proxy::kDirect
) {
877 proxy_dict
.reset(ProxyConfigDictionary::CreateDirect());
878 } else if (type
== ::onc::proxy::kWPAD
) {
879 proxy_dict
.reset(ProxyConfigDictionary::CreateAutoDetect());
880 } else if (type
== ::onc::proxy::kPAC
) {
882 onc_proxy_settings
.GetStringWithoutPathExpansion(::onc::proxy::kPAC
,
885 DCHECK(url
.is_valid()) << "Invalid URL in ProxySettings.PAC";
886 proxy_dict
.reset(ProxyConfigDictionary::CreatePacScript(url
.spec(), false));
887 } else if (type
== ::onc::proxy::kManual
) {
888 const base::DictionaryValue
* manual_dict
= nullptr;
889 onc_proxy_settings
.GetDictionaryWithoutPathExpansion(::onc::proxy::kManual
,
891 std::string manual_spec
;
892 AppendProxyServerForScheme(*manual_dict
, ::onc::proxy::kFtp
, &manual_spec
);
893 AppendProxyServerForScheme(*manual_dict
, ::onc::proxy::kHttp
, &manual_spec
);
894 AppendProxyServerForScheme(*manual_dict
, ::onc::proxy::kSocks
,
896 AppendProxyServerForScheme(*manual_dict
, ::onc::proxy::kHttps
,
899 const base::ListValue
* exclude_domains
= nullptr;
900 net::ProxyBypassRules bypass_rules
;
901 if (onc_proxy_settings
.GetListWithoutPathExpansion(
902 ::onc::proxy::kExcludeDomains
, &exclude_domains
)) {
903 bypass_rules
.AssignFrom(
904 ConvertOncExcludeDomainsToBypassRules(*exclude_domains
));
906 proxy_dict
.reset(ProxyConfigDictionary::CreateFixedServers(
907 manual_spec
, bypass_rules
.ToString()));
911 return proxy_dict
.Pass();
914 scoped_ptr
<base::DictionaryValue
> ConvertProxyConfigToOncProxySettings(
915 const base::DictionaryValue
& proxy_config_value
) {
916 // Create a ProxyConfigDictionary from the DictionaryValue.
917 scoped_ptr
<ProxyConfigDictionary
> proxy_config(
918 new ProxyConfigDictionary(&proxy_config_value
));
920 // Create the result DictionaryValue and populate it.
921 scoped_ptr
<base::DictionaryValue
> proxy_settings(new base::DictionaryValue
);
922 ProxyPrefs::ProxyMode mode
;
923 if (!proxy_config
->GetMode(&mode
))
926 case ProxyPrefs::MODE_DIRECT
: {
927 proxy_settings
->SetStringWithoutPathExpansion(::onc::proxy::kType
,
928 ::onc::proxy::kDirect
);
931 case ProxyPrefs::MODE_AUTO_DETECT
: {
932 proxy_settings
->SetStringWithoutPathExpansion(::onc::proxy::kType
,
933 ::onc::proxy::kWPAD
);
936 case ProxyPrefs::MODE_PAC_SCRIPT
: {
937 proxy_settings
->SetStringWithoutPathExpansion(::onc::proxy::kType
,
940 proxy_config
->GetPacUrl(&pac_url
);
941 proxy_settings
->SetStringWithoutPathExpansion(::onc::proxy::kPAC
,
945 case ProxyPrefs::MODE_FIXED_SERVERS
: {
946 proxy_settings
->SetString(::onc::proxy::kType
, ::onc::proxy::kManual
);
947 scoped_ptr
<base::DictionaryValue
> manual(new base::DictionaryValue
);
948 std::string proxy_rules_string
;
949 if (proxy_config
->GetProxyServer(&proxy_rules_string
)) {
950 net::ProxyConfig::ProxyRules proxy_rules
;
951 proxy_rules
.ParseFromString(proxy_rules_string
);
952 SetProxyForScheme(proxy_rules
, url::kFtpScheme
, ::onc::proxy::kFtp
,
954 SetProxyForScheme(proxy_rules
, url::kHttpScheme
, ::onc::proxy::kHttp
,
956 SetProxyForScheme(proxy_rules
, url::kHttpsScheme
, ::onc::proxy::kHttps
,
958 SetProxyForScheme(proxy_rules
, kSocksScheme
, ::onc::proxy::kSocks
,
961 proxy_settings
->SetWithoutPathExpansion(::onc::proxy::kManual
,
964 // Convert the 'bypass_list' string into dictionary entries.
965 std::string bypass_rules_string
;
966 if (proxy_config
->GetBypassList(&bypass_rules_string
)) {
967 net::ProxyBypassRules bypass_rules
;
968 bypass_rules
.ParseFromString(bypass_rules_string
);
969 scoped_ptr
<base::ListValue
> exclude_domains(new base::ListValue
);
970 for (const net::ProxyBypassRules::Rule
* rule
: bypass_rules
.rules())
971 exclude_domains
->AppendString(rule
->ToString());
972 if (!exclude_domains
->empty()) {
973 proxy_settings
->SetWithoutPathExpansion(::onc::proxy::kExcludeDomains
,
974 exclude_domains
.release());
980 LOG(ERROR
) << "Unexpected proxy mode in Shill config: " << mode
;
984 return proxy_settings
.Pass();
988 } // namespace chromeos