Skip to content

Commit

Permalink
Merge pull request containers#459 from smcv/multiple-seccomp
Browse files Browse the repository at this point in the history
Allow loading more than one seccomp program
  • Loading branch information
smcv authored Jan 31, 2022
2 parents b8fa0df + 3612534 commit 43c2d32
Show file tree
Hide file tree
Showing 5 changed files with 943 additions and 42 deletions.
2 changes: 2 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ test_programs = \
$(NULL)
test_scripts = \
tests/test-run.sh \
tests/test-seccomp.py \
tests/test-specifying-userns.sh \
tests/test-specifying-pidns.sh \
$(NULL)
test_extra_programs = \
test-bwrap \
tests/try-syscall \
$(NULL)

test-bwrap: bwrap
Expand Down
155 changes: 113 additions & 42 deletions bubblewrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,6 @@ struct _LockFile
LockFile *next;
};

static SetupOp *ops = NULL;
static SetupOp *last_op = NULL;
static LockFile *lock_files = NULL;
static LockFile *last_lock_file = NULL;

enum {
PRIV_SEP_OP_DONE,
PRIV_SEP_OP_BIND_MOUNT,
Expand All @@ -186,38 +181,104 @@ typedef struct
uint32_t arg2_offset;
} PrivSepOp;

/*
* DEFINE_LINKED_LIST:
* @Type: A struct with a `Type *next` member
* @name: Used to form the names of variables and functions
*
* Define a global linked list of @Type structures, with pointers
* `NAMEs` to the head of the list and `last_NAME` to the tail of the
* list.
*
* A new zero-filled item can be allocated and appended to the list
* by calling `_NAME_append_new()`, which returns the new item.
*/
#define DEFINE_LINKED_LIST(Type, name) \
static Type *name ## s = NULL; \
static Type *last_ ## name = NULL; \
\
static inline Type * \
_ ## name ## _append_new (void) \
{ \
Type *self = xcalloc (sizeof (Type)); \
\
if (last_ ## name != NULL) \
last_ ## name ->next = self; \
else \
name ## s = self; \
\
last_ ## name = self; \
return self; \
}

DEFINE_LINKED_LIST (SetupOp, op)

static SetupOp *
setup_op_new (SetupOpType type)
{
SetupOp *op = xcalloc (sizeof (SetupOp));
SetupOp *op = _op_append_new ();

op->type = type;
op->fd = -1;
op->flags = 0;
if (last_op != NULL)
last_op->next = op;
else
ops = op;

last_op = op;
return op;
}

DEFINE_LINKED_LIST (LockFile, lock_file)

static LockFile *
lock_file_new (const char *path)
{
LockFile *lock = xcalloc (sizeof (LockFile));
LockFile *lock = _lock_file_append_new ();

lock->path = path;
if (last_lock_file != NULL)
last_lock_file->next = lock;
else
lock_files = lock;

last_lock_file = lock;
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)
Expand Down Expand Up @@ -268,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"
Expand Down Expand Up @@ -502,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;
Expand Down Expand Up @@ -530,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)
{
Expand Down Expand Up @@ -2074,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");

Expand All @@ -2083,6 +2146,27 @@ parse_args_recurse (int *argcp,

opt_seccomp_fd = the_fd;

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;
}
Expand Down Expand Up @@ -2468,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};

Expand Down Expand Up @@ -3034,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);
Expand Down Expand Up @@ -3113,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);
}
}

Expand Down Expand Up @@ -3141,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)
{
Expand Down
17 changes: 17 additions & 0 deletions bwrap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Loading

0 comments on commit 43c2d32

Please sign in to comment.