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