diff options
author | Simon McVittie <smcv@collabora.com> | 2021-10-12 09:36:58 +0100 |
---|---|---|
committer | Simon McVittie <smcv@collabora.com> | 2022-01-31 17:10:10 +0000 |
commit | d625fda8da406cedcad2dcdd02af203d90543fae (patch) | |
tree | c236d9bdff4c165a060252bbdabe66d2b08b0c5c | |
parent | c267db329598346fb4a54f5968bd79573376a517 (diff) | |
download | bubblewrap-d625fda8da406cedcad2dcdd02af203d90543fae.tar.gz |
Allow loading more than one seccomp program
This will allow Flatpak to combine an allow-list (default-deny) of
known system calls with a deny-list (default-allow) of system calls
that are undesired.
Resolves: https://github.com/containers/bubblewrap/issues/453
Signed-off-by: Simon McVittie <smcv@collabora.com>
-rw-r--r-- | bubblewrap.c | 100 | ||||
-rw-r--r-- | bwrap.xml | 17 |
2 files changed, 94 insertions, 23 deletions
diff --git a/bubblewrap.c b/bubblewrap.c index d3d4156..c043158 100644 --- a/bubblewrap.c +++ b/bubblewrap.c @@ -235,6 +235,50 @@ lock_file_new (const char *path) return lock; } +typedef struct _SeccompProgram SeccompProgram; + +struct _SeccompProgram +{ + struct sock_fprog program; + SeccompProgram *next; +}; + +DEFINE_LINKED_LIST (SeccompProgram, seccomp_program) + +static SeccompProgram * +seccomp_program_new (int *fd) +{ + SeccompProgram *self = _seccomp_program_append_new (); + cleanup_free char *data = NULL; + size_t len; + + data = load_file_data (*fd, &len); + + if (data == NULL) + die_with_error ("Can't read seccomp data"); + + close (*fd); + *fd = -1; + + if (len % 8 != 0) + die ("Invalid seccomp data, must be multiple of 8"); + + self->program.len = len / 8; + self->program.filter = (struct sock_filter *) steal_pointer (&data); + return self; +} + +static void +seccomp_programs_apply (void) +{ + SeccompProgram *program; + + for (program = seccomp_programs; program != NULL; program = program->next) + { + if (prctl (PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &program->program) != 0) + die_with_error ("prctl(PR_SET_SECCOMP)"); + } +} static void usage (int ecode, FILE *out) @@ -285,7 +329,8 @@ usage (int ecode, FILE *out) " --bind-data FD DEST Copy from FD to file which is bind-mounted on DEST\n" " --ro-bind-data FD DEST Copy from FD to file which is readonly bind-mounted on DEST\n" " --symlink SRC DEST Create symlink at DEST with target SRC\n" - " --seccomp FD Load and use seccomp rules from FD\n" + " --seccomp FD Load and use seccomp rules from FD (not repeatable)\n" + " --add-seccomp FD Load and use seccomp rules from FD (repeatable)\n" " --block-fd FD Block on FD until some data to read is available\n" " --userns-block-fd FD Block on FD until the user namespace is ready\n" " --info-fd FD Write information about the running container to FD\n" @@ -519,7 +564,7 @@ monitor_child (int event_fd, pid_t child_pid, int setup_finished_fd) * When there are no other processes in the sandbox the wait will return * ECHILD, and we then exit pid 1 to clean up the sandbox. */ static int -do_init (int event_fd, pid_t initial_pid, struct sock_fprog *seccomp_prog) +do_init (int event_fd, pid_t initial_pid) { int initial_exit_status = 1; LockFile *lock; @@ -547,9 +592,7 @@ do_init (int event_fd, pid_t initial_pid, struct sock_fprog *seccomp_prog) /* Optionally bind our lifecycle to that of the caller */ handle_die_with_parent (); - if (seccomp_prog != NULL && - prctl (PR_SET_SECCOMP, SECCOMP_MODE_FILTER, seccomp_prog) != 0) - die_with_error ("prctl(PR_SET_SECCOMP)"); + seccomp_programs_apply (); while (TRUE) { @@ -2091,6 +2134,9 @@ parse_args_recurse (int *argcp, if (argc < 2) die ("--seccomp takes an argument"); + if (seccomp_programs != NULL) + die ("--seccomp cannot be combined with --add-seccomp-fd"); + if (opt_seccomp_fd != -1) warn_only_last_option ("--seccomp"); @@ -2103,6 +2149,27 @@ parse_args_recurse (int *argcp, argv += 1; argc -= 1; } + else if (strcmp (arg, "--add-seccomp-fd") == 0) + { + int the_fd; + char *endptr; + + if (argc < 2) + die ("--add-seccomp-fd takes an argument"); + + if (opt_seccomp_fd != -1) + die ("--add-seccomp-fd cannot be combined with --seccomp"); + + the_fd = strtol (argv[1], &endptr, 10); + if (argv[1][0] == 0 || endptr[0] != 0 || the_fd < 0) + die ("Invalid fd: %s", argv[1]); + + /* takes ownership of fd */ + seccomp_program_new (&the_fd); + + argv += 1; + argc -= 1; + } else if (strcmp (arg, "--userns") == 0) { int the_fd; @@ -2485,9 +2552,6 @@ main (int argc, struct stat sbuf; uint64_t val; int res UNUSED; - cleanup_free char *seccomp_data = NULL; - size_t seccomp_len; - struct sock_fprog seccomp_prog; cleanup_free char *args_data = NULL; int intermediate_pids_sockets[2] = {-1, -1}; @@ -3051,17 +3115,9 @@ main (int argc, if (opt_seccomp_fd != -1) { - seccomp_data = load_file_data (opt_seccomp_fd, &seccomp_len); - if (seccomp_data == NULL) - die_with_error ("Can't read seccomp data"); - - if (seccomp_len % 8 != 0) - die ("Invalid seccomp data, must be multiple of 8"); - - seccomp_prog.len = seccomp_len / 8; - seccomp_prog.filter = (struct sock_filter *) seccomp_data; - - close (opt_seccomp_fd); + assert (seccomp_programs == NULL); + /* takes ownership of fd */ + seccomp_program_new (&opt_seccomp_fd); } umask (old_umask); @@ -3130,7 +3186,7 @@ main (int argc, fdwalk (proc_fd, close_extra_fds, dont_close); } - return do_init (event_fd, pid, seccomp_data != NULL ? &seccomp_prog : NULL); + return do_init (event_fd, pid); } } @@ -3158,9 +3214,7 @@ main (int argc, /* Should be the last thing before execve() so that filters don't * need to handle anything above */ - if (seccomp_data != NULL && - prctl (PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &seccomp_prog) != 0) - die_with_error ("prctl(PR_SET_SECCOMP)"); + seccomp_programs_apply (); if (setup_finished_pipe[1] != -1) { @@ -328,6 +328,23 @@ Load and use seccomp rules from <arg choice="plain">FD</arg>. The rules need to be in the form of a compiled cBPF program, as generated by seccomp_export_bpf. + If this option is given more than once, only the last one is used. + Use <option>--add-seccomp-fd</option> if multiple seccomp programs + are needed. + </para></listitem> + </varlistentry> + <varlistentry> + <term><option>--add-seccomp-fd <arg choice="plain">FD</arg></option></term> + <listitem><para> + Load and use seccomp rules from <arg choice="plain">FD</arg>. + The rules need to be in the form of a compiled cBPF program, + as generated by seccomp_export_bpf. + This option can be repeated, in which case all the seccomp + programs will be loaded in the order given (note that the kernel + will evaluate them in reverse order, so the last program on the + bwrap command-line is evaluated first). All of them, except + possibly the last, must allow use of the PR_SET_SECCOMP prctl. + This option cannot be combined with <option>--seccomp</option>. </para></listitem> </varlistentry> <varlistentry> |