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
27using namespace ::onc;
28
29namespace chromeos {
30namespace onc {
31
32namespace {
33
34const char kUnableToDecrypt[] = "Unable to decrypt encrypted ONC";
35const char kUnableToDecode[] = "Unable to decode encrypted ONC";
36
37}  // namespace
38
39const char kEmptyUnencryptedConfiguration[] =
40    "{\"Type\":\"UnencryptedConfiguration\",\"NetworkConfigurations\":[],"
41    "\"Certificates\":[]}";
42
43scoped_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
58scoped_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
161std::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
178void 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
202void 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
231void 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
243namespace {
244
245class 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
281scoped_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
288namespace {
289
290std::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
317CertPEMsByGUIDMap 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
351bool 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
449scoped_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
459namespace {
460
461bool 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
477bool 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
494bool 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
522bool 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.
544bool 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
564bool 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
628bool 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
651bool ResolveServerCertRefsInNetwork(const CertPEMsByGUIDMap& certs_by_guid,
652                                    base::DictionaryValue* network_config) {
653  return ResolveServerCertRefsInObject(certs_by_guid,
654                                       kNetworkConfigurationSignature,
655                                       network_config);
656}
657
658NetworkTypePattern 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
677bool 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