From e577f9f1496eb2c1ced4d6ee5ad5916d95f74952 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Thu, 28 May 2020 18:45:48 +0200 Subject: [PATCH] seccomp: add support for seccomp notify add support for seccomp notify and add a basic support for emulating mknod and mknodat. The handler implementation is likely going to change, for now it is just a PoC to show how it would work. Requires: https://github.com/containers/crun/pull/438 Requires: libseccomp-2.5 Signed-off-by: Giuseppe Scrivano --- .cirrus.yml | 2 +- Makefile | 8 +- contrib/spec/conmon.spec.in | 1 + meson.build | 7 +- nix/default.nix | 3 +- src/cli.c | 9 ++ src/cli.h | 2 + src/conmon.c | 13 ++ src/conn_sock.c | 82 +++++++--- src/conn_sock.h | 1 + src/ctrl.c | 1 + src/globals.c | 1 + src/globals.h | 1 + src/seccomp_notify.c | 299 ++++++++++++++++++++++++++++++++++++ src/seccomp_notify.h | 18 +++ src/seccomp_notify_plugin.h | 40 +++++ 16 files changed, 463 insertions(+), 25 deletions(-) create mode 100644 src/seccomp_notify.c create mode 100644 src/seccomp_notify.h create mode 100644 src/seccomp_notify_plugin.h diff --git a/.cirrus.yml b/.cirrus.yml index d4f5b834..89fd2bdf 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -68,7 +68,7 @@ fedora_packaging_task: memory: 4 script: - - dnf install -y make glib2-devel git gcc rpm-build golang + - dnf install -y make glib2-devel git gcc rpm-build golang libseccomp-devel - cd $CIRRUS_WORKING_DIR - make - make -f .rpmbuild/Makefile diff --git a/Makefile b/Makefile index fae59377..d52e68f9 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,8 @@ GO ?= go PROJECT := github.com/containers/conmon PKG_CONFIG ?= pkg-config HEADERS := $(wildcard src/*.h) -OBJS := src/conmon.o src/cmsg.o src/ctr_logging.o src/utils.o src/cli.o src/globals.o src/cgroup.o src/conn_sock.o src/oom.o src/ctrl.o src/ctr_stdio.o src/parent_pipe_fd.o src/ctr_exit.o src/runtime_args.o src/close_fds.o + +OBJS := src/conmon.o src/cmsg.o src/ctr_logging.o src/utils.o src/cli.o src/globals.o src/cgroup.o src/conn_sock.o src/oom.o src/ctrl.o src/ctr_stdio.o src/parent_pipe_fd.o src/ctr_exit.o src/runtime_args.o src/close_fds.o src/seccomp_notify.o MAKEFILE_PATH := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) @@ -45,6 +46,11 @@ else ifeq ($(shell $(PKG_CONFIG) --exists libsystemd && echo "0" || echo "1"), 0 override CFLAGS += $(shell $(PKG_CONFIG) --cflags libsystemd) -D USE_JOURNALD=0 endif +ifeq ($(shell $(PKG_CONFIG) --exists libseccomp && echo "0" || echo "1"), 0) + override LIBS += $(shell $(PKG_CONFIG) --libs libseccomp) -ldl + override CFLAGS += $(shell $(PKG_CONFIG) --cflags libseccomp) +endif + # Update nix/nixpkgs.json its latest stable commit .PHONY: nixpkgs nixpkgs: diff --git a/contrib/spec/conmon.spec.in b/contrib/spec/conmon.spec.in index 8f0bef7d..8870b754 100644 --- a/contrib/spec/conmon.spec.in +++ b/contrib/spec/conmon.spec.in @@ -22,6 +22,7 @@ ExclusiveArch: aarch64 %{arm} ppc64le s390x x86_64 BuildRequires: gcc BuildRequires: glib2-devel BuildRequires: glibc-devel +BuildRequires: libseccomp-devel BuildRequires: git # If go_compiler is not set to 1, there is no virtual provide. Use golang instead. BuildRequires: golang diff --git a/meson.build b/meson.build index b429373b..1fe99670 100644 --- a/meson.build +++ b/meson.build @@ -34,6 +34,7 @@ add_project_arguments('-Os', '-Wall', '-Werror', language : 'c') glib = dependency('glib-2.0') +libdl = cc.find_library('dl') executable('conmon', ['src/conmon.c', @@ -67,8 +68,10 @@ executable('conmon', 'src/runtime_args.c', 'src/runtime_args.h', 'src/utils.c', - 'src/utils.h'], - dependencies : [glib], + 'src/utils.h', + 'src/seccomp_notify.c', + 'src/seccomp_notify.h'], + dependencies : [glib, libdl], install : true, install_dir : join_paths(get_option('libexecdir'), 'podman'), ) diff --git a/nix/default.nix b/nix/default.nix index 87f57df1..a509e352 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -13,6 +13,7 @@ let autogen = (static pkg.autogen); e2fsprogs = (static pkg.e2fsprogs); libuv = (static pkg.libuv); + libseccomp = (static pkg.libseccomp); glib = (static pkg.glib).overrideAttrs(x: { outputs = [ "bin" "out" "dev" ]; mesonFlags = [ @@ -60,7 +61,7 @@ let enableParallelBuilding = true; outputs = [ "out" ]; nativeBuildInputs = [ bash gitMinimal pcre pkg-config which ]; - buildInputs = [ glibc glibc.static glib ]; + buildInputs = [ glibc glibc.static glib libseccomp ]; prePatch = '' export CFLAGS='-static -pthread' export LDFLAGS='-s -w -static-libgcc -static' diff --git a/src/cli.c b/src/cli.c index 8e788f97..842caf0d 100644 --- a/src/cli.c +++ b/src/cli.c @@ -50,6 +50,8 @@ gboolean opt_sync = FALSE; gboolean opt_no_sync_log = FALSE; char *opt_sdnotify_socket = NULL; gboolean opt_full_attach_path = FALSE; +char *opt_seccomp_notify_socket = NULL; +char *opt_seccomp_notify_plugins = NULL; GOptionEntry opt_entries[] = { {"api-version", 0, 0, G_OPTION_ARG_NONE, &opt_api_version, "Conmon API version to use", NULL}, {"bundle", 'b', 0, G_OPTION_ARG_STRING, &opt_bundle_path, "Location of the OCI Bundle path", NULL}, @@ -100,6 +102,10 @@ GOptionEntry opt_entries[] = { {"version", 0, 0, G_OPTION_ARG_NONE, &opt_version, "Print the version and exit", NULL}, {"full-attach", 0, 0, G_OPTION_ARG_NONE, &opt_full_attach_path, "Don't truncate the path to the attach socket. This option causes conmon to ignore --socket-dir-path", NULL}, + {"seccomp-notify-socket", 0, 0, G_OPTION_ARG_STRING, &opt_seccomp_notify_socket, + "Path to the socket where the seccomp notification fd is received", NULL}, + {"seccomp-notify-plugins", 0, 0, G_OPTION_ARG_STRING, &opt_seccomp_notify_plugins, + "Plugins to use for managing the seccomp notifications", NULL}, {NULL, 0, 0, 0, NULL, NULL, NULL}}; @@ -150,6 +156,9 @@ void process_cli() if (opt_cuuid == NULL && (!opt_exec || opt_api_version >= 1)) nexit("Container UUID not provided. Use --cuuid"); + if (opt_seccomp_notify_plugins == NULL) + opt_seccomp_notify_plugins = getenv("CONMON_SECCOMP_NOTIFY_PLUGINS"); + if (opt_runtime_path == NULL) nexit("Runtime path not provided. Use --runtime"); if (access(opt_runtime_path, X_OK) < 0) diff --git a/src/cli.h b/src/cli.h index efb2ccad..8cd0c530 100644 --- a/src/cli.h +++ b/src/cli.h @@ -43,6 +43,8 @@ extern char *opt_log_tag; extern gboolean opt_no_sync_log; extern gboolean opt_sync; extern char *opt_sdnotify_socket; +extern char *opt_seccomp_notify_socket; +extern char *opt_seccomp_notify_plugins; extern GOptionEntry opt_entries[]; extern gboolean opt_full_attach_path; diff --git a/src/conmon.c b/src/conmon.c index c4173d44..50d1c1b8 100644 --- a/src/conmon.c +++ b/src/conmon.c @@ -18,6 +18,7 @@ #include "parent_pipe_fd.h" #include "ctr_exit.h" #include "close_fds.h" +#include "seccomp_notify.h" #include "runtime_args.h" #include @@ -131,6 +132,7 @@ int main(int argc, char *argv[]) } _cleanup_free_ char *csname = NULL; + _cleanup_free_ char *seccomp_listener = NULL; int workerfd_stdin = -1; int workerfd_stdout = -1; int workerfd_stderr = -1; @@ -173,6 +175,12 @@ int main(int argc, char *argv[]) g_unix_fd_add(winsz_fd_r, G_IO_IN, ctrl_winsz_cb, NULL); } + if (opt_seccomp_notify_socket != NULL) { + if (opt_seccomp_notify_plugins == NULL) + pexit("seccomp notify socket specified without any plugin"); + seccomp_listener = setup_seccomp_socket(opt_seccomp_notify_socket); + } + /* We always create a stderr pipe, because that way we can capture runc stderr messages before the tty is created */ if (pipe2(fds, O_CLOEXEC) < 0) @@ -315,6 +323,9 @@ int main(int argc, char *argv[]) if (workerfd_stderr > -1) close(workerfd_stderr); + if (seccomp_listener != NULL) + g_unix_fd_add(seccomp_socket_fd, G_IO_IN, seccomp_accept_cb, csname); + if (csname != NULL) { g_unix_fd_add(console_socket_fd, G_IO_IN, terminal_accept_cb, csname); /* Process any SIGCHLD we may have missed before the signal handler was in place. */ @@ -488,6 +499,8 @@ int main(int argc, char *argv[]) if (!g_file_set_contents(exit_file_path, status_str, -1, &err)) nexitf("Failed to write %s to exit file: %s", status_str, err->message); } + if (seccomp_listener != NULL) + unlink(seccomp_listener); /* Send the command exec exit code back to the parent */ if (opt_exec && sync_pipe_fd >= 0) diff --git a/src/conn_sock.c b/src/conn_sock.c index 75951a84..a0bb102f 100644 --- a/src/conn_sock.c +++ b/src/conn_sock.c @@ -25,6 +25,7 @@ static gboolean local_sock_write_cb(G_GNUC_UNUSED int fd, G_GNUC_UNUSED GIOCondi static char *bind_unix_socket(char *socket_relative_name, int sock_type, mode_t perms, struct remote_sock_s *remote_sock, gboolean use_full_attach_path); static char *socket_parent_dir(gboolean use_full_attach_path, size_t desired_len); +static char *setup_socket(int *fd, const char *path); /* Since our socket handling is abstract now, handling is based on sock_type, so we can pass around a structure that contains everything we need to handle I/O. Callbacks used to handle IO, for example, and whether this @@ -72,38 +73,79 @@ struct remote_sock_s remote_notify_sock = { }; /* External */ + char *setup_console_socket(void) +{ + return setup_socket(&console_socket_fd, NULL); +} + +char *setup_seccomp_socket(const char *socket) +{ + return setup_socket(&seccomp_socket_fd, socket); +} + #include + +static char *setup_socket(int *fd, const char *path) { struct sockaddr_un addr = {0}; - _cleanup_free_ const char *tmpdir = g_get_tmp_dir(); - char *csname = g_build_filename(tmpdir, "conmon-term.XXXXXX", NULL); - /* - * Generate a temporary name. Is this unsafe? Probably, but we can - * replace it with a rename(2) setup if necessary. - */ + char *csname = NULL; + _cleanup_close_ int sfd = -1; + _cleanup_free_ char *dname_buf = NULL; + _cleanup_free_ char *bname_buf = NULL; + char *dname = NULL, *bname = NULL; + + if (path != NULL) { + csname = strdup(path); + dname_buf = strdup(path); + bname_buf = strdup(path); + if (csname == NULL || dname_buf == NULL || bname_buf == NULL) { + pexit("Failed to allocate memory"); + return NULL; + } + dname = dirname(dname_buf); + if (dname == NULL) + pexitf("Cannot get dirname for %s", csname); - int unusedfd = g_mkstemp(csname); - if (unusedfd < 0) - pexit("Failed to generate random path for console-socket"); - close(unusedfd); + sfd = open(dname, O_CREAT|O_PATH, 0600); + if (sfd < 0) + pexit("Failed to create file for console-socket"); + + bname = basename(bname_buf); + if (bname == NULL) + pexitf("Cannot get basename for %s", csname); + } else { + _cleanup_free_ const char *tmpdir = g_get_tmp_dir(); + + csname = g_build_filename(tmpdir, "conmon-term.XXXXXX", NULL); + /* + * Generate a temporary name. Is this unsafe? Probably, but we can + * replace it with a rename(2) setup if necessary. + */ + sfd = g_mkstemp(csname); + if (sfd < 0) + pexit("Failed to generate random path for console-socket"); + /* XXX: This should be handled with a rename(2). */ + if (unlink(csname) < 0) + pexit("Failed to unlink temporary random path"); + } addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, csname, sizeof(addr.sun_path) - 1); + if (bname) + snprintf(addr.sun_path, sizeof(addr.sun_path) - 1, "/proc/self/fd/%d/%s", sfd, bname); + else + strncpy(addr.sun_path, csname, sizeof(addr.sun_path) - 1); ninfof("addr{sun_family=AF_UNIX, sun_path=%s}", addr.sun_path); /* Bind to the console socket path. */ - console_socket_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); - if (console_socket_fd < 0) - pexit("Failed to create console-socket"); - if (fchmod(console_socket_fd, 0700)) + *fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (*fd < 0) + pexit("Failed to create socket"); + if (fchmod(*fd, 0700)) pexit("Failed to change console-socket permissions"); - /* XXX: This should be handled with a rename(2). */ - if (unlink(csname) < 0) - pexit("Failed to unlink temporary random path"); - if (bind(console_socket_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) + if (bind(*fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) pexit("Failed to bind to console-socket"); - if (listen(console_socket_fd, 128) < 0) + if (listen(*fd, 128) < 0) pexit("Failed to listen on console-socket"); return csname; diff --git a/src/conn_sock.h b/src/conn_sock.h index 796c4a71..bf5f6f39 100644 --- a/src/conn_sock.h +++ b/src/conn_sock.h @@ -48,6 +48,7 @@ struct local_sock_s { }; char *setup_console_socket(void); +char *setup_seccomp_socket(const char *socket); char *setup_attach_socket(void); void setup_notify_socket(char *); void schedule_main_stdin_write(); diff --git a/src/ctrl.c b/src/ctrl.c index 176e20a0..d93206bb 100644 --- a/src/ctrl.c +++ b/src/ctrl.c @@ -8,6 +8,7 @@ #include "conn_sock.h" #include "cmsg.h" #include "cli.h" // opt_bundle_path +#include "seccomp_notify.h" #include #include diff --git a/src/globals.c b/src/globals.c index a5eeba30..0d052a89 100644 --- a/src/globals.c +++ b/src/globals.c @@ -9,6 +9,7 @@ int mainfd_stderr = -1; int attach_socket_fd = -1; int console_socket_fd = -1; +int seccomp_socket_fd = -1; int terminal_ctrl_fd = -1; int inotify_fd = -1; int winsz_fd_w = -1; diff --git a/src/globals.h b/src/globals.h index a21ceea6..053d7634 100644 --- a/src/globals.h +++ b/src/globals.h @@ -14,6 +14,7 @@ extern int mainfd_stderr; extern int attach_socket_fd; extern int console_socket_fd; +extern int seccomp_socket_fd; extern int terminal_ctrl_fd; extern int inotify_fd; extern int winsz_fd_w; diff --git a/src/seccomp_notify.c b/src/seccomp_notify.c new file mode 100644 index 00000000..77fd5460 --- /dev/null +++ b/src/seccomp_notify.c @@ -0,0 +1,299 @@ +#define _GNU_SOURCE +#if __STDC_VERSION__ >= 199901L +/* C99 or later */ +#else +#error conmon.c requires C99 or later +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cli.h" // opt_bundle_path +#include "utils.h" +#include "cmsg.h" +#include "seccomp_notify.h" + +#ifndef SECCOMP_USER_NOTIF_FLAG_CONTINUE +#define SECCOMP_USER_NOTIF_FLAG_CONTINUE (1UL << 0) +#endif + +static struct seccomp_notify_context_s *seccomp_notify_ctx; + +struct plugin { + void *handle; + void *opaque; + run_oci_seccomp_notify_handle_request_cb handle_request_cb; +}; + +struct seccomp_notify_context_s { + struct plugin *plugins; + size_t n_plugins; + + struct seccomp_notif_resp *sresp; + struct seccomp_notif *sreq; + struct seccomp_notif_sizes sizes; +}; + +static inline void *xmalloc0(size_t size); +static int seccomp_syscall(unsigned int op, unsigned int flags, void *args); +static void cleanup_seccomp_plugins(); + +gboolean seccomp_cb(int fd, GIOCondition condition, G_GNUC_UNUSED gpointer user_data) +{ + if (condition & G_IO_IN) { + if (seccomp_notify_ctx == NULL) + return G_SOURCE_REMOVE; + + int ret = seccomp_notify_plugins_event(seccomp_notify_ctx, fd); + return ret == 0 ? G_SOURCE_CONTINUE : G_SOURCE_REMOVE; + } + return G_SOURCE_CONTINUE; +} + +gboolean seccomp_accept_cb(int fd, G_GNUC_UNUSED GIOCondition condition, G_GNUC_UNUSED gpointer user_data) +{ + ninfof("about to accept from seccomp_socket_fd: %d", fd); + int connfd = accept4(fd, NULL, NULL, SOCK_CLOEXEC); + if (connfd < 0) { + nwarn("Failed to accept console-socket connection"); + return G_SOURCE_CONTINUE; + } + + struct file_t listener = recvfd(connfd); + close(connfd); + + _cleanup_free_ char *oci_config_path = g_strdup_printf("%s/config.json", opt_bundle_path); + if (oci_config_path == NULL) { + nwarn("Failed to allocate memory"); + return G_SOURCE_CONTINUE; + } + + struct seccomp_notify_conf_s conf = { + .runtime_root_path = NULL, + .name = opt_name, + .bundle_path = opt_bundle_path, + .oci_config_path = oci_config_path, + }; + int ret = seccomp_notify_plugins_load(&seccomp_notify_ctx, opt_seccomp_notify_plugins, &conf); + if (ret < 0) { + nwarn("Failed to initialize seccomp notify plugins"); + return G_SOURCE_CONTINUE; + } + + g_unix_set_fd_nonblocking(listener.fd, TRUE, NULL); + g_unix_fd_add(listener.fd, G_IO_IN | G_IO_HUP, seccomp_cb, NULL); + atexit(cleanup_seccomp_plugins); + + return G_SOURCE_CONTINUE; +} + +int seccomp_notify_plugins_load(struct seccomp_notify_context_s **out, const char *plugins, struct seccomp_notify_conf_s *conf) +{ + cleanup_seccomp_notify_context struct seccomp_notify_context_s *ctx = xmalloc0(sizeof *ctx); + _cleanup_free_ char *b = NULL; + char *it, *saveptr; + size_t s; + + if (seccomp_syscall(SECCOMP_GET_NOTIF_SIZES, 0, &ctx->sizes) < 0) { + pexit("Failed to get notifications size"); + return -1; + } + + ctx->sreq = xmalloc0(ctx->sizes.seccomp_notif); + ctx->sresp = xmalloc0(ctx->sizes.seccomp_notif_resp); + + ctx->n_plugins = 1; + for (it = b; it; it = strchr(it, ':')) + ctx->n_plugins++; + + ctx->plugins = xmalloc0(sizeof(struct plugin) * (ctx->n_plugins + 1)); + + b = strdup(plugins); + if (b == NULL) { + pexit("Failed to strdup"); + return -1; + } + for (s = 0, it = strtok_r(b, ":", &saveptr); it; s++, it = strtok_r(NULL, ":", &saveptr)) { + run_oci_seccomp_notify_plugin_version_cb version_cb; + run_oci_seccomp_notify_start_cb start_cb; + void *opq = NULL; + + ctx->plugins[s].handle = dlopen(it, RTLD_NOW); + if (ctx->plugins[s].handle == NULL) { + pexitf("cannot load `%s`: %s", it, dlerror()); + return -1; + } + + version_cb = (run_oci_seccomp_notify_plugin_version_cb)dlsym(ctx->plugins[s].handle, "run_oci_seccomp_notify_version"); + if (version_cb != NULL) { + int version; + + version = version_cb(); + if (version != 1) { + pexitf("invalid version supported by the plugin `%s`", it); + return -1; + } + } + + ctx->plugins[s].handle_request_cb = + (run_oci_seccomp_notify_handle_request_cb)dlsym(ctx->plugins[s].handle, "run_oci_seccomp_notify_handle_request"); + if (ctx->plugins[s].handle_request_cb == NULL) { + pexitf("plugin `%s` doesn't export `run_oci_seccomp_notify_handle_request`", it); + return -1; + } + + start_cb = (run_oci_seccomp_notify_start_cb)dlsym(ctx->plugins[s].handle, "run_oci_seccomp_notify_start"); + if (start_cb) { + int ret; + + ret = start_cb(&opq, conf, sizeof(*conf)); + if (ret != 0) { + pexitf("error loading `%s`", it); + return -1; + } + } + ctx->plugins[s].opaque = opq; + } + + /* Change ownership. */ + *out = ctx; + ctx = NULL; + return 0; +} + +int seccomp_notify_plugins_event(struct seccomp_notify_context_s *ctx, int seccomp_fd) +{ + size_t i; + int ret; + bool handled = false; + + memset(ctx->sreq, 0, ctx->sizes.seccomp_notif); + memset(ctx->sresp, 0, ctx->sizes.seccomp_notif_resp); + + ret = ioctl(seccomp_fd, SECCOMP_IOCTL_NOTIF_RECV, ctx->sreq); + if (ret < 0) { + if (errno == ENOENT) + return 0; + nwarnf("Failed to read notification from %d", seccomp_fd); + return -1; + } + + for (i = 0; i < ctx->n_plugins; i++) { + if (ctx->plugins[i].handle_request_cb) { + int resp_handled = 0; + int ret; + + ret = ctx->plugins[i].handle_request_cb(ctx->plugins[i].opaque, &ctx->sizes, ctx->sreq, ctx->sresp, seccomp_fd, + &resp_handled); + if (ret != 0) { + nwarnf("Failed to handle seccomp notification from fd %d", seccomp_fd); + return -1; + } + + switch (resp_handled) { + case RUN_OCI_SECCOMP_NOTIFY_HANDLE_NOT_HANDLED: + break; + + case RUN_OCI_SECCOMP_NOTIFY_HANDLE_SEND_RESPONSE: + handled = true; + break; + + /* The plugin will take care of it. */ + case RUN_OCI_SECCOMP_NOTIFY_HANDLE_DELAYED_RESPONSE: + return 0; + + case RUN_OCI_SECCOMP_NOTIFY_HANDLE_SEND_RESPONSE_AND_CONTINUE: + ctx->sresp->flags |= SECCOMP_USER_NOTIF_FLAG_CONTINUE; + handled = true; + break; + + default: + pexitf("Unknown handler action specified %d", handled); + return -1; + } + } + } + + /* No plugin could handle the request. */ + if (!handled) { + ctx->sresp->error = -ENOTSUP; + ctx->sresp->flags = 0; + } + + ctx->sresp->id = ctx->sreq->id; + ret = ioctl(seccomp_fd, SECCOMP_IOCTL_NOTIF_SEND, ctx->sresp); + if (ret < 0) { + if (errno == ENOENT) + return 0; + nwarnf("Failed to send seccomp notification on fd %d", seccomp_fd); + return -errno; + } + return 0; +} + +int seccomp_notify_plugins_free(struct seccomp_notify_context_s *ctx) +{ + size_t i; + + if (ctx == NULL) { + nwarnf("Invalid seccomp notification context"); + return -1; + } + + free(ctx->sreq); + free(ctx->sresp); + + for (i = 0; i < ctx->n_plugins; i++) { + if (ctx->plugins && ctx->plugins[i].handle) { + run_oci_seccomp_notify_stop_cb cb; + + cb = (run_oci_seccomp_notify_stop_cb)dlsym(ctx->plugins[i].handle, "run_oci_seccomp_notify_stop"); + if (cb) + cb(ctx->plugins[i].opaque); + dlclose(ctx->plugins[i].handle); + } + } + + free(ctx); + + return 0; +} + +static void cleanup_seccomp_plugins() +{ + if (seccomp_notify_ctx) { + seccomp_notify_plugins_free(seccomp_notify_ctx); + seccomp_notify_ctx = NULL; + } +} + +void cleanup_seccomp_notify_pluginsp(void *p) +{ + struct seccomp_notify_context_s **pp = p; + if (*pp) { + seccomp_notify_plugins_free(*pp); + *pp = NULL; + } +} + +static inline void *xmalloc0(size_t size) +{ + void *res = calloc(1, size); + if (res == NULL) + pexitf("calloc"); + return res; +} + +static int seccomp_syscall(unsigned int op, unsigned int flags, void *args) +{ + errno = 0; + return syscall(__NR_seccomp, op, flags, args); +} diff --git a/src/seccomp_notify.h b/src/seccomp_notify.h new file mode 100644 index 00000000..22cf824f --- /dev/null +++ b/src/seccomp_notify.h @@ -0,0 +1,18 @@ +#ifndef SECCOMP_NOTIFY_H +#define SECCOMP_NOTIFY_H + +#include "seccomp_notify_plugin.h" + +struct seccomp_notify_context_s; + +gboolean seccomp_cb(int fd, GIOCondition condition, G_GNUC_UNUSED gpointer user_data); +gboolean seccomp_accept_cb(int fd, G_GNUC_UNUSED GIOCondition condition, G_GNUC_UNUSED gpointer user_data); + +int seccomp_notify_plugins_load(struct seccomp_notify_context_s **out, const char *plugins, struct seccomp_notify_conf_s *conf); +int seccomp_notify_plugins_event(struct seccomp_notify_context_s *ctx, int seccomp_fd); +int seccomp_notify_plugins_free(struct seccomp_notify_context_s *ctx); + +#define cleanup_seccomp_notify_context __attribute__((cleanup(cleanup_seccomp_notify_pluginsp))) +void cleanup_seccomp_notify_pluginsp(void *p); + +#endif diff --git a/src/seccomp_notify_plugin.h b/src/seccomp_notify_plugin.h new file mode 100644 index 00000000..a35c3664 --- /dev/null +++ b/src/seccomp_notify_plugin.h @@ -0,0 +1,40 @@ +#ifndef SECCOMP_NOTIFY_PLUGINPLUGIN_H + +#include + +struct seccomp_notify_conf_s { + const char *runtime_root_path; + const char *name; + const char *bundle_path; + const char *oci_config_path; +}; + +/* The plugin doesn't know how to handle the request. */ +#define RUN_OCI_SECCOMP_NOTIFY_HANDLE_NOT_HANDLED 0 +/* The plugin filled the response and it is ready to write. */ +#define RUN_OCI_SECCOMP_NOTIFY_HANDLE_SEND_RESPONSE 1 +/* The plugin will handle the request and write directly to the fd. */ +#define RUN_OCI_SECCOMP_NOTIFY_HANDLE_DELAYED_RESPONSE 2 +/* Specify SECCOMP_USER_NOTIF_FLAG_CONTINUE in the flags. */ +#define RUN_OCI_SECCOMP_NOTIFY_HANDLE_SEND_RESPONSE_AND_CONTINUE 3 + +/* Configure the plugin. Return an opaque pointer that will be used for successive calls. */ +typedef int (*run_oci_seccomp_notify_start_cb)(void **opaque, struct seccomp_notify_conf_s *conf, size_t size_configuration); + +/* Try to handle a single request. It MUST be defined. + HANDLED specifies how the request was handled by the plugin: + 0: not handled, try next plugin or return ENOTSUP if it is the last plugin. + RUN_OCI_SECCOMP_NOTIFY_HANDLE_SEND_RESPONSE: sresp filled and ready to be notified to seccomp. + RUN_OCI_SECCOMP_NOTIFY_HANDLE_DELAYED_RESPONSE: the notification will be handled internally by the plugin and forwarded to seccomp_fd. It + is useful for asynchronous handling. +*/ +typedef int (*run_oci_seccomp_notify_handle_request_cb)(void *opaque, struct seccomp_notif_sizes *sizes, struct seccomp_notif *sreq, + struct seccomp_notif_resp *sresp, int seccomp_fd, int *handled); + +/* Stop the plugin. The opaque value is the return value from run_oci_seccomp_notify_start. */ +typedef int (*run_oci_seccomp_notify_stop_cb)(void *opaque); + +/* Retrieve the API version used by the plugin. It MUST return 1. */ +typedef int (*run_oci_seccomp_notify_plugin_version_cb)(); + +#endif