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 |