Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / chromeos / network / onc / onc_utils.cc
blob85c2b9d7152f92541a11bf5085c8bfab53ade064
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;
34 namespace chromeos {
35 namespace onc {
37 namespace {
39 const char kUnableToDecrypt[] = "Unable to decrypt encrypted ONC";
40 const char kUnableToDecode[] = "Unable to decode encrypted ONC";
42 } // namespace
44 const char kEmptyUnencryptedConfiguration[] =
45 "{\"Type\":\"UnencryptedConfiguration\",\"NetworkConfigurations\":[],"
46 "\"Certificates\":[]}";
48 scoped_ptr<base::DictionaryValue> ReadDictionaryFromJson(
49 const std::string& json) {
50 std::string error;
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;
57 delete root;
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;
67 std::string onc_type;
68 std::string initial_vector;
69 std::string salt;
70 std::string cipher;
71 std::string stretch_method;
72 std::string hmac_method;
73 std::string hmac;
74 int iterations;
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.";
88 return nullptr;
91 if (hmac_method != encrypted::kSHA1 ||
92 cipher != encrypted::kAES256 ||
93 stretch_method != encrypted::kPBKDF2) {
94 NET_LOG(ERROR) << "Encrypted ONC unsupported encryption scheme.";
95 return nullptr;
98 // Make sure iterations != 0, since that's not valid.
99 if (iterations == 0) {
100 NET_LOG(ERROR) << kUnableToDecrypt;
101 return nullptr;
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";
108 return nullptr;
111 if (!base::Base64Decode(salt, &salt)) {
112 NET_LOG(ERROR) << kUnableToDecode;
113 return nullptr;
116 scoped_ptr<crypto::SymmetricKey> key(
117 crypto::SymmetricKey::DeriveKeyFromPassword(crypto::SymmetricKey::AES,
118 passphrase,
119 salt,
120 iterations,
121 kKeySizeInBits));
123 if (!base::Base64Decode(initial_vector, &initial_vector)) {
124 NET_LOG(ERROR) << kUnableToDecode;
125 return nullptr;
127 if (!base::Base64Decode(ciphertext, &ciphertext)) {
128 NET_LOG(ERROR) << kUnableToDecode;
129 return nullptr;
131 if (!base::Base64Decode(hmac, &hmac)) {
132 NET_LOG(ERROR) << kUnableToDecode;
133 return nullptr;
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;
140 return nullptr;
143 crypto::Encryptor decryptor;
144 if (!decryptor.Init(key.get(), crypto::Encryptor::CBC, initial_vector)) {
145 NET_LOG(ERROR) << kUnableToDecrypt;
146 return nullptr;
149 std::string plaintext;
150 if (!decryptor.Decrypt(ciphertext, &plaintext)) {
151 NET_LOG(ERROR) << kUnableToDecrypt;
152 return nullptr;
155 scoped_ptr<base::DictionaryValue> new_root =
156 ReadDictionaryFromJson(plaintext);
157 if (!new_root) {
158 NET_LOG(ERROR) << "Property dictionary malformed.";
159 return nullptr;
162 return new_root.Pass();
165 std::string GetSourceAsString(ONCSource source) {
166 switch (source) {
167 case ONC_SOURCE_UNKNOWN:
168 return "unknown";
169 case ONC_SOURCE_NONE:
170 return "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;
179 return "unknown";
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))
187 return;
189 std::string login_id;
190 if (substitution.GetSubstitute(substitutes::kLoginIDField, &login_id)) {
191 base::ReplaceSubstringsAfterOffset(&user_string, 0,
192 substitutes::kLoginIDField,
193 login_id);
196 std::string email;
197 if (substitution.GetSubstitute(substitutes::kEmailField, &email)) {
198 base::ReplaceSubstringsAfterOffset(&user_string, 0,
199 substitutes::kEmailField,
200 email);
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();
220 it.Advance()) {
221 base::DictionaryValue* inner_object = nullptr;
222 if (!onc_object->GetDictionaryWithoutPathExpansion(it.key(), &inner_object))
223 continue;
225 const OncFieldSignature* field_signature =
226 GetFieldSignature(signature, it.key());
227 if (!field_signature)
228 continue;
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);
240 DCHECK(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();
253 it.Advance()) {
254 base::DictionaryValue* inner_object = nullptr;
255 if (!onc_object->GetDictionaryWithoutPathExpansion(it.key(), &inner_object))
256 continue;
258 const OncFieldSignature* field_signature =
259 GetFieldSignature(signature, it.key());
260 if (!field_signature)
261 continue;
263 FillInHexSSIDFieldsInOncObject(*field_signature->value_signature,
264 inner_object);
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,
272 &ssid_string)) {
273 return;
275 if (ssid_string.empty()) {
276 NET_LOG(ERROR) << "Found empty SSID field.";
277 return;
279 wifi_fields->SetStringWithoutPathExpansion(
280 ::onc::wifi::kHexSSID,
281 base::HexEncode(ssid_string.c_str(), ssid_string.size()));
284 namespace {
286 class OncMaskValues : public Mapper {
287 public:
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);
293 bool unused_error;
294 return masker.MapObject(signature, onc_object, &unused_error);
297 protected:
298 explicit OncMaskValues(const std::string& mask)
299 : mask_(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_));
309 } else {
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.
316 std::string mask_;
319 } // namespace
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);
328 namespace {
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);
342 std::string decoded;
343 if (pem_tokenizer.GetNext()) {
344 decoded = pem_tokenizer.data();
345 } else {
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();
354 return decoded;
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);
364 std::string guid;
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) {
370 continue;
372 std::string x509_data;
373 cert->GetStringWithoutPathExpansion(certificate::kX509, &x509_data);
375 std::string der = DecodePEM(x509_data);
376 std::string pem;
377 if (der.empty() || !net::X509Certificate::GetPEMEncodedFromDER(der, &pem)) {
378 LOG(ERROR) << "Certificate with GUID " << guid
379 << " is not in PEM encoding.";
380 continue;
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);
392 DCHECK(network);
393 FillInHexSSIDFieldsInOncObject(kNetworkConfigurationSignature, network);
397 } // namespace
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())
409 return true;
411 scoped_ptr<base::DictionaryValue> toplevel_onc =
412 ReadDictionaryFromJson(onc_blob);
413 if (!toplevel_onc) {
414 LOG(ERROR) << "ONC loaded from " << GetSourceAsString(onc_source)
415 << " is not a valid JSON dictionary.";
416 return false;
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,
422 &onc_type);
423 if (onc_type == toplevel_config::kEncryptedConfiguration) {
424 toplevel_onc = Decrypt(passphrase, *toplevel_onc);
425 if (!toplevel_onc) {
426 LOG(ERROR) << "Couldn't decrypt the ONC from "
427 << GetSourceAsString(onc_source);
428 return false;
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.
440 from_policy);
441 validator.SetOncSource(onc_source);
443 Validator::Result validation_result;
444 toplevel_onc = validator.ValidateAndRepairObject(
445 &kToplevelConfigurationSignature,
446 *toplevel_onc,
447 &validation_result);
449 if (from_policy) {
450 UMA_HISTOGRAM_BOOLEAN("Enterprise.ONC.PolicyValidation",
451 validation_result == Validator::VALID);
454 bool success = true;
455 if (validation_result == Validator::VALID_WITH_WARNINGS) {
456 LOG(WARNING) << "ONC from " << GetSourceAsString(onc_source)
457 << " produced warnings.";
458 success = false;
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.";
462 return false;
465 base::ListValue* validated_certs = nullptr;
466 if (toplevel_onc->GetListWithoutPathExpansion(toplevel_config::kCertificates,
467 &validated_certs)) {
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.";
483 success = false;
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);
496 return success;
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: "
505 << decoded;
506 return cert;
509 namespace {
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;
517 return false;
519 *pem_encoded = it->second;
520 if (pem_encoded->empty()) {
521 LOG(ERROR) << "Couldn't PEM-encode certificate with GUID " << guid_ref;
522 return false;
524 return true;
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))
533 return true;
535 std::string pem_encoded;
536 if (!GUIDRefToPEMEncoding(certs_by_guid, guid_ref, &pem_encoded))
537 return false;
539 onc_object->RemoveWithoutPathExpansion(key_guid_ref, nullptr);
540 onc_object->SetStringWithoutPathExpansion(key_pem, pem_encoded);
541 return true;
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,
550 &guid_ref_list)) {
551 return true;
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))
561 return false;
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());
568 return true;
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))
577 return true;
579 std::string pem_encoded;
580 if (!GUIDRefToPEMEncoding(certs_by_guid, guid_ref, &pem_encoded))
581 return false;
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());
587 return true;
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
592 // was successful.
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,
620 onc_object)) {
621 return false;
623 } else if (&signature == &kEAPSignature) {
624 if (!ResolveCertRefsOrRefToList(certs_by_guid,
625 eap::kServerCARefs,
626 eap::kServerCARef,
627 eap::kServerCAPEMs,
628 onc_object)) {
629 return false;
631 } else if (&signature == &kIPsecSignature) {
632 if (!ResolveCertRefsOrRefToList(certs_by_guid,
633 ipsec::kServerCARefs,
634 ipsec::kServerCARef,
635 ipsec::kServerCAPEMs,
636 onc_object)) {
637 return false;
639 } else if (&signature == &kIPsecSignature ||
640 &signature == &kOpenVPNSignature) {
641 if (!ResolveSingleCertRef(certs_by_guid,
642 openvpn::kServerCertRef,
643 openvpn::kServerCertPEM,
644 onc_object) ||
645 !ResolveCertRefsOrRefToList(certs_by_guid,
646 openvpn::kServerCARefs,
647 openvpn::kServerCARef,
648 openvpn::kServerCAPEMs,
649 onc_object)) {
650 return false;
654 // Recurse into nested objects.
655 for (base::DictionaryValue::Iterator it(*onc_object); !it.IsAtEnd();
656 it.Advance()) {
657 base::DictionaryValue* inner_object = nullptr;
658 if (!onc_object->GetDictionaryWithoutPathExpansion(it.key(), &inner_object))
659 continue;
661 const OncFieldSignature* field_signature =
662 GetFieldSignature(signature, it.key());
663 if (!field_signature)
664 continue;
666 if (!ResolveServerCertRefsInObject(certs_by_guid,
667 *field_signature->value_signature,
668 inner_object)) {
669 return false;
672 return true;
675 } // namespace
677 bool ResolveServerCertRefsInNetworks(const CertPEMsByGUIDMap& certs_by_guid,
678 base::ListValue* network_configs) {
679 bool success = true;
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)) {
685 std::string guid;
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 "
690 << guid;
691 it = network_configs->Erase(it, nullptr);
692 success = false;
693 continue;
695 ++it;
697 return success;
700 bool ResolveServerCertRefsInNetwork(const CertPEMsByGUIDMap& certs_by_guid,
701 base::DictionaryValue* network_config) {
702 return ResolveServerCertRefsInObject(certs_by_guid,
703 kNetworkConfigurationSignature,
704 network_config);
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;
735 } else {
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());
747 namespace {
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) {
758 std::string host;
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);
763 int port = 0;
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,
775 std::string* spec) {
776 const base::DictionaryValue* onc_proxy_location = nullptr;
777 if (!onc_manual.GetDictionaryWithoutPathExpansion(onc_scheme,
778 &onc_proxy_location)) {
779 return;
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;
793 } else {
794 NOTREACHED();
797 net::ProxyServer proxy_server = ConvertOncProxyLocationToHostPort(
798 default_proxy_scheme, *onc_proxy_location);
800 ProxyConfigDictionary::EncodeAndAppendProxyServer(url_scheme, proxy_server,
801 spec);
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) {
809 std::string rule;
810 (*it)->GetAsString(&rule);
811 rules.AddRuleFromString(rule);
813 return rules;
816 std::string SchemeToString(net::ProxyServer::Scheme scheme) {
817 switch (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:
829 return kQuicScheme;
830 case net::ProxyServer::SCHEME_INVALID:
831 break;
833 NOTREACHED();
834 return "";
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())
849 return;
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
855 // HTTP.
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());
868 } // namespace
870 scoped_ptr<base::DictionaryValue> ConvertOncProxySettingsToProxyConfig(
871 const base::DictionaryValue& onc_proxy_settings) {
872 std::string type;
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) {
881 std::string pac_url;
882 onc_proxy_settings.GetStringWithoutPathExpansion(::onc::proxy::kPAC,
883 &pac_url);
884 GURL url(pac_url);
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,
890 &manual_dict);
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,
895 &manual_spec);
896 AppendProxyServerForScheme(*manual_dict, ::onc::proxy::kHttps,
897 &manual_spec);
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()));
908 } else {
909 NOTREACHED();
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))
924 return nullptr;
925 switch (mode) {
926 case ProxyPrefs::MODE_DIRECT: {
927 proxy_settings->SetStringWithoutPathExpansion(::onc::proxy::kType,
928 ::onc::proxy::kDirect);
929 break;
931 case ProxyPrefs::MODE_AUTO_DETECT: {
932 proxy_settings->SetStringWithoutPathExpansion(::onc::proxy::kType,
933 ::onc::proxy::kWPAD);
934 break;
936 case ProxyPrefs::MODE_PAC_SCRIPT: {
937 proxy_settings->SetStringWithoutPathExpansion(::onc::proxy::kType,
938 ::onc::proxy::kPAC);
939 std::string pac_url;
940 proxy_config->GetPacUrl(&pac_url);
941 proxy_settings->SetStringWithoutPathExpansion(::onc::proxy::kPAC,
942 pac_url);
943 break;
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,
953 manual.get());
954 SetProxyForScheme(proxy_rules, url::kHttpScheme, ::onc::proxy::kHttp,
955 manual.get());
956 SetProxyForScheme(proxy_rules, url::kHttpsScheme, ::onc::proxy::kHttps,
957 manual.get());
958 SetProxyForScheme(proxy_rules, kSocksScheme, ::onc::proxy::kSocks,
959 manual.get());
961 proxy_settings->SetWithoutPathExpansion(::onc::proxy::kManual,
962 manual.release());
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());
977 break;
979 default: {
980 LOG(ERROR) << "Unexpected proxy mode in Shill config: " << mode;
981 return nullptr;
984 return proxy_settings.Pass();
987 } // namespace onc
988 } // namespace chromeos