blob: ffe0d496d27b04443883dfea28c655b2a2a65070 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/kerberos/kerberos_files_handler.h"
#include <memory>
#include <utility>
#include "base/base_paths.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/important_file_writer.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/strings/stringprintf.h"
#include "base/task/thread_pool.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_service.h"
namespace ash {
namespace {
base::FilePath GetKerberosDir() {
base::FilePath dir;
base::PathService::Get(base::DIR_HOME, &dir);
return dir.Append(kKrb5Directory);
}
// Writes |blob| into file <UserPath>/kerberos/|file_name|. First writes into
// temporary file and then replaces existing one. Prints an error or failure.
void WriteFile(const base::FilePath& path, std::optional<std::string> blob) {
if (!blob.has_value())
return;
if (!base::ImportantFileWriter::WriteFileAtomically(path, blob.value()))
LOG(ERROR) << "Failed to write file " << path.value();
}
// Deletes file at |path|. Prints an error or failure.
void RemoveFile(const base::FilePath& path) {
if (!base::DeleteFile(path))
LOG(ERROR) << "Failed to delete file " << path.value();
}
// Writes |krb5cc| to <DIR_HOME>/kerberos/krb5cc and |krb5config| to
// <DIR_HOME>/kerberos/krb5.conf if set. Creates directories if necessary.
void WriteFiles(std::optional<std::string> krb5cc,
std::optional<std::string> krb5config) {
base::FilePath dir = GetKerberosDir();
base::File::Error error;
if (!base::CreateDirectoryAndGetError(dir, &error)) {
LOG(ERROR) << "Failed to create '" << dir.value()
<< "' directory: " << base::File::ErrorToString(error);
return;
}
WriteFile(dir.Append(kKrb5CCFile), std::move(krb5cc));
WriteFile(dir.Append(kKrb5ConfFile), std::move(krb5config));
}
// Deletes <DIR_HOME>/kerberos/krb5cc and <DIR_HOME>/kerberos/krb5.conf.
void RemoveFiles() {
base::FilePath dir = GetKerberosDir();
RemoveFile(dir.Append(kKrb5CCFile));
RemoveFile(dir.Append(kKrb5ConfFile));
}
// If |config| has a value, puts canonicalization settings first depending on
// user policy. Whatever setting comes first wins, so even if krb5.conf sets
// rdns or dns_canonicalize_hostname below, it would get overridden.
std::optional<std::string> MaybeAdjustConfig(std::optional<std::string> config,
bool is_dns_cname_enabled) {
if (!config.has_value())
return std::nullopt;
static constexpr char kKrb5CnameSettings[] =
"[libdefaults]\n"
"\tdns_canonicalize_hostname = %s\n"
"\trdns = false\n";
std::string adjusted_config = base::StringPrintf(
kKrb5CnameSettings, is_dns_cname_enabled ? "true" : "false");
adjusted_config.append(config.value());
return adjusted_config;
}
} // namespace
const char kKrb5Directory[] = "kerberos";
const char kKrb5CCFile[] = "krb5cc";
const char kKrb5ConfFile[] = "krb5.conf";
KerberosFilesHandler::KerberosFilesHandler(
PrefService& local_state,
base::RepeatingClosure get_kerberos_files)
: get_kerberos_files_(std::move(get_kerberos_files)) {
// Listen to kDisableAuthNegotiateCnameLookup pref. It might change the
// Kerberos config.
negotiate_disable_cname_lookup_.Init(
prefs::kDisableAuthNegotiateCnameLookup, &local_state,
base::BindRepeating(
&KerberosFilesHandler::OnDisabledAuthNegotiateCnameLookupChanged,
weak_factory_.GetWeakPtr()));
}
KerberosFilesHandler::~KerberosFilesHandler() = default;
void KerberosFilesHandler::SetFiles(std::optional<std::string> krb5cc,
std::optional<std::string> krb5conf) {
krb5conf =
MaybeAdjustConfig(krb5conf, !negotiate_disable_cname_lookup_.GetValue());
base::ThreadPool::PostTaskAndReply(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce(&WriteFiles, std::move(krb5cc), std::move(krb5conf)),
base::BindOnce(&KerberosFilesHandler::OnFilesChanged,
weak_factory_.GetWeakPtr()));
}
void KerberosFilesHandler::DeleteFiles() {
// These files contain user credentials, so use BLOCK_SHUTDOWN here to make
// sure they do get deleted.
base::ThreadPool::PostTaskAndReply(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
base::BindOnce(&RemoveFiles),
base::BindOnce(&KerberosFilesHandler::OnFilesChanged,
weak_factory_.GetWeakPtr()));
}
void KerberosFilesHandler::SetFilesChangedForTesting(
base::OnceClosure callback) {
files_changed_for_testing_ = std::move(callback);
}
void KerberosFilesHandler::OnDisabledAuthNegotiateCnameLookupChanged() {
// Refresh kerberos files to adjust config for changed pref.
get_kerberos_files_.Run();
}
void KerberosFilesHandler::OnFilesChanged() {
if (files_changed_for_testing_)
std::move(files_changed_for_testing_).Run();
}
} // namespace ash