blob: bb95bdc2b9ce81b0cbdcd4c6617adfe0882096c1 [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
Mike Frysinger56379d72019-02-19 16:03:03 -050013#include <string>
14#include <vector>
15
Qijiang Fan713061e2021-03-08 15:45:12 +090016#include <base/check_op.h>
Ben Chan297c3c22013-07-17 17:34:12 -070017#include <base/strings/stringprintf.h>
18
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050019namespace debugd {
20
Ben Chanaf125862017-02-08 23:11:18 -080021namespace {
22
23const size_t kMaxWaitAttempts = 3;
24const unsigned int kDelayUSec = 1000;
25
26const char kMiniJail[] = "/sbin/minijail0";
27
Lann Martin419fe712017-06-07 10:45:47 -060028// waitpid(2) with a timeout of kMaxWaitAttempts * kDelayUSec.
29bool waitpid_awhile(pid_t pid) {
30 DCHECK_GT(pid, 0);
31 for (size_t attempt = 0; attempt < kMaxWaitAttempts; ++attempt) {
32 pid_t res = waitpid(pid, nullptr, WNOHANG);
33 if (res > 0) {
34 return true;
35 }
36 if (res < 0) {
37 PLOG(ERROR) << "waitpid(" << pid << ") failed";
38 return false;
39 }
40 usleep(kDelayUSec);
41 }
42 return false;
43}
44
Ben Chanaf125862017-02-08 23:11:18 -080045} // namespace
46
47const char SandboxedProcess::kDefaultUser[] = "debugd";
48const char SandboxedProcess::kDefaultGroup[] = "debugd";
Elly Fong-Jones215b5622013-03-20 14:32:18 -040049
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050050SandboxedProcess::SandboxedProcess()
Jorge Lucangeli Obesc99a12a2014-09-17 16:43:40 -070051 : sandboxing_(true),
52 access_root_mount_ns_(false),
Edward Hillad7de4e2017-07-05 14:45:17 -060053 set_capabilities_(false),
David Valleau09f46642017-12-19 17:55:14 -080054 inherit_usergroups_(false),
Jorge Lucangeli Obesc99a12a2014-09-17 16:43:40 -070055 user_(kDefaultUser),
Tom Hughesd6c2d392020-08-24 18:12:11 -070056 group_(kDefaultGroup) {}
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050057
Mike Frysinger56379d72019-02-19 16:03:03 -050058bool SandboxedProcess::Init(
59 const std::vector<std::string>& minijail_extra_args) {
Jorge Lucangeli Obes75cb7ba2017-07-06 10:52:48 -040060 if (sandboxing_ && (user_.empty() || group_.empty())) {
Tom Hughesd6c2d392020-08-24 18:12:11 -070061 // Cannot sandbox without user/group.
62 return false;
Jorge Lucangeli Obes75cb7ba2017-07-06 10:52:48 -040063 }
64
65 if (set_capabilities_ && (!sandboxing_ || user_ == "root")) {
Tom Hughesd6c2d392020-08-24 18:12:11 -070066 // Restricting capabilities requires dropping root.
67 return false;
Jorge Lucangeli Obes75cb7ba2017-07-06 10:52:48 -040068 }
69
Jorge Lucangeli Obes623f8ca2014-09-18 10:50:06 -070070 AddArg(kMiniJail);
71 // Enter a new mount namespace. This is done for every process to avoid
72 // affecting the original mount namespace.
73 AddArg("-v");
74
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050075 if (sandboxing_) {
Elly Fong-Jonese56a8f62013-01-23 15:50:21 -050076 if (user_ != "root") {
77 AddArg("-u");
78 AddArg(user_);
79 }
80 if (group_ != "root") {
81 AddArg("-g");
82 AddArg(group_);
83 }
David Valleau09f46642017-12-19 17:55:14 -080084 if (inherit_usergroups_) {
85 AddArg("-G");
86 }
Jorge Lucangeli Obes75cb7ba2017-07-06 10:52:48 -040087 if (set_capabilities_) {
Edward Hillad7de4e2017-07-05 14:45:17 -060088 AddStringOption("-c",
Jorge Lucangeli Obes75cb7ba2017-07-06 10:52:48 -040089 base::StringPrintf("0x%" PRIx64, capabilities_mask_));
Edward Hillad7de4e2017-07-05 14:45:17 -060090 }
91 }
92
Jorge Lucangeli Obes623f8ca2014-09-18 10:50:06 -070093 if (access_root_mount_ns_) {
94 // Enter root mount namespace.
95 AddStringOption("-V", "/proc/1/ns/mnt");
96 }
97
Justin Carlson73310fb2016-10-11 16:13:26 -070098 if (!seccomp_filter_policy_file_.empty()) {
99 AddStringOption("-S", seccomp_filter_policy_file_);
Justin Carlson187c7252016-10-28 11:03:12 -0700100
101 // Whenever we use a seccomp filter, we want no-new-privs so we can apply
102 // the policy after dropping other privs.
103 AddArg("-n");
Justin Carlson73310fb2016-10-11 16:13:26 -0700104 }
105
Mike Frysinger56379d72019-02-19 16:03:03 -0500106 for (const auto& arg : minijail_extra_args)
107 AddArg(arg);
108
Jorge Lucangeli Obes623f8ca2014-09-18 10:50:06 -0700109 AddArg("--");
110
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -0500111 return true;
112}
113
Mike Frysinger56379d72019-02-19 16:03:03 -0500114bool SandboxedProcess::Init() {
115 return Init({});
116}
117
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -0500118void SandboxedProcess::DisableSandbox() {
119 sandboxing_ = false;
120}
121
122void SandboxedProcess::SandboxAs(const std::string& user,
123 const std::string& group) {
124 sandboxing_ = true;
125 user_ = user;
126 group_ = group;
127}
128
David Valleau09f46642017-12-19 17:55:14 -0800129void SandboxedProcess::InheritUsergroups() {
130 inherit_usergroups_ = true;
131}
132
Edward Hillad7de4e2017-07-05 14:45:17 -0600133void SandboxedProcess::SetCapabilities(uint64_t capabilities_mask) {
134 set_capabilities_ = true;
135 capabilities_mask_ = capabilities_mask;
136}
137
Justin Carlson73310fb2016-10-11 16:13:26 -0700138void SandboxedProcess::SetSeccompFilterPolicyFile(const std::string& path) {
139 seccomp_filter_policy_file_ = path;
140}
141
Jorge Lucangeli Obesc99a12a2014-09-17 16:43:40 -0700142void SandboxedProcess::AllowAccessRootMountNamespace() {
143 access_root_mount_ns_ = true;
144}
145
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -0700146bool SandboxedProcess::KillProcessGroup() {
147 pid_t minijail_pid = pid();
148 if (minijail_pid == 0) {
149 LOG(ERROR) << "Process is not running";
150 return false;
151 }
152
153 // Minijail sets its process group ID equal to its PID,
154 // so we can use pid() as PGID. Check that's still the case.
155 pid_t pgid = getpgid(minijail_pid);
156 if (pgid < 0) {
157 PLOG(ERROR) << "getpgid(minijail_pid) failed";
158 return false;
159 }
160 if (pgid != minijail_pid) {
161 LOG(ERROR) << "Minijail PGID " << pgid << " is different from PID "
162 << minijail_pid;
163 return false;
164 }
165
Lann Martin419fe712017-06-07 10:45:47 -0600166 // Attempt to kill minijail gracefully with SIGINT and then SIGTERM.
167 // Note: we fall through to SIGKILLing the process group below even if this
168 // succeeds to ensure all descendents have been killed.
169 bool minijail_reaped = false;
170 for (auto sig : {SIGINT, SIGTERM}) {
171 if (kill(minijail_pid, sig) != 0) {
172 // ESRCH means the process already exited.
173 if (errno != ESRCH) {
Tom Hughesd6c2d392020-08-24 18:12:11 -0700174 PLOG(WARNING) << "failed to kill " << minijail_pid << " with signal "
175 << sig;
Lann Martin419fe712017-06-07 10:45:47 -0600176 }
177 break;
178 }
179 if (waitpid_awhile(minijail_pid)) {
180 minijail_reaped = true;
181 break;
182 }
183 }
184
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -0700185 // kill(-pgid) kills every process with process group ID |pgid|.
Lann Martin419fe712017-06-07 10:45:47 -0600186 if (kill(-pgid, SIGKILL) != 0) {
187 // ESRCH means the graceful exit above caught everything.
188 if (errno != ESRCH) {
189 PLOG(ERROR) << "kill(-pgid, SIGKILL) failed";
190 return false;
191 }
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -0700192 }
193
194 // If kill(2) succeeded, we release the PID.
195 UpdatePid(0);
196
197 // We only expect to reap one process, the Minijail process.
198 // If the jailed process dies first, Minijail or init will reap it.
199 // If the Minijail process dies first, we will reap it. The jailed process
200 // will then be reaped by init.
Lann Martin419fe712017-06-07 10:45:47 -0600201 if (!minijail_reaped && !waitpid_awhile(minijail_pid)) {
202 LOG(ERROR) << "Process " << minijail_pid << " did not terminate";
203 return false;
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -0700204 }
205
Lann Martin419fe712017-06-07 10:45:47 -0600206 return true;
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -0700207}
208
Ben Chana0011d82014-05-13 00:19:29 -0700209} // namespace debugd