Home | History | Annotate | Download | only in onc
      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.
      4 
      5 #include "chromeos/network/onc/onc_utils.h"
      6 
      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_util.h"
     12 #include "base/values.h"
     13 #include "chromeos/network/network_event_log.h"
     14 #include "chromeos/network/onc/onc_mapper.h"
     15 #include "chromeos/network/onc/onc_signature.h"
     16 #include "chromeos/network/onc/onc_utils.h"
     17 #include "chromeos/network/onc/onc_validator.h"
     18 #include "crypto/encryptor.h"
     19 #include "crypto/hmac.h"
     20 #include "crypto/symmetric_key.h"
     21 #include "net/cert/pem_tokenizer.h"
     22 #include "net/cert/x509_certificate.h"
     23 
     24 #define ONC_LOG_WARNING(message) NET_LOG_WARNING("ONC", message)
     25 #define ONC_LOG_ERROR(message) NET_LOG_ERROR("ONC", message)
     26 
     27 using namespace ::onc;
     28 
     29 namespace chromeos {
     30 namespace onc {
     31 
     32 namespace {
     33 
     34 const char kUnableToDecrypt[] = "Unable to decrypt encrypted ONC";
     35 const char kUnableToDecode[] = "Unable to decode encrypted ONC";
     36 
     37 }  // namespace
     38 
     39 const char kEmptyUnencryptedConfiguration[] =
     40     "{\"Type\":\"UnencryptedConfiguration\",\"NetworkConfigurations\":[],"
     41     "\"Certificates\":[]}";
     42 
     43 scoped_ptr<base::DictionaryValue> ReadDictionaryFromJson(
     44     const std::string& json) {
     45   std::string error;
     46   base::Value* root = base::JSONReader::ReadAndReturnError(
     47       json, base::JSON_ALLOW_TRAILING_COMMAS, NULL, &error);
     48 
     49   base::DictionaryValue* dict_ptr = NULL;
     50   if (!root || !root->GetAsDictionary(&dict_ptr)) {
     51     ONC_LOG_ERROR("Invalid JSON Dictionary: " + error);
     52     delete root;
     53   }
     54 
     55   return make_scoped_ptr(dict_ptr);
     56 }
     57 
     58 scoped_ptr<base::DictionaryValue> Decrypt(const std::string& passphrase,
     59                                           const base::DictionaryValue& root) {
     60   const int kKeySizeInBits = 256;
     61   const int kMaxIterationCount = 500000;
     62   std::string onc_type;
     63   std::string initial_vector;
     64   std::string salt;
     65   std::string cipher;
     66   std::string stretch_method;
     67   std::string hmac_method;
     68   std::string hmac;
     69   int iterations;
     70   std::string ciphertext;
     71 
     72   if (!root.GetString(encrypted::kCiphertext, &ciphertext) ||
     73       !root.GetString(encrypted::kCipher, &cipher) ||
     74       !root.GetString(encrypted::kHMAC, &hmac) ||
     75       !root.GetString(encrypted::kHMACMethod, &hmac_method) ||
     76       !root.GetString(encrypted::kIV, &initial_vector) ||
     77       !root.GetInteger(encrypted::kIterations, &iterations) ||
     78       !root.GetString(encrypted::kSalt, &salt) ||
     79       !root.GetString(encrypted::kStretch, &stretch_method) ||
     80       !root.GetString(toplevel_config::kType, &onc_type) ||
     81       onc_type != toplevel_config::kEncryptedConfiguration) {
     82 
     83     ONC_LOG_ERROR("Encrypted ONC malformed.");
     84     return scoped_ptr<base::DictionaryValue>();
     85   }
     86 
     87   if (hmac_method != encrypted::kSHA1 ||
     88       cipher != encrypted::kAES256 ||
     89       stretch_method != encrypted::kPBKDF2) {
     90     ONC_LOG_ERROR("Encrypted ONC unsupported encryption scheme.");
     91     return scoped_ptr<base::DictionaryValue>();
     92   }
     93 
     94   // Make sure iterations != 0, since that's not valid.
     95   if (iterations == 0) {
     96     ONC_LOG_ERROR(kUnableToDecrypt);
     97     return scoped_ptr<base::DictionaryValue>();
     98   }
     99 
    100   // Simply a sanity check to make sure we can't lock up the machine
    101   // for too long with a huge number (or a negative number).
    102   if (iterations < 0 || iterations > kMaxIterationCount) {
    103     ONC_LOG_ERROR("Too many iterations in encrypted ONC");
    104     return scoped_ptr<base::DictionaryValue>();
    105   }
    106 
    107   if (!base::Base64Decode(salt, &salt)) {
    108     ONC_LOG_ERROR(kUnableToDecode);
    109     return scoped_ptr<base::DictionaryValue>();
    110   }
    111 
    112   scoped_ptr<crypto::SymmetricKey> key(
    113       crypto::SymmetricKey::DeriveKeyFromPassword(crypto::SymmetricKey::AES,
    114                                                   passphrase,
    115                                                   salt,
    116                                                   iterations,
    117                                                   kKeySizeInBits));
    118 
    119   if (!base::Base64Decode(initial_vector, &initial_vector)) {
    120     ONC_LOG_ERROR(kUnableToDecode);
    121     return scoped_ptr<base::DictionaryValue>();
    122   }
    123   if (!base::Base64Decode(ciphertext, &ciphertext)) {
    124     ONC_LOG_ERROR(kUnableToDecode);
    125     return scoped_ptr<base::DictionaryValue>();
    126   }
    127   if (!base::Base64Decode(hmac, &hmac)) {
    128     ONC_LOG_ERROR(kUnableToDecode);
    129     return scoped_ptr<base::DictionaryValue>();
    130   }
    131 
    132   crypto::HMAC hmac_verifier(crypto::HMAC::SHA1);
    133   if (!hmac_verifier.Init(key.get()) ||
    134       !hmac_verifier.Verify(ciphertext, hmac)) {
    135     ONC_LOG_ERROR(kUnableToDecrypt);
    136     return scoped_ptr<base::DictionaryValue>();
    137   }
    138 
    139   crypto::Encryptor decryptor;
    140   if (!decryptor.Init(key.get(), crypto::Encryptor::CBC, initial_vector))  {
    141     ONC_LOG_ERROR(kUnableToDecrypt);
    142     return scoped_ptr<base::DictionaryValue>();
    143   }
    144 
    145   std::string plaintext;
    146   if (!decryptor.Decrypt(ciphertext, &plaintext)) {
    147     ONC_LOG_ERROR(kUnableToDecrypt);
    148     return scoped_ptr<base::DictionaryValue>();
    149   }
    150 
    151   scoped_ptr<base::DictionaryValue> new_root =
    152       ReadDictionaryFromJson(plaintext);
    153   if (new_root.get() == NULL) {
    154     ONC_LOG_ERROR("Property dictionary malformed.");
    155     return scoped_ptr<base::DictionaryValue>();
    156   }
    157 
    158   return new_root.Pass();
    159 }
    160 
    161 std::string GetSourceAsString(ONCSource source) {
    162   switch (source) {
    163     case ONC_SOURCE_UNKNOWN:
    164       return "unknown";
    165     case ONC_SOURCE_NONE:
    166       return "none";
    167     case ONC_SOURCE_DEVICE_POLICY:
    168       return "device policy";
    169     case ONC_SOURCE_USER_POLICY:
    170       return "user policy";
    171     case ONC_SOURCE_USER_IMPORT:
    172       return "user import";
    173   }
    174   NOTREACHED() << "unknown ONC source " << source;
    175   return "unknown";
    176 }
    177 
    178 void ExpandField(const std::string& fieldname,
    179                  const StringSubstitution& substitution,
    180                  base::DictionaryValue* onc_object) {
    181   std::string user_string;
    182   if (!onc_object->GetStringWithoutPathExpansion(fieldname, &user_string))
    183     return;
    184 
    185   std::string login_id;
    186   if (substitution.GetSubstitute(substitutes::kLoginIDField, &login_id)) {
    187     ReplaceSubstringsAfterOffset(&user_string, 0,
    188                                  substitutes::kLoginIDField,
    189                                  login_id);
    190   }
    191 
    192   std::string email;
    193   if (substitution.GetSubstitute(substitutes::kEmailField, &email)) {
    194     ReplaceSubstringsAfterOffset(&user_string, 0,
    195                                  substitutes::kEmailField,
    196                                  email);
    197   }
    198 
    199   onc_object->SetStringWithoutPathExpansion(fieldname, user_string);
    200 }
    201 
    202 void ExpandStringsInOncObject(
    203     const OncValueSignature& signature,
    204     const StringSubstitution& substitution,
    205     base::DictionaryValue* onc_object) {
    206   if (&signature == &kEAPSignature) {
    207     ExpandField(eap::kAnonymousIdentity, substitution, onc_object);
    208     ExpandField(eap::kIdentity, substitution, onc_object);
    209   } else if (&signature == &kL2TPSignature ||
    210              &signature == &kOpenVPNSignature) {
    211     ExpandField(vpn::kUsername, substitution, onc_object);
    212   }
    213 
    214   // Recurse into nested objects.
    215   for (base::DictionaryValue::Iterator it(*onc_object); !it.IsAtEnd();
    216        it.Advance()) {
    217     base::DictionaryValue* inner_object = NULL;
    218     if (!onc_object->GetDictionaryWithoutPathExpansion(it.key(), &inner_object))
    219       continue;
    220 
    221     const OncFieldSignature* field_signature =
    222         GetFieldSignature(signature, it.key());
    223     if (!field_signature)
    224       continue;
    225 
    226     ExpandStringsInOncObject(*field_signature->value_signature,
    227                              substitution, inner_object);
    228   }
    229 }
    230 
    231 void ExpandStringsInNetworks(const StringSubstitution& substitution,
    232                              base::ListValue* network_configs) {
    233   for (base::ListValue::iterator it = network_configs->begin();
    234        it != network_configs->end(); ++it) {
    235     base::DictionaryValue* network = NULL;
    236     (*it)->GetAsDictionary(&network);
    237     DCHECK(network);
    238     ExpandStringsInOncObject(
    239         kNetworkConfigurationSignature, substitution, network);
    240   }
    241 }
    242 
    243 namespace {
    244 
    245 class OncMaskValues : public Mapper {
    246  public:
    247   static scoped_ptr<base::DictionaryValue> Mask(
    248       const OncValueSignature& signature,
    249       const base::DictionaryValue& onc_object,
    250       const std::string& mask) {
    251     OncMaskValues masker(mask);
    252     bool unused_error;
    253     return masker.MapObject(signature, onc_object, &unused_error);
    254   }
    255 
    256  protected:
    257   explicit OncMaskValues(const std::string& mask)
    258       : mask_(mask) {
    259   }
    260 
    261   virtual scoped_ptr<base::Value> MapField(
    262       const std::string& field_name,
    263       const OncValueSignature& object_signature,
    264       const base::Value& onc_value,
    265       bool* found_unknown_field,
    266       bool* error) OVERRIDE {
    267     if (FieldIsCredential(object_signature, field_name)) {
    268       return scoped_ptr<base::Value>(new base::StringValue(mask_));
    269     } else {
    270       return Mapper::MapField(field_name, object_signature, onc_value,
    271                               found_unknown_field, error);
    272     }
    273   }
    274 
    275   // Mask to insert in place of the sensitive values.
    276   std::string mask_;
    277 };
    278 
    279 }  // namespace
    280 
    281 scoped_ptr<base::DictionaryValue> MaskCredentialsInOncObject(
    282     const OncValueSignature& signature,
    283     const base::DictionaryValue& onc_object,
    284     const std::string& mask) {
    285   return OncMaskValues::Mask(signature, onc_object, mask);
    286 }
    287 
    288 namespace {
    289 
    290 std::string DecodePEM(const std::string& pem_encoded) {
    291   // The PEM block header used for DER certificates
    292   const char kCertificateHeader[] = "CERTIFICATE";
    293 
    294   // This is an older PEM marker for DER certificates.
    295   const char kX509CertificateHeader[] = "X509 CERTIFICATE";
    296 
    297   std::vector<std::string> pem_headers;
    298   pem_headers.push_back(kCertificateHeader);
    299   pem_headers.push_back(kX509CertificateHeader);
    300 
    301   net::PEMTokenizer pem_tokenizer(pem_encoded, pem_headers);
    302   std::string decoded;
    303   if (pem_tokenizer.GetNext()) {
    304     decoded = pem_tokenizer.data();
    305   } else {
    306     // If we failed to read the data as a PEM file, then try plain base64 decode
    307     // in case the PEM marker strings are missing. For this to work, there has
    308     // to be no white space, and it has to only contain the base64-encoded data.
    309     if (!base::Base64Decode(pem_encoded, &decoded)) {
    310       LOG(ERROR) << "Unable to base64 decode X509 data: " << pem_encoded;
    311       return std::string();
    312     }
    313   }
    314   return decoded;
    315 }
    316 
    317 CertPEMsByGUIDMap GetServerAndCACertsByGUID(
    318     const base::ListValue& certificates) {
    319   CertPEMsByGUIDMap certs_by_guid;
    320   for (base::ListValue::const_iterator it = certificates.begin();
    321       it != certificates.end(); ++it) {
    322     base::DictionaryValue* cert = NULL;
    323     (*it)->GetAsDictionary(&cert);
    324 
    325     std::string guid;
    326     cert->GetStringWithoutPathExpansion(certificate::kGUID, &guid);
    327     std::string cert_type;
    328     cert->GetStringWithoutPathExpansion(certificate::kType, &cert_type);
    329     if (cert_type != certificate::kServer &&
    330         cert_type != certificate::kAuthority) {
    331       continue;
    332     }
    333     std::string x509_data;
    334     cert->GetStringWithoutPathExpansion(certificate::kX509, &x509_data);
    335 
    336     std::string der = DecodePEM(x509_data);
    337     std::string pem;
    338     if (der.empty() || !net::X509Certificate::GetPEMEncodedFromDER(der, &pem)) {
    339       LOG(ERROR) << "Certificate with GUID " << guid
    340                  << " is not in PEM encoding.";
    341       continue;
    342     }
    343     certs_by_guid[guid] = pem;
    344   }
    345 
    346   return certs_by_guid;
    347 }
    348 
    349 }  // namespace
    350 
    351 bool ParseAndValidateOncForImport(const std::string& onc_blob,
    352                                   ONCSource onc_source,
    353                                   const std::string& passphrase,
    354                                   base::ListValue* network_configs,
    355                                   base::DictionaryValue* global_network_config,
    356                                   base::ListValue* certificates) {
    357   network_configs->Clear();
    358   global_network_config->Clear();
    359   certificates->Clear();
    360   if (onc_blob.empty())
    361     return true;
    362 
    363   scoped_ptr<base::DictionaryValue> toplevel_onc =
    364       ReadDictionaryFromJson(onc_blob);
    365   if (toplevel_onc.get() == NULL) {
    366     LOG(ERROR) << "ONC loaded from " << GetSourceAsString(onc_source)
    367                << " is not a valid JSON dictionary.";
    368     return false;
    369   }
    370 
    371   // Check and see if this is an encrypted ONC file. If so, decrypt it.
    372   std::string onc_type;
    373   toplevel_onc->GetStringWithoutPathExpansion(toplevel_config::kType,
    374                                               &onc_type);
    375   if (onc_type == toplevel_config::kEncryptedConfiguration) {
    376     toplevel_onc = Decrypt(passphrase, *toplevel_onc);
    377     if (toplevel_onc.get() == NULL) {
    378       LOG(ERROR) << "Couldn't decrypt the ONC from "
    379                  << GetSourceAsString(onc_source);
    380       return false;
    381     }
    382   }
    383 
    384   bool from_policy = (onc_source == ONC_SOURCE_USER_POLICY ||
    385                       onc_source == ONC_SOURCE_DEVICE_POLICY);
    386 
    387   // Validate the ONC dictionary. We are liberal and ignore unknown field
    388   // names and ignore invalid field names in kRecommended arrays.
    389   Validator validator(false,  // Ignore unknown fields.
    390                       false,  // Ignore invalid recommended field names.
    391                       true,   // Fail on missing fields.
    392                       from_policy);
    393   validator.SetOncSource(onc_source);
    394 
    395   Validator::Result validation_result;
    396   toplevel_onc = validator.ValidateAndRepairObject(
    397       &kToplevelConfigurationSignature,
    398       *toplevel_onc,
    399       &validation_result);
    400 
    401   if (from_policy) {
    402     UMA_HISTOGRAM_BOOLEAN("Enterprise.ONC.PolicyValidation",
    403                           validation_result == Validator::VALID);
    404   }
    405 
    406   bool success = true;
    407   if (validation_result == Validator::VALID_WITH_WARNINGS) {
    408     LOG(WARNING) << "ONC from " << GetSourceAsString(onc_source)
    409                  << " produced warnings.";
    410     success = false;
    411   } else if (validation_result == Validator::INVALID || toplevel_onc == NULL) {
    412     LOG(ERROR) << "ONC from " << GetSourceAsString(onc_source)
    413                << " is invalid and couldn't be repaired.";
    414     return false;
    415   }
    416 
    417   base::ListValue* validated_certs = NULL;
    418   if (toplevel_onc->GetListWithoutPathExpansion(toplevel_config::kCertificates,
    419                                                 &validated_certs)) {
    420     certificates->Swap(validated_certs);
    421   }
    422 
    423   base::ListValue* validated_networks = NULL;
    424   if (toplevel_onc->GetListWithoutPathExpansion(
    425           toplevel_config::kNetworkConfigurations, &validated_networks)) {
    426     CertPEMsByGUIDMap server_and_ca_certs =
    427         GetServerAndCACertsByGUID(*certificates);
    428 
    429     if (!ResolveServerCertRefsInNetworks(server_and_ca_certs,
    430                                          validated_networks)) {
    431       LOG(ERROR) << "Some certificate references in the ONC policy for source "
    432                  << GetSourceAsString(onc_source) << " could not be resolved.";
    433       success = false;
    434     }
    435 
    436     network_configs->Swap(validated_networks);
    437   }
    438 
    439   base::DictionaryValue* validated_global_config = NULL;
    440   if (toplevel_onc->GetDictionaryWithoutPathExpansion(
    441           toplevel_config::kGlobalNetworkConfiguration,
    442           &validated_global_config)) {
    443     global_network_config->Swap(validated_global_config);
    444   }
    445 
    446   return success;
    447 }
    448 
    449 scoped_refptr<net::X509Certificate> DecodePEMCertificate(
    450     const std::string& pem_encoded) {
    451   std::string decoded = DecodePEM(pem_encoded);
    452   scoped_refptr<net::X509Certificate> cert =
    453       net::X509Certificate::CreateFromBytes(decoded.data(), decoded.size());
    454   LOG_IF(ERROR, !cert.get()) << "Couldn't create certificate from X509 data: "
    455                              << decoded;
    456   return cert;
    457 }
    458 
    459 namespace {
    460 
    461 bool GUIDRefToPEMEncoding(const CertPEMsByGUIDMap& certs_by_guid,
    462                           const std::string& guid_ref,
    463                           std::string* pem_encoded) {
    464   CertPEMsByGUIDMap::const_iterator it = certs_by_guid.find(guid_ref);
    465   if (it == certs_by_guid.end()) {
    466     LOG(ERROR) << "Couldn't resolve certificate reference " << guid_ref;
    467     return false;
    468   }
    469   *pem_encoded = it->second;
    470   if (pem_encoded->empty()) {
    471     LOG(ERROR) << "Couldn't PEM-encode certificate with GUID " << guid_ref;
    472     return false;
    473   }
    474   return true;
    475 }
    476 
    477 bool ResolveSingleCertRef(const CertPEMsByGUIDMap& certs_by_guid,
    478                           const std::string& key_guid_ref,
    479                           const std::string& key_pem,
    480                           base::DictionaryValue* onc_object) {
    481   std::string guid_ref;
    482   if (!onc_object->GetStringWithoutPathExpansion(key_guid_ref, &guid_ref))
    483     return true;
    484 
    485   std::string pem_encoded;
    486   if (!GUIDRefToPEMEncoding(certs_by_guid, guid_ref, &pem_encoded))
    487     return false;
    488 
    489   onc_object->RemoveWithoutPathExpansion(key_guid_ref, NULL);
    490   onc_object->SetStringWithoutPathExpansion(key_pem, pem_encoded);
    491   return true;
    492 }
    493 
    494 bool ResolveCertRefList(const CertPEMsByGUIDMap& certs_by_guid,
    495                         const std::string& key_guid_ref_list,
    496                         const std::string& key_pem_list,
    497                         base::DictionaryValue* onc_object) {
    498   const base::ListValue* guid_ref_list = NULL;
    499   if (!onc_object->GetListWithoutPathExpansion(key_guid_ref_list,
    500                                                &guid_ref_list)) {
    501     return true;
    502   }
    503 
    504   scoped_ptr<base::ListValue> pem_list(new base::ListValue);
    505   for (base::ListValue::const_iterator it = guid_ref_list->begin();
    506        it != guid_ref_list->end(); ++it) {
    507     std::string guid_ref;
    508     (*it)->GetAsString(&guid_ref);
    509 
    510     std::string pem_encoded;
    511     if (!GUIDRefToPEMEncoding(certs_by_guid, guid_ref, &pem_encoded))
    512       return false;
    513 
    514     pem_list->AppendString(pem_encoded);
    515   }
    516 
    517   onc_object->RemoveWithoutPathExpansion(key_guid_ref_list, NULL);
    518   onc_object->SetWithoutPathExpansion(key_pem_list, pem_list.release());
    519   return true;
    520 }
    521 
    522 bool ResolveSingleCertRefToList(const CertPEMsByGUIDMap& certs_by_guid,
    523                                 const std::string& key_guid_ref,
    524                                 const std::string& key_pem_list,
    525                                 base::DictionaryValue* onc_object) {
    526   std::string guid_ref;
    527   if (!onc_object->GetStringWithoutPathExpansion(key_guid_ref, &guid_ref))
    528     return true;
    529 
    530   std::string pem_encoded;
    531   if (!GUIDRefToPEMEncoding(certs_by_guid, guid_ref, &pem_encoded))
    532     return false;
    533 
    534   scoped_ptr<base::ListValue> pem_list(new base::ListValue);
    535   pem_list->AppendString(pem_encoded);
    536   onc_object->RemoveWithoutPathExpansion(key_guid_ref, NULL);
    537   onc_object->SetWithoutPathExpansion(key_pem_list, pem_list.release());
    538   return true;
    539 }
    540 
    541 // Resolves the reference list at |key_guid_refs| if present and otherwise the
    542 // single reference at |key_guid_ref|. Returns whether the respective resolving
    543 // was successful.
    544 bool ResolveCertRefsOrRefToList(const CertPEMsByGUIDMap& certs_by_guid,
    545                                 const std::string& key_guid_refs,
    546                                 const std::string& key_guid_ref,
    547                                 const std::string& key_pem_list,
    548                                 base::DictionaryValue* onc_object) {
    549   if (onc_object->HasKey(key_guid_refs)) {
    550     if (onc_object->HasKey(key_guid_ref)) {
    551       LOG(ERROR) << "Found both " << key_guid_refs << " and " << key_guid_ref
    552                  << ". Ignoring and removing the latter.";
    553       onc_object->RemoveWithoutPathExpansion(key_guid_ref, NULL);
    554     }
    555     return ResolveCertRefList(
    556         certs_by_guid, key_guid_refs, key_pem_list, onc_object);
    557   }
    558 
    559   // Only resolve |key_guid_ref| if |key_guid_refs| isn't present.
    560   return ResolveSingleCertRefToList(
    561       certs_by_guid, key_guid_ref, key_pem_list, onc_object);
    562 }
    563 
    564 bool ResolveServerCertRefsInObject(const CertPEMsByGUIDMap& certs_by_guid,
    565                                    const OncValueSignature& signature,
    566                                    base::DictionaryValue* onc_object) {
    567   if (&signature == &kCertificatePatternSignature) {
    568     if (!ResolveCertRefList(certs_by_guid,
    569                             client_cert::kIssuerCARef,
    570                             client_cert::kIssuerCAPEMs,
    571                             onc_object)) {
    572       return false;
    573     }
    574   } else if (&signature == &kEAPSignature) {
    575     if (!ResolveCertRefsOrRefToList(certs_by_guid,
    576                                     eap::kServerCARefs,
    577                                     eap::kServerCARef,
    578                                     eap::kServerCAPEMs,
    579                                     onc_object)) {
    580       return false;
    581     }
    582   } else if (&signature == &kIPsecSignature) {
    583     if (!ResolveCertRefsOrRefToList(certs_by_guid,
    584                                     ipsec::kServerCARefs,
    585                                     ipsec::kServerCARef,
    586                                     ipsec::kServerCAPEMs,
    587                                     onc_object)) {
    588       return false;
    589     }
    590   } else if (&signature == &kIPsecSignature ||
    591              &signature == &kOpenVPNSignature) {
    592     if (!ResolveSingleCertRef(certs_by_guid,
    593                               openvpn::kServerCertRef,
    594                               openvpn::kServerCertPEM,
    595                               onc_object) ||
    596         !ResolveCertRefsOrRefToList(certs_by_guid,
    597                                     openvpn::kServerCARefs,
    598                                     openvpn::kServerCARef,
    599                                     openvpn::kServerCAPEMs,
    600                                     onc_object)) {
    601       return false;
    602     }
    603   }
    604 
    605   // Recurse into nested objects.
    606   for (base::DictionaryValue::Iterator it(*onc_object); !it.IsAtEnd();
    607        it.Advance()) {
    608     base::DictionaryValue* inner_object = NULL;
    609     if (!onc_object->GetDictionaryWithoutPathExpansion(it.key(), &inner_object))
    610       continue;
    611 
    612     const OncFieldSignature* field_signature =
    613         GetFieldSignature(signature, it.key());
    614     if (!field_signature)
    615       continue;
    616 
    617     if (!ResolveServerCertRefsInObject(certs_by_guid,
    618                                        *field_signature->value_signature,
    619                                        inner_object)) {
    620       return false;
    621     }
    622   }
    623   return true;
    624 }
    625 
    626 }  // namespace
    627 
    628 bool ResolveServerCertRefsInNetworks(const CertPEMsByGUIDMap& certs_by_guid,
    629                                      base::ListValue* network_configs) {
    630   bool success = true;
    631   for (base::ListValue::iterator it = network_configs->begin();
    632        it != network_configs->end(); ) {
    633     base::DictionaryValue* network = NULL;
    634     (*it)->GetAsDictionary(&network);
    635     if (!ResolveServerCertRefsInNetwork(certs_by_guid, network)) {
    636       std::string guid;
    637       network->GetStringWithoutPathExpansion(network_config::kGUID, &guid);
    638       // This might happen even with correct validation, if the referenced
    639       // certificate couldn't be imported.
    640       LOG(ERROR) << "Couldn't resolve some certificate reference of network "
    641                  << guid;
    642       it = network_configs->Erase(it, NULL);
    643       success = false;
    644       continue;
    645     }
    646     ++it;
    647   }
    648   return success;
    649 }
    650 
    651 bool ResolveServerCertRefsInNetwork(const CertPEMsByGUIDMap& certs_by_guid,
    652                                     base::DictionaryValue* network_config) {
    653   return ResolveServerCertRefsInObject(certs_by_guid,
    654                                        kNetworkConfigurationSignature,
    655                                        network_config);
    656 }
    657 
    658 NetworkTypePattern NetworkTypePatternFromOncType(const std::string& type) {
    659   if (type == ::onc::network_type::kAllTypes)
    660     return NetworkTypePattern::Default();
    661   if (type == ::onc::network_type::kCellular)
    662     return NetworkTypePattern::Cellular();
    663   if (type == ::onc::network_type::kEthernet)
    664     return NetworkTypePattern::Ethernet();
    665   if (type == ::onc::network_type::kVPN)
    666     return NetworkTypePattern::VPN();
    667   if (type == ::onc::network_type::kWiFi)
    668     return NetworkTypePattern::WiFi();
    669   if (type == ::onc::network_type::kWimax)
    670     return NetworkTypePattern::Wimax();
    671   if (type == ::onc::network_type::kWireless)
    672     return NetworkTypePattern::Wireless();
    673   NOTREACHED();
    674   return NetworkTypePattern::Default();
    675 }
    676 
    677 bool IsRecommendedValue(const base::DictionaryValue* onc,
    678                         const std::string& property_key) {
    679   std::string property_basename, recommended_property_key;
    680   size_t pos = property_key.find_last_of('.');
    681   if (pos != std::string::npos) {
    682     // 'WiFi.AutoConnect' -> 'AutoConnect', 'WiFi.Recommended'
    683     property_basename = property_key.substr(pos + 1);
    684     recommended_property_key =
    685         property_key.substr(0, pos + 1) + ::onc::kRecommended;
    686   } else {
    687     // 'Name' -> 'Name', 'Recommended'
    688     property_basename = property_key;
    689     recommended_property_key = ::onc::kRecommended;
    690   }
    691 
    692   const base::ListValue* recommended_keys = NULL;
    693   return (onc->GetList(recommended_property_key, &recommended_keys) &&
    694           recommended_keys->Find(base::StringValue(property_basename)) !=
    695           recommended_keys->end());
    696 }
    697 
    698 }  // namespace onc
    699 }  // namespace chromeos
    700