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 "crypto/encryptor.h"
21 #include "crypto/hmac.h"
22 #include "crypto/symmetric_key.h"
23 #include "net/cert/pem_tokenizer.h"
24 #include "net/cert/x509_certificate.h"
26 using namespace ::onc
;
33 const char kUnableToDecrypt
[] = "Unable to decrypt encrypted ONC";
34 const char kUnableToDecode
[] = "Unable to decode encrypted ONC";
38 const char kEmptyUnencryptedConfiguration
[] =
39 "{\"Type\":\"UnencryptedConfiguration\",\"NetworkConfigurations\":[],"
40 "\"Certificates\":[]}";
42 scoped_ptr
<base::DictionaryValue
> ReadDictionaryFromJson(
43 const std::string
& json
) {
45 base::Value
* root
= base::JSONReader::ReadAndReturnError(
46 json
, base::JSON_ALLOW_TRAILING_COMMAS
, nullptr, &error
);
48 base::DictionaryValue
* dict_ptr
= nullptr;
49 if (!root
|| !root
->GetAsDictionary(&dict_ptr
)) {
50 NET_LOG(ERROR
) << "Invalid JSON Dictionary: " << error
;
54 return make_scoped_ptr(dict_ptr
);
57 scoped_ptr
<base::DictionaryValue
> Decrypt(const std::string
& passphrase
,
58 const base::DictionaryValue
& root
) {
59 const int kKeySizeInBits
= 256;
60 const int kMaxIterationCount
= 500000;
62 std::string initial_vector
;
65 std::string stretch_method
;
66 std::string hmac_method
;
69 std::string ciphertext
;
71 if (!root
.GetString(encrypted::kCiphertext
, &ciphertext
) ||
72 !root
.GetString(encrypted::kCipher
, &cipher
) ||
73 !root
.GetString(encrypted::kHMAC
, &hmac
) ||
74 !root
.GetString(encrypted::kHMACMethod
, &hmac_method
) ||
75 !root
.GetString(encrypted::kIV
, &initial_vector
) ||
76 !root
.GetInteger(encrypted::kIterations
, &iterations
) ||
77 !root
.GetString(encrypted::kSalt
, &salt
) ||
78 !root
.GetString(encrypted::kStretch
, &stretch_method
) ||
79 !root
.GetString(toplevel_config::kType
, &onc_type
) ||
80 onc_type
!= toplevel_config::kEncryptedConfiguration
) {
81 NET_LOG(ERROR
) << "Encrypted ONC malformed.";
85 if (hmac_method
!= encrypted::kSHA1
||
86 cipher
!= encrypted::kAES256
||
87 stretch_method
!= encrypted::kPBKDF2
) {
88 NET_LOG(ERROR
) << "Encrypted ONC unsupported encryption scheme.";
92 // Make sure iterations != 0, since that's not valid.
93 if (iterations
== 0) {
94 NET_LOG(ERROR
) << kUnableToDecrypt
;
98 // Simply a sanity check to make sure we can't lock up the machine
99 // for too long with a huge number (or a negative number).
100 if (iterations
< 0 || iterations
> kMaxIterationCount
) {
101 NET_LOG(ERROR
) << "Too many iterations in encrypted ONC";
105 if (!base::Base64Decode(salt
, &salt
)) {
106 NET_LOG(ERROR
) << kUnableToDecode
;
110 scoped_ptr
<crypto::SymmetricKey
> key(
111 crypto::SymmetricKey::DeriveKeyFromPassword(crypto::SymmetricKey::AES
,
117 if (!base::Base64Decode(initial_vector
, &initial_vector
)) {
118 NET_LOG(ERROR
) << kUnableToDecode
;
121 if (!base::Base64Decode(ciphertext
, &ciphertext
)) {
122 NET_LOG(ERROR
) << kUnableToDecode
;
125 if (!base::Base64Decode(hmac
, &hmac
)) {
126 NET_LOG(ERROR
) << kUnableToDecode
;
130 crypto::HMAC
hmac_verifier(crypto::HMAC::SHA1
);
131 if (!hmac_verifier
.Init(key
.get()) ||
132 !hmac_verifier
.Verify(ciphertext
, hmac
)) {
133 NET_LOG(ERROR
) << kUnableToDecrypt
;
137 crypto::Encryptor decryptor
;
138 if (!decryptor
.Init(key
.get(), crypto::Encryptor::CBC
, initial_vector
)) {
139 NET_LOG(ERROR
) << kUnableToDecrypt
;
143 std::string plaintext
;
144 if (!decryptor
.Decrypt(ciphertext
, &plaintext
)) {
145 NET_LOG(ERROR
) << kUnableToDecrypt
;
149 scoped_ptr
<base::DictionaryValue
> new_root
=
150 ReadDictionaryFromJson(plaintext
);
152 NET_LOG(ERROR
) << "Property dictionary malformed.";
156 return new_root
.Pass();
159 std::string
GetSourceAsString(ONCSource source
) {
161 case ONC_SOURCE_UNKNOWN
:
163 case ONC_SOURCE_NONE
:
165 case ONC_SOURCE_DEVICE_POLICY
:
166 return "device policy";
167 case ONC_SOURCE_USER_POLICY
:
168 return "user policy";
169 case ONC_SOURCE_USER_IMPORT
:
170 return "user import";
172 NOTREACHED() << "unknown ONC source " << source
;
176 void ExpandField(const std::string
& fieldname
,
177 const StringSubstitution
& substitution
,
178 base::DictionaryValue
* onc_object
) {
179 std::string user_string
;
180 if (!onc_object
->GetStringWithoutPathExpansion(fieldname
, &user_string
))
183 std::string login_id
;
184 if (substitution
.GetSubstitute(substitutes::kLoginIDField
, &login_id
)) {
185 ReplaceSubstringsAfterOffset(&user_string
, 0,
186 substitutes::kLoginIDField
,
191 if (substitution
.GetSubstitute(substitutes::kEmailField
, &email
)) {
192 ReplaceSubstringsAfterOffset(&user_string
, 0,
193 substitutes::kEmailField
,
197 onc_object
->SetStringWithoutPathExpansion(fieldname
, user_string
);
200 void ExpandStringsInOncObject(
201 const OncValueSignature
& signature
,
202 const StringSubstitution
& substitution
,
203 base::DictionaryValue
* onc_object
) {
204 if (&signature
== &kEAPSignature
) {
205 ExpandField(eap::kAnonymousIdentity
, substitution
, onc_object
);
206 ExpandField(eap::kIdentity
, substitution
, onc_object
);
207 } else if (&signature
== &kL2TPSignature
||
208 &signature
== &kOpenVPNSignature
) {
209 ExpandField(vpn::kUsername
, substitution
, onc_object
);
212 // Recurse into nested objects.
213 for (base::DictionaryValue::Iterator
it(*onc_object
); !it
.IsAtEnd();
215 base::DictionaryValue
* inner_object
= nullptr;
216 if (!onc_object
->GetDictionaryWithoutPathExpansion(it
.key(), &inner_object
))
219 const OncFieldSignature
* field_signature
=
220 GetFieldSignature(signature
, it
.key());
221 if (!field_signature
)
224 ExpandStringsInOncObject(*field_signature
->value_signature
,
225 substitution
, inner_object
);
229 void ExpandStringsInNetworks(const StringSubstitution
& substitution
,
230 base::ListValue
* network_configs
) {
231 for (base::Value
* entry
: *network_configs
) {
232 base::DictionaryValue
* network
= nullptr;
233 entry
->GetAsDictionary(&network
);
235 ExpandStringsInOncObject(
236 kNetworkConfigurationSignature
, substitution
, network
);
240 void FillInHexSSIDFieldsInOncObject(const OncValueSignature
& signature
,
241 base::DictionaryValue
* onc_object
) {
242 if (&signature
== &kWiFiSignature
)
243 FillInHexSSIDField(onc_object
);
245 // Recurse into nested objects.
246 for (base::DictionaryValue::Iterator
it(*onc_object
); !it
.IsAtEnd();
248 base::DictionaryValue
* inner_object
= nullptr;
249 if (!onc_object
->GetDictionaryWithoutPathExpansion(it
.key(), &inner_object
))
252 const OncFieldSignature
* field_signature
=
253 GetFieldSignature(signature
, it
.key());
254 if (!field_signature
)
257 FillInHexSSIDFieldsInOncObject(*field_signature
->value_signature
,
262 void FillInHexSSIDField(base::DictionaryValue
* wifi_fields
) {
263 std::string ssid_string
;
264 if (wifi_fields
->HasKey(::onc::wifi::kHexSSID
) ||
265 !wifi_fields
->GetStringWithoutPathExpansion(::onc::wifi::kSSID
,
269 if (ssid_string
.empty()) {
270 NET_LOG(ERROR
) << "Found empty SSID field.";
273 wifi_fields
->SetStringWithoutPathExpansion(
274 ::onc::wifi::kHexSSID
,
275 base::HexEncode(ssid_string
.c_str(), ssid_string
.size()));
280 class OncMaskValues
: public Mapper
{
282 static scoped_ptr
<base::DictionaryValue
> Mask(
283 const OncValueSignature
& signature
,
284 const base::DictionaryValue
& onc_object
,
285 const std::string
& mask
) {
286 OncMaskValues
masker(mask
);
288 return masker
.MapObject(signature
, onc_object
, &unused_error
);
292 explicit OncMaskValues(const std::string
& mask
)
296 scoped_ptr
<base::Value
> MapField(const std::string
& field_name
,
297 const OncValueSignature
& object_signature
,
298 const base::Value
& onc_value
,
299 bool* found_unknown_field
,
300 bool* error
) override
{
301 if (FieldIsCredential(object_signature
, field_name
)) {
302 return scoped_ptr
<base::Value
>(new base::StringValue(mask_
));
304 return Mapper::MapField(field_name
, object_signature
, onc_value
,
305 found_unknown_field
, error
);
309 // Mask to insert in place of the sensitive values.
315 scoped_ptr
<base::DictionaryValue
> MaskCredentialsInOncObject(
316 const OncValueSignature
& signature
,
317 const base::DictionaryValue
& onc_object
,
318 const std::string
& mask
) {
319 return OncMaskValues::Mask(signature
, onc_object
, mask
);
324 std::string
DecodePEM(const std::string
& pem_encoded
) {
325 // The PEM block header used for DER certificates
326 const char kCertificateHeader
[] = "CERTIFICATE";
328 // This is an older PEM marker for DER certificates.
329 const char kX509CertificateHeader
[] = "X509 CERTIFICATE";
331 std::vector
<std::string
> pem_headers
;
332 pem_headers
.push_back(kCertificateHeader
);
333 pem_headers
.push_back(kX509CertificateHeader
);
335 net::PEMTokenizer
pem_tokenizer(pem_encoded
, pem_headers
);
337 if (pem_tokenizer
.GetNext()) {
338 decoded
= pem_tokenizer
.data();
340 // If we failed to read the data as a PEM file, then try plain base64 decode
341 // in case the PEM marker strings are missing. For this to work, there has
342 // to be no white space, and it has to only contain the base64-encoded data.
343 if (!base::Base64Decode(pem_encoded
, &decoded
)) {
344 LOG(ERROR
) << "Unable to base64 decode X509 data: " << pem_encoded
;
345 return std::string();
351 CertPEMsByGUIDMap
GetServerAndCACertsByGUID(
352 const base::ListValue
& certificates
) {
353 CertPEMsByGUIDMap certs_by_guid
;
354 for (const base::Value
* entry
: certificates
) {
355 const base::DictionaryValue
* cert
= nullptr;
356 entry
->GetAsDictionary(&cert
);
359 cert
->GetStringWithoutPathExpansion(certificate::kGUID
, &guid
);
360 std::string cert_type
;
361 cert
->GetStringWithoutPathExpansion(certificate::kType
, &cert_type
);
362 if (cert_type
!= certificate::kServer
&&
363 cert_type
!= certificate::kAuthority
) {
366 std::string x509_data
;
367 cert
->GetStringWithoutPathExpansion(certificate::kX509
, &x509_data
);
369 std::string der
= DecodePEM(x509_data
);
371 if (der
.empty() || !net::X509Certificate::GetPEMEncodedFromDER(der
, &pem
)) {
372 LOG(ERROR
) << "Certificate with GUID " << guid
373 << " is not in PEM encoding.";
376 certs_by_guid
[guid
] = pem
;
379 return certs_by_guid
;
382 void FillInHexSSIDFieldsInNetworks(base::ListValue
* network_configs
) {
383 for (base::Value
* entry
: *network_configs
) {
384 base::DictionaryValue
* network
= nullptr;
385 entry
->GetAsDictionary(&network
);
387 FillInHexSSIDFieldsInOncObject(kNetworkConfigurationSignature
, network
);
393 bool ParseAndValidateOncForImport(const std::string
& onc_blob
,
394 ONCSource onc_source
,
395 const std::string
& passphrase
,
396 base::ListValue
* network_configs
,
397 base::DictionaryValue
* global_network_config
,
398 base::ListValue
* certificates
) {
399 network_configs
->Clear();
400 global_network_config
->Clear();
401 certificates
->Clear();
402 if (onc_blob
.empty())
405 scoped_ptr
<base::DictionaryValue
> toplevel_onc
=
406 ReadDictionaryFromJson(onc_blob
);
408 LOG(ERROR
) << "ONC loaded from " << GetSourceAsString(onc_source
)
409 << " is not a valid JSON dictionary.";
413 // Check and see if this is an encrypted ONC file. If so, decrypt it.
414 std::string onc_type
;
415 toplevel_onc
->GetStringWithoutPathExpansion(toplevel_config::kType
,
417 if (onc_type
== toplevel_config::kEncryptedConfiguration
) {
418 toplevel_onc
= Decrypt(passphrase
, *toplevel_onc
);
420 LOG(ERROR
) << "Couldn't decrypt the ONC from "
421 << GetSourceAsString(onc_source
);
426 bool from_policy
= (onc_source
== ONC_SOURCE_USER_POLICY
||
427 onc_source
== ONC_SOURCE_DEVICE_POLICY
);
429 // Validate the ONC dictionary. We are liberal and ignore unknown field
430 // names and ignore invalid field names in kRecommended arrays.
431 Validator
validator(false, // Ignore unknown fields.
432 false, // Ignore invalid recommended field names.
433 true, // Fail on missing fields.
435 validator
.SetOncSource(onc_source
);
437 Validator::Result validation_result
;
438 toplevel_onc
= validator
.ValidateAndRepairObject(
439 &kToplevelConfigurationSignature
,
444 UMA_HISTOGRAM_BOOLEAN("Enterprise.ONC.PolicyValidation",
445 validation_result
== Validator::VALID
);
449 if (validation_result
== Validator::VALID_WITH_WARNINGS
) {
450 LOG(WARNING
) << "ONC from " << GetSourceAsString(onc_source
)
451 << " produced warnings.";
453 } else if (validation_result
== Validator::INVALID
|| !toplevel_onc
) {
454 LOG(ERROR
) << "ONC from " << GetSourceAsString(onc_source
)
455 << " is invalid and couldn't be repaired.";
459 base::ListValue
* validated_certs
= nullptr;
460 if (toplevel_onc
->GetListWithoutPathExpansion(toplevel_config::kCertificates
,
462 certificates
->Swap(validated_certs
);
465 base::ListValue
* validated_networks
= nullptr;
466 if (toplevel_onc
->GetListWithoutPathExpansion(
467 toplevel_config::kNetworkConfigurations
, &validated_networks
)) {
468 FillInHexSSIDFieldsInNetworks(validated_networks
);
470 CertPEMsByGUIDMap server_and_ca_certs
=
471 GetServerAndCACertsByGUID(*certificates
);
473 if (!ResolveServerCertRefsInNetworks(server_and_ca_certs
,
474 validated_networks
)) {
475 LOG(ERROR
) << "Some certificate references in the ONC policy for source "
476 << GetSourceAsString(onc_source
) << " could not be resolved.";
480 network_configs
->Swap(validated_networks
);
483 base::DictionaryValue
* validated_global_config
= nullptr;
484 if (toplevel_onc
->GetDictionaryWithoutPathExpansion(
485 toplevel_config::kGlobalNetworkConfiguration
,
486 &validated_global_config
)) {
487 global_network_config
->Swap(validated_global_config
);
493 scoped_refptr
<net::X509Certificate
> DecodePEMCertificate(
494 const std::string
& pem_encoded
) {
495 std::string decoded
= DecodePEM(pem_encoded
);
496 scoped_refptr
<net::X509Certificate
> cert
=
497 net::X509Certificate::CreateFromBytes(decoded
.data(), decoded
.size());
498 LOG_IF(ERROR
, !cert
.get()) << "Couldn't create certificate from X509 data: "
505 bool GUIDRefToPEMEncoding(const CertPEMsByGUIDMap
& certs_by_guid
,
506 const std::string
& guid_ref
,
507 std::string
* pem_encoded
) {
508 CertPEMsByGUIDMap::const_iterator it
= certs_by_guid
.find(guid_ref
);
509 if (it
== certs_by_guid
.end()) {
510 LOG(ERROR
) << "Couldn't resolve certificate reference " << guid_ref
;
513 *pem_encoded
= it
->second
;
514 if (pem_encoded
->empty()) {
515 LOG(ERROR
) << "Couldn't PEM-encode certificate with GUID " << guid_ref
;
521 bool ResolveSingleCertRef(const CertPEMsByGUIDMap
& certs_by_guid
,
522 const std::string
& key_guid_ref
,
523 const std::string
& key_pem
,
524 base::DictionaryValue
* onc_object
) {
525 std::string guid_ref
;
526 if (!onc_object
->GetStringWithoutPathExpansion(key_guid_ref
, &guid_ref
))
529 std::string pem_encoded
;
530 if (!GUIDRefToPEMEncoding(certs_by_guid
, guid_ref
, &pem_encoded
))
533 onc_object
->RemoveWithoutPathExpansion(key_guid_ref
, nullptr);
534 onc_object
->SetStringWithoutPathExpansion(key_pem
, pem_encoded
);
538 bool ResolveCertRefList(const CertPEMsByGUIDMap
& certs_by_guid
,
539 const std::string
& key_guid_ref_list
,
540 const std::string
& key_pem_list
,
541 base::DictionaryValue
* onc_object
) {
542 const base::ListValue
* guid_ref_list
= nullptr;
543 if (!onc_object
->GetListWithoutPathExpansion(key_guid_ref_list
,
548 scoped_ptr
<base::ListValue
> pem_list(new base::ListValue
);
549 for (const base::Value
* entry
: *guid_ref_list
) {
550 std::string guid_ref
;
551 entry
->GetAsString(&guid_ref
);
553 std::string pem_encoded
;
554 if (!GUIDRefToPEMEncoding(certs_by_guid
, guid_ref
, &pem_encoded
))
557 pem_list
->AppendString(pem_encoded
);
560 onc_object
->RemoveWithoutPathExpansion(key_guid_ref_list
, nullptr);
561 onc_object
->SetWithoutPathExpansion(key_pem_list
, pem_list
.release());
565 bool ResolveSingleCertRefToList(const CertPEMsByGUIDMap
& certs_by_guid
,
566 const std::string
& key_guid_ref
,
567 const std::string
& key_pem_list
,
568 base::DictionaryValue
* onc_object
) {
569 std::string guid_ref
;
570 if (!onc_object
->GetStringWithoutPathExpansion(key_guid_ref
, &guid_ref
))
573 std::string pem_encoded
;
574 if (!GUIDRefToPEMEncoding(certs_by_guid
, guid_ref
, &pem_encoded
))
577 scoped_ptr
<base::ListValue
> pem_list(new base::ListValue
);
578 pem_list
->AppendString(pem_encoded
);
579 onc_object
->RemoveWithoutPathExpansion(key_guid_ref
, nullptr);
580 onc_object
->SetWithoutPathExpansion(key_pem_list
, pem_list
.release());
584 // Resolves the reference list at |key_guid_refs| if present and otherwise the
585 // single reference at |key_guid_ref|. Returns whether the respective resolving
587 bool ResolveCertRefsOrRefToList(const CertPEMsByGUIDMap
& certs_by_guid
,
588 const std::string
& key_guid_refs
,
589 const std::string
& key_guid_ref
,
590 const std::string
& key_pem_list
,
591 base::DictionaryValue
* onc_object
) {
592 if (onc_object
->HasKey(key_guid_refs
)) {
593 if (onc_object
->HasKey(key_guid_ref
)) {
594 LOG(ERROR
) << "Found both " << key_guid_refs
<< " and " << key_guid_ref
595 << ". Ignoring and removing the latter.";
596 onc_object
->RemoveWithoutPathExpansion(key_guid_ref
, nullptr);
598 return ResolveCertRefList(
599 certs_by_guid
, key_guid_refs
, key_pem_list
, onc_object
);
602 // Only resolve |key_guid_ref| if |key_guid_refs| isn't present.
603 return ResolveSingleCertRefToList(
604 certs_by_guid
, key_guid_ref
, key_pem_list
, onc_object
);
607 bool ResolveServerCertRefsInObject(const CertPEMsByGUIDMap
& certs_by_guid
,
608 const OncValueSignature
& signature
,
609 base::DictionaryValue
* onc_object
) {
610 if (&signature
== &kCertificatePatternSignature
) {
611 if (!ResolveCertRefList(certs_by_guid
,
612 client_cert::kIssuerCARef
,
613 client_cert::kIssuerCAPEMs
,
617 } else if (&signature
== &kEAPSignature
) {
618 if (!ResolveCertRefsOrRefToList(certs_by_guid
,
625 } else if (&signature
== &kIPsecSignature
) {
626 if (!ResolveCertRefsOrRefToList(certs_by_guid
,
627 ipsec::kServerCARefs
,
629 ipsec::kServerCAPEMs
,
633 } else if (&signature
== &kIPsecSignature
||
634 &signature
== &kOpenVPNSignature
) {
635 if (!ResolveSingleCertRef(certs_by_guid
,
636 openvpn::kServerCertRef
,
637 openvpn::kServerCertPEM
,
639 !ResolveCertRefsOrRefToList(certs_by_guid
,
640 openvpn::kServerCARefs
,
641 openvpn::kServerCARef
,
642 openvpn::kServerCAPEMs
,
648 // Recurse into nested objects.
649 for (base::DictionaryValue::Iterator
it(*onc_object
); !it
.IsAtEnd();
651 base::DictionaryValue
* inner_object
= nullptr;
652 if (!onc_object
->GetDictionaryWithoutPathExpansion(it
.key(), &inner_object
))
655 const OncFieldSignature
* field_signature
=
656 GetFieldSignature(signature
, it
.key());
657 if (!field_signature
)
660 if (!ResolveServerCertRefsInObject(certs_by_guid
,
661 *field_signature
->value_signature
,
671 bool ResolveServerCertRefsInNetworks(const CertPEMsByGUIDMap
& certs_by_guid
,
672 base::ListValue
* network_configs
) {
674 for (base::ListValue::iterator it
= network_configs
->begin();
675 it
!= network_configs
->end(); ) {
676 base::DictionaryValue
* network
= nullptr;
677 (*it
)->GetAsDictionary(&network
);
678 if (!ResolveServerCertRefsInNetwork(certs_by_guid
, network
)) {
680 network
->GetStringWithoutPathExpansion(network_config::kGUID
, &guid
);
681 // This might happen even with correct validation, if the referenced
682 // certificate couldn't be imported.
683 LOG(ERROR
) << "Couldn't resolve some certificate reference of network "
685 it
= network_configs
->Erase(it
, nullptr);
694 bool ResolveServerCertRefsInNetwork(const CertPEMsByGUIDMap
& certs_by_guid
,
695 base::DictionaryValue
* network_config
) {
696 return ResolveServerCertRefsInObject(certs_by_guid
,
697 kNetworkConfigurationSignature
,
701 NetworkTypePattern
NetworkTypePatternFromOncType(const std::string
& type
) {
702 if (type
== ::onc::network_type::kAllTypes
)
703 return NetworkTypePattern::Default();
704 if (type
== ::onc::network_type::kCellular
)
705 return NetworkTypePattern::Cellular();
706 if (type
== ::onc::network_type::kEthernet
)
707 return NetworkTypePattern::Ethernet();
708 if (type
== ::onc::network_type::kVPN
)
709 return NetworkTypePattern::VPN();
710 if (type
== ::onc::network_type::kWiFi
)
711 return NetworkTypePattern::WiFi();
712 if (type
== ::onc::network_type::kWimax
)
713 return NetworkTypePattern::Wimax();
714 if (type
== ::onc::network_type::kWireless
)
715 return NetworkTypePattern::Wireless();
717 return NetworkTypePattern::Default();
720 bool IsRecommendedValue(const base::DictionaryValue
* onc
,
721 const std::string
& property_key
) {
722 std::string property_basename
, recommended_property_key
;
723 size_t pos
= property_key
.find_last_of('.');
724 if (pos
!= std::string::npos
) {
725 // 'WiFi.AutoConnect' -> 'AutoConnect', 'WiFi.Recommended'
726 property_basename
= property_key
.substr(pos
+ 1);
727 recommended_property_key
=
728 property_key
.substr(0, pos
+ 1) + ::onc::kRecommended
;
730 // 'Name' -> 'Name', 'Recommended'
731 property_basename
= property_key
;
732 recommended_property_key
= ::onc::kRecommended
;
735 const base::ListValue
* recommended_keys
= nullptr;
736 return (onc
->GetList(recommended_property_key
, &recommended_keys
) &&
737 recommended_keys
->Find(base::StringValue(property_basename
)) !=
738 recommended_keys
->end());
742 } // namespace chromeos