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