blob: 076651b80c8e2dd0164d9cb05a1cd42950c0f57c [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 Justen54b2da92017-01-19 23:50:19 +01007#include <algorithm>
Lutz Justen6d7b9002016-12-06 21:52:57 +01008#include <stdlib.h>
9#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>
17
18#include "authpolicy/pipe_helper.h"
19
20namespace ah = authpolicy::helper;
Roman Sorokindaa3fc02016-10-26 16:24:26 +020021
Lutz Justendf64d032017-02-09 14:49:15 +010022namespace {
23
24// Splits string into lines and logs the lines. This works around a restriction
25// of syslog of 8kb per log and fixes unreadable logs where \n is replaced by
26// #012.
27void LogLongString(const char* header, const std::string& str) {
28 std::vector<std::string> lines = base::SplitString(
29 str, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
30 if (lines.size() <= 1) {
31 LOG(INFO) << header << str;
32 } else {
33 LOG(INFO) << header;
34 for (const std::string& line : lines)
35 LOG(INFO) << line;
36 }
37}
38
39} // namespace
40
Roman Sorokindaa3fc02016-10-26 16:24:26 +020041namespace authpolicy {
42
Lutz Justen6d7b9002016-12-06 21:52:57 +010043ProcessExecutor::ProcessExecutor(std::vector<std::string> args)
44 : jail_(minijail_new()), args_(std::move(args)) {}
Roman Sorokindaa3fc02016-10-26 16:24:26 +020045
Lutz Justen6d7b9002016-12-06 21:52:57 +010046ProcessExecutor::~ProcessExecutor() {
47 minijail_destroy(jail_);
Roman Sorokindaa3fc02016-10-26 16:24:26 +020048}
49
Lutz Justen6d7b9002016-12-06 21:52:57 +010050void ProcessExecutor::SetInputFile(int fd) {
Lutz Justen875dd422016-11-10 16:02:00 +010051 input_fd_ = fd;
Roman Sorokindaa3fc02016-10-26 16:24:26 +020052}
53
Lutz Justen6d7b9002016-12-06 21:52:57 +010054void ProcessExecutor::SetInputString(const std::string& input_str) {
55 input_str_ = input_str;
56}
57
58void ProcessExecutor::SetEnv(const std::string& key, const std::string& value) {
59 env_map_[key] = value;
60}
61
62void ProcessExecutor::SetSeccompFilter(const std::string& policy_file) {
Lutz Justen6d7b9002016-12-06 21:52:57 +010063 minijail_parse_seccomp_filters(jail_, policy_file.c_str());
64 minijail_use_seccomp_filter(jail_);
65}
66
Lutz Justen4b19b792017-01-13 17:02:44 +010067void ProcessExecutor::LogSeccompFilterFailures() {
68 minijail_log_seccomp_filter_failures(jail_);
69}
70
Lutz Justen6cb96902016-12-13 15:50:22 +010071void ProcessExecutor::SetNoNewPrivs() {
72 minijail_no_new_privs(jail_);
Lutz Justen6d7b9002016-12-06 21:52:57 +010073}
74
Lutz Justen2eb63952017-01-03 19:58:02 +010075void ProcessExecutor::KeepSupplementaryGroups() {
76 minijail_keep_supplementary_gids(jail_);
77}
78
Roman Sorokindaa3fc02016-10-26 16:24:26 +020079bool ProcessExecutor::Execute() {
Lutz Justen875dd422016-11-10 16:02:00 +010080 ResetOutput();
81 if (args_.empty() || args_[0].empty()) return true;
82
83 if (!base::FilePath(args_[0]).IsAbsolute()) {
84 LOG(ERROR) << "Command must be specified by absolute path.";
Lutz Justen6d7b9002016-12-06 21:52:57 +010085 exit_code_ = kExitCodeInternalError;
Lutz Justen875dd422016-11-10 16:02:00 +010086 return false;
87 }
88
Roman Sorokindaa3fc02016-10-26 16:24:26 +020089 if (LOG_IS_ON(INFO)) {
Lutz Justen875dd422016-11-10 16:02:00 +010090 std::string cmd = args_[0];
91 for (size_t n = 1; n < args_.size(); ++n)
92 cmd += base::StringPrintf(" '%s'", args_[n].c_str());
93 LOG(INFO) << "Executing " << cmd;
Roman Sorokindaa3fc02016-10-26 16:24:26 +020094 }
95
Lutz Justen6d7b9002016-12-06 21:52:57 +010096 // Convert args to array of pointers. Must be nullptr terminated.
97 std::vector<char*> args_ptr;
98 for (const auto& arg : args_)
99 args_ptr.push_back(const_cast<char*>(arg.c_str()));
100 args_ptr.push_back(nullptr);
101
102 // Save old environment and set ours. Note that clearenv() doesn't actually
103 // delete any pointers, so we can just keep the old pointers.
104 std::vector<char*> old_environ;
105 for (char** env = environ; env != nullptr && *env != nullptr; ++env)
106 old_environ.push_back(*env);
107 clearenv();
108 std::vector<std::string> env_list;
109 for (const auto& env : env_map_) {
110 env_list.push_back(env.first + "=" + env.second);
111 putenv(const_cast<char*>(env_list.back().c_str()));
112 }
113
114 // Execute the command.
115 pid_t pid = -1;
116 int child_stdin = -1, child_stdout = -1, child_stderr = -1;
Lutz Justen6df3b212017-01-16 11:36:40 +0100117 minijail_run_pid_pipes(jail_, args_ptr[0], args_ptr.data(), &pid,
118 &child_stdin, &child_stdout, &child_stderr);
Lutz Justen6d7b9002016-12-06 21:52:57 +0100119
Lutz Justen54b2da92017-01-19 23:50:19 +0100120 // Make sure the pipes never block.
121 if (!base::SetNonBlocking(child_stdin))
122 LOG(WARNING) << "Failed to set stdin non-blocking";
123 if (!base::SetNonBlocking(child_stdout))
124 LOG(WARNING) << "Failed to set stdout non-blocking";
125 if (!base::SetNonBlocking(child_stderr))
126 LOG(WARNING) << "Failed to set stderr non-blocking";
127
Lutz Justen6d7b9002016-12-06 21:52:57 +0100128 // Restore the environment.
129 clearenv();
130 for (char* env : old_environ)
131 putenv(env);
132
Lutz Justen54b2da92017-01-19 23:50:19 +0100133 // Write to child_stdin and read from child_stdout and child_stderr while
134 // there is still data to read/write.
135 bool io_success =
136 ah::PerformPipeIo(child_stdin, child_stdout, child_stderr, input_fd_,
137 input_str_, &out_data_, &err_data_);
Roman Sorokindaa3fc02016-10-26 16:24:26 +0200138
Lutz Justen6d7b9002016-12-06 21:52:57 +0100139 // Wait for the process to exit.
140 exit_code_ = minijail_wait(jail_);
141
Lutz Justen4b19b792017-01-13 17:02:44 +0100142 // Print out a useful error message for seccomp failures.
143 if (exit_code_ == MINIJAIL_ERR_JAIL)
144 LOG(ERROR) << "Seccomp filter blocked a system call";
145
Lutz Justen54b2da92017-01-19 23:50:19 +0100146 // Always exit AFTER minijail_wait! If we do it before, the exit code is never
147 // queried and the process is left dangling.
148 if (!io_success) {
149 LOG(ERROR) << "IO failed";
Lutz Justen6d7b9002016-12-06 21:52:57 +0100150 exit_code_ = kExitCodeInternalError;
Roman Sorokindaa3fc02016-10-26 16:24:26 +0200151 return false;
152 }
153
Lutz Justendf64d032017-02-09 14:49:15 +0100154 LogLongString("Stdout: ", out_data_);
155 LogLongString("Stderr: ", err_data_);
Lutz Justen6d7b9002016-12-06 21:52:57 +0100156 LOG(INFO) << "Exit code: " << exit_code_;
157 return exit_code_ == 0;
Roman Sorokindaa3fc02016-10-26 16:24:26 +0200158}
159
Lutz Justen875dd422016-11-10 16:02:00 +0100160void ProcessExecutor::ResetOutput() {
161 exit_code_ = 0;
162 out_data_.clear();
163 err_data_.clear();
164}
165
Roman Sorokindaa3fc02016-10-26 16:24:26 +0200166} // namespace authpolicy