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_DEVICE_POLICY:
    164       return "device policy";
    165     case ONC_SOURCE_USER_POLICY:
    166       return "user policy";
    167     case ONC_SOURCE_NONE:
    168       return "none";
    169     case ONC_SOURCE_USER_IMPORT:
    170       return "user import";
    171   }
    172   NOTREACHED() << "unknown ONC source " << source;
    173   return "unknown";
    174 }
    175 
    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))
    181     return;
    182 
    183   std::string login_id;
    184   if (substitution.GetSubstitute(substitutes::kLoginIDField, &login_id)) {
    185     ReplaceSubstringsAfterOffset(&user_string, 0,
    186                                  substitutes::kLoginIDField,
    187                                  login_id);
    188   }
    189 
    190   std::string email;
    191   if (substitution.GetSubstitute(substitutes::kEmailField, &email)) {
    192     ReplaceSubstringsAfterOffset(&user_string, 0,
    193                                  substitutes::kEmailField,
    194                                  email);
    195   }
    196 
    197   onc_object->SetStringWithoutPathExpansion(fieldname, user_string);
    198 }
    199 
    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);
    210   }
    211 
    212   // Recurse into nested objects.
    213   for (base::DictionaryValue::Iterator it(*onc_object); !it.IsAtEnd();
    214        it.Advance()) {
    215     base::DictionaryValue* inner_object = NULL;
    216     if (!onc_object->GetDictionaryWithoutPathExpansion(it.key(), &inner_object))
    217       continue;
    218 
    219     const OncFieldSignature* field_signature =
    220         GetFieldSignature(signature, it.key());
    221     if (!field_signature)
    222       continue;
    223 
    224     ExpandStringsInOncObject(*field_signature->value_signature,
    225                              substitution, inner_object);
    226   }
    227 }
    228 
    229 void ExpandStringsInNetworks(const StringSubstitution& substitution,
    230                              base::ListValue* network_configs) {
    231   for (base::ListValue::iterator it = network_configs->begin();
    232        it != network_configs->end(); ++it) {
    233     base::DictionaryValue* network = NULL;
    234     (*it)->GetAsDictionary(&network);
    235     DCHECK(network);
    236     ExpandStringsInOncObject(
    237         kNetworkConfigurationSignature, substitution, network);
    238   }
    239 }
    240 
    241 namespace {
    242 
    243 class OncMaskValues : public Mapper {
    244  public:
    245   static scoped_ptr<base::DictionaryValue> Mask(
    246       const OncValueSignature& signature,
    247       const base::DictionaryValue& onc_object,
    248       const std::string& mask) {
    249     OncMaskValues masker(mask);
    250     bool unused_error;
    251     return masker.MapObject(signature, onc_object, &unused_error);
    252   }
    253 
    254  protected:
    255   explicit OncMaskValues(const std::string& mask)
    256       : mask_(mask) {
    257   }
    258 
    259   virtual scoped_ptr<base::Value> MapField(
    260       const std::string& field_name,
    261       const OncValueSignature& object_signature,
    262       const base::Value& onc_value,
    263       bool* found_unknown_field,
    264       bool* error) OVERRIDE {
    265     if (FieldIsCredential(object_signature, field_name)) {
    266       return scoped_ptr<base::Value>(new base::StringValue(mask_));
    267     } else {
    268       return Mapper::MapField(field_name, object_signature, onc_value,
    269                               found_unknown_field, error);
    270     }
    271   }
    272 
    273   // Mask to insert in place of the sensitive values.
    274   std::string mask_;
    275 };
    276 
    277 }  // namespace
    278 
    279 scoped_ptr<base::DictionaryValue> MaskCredentialsInOncObject(
    280     const OncValueSignature& signature,
    281     const base::DictionaryValue& onc_object,
    282     const std::string& mask) {
    283   return OncMaskValues::Mask(signature, onc_object, mask);
    284 }
    285 
    286 namespace {
    287 
    288 std::string DecodePEM(const std::string& pem_encoded) {
    289   // The PEM block header used for DER certificates
    290   const char kCertificateHeader[] = "CERTIFICATE";
    291 
    292   // This is an older PEM marker for DER certificates.
    293   const char kX509CertificateHeader[] = "X509 CERTIFICATE";
    294 
    295   std::vector<std::string> pem_headers;
    296   pem_headers.push_back(kCertificateHeader);
    297   pem_headers.push_back(kX509CertificateHeader);
    298 
    299   net::PEMTokenizer pem_tokenizer(pem_encoded, pem_headers);
    300   std::string decoded;
    301   if (pem_tokenizer.GetNext()) {
    302     decoded = pem_tokenizer.data();
    303   } else {
    304     // If we failed to read the data as a PEM file, then try plain base64 decode
    305     // in case the PEM marker strings are missing. For this to work, there has
    306     // to be no white space, and it has to only contain the base64-encoded data.
    307     if (!base::Base64Decode(pem_encoded, &decoded)) {
    308       LOG(ERROR) << "Unable to base64 decode X509 data: " << pem_encoded;
    309       return std::string();
    310     }
    311   }
    312   return decoded;
    313 }
    314 
    315 CertPEMsByGUIDMap GetServerAndCACertsByGUID(
    316     const base::ListValue& certificates) {
    317   CertPEMsByGUIDMap certs_by_guid;
    318   for (base::ListValue::const_iterator it = certificates.begin();
    319       it != certificates.end(); ++it) {
    320     base::DictionaryValue* cert = NULL;
    321     (*it)->GetAsDictionary(&cert);
    322 
    323     std::string guid;
    324     cert->GetStringWithoutPathExpansion(certificate::kGUID, &guid);
    325     std::string cert_type;
    326     cert->GetStringWithoutPathExpansion(certificate::kType, &cert_type);
    327     if (cert_type != certificate::kServer &&
    328         cert_type != certificate::kAuthority) {
    329       continue;
    330     }
    331     std::string x509_data;
    332     cert->GetStringWithoutPathExpansion(certificate::kX509, &x509_data);
    333 
    334     std::string der = DecodePEM(x509_data);
    335     std::string pem;
    336     if (der.empty() || !net::X509Certificate::GetPEMEncodedFromDER(der, &pem)) {
    337       LOG(ERROR) << "Certificate with GUID " << guid
    338                  << " is not in PEM encoding.";
    339       continue;
    340     }
    341     certs_by_guid[guid] = pem;
    342   }
    343 
    344   return certs_by_guid;
    345 }
    346 
    347 }  // namespace
    348 
    349 bool ParseAndValidateOncForImport(const std::string& onc_blob,
    350                                   ONCSource onc_source,
    351                                   const std::string& passphrase,
    352                                   base::ListValue* network_configs,
    353                                   base::DictionaryValue* global_network_config,
    354                                   base::ListValue* certificates) {
    355   network_configs->Clear();
    356   global_network_config->Clear();
    357   certificates->Clear();
    358   if (onc_blob.empty())
    359     return true;
    360 
    361   scoped_ptr<base::DictionaryValue> toplevel_onc =
    362       ReadDictionaryFromJson(onc_blob);
    363   if (toplevel_onc.get() == NULL) {
    364     LOG(ERROR) << "ONC loaded from " << GetSourceAsString(onc_source)
    365                << " is not a valid JSON dictionary.";
    366     return false;
    367   }
    368 
    369   // Check and see if this is an encrypted ONC file. If so, decrypt it.
    370   std::string onc_type;
    371   toplevel_onc->GetStringWithoutPathExpansion(toplevel_config::kType,
    372                                               &onc_type);
    373   if (onc_type == toplevel_config::kEncryptedConfiguration) {
    374     toplevel_onc = Decrypt(passphrase, *toplevel_onc);
    375     if (toplevel_onc.get() == NULL) {
    376       LOG(ERROR) << "Couldn't decrypt the ONC from "
    377                  << GetSourceAsString(onc_source);
    378       return false;
    379     }
    380   }
    381 
    382   bool from_policy = (onc_source == ONC_SOURCE_USER_POLICY ||
    383                       onc_source == ONC_SOURCE_DEVICE_POLICY);
    384 
    385   // Validate the ONC dictionary. We are liberal and ignore unknown field
    386   // names and ignore invalid field names in kRecommended arrays.
    387   Validator validator(false,  // Ignore unknown fields.
    388                       false,  // Ignore invalid recommended field names.
    389                       true,   // Fail on missing fields.
    390                       from_policy);
    391   validator.SetOncSource(onc_source);
    392 
    393   Validator::Result validation_result;
    394   toplevel_onc = validator.ValidateAndRepairObject(
    395       &kToplevelConfigurationSignature,
    396       *toplevel_onc,
    397       &validation_result);
    398 
    399   if (from_policy) {
    400     UMA_HISTOGRAM_BOOLEAN("Enterprise.ONC.PolicyValidation",
    401                           validation_result == Validator::VALID);
    402   }
    403 
    404   bool success = true;
    405   if (validation_result == Validator::VALID_WITH_WARNINGS) {
    406     LOG(WARNING) << "ONC from " << GetSourceAsString(onc_source)
    407                  << " produced warnings.";
    408     success = false;
    409   } else if (validation_result == Validator::INVALID || toplevel_onc == NULL) {
    410     LOG(ERROR) << "ONC from " << GetSourceAsString(onc_source)
    411                << " is invalid and couldn't be repaired.";
    412     return false;
    413   }
    414 
    415   base::ListValue* validated_certs = NULL;
    416   if (toplevel_onc->GetListWithoutPathExpansion(toplevel_config::kCertificates,
    417                                                 &validated_certs)) {
    418     certificates->Swap(validated_certs);
    419   }
    420 
    421   base::ListValue* validated_networks = NULL;
    422   if (toplevel_onc->GetListWithoutPathExpansion(
    423           toplevel_config::kNetworkConfigurations, &validated_networks)) {
    424     CertPEMsByGUIDMap server_and_ca_certs =
    425         GetServerAndCACertsByGUID(*certificates);
    426 
    427     if (!ResolveServerCertRefsInNetworks(server_and_ca_certs,
    428                                          validated_networks)) {
    429       LOG(ERROR) << "Some certificate references in the ONC policy for source "
    430                  << GetSourceAsString(onc_source) << " could not be resolved.";
    431       success = false;
    432     }
    433 
    434     network_configs->Swap(validated_networks);
    435   }
    436 
    437   base::DictionaryValue* validated_global_config = NULL;
    438   if (toplevel_onc->GetDictionaryWithoutPathExpansion(
    439           toplevel_config::kGlobalNetworkConfiguration,
    440           &validated_global_config)) {
    441     global_network_config->Swap(validated_global_config);
    442   }
    443 
    444   return success;
    445 }
    446 
    447 scoped_refptr<net::X509Certificate> DecodePEMCertificate(
    448     const std::string& pem_encoded) {
    449   std::string decoded = DecodePEM(pem_encoded);
    450   scoped_refptr<net::X509Certificate> cert =
    451       net::X509Certificate::CreateFromBytes(decoded.data(), decoded.size());
    452   LOG_IF(ERROR, !cert.get()) << "Couldn't create certificate from X509 data: "
    453                              << decoded;
    454   return cert;
    455 }
    456 
    457 namespace {
    458 
    459 bool GUIDRefToPEMEncoding(const CertPEMsByGUIDMap& certs_by_guid,
    460                           const std::string& guid_ref,
    461                           std::string* pem_encoded) {
    462   CertPEMsByGUIDMap::const_iterator it = certs_by_guid.find(guid_ref);
    463   if (it == certs_by_guid.end()) {
    464     LOG(ERROR) << "Couldn't resolve certificate reference " << guid_ref;
    465     return false;
    466   }
    467   *pem_encoded = it->second;
    468   if (pem_encoded->empty()) {
    469     LOG(ERROR) << "Couldn't PEM-encode certificate with GUID " << guid_ref;
    470     return false;
    471   }
    472   return true;
    473 }
    474 
    475 bool ResolveSingleCertRef(const CertPEMsByGUIDMap& certs_by_guid,
    476                           const std::string& key_guid_ref,
    477                           const std::string& key_pem,
    478                           base::DictionaryValue* onc_object) {
    479   std::string guid_ref;
    480   if (!onc_object->GetStringWithoutPathExpansion(key_guid_ref, &guid_ref))
    481     return true;
    482 
    483   std::string pem_encoded;
    484   if (!GUIDRefToPEMEncoding(certs_by_guid, guid_ref, &pem_encoded))
    485     return false;
    486 
    487   onc_object->RemoveWithoutPathExpansion(key_guid_ref, NULL);
    488   onc_object->SetStringWithoutPathExpansion(key_pem, pem_encoded);
    489   return true;
    490 }
    491 
    492 bool ResolveCertRefList(const CertPEMsByGUIDMap& certs_by_guid,
    493                         const std::string& key_guid_ref_list,
    494                         const std::string& key_pem_list,
    495                         base::DictionaryValue* onc_object) {
    496   const base::ListValue* guid_ref_list = NULL;
    497   if (!onc_object->GetListWithoutPathExpansion(key_guid_ref_list,
    498                                                &guid_ref_list)) {
    499     return true;
    500   }
    501 
    502   scoped_ptr<base::ListValue> pem_list(new base::ListValue);
    503   for (base::ListValue::const_iterator it = guid_ref_list->begin();
    504        it != guid_ref_list->end(); ++it) {
    505     std::string guid_ref;
    506     (*it)->GetAsString(&guid_ref);
    507 
    508     std::string pem_encoded;
    509     if (!GUIDRefToPEMEncoding(certs_by_guid, guid_ref, &pem_encoded))
    510       return false;
    511 
    512     pem_list->AppendString(pem_encoded);
    513   }
    514 
    515   onc_object->RemoveWithoutPathExpansion(key_guid_ref_list, NULL);
    516   onc_object->SetWithoutPathExpansion(key_pem_list, pem_list.release());
    517   return true;
    518 }
    519 
    520 bool ResolveSingleCertRefToList(const CertPEMsByGUIDMap& certs_by_guid,
    521                                 const std::string& key_guid_ref,
    522                                 const std::string& key_pem_list,
    523                                 base::DictionaryValue* onc_object) {
    524   std::string guid_ref;
    525   if (!onc_object->GetStringWithoutPathExpansion(key_guid_ref, &guid_ref))
    526     return true;
    527 
    528   std::string pem_encoded;
    529   if (!GUIDRefToPEMEncoding(certs_by_guid, guid_ref, &pem_encoded))
    530     return false;
    531 
    532   scoped_ptr<base::ListValue> pem_list(new base::ListValue);
    533   pem_list->AppendString(pem_encoded);
    534   onc_object->RemoveWithoutPathExpansion(key_guid_ref, NULL);
    535   onc_object->SetWithoutPathExpansion(key_pem_list, pem_list.release());
    536   return true;
    537 }
    538 
    539 // Resolves the reference list at |key_guid_refs| if present and otherwise the
    540 // single reference at |key_guid_ref|. Returns whether the respective resolving
    541 // was successful.
    542 bool ResolveCertRefsOrRefToList(const CertPEMsByGUIDMap& certs_by_guid,
    543                                 const std::string& key_guid_refs,
    544                                 const std::string& key_guid_ref,
    545                                 const std::string& key_pem_list,
    546                                 base::DictionaryValue* onc_object) {
    547   if (onc_object->HasKey(key_guid_refs)) {
    548     if (onc_object->HasKey(key_guid_ref)) {
    549       LOG(ERROR) << "Found both " << key_guid_refs << " and " << key_guid_ref
    550                  << ". Ignoring and removing the latter.";
    551       onc_object->RemoveWithoutPathExpansion(key_guid_ref, NULL);
    552     }
    553     return ResolveCertRefList(
    554         certs_by_guid, key_guid_refs, key_pem_list, onc_object);
    555   }
    556 
    557   // Only resolve |key_guid_ref| if |key_guid_refs| isn't present.
    558   return ResolveSingleCertRefToList(
    559       certs_by_guid, key_guid_ref, key_pem_list, onc_object);
    560 }
    561 
    562 bool ResolveServerCertRefsInObject(const CertPEMsByGUIDMap& certs_by_guid,
    563                                    const OncValueSignature& signature,
    564                                    base::DictionaryValue* onc_object) {
    565   if (&signature == &kCertificatePatternSignature) {
    566     if (!ResolveCertRefList(certs_by_guid, certificate::kIssuerCARef,
    567                             certificate::kIssuerCAPEMs, onc_object)) {
    568       return false;
    569     }
    570   } else if (&signature == &kEAPSignature) {
    571     if (!ResolveCertRefsOrRefToList(certs_by_guid,
    572                                     eap::kServerCARefs,
    573                                     eap::kServerCARef,
    574                                     eap::kServerCAPEMs,
    575                                     onc_object)) {
    576       return false;
    577     }
    578   } else if (&signature == &kIPsecSignature) {
    579     if (!ResolveCertRefsOrRefToList(certs_by_guid,
    580                                     ipsec::kServerCARefs,
    581                                     ipsec::kServerCARef,
    582                                     ipsec::kServerCAPEMs,
    583                                     onc_object)) {
    584       return false;
    585     }
    586   } else if (&signature == &kIPsecSignature ||
    587              &signature == &kOpenVPNSignature) {
    588     if (!ResolveSingleCertRef(certs_by_guid,
    589                               openvpn::kServerCertRef,
    590                               openvpn::kServerCertPEM,
    591                               onc_object) ||
    592         !ResolveCertRefsOrRefToList(certs_by_guid,
    593                                     openvpn::kServerCARefs,
    594                                     openvpn::kServerCARef,
    595                                     openvpn::kServerCAPEMs,
    596                                     onc_object)) {
    597       return false;
    598     }
    599   }
    600 
    601   // Recurse into nested objects.
    602   for (base::DictionaryValue::Iterator it(*onc_object); !it.IsAtEnd();
    603        it.Advance()) {
    604     base::DictionaryValue* inner_object = NULL;
    605     if (!onc_object->GetDictionaryWithoutPathExpansion(it.key(), &inner_object))
    606       continue;
    607 
    608     const OncFieldSignature* field_signature =
    609         GetFieldSignature(signature, it.key());
    610     if (!field_signature)
    611       continue;
    612 
    613     if (!ResolveServerCertRefsInObject(certs_by_guid,
    614                                        *field_signature->value_signature,
    615                                        inner_object)) {
    616       return false;
    617     }
    618   }
    619   return true;
    620 }
    621 
    622 }  // namespace
    623 
    624 bool ResolveServerCertRefsInNetworks(const CertPEMsByGUIDMap& certs_by_guid,
    625                                      base::ListValue* network_configs) {
    626   bool success = true;
    627   for (base::ListValue::iterator it = network_configs->begin();
    628        it != network_configs->end(); ) {
    629     base::DictionaryValue* network = NULL;
    630     (*it)->GetAsDictionary(&network);
    631     if (!ResolveServerCertRefsInNetwork(certs_by_guid, network)) {
    632       std::string guid;
    633       network->GetStringWithoutPathExpansion(network_config::kGUID, &guid);
    634       // This might happen even with correct validation, if the referenced
    635       // certificate couldn't be imported.
    636       LOG(ERROR) << "Couldn't resolve some certificate reference of network "
    637                  << guid;
    638       it = network_configs->Erase(it, NULL);
    639       success = false;
    640       continue;
    641     }
    642     ++it;
    643   }
    644   return success;
    645 }
    646 
    647 bool ResolveServerCertRefsInNetwork(const CertPEMsByGUIDMap& certs_by_guid,
    648                                     base::DictionaryValue* network_config) {
    649   return ResolveServerCertRefsInObject(certs_by_guid,
    650                                        kNetworkConfigurationSignature,
    651                                        network_config);
    652 }
    653 
    654 }  // namespace onc
    655 }  // namespace chromeos
    656