- add sources.
[platform/framework/web/crosswalk.git] / src / chromeos / network / onc / onc_utils.cc
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     ResolveServerCertRefsInNetworks(server_and_ca_certs, validated_networks);
435     network_configs->Swap(validated_networks);
436   }
437
438   base::DictionaryValue* validated_global_config = NULL;
439   if (toplevel_onc->GetDictionaryWithoutPathExpansion(
440           toplevel_config::kGlobalNetworkConfiguration,
441           &validated_global_config)) {
442     global_network_config->Swap(validated_global_config);
443   }
444
445   return success;
446 }
447
448 scoped_refptr<net::X509Certificate> DecodePEMCertificate(
449     const std::string& pem_encoded) {
450   std::string decoded = DecodePEM(pem_encoded);
451   scoped_refptr<net::X509Certificate> cert =
452       net::X509Certificate::CreateFromBytes(decoded.data(), decoded.size());
453   LOG_IF(ERROR, !cert.get()) << "Couldn't create certificate from X509 data: "
454                              << decoded;
455   return cert;
456 }
457
458 namespace {
459
460 bool GUIDRefToPEMEncoding(const CertPEMsByGUIDMap& certs_by_guid,
461                           const std::string& guid_ref,
462                           std::string* pem_encoded) {
463   CertPEMsByGUIDMap::const_iterator it = certs_by_guid.find(guid_ref);
464   if (it == certs_by_guid.end()) {
465     LOG(ERROR) << "Couldn't resolve certificate reference " << guid_ref;
466     return false;
467   }
468   *pem_encoded = it->second;
469   if (pem_encoded->empty()) {
470     LOG(ERROR) << "Couldn't PEM-encode certificate with GUID " << guid_ref;
471     return false;
472   }
473   return true;
474 }
475
476 bool ResolveSingleCertRef(const CertPEMsByGUIDMap& certs_by_guid,
477                           const std::string& key_guid_ref,
478                           const std::string& key_pem,
479                           base::DictionaryValue* onc_object) {
480   std::string guid_ref;
481   if (!onc_object->GetStringWithoutPathExpansion(key_guid_ref, &guid_ref))
482     return true;
483
484   std::string pem_encoded;
485   if (!GUIDRefToPEMEncoding(certs_by_guid, guid_ref, &pem_encoded))
486     return false;
487
488   onc_object->RemoveWithoutPathExpansion(key_guid_ref, NULL);
489   onc_object->SetStringWithoutPathExpansion(key_pem, pem_encoded);
490   return true;
491 }
492
493 bool ResolveCertRefList(const CertPEMsByGUIDMap& certs_by_guid,
494                         const std::string& key_guid_ref_list,
495                         const std::string& key_pem_list,
496                         base::DictionaryValue* onc_object) {
497   const base::ListValue* guid_ref_list = NULL;
498   if (!onc_object->GetListWithoutPathExpansion(key_guid_ref_list,
499                                                &guid_ref_list)) {
500     return true;
501   }
502
503   scoped_ptr<base::ListValue> pem_list(new base::ListValue);
504   for (base::ListValue::const_iterator it = guid_ref_list->begin();
505        it != guid_ref_list->end(); ++it) {
506     std::string guid_ref;
507     (*it)->GetAsString(&guid_ref);
508
509     std::string pem_encoded;
510     if (!GUIDRefToPEMEncoding(certs_by_guid, guid_ref, &pem_encoded))
511       return false;
512
513     pem_list->AppendString(pem_encoded);
514   }
515
516   onc_object->RemoveWithoutPathExpansion(key_guid_ref_list, NULL);
517   onc_object->SetWithoutPathExpansion(key_pem_list, pem_list.release());
518   return true;
519 }
520
521 bool ResolveSingleCertRefToList(const CertPEMsByGUIDMap& certs_by_guid,
522                                 const std::string& key_guid_ref,
523                                 const std::string& key_pem_list,
524                                 base::DictionaryValue* onc_object) {
525   std::string guid_ref;
526   if (!onc_object->GetStringWithoutPathExpansion(key_guid_ref, &guid_ref))
527     return true;
528
529   std::string pem_encoded;
530   if (!GUIDRefToPEMEncoding(certs_by_guid, guid_ref, &pem_encoded))
531     return false;
532
533   scoped_ptr<base::ListValue> pem_list(new base::ListValue);
534   pem_list->AppendString(pem_encoded);
535   onc_object->RemoveWithoutPathExpansion(key_guid_ref, NULL);
536   onc_object->SetWithoutPathExpansion(key_pem_list, pem_list.release());
537   return true;
538 }
539
540 bool ResolveServerCertRefsInObject(const CertPEMsByGUIDMap& certs_by_guid,
541                                    const OncValueSignature& signature,
542                                    base::DictionaryValue* onc_object) {
543   if (&signature == &kCertificatePatternSignature) {
544     if (!ResolveCertRefList(certs_by_guid, certificate::kIssuerCARef,
545                             certificate::kIssuerCAPEMs, onc_object)) {
546       return false;
547     }
548   } else if (&signature == &kEAPSignature) {
549     if (!ResolveSingleCertRefToList(certs_by_guid, eap::kServerCARef,
550                                     eap::kServerCAPEMs, onc_object)) {
551       return false;
552     }
553   } else if (&signature == &kIPsecSignature) {
554     if (!ResolveSingleCertRefToList(certs_by_guid, ipsec::kServerCARef,
555                                     ipsec::kServerCAPEMs, onc_object)) {
556       return false;
557     }
558   } else if (&signature == &kIPsecSignature ||
559              &signature == &kOpenVPNSignature) {
560     if (!ResolveSingleCertRef(certs_by_guid, openvpn::kServerCertRef,
561                               openvpn::kServerCertPEM, onc_object) ||
562         !ResolveSingleCertRefToList(certs_by_guid, openvpn::kServerCARef,
563                                     openvpn::kServerCAPEMs, onc_object)) {
564       return false;
565     }
566   }
567
568   // Recurse into nested objects.
569   for (base::DictionaryValue::Iterator it(*onc_object); !it.IsAtEnd();
570        it.Advance()) {
571     base::DictionaryValue* inner_object = NULL;
572     if (!onc_object->GetDictionaryWithoutPathExpansion(it.key(), &inner_object))
573       continue;
574
575     const OncFieldSignature* field_signature =
576         GetFieldSignature(signature, it.key());
577     if (!field_signature)
578       continue;
579
580     if (!ResolveServerCertRefsInObject(certs_by_guid,
581                                        *field_signature->value_signature,
582                                        inner_object)) {
583       return false;
584     }
585   }
586   return true;
587 }
588
589 }  // namespace
590
591 bool ResolveServerCertRefsInNetworks(const CertPEMsByGUIDMap& certs_by_guid,
592                                      base::ListValue* network_configs) {
593   bool success = true;
594   for (base::ListValue::iterator it = network_configs->begin();
595        it != network_configs->end(); ) {
596     base::DictionaryValue* network = NULL;
597     (*it)->GetAsDictionary(&network);
598     if (!ResolveServerCertRefsInNetwork(certs_by_guid, network)) {
599       std::string guid;
600       network->GetStringWithoutPathExpansion(network_config::kGUID, &guid);
601       // This might happen even with correct validation, if the referenced
602       // certificate couldn't be imported.
603       LOG(ERROR) << "Couldn't resolve some certificate reference of network "
604                  << guid;
605       it = network_configs->Erase(it, NULL);
606       success = false;
607       continue;
608     }
609     ++it;
610   }
611   return success;
612 }
613
614 bool ResolveServerCertRefsInNetwork(const CertPEMsByGUIDMap& certs_by_guid,
615                                     base::DictionaryValue* network_config) {
616   return ResolveServerCertRefsInObject(certs_by_guid,
617                                        kNetworkConfigurationSignature,
618                                        network_config);
619 }
620
621 }  // namespace onc
622 }  // namespace chromeos