blob: 555304d289666e378c0e522bbc8763a30be796fe [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
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -070014namespace {
15const size_t kMaxWaitAttempts = 3;
16const unsigned int kDelayUSec = 1000;
17}
18
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050019namespace debugd {
20
Elly Fong-Jones215b5622013-03-20 14:32:18 -040021const char *SandboxedProcess::kDefaultUser = "debugd";
22const char *SandboxedProcess::kDefaultGroup = "debugd";
23
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050024SandboxedProcess::SandboxedProcess()
Jorge Lucangeli Obesc99a12a2014-09-17 16:43:40 -070025 : sandboxing_(true),
26 access_root_mount_ns_(false),
27 user_(kDefaultUser),
28 group_(kDefaultGroup) {
29}
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050030
Ricky Liang1ef73e52016-05-24 16:32:34 +080031SandboxedProcess::~SandboxedProcess() {
32 for (const auto& fd : bound_fds_)
33 close(fd);
34}
35
Ben Chan297c3c22013-07-17 17:34:12 -070036// static
37bool SandboxedProcess::GetHelperPath(const std::string& relative_path,
38 std::string* full_path) {
39 // This environment variable controls the root directory for debugd helpers,
40 // which lets people develop helpers even when verified boot is on.
41 const char* helpers_dir = getenv("DEBUGD_HELPERS");
42 std::string path = base::StringPrintf(
43 "%s/%s",
44 helpers_dir ? helpers_dir : "/usr/libexec/debugd/helpers",
45 relative_path.c_str());
46
47 if (path.length() > PATH_MAX)
48 return false;
49
50 *full_path = path;
51 return true;
52}
53
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050054bool SandboxedProcess::Init() {
55 const char *kMiniJail = "/sbin/minijail0";
Jorge Lucangeli Obes623f8ca2014-09-18 10:50:06 -070056
57 AddArg(kMiniJail);
58 // Enter a new mount namespace. This is done for every process to avoid
59 // affecting the original mount namespace.
60 AddArg("-v");
61
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050062 if (sandboxing_) {
63 if (user_.empty() || group_.empty())
64 return false;
Jorge Lucangeli Obesc99a12a2014-09-17 16:43:40 -070065
Elly Fong-Jonese56a8f62013-01-23 15:50:21 -050066 if (user_ != "root") {
67 AddArg("-u");
68 AddArg(user_);
69 }
70 if (group_ != "root") {
71 AddArg("-g");
72 AddArg(group_);
73 }
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050074 }
Jorge Lucangeli Obes623f8ca2014-09-18 10:50:06 -070075
76 if (access_root_mount_ns_) {
77 // Enter root mount namespace.
78 AddStringOption("-V", "/proc/1/ns/mnt");
79 }
80
Justin Carlson73310fb2016-10-11 16:13:26 -070081 if (!seccomp_filter_policy_file_.empty()) {
82 AddStringOption("-S", seccomp_filter_policy_file_);
Justin Carlson187c7252016-10-28 11:03:12 -070083
84 // Whenever we use a seccomp filter, we want no-new-privs so we can apply
85 // the policy after dropping other privs.
86 AddArg("-n");
Justin Carlson73310fb2016-10-11 16:13:26 -070087 }
88
Jorge Lucangeli Obes623f8ca2014-09-18 10:50:06 -070089 AddArg("--");
90
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050091 return true;
92}
93
Ricky Liang1ef73e52016-05-24 16:32:34 +080094void SandboxedProcess::BindFd(int parent_fd, int child_fd) {
95 ProcessImpl::BindFd(parent_fd, child_fd);
96 bound_fds_.push_back(parent_fd);
97}
98
Elly Fong-Jonesd9a16cd2012-11-12 16:09:49 -050099void SandboxedProcess::DisableSandbox() {
100 sandboxing_ = false;
101}
102
103void SandboxedProcess::SandboxAs(const std::string& user,
104 const std::string& group) {
105 sandboxing_ = true;
106 user_ = user;
107 group_ = group;
108}
109
Justin Carlson73310fb2016-10-11 16:13:26 -0700110void SandboxedProcess::SetSeccompFilterPolicyFile(const std::string& path) {
111 seccomp_filter_policy_file_ = path;
112}
113
Jorge Lucangeli Obesc99a12a2014-09-17 16:43:40 -0700114void SandboxedProcess::AllowAccessRootMountNamespace() {
115 access_root_mount_ns_ = true;
116}
117
Jorge Lucangeli Obes389a9ee2015-05-14 17:37:01 -0700118bool SandboxedProcess::KillProcessGroup() {
119 pid_t minijail_pid = pid();
120 if (minijail_pid == 0) {
121 LOG(ERROR) << "Process is not running";
122 return false;
123 }
124
125 // Minijail sets its process group ID equal to its PID,
126 // so we can use pid() as PGID. Check that's still the case.
127 pid_t pgid = getpgid(minijail_pid);
128 if (pgid < 0) {
129 PLOG(ERROR) << "getpgid(minijail_pid) failed";
130 return false;
131 }
132 if (pgid != minijail_pid) {
133 LOG(ERROR) << "Minijail PGID " << pgid << " is different from PID "
134 << minijail_pid;
135 return false;
136 }
137
138 // kill(-pgid) kills every process with process group ID |pgid|.
139 if (kill(-pgid, SIGKILL) < 0) {
140 PLOG(ERROR) << "kill(-pgid, SIGKILL) failed";
141 return false;
142 }
143
144 // If kill(2) succeeded, we release the PID.
145 UpdatePid(0);
146
147 // We only expect to reap one process, the Minijail process.
148 // If the jailed process dies first, Minijail or init will reap it.
149 // If the Minijail process dies first, we will reap it. The jailed process
150 // will then be reaped by init.
151 for (size_t attempt = 0; attempt < kMaxWaitAttempts; ++attempt) {
152 int status = 0;
153 // waitpid(-pgid) waits for any child process with process group ID |pgid|.
154 pid_t waited = waitpid(-pgid, &status, WNOHANG);
155 int saved_errno = errno;
156
157 if (waited < 0) {
158 if (saved_errno == ECHILD) {
159 // Processes with PGID |pgid| don't exist, so we're done.
160 return true;
161 }
162 PLOG(ERROR) << "waitpid(-pgid) failed";
163 return false;
164 }
165
166 if (waited > 0) {
167 if (waited != minijail_pid) {
168 LOG(WARNING) << "Expecting PID " << minijail_pid << ", got PID "
169 << waited;
170 }
171 return true;
172 }
173
174 usleep(kDelayUSec);
175 }
176
177 LOG(WARNING) << "Process " << minijail_pid << " did not terminate";
178 return false;
179}
180
Ben Chana0011d82014-05-13 00:19:29 -0700181} // namespace debugd