blob: 528532d7d9b63106ab422409944834229e2cd0ec [file] [log] [blame]
// Copyright 2017 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.
#include "libpasswordprovider/password_provider.h"
#include <grp.h>
#include <keyutils.h>
#include <vector>
#include "base/logging.h"
#include "libpasswordprovider/password.h"
namespace password_provider {
namespace {
constexpr int kDefaultGroupStringsLength = 1024;
constexpr char kKeyringDescription[] = "password keyring";
constexpr char kKeyringKeyType[] = "keyring";
constexpr char kPasswordKeyDescription[] = "password";
constexpr char kPasswordKeyType[] = "user";
constexpr char kPasswordViewersGroupName[] = "password-viewers";
key_serial_t RequestKey(const char* type, const char* description) {
key_serial_t keyring_serial =
find_key_by_type_and_desc(kKeyringKeyType, kKeyringDescription, 0);
if (keyring_serial == -1) {
// This is also called in cases where keys might not exist (e.g., cleaning
// up on logout) so not finding any keys is not an error.
VLOG(1) << "Error finding keyring. errno: " << errno;
return keyring_serial;
}
return keyctl_search(keyring_serial, type, description, 0);
}
int RevokeKey(const char* type, const char* description) {
key_serial_t key_serial = RequestKey(type, description);
if (key_serial == -1) {
return errno;
}
int result = keyctl_revoke(key_serial);
if (result == -1) {
return errno;
}
return result;
}
void HandleKeyError(const char* type, const char* description) {
int result = RevokeKey(type, description);
if (result != 0) {
PLOG(ERROR) << "Error revoking key: " << description;
}
}
} // namespace
PasswordProvider::PasswordProvider() {}
bool PasswordProvider::SavePassword(const Password& password) const {
DCHECK_GT(password.size(), 0);
DCHECK(password.GetRaw());
// Get the group ID for password-viewers
long group_name_length = sysconf(_SC_GETGR_R_SIZE_MAX); // NOLINT long
if (group_name_length == -1) {
group_name_length = kDefaultGroupStringsLength;
}
struct group group_info, *group_infop;
std::vector<char> group_name_buf(group_name_length);
int result =
getgrnam_r(kPasswordViewersGroupName, &group_info, group_name_buf.data(),
group_name_length, &group_infop);
if (result) {
LOG(WARNING) << "Error retrieving group ID for "
<< kPasswordViewersGroupName << " error: " << result;
group_info.gr_gid = -1;
} else if (group_infop == NULL) {
LOG(WARNING) << "Could not find group ID for " << kPasswordViewersGroupName;
group_info.gr_gid = -1;
}
key_serial_t keyring_id = add_key(kKeyringKeyType, kKeyringDescription, NULL,
0, KEY_SPEC_PROCESS_KEYRING);
if (keyring_id == -1) {
PLOG(ERROR) << "Error creating keyring.";
return false;
}
result = keyctl_chown(keyring_id, -1, group_info.gr_gid);
if (result == -1) {
// Don't return false here. Failing to change the group means that the key
// can't be retrieved by the users in the specified group. The security of
// the key is not compromised. Unit tests are not run as a superuser, and so
// can't chown the key and this call will always fail.
PLOG(ERROR) << "Could not change keyring group.";
}
result =
keyctl_setperm(keyring_id, KEY_POS_ALL | KEY_GRP_VIEW | KEY_GRP_READ |
KEY_GRP_SEARCH | KEY_GRP_WRITE);
if (result == -1) {
PLOG(ERROR) << "Error setting permissions on keyring. ";
return false;
}
key_serial_t key_serial =
add_key(kPasswordKeyType, kPasswordKeyDescription, password.GetRaw(),
password.size(), keyring_id);
if (key_serial == -1) {
PLOG(ERROR) << "Error adding key to keyring.";
return false;
}
result = keyctl_chown(key_serial, -1, group_info.gr_gid);
if (result == -1) {
// Don't return false here. Failing to change the group means that the key
// can't be retrieved by the users in the specified group. The security of
// the key is not compromised. Unit tests are not run as a superuser, and so
// can't chown the key and this call will always fail.
PLOG(ERROR) << "Could not change key group.";
}
result =
keyctl_setperm(key_serial, KEY_POS_ALL | KEY_GRP_VIEW | KEY_GRP_READ |
KEY_GRP_SEARCH | KEY_GRP_WRITE);
if (result == -1) {
PLOG(ERROR) << "Error setting permissions on key. ";
HandleKeyError(kPasswordKeyType, kPasswordKeyDescription);
return false;
}
return true;
}
std::unique_ptr<Password> PasswordProvider::GetPassword() const {
key_serial_t key_serial =
RequestKey(kPasswordKeyType, kPasswordKeyDescription);
if (key_serial == -1) {
PLOG(WARNING) << "Could not find key.";
return nullptr;
}
auto password = std::make_unique<Password>();
if (!password->Init()) {
LOG(ERROR) << "Error allocating buffer for password";
return nullptr;
}
int result =
keyctl_read(key_serial, password->GetMutableRaw(), password->max_size());
if (result > password->max_size()) {
LOG(ERROR) << "Password too large for buffer. Max size: "
<< password->max_size();
return nullptr;
}
if (result == -1) {
PLOG(ERROR) << "Error reading key.";
return nullptr;
}
password->SetSize(result);
return password;
}
bool PasswordProvider::DiscardPassword() const {
int result = RevokeKey(kPasswordKeyType, kPasswordKeyDescription);
if (result != 0) {
// This is also called in cases where keys might not exist (e.g., cleaning
// up on logout) so not finding any keys is not an error.
VLOG(1) << "Error revoking key. errno: " << errno;
return false;
}
return true;
}
} // namespace password_provider