| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 The Chromium OS 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 "cryptohome/authenticator.h" | |
| 6 | |
| 7 #include <limits.h> | |
| 8 #include <openssl/err.h> | |
| 9 #include <openssl/evp.h> | |
| 10 #include <openssl/sha.h> | |
| 11 #include <stdlib.h> | |
| 12 #include <unistd.h> | |
| 13 | |
| 14 #include "base/file_path.h" | |
| 15 #include "base/file_util.h" | |
| 16 #include "base/logging.h" | |
| 17 #include "chromeos/utility.h" | |
| 18 | |
| 19 namespace cryptohome { | |
| 20 | |
| 21 using namespace chromeos; | |
| 22 using namespace file_util; | |
| 23 using std::string; | |
| 24 | |
| 25 // system salt and user dirs start here... | |
| 26 const string kDefaultShadowRoot = "/home/.shadow/"; | |
| 27 | |
| 28 // String that appears at the start of OpenSSL cipher text with embedded salt | |
| 29 const string kOpenSSLMagic = "Salted__"; | |
| 30 | |
| 31 Authenticator::Authenticator(const string &shadow_root) | |
| 32 : shadow_root_(shadow_root) | |
| 33 {} | |
| 34 | |
| 35 Authenticator::Authenticator() : shadow_root_(kDefaultShadowRoot) {} | |
| 36 | |
| 37 Authenticator::~Authenticator() {} | |
| 38 | |
| 39 bool Authenticator::Init() { | |
| 40 FilePath path(shadow_root_); | |
| 41 return LoadFileBytes(path.Append("salt"), &system_salt_); | |
| 42 } | |
| 43 | |
| 44 Blob Authenticator::GetSystemSalt() const { | |
| 45 return system_salt_; | |
| 46 } | |
| 47 | |
| 48 // This is the analog to cryptohome::password_to_wrapper from the | |
| 49 // cryptohome script. It computes a SHA1(salt + str) and returns an | |
| 50 // ASCII encoded version of the result as a string. The hashing step is | |
| 51 // repeated |iters| number of times. | |
| 52 // | |
| 53 string Authenticator::IteratedWrapHashedPassword( | |
| 54 const FilePath &master_salt_file, const string &hashed_password, | |
| 55 const int iters) const { | |
| 56 | |
| 57 string master_salt; | |
| 58 if (!LoadFileString(master_salt_file, &master_salt)) { | |
| 59 return false; | |
| 60 } | |
| 61 | |
| 62 Blob blob(hashed_password.begin(), hashed_password.end()); | |
| 63 | |
| 64 for (int i = 0; i < iters; ++i) { | |
| 65 SHA_CTX ctx; | |
| 66 unsigned char md_value[SHA_DIGEST_LENGTH]; | |
| 67 | |
| 68 SHA1_Init(&ctx); | |
| 69 SHA1_Update(&ctx, master_salt.c_str(), master_salt.length()); | |
| 70 SHA1_Update(&ctx, &blob.front(), blob.size()); | |
| 71 SHA1_Final(md_value, &ctx); | |
| 72 | |
| 73 blob.assign(md_value, md_value + SHA_DIGEST_LENGTH); | |
| 74 } | |
| 75 | |
| 76 return AsciiEncode(blob); | |
| 77 } | |
| 78 | |
| 79 string Authenticator::WrapHashedPassword(const FilePath &master_salt_file, | |
| 80 const string &hashed_password) const { | |
| 81 return IteratedWrapHashedPassword(master_salt_file, hashed_password, 1); | |
| 82 } | |
| 83 | |
| 84 bool Authenticator::TestDecrypt(const string passphrase, | |
| 85 const Blob salt, | |
| 86 const Blob cipher_text) const { | |
| 87 if (salt.size() < PKCS5_SALT_LEN) { | |
| 88 LOG(ERROR) << "Invalid salt"; | |
| 89 return false; | |
| 90 } | |
| 91 | |
| 92 unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH]; | |
| 93 | |
| 94 int rv = EVP_BytesToKey( | |
| 95 EVP_aes_256_cbc(), EVP_sha1(), &salt.front(), | |
| 96 reinterpret_cast<const unsigned char *>(passphrase.c_str()), | |
| 97 passphrase.size(), 1, key, iv); | |
| 98 | |
| 99 if (rv != EVP_MAX_KEY_LENGTH) { | |
| 100 LOG(ERROR) << "Key size is " << rv << " bytes, should be " | |
| 101 << EVP_MAX_KEY_LENGTH; | |
| 102 return false; | |
| 103 } | |
| 104 | |
| 105 int pt_size = cipher_text.size(); | |
| 106 | |
| 107 unsigned char *plain_text = new unsigned char[pt_size]; | |
| 108 int final_size = 0; | |
| 109 | |
| 110 EVP_CIPHER_CTX d_ctx; | |
| 111 EVP_CIPHER_CTX_init(&d_ctx); | |
| 112 EVP_DecryptInit_ex(&d_ctx, EVP_aes_256_ecb(), NULL, key, iv); | |
| 113 rv = EVP_DecryptUpdate(&d_ctx, plain_text, &pt_size, | |
| 114 &cipher_text.front(), | |
| 115 cipher_text.size()); | |
| 116 | |
| 117 rv = EVP_DecryptFinal_ex(&d_ctx, plain_text + pt_size, &final_size); | |
| 118 | |
| 119 pt_size += final_size; | |
| 120 | |
| 121 chromeos::SecureMemset(plain_text, sizeof(plain_text), 0); | |
| 122 delete plain_text; | |
| 123 | |
| 124 if (rv != 1) { | |
| 125 | |
| 126 unsigned long err = ERR_get_error(); | |
| 127 ERR_load_ERR_strings(); | |
| 128 ERR_load_crypto_strings(); | |
| 129 | |
| 130 LOG(INFO) << "OpenSSL Error: " << err | |
| 131 << ": " << ERR_lib_error_string(err) | |
| 132 << ", " << ERR_func_error_string(err) | |
| 133 << ", " << ERR_reason_error_string(err); | |
| 134 | |
| 135 | |
| 136 return false; | |
| 137 } | |
| 138 | |
| 139 return true; | |
| 140 } | |
| 141 | |
| 142 bool Authenticator::TestOneMasterKey(const FilePath &master_key_file, | |
| 143 const string &hashed_password) const { | |
| 144 if (system_salt_.empty()) { | |
| 145 LOG(ERROR) << "System salt not loaded."; | |
| 146 return false; | |
| 147 } | |
| 148 | |
| 149 Blob cipher_text; | |
| 150 if (!LoadFileBytes(master_key_file, &cipher_text)) { | |
| 151 LOG(ERROR) << "Error loading master key from '" | |
| 152 << master_key_file.value() << "'"; | |
| 153 return false; | |
| 154 } | |
| 155 | |
| 156 unsigned int header_size = kOpenSSLMagic.length() + PKCS5_SALT_LEN; | |
| 157 if (cipher_text.size() <= header_size) { | |
| 158 LOG(ERROR) << "Master key file too short: '" | |
| 159 << master_key_file.value() << "'"; | |
| 160 return false; | |
| 161 } | |
| 162 | |
| 163 string magic(cipher_text.begin(), | |
| 164 cipher_text.begin() + kOpenSSLMagic.length()); | |
| 165 if (magic != kOpenSSLMagic) { | |
| 166 LOG(ERROR) << "Invalid magic in master key file: '" | |
| 167 << master_key_file.value() << "'"; | |
| 168 return false; | |
| 169 } | |
| 170 | |
| 171 Blob salt(cipher_text.begin() + kOpenSSLMagic.length(), | |
| 172 cipher_text.begin() + header_size); | |
| 173 | |
| 174 string passphrase = | |
| 175 WrapHashedPassword(FilePath(master_key_file.value() + ".salt"), | |
| 176 hashed_password); | |
| 177 | |
| 178 cipher_text.erase(cipher_text.begin(), cipher_text.begin() + header_size); | |
| 179 return TestDecrypt(passphrase, salt, cipher_text); | |
| 180 } | |
| 181 | |
| 182 bool Authenticator::TestAllMasterKeys(const Credentials &credentials) const { | |
| 183 #ifdef CHROMEOS_PAM_LOCALACCOUNT | |
| 184 if (credentials.IsLocalAccount()) { | |
| 185 LOG(WARNING) << "Logging in with local account credentials."; | |
| 186 return true; | |
| 187 } | |
| 188 #endif | |
| 189 | |
| 190 if (system_salt_.empty()) { | |
| 191 LOG(ERROR) << "System salt not loaded."; | |
| 192 return false; | |
| 193 } | |
| 194 | |
| 195 FilePath shadow_root(shadow_root_); | |
| 196 FilePath user_path = | |
| 197 shadow_root.Append(credentials.GetObfuscatedUsername(system_salt_)); | |
| 198 string weak_hash(credentials.GetPasswordWeakHash(system_salt_)); | |
| 199 char index_str[5]; | |
| 200 | |
| 201 // Test against all of the master keys (master.0, master.1, ...) | |
| 202 for (int i = 0; /* loop forever */ ; ++i) { | |
| 203 string filename("master."); | |
| 204 if (0 == snprintf(index_str, sizeof(index_str), "%i", i)) | |
| 205 return false; | |
| 206 filename.append(index_str); | |
| 207 | |
| 208 FilePath master_key_file = user_path.Append(filename); | |
| 209 | |
| 210 if (!AbsolutePath(&master_key_file)) { | |
| 211 // master.N can't be read, assume we're done and have failed | |
| 212 break; | |
| 213 } | |
| 214 | |
| 215 if (TestOneMasterKey(master_key_file, weak_hash)) { | |
| 216 // decrypted ok, return success | |
| 217 return true; | |
| 218 } | |
| 219 } | |
| 220 | |
| 221 return false; | |
| 222 } | |
| 223 | |
| 224 bool Authenticator::LoadFileBytes(const FilePath &path, Blob *blob) const { | |
| 225 CHECK(blob); | |
| 226 int64 file_size; | |
| 227 if (!PathExists(path)) { | |
| 228 LOG(ERROR) << path.value() << " does not exist!"; | |
| 229 return false; | |
| 230 } | |
| 231 if (!GetFileSize(path, &file_size)) { | |
| 232 LOG(ERROR) << "Could not get size of " << path.value(); | |
| 233 return false; | |
| 234 } | |
| 235 if (file_size > INT_MAX) { | |
| 236 LOG(ERROR) << "File " << path.value() << " is too large: " << file_size; | |
| 237 return false; | |
| 238 } | |
| 239 Blob buf(file_size); | |
| 240 int data_read = ReadFile(path, reinterpret_cast<char*>(&buf[0]), file_size); | |
| 241 // Cast is okay because of comparison to INT_MAX above | |
| 242 if (data_read != static_cast<int>(file_size)) { | |
| 243 LOG(ERROR) << "Could not read entire file " << file_size; | |
| 244 return false; | |
| 245 } | |
| 246 blob->swap(buf); | |
| 247 return true; | |
| 248 } | |
| 249 | |
| 250 bool Authenticator::LoadFileString(const FilePath &path, string *str) const { | |
| 251 return PathExists(path) && ReadFileToString(path, str); | |
| 252 } | |
| 253 | |
| 254 } // namespace cryptohome | |
| OLD | NEW |