blob: 21141738a5a0131d1f247181d4b696133aefc519 [file] [log] [blame]
Roman Sorokindaa3fc02016-10-26 16:24:26 +02001// Copyright 2016 The Chromium OS 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 "authpolicy/process_executor.h"
6
Lutz Justen6d7b9002016-12-06 21:52:57 +01007#include <stdlib.h>
Lutz Justenf44987e2017-04-03 11:26:23 +02008#include <algorithm>
Lutz Justen6d7b9002016-12-06 21:52:57 +01009#include <utility>
Roman Sorokindaa3fc02016-10-26 16:24:26 +020010
Qijiang Fan713061e2021-03-08 15:45:12 +090011#include <base/check.h>
Lutz Justen6d7b9002016-12-06 21:52:57 +010012#include <base/files/file_path.h>
Lutz Justen54b2da92017-01-19 23:50:19 +010013#include <base/files/file_util.h>
Lutz Justendf64d032017-02-09 14:49:15 +010014#include <base/strings/string_split.h>
Lutz Justen6d7b9002016-12-06 21:52:57 +010015#include <base/strings/stringprintf.h>
16#include <libminijail.h>
Lutz Justen841a8d82017-03-13 17:25:57 -070017#include <scoped_minijail.h>
Lutz Justen6d7b9002016-12-06 21:52:57 +010018
Lutz Justen63436ab2017-06-12 13:37:21 +020019#include "authpolicy/anonymizer.h"
Lutz Justen350cdde2018-01-19 09:42:11 +010020#include "authpolicy/log_colors.h"
Lutz Justend7b00fd2017-02-02 09:46:41 +010021#include "authpolicy/platform_helper.h"
Lutz Justen1589f9d2017-05-18 09:50:41 +020022#include "authpolicy/samba_helper.h"
Lutz Justen6d7b9002016-12-06 21:52:57 +010023
Lutz Justenf44987e2017-04-03 11:26:23 +020024namespace authpolicy {
Lutz Justen1b1519d2017-05-05 14:12:21 +020025namespace {
26
27// Prevent some environment variables from being wiped since they're used by
28// tests. crbug.com/718182.
29struct EnvVarDef {
30 const char* name_equals; // "name="
31 size_t size; // strlen("name=")
32};
33
34// Note: strlen doesn't work with constexpr.
35#define DEFINE_ENV_VAR(name) \
36 { name "=", sizeof(name "=") - 1 }
37
38constexpr EnvVarDef kWhitelistedEnvVars[]{
Tom Hughes8bda4e62020-08-24 18:02:53 -070039 DEFINE_ENV_VAR("ASAN_OPTIONS"), DEFINE_ENV_VAR("LSAN_OPTIONS"),
40 DEFINE_ENV_VAR("MSAN_OPTIONS"), DEFINE_ENV_VAR("TSAN_OPTIONS"),
Manoj Guptad93e7712018-08-24 09:39:08 -070041 DEFINE_ENV_VAR("UBSAN_OPTIONS"),
Lutz Justen1b1519d2017-05-05 14:12:21 +020042};
43
44#undef DEFINE_ENV_VAR
45
46} // namespace
Lutz Justendf64d032017-02-09 14:49:15 +010047
Lutz Justen6d7b9002016-12-06 21:52:57 +010048ProcessExecutor::ProcessExecutor(std::vector<std::string> args)
Lutz Justen841a8d82017-03-13 17:25:57 -070049 : args_(std::move(args)) {}
Roman Sorokindaa3fc02016-10-26 16:24:26 +020050
Lutz Justen6d7b9002016-12-06 21:52:57 +010051void ProcessExecutor::SetInputFile(int fd) {
Lutz Justen875dd422016-11-10 16:02:00 +010052 input_fd_ = fd;
Roman Sorokindaa3fc02016-10-26 16:24:26 +020053}
54
Lutz Justen6d7b9002016-12-06 21:52:57 +010055void ProcessExecutor::SetInputString(const std::string& input_str) {
56 input_str_ = input_str;
57}
58
59void ProcessExecutor::SetEnv(const std::string& key, const std::string& value) {
60 env_map_[key] = value;
61}
62
63void ProcessExecutor::SetSeccompFilter(const std::string& policy_file) {
Lutz Justen841a8d82017-03-13 17:25:57 -070064 seccomp_policy_file_ = policy_file;
Lutz Justen6d7b9002016-12-06 21:52:57 +010065}
66
Lutz Justen1589f9d2017-05-18 09:50:41 +020067void ProcessExecutor::LogSeccompFilterFailures(bool enabled) {
68 log_seccomp_failures_ = enabled;
Lutz Justen4b19b792017-01-13 17:02:44 +010069}
70
Lutz Justen1589f9d2017-05-18 09:50:41 +020071void ProcessExecutor::SetNoNewPrivs(bool enabled) {
72 no_new_privs_ = enabled;
Lutz Justen6d7b9002016-12-06 21:52:57 +010073}
74
Lutz Justen1589f9d2017-05-18 09:50:41 +020075void ProcessExecutor::KeepSupplementaryGroups(bool enabled) {
76 keep_supplementary_flags_ = enabled;
77}
78
79void ProcessExecutor::LogCommand(bool enabled) {
80 log_command_ = enabled;
81}
82
83void ProcessExecutor::LogOutput(bool enabled) {
84 log_output_ = enabled;
85}
86
87void ProcessExecutor::LogOutputOnError(bool enabled) {
88 log_output_on_error_ = enabled;
Lutz Justen2eb63952017-01-03 19:58:02 +010089}
90
Lutz Justen63436ab2017-06-12 13:37:21 +020091void ProcessExecutor::SetAnonymizer(Anonymizer* anonymizer) {
92 anonymizer_ = anonymizer;
93}
94
Roman Sorokindaa3fc02016-10-26 16:24:26 +020095bool ProcessExecutor::Execute() {
Lutz Justen875dd422016-11-10 16:02:00 +010096 ResetOutput();
Lutz Justenf44987e2017-04-03 11:26:23 +020097 if (args_.empty() || args_[0].empty())
98 return true;
Lutz Justen875dd422016-11-10 16:02:00 +010099
Lutz Justen63436ab2017-06-12 13:37:21 +0200100 bool need_anonymizer = log_command_ || log_output_ || log_output_on_error_;
101 CHECK(anonymizer_ || !need_anonymizer) << "Logs must be anonymized";
102
Lutz Justen875dd422016-11-10 16:02:00 +0100103 if (!base::FilePath(args_[0]).IsAbsolute()) {
104 LOG(ERROR) << "Command must be specified by absolute path.";
Lutz Justen6d7b9002016-12-06 21:52:57 +0100105 exit_code_ = kExitCodeInternalError;
Lutz Justen875dd422016-11-10 16:02:00 +0100106 return false;
107 }
108
Lutz Justen1589f9d2017-05-18 09:50:41 +0200109 if (log_command_ && LOG_IS_ON(INFO)) {
Lutz Justen875dd422016-11-10 16:02:00 +0100110 std::string cmd = args_[0];
111 for (size_t n = 1; n < args_.size(); ++n)
112 cmd += base::StringPrintf(" '%s'", args_[n].c_str());
Lutz Justen350cdde2018-01-19 09:42:11 +0100113 LOG(INFO) << kColorCommand << "Executing " << anonymizer_->Process(cmd)
114 << kColorReset;
Roman Sorokindaa3fc02016-10-26 16:24:26 +0200115 }
116
Lutz Justen6d7b9002016-12-06 21:52:57 +0100117 // Convert args to array of pointers. Must be nullptr terminated.
118 std::vector<char*> args_ptr;
119 for (const auto& arg : args_)
120 args_ptr.push_back(const_cast<char*>(arg.c_str()));
121 args_ptr.push_back(nullptr);
122
123 // Save old environment and set ours. Note that clearenv() doesn't actually
124 // delete any pointers, so we can just keep the old pointers.
125 std::vector<char*> old_environ;
126 for (char** env = environ; env != nullptr && *env != nullptr; ++env)
127 old_environ.push_back(*env);
128 clearenv();
Lutz Justen1b1519d2017-05-05 14:12:21 +0200129
130 // Store strings in list because putenv requires pointers to stay alive.
Lutz Justen6d7b9002016-12-06 21:52:57 +0100131 std::vector<std::string> env_list;
132 for (const auto& env : env_map_) {
133 env_list.push_back(env.first + "=" + env.second);
134 putenv(const_cast<char*>(env_list.back().c_str()));
135 }
136
Lutz Justen1b1519d2017-05-05 14:12:21 +0200137 // Add back whitelisted env vars. Note that |whitelisted_var.name_equals| is
138 // name= and |env| is name=value. A linear search seems fine, but consider
139 // using a map if kWhitelistedEnvVars grows.
140 for (char* env : old_environ) {
141 for (const EnvVarDef& whitelisted_var : kWhitelistedEnvVars) {
142 if (strncmp(env, whitelisted_var.name_equals, whitelisted_var.size) == 0)
143 putenv(env);
144 }
145 }
146
Lutz Justen841a8d82017-03-13 17:25:57 -0700147 // Prepare minijail.
148 ScopedMinijail jail(minijail_new());
149 if (log_seccomp_failures_)
150 minijail_log_seccomp_filter_failures(jail.get());
151 if (!seccomp_policy_file_.empty()) {
152 minijail_parse_seccomp_filters(jail.get(), seccomp_policy_file_.c_str());
153 minijail_use_seccomp_filter(jail.get());
154 }
155 if (no_new_privs_)
156 minijail_no_new_privs(jail.get());
157 if (keep_supplementary_flags_)
158 minijail_keep_supplementary_gids(jail.get());
159
Lutz Justen6d7b9002016-12-06 21:52:57 +0100160 // Execute the command.
161 pid_t pid = -1;
162 int child_stdin = -1, child_stdout = -1, child_stderr = -1;
Lutz Justen4b008222017-10-13 17:50:20 +0200163 minijail_run_pid_pipes(jail.get(), args_ptr[0], args_ptr.data(), &pid,
164 &child_stdin, &child_stdout, &child_stderr);
Lutz Justen6d7b9002016-12-06 21:52:57 +0100165
Lutz Justen54b2da92017-01-19 23:50:19 +0100166 // Make sure the pipes never block.
167 if (!base::SetNonBlocking(child_stdin))
168 LOG(WARNING) << "Failed to set stdin non-blocking";
169 if (!base::SetNonBlocking(child_stdout))
170 LOG(WARNING) << "Failed to set stdout non-blocking";
171 if (!base::SetNonBlocking(child_stderr))
172 LOG(WARNING) << "Failed to set stderr non-blocking";
173
Lutz Justen6d7b9002016-12-06 21:52:57 +0100174 // Restore the environment.
175 clearenv();
176 for (char* env : old_environ)
177 putenv(env);
178
Pavol Marko6e323032019-09-16 10:24:44 +0200179 if (perform_pipe_io_after_process_exit_for_testing_)
180 exit_code_ = minijail_wait(jail.get());
181
Lutz Justen54b2da92017-01-19 23:50:19 +0100182 // Write to child_stdin and read from child_stdout and child_stderr while
183 // there is still data to read/write.
Lutz Justen4b008222017-10-13 17:50:20 +0200184 bool io_success =
185 PerformPipeIo(child_stdin, child_stdout, child_stderr, input_fd_,
186 input_str_, &out_data_, &err_data_);
Roman Sorokindaa3fc02016-10-26 16:24:26 +0200187
Lutz Justen6d7b9002016-12-06 21:52:57 +0100188 // Wait for the process to exit.
Pavol Marko6e323032019-09-16 10:24:44 +0200189 if (!perform_pipe_io_after_process_exit_for_testing_)
190 exit_code_ = minijail_wait(jail.get());
Lutz Justen841a8d82017-03-13 17:25:57 -0700191 jail.reset();
Lutz Justen6d7b9002016-12-06 21:52:57 +0100192
Lutz Justen4b19b792017-01-13 17:02:44 +0100193 // Print out a useful error message for seccomp failures.
194 if (exit_code_ == MINIJAIL_ERR_JAIL)
195 LOG(ERROR) << "Seccomp filter blocked a system call";
196
Lutz Justen54b2da92017-01-19 23:50:19 +0100197 // Always exit AFTER minijail_wait! If we do it before, the exit code is never
198 // queried and the process is left dangling.
199 if (!io_success) {
200 LOG(ERROR) << "IO failed";
Lutz Justen6d7b9002016-12-06 21:52:57 +0100201 exit_code_ = kExitCodeInternalError;
Roman Sorokindaa3fc02016-10-26 16:24:26 +0200202 return false;
203 }
204
Lutz Justenac94e9b2017-07-13 15:55:40 +0200205 output_logged_ = false;
206 if (log_output_ || (log_output_on_error_ && exit_code_ != 0))
207 LogOutputOnce();
Lutz Justen350cdde2018-01-19 09:42:11 +0100208 LOG_IF(INFO, log_command_)
209 << kColorCommand << "Exit code: " << exit_code_ << kColorReset;
Lutz Justenf44987e2017-04-03 11:26:23 +0200210
Lutz Justen6d7b9002016-12-06 21:52:57 +0100211 return exit_code_ == 0;
Roman Sorokindaa3fc02016-10-26 16:24:26 +0200212}
213
Lutz Justenac94e9b2017-07-13 15:55:40 +0200214void ProcessExecutor::LogOutputOnce() {
215 if (output_logged_ || args_.empty() || !(log_output_on_error_ || log_output_))
216 return;
Lutz Justen350cdde2018-01-19 09:42:11 +0100217 LogLongString(kColorCommandStdout, args_[0] + " stdout: ", out_data_,
218 anonymizer_);
219 LogLongString(kColorCommandStderr, args_[0] + " stderr: ", err_data_,
220 anonymizer_);
Lutz Justenac94e9b2017-07-13 15:55:40 +0200221 output_logged_ = true;
222}
223
Pavol Marko6e323032019-09-16 10:24:44 +0200224void ProcessExecutor::SetPerformPipeIoAfterProcessExitForTesting(
225 bool perform_pipe_io_after_process_exit_for_testing) {
226 perform_pipe_io_after_process_exit_for_testing_ =
227 perform_pipe_io_after_process_exit_for_testing;
228}
229
Lutz Justen875dd422016-11-10 16:02:00 +0100230void ProcessExecutor::ResetOutput() {
231 exit_code_ = 0;
232 out_data_.clear();
233 err_data_.clear();
234}
235
Roman Sorokindaa3fc02016-10-26 16:24:26 +0200236} // namespace authpolicy