blob: 6fac5c26fc9b41948285c8b6d4e40febdb7b5e3b [file] [log] [blame]
Elly Jonese58176c2012-01-23 11:46:17 -05001/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Elly Jonescd7a9042011-07-22 13:56:51 -04002 * Use of this source code is governed by a BSD-style license that can be
Will Drewry32ac9f52011-08-18 21:36:27 -05003 * found in the LICENSE file.
4 */
Elly Jonescd7a9042011-07-22 13:56:51 -04005
6#define _BSD_SOURCE
7#define _GNU_SOURCE
Will Drewry32ac9f52011-08-18 21:36:27 -05008#include <ctype.h>
Elly Jonescd7a9042011-07-22 13:56:51 -04009#include <errno.h>
10#include <grp.h>
11#include <inttypes.h>
Will Drewryfe4a3722011-09-16 14:50:50 -050012#include <limits.h>
Elly Jonescd7a9042011-07-22 13:56:51 -040013#include <linux/capability.h>
14#include <linux/securebits.h>
15#include <pwd.h>
16#include <sched.h>
17#include <signal.h>
Will Drewry2f54b6a2011-09-16 13:45:31 -050018#include <stdarg.h>
Elly Jonescd7a9042011-07-22 13:56:51 -040019#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <syscall.h>
23#include <sys/capability.h>
24#include <sys/mount.h>
Will Drewryf89aef52011-09-16 16:48:57 -050025#include <sys/param.h>
Elly Jonescd7a9042011-07-22 13:56:51 -040026#include <sys/prctl.h>
27#include <sys/wait.h>
28#include <syslog.h>
29#include <unistd.h>
30
31#include "libminijail.h"
Will Drewry32ac9f52011-08-18 21:36:27 -050032#include "libsyscalls.h"
Elly Jonescd7a9042011-07-22 13:56:51 -040033#include "libminijail-private.h"
34
Will Drewry32ac9f52011-08-18 21:36:27 -050035/* Until these are reliably available in linux/prctl.h */
36#ifndef PR_SET_SECCOMP_FILTER
Elly Jonese1749eb2011-10-07 13:54:59 -040037# define PR_SECCOMP_FILTER_SYSCALL 0
38# define PR_SECCOMP_FILTER_EVENT 1
39# define PR_GET_SECCOMP_FILTER 35
40# define PR_SET_SECCOMP_FILTER 36
41# define PR_CLEAR_SECCOMP_FILTER 37
Will Drewry32ac9f52011-08-18 21:36:27 -050042#endif
43
Will Drewry32ac9f52011-08-18 21:36:27 -050044#define die(_msg, ...) do { \
Elly Jonese1749eb2011-10-07 13:54:59 -040045 syslog(LOG_ERR, "libminijail: " _msg, ## __VA_ARGS__); \
46 abort(); \
Will Drewry32ac9f52011-08-18 21:36:27 -050047} while (0)
Elly Jonescd7a9042011-07-22 13:56:51 -040048
Will Drewry32ac9f52011-08-18 21:36:27 -050049#define pdie(_msg, ...) \
Elly Jonese1749eb2011-10-07 13:54:59 -040050 die(_msg ": %s", ## __VA_ARGS__, strerror(errno))
Will Drewry32ac9f52011-08-18 21:36:27 -050051
52#define warn(_msg, ...) \
Elly Jonese1749eb2011-10-07 13:54:59 -040053 syslog(LOG_WARNING, "libminijail: " _msg, ## __VA_ARGS__)
Elly Jonescd7a9042011-07-22 13:56:51 -040054
Will Drewryf89aef52011-09-16 16:48:57 -050055struct seccomp_filter {
Elly Jonese1749eb2011-10-07 13:54:59 -040056 int nr;
57 char *filter;
58 struct seccomp_filter *next, *prev;
Will Drewryf89aef52011-09-16 16:48:57 -050059};
60
Elly Jones51a5b6c2011-10-12 19:09:26 -040061struct binding {
62 char *src;
63 char *dest;
64 int writeable;
65 struct binding *next;
66};
67
Will Drewryf89aef52011-09-16 16:48:57 -050068struct minijail {
Elly Jonese1749eb2011-10-07 13:54:59 -040069 struct {
70 int uid:1;
71 int gid:1;
72 int caps:1;
73 int vfs:1;
74 int pids:1;
75 int seccomp:1;
76 int readonly:1;
77 int usergroups:1;
78 int ptrace:1;
79 int seccomp_filter:1;
Elly Jones51a5b6c2011-10-12 19:09:26 -040080 int chroot:1;
Elly Jonese1749eb2011-10-07 13:54:59 -040081 } flags;
82 uid_t uid;
83 gid_t gid;
84 gid_t usergid;
85 char *user;
86 uint64_t caps;
87 pid_t initpid;
88 int filter_count;
Elly Jones51a5b6c2011-10-12 19:09:26 -040089 int binding_count;
90 char *chrootdir;
Elly Jonese1749eb2011-10-07 13:54:59 -040091 struct seccomp_filter *filters;
Elly Jones51a5b6c2011-10-12 19:09:26 -040092 struct binding *bindings_head;
93 struct binding *bindings_tail;
Will Drewryf89aef52011-09-16 16:48:57 -050094};
95
Will Drewry6ac91122011-10-21 16:38:58 -050096struct minijail API *minijail_new(void)
Elly Jonese1749eb2011-10-07 13:54:59 -040097{
Elly Jones51a5b6c2011-10-12 19:09:26 -040098 return calloc(1, sizeof(struct minijail));
Elly Jonescd7a9042011-07-22 13:56:51 -040099}
100
Will Drewry6ac91122011-10-21 16:38:58 -0500101void API minijail_change_uid(struct minijail *j, uid_t uid)
Elly Jonese1749eb2011-10-07 13:54:59 -0400102{
103 if (uid == 0)
104 die("useless change to uid 0");
105 j->uid = uid;
106 j->flags.uid = 1;
Elly Jonescd7a9042011-07-22 13:56:51 -0400107}
108
Will Drewry6ac91122011-10-21 16:38:58 -0500109void API minijail_change_gid(struct minijail *j, gid_t gid)
Elly Jonese1749eb2011-10-07 13:54:59 -0400110{
111 if (gid == 0)
112 die("useless change to gid 0");
113 j->gid = gid;
114 j->flags.gid = 1;
Elly Jonescd7a9042011-07-22 13:56:51 -0400115}
116
Will Drewry6ac91122011-10-21 16:38:58 -0500117int API minijail_change_user(struct minijail *j, const char *user)
Elly Jonese1749eb2011-10-07 13:54:59 -0400118{
119 char *buf = NULL;
120 struct passwd pw;
121 struct passwd *ppw = NULL;
122 ssize_t sz = sysconf(_SC_GETPW_R_SIZE_MAX);
123 if (sz == -1)
124 sz = 65536; /* your guess is as good as mine... */
Elly Joneseb300c52011-09-22 14:35:43 -0400125
Elly Jonese1749eb2011-10-07 13:54:59 -0400126 /* sysconf(_SC_GETPW_R_SIZE_MAX), under glibc, is documented to return
127 * the maximum needed size of the buffer, so we don't have to search.
128 */
129 buf = malloc(sz);
130 if (!buf)
131 return -ENOMEM;
132 getpwnam_r(user, &pw, buf, sz, &ppw);
133 free(buf);
134 if (!ppw)
135 return -errno;
136 minijail_change_uid(j, ppw->pw_uid);
137 j->user = strdup(user);
138 if (!j->user)
139 return -ENOMEM;
140 j->usergid = ppw->pw_gid;
141 return 0;
Elly Jonescd7a9042011-07-22 13:56:51 -0400142}
143
Will Drewry6ac91122011-10-21 16:38:58 -0500144int API minijail_change_group(struct minijail *j, const char *group)
Elly Jonese1749eb2011-10-07 13:54:59 -0400145{
146 char *buf = NULL;
147 struct group gr;
148 struct group *pgr = NULL;
149 ssize_t sz = sysconf(_SC_GETGR_R_SIZE_MAX);
150 if (sz == -1)
151 sz = 65536; /* and mine is as good as yours, really */
Elly Joneseb300c52011-09-22 14:35:43 -0400152
Elly Jonese1749eb2011-10-07 13:54:59 -0400153 /* sysconf(_SC_GETGR_R_SIZE_MAX), under glibc, is documented to return
154 * the maximum needed size of the buffer, so we don't have to search.
155 */
156 buf = malloc(sz);
157 if (!buf)
158 return -ENOMEM;
159 getgrnam_r(group, &gr, buf, sz, &pgr);
160 free(buf);
161 if (!pgr)
162 return -errno;
163 minijail_change_gid(j, pgr->gr_gid);
164 return 0;
Elly Jonescd7a9042011-07-22 13:56:51 -0400165}
166
Will Drewry6ac91122011-10-21 16:38:58 -0500167void API minijail_use_seccomp(struct minijail *j)
Elly Jonese1749eb2011-10-07 13:54:59 -0400168{
169 j->flags.seccomp = 1;
Elly Jonescd7a9042011-07-22 13:56:51 -0400170}
171
Will Drewry6ac91122011-10-21 16:38:58 -0500172void API minijail_use_seccomp_filter(struct minijail *j)
Elly Jonese1749eb2011-10-07 13:54:59 -0400173{
174 j->flags.seccomp_filter = 1;
Will Drewry32ac9f52011-08-18 21:36:27 -0500175}
176
Will Drewry6ac91122011-10-21 16:38:58 -0500177void API minijail_use_caps(struct minijail *j, uint64_t capmask)
Elly Jonese1749eb2011-10-07 13:54:59 -0400178{
179 j->caps = capmask;
180 j->flags.caps = 1;
Elly Jonescd7a9042011-07-22 13:56:51 -0400181}
182
Will Drewry6ac91122011-10-21 16:38:58 -0500183void API minijail_namespace_vfs(struct minijail *j)
Elly Jonese1749eb2011-10-07 13:54:59 -0400184{
185 j->flags.vfs = 1;
Elly Jonescd7a9042011-07-22 13:56:51 -0400186}
187
Will Drewry6ac91122011-10-21 16:38:58 -0500188void API minijail_namespace_pids(struct minijail *j)
Elly Jonese1749eb2011-10-07 13:54:59 -0400189{
Elly Jonese58176c2012-01-23 11:46:17 -0500190 j->flags.vfs = 1;
191 j->flags.readonly = 1;
Elly Jonese1749eb2011-10-07 13:54:59 -0400192 j->flags.pids = 1;
Elly Jonescd7a9042011-07-22 13:56:51 -0400193}
194
Will Drewry6ac91122011-10-21 16:38:58 -0500195void API minijail_remount_readonly(struct minijail *j)
Elly Jonese1749eb2011-10-07 13:54:59 -0400196{
197 j->flags.vfs = 1;
198 j->flags.readonly = 1;
Elly Jonescd7a9042011-07-22 13:56:51 -0400199}
200
Will Drewry6ac91122011-10-21 16:38:58 -0500201void API minijail_inherit_usergroups(struct minijail *j)
Elly Jonese1749eb2011-10-07 13:54:59 -0400202{
203 j->flags.usergroups = 1;
Elly Jonescd7a9042011-07-22 13:56:51 -0400204}
205
Will Drewry6ac91122011-10-21 16:38:58 -0500206void API minijail_disable_ptrace(struct minijail *j)
Elly Jonese1749eb2011-10-07 13:54:59 -0400207{
208 j->flags.ptrace = 1;
Elly Jonescd7a9042011-07-22 13:56:51 -0400209}
210
Will Drewry6ac91122011-10-21 16:38:58 -0500211int API minijail_enter_chroot(struct minijail *j, const char *dir) {
Elly Jones51a5b6c2011-10-12 19:09:26 -0400212 if (j->chrootdir)
213 return -EINVAL;
214 j->chrootdir = strdup(dir);
215 if (!j->chrootdir)
216 return -ENOMEM;
217 j->flags.chroot = 1;
218 return 0;
219}
220
Will Drewry6ac91122011-10-21 16:38:58 -0500221int API minijail_bind(struct minijail *j, const char *src, const char *dest,
222 int writeable) {
Elly Jones51a5b6c2011-10-12 19:09:26 -0400223 struct binding *b;
224
225 if (*dest != '/')
226 return -EINVAL;
227 b = calloc(1, sizeof(*b));
228 if (!b)
229 return -ENOMEM;
230 b->dest = strdup(dest);
231 if (!b->dest)
232 goto error;
233 b->src = strdup(src);
234 if (!b->src)
235 goto error;
236 b->writeable = writeable;
237
238 syslog(LOG_INFO, "libminijail: bind %s -> %s", src, dest);
239
240 /* Force vfs namespacing so the bind mounts don't leak out into the
241 * containing vfs namespace.
242 */
243 minijail_namespace_vfs(j);
244
245 if (j->bindings_tail)
246 j->bindings_tail->next = b;
247 else
248 j->bindings_head = b;
249 j->bindings_tail = b;
250 j->binding_count++;
251
252 return 0;
253
254error:
255 free(b->src);
256 free(b->dest);
257 free(b);
258 return -ENOMEM;
259}
260
Will Drewry6ac91122011-10-21 16:38:58 -0500261int API minijail_add_seccomp_filter(struct minijail *j, int nr,
262 const char *filter)
Elly Jonese1749eb2011-10-07 13:54:59 -0400263{
264 struct seccomp_filter *sf;
265 if (!filter || nr < 0)
266 return -EINVAL;
Will Drewry32ac9f52011-08-18 21:36:27 -0500267
Elly Jonese1749eb2011-10-07 13:54:59 -0400268 sf = malloc(sizeof(*sf));
269 if (!sf)
270 return -ENOMEM;
271 sf->nr = nr;
272 sf->filter = strndup(filter, MINIJAIL_MAX_SECCOMP_FILTER_LINE);
273 if (!sf->filter) {
274 free(sf);
275 return -ENOMEM;
276 }
Will Drewry32ac9f52011-08-18 21:36:27 -0500277
Elly Jonese1749eb2011-10-07 13:54:59 -0400278 j->filter_count++;
Will Drewryf89aef52011-09-16 16:48:57 -0500279
Elly Jonese1749eb2011-10-07 13:54:59 -0400280 if (!j->filters) {
281 j->filters = sf;
282 sf->next = sf;
283 sf->prev = sf;
284 return 0;
285 }
286 sf->next = j->filters;
287 sf->prev = j->filters->prev;
288 sf->prev->next = sf;
289 j->filters->prev = sf;
290 return 0;
Will Drewry32ac9f52011-08-18 21:36:27 -0500291}
292
Will Drewry6ac91122011-10-21 16:38:58 -0500293int API minijail_lookup_syscall(const char *name)
Elly Jonese1749eb2011-10-07 13:54:59 -0400294{
295 const struct syscall_entry *entry = syscall_table;
296 for (; entry->name && entry->nr >= 0; ++entry)
297 if (!strcmp(entry->name, name))
298 return entry->nr;
299 return -1;
Will Drewry32ac9f52011-08-18 21:36:27 -0500300}
301
Will Drewry6ac91122011-10-21 16:38:58 -0500302char *strip(char *s)
Elly Jonese1749eb2011-10-07 13:54:59 -0400303{
304 char *end;
305 while (*s && isblank(*s))
306 s++;
307 end = s + strlen(s) - 1;
308 while (*end && (isblank(*end) || *end == '\n'))
309 end--;
310 *(end + 1) = '\0';
311 return s;
Will Drewry32ac9f52011-08-18 21:36:27 -0500312}
313
Will Drewry6ac91122011-10-21 16:38:58 -0500314void API minijail_parse_seccomp_filters(struct minijail *j, const char *path)
Elly Jonese1749eb2011-10-07 13:54:59 -0400315{
316 FILE *file = fopen(path, "r");
317 char line[MINIJAIL_MAX_SECCOMP_FILTER_LINE];
Ben Chan1d697932011-10-14 10:53:32 -0700318 int count = 0;
Elly Jonese1749eb2011-10-07 13:54:59 -0400319 if (!file)
320 pdie("failed to open seccomp filters file");
Will Drewry32ac9f52011-08-18 21:36:27 -0500321
Elly Jonese1749eb2011-10-07 13:54:59 -0400322 /* Format is simple:
323 * syscall_name<COLON><FILTER STRING>[\n|EOF]
324 * #...comment...
325 * <empty line?
326 */
327 while (fgets(line, sizeof(line), file)) {
328 char *filter = line;
329 char *name = strsep(&filter, ":");
330 char *name_end = NULL;
331 int nr = -1;
Ben Chan1d697932011-10-14 10:53:32 -0700332 count++;
Will Drewry32ac9f52011-08-18 21:36:27 -0500333
Ben Chan1d697932011-10-14 10:53:32 -0700334 /* Allow comment lines */
335 if (*name == '#')
336 continue;
Will Drewry32ac9f52011-08-18 21:36:27 -0500337
Elly Jonese1749eb2011-10-07 13:54:59 -0400338 name = strip(name);
Will Drewry32ac9f52011-08-18 21:36:27 -0500339
Elly Jonese1749eb2011-10-07 13:54:59 -0400340 if (!filter) {
341 if (strlen(name))
342 die("invalid filter on line %d", count);
343 /* Allow empty lines */
344 continue;
345 }
Will Drewry32ac9f52011-08-18 21:36:27 -0500346
Elly Jonese1749eb2011-10-07 13:54:59 -0400347 filter = strip(filter);
Will Drewry32ac9f52011-08-18 21:36:27 -0500348
Elly Jonese1749eb2011-10-07 13:54:59 -0400349 /* Take direct syscall numbers */
350 nr = strtol(name, &name_end, 0);
351 /* Or fail-over to using names */
352 if (*name_end != '\0')
353 nr = minijail_lookup_syscall(name);
354 if (nr < 0)
355 die("syscall '%s' unknown", name);
Will Drewry32ac9f52011-08-18 21:36:27 -0500356
Elly Jonese1749eb2011-10-07 13:54:59 -0400357 if (minijail_add_seccomp_filter(j, nr, filter))
358 pdie("failed to add filter for syscall '%s'", name);
359 }
360 fclose(file);
Will Drewry32ac9f52011-08-18 21:36:27 -0500361}
362
Will Drewryf89aef52011-09-16 16:48:57 -0500363struct marshal_state {
Elly Jonese1749eb2011-10-07 13:54:59 -0400364 size_t available;
365 size_t total;
366 char *buf;
Will Drewryf89aef52011-09-16 16:48:57 -0500367};
368
Will Drewry6ac91122011-10-21 16:38:58 -0500369void marshal_state_init(struct marshal_state *state,
370 char *buf, size_t available)
Elly Jonese1749eb2011-10-07 13:54:59 -0400371{
372 state->available = available;
373 state->buf = buf;
374 state->total = 0;
Will Drewryf89aef52011-09-16 16:48:57 -0500375}
376
Will Drewry6ac91122011-10-21 16:38:58 -0500377void marshal_append(struct marshal_state *state,
378 char *src, size_t length)
Elly Jonese1749eb2011-10-07 13:54:59 -0400379{
380 size_t copy_len = MIN(state->available, length);
Will Drewryf89aef52011-09-16 16:48:57 -0500381
Elly Jonese1749eb2011-10-07 13:54:59 -0400382 /* Up to |available| will be written. */
383 if (copy_len) {
384 memcpy(state->buf, src, copy_len);
385 state->buf += copy_len;
386 state->available -= copy_len;
387 }
388 /* |total| will contain the expected length. */
389 state->total += length;
Will Drewryf89aef52011-09-16 16:48:57 -0500390}
391
Will Drewry6ac91122011-10-21 16:38:58 -0500392void minijail_marshal_helper(struct marshal_state *state,
393 const struct minijail *j)
Elly Jonese1749eb2011-10-07 13:54:59 -0400394{
Elly Jones51a5b6c2011-10-12 19:09:26 -0400395 struct binding *b = NULL;
Elly Jonese1749eb2011-10-07 13:54:59 -0400396 marshal_append(state, (char *)j, sizeof(*j));
397 if (j->user)
398 marshal_append(state, j->user, strlen(j->user) + 1);
Elly Jones51a5b6c2011-10-12 19:09:26 -0400399 if (j->chrootdir)
400 marshal_append(state, j->chrootdir, strlen(j->chrootdir) + 1);
Elly Jonese1749eb2011-10-07 13:54:59 -0400401 if (j->flags.seccomp_filter && j->filters) {
402 struct seccomp_filter *f = j->filters;
403 do {
404 marshal_append(state, (char *)&f->nr, sizeof(f->nr));
405 marshal_append(state, f->filter, strlen(f->filter) + 1);
406 f = f->next;
407 } while (f != j->filters);
408 }
Elly Jones51a5b6c2011-10-12 19:09:26 -0400409 for (b = j->bindings_head; b; b = b->next) {
410 marshal_append(state, b->src, strlen(b->src) + 1);
411 marshal_append(state, b->dest, strlen(b->dest) + 1);
412 marshal_append(state, (char *)&b->writeable, sizeof(b->writeable));
413 }
Will Drewryf89aef52011-09-16 16:48:57 -0500414}
415
Will Drewry6ac91122011-10-21 16:38:58 -0500416size_t API minijail_size(const struct minijail *j)
Elly Jonese1749eb2011-10-07 13:54:59 -0400417{
418 struct marshal_state state;
419 marshal_state_init(&state, NULL, 0);
420 minijail_marshal_helper(&state, j);
421 return state.total;
Will Drewry2ddaad02011-09-16 11:36:08 -0500422}
423
Elly Jonese1749eb2011-10-07 13:54:59 -0400424int minijail_marshal(const struct minijail *j, char *buf, size_t available)
425{
426 struct marshal_state state;
427 marshal_state_init(&state, buf, available);
428 minijail_marshal_helper(&state, j);
429 return (state.total > available);
Will Drewry2ddaad02011-09-16 11:36:08 -0500430}
431
Elly Jones51a5b6c2011-10-12 19:09:26 -0400432/* consumebytes: consumes @length bytes from a buffer @buf of length @buflength
433 * @length Number of bytes to consume
434 * @buf Buffer to consume from
435 * @buflength Size of @buf
436 *
437 * Returns a pointer to the base of the bytes, or NULL for errors.
438 */
Will Drewry6ac91122011-10-21 16:38:58 -0500439void *consumebytes(size_t length, char **buf, size_t *buflength) {
Elly Jones51a5b6c2011-10-12 19:09:26 -0400440 char *p = *buf;
441 if (length > *buflength)
442 return NULL;
443 *buf += length;
444 *buflength -= length;
445 return p;
446}
447
448/* consumestr: consumes a C string from a buffer @buf of length @length
449 * @buf Buffer to consume
450 * @length Length of buffer
451 *
452 * Returns a pointer to the base of the string, or NULL for errors.
453 */
Will Drewry6ac91122011-10-21 16:38:58 -0500454char *consumestr(char **buf, size_t *buflength) {
Elly Jones51a5b6c2011-10-12 19:09:26 -0400455 size_t len = strnlen(*buf, *buflength);
456 if (len == *buflength)
457 /* There's no null-terminator */
458 return NULL;
459 return consumebytes(len + 1, buf, buflength);
460}
461
Elly Jonese1749eb2011-10-07 13:54:59 -0400462int minijail_unmarshal(struct minijail *j, char *serialized, size_t length)
463{
Elly Jones51a5b6c2011-10-12 19:09:26 -0400464 int i;
465 int count;
Will Drewrybee7ba72011-10-21 20:47:01 -0500466 int ret = -EINVAL;
467
Elly Jonese1749eb2011-10-07 13:54:59 -0400468 if (length < sizeof(*j))
Will Drewrybee7ba72011-10-21 20:47:01 -0500469 goto out;
Elly Jonese1749eb2011-10-07 13:54:59 -0400470 memcpy((void *)j, serialized, sizeof(*j));
471 serialized += sizeof(*j);
472 length -= sizeof(*j);
Will Drewryf89aef52011-09-16 16:48:57 -0500473
Will Drewrybee7ba72011-10-21 20:47:01 -0500474 /* Potentially stale pointers not used as signals. */
475 j->bindings_head = NULL;
476 j->bindings_tail = NULL;
477 j->filters = NULL;
478
Elly Jonese1749eb2011-10-07 13:54:59 -0400479 if (j->user) { /* stale pointer */
Elly Jones51a5b6c2011-10-12 19:09:26 -0400480 char *user = consumestr(&serialized, &length);
481 if (!user)
Will Drewrybee7ba72011-10-21 20:47:01 -0500482 goto clear_pointers;
Elly Jones51a5b6c2011-10-12 19:09:26 -0400483 j->user = strdup(user);
Will Drewrybee7ba72011-10-21 20:47:01 -0500484 if (!j->user)
485 goto clear_pointers;
Elly Jonese1749eb2011-10-07 13:54:59 -0400486 }
Will Drewryf89aef52011-09-16 16:48:57 -0500487
Elly Jonesa8d1e1b2011-10-21 15:38:00 -0400488 if (j->chrootdir) { /* stale pointer */
489 char *chrootdir = consumestr(&serialized, &length);
490 if (!chrootdir)
Will Drewrybee7ba72011-10-21 20:47:01 -0500491 goto bad_chrootdir;
Elly Jonesa8d1e1b2011-10-21 15:38:00 -0400492 j->chrootdir = strdup(chrootdir);
Will Drewrybee7ba72011-10-21 20:47:01 -0500493 if (!j->chrootdir)
494 goto bad_chrootdir;
Elly Jonesa8d1e1b2011-10-21 15:38:00 -0400495 }
496
Elly Jonese1749eb2011-10-07 13:54:59 -0400497 if (j->flags.seccomp_filter && j->filter_count) {
Elly Jones51a5b6c2011-10-12 19:09:26 -0400498 count = j->filter_count;
Elly Jonese1749eb2011-10-07 13:54:59 -0400499 /* Let add_seccomp_filter recompute the value. */
500 j->filter_count = 0;
Elly Jonese1749eb2011-10-07 13:54:59 -0400501 for (; count > 0; --count) {
Elly Jones51a5b6c2011-10-12 19:09:26 -0400502 int *nr = (int *)consumebytes(sizeof(*nr), &serialized,
503 &length);
Elly Jonese1749eb2011-10-07 13:54:59 -0400504 char *filter;
Elly Jones51a5b6c2011-10-12 19:09:26 -0400505 if (!nr)
Will Drewrybee7ba72011-10-21 20:47:01 -0500506 goto bad_filters;
Elly Jones51a5b6c2011-10-12 19:09:26 -0400507 filter = consumestr(&serialized, &length);
508 if (!filter)
Will Drewrybee7ba72011-10-21 20:47:01 -0500509 goto bad_filters;
Elly Jonese1749eb2011-10-07 13:54:59 -0400510 if (minijail_add_seccomp_filter(j, *nr, filter))
Will Drewrybee7ba72011-10-21 20:47:01 -0500511 goto bad_filters;
Elly Jonese1749eb2011-10-07 13:54:59 -0400512 }
513 }
Elly Jones51a5b6c2011-10-12 19:09:26 -0400514
515 count = j->binding_count;
Elly Jones51a5b6c2011-10-12 19:09:26 -0400516 j->binding_count = 0;
517 for (i = 0; i < count; ++i) {
518 int *writeable;
519 const char *dest;
520 const char *src = consumestr(&serialized, &length);
521 if (!src)
Will Drewrybee7ba72011-10-21 20:47:01 -0500522 goto bad_bindings;
Elly Jones51a5b6c2011-10-12 19:09:26 -0400523 dest = consumestr(&serialized, &length);
524 if (!dest)
Will Drewrybee7ba72011-10-21 20:47:01 -0500525 goto bad_bindings;
Elly Jones51a5b6c2011-10-12 19:09:26 -0400526 writeable = consumebytes(sizeof(*writeable), &serialized, &length);
527 if (!writeable)
Will Drewrybee7ba72011-10-21 20:47:01 -0500528 goto bad_bindings;
Elly Jones51a5b6c2011-10-12 19:09:26 -0400529 if (minijail_bind(j, src, dest, *writeable))
Will Drewrybee7ba72011-10-21 20:47:01 -0500530 goto bad_bindings;
Elly Jones51a5b6c2011-10-12 19:09:26 -0400531 }
532
Elly Jonese1749eb2011-10-07 13:54:59 -0400533 return 0;
Will Drewrybee7ba72011-10-21 20:47:01 -0500534
535bad_bindings:
536bad_filters:
537 if (j->chrootdir)
538 free(j->chrootdir);
539bad_chrootdir:
540 if (j->user)
541 free(j->user);
542clear_pointers:
543 j->user = NULL;
544 j->chrootdir = NULL;
545out:
546 return ret;
Will Drewry2ddaad02011-09-16 11:36:08 -0500547}
548
Elly Jonese1749eb2011-10-07 13:54:59 -0400549void minijail_preenter(struct minijail *j)
550{
551 /* Strip out options which are minijail_run() only. */
552 j->flags.vfs = 0;
553 j->flags.readonly = 0;
554 j->flags.pids = 0;
Will Drewryfe4a3722011-09-16 14:50:50 -0500555}
556
Elly Jonese1749eb2011-10-07 13:54:59 -0400557void minijail_preexec(struct minijail *j)
558{
559 int vfs = j->flags.vfs;
560 int readonly = j->flags.readonly;
561 if (j->user)
562 free(j->user);
563 j->user = NULL;
564 memset(&j->flags, 0, sizeof(j->flags));
565 /* Now restore anything we meant to keep. */
566 j->flags.vfs = vfs;
567 j->flags.readonly = readonly;
568 /* Note, pidns will already have been used before this call. */
Will Drewry2ddaad02011-09-16 11:36:08 -0500569}
570
Elly Jones51a5b6c2011-10-12 19:09:26 -0400571/* bind_one: Applies bindings from @b for @j, recursing as needed.
572 * @j Minijail these bindings are for
573 * @b Head of list of bindings
574 *
575 * Returns 0 for success.
576 */
Will Drewry6ac91122011-10-21 16:38:58 -0500577int bind_one(const struct minijail *j, struct binding *b) {
Elly Jones51a5b6c2011-10-12 19:09:26 -0400578 int ret = 0;
579 char *dest = NULL;
Elly Jones51a5b6c2011-10-12 19:09:26 -0400580 if (ret)
581 return ret;
582 /* dest has a leading "/" */
583 if (asprintf(&dest, "%s%s", j->chrootdir, b->dest) < 0)
584 return -ENOMEM;
Elly Jonesa1059632011-12-15 15:17:07 -0500585 ret = mount(b->src, dest, NULL, MS_BIND, NULL);
Elly Jones51a5b6c2011-10-12 19:09:26 -0400586 if (ret)
587 pdie("bind: %s -> %s", b->src, dest);
Elly Jonesa1059632011-12-15 15:17:07 -0500588 if (!b->writeable) {
589 ret = mount(b->src, dest, NULL,
590 MS_BIND | MS_REMOUNT | MS_RDONLY, NULL);
591 if (ret)
592 pdie("bind ro: %s -> %s", b->src, dest);
593 }
Elly Jones51a5b6c2011-10-12 19:09:26 -0400594 free(dest);
595 if (b->next)
596 return bind_one(j, b->next);
597 return ret;
598}
599
Will Drewry6ac91122011-10-21 16:38:58 -0500600int enter_chroot(const struct minijail *j) {
Elly Jones51a5b6c2011-10-12 19:09:26 -0400601 int ret;
602 if (j->bindings_head && (ret = bind_one(j, j->bindings_head)))
603 return ret;
604
605 if (chroot(j->chrootdir))
606 return -errno;
607
608 if (chdir("/"))
609 return -errno;
610
611 return 0;
612}
613
Will Drewry6ac91122011-10-21 16:38:58 -0500614int remount_readonly(void)
Elly Jonese1749eb2011-10-07 13:54:59 -0400615{
616 const char *kProcPath = "/proc";
617 const unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
618 /* Right now, we're holding a reference to our parent's old mount of
619 * /proc in our namespace, which means using MS_REMOUNT here would
620 * mutate our parent's mount as well, even though we're in a VFS
621 * namespace (!). Instead, remove their mount from our namespace
622 * and make our own.
623 */
624 if (umount(kProcPath))
625 return -errno;
626 if (mount("", kProcPath, "proc", kSafeFlags | MS_RDONLY, ""))
627 return -errno;
628 return 0;
Elly Jonescd7a9042011-07-22 13:56:51 -0400629}
630
Will Drewry6ac91122011-10-21 16:38:58 -0500631void drop_caps(const struct minijail *j)
Elly Jonese1749eb2011-10-07 13:54:59 -0400632{
633 cap_t caps = cap_get_proc();
634 cap_value_t raise_flag[1];
635 unsigned int i;
636 if (!caps)
637 die("can't get process caps");
638 if (cap_clear_flag(caps, CAP_INHERITABLE))
639 die("can't clear inheritable caps");
640 if (cap_clear_flag(caps, CAP_EFFECTIVE))
641 die("can't clear effective caps");
642 if (cap_clear_flag(caps, CAP_PERMITTED))
643 die("can't clear permitted caps");
644 for (i = 0; i < sizeof(j->caps) * 8 && cap_valid((int)i); ++i) {
645 if (i != CAP_SETPCAP && !(j->caps & (1 << i)))
646 continue;
647 raise_flag[0] = i;
648 if (cap_set_flag(caps, CAP_EFFECTIVE, 1, raise_flag, CAP_SET))
649 die("can't add effective cap");
650 if (cap_set_flag(caps, CAP_PERMITTED, 1, raise_flag, CAP_SET))
651 die("can't add permitted cap");
652 if (cap_set_flag(caps, CAP_INHERITABLE, 1, raise_flag, CAP_SET))
653 die("can't add inheritable cap");
654 }
655 if (cap_set_proc(caps))
656 die("can't apply cleaned capset");
657 cap_free(caps);
658 for (i = 0; i < sizeof(j->caps) * 8 && cap_valid((int)i); ++i) {
659 if (j->caps & (1 << i))
660 continue;
661 if (prctl(PR_CAPBSET_DROP, i))
662 pdie("prctl(PR_CAPBSET_DROP)");
663 }
Elly Jonescd7a9042011-07-22 13:56:51 -0400664}
665
Will Drewry6ac91122011-10-21 16:38:58 -0500666int setup_seccomp_filters(const struct minijail *j)
Elly Jonese1749eb2011-10-07 13:54:59 -0400667{
668 const struct seccomp_filter *sf = j->filters;
669 int ret = 0;
670 int broaden = 0;
Will Drewry32ac9f52011-08-18 21:36:27 -0500671
Elly Jonese1749eb2011-10-07 13:54:59 -0400672 /* No filters installed isn't necessarily an error. */
673 if (!sf)
674 return ret;
Will Drewry32ac9f52011-08-18 21:36:27 -0500675
Elly Jonese1749eb2011-10-07 13:54:59 -0400676 do {
677 errno = 0;
678 ret = prctl(PR_SET_SECCOMP_FILTER, PR_SECCOMP_FILTER_SYSCALL,
679 sf->nr, broaden ? "1" : sf->filter);
680 if (ret) {
681 switch (errno) {
682 case ENOSYS:
683 /* TODO(wad) make this a config option */
684 if (broaden)
685 die("CONFIG_SECCOMP_FILTER is not"
686 "supported by your kernel");
687 warn("missing CONFIG_FTRACE_SYSCALLS; relaxing"
688 "the filter for %d", sf->nr);
689 broaden = 1;
690 continue;
691 case E2BIG:
692 warn("seccomp filter too long: %d", sf->nr);
693 pdie("filter too long");
694 case ENOSPC:
695 pdie("too many seccomp filters");
696 case EPERM:
697 warn("syscall filter disallowed for %d",
698 sf->nr);
699 pdie("failed to install seccomp filter");
700 case EINVAL:
701 warn("seccomp filter or call method is"
702 " invalid. %d:'%s'", sf->nr, sf->filter);
703 default:
704 pdie("failed to install seccomp filter");
705 }
706 }
707 sf = sf->next;
708 broaden = 0;
709 } while (sf != j->filters);
710 return ret;
Will Drewry32ac9f52011-08-18 21:36:27 -0500711}
712
Will Drewry6ac91122011-10-21 16:38:58 -0500713void API minijail_enter(const struct minijail *j)
Elly Jonese1749eb2011-10-07 13:54:59 -0400714{
715 if (j->flags.pids)
716 die("tried to enter a pid-namespaced jail;"
717 "try minijail_run()?");
Elly Jonescd7a9042011-07-22 13:56:51 -0400718
Elly Jonese1749eb2011-10-07 13:54:59 -0400719 if (j->flags.seccomp_filter && setup_seccomp_filters(j))
720 pdie("failed to configure seccomp filters");
Will Drewry32ac9f52011-08-18 21:36:27 -0500721
Elly Jonese1749eb2011-10-07 13:54:59 -0400722 if (j->flags.usergroups && !j->user)
723 die("usergroup inheritance without username");
Elly Jonescd7a9042011-07-22 13:56:51 -0400724
Elly Jonese1749eb2011-10-07 13:54:59 -0400725 /* We can't recover from failures if we've dropped privileges partially,
726 * so we don't even try. If any of our operations fail, we abort() the
727 * entire process.
728 */
729 if (j->flags.vfs && unshare(CLONE_NEWNS))
730 pdie("unshare");
Elly Jonescd7a9042011-07-22 13:56:51 -0400731
Elly Jones51a5b6c2011-10-12 19:09:26 -0400732 if (j->flags.chroot && enter_chroot(j))
733 pdie("chroot");
734
Elly Jonese1749eb2011-10-07 13:54:59 -0400735 if (j->flags.readonly && remount_readonly())
736 pdie("remount");
Elly Jonescd7a9042011-07-22 13:56:51 -0400737
Elly Jonese1749eb2011-10-07 13:54:59 -0400738 if (j->flags.caps) {
739 /* POSIX capabilities are a bit tricky. If we drop our
740 * capability to change uids, our attempt to use setuid()
741 * below will fail. Hang on to root caps across setuid(), then
742 * lock securebits.
743 */
744 if (prctl(PR_SET_KEEPCAPS, 1))
745 pdie("prctl(PR_SET_KEEPCAPS)");
746 if (prctl
747 (PR_SET_SECUREBITS, SECURE_ALL_BITS | SECURE_ALL_LOCKS))
748 pdie("prctl(PR_SET_SECUREBITS)");
749 }
Elly Jonescd7a9042011-07-22 13:56:51 -0400750
Elly Jonese1749eb2011-10-07 13:54:59 -0400751 if (j->flags.usergroups) {
752 if (initgroups(j->user, j->usergid))
753 pdie("initgroups");
754 } else {
755 /* Only attempt to clear supplemental groups if we are changing
756 * users. */
757 if ((j->uid || j->gid) && setgroups(0, NULL))
758 pdie("setgroups");
759 }
Elly Jonescd7a9042011-07-22 13:56:51 -0400760
Elly Jonese1749eb2011-10-07 13:54:59 -0400761 if (j->flags.gid && setresgid(j->gid, j->gid, j->gid))
762 pdie("setresgid");
Elly Jonescd7a9042011-07-22 13:56:51 -0400763
Elly Jonese1749eb2011-10-07 13:54:59 -0400764 if (j->flags.uid && setresuid(j->uid, j->uid, j->uid))
765 pdie("setresuid");
Elly Jonescd7a9042011-07-22 13:56:51 -0400766
Elly Jonese1749eb2011-10-07 13:54:59 -0400767 if (j->flags.caps)
768 drop_caps(j);
Elly Jonescd7a9042011-07-22 13:56:51 -0400769
Elly Jonese1749eb2011-10-07 13:54:59 -0400770 /* seccomp has to come last since it cuts off all the other
771 * privilege-dropping syscalls :)
772 */
773 if (j->flags.seccomp_filter && prctl(PR_SET_SECCOMP, 13))
774 pdie("prctl(PR_SET_SECCOMP, 13)");
Will Drewry32ac9f52011-08-18 21:36:27 -0500775
Elly Jonese1749eb2011-10-07 13:54:59 -0400776 if (j->flags.seccomp && prctl(PR_SET_SECCOMP, 1))
777 pdie("prctl(PR_SET_SECCOMP)");
Elly Jonescd7a9042011-07-22 13:56:51 -0400778}
779
Will Drewry6ac91122011-10-21 16:38:58 -0500780/* TODO(wad) will visibility affect this variable? */
Elly Jonescd7a9042011-07-22 13:56:51 -0400781static int init_exitstatus = 0;
782
Will Drewry6ac91122011-10-21 16:38:58 -0500783void init_term(int __attribute__ ((unused)) sig)
Elly Jonese1749eb2011-10-07 13:54:59 -0400784{
785 _exit(init_exitstatus);
Elly Jonescd7a9042011-07-22 13:56:51 -0400786}
787
Will Drewry6ac91122011-10-21 16:38:58 -0500788int init(pid_t rootpid)
Elly Jonese1749eb2011-10-07 13:54:59 -0400789{
790 pid_t pid;
791 int status;
792 /* so that we exit with the right status */
793 signal(SIGTERM, init_term);
794 /* TODO(wad) self jail with seccomp_filters here. */
795 while ((pid = wait(&status)) > 0) {
796 /* This loop will only end when either there are no processes
797 * left inside our pid namespace or we get a signal.
798 */
799 if (pid == rootpid)
800 init_exitstatus = status;
801 }
802 if (!WIFEXITED(init_exitstatus))
803 _exit(MINIJAIL_ERR_INIT);
804 _exit(WEXITSTATUS(init_exitstatus));
Elly Jonescd7a9042011-07-22 13:56:51 -0400805}
806
Will Drewry6ac91122011-10-21 16:38:58 -0500807int API minijail_from_fd(int fd, struct minijail *j)
Elly Jonese1749eb2011-10-07 13:54:59 -0400808{
809 size_t sz = 0;
810 size_t bytes = read(fd, &sz, sizeof(sz));
811 char *buf;
812 int r;
813 if (sizeof(sz) != bytes)
814 return -EINVAL;
815 if (sz > USHRT_MAX) /* Arbitrary sanity check */
816 return -E2BIG;
817 buf = malloc(sz);
818 if (!buf)
819 return -ENOMEM;
820 bytes = read(fd, buf, sz);
821 if (bytes != sz) {
822 free(buf);
823 return -EINVAL;
824 }
825 r = minijail_unmarshal(j, buf, sz);
826 free(buf);
827 return r;
Will Drewry2f54b6a2011-09-16 13:45:31 -0500828}
829
Will Drewry6ac91122011-10-21 16:38:58 -0500830int API minijail_to_fd(struct minijail *j, int fd)
Elly Jonese1749eb2011-10-07 13:54:59 -0400831{
832 char *buf;
833 size_t sz = minijail_size(j);
834 ssize_t written;
835 int r;
Elly Jonescd7a9042011-07-22 13:56:51 -0400836
Elly Jonese1749eb2011-10-07 13:54:59 -0400837 if (!sz)
838 return -EINVAL;
839 buf = malloc(sz);
840 r = minijail_marshal(j, buf, sz);
841 if (r) {
842 free(buf);
843 return r;
844 }
845 /* Sends [size][minijail]. */
846 written = write(fd, &sz, sizeof(sz));
847 if (written != sizeof(sz)) {
848 free(buf);
849 return -EFAULT;
850 }
851 written = write(fd, buf, sz);
852 if (written < 0 || (size_t) written != sz) {
853 free(buf);
854 return -EFAULT;
855 }
856 free(buf);
857 return 0;
Will Drewry2f54b6a2011-09-16 13:45:31 -0500858}
Elly Jonescd7a9042011-07-22 13:56:51 -0400859
Will Drewry6ac91122011-10-21 16:38:58 -0500860int setup_preload(void)
Elly Jonese1749eb2011-10-07 13:54:59 -0400861{
862 char *oldenv = getenv(kLdPreloadEnvVar) ? : "";
863 char *newenv = malloc(strlen(oldenv) + 2 + strlen(PRELOADPATH));
864 if (!newenv)
865 return -ENOMEM;
Elly Jonescd7a9042011-07-22 13:56:51 -0400866
Elly Jonese1749eb2011-10-07 13:54:59 -0400867 /* Only insert a separating space if we have something to separate... */
868 sprintf(newenv, "%s%s%s", oldenv, strlen(oldenv) ? " " : "",
869 PRELOADPATH);
Elly Jonescd7a9042011-07-22 13:56:51 -0400870
Elly Jonese1749eb2011-10-07 13:54:59 -0400871 /* setenv() makes a copy of the string we give it */
872 setenv(kLdPreloadEnvVar, newenv, 1);
873 free(newenv);
874 return 0;
Elly Jonescd7a9042011-07-22 13:56:51 -0400875}
876
Will Drewry6ac91122011-10-21 16:38:58 -0500877int setup_pipe(int fds[2])
Elly Jonese1749eb2011-10-07 13:54:59 -0400878{
879 int r = pipe(fds);
880 char fd_buf[11];
881 if (r)
882 return r;
883 r = snprintf(fd_buf, sizeof(fd_buf), "%d", fds[0]);
884 if (r <= 0)
885 return -EINVAL;
886 setenv(kFdEnvVar, fd_buf, 1);
887 return 0;
Will Drewryf89aef52011-09-16 16:48:57 -0500888}
889
Will Drewry6ac91122011-10-21 16:38:58 -0500890int API minijail_run(struct minijail *j, const char *filename,
891 char *const argv[])
Elly Jonese1749eb2011-10-07 13:54:59 -0400892{
893 unsigned int pidns = j->flags.pids ? CLONE_NEWPID : 0;
894 char *oldenv, *oldenv_copy = NULL;
895 pid_t child_pid;
896 int pipe_fds[2];
897 int ret;
Ben Chan541c7e52011-08-26 14:55:53 -0700898
Elly Jonese1749eb2011-10-07 13:54:59 -0400899 oldenv = getenv(kLdPreloadEnvVar);
900 if (oldenv) {
901 oldenv_copy = strdup(oldenv);
902 if (!oldenv_copy)
903 return -ENOMEM;
904 }
Will Drewryf89aef52011-09-16 16:48:57 -0500905
Elly Jonese1749eb2011-10-07 13:54:59 -0400906 if (setup_preload())
907 return -EFAULT;
Will Drewry2f54b6a2011-09-16 13:45:31 -0500908
Elly Jonese1749eb2011-10-07 13:54:59 -0400909 /* Before we fork(2) and execve(2) the child process, we need to open
910 * a pipe(2) to send the minijail configuration over.
911 */
912 if (setup_pipe(pipe_fds))
913 return -EFAULT;
Elly Jonescd7a9042011-07-22 13:56:51 -0400914
Elly Jonese1749eb2011-10-07 13:54:59 -0400915 child_pid = syscall(SYS_clone, pidns | SIGCHLD, NULL);
916 if (child_pid < 0) {
917 free(oldenv_copy);
918 return child_pid;
919 }
Will Drewryf89aef52011-09-16 16:48:57 -0500920
Elly Jonese1749eb2011-10-07 13:54:59 -0400921 if (child_pid) {
922 /* Restore parent's LD_PRELOAD. */
923 if (oldenv_copy) {
924 setenv(kLdPreloadEnvVar, oldenv_copy, 1);
925 free(oldenv_copy);
926 } else {
927 unsetenv(kLdPreloadEnvVar);
928 }
929 unsetenv(kFdEnvVar);
930 j->initpid = child_pid;
931 close(pipe_fds[0]); /* read endpoint */
932 ret = minijail_to_fd(j, pipe_fds[1]);
933 close(pipe_fds[1]); /* write endpoint */
934 if (ret) {
935 kill(j->initpid, SIGKILL);
936 die("failed to send marshalled minijail");
937 }
938 return 0;
939 }
940 free(oldenv_copy);
Ben Chan541c7e52011-08-26 14:55:53 -0700941
Elly Jonese1749eb2011-10-07 13:54:59 -0400942 /* Drop everything that cannot be inherited across execve. */
943 minijail_preexec(j);
944 /* Jail this process and its descendants... */
945 minijail_enter(j);
Elly Jonescd7a9042011-07-22 13:56:51 -0400946
Elly Jonese1749eb2011-10-07 13:54:59 -0400947 if (pidns) {
948 /* pid namespace: this process will become init inside the new
949 * namespace, so fork off a child to actually run the program
950 * (we don't want all programs we might exec to have to know
951 * how to be init).
952 */
953 child_pid = fork();
954 if (child_pid < 0)
955 _exit(child_pid);
956 else if (child_pid > 0)
957 init(child_pid); /* never returns */
958 }
Elly Jonescd7a9042011-07-22 13:56:51 -0400959
Elly Jonese1749eb2011-10-07 13:54:59 -0400960 /* If we aren't pid-namespaced:
961 * calling process
962 * -> execve()-ing process
963 * If we are:
964 * calling process
965 * -> init()-ing process
966 * -> execve()-ing process
967 */
968 _exit(execve(filename, argv, environ));
Elly Jonescd7a9042011-07-22 13:56:51 -0400969}
970
Will Drewry6ac91122011-10-21 16:38:58 -0500971int API minijail_kill(struct minijail *j)
Elly Jonese1749eb2011-10-07 13:54:59 -0400972{
973 int st;
974 if (kill(j->initpid, SIGTERM))
975 return -errno;
976 if (waitpid(j->initpid, &st, 0) < 0)
977 return -errno;
978 return st;
Elly Jonescd7a9042011-07-22 13:56:51 -0400979}
980
Will Drewry6ac91122011-10-21 16:38:58 -0500981int API minijail_wait(struct minijail *j)
Elly Jonese1749eb2011-10-07 13:54:59 -0400982{
983 int st;
984 if (waitpid(j->initpid, &st, 0) < 0)
985 return -errno;
986 if (!WIFEXITED(st))
987 return MINIJAIL_ERR_JAIL;
988 return WEXITSTATUS(st);
Elly Jonescd7a9042011-07-22 13:56:51 -0400989}
990
Will Drewry6ac91122011-10-21 16:38:58 -0500991void API minijail_destroy(struct minijail *j)
Elly Jonese1749eb2011-10-07 13:54:59 -0400992{
993 struct seccomp_filter *f = j->filters;
994 /* Unlink the tail and head */
995 if (f)
996 f->prev->next = NULL;
997 while (f) {
998 struct seccomp_filter *next = f->next;
999 free(f->filter);
1000 free(f);
1001 f = next;
1002 }
Elly Jones51a5b6c2011-10-12 19:09:26 -04001003 while (j->bindings_head) {
1004 struct binding *b = j->bindings_head;
1005 j->bindings_head = j->bindings_head->next;
1006 free(b->dest);
1007 free(b->src);
1008 free(b);
1009 }
1010 j->bindings_tail = NULL;
Elly Jonese1749eb2011-10-07 13:54:59 -04001011 if (j->user)
1012 free(j->user);
Will Drewrybee7ba72011-10-21 20:47:01 -05001013 if (j->chrootdir)
1014 free(j->chrootdir);
Elly Jonese1749eb2011-10-07 13:54:59 -04001015 free(j);
Elly Jonescd7a9042011-07-22 13:56:51 -04001016}