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/macros.h"
11 #include "base/metrics/histogram.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h"
14 #include "base/values.h"
15 #include "chromeos/network/network_event_log.h"
16 #include "chromeos/network/onc/onc_mapper.h"
17 #include "chromeos/network/onc/onc_signature.h"
18 #include "chromeos/network/onc/onc_utils.h"
19 #include "chromeos/network/onc/onc_validator.h"
20 #include "components/device_event_log/device_event_log.h"
21 #include "components/proxy_config/proxy_config_dictionary.h"
22 #include "crypto/encryptor.h"
23 #include "crypto/hmac.h"
24 #include "crypto/symmetric_key.h"
25 #include "net/base/host_port_pair.h"
26 #include "net/cert/pem_tokenizer.h"
27 #include "net/cert/x509_certificate.h"
28 #include "net/proxy/proxy_bypass_rules.h"
29 #include "net/proxy/proxy_config.h"
30 #include "net/proxy/proxy_server.h"
31 #include "url/url_constants.h"
33 using namespace ::onc
;
40 const char kUnableToDecrypt
[] = "Unable to decrypt encrypted ONC";
41 const char kUnableToDecode
[] = "Unable to decode encrypted ONC";
45 const char kEmptyUnencryptedConfiguration
[] =
46 "{\"Type\":\"UnencryptedConfiguration\",\"NetworkConfigurations\":[],"
47 "\"Certificates\":[]}";
49 scoped_ptr
<base::DictionaryValue
> ReadDictionaryFromJson(
50 const std::string
& json
) {
52 scoped_ptr
<base::Value
> root
= base::JSONReader::ReadAndReturnError(
53 json
, base::JSON_ALLOW_TRAILING_COMMAS
, nullptr, &error
);
55 base::DictionaryValue
* dict_ptr
= nullptr;
56 if (!root
|| !root
->GetAsDictionary(&dict_ptr
)) {
57 NET_LOG(ERROR
) << "Invalid JSON Dictionary: " << error
;
60 ignore_result(root
.release());
61 return make_scoped_ptr(dict_ptr
);
64 scoped_ptr
<base::DictionaryValue
> Decrypt(const std::string
& passphrase
,
65 const base::DictionaryValue
& root
) {
66 const int kKeySizeInBits
= 256;
67 const int kMaxIterationCount
= 500000;
69 std::string initial_vector
;
72 std::string stretch_method
;
73 std::string hmac_method
;
76 std::string ciphertext
;
78 if (!root
.GetString(encrypted::kCiphertext
, &ciphertext
) ||
79 !root
.GetString(encrypted::kCipher
, &cipher
) ||
80 !root
.GetString(encrypted::kHMAC
, &hmac
) ||
81 !root
.GetString(encrypted::kHMACMethod
, &hmac_method
) ||
82 !root
.GetString(encrypted::kIV
, &initial_vector
) ||
83 !root
.GetInteger(encrypted::kIterations
, &iterations
) ||
84 !root
.GetString(encrypted::kSalt
, &salt
) ||
85 !root
.GetString(encrypted::kStretch
, &stretch_method
) ||
86 !root
.GetString(toplevel_config::kType
, &onc_type
) ||
87 onc_type
!= toplevel_config::kEncryptedConfiguration
) {
88 NET_LOG(ERROR
) << "Encrypted ONC malformed.";
92 if (hmac_method
!= encrypted::kSHA1
||
93 cipher
!= encrypted::kAES256
||
94 stretch_method
!= encrypted::kPBKDF2
) {
95 NET_LOG(ERROR
) << "Encrypted ONC unsupported encryption scheme.";
99 // Make sure iterations != 0, since that's not valid.
100 if (iterations
== 0) {
101 NET_LOG(ERROR
) << kUnableToDecrypt
;
105 // Simply a sanity check to make sure we can't lock up the machine
106 // for too long with a huge number (or a negative number).
107 if (iterations
< 0 || iterations
> kMaxIterationCount
) {
108 NET_LOG(ERROR
) << "Too many iterations in encrypted ONC";
112 if (!base::Base64Decode(salt
, &salt
)) {
113 NET_LOG(ERROR
) << kUnableToDecode
;
117 scoped_ptr
<crypto::SymmetricKey
> key(
118 crypto::SymmetricKey::DeriveKeyFromPassword(crypto::SymmetricKey::AES
,
124 if (!base::Base64Decode(initial_vector
, &initial_vector
)) {
125 NET_LOG(ERROR
) << kUnableToDecode
;
128 if (!base::Base64Decode(ciphertext
, &ciphertext
)) {
129 NET_LOG(ERROR
) << kUnableToDecode
;
132 if (!base::Base64Decode(hmac
, &hmac
)) {
133 NET_LOG(ERROR
) << kUnableToDecode
;
137 crypto::HMAC
hmac_verifier(crypto::HMAC::SHA1
);
138 if (!hmac_verifier
.Init(key
.get()) ||
139 !hmac_verifier
.Verify(ciphertext
, hmac
)) {
140 NET_LOG(ERROR
) << kUnableToDecrypt
;
144 crypto::Encryptor decryptor
;
145 if (!decryptor
.Init(key
.get(), crypto::Encryptor::CBC
, initial_vector
)) {
146 NET_LOG(ERROR
) << kUnableToDecrypt
;
150 std::string plaintext
;
151 if (!decryptor
.Decrypt(ciphertext
, &plaintext
)) {
152 NET_LOG(ERROR
) << kUnableToDecrypt
;
156 scoped_ptr
<base::DictionaryValue
> new_root
=
157 ReadDictionaryFromJson(plaintext
);
159 NET_LOG(ERROR
) << "Property dictionary malformed.";
163 return new_root
.Pass();
166 std::string
GetSourceAsString(ONCSource source
) {
168 case ONC_SOURCE_UNKNOWN
:
170 case ONC_SOURCE_NONE
:
172 case ONC_SOURCE_DEVICE_POLICY
:
173 return "device policy";
174 case ONC_SOURCE_USER_POLICY
:
175 return "user policy";
176 case ONC_SOURCE_USER_IMPORT
:
177 return "user import";
179 NOTREACHED() << "unknown ONC source " << source
;
183 void ExpandField(const std::string
& fieldname
,
184 const StringSubstitution
& substitution
,
185 base::DictionaryValue
* onc_object
) {
186 std::string user_string
;
187 if (!onc_object
->GetStringWithoutPathExpansion(fieldname
, &user_string
))
190 std::string login_id
;
191 if (substitution
.GetSubstitute(substitutes::kLoginIDField
, &login_id
)) {
192 base::ReplaceSubstringsAfterOffset(&user_string
, 0,
193 substitutes::kLoginIDField
,
198 if (substitution
.GetSubstitute(substitutes::kEmailField
, &email
)) {
199 base::ReplaceSubstringsAfterOffset(&user_string
, 0,
200 substitutes::kEmailField
,
204 onc_object
->SetStringWithoutPathExpansion(fieldname
, user_string
);
207 void ExpandStringsInOncObject(
208 const OncValueSignature
& signature
,
209 const StringSubstitution
& substitution
,
210 base::DictionaryValue
* onc_object
) {
211 if (&signature
== &kEAPSignature
) {
212 ExpandField(eap::kAnonymousIdentity
, substitution
, onc_object
);
213 ExpandField(eap::kIdentity
, substitution
, onc_object
);
214 } else if (&signature
== &kL2TPSignature
||
215 &signature
== &kOpenVPNSignature
) {
216 ExpandField(vpn::kUsername
, substitution
, onc_object
);
219 // Recurse into nested objects.
220 for (base::DictionaryValue::Iterator
it(*onc_object
); !it
.IsAtEnd();
222 base::DictionaryValue
* inner_object
= nullptr;
223 if (!onc_object
->GetDictionaryWithoutPathExpansion(it
.key(), &inner_object
))
226 const OncFieldSignature
* field_signature
=
227 GetFieldSignature(signature
, it
.key());
228 if (!field_signature
)
231 ExpandStringsInOncObject(*field_signature
->value_signature
,
232 substitution
, inner_object
);
236 void ExpandStringsInNetworks(const StringSubstitution
& substitution
,
237 base::ListValue
* network_configs
) {
238 for (base::Value
* entry
: *network_configs
) {
239 base::DictionaryValue
* network
= nullptr;
240 entry
->GetAsDictionary(&network
);
242 ExpandStringsInOncObject(
243 kNetworkConfigurationSignature
, substitution
, network
);
247 void FillInHexSSIDFieldsInOncObject(const OncValueSignature
& signature
,
248 base::DictionaryValue
* onc_object
) {
249 if (&signature
== &kWiFiSignature
)
250 FillInHexSSIDField(onc_object
);
252 // Recurse into nested objects.
253 for (base::DictionaryValue::Iterator
it(*onc_object
); !it
.IsAtEnd();
255 base::DictionaryValue
* inner_object
= nullptr;
256 if (!onc_object
->GetDictionaryWithoutPathExpansion(it
.key(), &inner_object
))
259 const OncFieldSignature
* field_signature
=
260 GetFieldSignature(signature
, it
.key());
261 if (!field_signature
)
264 FillInHexSSIDFieldsInOncObject(*field_signature
->value_signature
,
269 void FillInHexSSIDField(base::DictionaryValue
* wifi_fields
) {
270 std::string ssid_string
;
271 if (wifi_fields
->HasKey(::onc::wifi::kHexSSID
) ||
272 !wifi_fields
->GetStringWithoutPathExpansion(::onc::wifi::kSSID
,
276 if (ssid_string
.empty()) {
277 NET_LOG(ERROR
) << "Found empty SSID field.";
280 wifi_fields
->SetStringWithoutPathExpansion(
281 ::onc::wifi::kHexSSID
,
282 base::HexEncode(ssid_string
.c_str(), ssid_string
.size()));
287 class OncMaskValues
: public Mapper
{
289 static scoped_ptr
<base::DictionaryValue
> Mask(
290 const OncValueSignature
& signature
,
291 const base::DictionaryValue
& onc_object
,
292 const std::string
& mask
) {
293 OncMaskValues
masker(mask
);
295 return masker
.MapObject(signature
, onc_object
, &unused_error
);
299 explicit OncMaskValues(const std::string
& mask
)
303 scoped_ptr
<base::Value
> MapField(const std::string
& field_name
,
304 const OncValueSignature
& object_signature
,
305 const base::Value
& onc_value
,
306 bool* found_unknown_field
,
307 bool* error
) override
{
308 if (FieldIsCredential(object_signature
, field_name
)) {
309 return scoped_ptr
<base::Value
>(new base::StringValue(mask_
));
311 return Mapper::MapField(field_name
, object_signature
, onc_value
,
312 found_unknown_field
, error
);
316 // Mask to insert in place of the sensitive values.
322 scoped_ptr
<base::DictionaryValue
> MaskCredentialsInOncObject(
323 const OncValueSignature
& signature
,
324 const base::DictionaryValue
& onc_object
,
325 const std::string
& mask
) {
326 return OncMaskValues::Mask(signature
, onc_object
, mask
);
331 std::string
DecodePEM(const std::string
& pem_encoded
) {
332 // The PEM block header used for DER certificates
333 const char kCertificateHeader
[] = "CERTIFICATE";
335 // This is an older PEM marker for DER certificates.
336 const char kX509CertificateHeader
[] = "X509 CERTIFICATE";
338 std::vector
<std::string
> pem_headers
;
339 pem_headers
.push_back(kCertificateHeader
);
340 pem_headers
.push_back(kX509CertificateHeader
);
342 net::PEMTokenizer
pem_tokenizer(pem_encoded
, pem_headers
);
344 if (pem_tokenizer
.GetNext()) {
345 decoded
= pem_tokenizer
.data();
347 // If we failed to read the data as a PEM file, then try plain base64 decode
348 // in case the PEM marker strings are missing. For this to work, there has
349 // to be no white space, and it has to only contain the base64-encoded data.
350 if (!base::Base64Decode(pem_encoded
, &decoded
)) {
351 LOG(ERROR
) << "Unable to base64 decode X509 data: " << pem_encoded
;
352 return std::string();
358 CertPEMsByGUIDMap
GetServerAndCACertsByGUID(
359 const base::ListValue
& certificates
) {
360 CertPEMsByGUIDMap certs_by_guid
;
361 for (const base::Value
* entry
: certificates
) {
362 const base::DictionaryValue
* cert
= nullptr;
363 entry
->GetAsDictionary(&cert
);
366 cert
->GetStringWithoutPathExpansion(certificate::kGUID
, &guid
);
367 std::string cert_type
;
368 cert
->GetStringWithoutPathExpansion(certificate::kType
, &cert_type
);
369 if (cert_type
!= certificate::kServer
&&
370 cert_type
!= certificate::kAuthority
) {
373 std::string x509_data
;
374 cert
->GetStringWithoutPathExpansion(certificate::kX509
, &x509_data
);
376 std::string der
= DecodePEM(x509_data
);
378 if (der
.empty() || !net::X509Certificate::GetPEMEncodedFromDER(der
, &pem
)) {
379 LOG(ERROR
) << "Certificate with GUID " << guid
380 << " is not in PEM encoding.";
383 certs_by_guid
[guid
] = pem
;
386 return certs_by_guid
;
389 void FillInHexSSIDFieldsInNetworks(base::ListValue
* network_configs
) {
390 for (base::Value
* entry
: *network_configs
) {
391 base::DictionaryValue
* network
= nullptr;
392 entry
->GetAsDictionary(&network
);
394 FillInHexSSIDFieldsInOncObject(kNetworkConfigurationSignature
, network
);
400 bool ParseAndValidateOncForImport(const std::string
& onc_blob
,
401 ONCSource onc_source
,
402 const std::string
& passphrase
,
403 base::ListValue
* network_configs
,
404 base::DictionaryValue
* global_network_config
,
405 base::ListValue
* certificates
) {
406 network_configs
->Clear();
407 global_network_config
->Clear();
408 certificates
->Clear();
409 if (onc_blob
.empty())
412 scoped_ptr
<base::DictionaryValue
> toplevel_onc
=
413 ReadDictionaryFromJson(onc_blob
);
415 LOG(ERROR
) << "ONC loaded from " << GetSourceAsString(onc_source
)
416 << " is not a valid JSON dictionary.";
420 // Check and see if this is an encrypted ONC file. If so, decrypt it.
421 std::string onc_type
;
422 toplevel_onc
->GetStringWithoutPathExpansion(toplevel_config::kType
,
424 if (onc_type
== toplevel_config::kEncryptedConfiguration
) {
425 toplevel_onc
= Decrypt(passphrase
, *toplevel_onc
);
427 LOG(ERROR
) << "Couldn't decrypt the ONC from "
428 << GetSourceAsString(onc_source
);
433 bool from_policy
= (onc_source
== ONC_SOURCE_USER_POLICY
||
434 onc_source
== ONC_SOURCE_DEVICE_POLICY
);
436 // Validate the ONC dictionary. We are liberal and ignore unknown field
437 // names and ignore invalid field names in kRecommended arrays.
438 Validator
validator(false, // Ignore unknown fields.
439 false, // Ignore invalid recommended field names.
440 true, // Fail on missing fields.
442 validator
.SetOncSource(onc_source
);
444 Validator::Result validation_result
;
445 toplevel_onc
= validator
.ValidateAndRepairObject(
446 &kToplevelConfigurationSignature
,
451 UMA_HISTOGRAM_BOOLEAN("Enterprise.ONC.PolicyValidation",
452 validation_result
== Validator::VALID
);
456 if (validation_result
== Validator::VALID_WITH_WARNINGS
) {
457 LOG(WARNING
) << "ONC from " << GetSourceAsString(onc_source
)
458 << " produced warnings.";
460 } else if (validation_result
== Validator::INVALID
|| !toplevel_onc
) {
461 LOG(ERROR
) << "ONC from " << GetSourceAsString(onc_source
)
462 << " is invalid and couldn't be repaired.";
466 base::ListValue
* validated_certs
= nullptr;
467 if (toplevel_onc
->GetListWithoutPathExpansion(toplevel_config::kCertificates
,
469 certificates
->Swap(validated_certs
);
472 base::ListValue
* validated_networks
= nullptr;
473 if (toplevel_onc
->GetListWithoutPathExpansion(
474 toplevel_config::kNetworkConfigurations
, &validated_networks
)) {
475 FillInHexSSIDFieldsInNetworks(validated_networks
);
477 CertPEMsByGUIDMap server_and_ca_certs
=
478 GetServerAndCACertsByGUID(*certificates
);
480 if (!ResolveServerCertRefsInNetworks(server_and_ca_certs
,
481 validated_networks
)) {
482 LOG(ERROR
) << "Some certificate references in the ONC policy for source "
483 << GetSourceAsString(onc_source
) << " could not be resolved.";
487 network_configs
->Swap(validated_networks
);
490 base::DictionaryValue
* validated_global_config
= nullptr;
491 if (toplevel_onc
->GetDictionaryWithoutPathExpansion(
492 toplevel_config::kGlobalNetworkConfiguration
,
493 &validated_global_config
)) {
494 global_network_config
->Swap(validated_global_config
);
500 scoped_refptr
<net::X509Certificate
> DecodePEMCertificate(
501 const std::string
& pem_encoded
) {
502 std::string decoded
= DecodePEM(pem_encoded
);
503 scoped_refptr
<net::X509Certificate
> cert
=
504 net::X509Certificate::CreateFromBytes(decoded
.data(), decoded
.size());
505 LOG_IF(ERROR
, !cert
.get()) << "Couldn't create certificate from X509 data: "
512 bool GUIDRefToPEMEncoding(const CertPEMsByGUIDMap
& certs_by_guid
,
513 const std::string
& guid_ref
,
514 std::string
* pem_encoded
) {
515 CertPEMsByGUIDMap::const_iterator it
= certs_by_guid
.find(guid_ref
);
516 if (it
== certs_by_guid
.end()) {
517 LOG(ERROR
) << "Couldn't resolve certificate reference " << guid_ref
;
520 *pem_encoded
= it
->second
;
521 if (pem_encoded
->empty()) {
522 LOG(ERROR
) << "Couldn't PEM-encode certificate with GUID " << guid_ref
;
528 bool ResolveSingleCertRef(const CertPEMsByGUIDMap
& certs_by_guid
,
529 const std::string
& key_guid_ref
,
530 const std::string
& key_pem
,
531 base::DictionaryValue
* onc_object
) {
532 std::string guid_ref
;
533 if (!onc_object
->GetStringWithoutPathExpansion(key_guid_ref
, &guid_ref
))
536 std::string pem_encoded
;
537 if (!GUIDRefToPEMEncoding(certs_by_guid
, guid_ref
, &pem_encoded
))
540 onc_object
->RemoveWithoutPathExpansion(key_guid_ref
, nullptr);
541 onc_object
->SetStringWithoutPathExpansion(key_pem
, pem_encoded
);
545 bool ResolveCertRefList(const CertPEMsByGUIDMap
& certs_by_guid
,
546 const std::string
& key_guid_ref_list
,
547 const std::string
& key_pem_list
,
548 base::DictionaryValue
* onc_object
) {
549 const base::ListValue
* guid_ref_list
= nullptr;
550 if (!onc_object
->GetListWithoutPathExpansion(key_guid_ref_list
,
555 scoped_ptr
<base::ListValue
> pem_list(new base::ListValue
);
556 for (const base::Value
* entry
: *guid_ref_list
) {
557 std::string guid_ref
;
558 entry
->GetAsString(&guid_ref
);
560 std::string pem_encoded
;
561 if (!GUIDRefToPEMEncoding(certs_by_guid
, guid_ref
, &pem_encoded
))
564 pem_list
->AppendString(pem_encoded
);
567 onc_object
->RemoveWithoutPathExpansion(key_guid_ref_list
, nullptr);
568 onc_object
->SetWithoutPathExpansion(key_pem_list
, pem_list
.release());
572 bool ResolveSingleCertRefToList(const CertPEMsByGUIDMap
& certs_by_guid
,
573 const std::string
& key_guid_ref
,
574 const std::string
& key_pem_list
,
575 base::DictionaryValue
* onc_object
) {
576 std::string guid_ref
;
577 if (!onc_object
->GetStringWithoutPathExpansion(key_guid_ref
, &guid_ref
))
580 std::string pem_encoded
;
581 if (!GUIDRefToPEMEncoding(certs_by_guid
, guid_ref
, &pem_encoded
))
584 scoped_ptr
<base::ListValue
> pem_list(new base::ListValue
);
585 pem_list
->AppendString(pem_encoded
);
586 onc_object
->RemoveWithoutPathExpansion(key_guid_ref
, nullptr);
587 onc_object
->SetWithoutPathExpansion(key_pem_list
, pem_list
.release());
591 // Resolves the reference list at |key_guid_refs| if present and otherwise the
592 // single reference at |key_guid_ref|. Returns whether the respective resolving
594 bool ResolveCertRefsOrRefToList(const CertPEMsByGUIDMap
& certs_by_guid
,
595 const std::string
& key_guid_refs
,
596 const std::string
& key_guid_ref
,
597 const std::string
& key_pem_list
,
598 base::DictionaryValue
* onc_object
) {
599 if (onc_object
->HasKey(key_guid_refs
)) {
600 if (onc_object
->HasKey(key_guid_ref
)) {
601 LOG(ERROR
) << "Found both " << key_guid_refs
<< " and " << key_guid_ref
602 << ". Ignoring and removing the latter.";
603 onc_object
->RemoveWithoutPathExpansion(key_guid_ref
, nullptr);
605 return ResolveCertRefList(
606 certs_by_guid
, key_guid_refs
, key_pem_list
, onc_object
);
609 // Only resolve |key_guid_ref| if |key_guid_refs| isn't present.
610 return ResolveSingleCertRefToList(
611 certs_by_guid
, key_guid_ref
, key_pem_list
, onc_object
);
614 bool ResolveServerCertRefsInObject(const CertPEMsByGUIDMap
& certs_by_guid
,
615 const OncValueSignature
& signature
,
616 base::DictionaryValue
* onc_object
) {
617 if (&signature
== &kCertificatePatternSignature
) {
618 if (!ResolveCertRefList(certs_by_guid
,
619 client_cert::kIssuerCARef
,
620 client_cert::kIssuerCAPEMs
,
624 } else if (&signature
== &kEAPSignature
) {
625 if (!ResolveCertRefsOrRefToList(certs_by_guid
,
632 } else if (&signature
== &kIPsecSignature
) {
633 if (!ResolveCertRefsOrRefToList(certs_by_guid
,
634 ipsec::kServerCARefs
,
636 ipsec::kServerCAPEMs
,
640 } else if (&signature
== &kIPsecSignature
||
641 &signature
== &kOpenVPNSignature
) {
642 if (!ResolveSingleCertRef(certs_by_guid
,
643 openvpn::kServerCertRef
,
644 openvpn::kServerCertPEM
,
646 !ResolveCertRefsOrRefToList(certs_by_guid
,
647 openvpn::kServerCARefs
,
648 openvpn::kServerCARef
,
649 openvpn::kServerCAPEMs
,
655 // Recurse into nested objects.
656 for (base::DictionaryValue::Iterator
it(*onc_object
); !it
.IsAtEnd();
658 base::DictionaryValue
* inner_object
= nullptr;
659 if (!onc_object
->GetDictionaryWithoutPathExpansion(it
.key(), &inner_object
))
662 const OncFieldSignature
* field_signature
=
663 GetFieldSignature(signature
, it
.key());
664 if (!field_signature
)
667 if (!ResolveServerCertRefsInObject(certs_by_guid
,
668 *field_signature
->value_signature
,
678 bool ResolveServerCertRefsInNetworks(const CertPEMsByGUIDMap
& certs_by_guid
,
679 base::ListValue
* network_configs
) {
681 for (base::ListValue::iterator it
= network_configs
->begin();
682 it
!= network_configs
->end(); ) {
683 base::DictionaryValue
* network
= nullptr;
684 (*it
)->GetAsDictionary(&network
);
685 if (!ResolveServerCertRefsInNetwork(certs_by_guid
, network
)) {
687 network
->GetStringWithoutPathExpansion(network_config::kGUID
, &guid
);
688 // This might happen even with correct validation, if the referenced
689 // certificate couldn't be imported.
690 LOG(ERROR
) << "Couldn't resolve some certificate reference of network "
692 it
= network_configs
->Erase(it
, nullptr);
701 bool ResolveServerCertRefsInNetwork(const CertPEMsByGUIDMap
& certs_by_guid
,
702 base::DictionaryValue
* network_config
) {
703 return ResolveServerCertRefsInObject(certs_by_guid
,
704 kNetworkConfigurationSignature
,
708 NetworkTypePattern
NetworkTypePatternFromOncType(const std::string
& type
) {
709 if (type
== ::onc::network_type::kAllTypes
)
710 return NetworkTypePattern::Default();
711 if (type
== ::onc::network_type::kCellular
)
712 return NetworkTypePattern::Cellular();
713 if (type
== ::onc::network_type::kEthernet
)
714 return NetworkTypePattern::Ethernet();
715 if (type
== ::onc::network_type::kVPN
)
716 return NetworkTypePattern::VPN();
717 if (type
== ::onc::network_type::kWiFi
)
718 return NetworkTypePattern::WiFi();
719 if (type
== ::onc::network_type::kWimax
)
720 return NetworkTypePattern::Wimax();
721 if (type
== ::onc::network_type::kWireless
)
722 return NetworkTypePattern::Wireless();
723 NOTREACHED() << "Unrecognized ONC type: " << type
;
724 return NetworkTypePattern::Default();
727 bool IsRecommendedValue(const base::DictionaryValue
* onc
,
728 const std::string
& property_key
) {
729 std::string property_basename
, recommended_property_key
;
730 size_t pos
= property_key
.find_last_of('.');
731 if (pos
!= std::string::npos
) {
732 // 'WiFi.AutoConnect' -> 'AutoConnect', 'WiFi.Recommended'
733 property_basename
= property_key
.substr(pos
+ 1);
734 recommended_property_key
=
735 property_key
.substr(0, pos
+ 1) + ::onc::kRecommended
;
737 // 'Name' -> 'Name', 'Recommended'
738 property_basename
= property_key
;
739 recommended_property_key
= ::onc::kRecommended
;
742 const base::ListValue
* recommended_keys
= nullptr;
743 return (onc
->GetList(recommended_property_key
, &recommended_keys
) &&
744 recommended_keys
->Find(base::StringValue(property_basename
)) !=
745 recommended_keys
->end());
750 const char kDirectScheme
[] = "direct";
751 const char kQuicScheme
[] = "quic";
752 const char kSocksScheme
[] = "socks";
753 const char kSocks4Scheme
[] = "socks4";
754 const char kSocks5Scheme
[] = "socks5";
756 net::ProxyServer
ConvertOncProxyLocationToHostPort(
757 net::ProxyServer::Scheme default_proxy_scheme
,
758 const base::DictionaryValue
& onc_proxy_location
) {
760 onc_proxy_location
.GetStringWithoutPathExpansion(::onc::proxy::kHost
, &host
);
761 // Parse |host| according to the format [<scheme>"://"]<server>[":"<port>].
762 net::ProxyServer proxy_server
=
763 net::ProxyServer::FromURI(host
, default_proxy_scheme
);
765 onc_proxy_location
.GetIntegerWithoutPathExpansion(::onc::proxy::kPort
, &port
);
767 // Replace the port parsed from |host| by the provided |port|.
768 return net::ProxyServer(
769 proxy_server
.scheme(),
770 net::HostPortPair(proxy_server
.host_port_pair().host(),
771 static_cast<uint16
>(port
)));
774 void AppendProxyServerForScheme(const base::DictionaryValue
& onc_manual
,
775 const std::string
& onc_scheme
,
777 const base::DictionaryValue
* onc_proxy_location
= nullptr;
778 if (!onc_manual
.GetDictionaryWithoutPathExpansion(onc_scheme
,
779 &onc_proxy_location
)) {
783 net::ProxyServer::Scheme default_proxy_scheme
= net::ProxyServer::SCHEME_HTTP
;
784 std::string url_scheme
;
785 if (onc_scheme
== ::onc::proxy::kFtp
) {
786 url_scheme
= url::kFtpScheme
;
787 } else if (onc_scheme
== ::onc::proxy::kHttp
) {
788 url_scheme
= url::kHttpScheme
;
789 } else if (onc_scheme
== ::onc::proxy::kHttps
) {
790 url_scheme
= url::kHttpsScheme
;
791 } else if (onc_scheme
== ::onc::proxy::kSocks
) {
792 default_proxy_scheme
= net::ProxyServer::SCHEME_SOCKS4
;
793 url_scheme
= kSocksScheme
;
798 net::ProxyServer proxy_server
= ConvertOncProxyLocationToHostPort(
799 default_proxy_scheme
, *onc_proxy_location
);
801 ProxyConfigDictionary::EncodeAndAppendProxyServer(url_scheme
, proxy_server
,
805 net::ProxyBypassRules
ConvertOncExcludeDomainsToBypassRules(
806 const base::ListValue
& onc_exclude_domains
) {
807 net::ProxyBypassRules rules
;
808 for (base::ListValue::const_iterator it
= onc_exclude_domains
.begin();
809 it
!= onc_exclude_domains
.end(); ++it
) {
811 (*it
)->GetAsString(&rule
);
812 rules
.AddRuleFromString(rule
);
817 std::string
SchemeToString(net::ProxyServer::Scheme scheme
) {
819 case net::ProxyServer::SCHEME_DIRECT
:
820 return kDirectScheme
;
821 case net::ProxyServer::SCHEME_HTTP
:
822 return url::kHttpScheme
;
823 case net::ProxyServer::SCHEME_SOCKS4
:
824 return kSocks4Scheme
;
825 case net::ProxyServer::SCHEME_SOCKS5
:
826 return kSocks5Scheme
;
827 case net::ProxyServer::SCHEME_HTTPS
:
828 return url::kHttpsScheme
;
829 case net::ProxyServer::SCHEME_QUIC
:
831 case net::ProxyServer::SCHEME_INVALID
:
838 void SetProxyForScheme(const net::ProxyConfig::ProxyRules
& proxy_rules
,
839 const std::string
& scheme
,
840 const std::string
& onc_scheme
,
841 base::DictionaryValue
* dict
) {
842 const net::ProxyList
* proxy_list
= nullptr;
843 if (proxy_rules
.type
== net::ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY
) {
844 proxy_list
= &proxy_rules
.single_proxies
;
845 } else if (proxy_rules
.type
==
846 net::ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME
) {
847 proxy_list
= proxy_rules
.MapUrlSchemeToProxyList(scheme
);
849 if (!proxy_list
|| proxy_list
->IsEmpty())
851 const net::ProxyServer
& server
= proxy_list
->Get();
852 scoped_ptr
<base::DictionaryValue
> url_dict(new base::DictionaryValue
);
853 std::string host
= server
.host_port_pair().host();
855 // For all proxy types except SOCKS, the default scheme of the proxy host is
857 net::ProxyServer::Scheme default_scheme
=
858 (onc_scheme
== ::onc::proxy::kSocks
) ? net::ProxyServer::SCHEME_SOCKS4
859 : net::ProxyServer::SCHEME_HTTP
;
860 // Only prefix the host with a non-default scheme.
861 if (server
.scheme() != default_scheme
)
862 host
= SchemeToString(server
.scheme()) + "://" + host
;
863 url_dict
->SetStringWithoutPathExpansion(::onc::proxy::kHost
, host
);
864 url_dict
->SetIntegerWithoutPathExpansion(::onc::proxy::kPort
,
865 server
.host_port_pair().port());
866 dict
->SetWithoutPathExpansion(onc_scheme
, url_dict
.release());
871 scoped_ptr
<base::DictionaryValue
> ConvertOncProxySettingsToProxyConfig(
872 const base::DictionaryValue
& onc_proxy_settings
) {
874 onc_proxy_settings
.GetStringWithoutPathExpansion(::onc::proxy::kType
, &type
);
875 scoped_ptr
<base::DictionaryValue
> proxy_dict
;
877 if (type
== ::onc::proxy::kDirect
) {
878 proxy_dict
.reset(ProxyConfigDictionary::CreateDirect());
879 } else if (type
== ::onc::proxy::kWPAD
) {
880 proxy_dict
.reset(ProxyConfigDictionary::CreateAutoDetect());
881 } else if (type
== ::onc::proxy::kPAC
) {
883 onc_proxy_settings
.GetStringWithoutPathExpansion(::onc::proxy::kPAC
,
886 DCHECK(url
.is_valid()) << "Invalid URL in ProxySettings.PAC";
887 proxy_dict
.reset(ProxyConfigDictionary::CreatePacScript(url
.spec(), false));
888 } else if (type
== ::onc::proxy::kManual
) {
889 const base::DictionaryValue
* manual_dict
= nullptr;
890 onc_proxy_settings
.GetDictionaryWithoutPathExpansion(::onc::proxy::kManual
,
892 std::string manual_spec
;
893 AppendProxyServerForScheme(*manual_dict
, ::onc::proxy::kFtp
, &manual_spec
);
894 AppendProxyServerForScheme(*manual_dict
, ::onc::proxy::kHttp
, &manual_spec
);
895 AppendProxyServerForScheme(*manual_dict
, ::onc::proxy::kSocks
,
897 AppendProxyServerForScheme(*manual_dict
, ::onc::proxy::kHttps
,
900 const base::ListValue
* exclude_domains
= nullptr;
901 net::ProxyBypassRules bypass_rules
;
902 if (onc_proxy_settings
.GetListWithoutPathExpansion(
903 ::onc::proxy::kExcludeDomains
, &exclude_domains
)) {
904 bypass_rules
.AssignFrom(
905 ConvertOncExcludeDomainsToBypassRules(*exclude_domains
));
907 proxy_dict
.reset(ProxyConfigDictionary::CreateFixedServers(
908 manual_spec
, bypass_rules
.ToString()));
912 return proxy_dict
.Pass();
915 scoped_ptr
<base::DictionaryValue
> ConvertProxyConfigToOncProxySettings(
916 const base::DictionaryValue
& proxy_config_value
) {
917 // Create a ProxyConfigDictionary from the DictionaryValue.
918 scoped_ptr
<ProxyConfigDictionary
> proxy_config(
919 new ProxyConfigDictionary(&proxy_config_value
));
921 // Create the result DictionaryValue and populate it.
922 scoped_ptr
<base::DictionaryValue
> proxy_settings(new base::DictionaryValue
);
923 ProxyPrefs::ProxyMode mode
;
924 if (!proxy_config
->GetMode(&mode
))
927 case ProxyPrefs::MODE_DIRECT
: {
928 proxy_settings
->SetStringWithoutPathExpansion(::onc::proxy::kType
,
929 ::onc::proxy::kDirect
);
932 case ProxyPrefs::MODE_AUTO_DETECT
: {
933 proxy_settings
->SetStringWithoutPathExpansion(::onc::proxy::kType
,
934 ::onc::proxy::kWPAD
);
937 case ProxyPrefs::MODE_PAC_SCRIPT
: {
938 proxy_settings
->SetStringWithoutPathExpansion(::onc::proxy::kType
,
941 proxy_config
->GetPacUrl(&pac_url
);
942 proxy_settings
->SetStringWithoutPathExpansion(::onc::proxy::kPAC
,
946 case ProxyPrefs::MODE_FIXED_SERVERS
: {
947 proxy_settings
->SetString(::onc::proxy::kType
, ::onc::proxy::kManual
);
948 scoped_ptr
<base::DictionaryValue
> manual(new base::DictionaryValue
);
949 std::string proxy_rules_string
;
950 if (proxy_config
->GetProxyServer(&proxy_rules_string
)) {
951 net::ProxyConfig::ProxyRules proxy_rules
;
952 proxy_rules
.ParseFromString(proxy_rules_string
);
953 SetProxyForScheme(proxy_rules
, url::kFtpScheme
, ::onc::proxy::kFtp
,
955 SetProxyForScheme(proxy_rules
, url::kHttpScheme
, ::onc::proxy::kHttp
,
957 SetProxyForScheme(proxy_rules
, url::kHttpsScheme
, ::onc::proxy::kHttps
,
959 SetProxyForScheme(proxy_rules
, kSocksScheme
, ::onc::proxy::kSocks
,
962 proxy_settings
->SetWithoutPathExpansion(::onc::proxy::kManual
,
965 // Convert the 'bypass_list' string into dictionary entries.
966 std::string bypass_rules_string
;
967 if (proxy_config
->GetBypassList(&bypass_rules_string
)) {
968 net::ProxyBypassRules bypass_rules
;
969 bypass_rules
.ParseFromString(bypass_rules_string
);
970 scoped_ptr
<base::ListValue
> exclude_domains(new base::ListValue
);
971 for (const net::ProxyBypassRules::Rule
* rule
: bypass_rules
.rules())
972 exclude_domains
->AppendString(rule
->ToString());
973 if (!exclude_domains
->empty()) {
974 proxy_settings
->SetWithoutPathExpansion(::onc::proxy::kExcludeDomains
,
975 exclude_domains
.release());
981 LOG(ERROR
) << "Unexpected proxy mode in Shill config: " << mode
;
985 return proxy_settings
.Pass();
989 } // namespace chromeos