Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chromeos / network / onc / onc_utils.cc
blob771c51b057963d324c0b3a5669cf86303a51b34b
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;
35 namespace chromeos {
36 namespace onc {
38 namespace {
40 const char kUnableToDecrypt[] = "Unable to decrypt encrypted ONC";
41 const char kUnableToDecode[] = "Unable to decode encrypted ONC";
43 } // namespace
45 const char kEmptyUnencryptedConfiguration[] =
46 "{\"Type\":\"UnencryptedConfiguration\",\"NetworkConfigurations\":[],"
47 "\"Certificates\":[]}";
49 scoped_ptr<base::DictionaryValue> ReadDictionaryFromJson(
50 const std::string& json) {
51 std::string error;
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;
58 return nullptr;
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;
68 std::string onc_type;
69 std::string initial_vector;
70 std::string salt;
71 std::string cipher;
72 std::string stretch_method;
73 std::string hmac_method;
74 std::string hmac;
75 int iterations;
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.";
89 return nullptr;
92 if (hmac_method != encrypted::kSHA1 ||
93 cipher != encrypted::kAES256 ||
94 stretch_method != encrypted::kPBKDF2) {
95 NET_LOG(ERROR) << "Encrypted ONC unsupported encryption scheme.";
96 return nullptr;
99 // Make sure iterations != 0, since that's not valid.
100 if (iterations == 0) {
101 NET_LOG(ERROR) << kUnableToDecrypt;
102 return nullptr;
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";
109 return nullptr;
112 if (!base::Base64Decode(salt, &salt)) {
113 NET_LOG(ERROR) << kUnableToDecode;
114 return nullptr;
117 scoped_ptr<crypto::SymmetricKey> key(
118 crypto::SymmetricKey::DeriveKeyFromPassword(crypto::SymmetricKey::AES,
119 passphrase,
120 salt,
121 iterations,
122 kKeySizeInBits));
124 if (!base::Base64Decode(initial_vector, &initial_vector)) {
125 NET_LOG(ERROR) << kUnableToDecode;
126 return nullptr;
128 if (!base::Base64Decode(ciphertext, &ciphertext)) {
129 NET_LOG(ERROR) << kUnableToDecode;
130 return nullptr;
132 if (!base::Base64Decode(hmac, &hmac)) {
133 NET_LOG(ERROR) << kUnableToDecode;
134 return nullptr;
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;
141 return nullptr;
144 crypto::Encryptor decryptor;
145 if (!decryptor.Init(key.get(), crypto::Encryptor::CBC, initial_vector)) {
146 NET_LOG(ERROR) << kUnableToDecrypt;
147 return nullptr;
150 std::string plaintext;
151 if (!decryptor.Decrypt(ciphertext, &plaintext)) {
152 NET_LOG(ERROR) << kUnableToDecrypt;
153 return nullptr;
156 scoped_ptr<base::DictionaryValue> new_root =
157 ReadDictionaryFromJson(plaintext);
158 if (!new_root) {
159 NET_LOG(ERROR) << "Property dictionary malformed.";
160 return nullptr;
163 return new_root.Pass();
166 std::string GetSourceAsString(ONCSource source) {
167 switch (source) {
168 case ONC_SOURCE_UNKNOWN:
169 return "unknown";
170 case ONC_SOURCE_NONE:
171 return "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;
180 return "unknown";
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))
188 return;
190 std::string login_id;
191 if (substitution.GetSubstitute(substitutes::kLoginIDField, &login_id)) {
192 base::ReplaceSubstringsAfterOffset(&user_string, 0,
193 substitutes::kLoginIDField,
194 login_id);
197 std::string email;
198 if (substitution.GetSubstitute(substitutes::kEmailField, &email)) {
199 base::ReplaceSubstringsAfterOffset(&user_string, 0,
200 substitutes::kEmailField,
201 email);
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();
221 it.Advance()) {
222 base::DictionaryValue* inner_object = nullptr;
223 if (!onc_object->GetDictionaryWithoutPathExpansion(it.key(), &inner_object))
224 continue;
226 const OncFieldSignature* field_signature =
227 GetFieldSignature(signature, it.key());
228 if (!field_signature)
229 continue;
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);
241 DCHECK(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();
254 it.Advance()) {
255 base::DictionaryValue* inner_object = nullptr;
256 if (!onc_object->GetDictionaryWithoutPathExpansion(it.key(), &inner_object))
257 continue;
259 const OncFieldSignature* field_signature =
260 GetFieldSignature(signature, it.key());
261 if (!field_signature)
262 continue;
264 FillInHexSSIDFieldsInOncObject(*field_signature->value_signature,
265 inner_object);
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,
273 &ssid_string)) {
274 return;
276 if (ssid_string.empty()) {
277 NET_LOG(ERROR) << "Found empty SSID field.";
278 return;
280 wifi_fields->SetStringWithoutPathExpansion(
281 ::onc::wifi::kHexSSID,
282 base::HexEncode(ssid_string.c_str(), ssid_string.size()));
285 namespace {
287 class OncMaskValues : public Mapper {
288 public:
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);
294 bool unused_error;
295 return masker.MapObject(signature, onc_object, &unused_error);
298 protected:
299 explicit OncMaskValues(const std::string& mask)
300 : mask_(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_));
310 } else {
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.
317 std::string mask_;
320 } // namespace
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);
329 namespace {
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);
343 std::string decoded;
344 if (pem_tokenizer.GetNext()) {
345 decoded = pem_tokenizer.data();
346 } else {
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();
355 return decoded;
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);
365 std::string guid;
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) {
371 continue;
373 std::string x509_data;
374 cert->GetStringWithoutPathExpansion(certificate::kX509, &x509_data);
376 std::string der = DecodePEM(x509_data);
377 std::string pem;
378 if (der.empty() || !net::X509Certificate::GetPEMEncodedFromDER(der, &pem)) {
379 LOG(ERROR) << "Certificate with GUID " << guid
380 << " is not in PEM encoding.";
381 continue;
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);
393 DCHECK(network);
394 FillInHexSSIDFieldsInOncObject(kNetworkConfigurationSignature, network);
398 } // namespace
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())
410 return true;
412 scoped_ptr<base::DictionaryValue> toplevel_onc =
413 ReadDictionaryFromJson(onc_blob);
414 if (!toplevel_onc) {
415 LOG(ERROR) << "ONC loaded from " << GetSourceAsString(onc_source)
416 << " is not a valid JSON dictionary.";
417 return false;
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,
423 &onc_type);
424 if (onc_type == toplevel_config::kEncryptedConfiguration) {
425 toplevel_onc = Decrypt(passphrase, *toplevel_onc);
426 if (!toplevel_onc) {
427 LOG(ERROR) << "Couldn't decrypt the ONC from "
428 << GetSourceAsString(onc_source);
429 return false;
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.
441 from_policy);
442 validator.SetOncSource(onc_source);
444 Validator::Result validation_result;
445 toplevel_onc = validator.ValidateAndRepairObject(
446 &kToplevelConfigurationSignature,
447 *toplevel_onc,
448 &validation_result);
450 if (from_policy) {
451 UMA_HISTOGRAM_BOOLEAN("Enterprise.ONC.PolicyValidation",
452 validation_result == Validator::VALID);
455 bool success = true;
456 if (validation_result == Validator::VALID_WITH_WARNINGS) {
457 LOG(WARNING) << "ONC from " << GetSourceAsString(onc_source)
458 << " produced warnings.";
459 success = false;
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.";
463 return false;
466 base::ListValue* validated_certs = nullptr;
467 if (toplevel_onc->GetListWithoutPathExpansion(toplevel_config::kCertificates,
468 &validated_certs)) {
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.";
484 success = false;
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);
497 return success;
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: "
506 << decoded;
507 return cert;
510 namespace {
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;
518 return false;
520 *pem_encoded = it->second;
521 if (pem_encoded->empty()) {
522 LOG(ERROR) << "Couldn't PEM-encode certificate with GUID " << guid_ref;
523 return false;
525 return true;
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))
534 return true;
536 std::string pem_encoded;
537 if (!GUIDRefToPEMEncoding(certs_by_guid, guid_ref, &pem_encoded))
538 return false;
540 onc_object->RemoveWithoutPathExpansion(key_guid_ref, nullptr);
541 onc_object->SetStringWithoutPathExpansion(key_pem, pem_encoded);
542 return true;
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,
551 &guid_ref_list)) {
552 return true;
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))
562 return false;
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());
569 return true;
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))
578 return true;
580 std::string pem_encoded;
581 if (!GUIDRefToPEMEncoding(certs_by_guid, guid_ref, &pem_encoded))
582 return false;
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());
588 return true;
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
593 // was successful.
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,
621 onc_object)) {
622 return false;
624 } else if (&signature == &kEAPSignature) {
625 if (!ResolveCertRefsOrRefToList(certs_by_guid,
626 eap::kServerCARefs,
627 eap::kServerCARef,
628 eap::kServerCAPEMs,
629 onc_object)) {
630 return false;
632 } else if (&signature == &kIPsecSignature) {
633 if (!ResolveCertRefsOrRefToList(certs_by_guid,
634 ipsec::kServerCARefs,
635 ipsec::kServerCARef,
636 ipsec::kServerCAPEMs,
637 onc_object)) {
638 return false;
640 } else if (&signature == &kIPsecSignature ||
641 &signature == &kOpenVPNSignature) {
642 if (!ResolveSingleCertRef(certs_by_guid,
643 openvpn::kServerCertRef,
644 openvpn::kServerCertPEM,
645 onc_object) ||
646 !ResolveCertRefsOrRefToList(certs_by_guid,
647 openvpn::kServerCARefs,
648 openvpn::kServerCARef,
649 openvpn::kServerCAPEMs,
650 onc_object)) {
651 return false;
655 // Recurse into nested objects.
656 for (base::DictionaryValue::Iterator it(*onc_object); !it.IsAtEnd();
657 it.Advance()) {
658 base::DictionaryValue* inner_object = nullptr;
659 if (!onc_object->GetDictionaryWithoutPathExpansion(it.key(), &inner_object))
660 continue;
662 const OncFieldSignature* field_signature =
663 GetFieldSignature(signature, it.key());
664 if (!field_signature)
665 continue;
667 if (!ResolveServerCertRefsInObject(certs_by_guid,
668 *field_signature->value_signature,
669 inner_object)) {
670 return false;
673 return true;
676 } // namespace
678 bool ResolveServerCertRefsInNetworks(const CertPEMsByGUIDMap& certs_by_guid,
679 base::ListValue* network_configs) {
680 bool success = true;
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)) {
686 std::string guid;
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 "
691 << guid;
692 it = network_configs->Erase(it, nullptr);
693 success = false;
694 continue;
696 ++it;
698 return success;
701 bool ResolveServerCertRefsInNetwork(const CertPEMsByGUIDMap& certs_by_guid,
702 base::DictionaryValue* network_config) {
703 return ResolveServerCertRefsInObject(certs_by_guid,
704 kNetworkConfigurationSignature,
705 network_config);
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;
736 } else {
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());
748 namespace {
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) {
759 std::string host;
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);
764 int port = 0;
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,
776 std::string* spec) {
777 const base::DictionaryValue* onc_proxy_location = nullptr;
778 if (!onc_manual.GetDictionaryWithoutPathExpansion(onc_scheme,
779 &onc_proxy_location)) {
780 return;
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;
794 } else {
795 NOTREACHED();
798 net::ProxyServer proxy_server = ConvertOncProxyLocationToHostPort(
799 default_proxy_scheme, *onc_proxy_location);
801 ProxyConfigDictionary::EncodeAndAppendProxyServer(url_scheme, proxy_server,
802 spec);
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) {
810 std::string rule;
811 (*it)->GetAsString(&rule);
812 rules.AddRuleFromString(rule);
814 return rules;
817 std::string SchemeToString(net::ProxyServer::Scheme scheme) {
818 switch (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:
830 return kQuicScheme;
831 case net::ProxyServer::SCHEME_INVALID:
832 break;
834 NOTREACHED();
835 return "";
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())
850 return;
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
856 // HTTP.
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());
869 } // namespace
871 scoped_ptr<base::DictionaryValue> ConvertOncProxySettingsToProxyConfig(
872 const base::DictionaryValue& onc_proxy_settings) {
873 std::string type;
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) {
882 std::string pac_url;
883 onc_proxy_settings.GetStringWithoutPathExpansion(::onc::proxy::kPAC,
884 &pac_url);
885 GURL url(pac_url);
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,
891 &manual_dict);
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,
896 &manual_spec);
897 AppendProxyServerForScheme(*manual_dict, ::onc::proxy::kHttps,
898 &manual_spec);
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()));
909 } else {
910 NOTREACHED();
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))
925 return nullptr;
926 switch (mode) {
927 case ProxyPrefs::MODE_DIRECT: {
928 proxy_settings->SetStringWithoutPathExpansion(::onc::proxy::kType,
929 ::onc::proxy::kDirect);
930 break;
932 case ProxyPrefs::MODE_AUTO_DETECT: {
933 proxy_settings->SetStringWithoutPathExpansion(::onc::proxy::kType,
934 ::onc::proxy::kWPAD);
935 break;
937 case ProxyPrefs::MODE_PAC_SCRIPT: {
938 proxy_settings->SetStringWithoutPathExpansion(::onc::proxy::kType,
939 ::onc::proxy::kPAC);
940 std::string pac_url;
941 proxy_config->GetPacUrl(&pac_url);
942 proxy_settings->SetStringWithoutPathExpansion(::onc::proxy::kPAC,
943 pac_url);
944 break;
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,
954 manual.get());
955 SetProxyForScheme(proxy_rules, url::kHttpScheme, ::onc::proxy::kHttp,
956 manual.get());
957 SetProxyForScheme(proxy_rules, url::kHttpsScheme, ::onc::proxy::kHttps,
958 manual.get());
959 SetProxyForScheme(proxy_rules, kSocksScheme, ::onc::proxy::kSocks,
960 manual.get());
962 proxy_settings->SetWithoutPathExpansion(::onc::proxy::kManual,
963 manual.release());
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());
978 break;
980 default: {
981 LOG(ERROR) << "Unexpected proxy mode in Shill config: " << mode;
982 return nullptr;
985 return proxy_settings.Pass();
988 } // namespace onc
989 } // namespace chromeos