blob: 167acdaa21941fd27335fad0ef21d939a03a9d56 [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
Edward Hillad7de4e2017-07-05 14:45:17 -06007#include <inttypes.h>
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -07008#include <signal.h>
Eric Carusoa6f1adb2017-05-24 14:19:46 -07009#include <stdlib.h>
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -070010#include <sys/types.h>
11#include <sys/wait.h>
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -070012
Ben Chan297c3c22013-07-17 17:34:12 -070013#include <base/strings/stringprintf.h>
14
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050015namespace debugd {
16
Ben Chanaf125862017-02-08 23:11:18 -080017namespace {
18
19const size_t kMaxWaitAttempts = 3;
20const unsigned int kDelayUSec = 1000;
21
22const char kMiniJail[] = "/sbin/minijail0";
23
Lann Martin419fe712017-06-07 10:45:47 -060024// waitpid(2) with a timeout of kMaxWaitAttempts * kDelayUSec.
25bool waitpid_awhile(pid_t pid) {
26 DCHECK_GT(pid, 0);
27 for (size_t attempt = 0; attempt < kMaxWaitAttempts; ++attempt) {
28 pid_t res = waitpid(pid, nullptr, WNOHANG);
29 if (res > 0) {
30 return true;
31 }
32 if (res < 0) {
33 PLOG(ERROR) << "waitpid(" << pid << ") failed";
34 return false;
35 }
36 usleep(kDelayUSec);
37 }
38 return false;
39}
40
Ben Chanaf125862017-02-08 23:11:18 -080041} // namespace
42
43const char SandboxedProcess::kDefaultUser[] = "debugd";
44const char SandboxedProcess::kDefaultGroup[] = "debugd";
Elly Fong-Jones215b5622013-03-20 14:32:18 -040045
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050046SandboxedProcess::SandboxedProcess()
Jorge Lucangeli Obesc99a12a2014-09-17 16:43:40 -070047 : sandboxing_(true),
48 access_root_mount_ns_(false),
Edward Hillad7de4e2017-07-05 14:45:17 -060049 set_capabilities_(false),
Jorge Lucangeli Obesc99a12a2014-09-17 16:43:40 -070050 user_(kDefaultUser),
51 group_(kDefaultGroup) {
52}
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050053
Ben Chan297c3c22013-07-17 17:34:12 -070054// static
55bool SandboxedProcess::GetHelperPath(const std::string& relative_path,
56 std::string* full_path) {
57 // This environment variable controls the root directory for debugd helpers,
58 // which lets people develop helpers even when verified boot is on.
59 const char* helpers_dir = getenv("DEBUGD_HELPERS");
60 std::string path = base::StringPrintf(
61 "%s/%s",
62 helpers_dir ? helpers_dir : "/usr/libexec/debugd/helpers",
63 relative_path.c_str());
64
65 if (path.length() > PATH_MAX)
66 return false;
67
68 *full_path = path;
69 return true;
70}
71
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050072bool SandboxedProcess::Init() {
Jorge Lucangeli Obes623f8ca2014-09-18 10:50:06 -070073 AddArg(kMiniJail);
74 // Enter a new mount namespace. This is done for every process to avoid
75 // affecting the original mount namespace.
76 AddArg("-v");
77
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050078 if (sandboxing_) {
79 if (user_.empty() || group_.empty())
80 return false;
Jorge Lucangeli Obesc99a12a2014-09-17 16:43:40 -070081
Elly Fong-Jonese56a8f62013-01-23 15:50:21 -050082 if (user_ != "root") {
83 AddArg("-u");
84 AddArg(user_);
85 }
86 if (group_ != "root") {
87 AddArg("-g");
88 AddArg(group_);
89 }
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050090 }
Jorge Lucangeli Obes623f8ca2014-09-18 10:50:06 -070091
Edward Hillad7de4e2017-07-05 14:45:17 -060092 if (set_capabilities_) {
93 if (sandboxing_ && user_ != "root") {
94 AddStringOption("-c",
95 base::StringPrintf("0x%" PRIx64, capabilities_mask_));
96 } else {
97 // Restricting capabilities requires dropping root.
98 return false;
99 }
100 }
101
Jorge Lucangeli Obes623f8ca2014-09-18 10:50:06 -0700102 if (access_root_mount_ns_) {
103 // Enter root mount namespace.
104 AddStringOption("-V", "/proc/1/ns/mnt");
105 }
106
Justin Carlson73310fb2016-10-11 16:13:26 -0700107 if (!seccomp_filter_policy_file_.empty()) {
108 AddStringOption("-S", seccomp_filter_policy_file_);
Justin Carlson187c7252016-10-28 11:03:12 -0700109
110 // Whenever we use a seccomp filter, we want no-new-privs so we can apply
111 // the policy after dropping other privs.
112 AddArg("-n");
Justin Carlson73310fb2016-10-11 16:13:26 -0700113 }
114
Jorge Lucangeli Obes623f8ca2014-09-18 10:50:06 -0700115 AddArg("--");
116
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -0500117 return true;
118}
119
120void SandboxedProcess::DisableSandbox() {
121 sandboxing_ = false;
122}
123
124void SandboxedProcess::SandboxAs(const std::string& user,
125 const std::string& group) {
126 sandboxing_ = true;
127 user_ = user;
128 group_ = group;
129}
130
Edward Hillad7de4e2017-07-05 14:45:17 -0600131void SandboxedProcess::SetCapabilities(uint64_t capabilities_mask) {
132 set_capabilities_ = true;
133 capabilities_mask_ = capabilities_mask;
134}
135
Justin Carlson73310fb2016-10-11 16:13:26 -0700136void SandboxedProcess::SetSeccompFilterPolicyFile(const std::string& path) {
137 seccomp_filter_policy_file_ = path;
138}
139
Jorge Lucangeli Obesc99a12a2014-09-17 16:43:40 -0700140void SandboxedProcess::AllowAccessRootMountNamespace() {
141 access_root_mount_ns_ = true;
142}
143
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -0700144bool SandboxedProcess::KillProcessGroup() {
145 pid_t minijail_pid = pid();
146 if (minijail_pid == 0) {
147 LOG(ERROR) << "Process is not running";
148 return false;
149 }
150
151 // Minijail sets its process group ID equal to its PID,
152 // so we can use pid() as PGID. Check that's still the case.
153 pid_t pgid = getpgid(minijail_pid);
154 if (pgid < 0) {
155 PLOG(ERROR) << "getpgid(minijail_pid) failed";
156 return false;
157 }
158 if (pgid != minijail_pid) {
159 LOG(ERROR) << "Minijail PGID " << pgid << " is different from PID "
160 << minijail_pid;
161 return false;
162 }
163
Lann Martin419fe712017-06-07 10:45:47 -0600164 // Attempt to kill minijail gracefully with SIGINT and then SIGTERM.
165 // Note: we fall through to SIGKILLing the process group below even if this
166 // succeeds to ensure all descendents have been killed.
167 bool minijail_reaped = false;
168 for (auto sig : {SIGINT, SIGTERM}) {
169 if (kill(minijail_pid, sig) != 0) {
170 // ESRCH means the process already exited.
171 if (errno != ESRCH) {
172 PLOG(WARNING) << "failed to kill " << minijail_pid
173 << " with signal " << sig;
174 }
175 break;
176 }
177 if (waitpid_awhile(minijail_pid)) {
178 minijail_reaped = true;
179 break;
180 }
181 }
182
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -0700183 // kill(-pgid) kills every process with process group ID |pgid|.
Lann Martin419fe712017-06-07 10:45:47 -0600184 if (kill(-pgid, SIGKILL) != 0) {
185 // ESRCH means the graceful exit above caught everything.
186 if (errno != ESRCH) {
187 PLOG(ERROR) << "kill(-pgid, SIGKILL) failed";
188 return false;
189 }
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -0700190 }
191
192 // If kill(2) succeeded, we release the PID.
193 UpdatePid(0);
194
195 // We only expect to reap one process, the Minijail process.
196 // If the jailed process dies first, Minijail or init will reap it.
197 // If the Minijail process dies first, we will reap it. The jailed process
198 // will then be reaped by init.
Lann Martin419fe712017-06-07 10:45:47 -0600199 if (!minijail_reaped && !waitpid_awhile(minijail_pid)) {
200 LOG(ERROR) << "Process " << minijail_pid << " did not terminate";
201 return false;
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -0700202 }
203
Lann Martin419fe712017-06-07 10:45:47 -0600204 return true;
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -0700205}
206
Ben Chana0011d82014-05-13 00:19:29 -0700207} // namespace debugd