| OLD | NEW |
| 1 // Copyright (c) 2009-2010 The Chromium OS Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // Contains the implementation of class Mount | 5 // Contains the implementation of class Mount |
| 6 | 6 |
| 7 // TODO(fes): Use correct ordering of file includes once the platform-specific | 7 #include "mount.h" |
| 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 | 8 |
| 20 extern "C" { | |
| 21 #include <ecryptfs.h> | |
| 22 #include <keyutils.h> | |
| 23 } | |
| 24 #include <dirent.h> | |
| 25 #include <errno.h> | 9 #include <errno.h> |
| 26 #include <openssl/err.h> | 10 |
| 27 #include <openssl/evp.h> | 11 #include <base/file_util.h> |
| 28 #include <openssl/rand.h> | 12 #include <base/logging.h> |
| 29 #include <openssl/sha.h> | 13 #include <base/platform_thread.h> |
| 30 #include <pwd.h> | 14 #include <base/time.h> |
| 31 #include <signal.h> | 15 #include <base/string_util.h> |
| 32 #include <sys/mount.h> | 16 #include <chromeos/utility.h> |
| 33 #include <sys/stat.h> | 17 |
| 34 #include <sys/types.h> | 18 #include "crypto.h" |
| 19 #include "cryptohome_common.h" |
| 20 #include "platform.h" |
| 21 #include "username_passkey.h" |
| 35 | 22 |
| 36 using std::string; | 23 using std::string; |
| 37 | 24 |
| 38 namespace cryptohome { | 25 namespace cryptohome { |
| 39 | 26 |
| 40 const string kDefaultEntropySource = "/dev/urandom"; | 27 const std::string kDefaultHomeDir = "/home/chronos/user"; |
| 41 const string kDefaultHomeDir = "/home/chronos/user"; | 28 const std::string kDefaultShadowRoot = "/home/.shadow"; |
| 42 const int kDefaultMountOptions = MS_NOEXEC | MS_NOSUID | MS_NODEV; | 29 const std::string kDefaultSharedUser = "chronos"; |
| 43 const string kDefaultShadowRoot = "/home/.shadow"; | 30 const std::string kDefaultSkeletonSource = "/etc/skel"; |
| 44 const string kDefaultSharedUser = "chronos"; | 31 // TODO(fes): Remove once UI for BWSI switches to MountGuest() |
| 45 const string kDefaultSkeletonSource = "/etc/skel"; | 32 const std::string kIncognitoUser = "incognito"; |
| 46 const string kIncognitoUser = "incognito"; | |
| 47 const string kMtab = "/etc/mtab"; | |
| 48 const string kOpenSSLMagic = "Salted__"; | |
| 49 const std::string kProcDir = "/proc"; | |
| 50 | 33 |
| 51 Mount::Mount() | 34 Mount::Mount() |
| 52 : default_user_(-1), | 35 : default_user_(-1), |
| 53 default_group_(-1), | 36 default_group_(-1), |
| 54 default_username_(kDefaultSharedUser), | 37 default_username_(kDefaultSharedUser), |
| 55 entropy_source_(kDefaultEntropySource), | |
| 56 home_dir_(kDefaultHomeDir), | 38 home_dir_(kDefaultHomeDir), |
| 57 shadow_root_(kDefaultShadowRoot), | 39 shadow_root_(kDefaultShadowRoot), |
| 58 skel_source_(kDefaultSkeletonSource), | 40 skel_source_(kDefaultSkeletonSource), |
| 59 system_salt_(), | 41 system_salt_(), |
| 60 set_vault_ownership_(true) { | 42 set_vault_ownership_(true), |
| 61 } | 43 default_crypto_(new Crypto()), |
| 62 | 44 crypto_(default_crypto_.get()), |
| 63 Mount::Mount(const std::string& username, const std::string& entropy_source, | 45 default_platform_(new Platform()), |
| 64 const std::string& home_dir, const std::string& shadow_root, | 46 platform_(default_platform_.get()) { |
| 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 } | 47 } |
| 76 | 48 |
| 77 Mount::~Mount() { | 49 Mount::~Mount() { |
| 78 } | 50 } |
| 79 | 51 |
| 80 bool Mount::Init() { | 52 bool Mount::Init() { |
| 81 bool result = true; | 53 bool result = true; |
| 82 | 54 |
| 83 // Load the passwd entry for the shared user | 55 // Get the user id and group id of the default user |
| 84 struct passwd* user_info = getpwnam(default_username_.c_str()); | 56 if (!platform_->GetUserId(default_username_, &default_user_, |
| 85 if (user_info) { | 57 &default_group_)) { |
| 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; | 58 result = false; |
| 91 } | 59 } |
| 92 | 60 |
| 93 // One-time load of the global system salt (used in generating username | 61 // One-time load of the global system salt (used in generating username |
| 94 // hashes) | 62 // hashes) |
| 95 if (!LoadFileBytes(FilePath(StringPrintf("%s/salt", shadow_root_.c_str())), | 63 if (!LoadFileBytes(FilePath(StringPrintf("%s/salt", shadow_root_.c_str())), |
| 96 system_salt_)) { | 64 &system_salt_)) { |
| 97 result = false; | 65 result = false; |
| 98 } | 66 } |
| 99 | 67 |
| 100 return result; | 68 return result; |
| 101 } | 69 } |
| 102 | 70 |
| 103 bool Mount::EnsureCryptohome(const Credentials& credentials, bool* created) { | 71 bool Mount::EnsureCryptohome(const Credentials& credentials, bool* created) { |
| 104 // If the user has an old-style cryptohome, delete it | 72 // If the user has an old-style cryptohome, delete it |
| 105 FilePath old_image_path(StringPrintf("%s/image", | 73 FilePath old_image_path(StringPrintf("%s/image", |
| 106 GetUserDirectory(credentials).c_str())); | 74 GetUserDirectory(credentials).c_str())); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 119 return result; | 87 return result; |
| 120 } | 88 } |
| 121 if (created) { | 89 if (created) { |
| 122 *created = false; | 90 *created = false; |
| 123 } | 91 } |
| 124 return true; | 92 return true; |
| 125 } | 93 } |
| 126 | 94 |
| 127 bool Mount::MountCryptohome(const Credentials& credentials, int index, | 95 bool Mount::MountCryptohome(const Credentials& credentials, int index, |
| 128 MountError* mount_error) { | 96 MountError* mount_error) { |
| 129 std::string username = credentials.GetFullUsername(); | 97 std::string username = credentials.GetFullUsernameString(); |
| 130 if (username.compare(kIncognitoUser) == 0) { | 98 if (username.compare(kIncognitoUser) == 0) { |
| 131 // TODO(fes): Have incognito set error conditions? | 99 // TODO(fes): Have guest set error conditions? |
| 132 if (mount_error) { | 100 if (mount_error) { |
| 133 *mount_error = MOUNT_ERROR_NONE; | 101 *mount_error = MOUNT_ERROR_NONE; |
| 134 } | 102 } |
| 135 return MountIncognitoCryptohome(); | 103 return MountGuestCryptohome(); |
| 136 } | 104 } |
| 137 | 105 |
| 138 bool created = false; | 106 bool created = false; |
| 139 if (!EnsureCryptohome(credentials, &created)) { | 107 if (!EnsureCryptohome(credentials, &created)) { |
| 140 LOG(ERROR) << "Error creating cryptohome."; | 108 LOG(ERROR) << "Error creating cryptohome."; |
| 141 if (mount_error) { | 109 if (mount_error) { |
| 142 *mount_error = MOUNT_ERROR_FATAL; | 110 *mount_error = MOUNT_ERROR_FATAL; |
| 143 } | 111 } |
| 144 return false; | 112 return false; |
| 145 } | 113 } |
| 146 | 114 |
| 147 FilePath user_key_file(GetUserKeyFile(credentials, index)); | 115 // Attempt to unwrap the vault keyset with the specified credentials |
| 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; | 116 VaultKeyset vault_keyset; |
| 158 bool mount_result = UnwrapMasterKey(user_key_file, passkey_wrapper, | 117 if (!UnwrapVaultKeyset(credentials, index, &vault_keyset, mount_error)) { |
| 159 &vault_keyset); | |
| 160 | |
| 161 if (!mount_result) { | |
| 162 if (mount_error) { | |
| 163 *mount_error = Mount::MOUNT_ERROR_KEY_FAILURE; | |
| 164 } | |
| 165 return false; | 118 return false; |
| 166 } | 119 } |
| 167 | 120 |
| 121 crypto_->ClearKeyset(); |
| 122 |
| 168 // Add the decrypted key to the keyring so that ecryptfs can use it | 123 // Add the decrypted key to the keyring so that ecryptfs can use it |
| 169 string key_signature, fnek_signature; | 124 string key_signature, fnek_signature; |
| 170 if (!AddKeyToEcryptfsKeyring(vault_keyset, &key_signature, &fnek_signature)) { | 125 if (!crypto_->AddKeyset(vault_keyset, &key_signature, &fnek_signature)) { |
| 171 LOG(ERROR) << "Cryptohome mount failed because of keyring failure."; | 126 LOG(INFO) << "Cryptohome mount failed because of keyring failure."; |
| 172 if (mount_error) { | 127 if (mount_error) { |
| 173 *mount_error = MOUNT_ERROR_FATAL; | 128 *mount_error = MOUNT_ERROR_FATAL; |
| 174 } | 129 } |
| 175 return false; | 130 return false; |
| 176 } | 131 } |
| 177 | 132 |
| 178 // Specify the ecryptfs options for mounting the user's cryptohome | 133 // Specify the ecryptfs options for mounting the user's cryptohome |
| 179 string ecryptfs_options = StringPrintf("ecryptfs_cipher=aes" | 134 string ecryptfs_options = StringPrintf("ecryptfs_cipher=aes" |
| 180 ",ecryptfs_key_bytes=%d" | 135 ",ecryptfs_key_bytes=%d" |
| 181 ",ecryptfs_fnek_sig=%s,ecryptfs_sig=%s" | 136 ",ecryptfs_fnek_sig=%s,ecryptfs_sig=%s" |
| 182 ",ecryptfs_unlink_sigs", | 137 ",ecryptfs_unlink_sigs", |
| 183 CRYPTOHOME_AES_KEY_BYTES, | 138 CRYPTOHOME_AES_KEY_BYTES, |
| 184 fnek_signature.c_str(), | 139 fnek_signature.c_str(), |
| 185 key_signature.c_str()); | 140 key_signature.c_str()); |
| 186 | 141 |
| 187 // TODO(fes): mount(1) -> mount(2): how to issue "user" | 142 // Mount cryptohome |
| 188 string vault_path = GetUserVaultPath(credentials); | 143 string vault_path = GetUserVaultPath(credentials); |
| 189 // Attempt to mount the user's cryptohome | 144 if (!platform_->Mount(vault_path, home_dir_, "ecryptfs", ecryptfs_options)) { |
| 190 if (mount(vault_path.c_str(), home_dir_.c_str(), | 145 LOG(INFO) << "Cryptohome mount failed: " << errno << " for vault: " |
| 191 "ecryptfs", kDefaultMountOptions, ecryptfs_options.c_str())) { | |
| 192 LOG(ERROR) << "Cryptohome mount failed: " << errno << " for vault: " | |
| 193 << vault_path; | 146 << vault_path; |
| 194 if (mount_error) { | 147 if (mount_error) { |
| 195 *mount_error = MOUNT_ERROR_FATAL; | 148 *mount_error = MOUNT_ERROR_FATAL; |
| 196 } | 149 } |
| 197 return false; | 150 return false; |
| 198 } | 151 } |
| 199 | 152 |
| 200 if (created) { | 153 if (created) { |
| 201 CopySkeletonForUser(credentials); | 154 CopySkeletonForUser(credentials); |
| 202 } | 155 } |
| 203 | 156 |
| 204 if (mount_error) { | 157 if (mount_error) { |
| 205 *mount_error = MOUNT_ERROR_NONE; | 158 *mount_error = MOUNT_ERROR_NONE; |
| 206 } | 159 } |
| 207 return true; | 160 return true; |
| 208 } | 161 } |
| 209 | 162 |
| 210 bool Mount::UnmountCryptohome() { | 163 bool Mount::UnmountCryptohome() { |
| 211 // Try an immediate unmount | 164 // Try an immediate unmount |
| 212 bool was_busy; | 165 bool was_busy; |
| 213 if (!Unmount(home_dir_.c_str(), false, &was_busy)) { | 166 if (!platform_->Unmount(home_dir_, false, &was_busy)) { |
| 214 // If the unmount fails, do a lazy unmount | |
| 215 Unmount(home_dir_.c_str(), true, NULL); | |
| 216 if (was_busy) { | 167 if (was_busy) { |
| 217 // Signal processes to close | 168 // Signal processes to close |
| 218 if (TerminatePidsWithOpenFiles(home_dir_, false)) { | 169 if (platform_->TerminatePidsWithOpenFiles(home_dir_, false)) { |
| 219 // Then we had to send a SIGINT to some processes with open files. Give | 170 // Then we had to send a SIGINT to some processes with open files. Give |
| 220 // a "grace" period before killing with a SIGKILL. | 171 // a "grace" period before killing with a SIGKILL. |
| 221 // TODO(fes): This isn't ideal, nor is it accurate (a static sleep can't | 172 // 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 | 173 // 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. | 174 // namespace-based mounts, then we can get away from this construct. |
| 224 PlatformThread::Sleep(100); | 175 PlatformThread::Sleep(100); |
| 225 sync(); | 176 sync(); |
| 226 if (TerminatePidsWithOpenFiles(home_dir_, true)) { | 177 if (platform_->TerminatePidsWithOpenFiles(home_dir_, true)) { |
| 227 PlatformThread::Sleep(100); | 178 PlatformThread::Sleep(100); |
| 228 } | 179 } |
| 229 } | 180 } |
| 230 } | 181 } |
| 182 // Failed to unmount immediately, do a lazy unmount |
| 183 platform_->Unmount(home_dir_, true, NULL); |
| 231 sync(); | 184 sync(); |
| 232 // TODO(fes): This should return an error condition if it is still mounted. | 185 // 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 | 186 // Right now it doesn't, since it should get cleaned up outside of |
| 234 // cryptohome since we failed over to a lazy unmount | 187 // cryptohome since we failed over to a lazy unmount |
| 235 } | 188 } |
| 236 | 189 |
| 237 // TODO(fes): Do we need to keep this behavior? | 190 // TODO(fes): Do we need to keep this behavior? |
| 238 //TerminatePidsForUser(default_user_, true); | 191 //TerminatePidsForUser(default_user_, true); |
| 239 | 192 |
| 240 // Clear the user keyring if the unmount was successful | 193 // Clear the user keyring if the unmount was successful |
| 241 keyctl(KEYCTL_CLEAR, KEY_SPEC_USER_KEYRING); | 194 crypto_->ClearKeyset(); |
| 242 | 195 |
| 243 return true; | 196 return true; |
| 244 } | 197 } |
| 245 | 198 |
| 246 bool Mount::RemoveCryptohome(const Credentials& credentials) { | 199 bool Mount::RemoveCryptohome(const Credentials& credentials) { |
| 247 std::string user_dir = GetUserDirectory(credentials); | 200 std::string user_dir = GetUserDirectory(credentials); |
| 248 CHECK(user_dir.length() > (shadow_root_.length() + 1)); | 201 CHECK(user_dir.length() > (shadow_root_.length() + 1)); |
| 249 | 202 |
| 250 return file_util::Delete(FilePath(user_dir), true); | 203 return file_util::Delete(FilePath(user_dir), true); |
| 251 } | 204 } |
| 252 | 205 |
| 253 bool Mount::IsCryptohomeMounted() { | 206 bool Mount::IsCryptohomeMounted() { |
| 254 // Trivial string match from /etc/mtab to see if the cryptohome mount point is | 207 return platform_->IsDirectoryMounted(home_dir_); |
| 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 } | 208 } |
| 266 | 209 |
| 267 bool Mount::IsCryptohomeMountedForUser(const Credentials& credentials) { | 210 bool Mount::IsCryptohomeMountedForUser(const Credentials& credentials) { |
| 268 // Trivial string match from /etc/mtab to see if the cryptohome mount point | 211 return platform_->IsDirectoryMountedWith(home_dir_, |
| 269 // and the user's vault path are present. Assumes this user is mounted if it | 212 GetUserVaultPath(credentials)); |
| 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 } | 213 } |
| 284 | 214 |
| 285 bool Mount::CreateCryptohome(const Credentials& credentials, int index) { | 215 bool Mount::CreateCryptohome(const Credentials& credentials, int index) { |
| 286 // Save the current umask | 216 int original_mask = platform_->SetMask(kDefaultUmask); |
| 287 mode_t original_mask = umask(S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | |
| 288 | S_IXOTH); | |
| 289 | 217 |
| 290 // Create the user's entry in the shadow root | 218 // Create the user's entry in the shadow root |
| 291 FilePath user_dir(GetUserDirectory(credentials)); | 219 FilePath user_dir(GetUserDirectory(credentials)); |
| 292 file_util::CreateDirectory(user_dir); | 220 file_util::CreateDirectory(user_dir); |
| 293 | 221 |
| 294 // Generates a new master key and salt at the given index | 222 // Generates a new master key and salt at the given index |
| 295 if (!CreateMasterKey(credentials, index)) { | 223 if (!CreateMasterKey(credentials, index)) { |
| 296 umask(original_mask); | 224 platform_->SetMask(original_mask); |
| 297 return false; | 225 return false; |
| 298 } | 226 } |
| 299 | 227 |
| 300 // Create the user's path and set the proper ownership | 228 // Create the user's path and set the proper ownership |
| 301 FilePath vault_path(GetUserVaultPath(credentials)); | 229 std::string vault_path = GetUserVaultPath(credentials); |
| 302 if (!file_util::CreateDirectory(vault_path)) { | 230 if (!file_util::CreateDirectory(FilePath(vault_path))) { |
| 303 LOG(ERROR) << "Couldn't create vault path: " << vault_path.value().c_str(); | 231 LOG(ERROR) << "Couldn't create vault path: " << vault_path.c_str(); |
| 304 umask(original_mask); | 232 platform_->SetMask(original_mask); |
| 305 return false; | 233 return false; |
| 306 } | 234 } |
| 307 if (set_vault_ownership_) { | 235 if (set_vault_ownership_) { |
| 308 // TODO(fes): Move platform-specific calls to a separate class | 236 if (!platform_->SetOwnership(vault_path, default_user_, default_group_)) { |
| 309 if (chown(vault_path.value().c_str(), default_user_, default_group_)) { | |
| 310 LOG(ERROR) << "Couldn't change owner (" << default_user_ << ":" | 237 LOG(ERROR) << "Couldn't change owner (" << default_user_ << ":" |
| 311 << default_group_ << ") of vault path: " | 238 << default_group_ << ") of vault path: " |
| 312 << vault_path.value().c_str(); | 239 << vault_path.c_str(); |
| 313 umask(original_mask); | 240 platform_->SetMask(original_mask); |
| 314 return false; | 241 return false; |
| 315 } | 242 } |
| 316 } | 243 } |
| 317 | 244 |
| 318 // Restore the umask | 245 // Restore the umask |
| 319 umask(original_mask); | 246 platform_->SetMask(original_mask); |
| 320 return true; | 247 return true; |
| 321 } | 248 } |
| 322 | 249 |
| 323 bool Mount::TestCredentials(const Credentials& credentials) { | 250 bool Mount::TestCredentials(const Credentials& credentials) { |
| 324 // Iterate over the keys in the user's entry in the shadow root to see if | 251 // Iterate over the keys in the user's entry in the shadow root to see if |
| 325 // these credentials successfully decrypt any | 252 // these credentials successfully decrypt any |
| 326 for (int index = 0; /* loop forever */; index++) { | 253 for (int index = 0; /* loop forever */; index++) { |
| 327 FilePath user_key_file(GetUserKeyFile(credentials, index)); | 254 MountError mount_error; |
| 328 if (!file_util::AbsolutePath(&user_key_file)) { | 255 VaultKeyset vault_keyset; |
| 256 if (UnwrapVaultKeyset(credentials, index, &vault_keyset, &mount_error)) { |
| 257 return true; |
| 258 } else if (mount_error != Mount::MOUNT_ERROR_KEY_FAILURE) { |
| 329 break; | 259 break; |
| 330 } | 260 } |
| 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 } | 261 } |
| 342 return false; | 262 return false; |
| 343 } | 263 } |
| 344 | 264 |
| 265 bool Mount::UnwrapVaultKeyset(const Credentials& credentials, int index, |
| 266 VaultKeyset* vault_keyset, MountError* error) { |
| 267 // Generate the passkey wrapper (key encryption key) |
| 268 SecureBlob user_salt; |
| 269 GetUserSalt(credentials, index, false, &user_salt); |
| 270 if (user_salt.size() == 0) { |
| 271 if (error) { |
| 272 *error = MOUNT_ERROR_FATAL; |
| 273 } |
| 274 return false; |
| 275 } |
| 276 SecureBlob passkey; |
| 277 credentials.GetPasskey(&passkey); |
| 278 SecureBlob passkey_wrapper; |
| 279 crypto_->PasskeyToWrapper(passkey, user_salt, 1, &passkey_wrapper); |
| 280 |
| 281 // Load the encrypted keyset |
| 282 FilePath user_key_file(GetUserKeyFile(credentials, index)); |
| 283 if (!file_util::PathExists(user_key_file)) { |
| 284 if (error) { |
| 285 *error = MOUNT_ERROR_NO_SUCH_FILE; |
| 286 } |
| 287 return false; |
| 288 } |
| 289 SecureBlob cipher_text; |
| 290 if (!LoadFileBytes(user_key_file, &cipher_text)) { |
| 291 if (error) { |
| 292 *error = MOUNT_ERROR_FATAL; |
| 293 } |
| 294 return false; |
| 295 } |
| 296 |
| 297 // Attempt to unwrap the master key at the index with the passkey wrapper |
| 298 if (!crypto_->UnwrapVaultKeyset(cipher_text, passkey_wrapper, vault_keyset)) { |
| 299 if (error) { |
| 300 *error = Mount::MOUNT_ERROR_KEY_FAILURE; |
| 301 } |
| 302 return false; |
| 303 } |
| 304 |
| 305 return true; |
| 306 } |
| 307 |
| 345 bool Mount::MigratePasskey(const Credentials& credentials, | 308 bool Mount::MigratePasskey(const Credentials& credentials, |
| 346 const char* old_key) { | 309 const char* old_key) { |
| 347 // Iterate over the keys in the user's entry in the shadow root to see if | 310 // Iterate over the keys in the user's entry in the shadow root to see if |
| 348 // these credentials successfully decrypt any | 311 // these credentials successfully decrypt any |
| 349 std::string username = credentials.GetFullUsername(); | 312 std::string username = credentials.GetFullUsernameString(); |
| 350 UsernamePasskey old_credentials(username.c_str(), username.length(), | 313 UsernamePasskey old_credentials(username.c_str(), |
| 351 old_key, strlen(old_key)); | 314 SecureBlob(old_key, strlen(old_key))); |
| 352 for (int index = 0; /* loop forever */; index++) { | 315 for (int index = 0; /* loop forever */; index++) { |
| 353 FilePath user_key_file(GetUserKeyFile(old_credentials, index)); | 316 // Attempt to unwrap the vault keyset with the specified credentials |
| 354 if (!file_util::AbsolutePath(&user_key_file)) { | 317 MountError mount_error; |
| 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; | 318 VaultKeyset vault_keyset; |
| 364 if (UnwrapMasterKey(user_key_file, passkey_wrapper, &vault_keyset)) { | 319 if (UnwrapVaultKeyset(old_credentials, index, &vault_keyset, |
| 320 &mount_error)) { |
| 365 // Save to the next key index first so that if there is a failure, we | 321 // Save to the next key index first so that if there is a failure, we |
| 366 // don't delete the existing working keyset. | 322 // don't delete the existing working keyset. |
| 367 bool save_result = SaveVaultKeyset(credentials, vault_keyset, index + 1); | 323 bool save_result = SaveVaultKeyset(credentials, vault_keyset, index + 1); |
| 368 if (save_result) { | 324 if (save_result) { |
| 369 // Saved okay, move to index 0. | 325 // Saved okay, move to index 0. |
| 370 if (!file_util::ReplaceFile(FilePath(GetUserKeyFile(credentials, | 326 if (!file_util::ReplaceFile(FilePath(GetUserKeyFile(credentials, |
| 371 index + 1)), | 327 index + 1)), |
| 372 FilePath(GetUserKeyFile(credentials, 0)))) { | 328 FilePath(GetUserKeyFile(credentials, 0)))) { |
| 373 return false; | 329 return false; |
| 374 } | 330 } |
| (...skipping 14 matching lines...) Expand all Loading... |
| 389 } | 345 } |
| 390 return true; | 346 return true; |
| 391 } else { | 347 } else { |
| 392 // Couldn't save the vault keyset, delete it | 348 // Couldn't save the vault keyset, delete it |
| 393 file_util::Delete(FilePath(GetUserKeyFile(credentials, index + 1)), | 349 file_util::Delete(FilePath(GetUserKeyFile(credentials, index + 1)), |
| 394 false); | 350 false); |
| 395 file_util::Delete(FilePath(GetUserSaltFile(credentials, index + 1)), | 351 file_util::Delete(FilePath(GetUserSaltFile(credentials, index + 1)), |
| 396 false); | 352 false); |
| 397 return false; | 353 return false; |
| 398 } | 354 } |
| 355 } else if (mount_error != Mount::MOUNT_ERROR_KEY_FAILURE) { |
| 356 break; |
| 399 } | 357 } |
| 400 } | 358 } |
| 401 return false; | 359 return false; |
| 402 } | 360 } |
| 403 | 361 |
| 404 bool Mount::MountIncognitoCryptohome() { | 362 bool Mount::MountGuestCryptohome() { |
| 405 // Attempt to mount incognitofs | 363 // Attempt to mount guestfs |
| 406 if (mount("incognitofs", home_dir_.c_str(), "tmpfs", | 364 if (!platform_->Mount("guestfs", home_dir_, "tmpfs", "")) { |
| 407 kDefaultMountOptions, "")) { | 365 LOG(ERROR) << "Cryptohome mount failed: " << errno << " for guestfs"; |
| 408 LOG(ERROR) << "Cryptohome mount failed: " << errno << " for incognitofs"; | |
| 409 return false; | 366 return false; |
| 410 } | 367 } |
| 411 if (set_vault_ownership_) { | 368 if (set_vault_ownership_) { |
| 412 if (chown(home_dir_.c_str(), default_user_, default_group_)) { | 369 if (!platform_->SetOwnership(home_dir_, default_user_, default_group_)) { |
| 413 LOG(ERROR) << "Couldn't change owner (" << default_user_ << ":" | 370 LOG(ERROR) << "Couldn't change owner (" << default_user_ << ":" |
| 414 << default_group_ << ") of incognitofs path: " | 371 << default_group_ << ") of guestfs path: " |
| 415 << home_dir_.c_str(); | 372 << home_dir_.c_str(); |
| 416 bool was_busy; | 373 bool was_busy; |
| 417 Unmount(home_dir_.c_str(), false, &was_busy); | 374 platform_->Unmount(home_dir_.c_str(), false, &was_busy); |
| 418 return false; | 375 return false; |
| 419 } | 376 } |
| 420 } | 377 } |
| 421 CopySkeleton(); | 378 CopySkeleton(); |
| 422 return true; | 379 return true; |
| 423 } | 380 } |
| 424 | 381 |
| 425 string Mount::GetUserDirectory(const Credentials& credentials) { | 382 string Mount::GetUserDirectory(const Credentials& credentials) { |
| 426 return StringPrintf("%s/%s", | 383 return StringPrintf("%s/%s", |
| 427 shadow_root_.c_str(), | 384 shadow_root_.c_str(), |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 488 if (IsCryptohomeMountedForUser(credentials)) { | 445 if (IsCryptohomeMountedForUser(credentials)) { |
| 489 CopySkeleton(); | 446 CopySkeleton(); |
| 490 } | 447 } |
| 491 } | 448 } |
| 492 | 449 |
| 493 void Mount::CopySkeleton() { | 450 void Mount::CopySkeleton() { |
| 494 RecursiveCopy(FilePath(home_dir_), FilePath(skel_source_)); | 451 RecursiveCopy(FilePath(home_dir_), FilePath(skel_source_)); |
| 495 } | 452 } |
| 496 | 453 |
| 497 void Mount::GetSecureRandom(unsigned char *rand, int length) const { | 454 void Mount::GetSecureRandom(unsigned char *rand, int length) const { |
| 498 // TODO(fes): Get assistance from the TPM when it is available | 455 crypto_->GetSecureRandom(rand, length); |
| 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 } | 456 } |
| 612 | 457 |
| 613 bool Mount::CreateMasterKey(const Credentials& credentials, int index) { | 458 bool Mount::CreateMasterKey(const Credentials& credentials, int index) { |
| 614 VaultKeyset vault_keyset; | 459 VaultKeyset vault_keyset; |
| 615 vault_keyset.CreateRandom(*this); | 460 vault_keyset.CreateRandom(*this); |
| 616 return SaveVaultKeyset(credentials, vault_keyset, index); | 461 return SaveVaultKeyset(credentials, vault_keyset, index); |
| 617 } | 462 } |
| 618 | 463 |
| 619 bool Mount::SaveVaultKeyset(const Credentials& credentials, | 464 bool Mount::SaveVaultKeyset(const Credentials& credentials, |
| 620 const VaultKeyset& vault_keyset, | 465 const VaultKeyset& vault_keyset, |
| 621 int index) { | 466 int index) { |
| 622 unsigned char wrapper_key[EVP_MAX_KEY_LENGTH]; | 467 // Get the vault keyset wrapper |
| 623 unsigned char iv[EVP_MAX_IV_LENGTH]; | 468 SecureBlob user_salt; |
| 624 unsigned char salt[PKCS5_SALT_LEN]; | 469 GetUserSalt(credentials, index, true, &user_salt); |
| 470 SecureBlob passkey; |
| 471 credentials.GetPasskey(&passkey); |
| 472 SecureBlob passkey_wrapper; |
| 473 crypto_->PasskeyToWrapper(passkey, user_salt, 1, &passkey_wrapper); |
| 625 | 474 |
| 626 // Create a salt for this master key | 475 // Wrap the vault keyset |
| 627 GetSecureRandom(salt, sizeof(salt)); | 476 SecureBlob salt(CRYPTOHOME_DEFAULT_KEY_SALT_SIZE); |
| 628 SecureBlob user_salt = GetUserSalt(credentials, index, true); | 477 SecureBlob cipher_text; |
| 629 | 478 crypto_->GetSecureRandom(static_cast<unsigned char*>(salt.data()), |
| 630 // Convert the passkey to a passkey wrapper | 479 salt.size()); |
| 631 SecureBlob passkey_wrapper = | 480 if (!crypto_->WrapVaultKeyset(vault_keyset, passkey_wrapper, salt, |
| 632 PasskeyToWrapper(credentials.GetPasskey(), user_salt, 1); | 481 &cipher_text)) { |
| 633 | 482 LOG(ERROR) << "Wrapping vault keyset failed"; |
| 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; | 483 return false; |
| 639 } | 484 } |
| 640 | 485 |
| 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 | 486 // Save the master key |
| 678 unsigned int data_written = file_util::WriteFile( | 487 unsigned int data_written = file_util::WriteFile( |
| 679 FilePath(GetUserKeyFile(credentials, index)), | 488 FilePath(GetUserKeyFile(credentials, index)), |
| 680 reinterpret_cast<const char*>(&cipher_text[0]), | 489 static_cast<const char*>(cipher_text.const_data()), |
| 681 cipher_text.size()); | 490 cipher_text.size()); |
| 682 | 491 |
| 683 if (data_written != cipher_text.size()) { | 492 if (data_written != cipher_text.size()) { |
| 684 LOG(ERROR) << "Write to master key failed"; | 493 LOG(ERROR) << "Write to master key failed"; |
| 685 return false; | 494 return false; |
| 686 } | 495 } |
| 687 return true; | 496 return true; |
| 688 } | 497 } |
| 689 | 498 |
| 690 SecureBlob Mount::PasskeyToWrapper(const chromeos::Blob& passkey, | 499 void Mount::GetSystemSalt(chromeos::Blob* salt) { |
| 691 const chromeos::Blob& salt, int iters) { | 500 *salt = system_salt_; |
| 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 } | 501 } |
| 718 | 502 |
| 719 SecureBlob Mount::GetSystemSalt() { | 503 void Mount::GetUserSalt(const Credentials& credentials, int index, bool force, |
| 720 return system_salt_; | 504 SecureBlob* salt) { |
| 721 } | |
| 722 | |
| 723 SecureBlob Mount::GetUserSalt(const Credentials& credentials, int index, | |
| 724 bool force) { | |
| 725 FilePath path(GetUserSaltFile(credentials, index)); | 505 FilePath path(GetUserSaltFile(credentials, index)); |
| 726 return GetOrCreateSalt(path, CRYPTOHOME_DEFAULT_SALT_LENGTH, force); | 506 crypto_->GetOrCreateSalt(path, CRYPTOHOME_DEFAULT_SALT_LENGTH, force, salt); |
| 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 } | 507 } |
| 823 | 508 |
| 824 bool Mount::LoadFileBytes(const FilePath& path, | 509 bool Mount::LoadFileBytes(const FilePath& path, |
| 825 SecureBlob& blob) { | 510 SecureBlob* blob) { |
| 826 int64 file_size; | 511 int64 file_size; |
| 827 if (!file_util::PathExists(path)) { | 512 if (!file_util::PathExists(path)) { |
| 828 LOG(ERROR) << path.value() << " does not exist!"; | 513 LOG(INFO) << path.value() << " does not exist!"; |
| 829 return false; | 514 return false; |
| 830 } | 515 } |
| 831 if (!file_util::GetFileSize(path, &file_size)) { | 516 if (!file_util::GetFileSize(path, &file_size)) { |
| 832 LOG(ERROR) << "Could not get size of " << path.value(); | 517 LOG(ERROR) << "Could not get size of " << path.value(); |
| 833 return false; | 518 return false; |
| 834 } | 519 } |
| 835 if (file_size > INT_MAX) { | 520 if (file_size > INT_MAX) { |
| 836 LOG(ERROR) << "File " << path.value() << " is too large: " << file_size; | 521 LOG(ERROR) << "File " << path.value() << " is too large: " << file_size; |
| 837 return false; | 522 return false; |
| 838 } | 523 } |
| 839 SecureBlob buf(file_size); | 524 SecureBlob buf(file_size); |
| 840 int data_read = file_util::ReadFile(path, reinterpret_cast<char*>(&buf[0]), | 525 int data_read = file_util::ReadFile(path, reinterpret_cast<char*>(&buf[0]), |
| 841 file_size); | 526 file_size); |
| 842 // Cast is okay because of comparison to INT_MAX above | 527 // Cast is okay because of comparison to INT_MAX above |
| 843 if (data_read != static_cast<int>(file_size)) { | 528 if (data_read != static_cast<int>(file_size)) { |
| 844 LOG(ERROR) << "Could not read entire file " << file_size; | 529 LOG(ERROR) << "Could not read entire file " << file_size; |
| 845 return false; | 530 return false; |
| 846 } | 531 } |
| 847 blob.swap(buf); | 532 blob->swap(buf); |
| 848 return true; | 533 return true; |
| 849 } | 534 } |
| 850 | 535 |
| 851 bool Mount::TerminatePidsWithOpenFiles(const std::string& path, bool hard) { | 536 } // namespace cryptohome |
| 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)) { | |
| 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 | |
| OLD | NEW |