Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: src/platform/cryptohome/authenticator.cc

Issue 2051003: Initial patch from Will. (Closed) Base URL: ssh://git@chromiumos-git/chromiumos
Patch Set: Address style nits. Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698