blob: 9af8e61a3f25cbcd4e099c440870b9a9bf458c51 [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>
Eric Carusoa6f1adb2017-05-24 14:19:46 -07008#include <stdlib.h>
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -07009#include <sys/types.h>
10#include <sys/wait.h>
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -070011
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
Lann Martin419fe712017-06-07 10:45:47 -060023// waitpid(2) with a timeout of kMaxWaitAttempts * kDelayUSec.
24bool waitpid_awhile(pid_t pid) {
25 DCHECK_GT(pid, 0);
26 for (size_t attempt = 0; attempt < kMaxWaitAttempts; ++attempt) {
27 pid_t res = waitpid(pid, nullptr, WNOHANG);
28 if (res > 0) {
29 return true;
30 }
31 if (res < 0) {
32 PLOG(ERROR) << "waitpid(" << pid << ") failed";
33 return false;
34 }
35 usleep(kDelayUSec);
36 }
37 return false;
38}
39
Ben Chanaf125862017-02-08 23:11:18 -080040} // namespace
41
42const char SandboxedProcess::kDefaultUser[] = "debugd";
43const char SandboxedProcess::kDefaultGroup[] = "debugd";
Elly Fong-Jones215b5622013-03-20 14:32:18 -040044
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050045SandboxedProcess::SandboxedProcess()
Jorge Lucangeli Obesc99a12a2014-09-17 16:43:40 -070046 : sandboxing_(true),
47 access_root_mount_ns_(false),
48 user_(kDefaultUser),
49 group_(kDefaultGroup) {
50}
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050051
Ben Chan297c3c22013-07-17 17:34:12 -070052// static
53bool SandboxedProcess::GetHelperPath(const std::string& relative_path,
54 std::string* full_path) {
55 // This environment variable controls the root directory for debugd helpers,
56 // which lets people develop helpers even when verified boot is on.
57 const char* helpers_dir = getenv("DEBUGD_HELPERS");
58 std::string path = base::StringPrintf(
59 "%s/%s",
60 helpers_dir ? helpers_dir : "/usr/libexec/debugd/helpers",
61 relative_path.c_str());
62
63 if (path.length() > PATH_MAX)
64 return false;
65
66 *full_path = path;
67 return true;
68}
69
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050070bool SandboxedProcess::Init() {
Jorge Lucangeli Obes623f8ca2014-09-18 10:50:06 -070071 AddArg(kMiniJail);
72 // Enter a new mount namespace. This is done for every process to avoid
73 // affecting the original mount namespace.
74 AddArg("-v");
75
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050076 if (sandboxing_) {
77 if (user_.empty() || group_.empty())
78 return false;
Jorge Lucangeli Obesc99a12a2014-09-17 16:43:40 -070079
Elly Fong-Jonese56a8f62013-01-23 15:50:21 -050080 if (user_ != "root") {
81 AddArg("-u");
82 AddArg(user_);
83 }
84 if (group_ != "root") {
85 AddArg("-g");
86 AddArg(group_);
87 }
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050088 }
Jorge Lucangeli Obes623f8ca2014-09-18 10:50:06 -070089
90 if (access_root_mount_ns_) {
91 // Enter root mount namespace.
92 AddStringOption("-V", "/proc/1/ns/mnt");
93 }
94
Justin Carlson73310fb2016-10-11 16:13:26 -070095 if (!seccomp_filter_policy_file_.empty()) {
96 AddStringOption("-S", seccomp_filter_policy_file_);
Justin Carlson187c7252016-10-28 11:03:12 -070097
98 // Whenever we use a seccomp filter, we want no-new-privs so we can apply
99 // the policy after dropping other privs.
100 AddArg("-n");
Justin Carlson73310fb2016-10-11 16:13:26 -0700101 }
102
Jorge Lucangeli Obes623f8ca2014-09-18 10:50:06 -0700103 AddArg("--");
104
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -0500105 return true;
106}
107
108void SandboxedProcess::DisableSandbox() {
109 sandboxing_ = false;
110}
111
112void SandboxedProcess::SandboxAs(const std::string& user,
113 const std::string& group) {
114 sandboxing_ = true;
115 user_ = user;
116 group_ = group;
117}
118
Justin Carlson73310fb2016-10-11 16:13:26 -0700119void SandboxedProcess::SetSeccompFilterPolicyFile(const std::string& path) {
120 seccomp_filter_policy_file_ = path;
121}
122
Jorge Lucangeli Obesc99a12a2014-09-17 16:43:40 -0700123void SandboxedProcess::AllowAccessRootMountNamespace() {
124 access_root_mount_ns_ = true;
125}
126
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -0700127bool SandboxedProcess::KillProcessGroup() {
128 pid_t minijail_pid = pid();
129 if (minijail_pid == 0) {
130 LOG(ERROR) << "Process is not running";
131 return false;
132 }
133
134 // Minijail sets its process group ID equal to its PID,
135 // so we can use pid() as PGID. Check that's still the case.
136 pid_t pgid = getpgid(minijail_pid);
137 if (pgid < 0) {
138 PLOG(ERROR) << "getpgid(minijail_pid) failed";
139 return false;
140 }
141 if (pgid != minijail_pid) {
142 LOG(ERROR) << "Minijail PGID " << pgid << " is different from PID "
143 << minijail_pid;
144 return false;
145 }
146
Lann Martin419fe712017-06-07 10:45:47 -0600147 // Attempt to kill minijail gracefully with SIGINT and then SIGTERM.
148 // Note: we fall through to SIGKILLing the process group below even if this
149 // succeeds to ensure all descendents have been killed.
150 bool minijail_reaped = false;
151 for (auto sig : {SIGINT, SIGTERM}) {
152 if (kill(minijail_pid, sig) != 0) {
153 // ESRCH means the process already exited.
154 if (errno != ESRCH) {
155 PLOG(WARNING) << "failed to kill " << minijail_pid
156 << " with signal " << sig;
157 }
158 break;
159 }
160 if (waitpid_awhile(minijail_pid)) {
161 minijail_reaped = true;
162 break;
163 }
164 }
165
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -0700166 // kill(-pgid) kills every process with process group ID |pgid|.
Lann Martin419fe712017-06-07 10:45:47 -0600167 if (kill(-pgid, SIGKILL) != 0) {
168 // ESRCH means the graceful exit above caught everything.
169 if (errno != ESRCH) {
170 PLOG(ERROR) << "kill(-pgid, SIGKILL) failed";
171 return false;
172 }
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -0700173 }
174
175 // If kill(2) succeeded, we release the PID.
176 UpdatePid(0);
177
178 // We only expect to reap one process, the Minijail process.
179 // If the jailed process dies first, Minijail or init will reap it.
180 // If the Minijail process dies first, we will reap it. The jailed process
181 // will then be reaped by init.
Lann Martin419fe712017-06-07 10:45:47 -0600182 if (!minijail_reaped && !waitpid_awhile(minijail_pid)) {
183 LOG(ERROR) << "Process " << minijail_pid << " did not terminate";
184 return false;
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -0700185 }
186
Lann Martin419fe712017-06-07 10:45:47 -0600187 return true;
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -0700188}
189
Ben Chana0011d82014-05-13 00:19:29 -0700190} // namespace debugd