blob: b1c3be882e4616d370ee299f825aefc44226811f [file] [log] [blame]
// 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/file_path.h>
#include <base/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 file_util::DirectoryExists(*token_path);
}
bool TokenFileManager::CreateUserTokenDirectory(const FilePath& token_path) {
if (file_util::DirectoryExists(token_path)) {
LOG(ERROR) << "Tried to create " << token_path.value()
<< " which already exists";
return false;
}
if (!file_util::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 = file_util::WriteFile(
salt_file, reinterpret_cast<const char *>(salt.const_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 (!file_util::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(
reinterpret_cast<const char *>(auth_data.const_data()),
auth_data.size(),
reinterpret_cast<const unsigned char *>(salt.const_data()),
kSaltBytes,
kSaltIterations,
EVP_sha512(),
kSaltedKeyBytes,
reinterpret_cast<unsigned char *>(out_key.data()))) {
LOG(ERROR) << "Could not salt authorization data.";
return false;
}
salted_auth_data->swap(out_key);
return true;
}
} // namespace chaps