blob: 635f535b55ba28be14002bebe3bf9ea608a64a2c [file] [log] [blame]
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -05001// Copyright (c) 2012 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
Alex Vakulenko262be3f2014-07-30 15:25:50 -07005#include "debugd/src/sandboxed_process.h"
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -05006
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -07007#include <signal.h>
8#include <sys/types.h>
9#include <sys/wait.h>
10#include <unistd.h>
11
Ben Chan297c3c22013-07-17 17:34:12 -070012#include <base/strings/stringprintf.h>
13
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050014namespace debugd {
15
Ben Chanaf125862017-02-08 23:11:18 -080016namespace {
17
18const size_t kMaxWaitAttempts = 3;
19const unsigned int kDelayUSec = 1000;
20
21const char kMiniJail[] = "/sbin/minijail0";
22
23} // namespace
24
25const char SandboxedProcess::kDefaultUser[] = "debugd";
26const char SandboxedProcess::kDefaultGroup[] = "debugd";
Elly Fong-Jones215b5622013-03-20 14:32:18 -040027
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050028SandboxedProcess::SandboxedProcess()
Jorge Lucangeli Obesc99a12a2014-09-17 16:43:40 -070029 : sandboxing_(true),
30 access_root_mount_ns_(false),
31 user_(kDefaultUser),
32 group_(kDefaultGroup) {
33}
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050034
Ricky Liang1ef73e52016-05-24 16:32:34 +080035SandboxedProcess::~SandboxedProcess() {
36 for (const auto& fd : bound_fds_)
37 close(fd);
38}
39
Ben Chan297c3c22013-07-17 17:34:12 -070040// static
41bool SandboxedProcess::GetHelperPath(const std::string& relative_path,
42 std::string* full_path) {
43 // This environment variable controls the root directory for debugd helpers,
44 // which lets people develop helpers even when verified boot is on.
45 const char* helpers_dir = getenv("DEBUGD_HELPERS");
46 std::string path = base::StringPrintf(
47 "%s/%s",
48 helpers_dir ? helpers_dir : "/usr/libexec/debugd/helpers",
49 relative_path.c_str());
50
51 if (path.length() > PATH_MAX)
52 return false;
53
54 *full_path = path;
55 return true;
56}
57
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050058bool SandboxedProcess::Init() {
Jorge Lucangeli Obes623f8ca2014-09-18 10:50:06 -070059 AddArg(kMiniJail);
60 // Enter a new mount namespace. This is done for every process to avoid
61 // affecting the original mount namespace.
62 AddArg("-v");
63
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050064 if (sandboxing_) {
65 if (user_.empty() || group_.empty())
66 return false;
Jorge Lucangeli Obesc99a12a2014-09-17 16:43:40 -070067
Elly Fong-Jonese56a8f62013-01-23 15:50:21 -050068 if (user_ != "root") {
69 AddArg("-u");
70 AddArg(user_);
71 }
72 if (group_ != "root") {
73 AddArg("-g");
74 AddArg(group_);
75 }
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050076 }
Jorge Lucangeli Obes623f8ca2014-09-18 10:50:06 -070077
78 if (access_root_mount_ns_) {
79 // Enter root mount namespace.
80 AddStringOption("-V", "/proc/1/ns/mnt");
81 }
82
Justin Carlson73310fb2016-10-11 16:13:26 -070083 if (!seccomp_filter_policy_file_.empty()) {
84 AddStringOption("-S", seccomp_filter_policy_file_);
Justin Carlson187c7252016-10-28 11:03:12 -070085
86 // Whenever we use a seccomp filter, we want no-new-privs so we can apply
87 // the policy after dropping other privs.
88 AddArg("-n");
Justin Carlson73310fb2016-10-11 16:13:26 -070089 }
90
Jorge Lucangeli Obes623f8ca2014-09-18 10:50:06 -070091 AddArg("--");
92
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050093 return true;
94}
95
Ricky Liang1ef73e52016-05-24 16:32:34 +080096void SandboxedProcess::BindFd(int parent_fd, int child_fd) {
97 ProcessImpl::BindFd(parent_fd, child_fd);
98 bound_fds_.push_back(parent_fd);
99}
100
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -0500101void SandboxedProcess::DisableSandbox() {
102 sandboxing_ = false;
103}
104
105void SandboxedProcess::SandboxAs(const std::string& user,
106 const std::string& group) {
107 sandboxing_ = true;
108 user_ = user;
109 group_ = group;
110}
111
Justin Carlson73310fb2016-10-11 16:13:26 -0700112void SandboxedProcess::SetSeccompFilterPolicyFile(const std::string& path) {
113 seccomp_filter_policy_file_ = path;
114}
115
Jorge Lucangeli Obesc99a12a2014-09-17 16:43:40 -0700116void SandboxedProcess::AllowAccessRootMountNamespace() {
117 access_root_mount_ns_ = true;
118}
119
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -0700120bool SandboxedProcess::KillProcessGroup() {
121 pid_t minijail_pid = pid();
122 if (minijail_pid == 0) {
123 LOG(ERROR) << "Process is not running";
124 return false;
125 }
126
127 // Minijail sets its process group ID equal to its PID,
128 // so we can use pid() as PGID. Check that's still the case.
129 pid_t pgid = getpgid(minijail_pid);
130 if (pgid < 0) {
131 PLOG(ERROR) << "getpgid(minijail_pid) failed";
132 return false;
133 }
134 if (pgid != minijail_pid) {
135 LOG(ERROR) << "Minijail PGID " << pgid << " is different from PID "
136 << minijail_pid;
137 return false;
138 }
139
140 // kill(-pgid) kills every process with process group ID |pgid|.
141 if (kill(-pgid, SIGKILL) < 0) {
142 PLOG(ERROR) << "kill(-pgid, SIGKILL) failed";
143 return false;
144 }
145
146 // If kill(2) succeeded, we release the PID.
147 UpdatePid(0);
148
149 // We only expect to reap one process, the Minijail process.
150 // If the jailed process dies first, Minijail or init will reap it.
151 // If the Minijail process dies first, we will reap it. The jailed process
152 // will then be reaped by init.
153 for (size_t attempt = 0; attempt < kMaxWaitAttempts; ++attempt) {
154 int status = 0;
155 // waitpid(-pgid) waits for any child process with process group ID |pgid|.
156 pid_t waited = waitpid(-pgid, &status, WNOHANG);
157 int saved_errno = errno;
158
159 if (waited < 0) {
160 if (saved_errno == ECHILD) {
161 // Processes with PGID |pgid| don't exist, so we're done.
162 return true;
163 }
164 PLOG(ERROR) << "waitpid(-pgid) failed";
165 return false;
166 }
167
168 if (waited > 0) {
169 if (waited != minijail_pid) {
170 LOG(WARNING) << "Expecting PID " << minijail_pid << ", got PID "
171 << waited;
172 }
173 return true;
174 }
175
176 usleep(kDelayUSec);
177 }
178
179 LOG(WARNING) << "Process " << minijail_pid << " did not terminate";
180 return false;
181}
182
Ben Chana0011d82014-05-13 00:19:29 -0700183} // namespace debugd