| // Copyright (c) 2013 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // A token manager provides a set of methods for login agents to create and |
| // validate token files. |
| |
| #include "chaps/token_file_manager.h" |
| |
| #include <grp.h> |
| #include <pwd.h> |
| #include <sys/stat.h> |
| |
| #include <string> |
| |
| #include <base/files/file_path.h> |
| #include <base/files/file_util.h> |
| #include <chromeos/secure_blob.h> |
| #include <openssl/evp.h> |
| #include <openssl/rand.h> |
| |
| #include "chaps/chaps_utility.h" |
| |
| using std::string; |
| using base::FilePath; |
| using chromeos::SecureBlob; |
| |
| namespace chaps { |
| |
| namespace { |
| |
| const FilePath::CharType kTokenFilePath[] = |
| FILE_PATH_LITERAL("/var/lib/chaps/tokens/"); |
| |
| const mode_t kTokenDirectoryPermissions = (S_IRUSR | S_IWUSR | S_IXUSR); |
| const mode_t kFilePermissionsMask = (S_IRWXU | S_IRWXG | S_IRWXO); |
| |
| const FilePath::CharType kSaltFileName[] = "salt"; |
| const uint32_t kSaltIterations = 4096; |
| const size_t kSaltBytes = 32; |
| const size_t kSaltedKeyBytes = 32; |
| |
| } // namespace |
| |
| TokenFileManager::TokenFileManager(uid_t chapsd_uid, gid_t chapsd_gid) |
| : chapsd_uid_(chapsd_uid), |
| chapsd_gid_(chapsd_gid) { } |
| |
| TokenFileManager::~TokenFileManager() { } |
| |
| bool TokenFileManager::GetUserTokenPath(const string& user, |
| FilePath* token_path) { |
| CHECK(token_path); |
| *token_path = FilePath(kTokenFilePath).Append(user); |
| return base::DirectoryExists(*token_path); |
| } |
| |
| bool TokenFileManager::CreateUserTokenDirectory(const FilePath& token_path) { |
| if (base::DirectoryExists(token_path)) { |
| LOG(ERROR) << "Tried to create " << token_path.value() |
| << " which already exists"; |
| return false; |
| } |
| if (!base::CreateDirectory(token_path)) { |
| LOG(ERROR) << "Failed to create " << token_path.value(); |
| return false; |
| } |
| |
| // Change permissions to be readable and writable only by user chaps. |
| if (chmod(token_path.value().c_str(), kTokenDirectoryPermissions)) { |
| LOG(ERROR) << "Failed to change permissions of token directory " |
| << token_path.value(); |
| return false; |
| } |
| if (chown(token_path.value().c_str(), chapsd_uid_, chapsd_gid_)) { |
| LOG(ERROR) << "Failed to change ownership of token directory " |
| << token_path.value(); |
| return false; |
| } |
| |
| // Create random salt file. |
| string salt_string; |
| salt_string.resize(kSaltBytes); |
| if (1 != RAND_bytes(ConvertStringToByteBuffer(salt_string.data()), |
| kSaltBytes)) { |
| LOG(ERROR) << "Failed to generate random salt for token directory " |
| << token_path.value(); |
| return false; |
| } |
| SecureBlob salt(salt_string); |
| ClearString(&salt_string); |
| |
| FilePath salt_file = token_path.Append(kSaltFileName); |
| int bytes_written = base::WriteFile( |
| salt_file, reinterpret_cast<const char*>(salt.data()), kSaltBytes); |
| if (bytes_written != static_cast<int>(kSaltBytes)) { |
| LOG(ERROR) << "Failed to write salt file in token directory " |
| << token_path.value(); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool TokenFileManager::CheckUserTokenPermissions(const FilePath& token_path) { |
| struct stat token_stat; |
| if (stat(token_path.value().c_str(), &token_stat)) { |
| LOG(ERROR) << "Failed to stat token directory " << token_path.value(); |
| return false; |
| } |
| if ((token_stat.st_mode & kFilePermissionsMask) != |
| kTokenDirectoryPermissions) { |
| LOG(ERROR) << "Incorrect permissions on token directory " |
| << token_path.value(); |
| return false; |
| } |
| if (token_stat.st_uid != chapsd_uid_) { |
| LOG(ERROR) << "Incorrect owner for token directory " << token_path.value(); |
| return false; |
| } |
| if (token_stat.st_gid != chapsd_gid_) { |
| LOG(ERROR) << "Incorrect group for token directory " << token_path.value(); |
| return false; |
| } |
| return true; |
| } |
| |
| bool TokenFileManager::SaltAuthData(const FilePath& token_path, |
| const SecureBlob& auth_data, |
| SecureBlob* salted_auth_data) { |
| string salt_string; |
| FilePath salt_file = token_path.Append(kSaltFileName); |
| if (!base::ReadFileToString(salt_file, &salt_string)) { |
| LOG(ERROR) << "Failed to read salt file in token directory " |
| << token_path.value(); |
| return false; |
| } |
| SecureBlob salt(salt_string); |
| ClearString(&salt_string); |
| if (salt.size() != kSaltBytes) { |
| LOG(ERROR) << "Salt invalid for token directory " << token_path.value(); |
| return false; |
| } |
| |
| SecureBlob out_key(kSaltedKeyBytes); |
| if (1 != PKCS5_PBKDF2_HMAC(static_cast<const char *>(auth_data.data()), |
| auth_data.size(), |
| salt.data(), |
| kSaltBytes, |
| kSaltIterations, |
| EVP_sha512(), |
| kSaltedKeyBytes, |
| out_key.data()) { |
| LOG(ERROR) << "Could not salt authorization data."; |
| return false; |
| } |
| |
| salted_auth_data->swap(out_key); |
| return true; |
| } |
| |
| } // namespace chaps |