blob: 6da76241eeac2d7f60c9b234901e5eee4d699b2c [file] [log] [blame]
// Copyright 2019 The Chromium 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 "chrome/browser/chromeos/authpolicy/kerberos_files_handler.h"
#include <memory>
#include <utility>
#include <vector>
#include "base/environment.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/path_service.h"
#include "base/strings/stringprintf.h"
#include "base/task/post_task.h"
#include "chrome/browser/browser_process.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/common/network_service_util.h"
#include "services/network/public/mojom/network_service.mojom.h"
namespace chromeos {
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, base::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, false /* recursive */))
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(base::Optional<std::string> krb5cc,
base::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.
base::Optional<std::string> MaybeAdjustConfig(
base::Optional<std::string> config,
bool is_dns_cname_enabled) {
if (!config.has_value())
return base::nullopt;
std::string adjusted_config = base::StringPrintf(
kKrb5CnameSettings, is_dns_cname_enabled ? "true" : "false");
adjusted_config.append(config.value());
return adjusted_config;
}
} // namespace
const char kKrb5CnameSettings[] =
"[libdefaults]\n"
"\tdns_canonicalize_hostname = %s\n"
"\trdns = false\n";
const char kKrb5CCEnvName[] = "KRB5CCNAME";
const char kKrb5ConfEnvName[] = "KRB5_CONFIG";
const char kKrb5CCFilePrefix[] = "FILE:";
const char kKrb5Directory[] = "kerberos";
const char kKrb5CCFile[] = "krb5cc";
const char kKrb5ConfFile[] = "krb5.conf";
KerberosFilesHandler::KerberosFilesHandler(
base::RepeatingClosure get_kerberos_files)
: get_kerberos_files_(std::move(get_kerberos_files)) {
// Set environment variables for GSSAPI library.
std::unique_ptr<base::Environment> env(base::Environment::Create());
base::FilePath path;
base::PathService::Get(base::DIR_HOME, &path);
path = path.Append(kKrb5Directory);
std::string krb5cc_env_value =
kKrb5CCFilePrefix + path.Append(kKrb5CCFile).value();
std::string krb5conf_env_value = path.Append(kKrb5ConfFile).value();
env->SetVar(kKrb5CCEnvName, krb5cc_env_value);
env->SetVar(kKrb5ConfEnvName, krb5conf_env_value);
// Send the environment variables to the network service if it's running
// out of process.
if (content::IsOutOfProcessNetworkService()) {
std::vector<network::mojom::EnvironmentVariablePtr> environment;
environment.push_back(network::mojom::EnvironmentVariable::New(
kKrb5CCEnvName, krb5cc_env_value));
environment.push_back(network::mojom::EnvironmentVariable::New(
kKrb5ConfEnvName, krb5conf_env_value));
content::GetNetworkService()->SetEnvironment(std::move(environment));
}
// Listen to kDisableAuthNegotiateCnameLookup pref. It might change the
// Kerberos config.
negotiate_disable_cname_lookup_.Init(
prefs::kDisableAuthNegotiateCnameLookup, g_browser_process->local_state(),
base::BindRepeating(
&KerberosFilesHandler::OnDisabledAuthNegotiateCnameLookupChanged,
weak_factory_.GetWeakPtr()));
}
KerberosFilesHandler::~KerberosFilesHandler() = default;
void KerberosFilesHandler::SetFiles(base::Optional<std::string> krb5cc,
base::Optional<std::string> krb5conf) {
krb5conf =
MaybeAdjustConfig(krb5conf, !negotiate_disable_cname_lookup_.GetValue());
base::PostTaskWithTraitsAndReply(
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::PostTaskWithTraitsAndReply(
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 chromeos