onc_utils.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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/string_util.h"
11#include "base/values.h"
12#include "chromeos/network/network_event_log.h"
13#include "crypto/encryptor.h"
14#include "crypto/hmac.h"
15#include "crypto/symmetric_key.h"
16
17#define ONC_LOG_WARNING(message) NET_LOG_WARNING("ONC", message)
18#define ONC_LOG_ERROR(message) NET_LOG_ERROR("ONC", message)
19
20namespace chromeos {
21namespace onc {
22
23namespace {
24
25const char kUnableToDecrypt[] = "Unable to decrypt encrypted ONC";
26const char kUnableToDecode[] = "Unable to decode encrypted ONC";
27
28}  // namespace
29
30const char kEmptyUnencryptedConfiguration[] =
31    "{\"Type\":\"UnencryptedConfiguration\",\"NetworkConfigurations\":[],"
32    "\"Certificates\":[]}";
33
34scoped_ptr<base::DictionaryValue> ReadDictionaryFromJson(
35    const std::string& json) {
36  std::string error;
37  base::Value* root = base::JSONReader::ReadAndReturnError(
38      json, base::JSON_ALLOW_TRAILING_COMMAS, NULL, &error);
39
40  base::DictionaryValue* dict_ptr = NULL;
41  if (!root || !root->GetAsDictionary(&dict_ptr)) {
42    ONC_LOG_ERROR("Invalid JSON Dictionary: " + error);
43    delete root;
44  }
45
46  return make_scoped_ptr(dict_ptr);
47}
48
49scoped_ptr<base::DictionaryValue> Decrypt(const std::string& passphrase,
50                                          const base::DictionaryValue& root) {
51  const int kKeySizeInBits = 256;
52  const int kMaxIterationCount = 500000;
53  std::string onc_type;
54  std::string initial_vector;
55  std::string salt;
56  std::string cipher;
57  std::string stretch_method;
58  std::string hmac_method;
59  std::string hmac;
60  int iterations;
61  std::string ciphertext;
62
63  if (!root.GetString(encrypted::kCiphertext, &ciphertext) ||
64      !root.GetString(encrypted::kCipher, &cipher) ||
65      !root.GetString(encrypted::kHMAC, &hmac) ||
66      !root.GetString(encrypted::kHMACMethod, &hmac_method) ||
67      !root.GetString(encrypted::kIV, &initial_vector) ||
68      !root.GetInteger(encrypted::kIterations, &iterations) ||
69      !root.GetString(encrypted::kSalt, &salt) ||
70      !root.GetString(encrypted::kStretch, &stretch_method) ||
71      !root.GetString(toplevel_config::kType, &onc_type) ||
72      onc_type != toplevel_config::kEncryptedConfiguration) {
73
74    ONC_LOG_ERROR("Encrypted ONC malformed.");
75    return scoped_ptr<base::DictionaryValue>();
76  }
77
78  if (hmac_method != encrypted::kSHA1 ||
79      cipher != encrypted::kAES256 ||
80      stretch_method != encrypted::kPBKDF2) {
81    ONC_LOG_ERROR("Encrypted ONC unsupported encryption scheme.");
82    return scoped_ptr<base::DictionaryValue>();
83  }
84
85  // Make sure iterations != 0, since that's not valid.
86  if (iterations == 0) {
87    ONC_LOG_ERROR(kUnableToDecrypt);
88    return scoped_ptr<base::DictionaryValue>();
89  }
90
91  // Simply a sanity check to make sure we can't lock up the machine
92  // for too long with a huge number (or a negative number).
93  if (iterations < 0 || iterations > kMaxIterationCount) {
94    ONC_LOG_ERROR("Too many iterations in encrypted ONC");
95    return scoped_ptr<base::DictionaryValue>();
96  }
97
98  if (!base::Base64Decode(salt, &salt)) {
99    ONC_LOG_ERROR(kUnableToDecode);
100    return scoped_ptr<base::DictionaryValue>();
101  }
102
103  scoped_ptr<crypto::SymmetricKey> key(
104      crypto::SymmetricKey::DeriveKeyFromPassword(crypto::SymmetricKey::AES,
105                                                  passphrase,
106                                                  salt,
107                                                  iterations,
108                                                  kKeySizeInBits));
109
110  if (!base::Base64Decode(initial_vector, &initial_vector)) {
111    ONC_LOG_ERROR(kUnableToDecode);
112    return scoped_ptr<base::DictionaryValue>();
113  }
114  if (!base::Base64Decode(ciphertext, &ciphertext)) {
115    ONC_LOG_ERROR(kUnableToDecode);
116    return scoped_ptr<base::DictionaryValue>();
117  }
118  if (!base::Base64Decode(hmac, &hmac)) {
119    ONC_LOG_ERROR(kUnableToDecode);
120    return scoped_ptr<base::DictionaryValue>();
121  }
122
123  crypto::HMAC hmac_verifier(crypto::HMAC::SHA1);
124  if (!hmac_verifier.Init(key.get()) ||
125      !hmac_verifier.Verify(ciphertext, hmac)) {
126    ONC_LOG_ERROR(kUnableToDecrypt);
127    return scoped_ptr<base::DictionaryValue>();
128  }
129
130  crypto::Encryptor decryptor;
131  if (!decryptor.Init(key.get(), crypto::Encryptor::CBC, initial_vector))  {
132    ONC_LOG_ERROR(kUnableToDecrypt);
133    return scoped_ptr<base::DictionaryValue>();
134  }
135
136  std::string plaintext;
137  if (!decryptor.Decrypt(ciphertext, &plaintext)) {
138    ONC_LOG_ERROR(kUnableToDecrypt);
139    return scoped_ptr<base::DictionaryValue>();
140  }
141
142  scoped_ptr<base::DictionaryValue> new_root =
143      ReadDictionaryFromJson(plaintext);
144  if (new_root.get() == NULL) {
145    ONC_LOG_ERROR("Property dictionary malformed.");
146    return scoped_ptr<base::DictionaryValue>();
147  }
148
149  return new_root.Pass();
150}
151
152std::string GetSourceAsString(ONCSource source) {
153  switch (source) {
154    case ONC_SOURCE_DEVICE_POLICY:
155      return "device policy";
156    case ONC_SOURCE_USER_POLICY:
157      return "user policy";
158    case ONC_SOURCE_NONE:
159      return "none";
160    case ONC_SOURCE_USER_IMPORT:
161      return "user import";
162  }
163  NOTREACHED() << "unknown ONC source " << source;
164  return "unknown";
165}
166
167void ExpandField(const std::string fieldname,
168                 const StringSubstitution& substitution,
169                 base::DictionaryValue* onc_object) {
170  std::string user_string;
171  if (!onc_object->GetStringWithoutPathExpansion(fieldname, &user_string))
172    return;
173
174  std::string login_id;
175  if (substitution.GetSubstitute(substitutes::kLoginIDField, &login_id)) {
176    ReplaceSubstringsAfterOffset(&user_string, 0,
177                                 onc::substitutes::kLoginIDField,
178                                 login_id);
179  }
180
181  std::string email;
182  if (substitution.GetSubstitute(substitutes::kEmailField, &email)) {
183    ReplaceSubstringsAfterOffset(&user_string, 0,
184                                 onc::substitutes::kEmailField,
185                                 email);
186  }
187
188  onc_object->SetStringWithoutPathExpansion(fieldname, user_string);
189}
190
191void ExpandStringsInOncObject(
192    const OncValueSignature& signature,
193    const StringSubstitution& substitution,
194    base::DictionaryValue* onc_object) {
195  if (&signature == &kEAPSignature) {
196    ExpandField(eap::kAnonymousIdentity, substitution, onc_object);
197    ExpandField(eap::kIdentity, substitution, onc_object);
198  } else if (&signature == &kL2TPSignature ||
199             &signature == &kOpenVPNSignature) {
200    ExpandField(vpn::kUsername, substitution, onc_object);
201  }
202
203  // Recurse into nested objects.
204  for (base::DictionaryValue::Iterator it(*onc_object); !it.IsAtEnd();
205       it.Advance()) {
206    base::DictionaryValue* inner_object = NULL;
207    if (!onc_object->GetDictionaryWithoutPathExpansion(it.key(), &inner_object))
208      continue;
209
210    const OncFieldSignature* field_signature =
211        GetFieldSignature(signature, it.key());
212
213    ExpandStringsInOncObject(*field_signature->value_signature,
214                             substitution, inner_object);
215  }
216}
217
218}  // namespace onc
219}  // namespace chromeos
220