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

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

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

Powered by Google App Engine
This is Rietveld 408576698