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