blob: 9600047ed76fafcac6f1264f584576d2e3948471 [file] [log] [blame]
Elly Jonescd7a9042011-07-22 13:56:51 -04001/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2 * 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
Elly Jonese1749eb2011-10-07 13:54:59 -040096struct minijail *minijail_new(void)
97{
Elly Jones51a5b6c2011-10-12 19:09:26 -040098 return calloc(1, sizeof(struct minijail));
Elly Jonescd7a9042011-07-22 13:56:51 -040099}
100
Elly Jonese1749eb2011-10-07 13:54:59 -0400101void minijail_change_uid(struct minijail *j, uid_t uid)
102{
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
Elly Jonese1749eb2011-10-07 13:54:59 -0400109void minijail_change_gid(struct minijail *j, gid_t gid)
110{
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
Elly Jonese1749eb2011-10-07 13:54:59 -0400117int minijail_change_user(struct minijail *j, const char *user)
118{
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
Elly Jonese1749eb2011-10-07 13:54:59 -0400144int minijail_change_group(struct minijail *j, const char *group)
145{
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
Elly Jonese1749eb2011-10-07 13:54:59 -0400167void minijail_use_seccomp(struct minijail *j)
168{
169 j->flags.seccomp = 1;
Elly Jonescd7a9042011-07-22 13:56:51 -0400170}
171
Elly Jonese1749eb2011-10-07 13:54:59 -0400172void minijail_use_seccomp_filter(struct minijail *j)
173{
174 j->flags.seccomp_filter = 1;
Will Drewry32ac9f52011-08-18 21:36:27 -0500175}
176
Elly Jonese1749eb2011-10-07 13:54:59 -0400177void minijail_use_caps(struct minijail *j, uint64_t capmask)
178{
179 j->caps = capmask;
180 j->flags.caps = 1;
Elly Jonescd7a9042011-07-22 13:56:51 -0400181}
182
Elly Jonese1749eb2011-10-07 13:54:59 -0400183void minijail_namespace_vfs(struct minijail *j)
184{
185 j->flags.vfs = 1;
Elly Jonescd7a9042011-07-22 13:56:51 -0400186}
187
Elly Jonese1749eb2011-10-07 13:54:59 -0400188void minijail_namespace_pids(struct minijail *j)
189{
190 j->flags.pids = 1;
Elly Jonescd7a9042011-07-22 13:56:51 -0400191}
192
Elly Jonese1749eb2011-10-07 13:54:59 -0400193void minijail_remount_readonly(struct minijail *j)
194{
195 j->flags.vfs = 1;
196 j->flags.readonly = 1;
Elly Jonescd7a9042011-07-22 13:56:51 -0400197}
198
Elly Jonese1749eb2011-10-07 13:54:59 -0400199void minijail_inherit_usergroups(struct minijail *j)
200{
201 j->flags.usergroups = 1;
Elly Jonescd7a9042011-07-22 13:56:51 -0400202}
203
Elly Jonese1749eb2011-10-07 13:54:59 -0400204void minijail_disable_ptrace(struct minijail *j)
205{
206 j->flags.ptrace = 1;
Elly Jonescd7a9042011-07-22 13:56:51 -0400207}
208
Elly Jones51a5b6c2011-10-12 19:09:26 -0400209int minijail_enter_chroot(struct minijail *j, const char *dir) {
210 if (j->chrootdir)
211 return -EINVAL;
212 j->chrootdir = strdup(dir);
213 if (!j->chrootdir)
214 return -ENOMEM;
215 j->flags.chroot = 1;
216 return 0;
217}
218
219int minijail_bind(struct minijail *j, const char *src, const char *dest,
220 int writeable) {
221 struct binding *b;
222
223 if (*dest != '/')
224 return -EINVAL;
225 b = calloc(1, sizeof(*b));
226 if (!b)
227 return -ENOMEM;
228 b->dest = strdup(dest);
229 if (!b->dest)
230 goto error;
231 b->src = strdup(src);
232 if (!b->src)
233 goto error;
234 b->writeable = writeable;
235
236 syslog(LOG_INFO, "libminijail: bind %s -> %s", src, dest);
237
238 /* Force vfs namespacing so the bind mounts don't leak out into the
239 * containing vfs namespace.
240 */
241 minijail_namespace_vfs(j);
242
243 if (j->bindings_tail)
244 j->bindings_tail->next = b;
245 else
246 j->bindings_head = b;
247 j->bindings_tail = b;
248 j->binding_count++;
249
250 return 0;
251
252error:
253 free(b->src);
254 free(b->dest);
255 free(b);
256 return -ENOMEM;
257}
258
Elly Jonese1749eb2011-10-07 13:54:59 -0400259int minijail_add_seccomp_filter(struct minijail *j, int nr, const char *filter)
260{
261 struct seccomp_filter *sf;
262 if (!filter || nr < 0)
263 return -EINVAL;
Will Drewry32ac9f52011-08-18 21:36:27 -0500264
Elly Jonese1749eb2011-10-07 13:54:59 -0400265 sf = malloc(sizeof(*sf));
266 if (!sf)
267 return -ENOMEM;
268 sf->nr = nr;
269 sf->filter = strndup(filter, MINIJAIL_MAX_SECCOMP_FILTER_LINE);
270 if (!sf->filter) {
271 free(sf);
272 return -ENOMEM;
273 }
Will Drewry32ac9f52011-08-18 21:36:27 -0500274
Elly Jonese1749eb2011-10-07 13:54:59 -0400275 j->filter_count++;
Will Drewryf89aef52011-09-16 16:48:57 -0500276
Elly Jonese1749eb2011-10-07 13:54:59 -0400277 if (!j->filters) {
278 j->filters = sf;
279 sf->next = sf;
280 sf->prev = sf;
281 return 0;
282 }
283 sf->next = j->filters;
284 sf->prev = j->filters->prev;
285 sf->prev->next = sf;
286 j->filters->prev = sf;
287 return 0;
Will Drewry32ac9f52011-08-18 21:36:27 -0500288}
289
Elly Jonese1749eb2011-10-07 13:54:59 -0400290int minijail_lookup_syscall(const char *name)
291{
292 const struct syscall_entry *entry = syscall_table;
293 for (; entry->name && entry->nr >= 0; ++entry)
294 if (!strcmp(entry->name, name))
295 return entry->nr;
296 return -1;
Will Drewry32ac9f52011-08-18 21:36:27 -0500297}
298
Elly Jonese1749eb2011-10-07 13:54:59 -0400299static char *strip(char *s)
300{
301 char *end;
302 while (*s && isblank(*s))
303 s++;
304 end = s + strlen(s) - 1;
305 while (*end && (isblank(*end) || *end == '\n'))
306 end--;
307 *(end + 1) = '\0';
308 return s;
Will Drewry32ac9f52011-08-18 21:36:27 -0500309}
310
Elly Jonese1749eb2011-10-07 13:54:59 -0400311void minijail_parse_seccomp_filters(struct minijail *j, const char *path)
312{
313 FILE *file = fopen(path, "r");
314 char line[MINIJAIL_MAX_SECCOMP_FILTER_LINE];
315 int count = 1;
316 if (!file)
317 pdie("failed to open seccomp filters file");
Will Drewry32ac9f52011-08-18 21:36:27 -0500318
Elly Jonese1749eb2011-10-07 13:54:59 -0400319 /* Format is simple:
320 * syscall_name<COLON><FILTER STRING>[\n|EOF]
321 * #...comment...
322 * <empty line?
323 */
324 while (fgets(line, sizeof(line), file)) {
325 char *filter = line;
326 char *name = strsep(&filter, ":");
327 char *name_end = NULL;
328 int nr = -1;
Will Drewry32ac9f52011-08-18 21:36:27 -0500329
Elly Jonese1749eb2011-10-07 13:54:59 -0400330 if (!name)
331 die("invalid filter on line %d", count);
Will Drewry32ac9f52011-08-18 21:36:27 -0500332
Elly Jonese1749eb2011-10-07 13:54:59 -0400333 name = strip(name);
Will Drewry32ac9f52011-08-18 21:36:27 -0500334
Elly Jonese1749eb2011-10-07 13:54:59 -0400335 if (!filter) {
336 if (strlen(name))
337 die("invalid filter on line %d", count);
338 /* Allow empty lines */
339 continue;
340 }
Will Drewry32ac9f52011-08-18 21:36:27 -0500341
Elly Jonese1749eb2011-10-07 13:54:59 -0400342 /* Allow comment lines */
343 if (*name == '#')
344 continue;
Will Drewry32ac9f52011-08-18 21:36:27 -0500345
Elly Jonese1749eb2011-10-07 13:54:59 -0400346 filter = strip(filter);
Will Drewry32ac9f52011-08-18 21:36:27 -0500347
Elly Jonese1749eb2011-10-07 13:54:59 -0400348 /* Take direct syscall numbers */
349 nr = strtol(name, &name_end, 0);
350 /* Or fail-over to using names */
351 if (*name_end != '\0')
352 nr = minijail_lookup_syscall(name);
353 if (nr < 0)
354 die("syscall '%s' unknown", name);
Will Drewry32ac9f52011-08-18 21:36:27 -0500355
Elly Jonese1749eb2011-10-07 13:54:59 -0400356 if (minijail_add_seccomp_filter(j, nr, filter))
357 pdie("failed to add filter for syscall '%s'", name);
358 }
359 fclose(file);
Will Drewry32ac9f52011-08-18 21:36:27 -0500360}
361
Will Drewryf89aef52011-09-16 16:48:57 -0500362struct marshal_state {
Elly Jonese1749eb2011-10-07 13:54:59 -0400363 size_t available;
364 size_t total;
365 char *buf;
Will Drewryf89aef52011-09-16 16:48:57 -0500366};
367
368static void marshal_state_init(struct marshal_state *state,
Elly Jonese1749eb2011-10-07 13:54:59 -0400369 char *buf, size_t available)
370{
371 state->available = available;
372 state->buf = buf;
373 state->total = 0;
Will Drewryf89aef52011-09-16 16:48:57 -0500374}
375
376static void marshal_append(struct marshal_state *state,
Elly Jonese1749eb2011-10-07 13:54:59 -0400377 char *src, size_t length)
378{
379 size_t copy_len = MIN(state->available, length);
Will Drewryf89aef52011-09-16 16:48:57 -0500380
Elly Jonese1749eb2011-10-07 13:54:59 -0400381 /* Up to |available| will be written. */
382 if (copy_len) {
383 memcpy(state->buf, src, copy_len);
384 state->buf += copy_len;
385 state->available -= copy_len;
386 }
387 /* |total| will contain the expected length. */
388 state->total += length;
Will Drewryf89aef52011-09-16 16:48:57 -0500389}
390
391static void minijail_marshal_helper(struct marshal_state *state,
Elly Jonese1749eb2011-10-07 13:54:59 -0400392 const struct minijail *j)
393{
Elly Jones51a5b6c2011-10-12 19:09:26 -0400394 struct binding *b = NULL;
Elly Jonese1749eb2011-10-07 13:54:59 -0400395 marshal_append(state, (char *)j, sizeof(*j));
396 if (j->user)
397 marshal_append(state, j->user, strlen(j->user) + 1);
Elly Jones51a5b6c2011-10-12 19:09:26 -0400398 if (j->chrootdir)
399 marshal_append(state, j->chrootdir, strlen(j->chrootdir) + 1);
Elly Jonese1749eb2011-10-07 13:54:59 -0400400 if (j->flags.seccomp_filter && j->filters) {
401 struct seccomp_filter *f = j->filters;
402 do {
403 marshal_append(state, (char *)&f->nr, sizeof(f->nr));
404 marshal_append(state, f->filter, strlen(f->filter) + 1);
405 f = f->next;
406 } while (f != j->filters);
407 }
Elly Jones51a5b6c2011-10-12 19:09:26 -0400408 for (b = j->bindings_head; b; b = b->next) {
409 marshal_append(state, b->src, strlen(b->src) + 1);
410 marshal_append(state, b->dest, strlen(b->dest) + 1);
411 marshal_append(state, (char *)&b->writeable, sizeof(b->writeable));
412 }
Will Drewryf89aef52011-09-16 16:48:57 -0500413}
414
Elly Jonese1749eb2011-10-07 13:54:59 -0400415size_t minijail_size(const struct minijail *j)
416{
417 struct marshal_state state;
418 marshal_state_init(&state, NULL, 0);
419 minijail_marshal_helper(&state, j);
420 return state.total;
Will Drewry2ddaad02011-09-16 11:36:08 -0500421}
422
Elly Jonese1749eb2011-10-07 13:54:59 -0400423int minijail_marshal(const struct minijail *j, char *buf, size_t available)
424{
425 struct marshal_state state;
426 marshal_state_init(&state, buf, available);
427 minijail_marshal_helper(&state, j);
428 return (state.total > available);
Will Drewry2ddaad02011-09-16 11:36:08 -0500429}
430
Elly Jones51a5b6c2011-10-12 19:09:26 -0400431/* consumebytes: consumes @length bytes from a buffer @buf of length @buflength
432 * @length Number of bytes to consume
433 * @buf Buffer to consume from
434 * @buflength Size of @buf
435 *
436 * Returns a pointer to the base of the bytes, or NULL for errors.
437 */
438static void *consumebytes(size_t length, char **buf, size_t *buflength) {
439 char *p = *buf;
440 if (length > *buflength)
441 return NULL;
442 *buf += length;
443 *buflength -= length;
444 return p;
445}
446
447/* consumestr: consumes a C string from a buffer @buf of length @length
448 * @buf Buffer to consume
449 * @length Length of buffer
450 *
451 * Returns a pointer to the base of the string, or NULL for errors.
452 */
453static char *consumestr(char **buf, size_t *buflength) {
454 size_t len = strnlen(*buf, *buflength);
455 if (len == *buflength)
456 /* There's no null-terminator */
457 return NULL;
458 return consumebytes(len + 1, buf, buflength);
459}
460
Elly Jonese1749eb2011-10-07 13:54:59 -0400461int minijail_unmarshal(struct minijail *j, char *serialized, size_t length)
462{
Elly Jones51a5b6c2011-10-12 19:09:26 -0400463 int i;
464 int count;
Elly Jonese1749eb2011-10-07 13:54:59 -0400465 if (length < sizeof(*j))
466 return -EINVAL;
467 memcpy((void *)j, serialized, sizeof(*j));
468 serialized += sizeof(*j);
469 length -= sizeof(*j);
Will Drewryf89aef52011-09-16 16:48:57 -0500470
Elly Jonese1749eb2011-10-07 13:54:59 -0400471 if (j->user) { /* stale pointer */
Elly Jones51a5b6c2011-10-12 19:09:26 -0400472 char *user = consumestr(&serialized, &length);
473 if (!user)
Elly Jonese1749eb2011-10-07 13:54:59 -0400474 return -EINVAL;
Elly Jones51a5b6c2011-10-12 19:09:26 -0400475 j->user = strdup(user);
Elly Jonese1749eb2011-10-07 13:54:59 -0400476 }
Will Drewryf89aef52011-09-16 16:48:57 -0500477
Elly Jonese1749eb2011-10-07 13:54:59 -0400478 if (j->flags.seccomp_filter && j->filter_count) {
Elly Jones51a5b6c2011-10-12 19:09:26 -0400479 count = j->filter_count;
Elly Jonese1749eb2011-10-07 13:54:59 -0400480 /* Let add_seccomp_filter recompute the value. */
481 j->filter_count = 0;
482 j->filters = NULL; /* Don't follow the stale pointer. */
483 for (; count > 0; --count) {
Elly Jones51a5b6c2011-10-12 19:09:26 -0400484 int *nr = (int *)consumebytes(sizeof(*nr), &serialized,
485 &length);
Elly Jonese1749eb2011-10-07 13:54:59 -0400486 char *filter;
Elly Jones51a5b6c2011-10-12 19:09:26 -0400487 if (!nr)
Elly Jonese1749eb2011-10-07 13:54:59 -0400488 return -EINVAL;
Elly Jones51a5b6c2011-10-12 19:09:26 -0400489 filter = consumestr(&serialized, &length);
490 if (!filter)
Elly Jonese1749eb2011-10-07 13:54:59 -0400491 return -EINVAL;
Elly Jonese1749eb2011-10-07 13:54:59 -0400492 if (minijail_add_seccomp_filter(j, *nr, filter))
493 return -EINVAL;
Elly Jonese1749eb2011-10-07 13:54:59 -0400494 }
495 }
Elly Jones51a5b6c2011-10-12 19:09:26 -0400496
497 count = j->binding_count;
498 j->bindings_head = NULL;
499 j->bindings_tail = NULL;
500 j->binding_count = 0;
501 for (i = 0; i < count; ++i) {
502 int *writeable;
503 const char *dest;
504 const char *src = consumestr(&serialized, &length);
505 if (!src)
506 return -EINVAL;
507 dest = consumestr(&serialized, &length);
508 if (!dest)
509 return -EINVAL;
510 writeable = consumebytes(sizeof(*writeable), &serialized, &length);
511 if (!writeable)
512 return -EINVAL;
513 if (minijail_bind(j, src, dest, *writeable))
514 return -EINVAL;
515 }
516
Elly Jonese1749eb2011-10-07 13:54:59 -0400517 return 0;
Will Drewry2ddaad02011-09-16 11:36:08 -0500518}
519
Elly Jonese1749eb2011-10-07 13:54:59 -0400520void minijail_preenter(struct minijail *j)
521{
522 /* Strip out options which are minijail_run() only. */
523 j->flags.vfs = 0;
524 j->flags.readonly = 0;
525 j->flags.pids = 0;
Will Drewryfe4a3722011-09-16 14:50:50 -0500526}
527
Elly Jonese1749eb2011-10-07 13:54:59 -0400528void minijail_preexec(struct minijail *j)
529{
530 int vfs = j->flags.vfs;
531 int readonly = j->flags.readonly;
532 if (j->user)
533 free(j->user);
534 j->user = NULL;
535 memset(&j->flags, 0, sizeof(j->flags));
536 /* Now restore anything we meant to keep. */
537 j->flags.vfs = vfs;
538 j->flags.readonly = readonly;
539 /* Note, pidns will already have been used before this call. */
Will Drewry2ddaad02011-09-16 11:36:08 -0500540}
541
Elly Jones51a5b6c2011-10-12 19:09:26 -0400542/* bind_one: Applies bindings from @b for @j, recursing as needed.
543 * @j Minijail these bindings are for
544 * @b Head of list of bindings
545 *
546 * Returns 0 for success.
547 */
548static int bind_one(const struct minijail *j, struct binding *b) {
549 int ret = 0;
550 char *dest = NULL;
551 int mflags = MS_BIND | (b->writeable ? 0 : MS_RDONLY);
552 if (ret)
553 return ret;
554 /* dest has a leading "/" */
555 if (asprintf(&dest, "%s%s", j->chrootdir, b->dest) < 0)
556 return -ENOMEM;
557 ret = mount(b->src, dest, NULL, mflags, NULL);
558 if (ret)
559 pdie("bind: %s -> %s", b->src, dest);
560 free(dest);
561 if (b->next)
562 return bind_one(j, b->next);
563 return ret;
564}
565
566static int enter_chroot(const struct minijail *j) {
567 int ret;
568 if (j->bindings_head && (ret = bind_one(j, j->bindings_head)))
569 return ret;
570
571 if (chroot(j->chrootdir))
572 return -errno;
573
574 if (chdir("/"))
575 return -errno;
576
577 return 0;
578}
579
580
581
Elly Jonese1749eb2011-10-07 13:54:59 -0400582static int remount_readonly(void)
583{
584 const char *kProcPath = "/proc";
585 const unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
586 /* Right now, we're holding a reference to our parent's old mount of
587 * /proc in our namespace, which means using MS_REMOUNT here would
588 * mutate our parent's mount as well, even though we're in a VFS
589 * namespace (!). Instead, remove their mount from our namespace
590 * and make our own.
591 */
592 if (umount(kProcPath))
593 return -errno;
594 if (mount("", kProcPath, "proc", kSafeFlags | MS_RDONLY, ""))
595 return -errno;
596 return 0;
Elly Jonescd7a9042011-07-22 13:56:51 -0400597}
598
Elly Jonese1749eb2011-10-07 13:54:59 -0400599static void drop_caps(const struct minijail *j)
600{
601 cap_t caps = cap_get_proc();
602 cap_value_t raise_flag[1];
603 unsigned int i;
604 if (!caps)
605 die("can't get process caps");
606 if (cap_clear_flag(caps, CAP_INHERITABLE))
607 die("can't clear inheritable caps");
608 if (cap_clear_flag(caps, CAP_EFFECTIVE))
609 die("can't clear effective caps");
610 if (cap_clear_flag(caps, CAP_PERMITTED))
611 die("can't clear permitted caps");
612 for (i = 0; i < sizeof(j->caps) * 8 && cap_valid((int)i); ++i) {
613 if (i != CAP_SETPCAP && !(j->caps & (1 << i)))
614 continue;
615 raise_flag[0] = i;
616 if (cap_set_flag(caps, CAP_EFFECTIVE, 1, raise_flag, CAP_SET))
617 die("can't add effective cap");
618 if (cap_set_flag(caps, CAP_PERMITTED, 1, raise_flag, CAP_SET))
619 die("can't add permitted cap");
620 if (cap_set_flag(caps, CAP_INHERITABLE, 1, raise_flag, CAP_SET))
621 die("can't add inheritable cap");
622 }
623 if (cap_set_proc(caps))
624 die("can't apply cleaned capset");
625 cap_free(caps);
626 for (i = 0; i < sizeof(j->caps) * 8 && cap_valid((int)i); ++i) {
627 if (j->caps & (1 << i))
628 continue;
629 if (prctl(PR_CAPBSET_DROP, i))
630 pdie("prctl(PR_CAPBSET_DROP)");
631 }
Elly Jonescd7a9042011-07-22 13:56:51 -0400632}
633
Elly Jonese1749eb2011-10-07 13:54:59 -0400634static int setup_seccomp_filters(const struct minijail *j)
635{
636 const struct seccomp_filter *sf = j->filters;
637 int ret = 0;
638 int broaden = 0;
Will Drewry32ac9f52011-08-18 21:36:27 -0500639
Elly Jonese1749eb2011-10-07 13:54:59 -0400640 /* No filters installed isn't necessarily an error. */
641 if (!sf)
642 return ret;
Will Drewry32ac9f52011-08-18 21:36:27 -0500643
Elly Jonese1749eb2011-10-07 13:54:59 -0400644 do {
645 errno = 0;
646 ret = prctl(PR_SET_SECCOMP_FILTER, PR_SECCOMP_FILTER_SYSCALL,
647 sf->nr, broaden ? "1" : sf->filter);
648 if (ret) {
649 switch (errno) {
650 case ENOSYS:
651 /* TODO(wad) make this a config option */
652 if (broaden)
653 die("CONFIG_SECCOMP_FILTER is not"
654 "supported by your kernel");
655 warn("missing CONFIG_FTRACE_SYSCALLS; relaxing"
656 "the filter for %d", sf->nr);
657 broaden = 1;
658 continue;
659 case E2BIG:
660 warn("seccomp filter too long: %d", sf->nr);
661 pdie("filter too long");
662 case ENOSPC:
663 pdie("too many seccomp filters");
664 case EPERM:
665 warn("syscall filter disallowed for %d",
666 sf->nr);
667 pdie("failed to install seccomp filter");
668 case EINVAL:
669 warn("seccomp filter or call method is"
670 " invalid. %d:'%s'", sf->nr, sf->filter);
671 default:
672 pdie("failed to install seccomp filter");
673 }
674 }
675 sf = sf->next;
676 broaden = 0;
677 } while (sf != j->filters);
678 return ret;
Will Drewry32ac9f52011-08-18 21:36:27 -0500679}
680
Elly Jonese1749eb2011-10-07 13:54:59 -0400681void minijail_enter(const struct minijail *j)
682{
683 if (j->flags.pids)
684 die("tried to enter a pid-namespaced jail;"
685 "try minijail_run()?");
Elly Jonescd7a9042011-07-22 13:56:51 -0400686
Elly Jonese1749eb2011-10-07 13:54:59 -0400687 if (j->flags.seccomp_filter && setup_seccomp_filters(j))
688 pdie("failed to configure seccomp filters");
Will Drewry32ac9f52011-08-18 21:36:27 -0500689
Elly Jonese1749eb2011-10-07 13:54:59 -0400690 if (j->flags.usergroups && !j->user)
691 die("usergroup inheritance without username");
Elly Jonescd7a9042011-07-22 13:56:51 -0400692
Elly Jonese1749eb2011-10-07 13:54:59 -0400693 /* We can't recover from failures if we've dropped privileges partially,
694 * so we don't even try. If any of our operations fail, we abort() the
695 * entire process.
696 */
697 if (j->flags.vfs && unshare(CLONE_NEWNS))
698 pdie("unshare");
Elly Jonescd7a9042011-07-22 13:56:51 -0400699
Elly Jones51a5b6c2011-10-12 19:09:26 -0400700 if (j->flags.chroot && enter_chroot(j))
701 pdie("chroot");
702
Elly Jonese1749eb2011-10-07 13:54:59 -0400703 if (j->flags.readonly && remount_readonly())
704 pdie("remount");
Elly Jonescd7a9042011-07-22 13:56:51 -0400705
Elly Jonese1749eb2011-10-07 13:54:59 -0400706 if (j->flags.caps) {
707 /* POSIX capabilities are a bit tricky. If we drop our
708 * capability to change uids, our attempt to use setuid()
709 * below will fail. Hang on to root caps across setuid(), then
710 * lock securebits.
711 */
712 if (prctl(PR_SET_KEEPCAPS, 1))
713 pdie("prctl(PR_SET_KEEPCAPS)");
714 if (prctl
715 (PR_SET_SECUREBITS, SECURE_ALL_BITS | SECURE_ALL_LOCKS))
716 pdie("prctl(PR_SET_SECUREBITS)");
717 }
Elly Jonescd7a9042011-07-22 13:56:51 -0400718
Elly Jonese1749eb2011-10-07 13:54:59 -0400719 if (j->flags.usergroups) {
720 if (initgroups(j->user, j->usergid))
721 pdie("initgroups");
722 } else {
723 /* Only attempt to clear supplemental groups if we are changing
724 * users. */
725 if ((j->uid || j->gid) && setgroups(0, NULL))
726 pdie("setgroups");
727 }
Elly Jonescd7a9042011-07-22 13:56:51 -0400728
Elly Jonese1749eb2011-10-07 13:54:59 -0400729 if (j->flags.gid && setresgid(j->gid, j->gid, j->gid))
730 pdie("setresgid");
Elly Jonescd7a9042011-07-22 13:56:51 -0400731
Elly Jonese1749eb2011-10-07 13:54:59 -0400732 if (j->flags.uid && setresuid(j->uid, j->uid, j->uid))
733 pdie("setresuid");
Elly Jonescd7a9042011-07-22 13:56:51 -0400734
Elly Jonese1749eb2011-10-07 13:54:59 -0400735 if (j->flags.caps)
736 drop_caps(j);
Elly Jonescd7a9042011-07-22 13:56:51 -0400737
Elly Jonese1749eb2011-10-07 13:54:59 -0400738 /* seccomp has to come last since it cuts off all the other
739 * privilege-dropping syscalls :)
740 */
741 if (j->flags.seccomp_filter && prctl(PR_SET_SECCOMP, 13))
742 pdie("prctl(PR_SET_SECCOMP, 13)");
Will Drewry32ac9f52011-08-18 21:36:27 -0500743
Elly Jonese1749eb2011-10-07 13:54:59 -0400744 if (j->flags.seccomp && prctl(PR_SET_SECCOMP, 1))
745 pdie("prctl(PR_SET_SECCOMP)");
Elly Jonescd7a9042011-07-22 13:56:51 -0400746}
747
748static int init_exitstatus = 0;
749
Elly Jonese1749eb2011-10-07 13:54:59 -0400750static void init_term(int __attribute__ ((unused)) sig)
751{
752 _exit(init_exitstatus);
Elly Jonescd7a9042011-07-22 13:56:51 -0400753}
754
Elly Jonese1749eb2011-10-07 13:54:59 -0400755static int init(pid_t rootpid)
756{
757 pid_t pid;
758 int status;
759 /* so that we exit with the right status */
760 signal(SIGTERM, init_term);
761 /* TODO(wad) self jail with seccomp_filters here. */
762 while ((pid = wait(&status)) > 0) {
763 /* This loop will only end when either there are no processes
764 * left inside our pid namespace or we get a signal.
765 */
766 if (pid == rootpid)
767 init_exitstatus = status;
768 }
769 if (!WIFEXITED(init_exitstatus))
770 _exit(MINIJAIL_ERR_INIT);
771 _exit(WEXITSTATUS(init_exitstatus));
Elly Jonescd7a9042011-07-22 13:56:51 -0400772}
773
Elly Jonese1749eb2011-10-07 13:54:59 -0400774int minijail_from_fd(int fd, struct minijail *j)
775{
776 size_t sz = 0;
777 size_t bytes = read(fd, &sz, sizeof(sz));
778 char *buf;
779 int r;
780 if (sizeof(sz) != bytes)
781 return -EINVAL;
782 if (sz > USHRT_MAX) /* Arbitrary sanity check */
783 return -E2BIG;
784 buf = malloc(sz);
785 if (!buf)
786 return -ENOMEM;
787 bytes = read(fd, buf, sz);
788 if (bytes != sz) {
789 free(buf);
790 return -EINVAL;
791 }
792 r = minijail_unmarshal(j, buf, sz);
793 free(buf);
794 return r;
Will Drewry2f54b6a2011-09-16 13:45:31 -0500795}
796
Elly Jonese1749eb2011-10-07 13:54:59 -0400797int minijail_to_fd(struct minijail *j, int fd)
798{
799 char *buf;
800 size_t sz = minijail_size(j);
801 ssize_t written;
802 int r;
Elly Jonescd7a9042011-07-22 13:56:51 -0400803
Elly Jonese1749eb2011-10-07 13:54:59 -0400804 if (!sz)
805 return -EINVAL;
806 buf = malloc(sz);
807 r = minijail_marshal(j, buf, sz);
808 if (r) {
809 free(buf);
810 return r;
811 }
812 /* Sends [size][minijail]. */
813 written = write(fd, &sz, sizeof(sz));
814 if (written != sizeof(sz)) {
815 free(buf);
816 return -EFAULT;
817 }
818 written = write(fd, buf, sz);
819 if (written < 0 || (size_t) written != sz) {
820 free(buf);
821 return -EFAULT;
822 }
823 free(buf);
824 return 0;
Will Drewry2f54b6a2011-09-16 13:45:31 -0500825}
Elly Jonescd7a9042011-07-22 13:56:51 -0400826
Elly Jonese1749eb2011-10-07 13:54:59 -0400827static int setup_preload(void)
828{
829 char *oldenv = getenv(kLdPreloadEnvVar) ? : "";
830 char *newenv = malloc(strlen(oldenv) + 2 + strlen(PRELOADPATH));
831 if (!newenv)
832 return -ENOMEM;
Elly Jonescd7a9042011-07-22 13:56:51 -0400833
Elly Jonese1749eb2011-10-07 13:54:59 -0400834 /* Only insert a separating space if we have something to separate... */
835 sprintf(newenv, "%s%s%s", oldenv, strlen(oldenv) ? " " : "",
836 PRELOADPATH);
Elly Jonescd7a9042011-07-22 13:56:51 -0400837
Elly Jonese1749eb2011-10-07 13:54:59 -0400838 /* setenv() makes a copy of the string we give it */
839 setenv(kLdPreloadEnvVar, newenv, 1);
840 free(newenv);
841 return 0;
Elly Jonescd7a9042011-07-22 13:56:51 -0400842}
843
Elly Jonese1749eb2011-10-07 13:54:59 -0400844static int setup_pipe(int fds[2])
845{
846 int r = pipe(fds);
847 char fd_buf[11];
848 if (r)
849 return r;
850 r = snprintf(fd_buf, sizeof(fd_buf), "%d", fds[0]);
851 if (r <= 0)
852 return -EINVAL;
853 setenv(kFdEnvVar, fd_buf, 1);
854 return 0;
Will Drewryf89aef52011-09-16 16:48:57 -0500855}
856
Elly Jonese1749eb2011-10-07 13:54:59 -0400857int minijail_run(struct minijail *j, const char *filename, char *const argv[])
858{
859 unsigned int pidns = j->flags.pids ? CLONE_NEWPID : 0;
860 char *oldenv, *oldenv_copy = NULL;
861 pid_t child_pid;
862 int pipe_fds[2];
863 int ret;
Ben Chan541c7e52011-08-26 14:55:53 -0700864
Elly Jonese1749eb2011-10-07 13:54:59 -0400865 oldenv = getenv(kLdPreloadEnvVar);
866 if (oldenv) {
867 oldenv_copy = strdup(oldenv);
868 if (!oldenv_copy)
869 return -ENOMEM;
870 }
Will Drewryf89aef52011-09-16 16:48:57 -0500871
Elly Jonese1749eb2011-10-07 13:54:59 -0400872 if (setup_preload())
873 return -EFAULT;
Will Drewry2f54b6a2011-09-16 13:45:31 -0500874
Elly Jonese1749eb2011-10-07 13:54:59 -0400875 /* Before we fork(2) and execve(2) the child process, we need to open
876 * a pipe(2) to send the minijail configuration over.
877 */
878 if (setup_pipe(pipe_fds))
879 return -EFAULT;
Elly Jonescd7a9042011-07-22 13:56:51 -0400880
Elly Jonese1749eb2011-10-07 13:54:59 -0400881 child_pid = syscall(SYS_clone, pidns | SIGCHLD, NULL);
882 if (child_pid < 0) {
883 free(oldenv_copy);
884 return child_pid;
885 }
Will Drewryf89aef52011-09-16 16:48:57 -0500886
Elly Jonese1749eb2011-10-07 13:54:59 -0400887 if (child_pid) {
888 /* Restore parent's LD_PRELOAD. */
889 if (oldenv_copy) {
890 setenv(kLdPreloadEnvVar, oldenv_copy, 1);
891 free(oldenv_copy);
892 } else {
893 unsetenv(kLdPreloadEnvVar);
894 }
895 unsetenv(kFdEnvVar);
896 j->initpid = child_pid;
897 close(pipe_fds[0]); /* read endpoint */
898 ret = minijail_to_fd(j, pipe_fds[1]);
899 close(pipe_fds[1]); /* write endpoint */
900 if (ret) {
901 kill(j->initpid, SIGKILL);
902 die("failed to send marshalled minijail");
903 }
904 return 0;
905 }
906 free(oldenv_copy);
Ben Chan541c7e52011-08-26 14:55:53 -0700907
Elly Jonese1749eb2011-10-07 13:54:59 -0400908 /* Drop everything that cannot be inherited across execve. */
909 minijail_preexec(j);
910 /* Jail this process and its descendants... */
911 minijail_enter(j);
Elly Jonescd7a9042011-07-22 13:56:51 -0400912
Elly Jonese1749eb2011-10-07 13:54:59 -0400913 if (pidns) {
914 /* pid namespace: this process will become init inside the new
915 * namespace, so fork off a child to actually run the program
916 * (we don't want all programs we might exec to have to know
917 * how to be init).
918 */
919 child_pid = fork();
920 if (child_pid < 0)
921 _exit(child_pid);
922 else if (child_pid > 0)
923 init(child_pid); /* never returns */
924 }
Elly Jonescd7a9042011-07-22 13:56:51 -0400925
Elly Jonese1749eb2011-10-07 13:54:59 -0400926 /* If we aren't pid-namespaced:
927 * calling process
928 * -> execve()-ing process
929 * If we are:
930 * calling process
931 * -> init()-ing process
932 * -> execve()-ing process
933 */
934 _exit(execve(filename, argv, environ));
Elly Jonescd7a9042011-07-22 13:56:51 -0400935}
936
Elly Jonese1749eb2011-10-07 13:54:59 -0400937int minijail_kill(struct minijail *j)
938{
939 int st;
940 if (kill(j->initpid, SIGTERM))
941 return -errno;
942 if (waitpid(j->initpid, &st, 0) < 0)
943 return -errno;
944 return st;
Elly Jonescd7a9042011-07-22 13:56:51 -0400945}
946
Elly Jonese1749eb2011-10-07 13:54:59 -0400947int minijail_wait(struct minijail *j)
948{
949 int st;
950 if (waitpid(j->initpid, &st, 0) < 0)
951 return -errno;
952 if (!WIFEXITED(st))
953 return MINIJAIL_ERR_JAIL;
954 return WEXITSTATUS(st);
Elly Jonescd7a9042011-07-22 13:56:51 -0400955}
956
Elly Jonese1749eb2011-10-07 13:54:59 -0400957void minijail_destroy(struct minijail *j)
958{
959 struct seccomp_filter *f = j->filters;
960 /* Unlink the tail and head */
961 if (f)
962 f->prev->next = NULL;
963 while (f) {
964 struct seccomp_filter *next = f->next;
965 free(f->filter);
966 free(f);
967 f = next;
968 }
Elly Jones51a5b6c2011-10-12 19:09:26 -0400969 while (j->bindings_head) {
970 struct binding *b = j->bindings_head;
971 j->bindings_head = j->bindings_head->next;
972 free(b->dest);
973 free(b->src);
974 free(b);
975 }
976 j->bindings_tail = NULL;
Elly Jonese1749eb2011-10-07 13:54:59 -0400977 if (j->user)
978 free(j->user);
979 free(j);
Elly Jonescd7a9042011-07-22 13:56:51 -0400980}