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

Side by Side Diff: src/platform/cryptohome/mount.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 // Contains the implementation of class Mount
6
7 // TODO(fes): Use correct ordering of file includes once the platform-specific
8 // calls are moved into a separate file. Right now, this is required in order
9 // to avoid redefinitions.
10 #include "base/file_util.h"
11 #include "base/logging.h"
12 #include "base/platform_thread.h"
13 #include "base/time.h"
14 #include "base/string_util.h"
15 #include "chromeos/utility.h"
16 #include "cryptohome/cryptohome_common.h"
17 #include "cryptohome/mount.h"
18 #include "cryptohome/username_passkey.h"
19
20 extern "C" {
21 #include <ecryptfs.h>
22 #include <keyutils.h>
23 }
24 #include <dirent.h>
25 #include <errno.h>
26 #include <openssl/err.h>
27 #include <openssl/evp.h>
28 #include <openssl/rand.h>
29 #include <openssl/sha.h>
30 #include <pwd.h>
31 #include <signal.h>
32 #include <sys/mount.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35
36 using std::string;
37
38 namespace cryptohome {
39
40 const string kDefaultEntropySource = "/dev/urandom";
41 const string kDefaultHomeDir = "/home/chronos/user";
42 const int kDefaultMountOptions = MS_NOEXEC | MS_NOSUID | MS_NODEV;
43 const string kDefaultShadowRoot = "/home/.shadow";
44 const string kDefaultSharedUser = "chronos";
45 const string kDefaultSkeletonSource = "/etc/skel";
46 const string kIncognitoUser = "incognito";
47 const string kMtab = "/etc/mtab";
48 const string kOpenSSLMagic = "Salted__";
49 const std::string kProcDir = "/proc";
50
51 Mount::Mount()
52 : default_user_(-1),
53 default_group_(-1),
54 default_username_(kDefaultSharedUser),
55 entropy_source_(kDefaultEntropySource),
56 home_dir_(kDefaultHomeDir),
57 shadow_root_(kDefaultShadowRoot),
58 skel_source_(kDefaultSkeletonSource),
59 system_salt_(),
60 set_vault_ownership_(true) {
61 }
62
63 Mount::Mount(const std::string& username, const std::string& entropy_source,
64 const std::string& home_dir, const std::string& shadow_root,
65 const std::string& skel_source)
66 : default_user_(-1),
67 default_group_(-1),
68 default_username_(username),
69 entropy_source_(entropy_source),
70 home_dir_(home_dir),
71 shadow_root_(shadow_root),
72 skel_source_(skel_source),
73 system_salt_(),
74 set_vault_ownership_(true) {
75 }
76
77 Mount::~Mount() {
78 }
79
80 bool Mount::Init() {
81 bool result = true;
82
83 // Load the passwd entry for the shared user
84 struct passwd* user_info = getpwnam(default_username_.c_str());
mschilder 2010/05/27 04:20:18 getpwnam_r() instead?
85 if (user_info) {
86 // Store the user's uid/gid for later use in changing vault ownership
87 default_user_ = user_info->pw_uid;
88 default_group_ = user_info->pw_gid;
89 } else {
90 result = false;
91 }
92
93 // One-time load of the global system salt (used in generating username
94 // hashes)
95 if (!LoadFileBytes(FilePath(StringPrintf("%s/salt", shadow_root_.c_str())),
96 system_salt_)) {
97 result = false;
98 }
99
100 return result;
101 }
102
103 bool Mount::EnsureCryptohome(const Credentials& credentials, bool* created) {
104 // If the user has an old-style cryptohome, delete it
105 FilePath old_image_path(StringPrintf("%s/image",
106 GetUserDirectory(credentials).c_str()));
107 if (file_util::PathExists(old_image_path)) {
108 file_util::Delete(FilePath(GetUserDirectory(credentials)), true);
109 }
110 // Now check for the presence of a vault directory
111 FilePath vault_path(GetUserVaultPath(credentials));
112 if (!file_util::DirectoryExists(vault_path)) {
113 // If the vault directory doesn't exist, then create the cryptohome from
114 // scratch
115 bool result = CreateCryptohome(credentials, 0);
116 if (created) {
117 *created = result;
118 }
119 return result;
120 }
121 if (created) {
122 *created = false;
123 }
124 return true;
125 }
126
127 bool Mount::MountCryptohome(const Credentials& credentials, int index,
128 MountError* mount_error) {
129 std::string username = credentials.GetFullUsername();
130 if (username.compare(kIncognitoUser) == 0) {
131 // TODO(fes): Have incognito set error conditions?
132 if (mount_error) {
133 *mount_error = MOUNT_ERROR_NONE;
134 }
135 return MountIncognitoCryptohome();
136 }
137
138 bool created = false;
139 if (!EnsureCryptohome(credentials, &created)) {
140 LOG(ERROR) << "Error creating cryptohome.";
141 if (mount_error) {
142 *mount_error = MOUNT_ERROR_FATAL;
143 }
144 return false;
145 }
146
147 FilePath user_key_file(GetUserKeyFile(credentials, index));
148
149 // Retrieve the user's salt for the key index
150 SecureBlob user_salt = GetUserSalt(credentials, index);
151
152 // Generate the passkey wrapper (key encryption key)
153 SecureBlob passkey_wrapper =
154 PasskeyToWrapper(credentials.GetPasskey(), user_salt, 1);
155
156 // Attempt to unwrap the master key at the index with the passkey wrapper
157 VaultKeyset vault_keyset;
158 bool mount_result = UnwrapMasterKey(user_key_file, passkey_wrapper,
159 &vault_keyset);
160
161 if (!mount_result) {
162 if (mount_error) {
163 *mount_error = Mount::MOUNT_ERROR_KEY_FAILURE;
164 }
165 return false;
166 }
167
168 // Add the decrypted key to the keyring so that ecryptfs can use it
169 string key_signature, fnek_signature;
170 if (!AddKeyToEcryptfsKeyring(vault_keyset, &key_signature, &fnek_signature)) {
171 LOG(ERROR) << "Cryptohome mount failed because of keyring failure.";
172 if (mount_error) {
173 *mount_error = MOUNT_ERROR_FATAL;
174 }
175 return false;
176 }
177
178 // Specify the ecryptfs options for mounting the user's cryptohome
179 string ecryptfs_options = StringPrintf("ecryptfs_cipher=aes"
180 ",ecryptfs_key_bytes=%d"
181 ",ecryptfs_fnek_sig=%s,ecryptfs_sig=%s"
182 ",ecryptfs_unlink_sigs",
183 CRYPTOHOME_AES_KEY_BYTES,
184 fnek_signature.c_str(),
185 key_signature.c_str());
186
187 // TODO(fes): mount(1) -> mount(2): how to issue "user"
188 string vault_path = GetUserVaultPath(credentials);
189 // Attempt to mount the user's cryptohome
190 if (mount(vault_path.c_str(), home_dir_.c_str(),
191 "ecryptfs", kDefaultMountOptions, ecryptfs_options.c_str())) {
192 LOG(ERROR) << "Cryptohome mount failed: " << errno << " for vault: "
193 << vault_path;
194 if (mount_error) {
195 *mount_error = MOUNT_ERROR_FATAL;
196 }
197 return false;
198 }
199
200 if (created) {
201 CopySkeletonForUser(credentials);
202 }
203
204 if (mount_error) {
205 *mount_error = MOUNT_ERROR_NONE;
206 }
207 return true;
208 }
209
210 bool Mount::UnmountCryptohome() {
211 // Try an immediate unmount
212 bool was_busy;
213 if (!Unmount(home_dir_.c_str(), false, &was_busy)) {
214 // If the unmount fails, do a lazy unmount
215 Unmount(home_dir_.c_str(), true, NULL);
216 if (was_busy) {
217 // Signal processes to close
218 if (TerminatePidsWithOpenFiles(home_dir_, false)) {
219 // Then we had to send a SIGINT to some processes with open files. Give
220 // a "grace" period before killing with a SIGKILL.
221 // TODO(fes): This isn't ideal, nor is it accurate (a static sleep can't
222 // guarantee that processes will clean up in time). If we switch to VFS
223 // namespace-based mounts, then we can get away from this construct.
224 PlatformThread::Sleep(100);
225 sync();
226 if (TerminatePidsWithOpenFiles(home_dir_, true)) {
227 PlatformThread::Sleep(100);
228 }
229 }
230 }
231 sync();
232 // TODO(fes): This should return an error condition if it is still mounted.
233 // Right now it doesn't, since it should get cleaned up outside of
234 // cryptohome since we failed over to a lazy unmount
235 }
236
237 // TODO(fes): Do we need to keep this behavior?
238 //TerminatePidsForUser(default_user_, true);
239
240 // Clear the user keyring if the unmount was successful
241 keyctl(KEYCTL_CLEAR, KEY_SPEC_USER_KEYRING);
242
243 return true;
244 }
245
246 bool Mount::RemoveCryptohome(const Credentials& credentials) {
247 std::string user_dir = GetUserDirectory(credentials);
248 CHECK(user_dir.length() > (shadow_root_.length() + 1));
249
250 return file_util::Delete(FilePath(user_dir), true);
251 }
252
253 bool Mount::IsCryptohomeMounted() {
254 // Trivial string match from /etc/mtab to see if the cryptohome mount point is
255 // listed. This works because Chrome OS is a controlled environment and the
256 // only way /home/chronos/user should be mounted is if cryptohome mounted it.
257 string contents;
258 if (file_util::ReadFileToString(FilePath(kMtab), &contents)) {
259 if (contents.find(StringPrintf(" %s ", home_dir_.c_str()).c_str())
260 != string::npos) {
261 return true;
262 }
263 }
264 return false;
265 }
266
267 bool Mount::IsCryptohomeMountedForUser(const Credentials& credentials) {
268 // Trivial string match from /etc/mtab to see if the cryptohome mount point
269 // and the user's vault path are present. Assumes this user is mounted if it
270 // finds both. This will need to change if simultaneous login is implemented.
271 string contents;
272 if (file_util::ReadFileToString(FilePath(kMtab), &contents)) {
273 FilePath vault_path(GetUserVaultPath(credentials));
274 if ((contents.find(StringPrintf(" %s ", home_dir_.c_str()).c_str())
275 != string::npos)
276 && (contents.find(StringPrintf("%s ",
277 vault_path.value().c_str()).c_str())
278 != string::npos)) {
279 return true;
280 }
281 }
282 return false;
283 }
284
285 bool Mount::CreateCryptohome(const Credentials& credentials, int index) {
286 // Save the current umask
287 mode_t original_mask = umask(S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH
288 | S_IXOTH);
289
290 // Create the user's entry in the shadow root
291 FilePath user_dir(GetUserDirectory(credentials));
292 file_util::CreateDirectory(user_dir);
293
294 // Generates a new master key and salt at the given index
295 if (!CreateMasterKey(credentials, index)) {
296 umask(original_mask);
297 return false;
298 }
299
300 // Create the user's path and set the proper ownership
301 FilePath vault_path(GetUserVaultPath(credentials));
302 if (!file_util::CreateDirectory(vault_path)) {
303 LOG(ERROR) << "Couldn't create vault path: " << vault_path.value().c_str();
304 umask(original_mask);
305 return false;
306 }
307 if (set_vault_ownership_) {
308 // TODO(fes): Move platform-specific calls to a separate class
309 if (chown(vault_path.value().c_str(), default_user_, default_group_)) {
310 LOG(ERROR) << "Couldn't change owner (" << default_user_ << ":"
311 << default_group_ << ") of vault path: "
312 << vault_path.value().c_str();
313 umask(original_mask);
314 return false;
315 }
316 }
317
318 // Restore the umask
319 umask(original_mask);
320 return true;
321 }
322
323 bool Mount::TestCredentials(const Credentials& credentials) {
324 // Iterate over the keys in the user's entry in the shadow root to see if
325 // these credentials successfully decrypt any
326 for (int index = 0; /* loop forever */; index++) {
327 FilePath user_key_file(GetUserKeyFile(credentials, index));
328 if (!file_util::AbsolutePath(&user_key_file)) {
329 break;
330 }
331
332 SecureBlob user_salt = GetUserSalt(credentials, index);
333
334 SecureBlob passkey_wrapper = PasskeyToWrapper(credentials.GetPasskey(),
335 user_salt, 1);
336
337 VaultKeyset vault_keyset;
338 if (UnwrapMasterKey(user_key_file, passkey_wrapper, &vault_keyset)) {
339 return true;
340 }
341 }
342 return false;
343 }
344
345 bool Mount::MigratePasskey(const Credentials& credentials,
346 const char* old_key) {
347 // Iterate over the keys in the user's entry in the shadow root to see if
348 // these credentials successfully decrypt any
349 std::string username = credentials.GetFullUsername();
350 UsernamePasskey old_credentials(username.c_str(), username.length(),
351 old_key, strlen(old_key));
352 for (int index = 0; /* loop forever */; index++) {
353 FilePath user_key_file(GetUserKeyFile(old_credentials, index));
354 if (!file_util::AbsolutePath(&user_key_file)) {
355 break;
356 }
357
358 SecureBlob user_salt = GetUserSalt(old_credentials, index);
359
360 SecureBlob passkey_wrapper = PasskeyToWrapper(old_credentials.GetPasskey(),
361 user_salt, 1);
362
363 VaultKeyset vault_keyset;
364 if (UnwrapMasterKey(user_key_file, passkey_wrapper, &vault_keyset)) {
365 // Save to the next key index first so that if there is a failure, we
366 // don't delete the existing working keyset.
367 bool save_result = SaveVaultKeyset(credentials, vault_keyset, index + 1);
368 if (save_result) {
369 // Saved okay, move to index 0.
370 if (!file_util::ReplaceFile(FilePath(GetUserKeyFile(credentials,
371 index + 1)),
372 FilePath(GetUserKeyFile(credentials, 0)))) {
373 return false;
374 }
375 if (!file_util::ReplaceFile(FilePath(GetUserSaltFile(credentials,
376 index + 1)),
377 FilePath(GetUserSaltFile(credentials, 0)))) {
378 return false;
379 }
380 // Remove older keys
381 for (index = 1; /* loop forever */; index++) {
382 FilePath old_user_key_file(GetUserKeyFile(old_credentials, index));
383 FilePath old_user_salt_file(GetUserSaltFile(old_credentials, index));
384 if (!file_util::AbsolutePath(&old_user_key_file)) {
385 break;
386 }
387 file_util::Delete(old_user_key_file, false);
388 file_util::Delete(old_user_salt_file, false);
389 }
390 return true;
391 } else {
392 // Couldn't save the vault keyset, delete it
393 file_util::Delete(FilePath(GetUserKeyFile(credentials, index + 1)),
394 false);
395 file_util::Delete(FilePath(GetUserSaltFile(credentials, index + 1)),
396 false);
397 return false;
398 }
399 }
400 }
401 return false;
402 }
403
404 bool Mount::MountIncognitoCryptohome() {
405 // Attempt to mount incognitofs
406 if (mount("incognitofs", home_dir_.c_str(), "tmpfs",
407 kDefaultMountOptions, "")) {
408 LOG(ERROR) << "Cryptohome mount failed: " << errno << " for incognitofs";
409 return false;
410 }
411 if (set_vault_ownership_) {
412 if (chown(home_dir_.c_str(), default_user_, default_group_)) {
413 LOG(ERROR) << "Couldn't change owner (" << default_user_ << ":"
414 << default_group_ << ") of incognitofs path: "
415 << home_dir_.c_str();
416 bool was_busy;
417 Unmount(home_dir_.c_str(), false, &was_busy);
418 return false;
419 }
420 }
421 CopySkeleton();
422 return true;
423 }
424
425 string Mount::GetUserDirectory(const Credentials& credentials) {
426 return StringPrintf("%s/%s",
427 shadow_root_.c_str(),
428 credentials.GetObfuscatedUsername(system_salt_).c_str());
429 }
430
431 string Mount::GetUserSaltFile(const Credentials& credentials, int index) {
432 return StringPrintf("%s/%s/master.%d.salt",
433 shadow_root_.c_str(),
434 credentials.GetObfuscatedUsername(system_salt_).c_str(),
435 index);
436 }
437
438 string Mount::GetUserKeyFile(const Credentials& credentials, int index) {
439 return StringPrintf("%s/%s/master.%d",
440 shadow_root_.c_str(),
441 credentials.GetObfuscatedUsername(system_salt_).c_str(),
442 index);
443 }
444
445 string Mount::GetUserVaultPath(const Credentials& credentials) {
446 return StringPrintf("%s/%s/vault",
447 shadow_root_.c_str(),
448 credentials.GetObfuscatedUsername(system_salt_).c_str());
449 }
450
451 void Mount::RecursiveCopy(const FilePath& destination,
452 const FilePath& source) {
453 file_util::FileEnumerator file_enumerator(source, false,
454 file_util::FileEnumerator::FILES);
455 FilePath next_path;
456 while (!(next_path = file_enumerator.Next()).empty()) {
457 FilePath file_name = next_path.BaseName();
458 FilePath destination_file = destination.Append(file_name);
459 file_util::CopyFile(next_path, destination_file);
460 if (set_vault_ownership_) {
461 if (chown(destination_file.value().c_str(), default_user_,
462 default_group_)) {
463 LOG(ERROR) << "Couldn't change owner (" << default_user_ << ":"
464 << default_group_ << ") of skeleton path: "
465 << destination_file.value().c_str();
466 }
467 }
468 }
469 file_util::FileEnumerator dir_enumerator(source, false,
470 file_util::FileEnumerator::DIRECTORIES);
471 while (!(next_path = dir_enumerator.Next()).empty()) {
472 FilePath dir_name = next_path.BaseName();
473 FilePath destination_dir = destination.Append(dir_name);
474 file_util::CreateDirectory(destination_dir);
475 if (set_vault_ownership_) {
476 if (chown(destination_dir.value().c_str(), default_user_,
477 default_group_)) {
478 LOG(ERROR) << "Couldn't change owner (" << default_user_ << ":"
479 << default_group_ << ") of skeleton path: "
480 << destination_dir.value().c_str();
481 }
482 }
483 RecursiveCopy(destination_dir, next_path);
484 }
485 }
486
487 void Mount::CopySkeletonForUser(const Credentials& credentials) {
488 if (IsCryptohomeMountedForUser(credentials)) {
489 CopySkeleton();
490 }
491 }
492
493 void Mount::CopySkeleton() {
494 RecursiveCopy(FilePath(home_dir_), FilePath(skel_source_));
495 }
496
497 void Mount::GetSecureRandom(unsigned char *rand, int length) const {
498 // TODO(fes): Get assistance from the TPM when it is available
499 // Seed the OpenSSL random number generator until it is happy
500 while (!RAND_status()) {
501 char buffer[256];
502 file_util::ReadFile(FilePath(entropy_source_), buffer, sizeof(buffer));
503 RAND_add(buffer, sizeof(buffer), sizeof(buffer));
504 }
505
506 // Have OpenSSL generate the random bytes
507 RAND_bytes(rand, length);
508 }
509
510 bool Mount::Unmount(const std::string& path, bool lazy, bool* was_busy) {
511 if (lazy) {
512 // TODO(fes): Place platform-specific calls in a separate class so that
513 // they can be mocked.
514 if (umount2(path.c_str(), MNT_DETACH)) {
515 if (was_busy) {
516 *was_busy = (errno == EBUSY);
517 }
518 return false;
519 }
520 } else {
521 if (umount(path.c_str())) {
522 if (was_busy) {
523 *was_busy = (errno == EBUSY);
524 }
525 return false;
526 }
527 }
528 if (was_busy) {
529 *was_busy = false;
530 }
531 return true;
532 }
533
534 bool Mount::UnwrapMasterKey(const FilePath& path,
535 const chromeos::Blob& passkey,
536 VaultKeyset* vault_keyset) {
537 // TODO(fes): Update this with openssl/tpm_engine or opencryptoki once
538 // available
539 // Load the encrypted master key
540 SecureBlob cipher_text;
541 if (!LoadFileBytes(path, cipher_text)) {
542 LOG(ERROR) << "Unable to read master key file";
543 return false;
544 }
545
546 unsigned int header_size = kOpenSSLMagic.length() + PKCS5_SALT_LEN;
547
548 if (cipher_text.size() < header_size) {
549 LOG(ERROR) << "Master key file too short";
550 return false;
551 }
552
553 // Grab the salt used in converting the passkey to a key (OpenSSL
554 // passkey-encrypted files have the format:
555 // Salted__<8-byte-salt><ciphertext>)
556 unsigned char salt[PKCS5_SALT_LEN];
557 memcpy(salt, &cipher_text[kOpenSSLMagic.length()], PKCS5_SALT_LEN);
558
559 cipher_text.erase(cipher_text.begin(), cipher_text.begin() + header_size);
560
561 unsigned char wrapper_key[EVP_MAX_KEY_LENGTH];
562 unsigned char iv[EVP_MAX_IV_LENGTH];
563
564 // Convert the passkey to a key encryption key
565 if (!EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, &passkey[0],
566 passkey.size(), 1, wrapper_key, iv)) {
567 LOG(ERROR) << "Failure converting bytes to key";
568 return false;
569 }
570
571 SecureBlob plain_text(cipher_text.size());
572
573 int final_size = 0;
574 int decrypt_size = plain_text.size();
575
576 // Do the actual decryption
577 EVP_CIPHER_CTX d_ctx;
578 EVP_CIPHER_CTX_init(&d_ctx);
579 EVP_DecryptInit_ex(&d_ctx, EVP_aes_256_ecb(), NULL, wrapper_key, iv);
580 if (!EVP_DecryptUpdate(&d_ctx, &plain_text[0], &decrypt_size,
581 &cipher_text[0],
582 cipher_text.size())) {
583 LOG(ERROR) << "DecryptUpdate failed";
584 return false;
585 }
586 if (!EVP_DecryptFinal_ex(&d_ctx, &plain_text[decrypt_size], &final_size)) {
587 unsigned long err = ERR_get_error();
588 ERR_load_ERR_strings();
589 ERR_load_crypto_strings();
590
591 LOG(ERROR) << "DecryptFinal Error: " << err
592 << ": " << ERR_lib_error_string(err)
593 << ", " << ERR_func_error_string(err)
594 << ", " << ERR_reason_error_string(err);
595
596 return false;
597 }
598 final_size += decrypt_size;
599
600 plain_text.resize(final_size);
601
602 if (plain_text.size() != VaultKeyset::SerializedSize()) {
603 LOG(ERROR) << "Plain text was not the correct size: " << plain_text.size()
604 << ", expected: " << VaultKeyset::SerializedSize();
605 return false;
606 }
607
608 (*vault_keyset).AssignBuffer(plain_text);
609
610 return true;
611 }
612
613 bool Mount::CreateMasterKey(const Credentials& credentials, int index) {
614 VaultKeyset vault_keyset;
615 vault_keyset.CreateRandom(*this);
616 return SaveVaultKeyset(credentials, vault_keyset, index);
617 }
618
619 bool Mount::SaveVaultKeyset(const Credentials& credentials,
620 const VaultKeyset& vault_keyset,
621 int index) {
622 unsigned char wrapper_key[EVP_MAX_KEY_LENGTH];
623 unsigned char iv[EVP_MAX_IV_LENGTH];
624 unsigned char salt[PKCS5_SALT_LEN];
625
626 // Create a salt for this master key
627 GetSecureRandom(salt, sizeof(salt));
628 SecureBlob user_salt = GetUserSalt(credentials, index, true);
629
630 // Convert the passkey to a passkey wrapper
631 SecureBlob passkey_wrapper =
632 PasskeyToWrapper(credentials.GetPasskey(), user_salt, 1);
633
634 // Convert the passkey wrapper to a key encryption key
635 if (!EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, &passkey_wrapper[0],
636 passkey_wrapper.size(), 1, wrapper_key, iv)) {
637 LOG(ERROR) << "Failure converting bytes to key";
638 return false;
639 }
640
641 SecureBlob keyset_blob = vault_keyset.ToBuffer();
642
643 // Store the salt and encrypt the master key
644 unsigned int header_size = kOpenSSLMagic.length() + PKCS5_SALT_LEN;
645 SecureBlob cipher_text(header_size
646 + keyset_blob.size()
647 + EVP_CIPHER_block_size(EVP_aes_256_ecb()));
648 memcpy(&cipher_text[0], kOpenSSLMagic.c_str(), kOpenSSLMagic.length());
649 memcpy(&cipher_text[kOpenSSLMagic.length()], salt, PKCS5_SALT_LEN);
650
651 int current_size = header_size;
652 int encrypt_size = 0;
653 EVP_CIPHER_CTX e_ctx;
654 EVP_CIPHER_CTX_init(&e_ctx);
655
656 // Encrypt the keyset
657 EVP_EncryptInit_ex(&e_ctx, EVP_aes_256_ecb(), NULL, wrapper_key, iv);
658 if (!EVP_EncryptUpdate(&e_ctx, &cipher_text[current_size], &encrypt_size,
659 &keyset_blob[0],
660 keyset_blob.size())) {
661 LOG(ERROR) << "EncryptUpdate failed";
662 return false;
663 }
664 current_size += encrypt_size;
665 encrypt_size = 0;
666
667 // Finish the encryption
668 if (!EVP_EncryptFinal_ex(&e_ctx, &cipher_text[current_size], &encrypt_size)) {
669 LOG(ERROR) << "EncryptFinal failed";
670 return false;
671 }
672 current_size += encrypt_size;
673 cipher_text.resize(current_size);
674
675 chromeos::SecureMemset(wrapper_key, sizeof(wrapper_key), 0);
676
677 // Save the master key
678 unsigned int data_written = file_util::WriteFile(
679 FilePath(GetUserKeyFile(credentials, index)),
680 reinterpret_cast<const char*>(&cipher_text[0]),
681 cipher_text.size());
682
683 if (data_written != cipher_text.size()) {
684 LOG(ERROR) << "Write to master key failed";
685 return false;
686 }
687 return true;
688 }
689
690 SecureBlob Mount::PasskeyToWrapper(const chromeos::Blob& passkey,
691 const chromeos::Blob& salt, int iters) {
692 // TODO(fes): Update this when TPM support is available, or use a memory-
693 // bound strengthening algorithm.
694 int update_length = passkey.size();
695 SecureBlob holder(CRYPTOHOME_MAX(update_length, SHA_DIGEST_LENGTH));
696 memcpy(&holder[0], &passkey[0], update_length);
697
698 // Repeatedly hash the user passkey and salt to generate the wrapper
699 for (int i = 0; i < iters; ++i) {
700 SHA_CTX ctx;
701 unsigned char md_value[SHA_DIGEST_LENGTH];
702
703 SHA1_Init(&ctx);
704 SHA1_Update(&ctx, &salt[0], salt.size());
705 SHA1_Update(&ctx, &holder[0], update_length);
706 SHA1_Final(md_value, &ctx);
707
708 memcpy(&holder[0], md_value, SHA_DIGEST_LENGTH);
709 update_length = SHA_DIGEST_LENGTH;
710 }
711
712 holder.resize(update_length);
713 SecureBlob wrapper(update_length * 2);
714 AsciiEncodeToBuffer(holder, reinterpret_cast<char*>(&wrapper[0]),
715 wrapper.size());
716 return wrapper;
717 }
718
719 SecureBlob Mount::GetSystemSalt() {
720 return system_salt_;
721 }
722
723 SecureBlob Mount::GetUserSalt(const Credentials& credentials, int index,
724 bool force) {
725 FilePath path(GetUserSaltFile(credentials, index));
726 return GetOrCreateSalt(path, CRYPTOHOME_DEFAULT_SALT_LENGTH, force);
727 }
728
729 SecureBlob Mount::GetOrCreateSalt(const FilePath& path, int length,
730 bool force) {
731 SecureBlob salt;
732 if (force || !file_util::PathExists(path)) {
733 // If this salt doesn't exist, automatically create it
734 salt.resize(length);
735 GetSecureRandom(&salt[0], salt.size());
736 int data_written = file_util::WriteFile(path,
737 reinterpret_cast<const char*>(&salt[0]),
738 length);
739 if (data_written != length) {
740 LOG(ERROR) << "Could not write user salt";
741 return SecureBlob();
742 }
743 } else {
744 // Otherwise just load the contents of the salt
745 int64 file_size;
746 if (!file_util::GetFileSize(path, &file_size)) {
747 LOG(ERROR) << "Could not get size of " << path.value();
748 return SecureBlob();
749 }
750 if (file_size > INT_MAX) {
751 LOG(ERROR) << "File " << path.value() << " is too large: " << file_size;
752 return SecureBlob();
753 }
754 salt.resize(file_size);
755 int data_read = file_util::ReadFile(path, reinterpret_cast<char*>(&salt[0]),
756 file_size);
757 if (data_read != file_size) {
758 LOG(ERROR) << "Could not read entire file " << file_size;
759 return SecureBlob();
760 }
761 }
762 return salt;
763 }
764
765 void Mount::AsciiEncodeToBuffer(const chromeos::Blob& blob, char* buffer,
766 int buffer_length) {
767 const char hex_chars[] = "0123456789abcdef";
768 int i = 0;
769 for (chromeos::Blob::const_iterator it = blob.begin();
770 it < blob.end() && (i + 1) < buffer_length; ++it) {
771 buffer[i++] = hex_chars[((*it) >> 4) & 0x0f];
772 buffer[i++] = hex_chars[(*it) & 0x0f];
773 }
774 if (i < buffer_length) {
775 buffer[i] = '\0';
776 }
777 }
778
779 bool Mount::AddKeyToEcryptfsKeyring(const VaultKeyset& vault_keyset,
780 string* key_signature,
781 string* fnek_signature) {
782 // Clear the user keyring
783 keyctl(KEYCTL_CLEAR, KEY_SPEC_USER_KEYRING);
784
785 // Add the FEK
786 *key_signature = chromeos::AsciiEncode(vault_keyset.FEK_SIG());
787 if (!PushVaultKey(vault_keyset.FEK(), *key_signature,
788 vault_keyset.FEK_SALT())) {
789 LOG(ERROR) << "Couldn't add ecryptfs key to keyring";
790 return false;
791 }
792
793 // Add the FNEK
794 *fnek_signature = chromeos::AsciiEncode(vault_keyset.FNEK_SIG());
795 if (!PushVaultKey(vault_keyset.FNEK(), *fnek_signature,
796 vault_keyset.FNEK_SALT())) {
797 LOG(ERROR) << "Couldn't add ecryptfs fnek key to keyring";
798 return false;
799 }
800
801 return true;
802 }
803
804 bool Mount::PushVaultKey(const SecureBlob& key, const std::string& key_sig,
805 const SecureBlob& salt) {
806 DCHECK(key.size() == ECRYPTFS_MAX_KEY_BYTES);
807 DCHECK(key_sig.length() == (ECRYPTFS_SIG_SIZE * 2));
808 DCHECK(salt.size() == ECRYPTFS_SALT_SIZE);
809
810 struct ecryptfs_auth_tok auth_token;
811
812 generate_payload(&auth_token, const_cast<char*>(key_sig.c_str()),
813 const_cast<char*>(reinterpret_cast<const char*>(&salt[0])),
814 const_cast<char*>(reinterpret_cast<const char*>(&key[0])));
815
816 if (ecryptfs_add_auth_tok_to_keyring(&auth_token,
817 const_cast<char*>(key_sig.c_str())) < 0) {
818 LOG(ERROR) << "PushVaultKey failed";
819 }
820
821 return true;
822 }
823
824 bool Mount::LoadFileBytes(const FilePath& path,
825 SecureBlob& blob) {
826 int64 file_size;
827 if (!file_util::PathExists(path)) {
828 LOG(ERROR) << path.value() << " does not exist!";
829 return false;
830 }
831 if (!file_util::GetFileSize(path, &file_size)) {
832 LOG(ERROR) << "Could not get size of " << path.value();
833 return false;
834 }
835 if (file_size > INT_MAX) {
836 LOG(ERROR) << "File " << path.value() << " is too large: " << file_size;
837 return false;
838 }
839 SecureBlob buf(file_size);
840 int data_read = file_util::ReadFile(path, reinterpret_cast<char*>(&buf[0]),
841 file_size);
842 // Cast is okay because of comparison to INT_MAX above
843 if (data_read != static_cast<int>(file_size)) {
844 LOG(ERROR) << "Could not read entire file " << file_size;
845 return false;
846 }
847 blob.swap(buf);
848 return true;
849 }
850
851 bool Mount::TerminatePidsWithOpenFiles(const std::string& path, bool hard) {
852 std::vector<pid_t> pids = LookForOpenFiles(path);
853 for (std::vector<pid_t>::iterator it = pids.begin(); it != pids.end(); it++) {
854 pid_t pid = static_cast<pid_t>(*it);
855 if (pid != getpid()) {
856 if (hard) {
857 kill(pid, SIGTERM);
858 } else {
859 kill(pid, SIGKILL);
860 }
861 }
862 }
863 return (pids.size() != 0);
864 }
865
866 std::vector<pid_t> Mount::LookForOpenFiles(const std::string& path_in) {
867 // Make sure that if we get a directory, it has a trailing separator
868 FilePath file_path(path_in);
869 file_util::EnsureEndsWithSeparator(&file_path);
870 std::string path = file_path.value();
871
872 std::vector<pid_t> pids;
873
874 // Open /proc
875 DIR* proc_dir = opendir(kProcDir.c_str());
876
877 if (!proc_dir) {
878 return pids;
879 }
880
881 int linkbuf_length = path.length();
882 std::vector<char> linkbuf(linkbuf_length);
883
884 // List PIDs in /proc
885 while (struct dirent* pid_dirent = readdir(proc_dir)) {
886 pid_t pid = static_cast<pid_t>(atoi(pid_dirent->d_name));
887 // Ignore PID 1 and errors
888 if (pid <= 1) {
889 continue;
890 }
891 // Open /proc/<pid>/fd
892 std::string fd_dirname = StringPrintf("%s/%d/fd", kProcDir.c_str(), pid);
893 DIR* fd_dir = opendir(fd_dirname.c_str());
894 if (!fd_dir) {
895 continue;
896 }
897
898 // List open file descriptors
899 while (struct dirent* fd_dirent = readdir(fd_dir)) {
mschilder 2010/05/27 04:20:18 readdir_r()?
900 std::string fd_path = StringPrintf("%s/%s", fd_dirname.c_str(),
901 fd_dirent->d_name);
902 ssize_t link_length = readlink(fd_path.c_str(), &linkbuf[0],
903 linkbuf.size());
904 if (link_length > 0) {
905 std::string open_file(&linkbuf[0], link_length);
906 if (open_file.length() >= path.length()) {
907 if (open_file.substr(0, path.length()).compare(path) == 0) {
908 pids.push_back(pid);
909 break;
910 }
911 }
912 }
913 }
914
915 closedir(fd_dir);
916 }
917
918 closedir(proc_dir);
919
920 return pids;
921 }
922
923 bool Mount::TerminatePidsForUser(const uid_t uid, bool hard) {
924 std::vector<pid_t> pids = GetPidsForUser(uid);
925 for (std::vector<pid_t>::iterator it = pids.begin(); it != pids.end(); it++) {
926 pid_t pid = static_cast<pid_t>(*it);
927 if (pid != getpid()) {
928 if (hard) {
929 kill(pid, SIGTERM);
930 } else {
931 kill(pid, SIGKILL);
932 }
933 }
934 }
935 return (pids.size() != 0);
936 }
937
938 // TODO(fes): Pull this into a separate helper class
939 std::vector<pid_t> Mount::GetPidsForUser(uid_t uid) {
940 std::vector<pid_t> pids;
941
942 // Open /proc
943 DIR* proc_dir = opendir(kProcDir.c_str());
944
945 if (!proc_dir) {
946 return pids;
947 }
948
949 // List PIDs in /proc
950 while (struct dirent* pid_dirent = readdir(proc_dir)) {
951 pid_t pid = static_cast<pid_t>(atoi(pid_dirent->d_name));
952 if (pid <= 0) {
953 continue;
954 }
955 // Open /proc/<pid>/status
956 std::string stat_path = StringPrintf("%s/%d/status", kProcDir.c_str(),
957 pid);
958 string contents;
959 if (!file_util::ReadFileToString(FilePath(stat_path), &contents)) {
960 continue;
961 }
962
963 size_t uid_loc = contents.find("Uid:");
964 if (!uid_loc) {
965 continue;
966 }
967 uid_loc += 4;
968
969 size_t uid_end = contents.find("\n", uid_loc);
970 if (!uid_end) {
971 continue;
972 }
973
974 contents = contents.substr(uid_loc, uid_end - uid_loc);
975
976 std::vector<std::string> tokens;
977 Tokenize(contents, " \t", &tokens);
978
979 for (std::vector<std::string>::iterator it = tokens.begin();
980 it != tokens.end(); it++) {
981 std::string& value = *it;
982 if (value.length() == 0) {
983 continue;
984 }
985 uid_t check_uid = static_cast<uid_t>(atoi(value.c_str()));
986 if (check_uid == uid) {
987 pids.push_back(pid);
988 break;
989 }
990 }
991 }
992
993 closedir(proc_dir);
994
995 return pids;
996 }
997
998 } // cryptohome
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698