blob: bb97ee8f1a93adc0e734632596603c975f61dc14 [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
Lutz Justen6d7b9002016-12-06 21:52:57 +010011#include <base/files/file_path.h>
Lutz Justen54b2da92017-01-19 23:50:19 +010012#include <base/files/file_util.h>
Lutz Justen6d7b9002016-12-06 21:52:57 +010013#include <base/files/scoped_file.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 Justend7b00fd2017-02-02 09:46:41 +010019#include "authpolicy/platform_helper.h"
Lutz Justen1589f9d2017-05-18 09:50:41 +020020#include "authpolicy/samba_helper.h"
Lutz Justen6d7b9002016-12-06 21:52:57 +010021
Lutz Justenf44987e2017-04-03 11:26:23 +020022namespace authpolicy {
Lutz Justendf64d032017-02-09 14:49:15 +010023
Lutz Justen6d7b9002016-12-06 21:52:57 +010024ProcessExecutor::ProcessExecutor(std::vector<std::string> args)
Lutz Justen841a8d82017-03-13 17:25:57 -070025 : args_(std::move(args)) {}
Roman Sorokindaa3fc02016-10-26 16:24:26 +020026
Lutz Justen6d7b9002016-12-06 21:52:57 +010027void ProcessExecutor::SetInputFile(int fd) {
Lutz Justen875dd422016-11-10 16:02:00 +010028 input_fd_ = fd;
Roman Sorokindaa3fc02016-10-26 16:24:26 +020029}
30
Lutz Justen6d7b9002016-12-06 21:52:57 +010031void ProcessExecutor::SetInputString(const std::string& input_str) {
32 input_str_ = input_str;
33}
34
35void ProcessExecutor::SetEnv(const std::string& key, const std::string& value) {
36 env_map_[key] = value;
37}
38
39void ProcessExecutor::SetSeccompFilter(const std::string& policy_file) {
Lutz Justen841a8d82017-03-13 17:25:57 -070040 seccomp_policy_file_ = policy_file;
Lutz Justen6d7b9002016-12-06 21:52:57 +010041}
42
Lutz Justen1589f9d2017-05-18 09:50:41 +020043void ProcessExecutor::LogSeccompFilterFailures(bool enabled) {
44 log_seccomp_failures_ = enabled;
Lutz Justen4b19b792017-01-13 17:02:44 +010045}
46
Lutz Justen1589f9d2017-05-18 09:50:41 +020047void ProcessExecutor::SetNoNewPrivs(bool enabled) {
48 no_new_privs_ = enabled;
Lutz Justen6d7b9002016-12-06 21:52:57 +010049}
50
Lutz Justen1589f9d2017-05-18 09:50:41 +020051void ProcessExecutor::KeepSupplementaryGroups(bool enabled) {
52 keep_supplementary_flags_ = enabled;
53}
54
55void ProcessExecutor::LogCommand(bool enabled) {
56 log_command_ = enabled;
57}
58
59void ProcessExecutor::LogOutput(bool enabled) {
60 log_output_ = enabled;
61}
62
63void ProcessExecutor::LogOutputOnError(bool enabled) {
64 log_output_on_error_ = enabled;
Lutz Justen2eb63952017-01-03 19:58:02 +010065}
66
Roman Sorokindaa3fc02016-10-26 16:24:26 +020067bool ProcessExecutor::Execute() {
Lutz Justen875dd422016-11-10 16:02:00 +010068 ResetOutput();
Lutz Justenf44987e2017-04-03 11:26:23 +020069 if (args_.empty() || args_[0].empty())
70 return true;
Lutz Justen875dd422016-11-10 16:02:00 +010071
72 if (!base::FilePath(args_[0]).IsAbsolute()) {
73 LOG(ERROR) << "Command must be specified by absolute path.";
Lutz Justen6d7b9002016-12-06 21:52:57 +010074 exit_code_ = kExitCodeInternalError;
Lutz Justen875dd422016-11-10 16:02:00 +010075 return false;
76 }
77
Lutz Justen1589f9d2017-05-18 09:50:41 +020078 if (log_command_ && LOG_IS_ON(INFO)) {
Lutz Justen875dd422016-11-10 16:02:00 +010079 std::string cmd = args_[0];
80 for (size_t n = 1; n < args_.size(); ++n)
81 cmd += base::StringPrintf(" '%s'", args_[n].c_str());
82 LOG(INFO) << "Executing " << cmd;
Roman Sorokindaa3fc02016-10-26 16:24:26 +020083 }
84
Lutz Justen6d7b9002016-12-06 21:52:57 +010085 // Convert args to array of pointers. Must be nullptr terminated.
86 std::vector<char*> args_ptr;
87 for (const auto& arg : args_)
88 args_ptr.push_back(const_cast<char*>(arg.c_str()));
89 args_ptr.push_back(nullptr);
90
91 // Save old environment and set ours. Note that clearenv() doesn't actually
92 // delete any pointers, so we can just keep the old pointers.
93 std::vector<char*> old_environ;
94 for (char** env = environ; env != nullptr && *env != nullptr; ++env)
95 old_environ.push_back(*env);
96 clearenv();
97 std::vector<std::string> env_list;
98 for (const auto& env : env_map_) {
99 env_list.push_back(env.first + "=" + env.second);
100 putenv(const_cast<char*>(env_list.back().c_str()));
101 }
102
Lutz Justen841a8d82017-03-13 17:25:57 -0700103 // Prepare minijail.
104 ScopedMinijail jail(minijail_new());
105 if (log_seccomp_failures_)
106 minijail_log_seccomp_filter_failures(jail.get());
107 if (!seccomp_policy_file_.empty()) {
108 minijail_parse_seccomp_filters(jail.get(), seccomp_policy_file_.c_str());
109 minijail_use_seccomp_filter(jail.get());
110 }
111 if (no_new_privs_)
112 minijail_no_new_privs(jail.get());
113 if (keep_supplementary_flags_)
114 minijail_keep_supplementary_gids(jail.get());
115
Lutz Justen6d7b9002016-12-06 21:52:57 +0100116 // Execute the command.
117 pid_t pid = -1;
118 int child_stdin = -1, child_stdout = -1, child_stderr = -1;
Lutz Justen841a8d82017-03-13 17:25:57 -0700119 minijail_run_pid_pipes(jail.get(),
120 args_ptr[0],
121 args_ptr.data(),
122 &pid,
123 &child_stdin,
124 &child_stdout,
125 &child_stderr);
Lutz Justen6d7b9002016-12-06 21:52:57 +0100126
Lutz Justen54b2da92017-01-19 23:50:19 +0100127 // Make sure the pipes never block.
128 if (!base::SetNonBlocking(child_stdin))
129 LOG(WARNING) << "Failed to set stdin non-blocking";
130 if (!base::SetNonBlocking(child_stdout))
131 LOG(WARNING) << "Failed to set stdout non-blocking";
132 if (!base::SetNonBlocking(child_stderr))
133 LOG(WARNING) << "Failed to set stderr non-blocking";
134
Lutz Justen6d7b9002016-12-06 21:52:57 +0100135 // Restore the environment.
136 clearenv();
137 for (char* env : old_environ)
138 putenv(env);
139
Lutz Justen54b2da92017-01-19 23:50:19 +0100140 // Write to child_stdin and read from child_stdout and child_stderr while
141 // there is still data to read/write.
Roman Sorokindd722db2017-02-01 14:02:52 +0100142 bool io_success = PerformPipeIo(child_stdin,
143 child_stdout,
144 child_stderr,
145 input_fd_,
146 input_str_,
147 &out_data_,
148 &err_data_);
Roman Sorokindaa3fc02016-10-26 16:24:26 +0200149
Lutz Justen6d7b9002016-12-06 21:52:57 +0100150 // Wait for the process to exit.
Lutz Justen841a8d82017-03-13 17:25:57 -0700151 exit_code_ = minijail_wait(jail.get());
152 jail.reset();
Lutz Justen6d7b9002016-12-06 21:52:57 +0100153
Lutz Justen4b19b792017-01-13 17:02:44 +0100154 // Print out a useful error message for seccomp failures.
155 if (exit_code_ == MINIJAIL_ERR_JAIL)
156 LOG(ERROR) << "Seccomp filter blocked a system call";
157
Lutz Justen54b2da92017-01-19 23:50:19 +0100158 // Always exit AFTER minijail_wait! If we do it before, the exit code is never
159 // queried and the process is left dangling.
160 if (!io_success) {
161 LOG(ERROR) << "IO failed";
Lutz Justen6d7b9002016-12-06 21:52:57 +0100162 exit_code_ = kExitCodeInternalError;
Roman Sorokindaa3fc02016-10-26 16:24:26 +0200163 return false;
164 }
165
Lutz Justen1589f9d2017-05-18 09:50:41 +0200166 if (log_output_ || (log_output_on_error_ && exit_code_ != 0)) {
167 LogLongString("Stdout: ", out_data_);
168 LogLongString("Stderr: ", err_data_);
169 }
170 LOG_IF(INFO, log_command_) << "Exit code: " << exit_code_;
Lutz Justenf44987e2017-04-03 11:26:23 +0200171
Lutz Justen6d7b9002016-12-06 21:52:57 +0100172 return exit_code_ == 0;
Roman Sorokindaa3fc02016-10-26 16:24:26 +0200173}
174
Lutz Justen875dd422016-11-10 16:02:00 +0100175void ProcessExecutor::ResetOutput() {
176 exit_code_ = 0;
177 out_data_.clear();
178 err_data_.clear();
179}
180
Roman Sorokindaa3fc02016-10-26 16:24:26 +0200181} // namespace authpolicy