blob: 89dd776f6131375bf260f7970c06b26a327b3936 [file] [log] [blame]
// Copyright 2018 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 "authpolicy/auth_data_cache.h"
#include <utility>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include "base/time/clock.h"
#include "base/time/default_clock.h"
#include "authpolicy/log_colors.h"
namespace authpolicy {
namespace {
constexpr char kLogHeader[] = "Auth Data Cache: ";
#define LOG_START(severity) \
LOG_IF(severity, flags_->log_caches()) << kColorCaches << kLogHeader
#define PLOG_START(severity) \
PLOG_IF(severity, flags_->log_caches()) << kColorCaches << kLogHeader
#define LOG_END kColorReset
// Size limit when loading the cached data file (256 kb).
constexpr size_t kCacheSizeLimit = 256 * 1024;
} // namespace
AuthDataCache::AuthDataCache(const protos::DebugFlags* flags)
: flags_(flags), clock_(std::make_unique<base::DefaultClock>()) {}
AuthDataCache::~AuthDataCache() = default;
bool AuthDataCache::Load(const base::FilePath& path) {
data_.Clear();
if (!enabled_)
return true;
if (!base::PathExists(path)) {
LOG_START(ERROR) << "File '" << path.value() << "' does not exist"
<< LOG_END;
return false;
}
std::string data_blob;
if (!base::ReadFileToStringWithMaxSize(path, &data_blob, kCacheSizeLimit)) {
PLOG_START(ERROR) << "Failed to read '" << path.value() << "'" << LOG_END;
return false;
}
if (!data_.ParseFromString(data_blob)) {
LOG_START(ERROR) << "Failed to parse data from string" << LOG_END;
return false;
}
LOG_START(INFO) << "Read '" << path.value() << "'" << LOG_END;
return true;
}
bool AuthDataCache::Save(const base::FilePath& path) {
if (!enabled_)
return true;
std::string data_blob;
if (!data_.SerializeToString(&data_blob)) {
LOG_START(ERROR) << "Failed to serialize data to string" << LOG_END;
return false;
}
const int data_size = static_cast<int>(data_blob.size());
if (base::WriteFile(path, data_blob.data(), data_size) != data_size) {
LOG_START(ERROR) << "Failed to write '" << path.value() << "'" << LOG_END;
return false;
}
// Lock access to authpolicyd rw.
int mode =
base::FILE_PERMISSION_READ_BY_USER | base::FILE_PERMISSION_WRITE_BY_USER;
if (!base::SetPosixFilePermissions(path, mode)) {
LOG_START(ERROR) << "Failed to set permissions on '" << path.value() << "'"
<< LOG_END;
return false;
}
LOG_START(INFO) << "Wrote '" << path.value() << "'" << LOG_END;
return true;
}
void AuthDataCache::Clear() {
data_.Clear();
}
base::Optional<std::string> AuthDataCache::GetWorkgroup(
const std::string& realm) const {
const protos::CachedRealmData* realm_data = GetRealmDataForRead(realm);
if (!realm_data || !realm_data->has_workgroup()) {
LOG_START(INFO) << "No workgroup cached" << LOG_END;
return base::nullopt;
}
LOG_START(INFO) << "Using cached workgroup" << LOG_END;
return realm_data->workgroup();
}
base::Optional<std::string> AuthDataCache::GetKdcIp(
const std::string& realm) const {
const protos::CachedRealmData* realm_data = GetRealmDataForRead(realm);
if (!realm_data || !realm_data->has_kdc_ip()) {
LOG_START(INFO) << "No KDC IP cached" << LOG_END;
return base::nullopt;
}
LOG_START(INFO) << "Using cached KDC IP" << LOG_END;
return realm_data->kdc_ip();
}
base::Optional<std::string> AuthDataCache::GetDcName(
const std::string& realm) const {
const protos::CachedRealmData* realm_data = GetRealmDataForRead(realm);
if (!realm_data || !realm_data->has_dc_name()) {
LOG_START(INFO) << "No DC name cached" << LOG_END;
return base::nullopt;
}
LOG_START(INFO) << "Using cached DC name" << LOG_END;
return realm_data->dc_name();
}
base::Optional<bool> AuthDataCache::GetIsAffiliated(
const std::string& realm) const {
const protos::CachedRealmData* realm_data = GetRealmDataForRead(realm);
if (!realm_data || !realm_data->has_is_affiliated()) {
LOG_START(INFO) << "No affiliation flag cached" << LOG_END;
return base::nullopt;
}
LOG_START(INFO) << "Using cached affiliation flag" << LOG_END;
return realm_data->is_affiliated();
}
void AuthDataCache::SetWorkgroup(const std::string& realm,
const std::string& workgroup) {
protos::CachedRealmData* realm_data = GetRealmDataForWrite(realm);
if (realm_data) {
LOG_START(INFO) << "Setting workgroup" << LOG_END;
realm_data->set_workgroup(workgroup);
}
}
void AuthDataCache::SetKdcIp(const std::string& realm,
const std::string& kdc_ip) {
protos::CachedRealmData* realm_data = GetRealmDataForWrite(realm);
if (realm_data) {
LOG_START(INFO) << "Setting KDC IP" << LOG_END;
realm_data->set_kdc_ip(kdc_ip);
}
}
void AuthDataCache::SetDcName(const std::string& realm,
const std::string& dc_name) {
protos::CachedRealmData* realm_data = GetRealmDataForWrite(realm);
if (realm_data) {
LOG_START(INFO) << "Setting DC name" << LOG_END;
realm_data->set_dc_name(dc_name);
}
}
void AuthDataCache::SetIsAffiliated(const std::string& realm,
bool is_affiliated) {
protos::CachedRealmData* realm_data = GetRealmDataForWrite(realm);
if (realm_data) {
realm_data->set_is_affiliated(is_affiliated);
LOG_START(INFO) << "Setting affiliation flag" << LOG_END;
}
}
void AuthDataCache::RemoveEntriesOlderThan(base::TimeDelta max_age) {
base::Time now = clock_->Now();
auto realm_data_map = data_.mutable_realm_data();
for (auto it = realm_data_map->begin(); it != realm_data_map->end();
/* empty */) {
// Note: If the clock goes backwards for some reason, clear cache as well
// just in case the clock was reset.
protos::CachedRealmData& realm_data = it->second;
base::TimeDelta age =
now - base::Time::FromInternalValue(realm_data.cache_time());
if (age < base::TimeDelta() || age >= max_age) {
LOG_START(INFO) << "Removing entry from cache (age=" << age << ")"
<< LOG_END;
it = realm_data_map->erase(it);
} else {
++it;
}
}
}
void AuthDataCache::SetClockForTesting(std::unique_ptr<base::Clock> clock) {
clock_ = std::move(clock);
}
const protos::CachedRealmData* AuthDataCache::GetRealmDataForRead(
const std::string& realm) const {
if (!enabled_)
return nullptr;
auto it = data_.realm_data().find(realm);
if (it == data_.realm_data().end())
return nullptr;
return &it->second;
}
protos::CachedRealmData* AuthDataCache::GetRealmDataForWrite(
const std::string& realm) {
if (!enabled_)
return nullptr;
protos::CachedRealmData* realm_data = &(*data_.mutable_realm_data())[realm];
// Set cache time only on creation, but not on updates.
if (!realm_data->has_cache_time())
realm_data->set_cache_time(clock_->Now().ToInternalValue());
return realm_data;
}
} // namespace authpolicy