1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/authpolicy/kerberos_files_handler.h"
6
7 #include <memory>
8 #include <utility>
9 #include <vector>
10
11 #include "base/environment.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/files/important_file_writer.h"
15 #include "base/location.h"
16 #include "base/path_service.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/task/post_task.h"
19 #include "base/task/thread_pool.h"
20 #include "chrome/browser/browser_process.h"
21 #include "chrome/common/pref_names.h"
22 #include "content/public/browser/network_service_instance.h"
23 #include "content/public/common/network_service_util.h"
24 #include "services/network/public/mojom/network_service.mojom.h"
25
26 namespace chromeos {
27
28 namespace {
29
GetKerberosDir()30 base::FilePath GetKerberosDir() {
31 base::FilePath dir;
32 base::PathService::Get(base::DIR_HOME, &dir);
33 return dir.Append(kKrb5Directory);
34 }
35
36 // Writes |blob| into file <UserPath>/kerberos/|file_name|. First writes into
37 // temporary file and then replaces existing one. Prints an error or failure.
WriteFile(const base::FilePath & path,base::Optional<std::string> blob)38 void WriteFile(const base::FilePath& path, base::Optional<std::string> blob) {
39 if (!blob.has_value())
40 return;
41 if (!base::ImportantFileWriter::WriteFileAtomically(path, blob.value()))
42 LOG(ERROR) << "Failed to write file " << path.value();
43 }
44
45 // Deletes file at |path|. Prints an error or failure.
RemoveFile(const base::FilePath & path)46 void RemoveFile(const base::FilePath& path) {
47 if (!base::DeleteFile(path))
48 LOG(ERROR) << "Failed to delete file " << path.value();
49 }
50
51 // Writes |krb5cc| to <DIR_HOME>/kerberos/krb5cc and |krb5config| to
52 // <DIR_HOME>/kerberos/krb5.conf if set. Creates directories if necessary.
WriteFiles(base::Optional<std::string> krb5cc,base::Optional<std::string> krb5config)53 void WriteFiles(base::Optional<std::string> krb5cc,
54 base::Optional<std::string> krb5config) {
55 base::FilePath dir = GetKerberosDir();
56 base::File::Error error;
57 if (!base::CreateDirectoryAndGetError(dir, &error)) {
58 LOG(ERROR) << "Failed to create '" << dir.value()
59 << "' directory: " << base::File::ErrorToString(error);
60 return;
61 }
62
63 WriteFile(dir.Append(kKrb5CCFile), std::move(krb5cc));
64 WriteFile(dir.Append(kKrb5ConfFile), std::move(krb5config));
65 }
66
67 // Deletes <DIR_HOME>/kerberos/krb5cc and <DIR_HOME>/kerberos/krb5.conf.
RemoveFiles()68 void RemoveFiles() {
69 base::FilePath dir = GetKerberosDir();
70 RemoveFile(dir.Append(kKrb5CCFile));
71 RemoveFile(dir.Append(kKrb5ConfFile));
72 }
73
74 // If |config| has a value, puts canonicalization settings first depending on
75 // user policy. Whatever setting comes first wins, so even if krb5.conf sets
76 // rdns or dns_canonicalize_hostname below, it would get overridden.
MaybeAdjustConfig(base::Optional<std::string> config,bool is_dns_cname_enabled)77 base::Optional<std::string> MaybeAdjustConfig(
78 base::Optional<std::string> config,
79 bool is_dns_cname_enabled) {
80 if (!config.has_value())
81 return base::nullopt;
82 std::string adjusted_config = base::StringPrintf(
83 kKrb5CnameSettings, is_dns_cname_enabled ? "true" : "false");
84 adjusted_config.append(config.value());
85 return adjusted_config;
86 }
87
88 } // namespace
89
90 const char kKrb5CnameSettings[] =
91 "[libdefaults]\n"
92 "\tdns_canonicalize_hostname = %s\n"
93 "\trdns = false\n";
94 const char kKrb5CCEnvName[] = "KRB5CCNAME";
95 const char kKrb5ConfEnvName[] = "KRB5_CONFIG";
96 const char kKrb5CCFilePrefix[] = "FILE:";
97 const char kKrb5Directory[] = "kerberos";
98 const char kKrb5CCFile[] = "krb5cc";
99 const char kKrb5ConfFile[] = "krb5.conf";
100
KerberosFilesHandler(base::RepeatingClosure get_kerberos_files)101 KerberosFilesHandler::KerberosFilesHandler(
102 base::RepeatingClosure get_kerberos_files)
103 : get_kerberos_files_(std::move(get_kerberos_files)) {
104 // Set environment variables for GSSAPI library.
105 std::unique_ptr<base::Environment> env(base::Environment::Create());
106 base::FilePath path;
107 CHECK(base::PathService::Get(base::DIR_HOME, &path));
108 path = path.Append(kKrb5Directory);
109 std::string krb5cc_env_value =
110 kKrb5CCFilePrefix + path.Append(kKrb5CCFile).value();
111 std::string krb5conf_env_value = path.Append(kKrb5ConfFile).value();
112 env->SetVar(kKrb5CCEnvName, krb5cc_env_value);
113 env->SetVar(kKrb5ConfEnvName, krb5conf_env_value);
114
115 // Send the environment variables to the network service if it's running
116 // out of process.
117 if (content::IsOutOfProcessNetworkService()) {
118 std::vector<network::mojom::EnvironmentVariablePtr> environment;
119 environment.push_back(network::mojom::EnvironmentVariable::New(
120 kKrb5CCEnvName, krb5cc_env_value));
121 environment.push_back(network::mojom::EnvironmentVariable::New(
122 kKrb5ConfEnvName, krb5conf_env_value));
123 content::GetNetworkService()->SetEnvironment(std::move(environment));
124 }
125
126 // Listen to kDisableAuthNegotiateCnameLookup pref. It might change the
127 // Kerberos config.
128 negotiate_disable_cname_lookup_.Init(
129 prefs::kDisableAuthNegotiateCnameLookup, g_browser_process->local_state(),
130 base::BindRepeating(
131 &KerberosFilesHandler::OnDisabledAuthNegotiateCnameLookupChanged,
132 weak_factory_.GetWeakPtr()));
133 }
134
135 KerberosFilesHandler::~KerberosFilesHandler() = default;
136
SetFiles(base::Optional<std::string> krb5cc,base::Optional<std::string> krb5conf)137 void KerberosFilesHandler::SetFiles(base::Optional<std::string> krb5cc,
138 base::Optional<std::string> krb5conf) {
139 krb5conf =
140 MaybeAdjustConfig(krb5conf, !negotiate_disable_cname_lookup_.GetValue());
141 base::ThreadPool::PostTaskAndReply(
142 FROM_HERE,
143 {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
144 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
145 base::BindOnce(&WriteFiles, std::move(krb5cc), std::move(krb5conf)),
146 base::BindOnce(&KerberosFilesHandler::OnFilesChanged,
147 weak_factory_.GetWeakPtr()));
148 }
149
DeleteFiles()150 void KerberosFilesHandler::DeleteFiles() {
151 // These files contain user credentials, so use BLOCK_SHUTDOWN here to make
152 // sure they do get deleted.
153 base::ThreadPool::PostTaskAndReply(
154 FROM_HERE,
155 {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
156 base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
157 base::BindOnce(&RemoveFiles),
158 base::BindOnce(&KerberosFilesHandler::OnFilesChanged,
159 weak_factory_.GetWeakPtr()));
160 }
161
SetFilesChangedForTesting(base::OnceClosure callback)162 void KerberosFilesHandler::SetFilesChangedForTesting(
163 base::OnceClosure callback) {
164 files_changed_for_testing_ = std::move(callback);
165 }
166
OnDisabledAuthNegotiateCnameLookupChanged()167 void KerberosFilesHandler::OnDisabledAuthNegotiateCnameLookupChanged() {
168 // Refresh kerberos files to adjust config for changed pref.
169 get_kerberos_files_.Run();
170 }
171
OnFilesChanged()172 void KerberosFilesHandler::OnFilesChanged() {
173 if (files_changed_for_testing_)
174 std::move(files_changed_for_testing_).Run();
175 }
176
177 } // namespace chromeos
178