From cdb2a47f9b0ffc004c6c92989fa10741481617d1 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Sun, 21 Apr 2019 21:03:51 +0200 Subject: [PATCH 1/6] seccomp: SECCOMP_RET_USER_NOTIF support Signed-off-by: Christian Brauner --- configure.ac | 1 + src/lxc/af_unix.c | 21 +++-- src/lxc/attach.c | 36 ++++++- src/lxc/commands.c | 210 ++++++++++++++++++++++++++++++----------- src/lxc/commands.h | 6 ++ src/lxc/conf.c | 7 ++ src/lxc/conf.h | 7 ++ src/lxc/lxccontainer.c | 23 +++++ src/lxc/lxccontainer.h | 21 +++++ src/lxc/lxcseccomp.h | 13 +++ src/lxc/macro.h | 6 ++ src/lxc/seccomp.c | 88 +++++++++++++++++ src/lxc/start.c | 62 ++++++++++-- 13 files changed, 430 insertions(+), 71 deletions(-) diff --git a/configure.ac b/configure.ac index a12360250..8d6774f23 100644 --- a/configure.ac +++ b/configure.ac @@ -363,6 +363,7 @@ AM_COND_IF([ENABLE_CAP], OLD_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $SECCOMP_CFLAGS" AC_CHECK_TYPES([scmp_filter_ctx], [], [], [[#include ]]) +AC_CHECK_DECLS([seccomp_notif_get_fd], [], [], [[#include ]]) AC_CHECK_DECLS([seccomp_syscall_resolve_name_arch], [], [], [[#include ]]) CFLAGS="$OLD_CFLAGS" diff --git a/src/lxc/af_unix.c b/src/lxc/af_unix.c index 3b00d64fb..06700fac1 100644 --- a/src/lxc/af_unix.c +++ b/src/lxc/af_unix.c @@ -201,7 +201,8 @@ int lxc_abstract_unix_recv_fds(int fd, int *recvfds, int num_recvfds, struct iovec iov; struct cmsghdr *cmsg = NULL; char buf[1] = {0}; - size_t cmsgbufsize = CMSG_SPACE(num_recvfds * sizeof(int)); + size_t cmsgbufsize = CMSG_SPACE(sizeof(struct ucred)) + + CMSG_SPACE(num_recvfds * sizeof(int)); memset(&msg, 0, sizeof(msg)); memset(&iov, 0, sizeof(iov)); @@ -224,12 +225,20 @@ int lxc_abstract_unix_recv_fds(int fd, int *recvfds, int num_recvfds, if (ret <= 0) goto out; - cmsg = CMSG_FIRSTHDR(&msg); + /* + * If SO_PASSCRED is set we will always get a ucred message. + */ + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_type != SCM_RIGHTS) + continue; - memset(recvfds, -1, num_recvfds * sizeof(int)); - if (cmsg && cmsg->cmsg_len == CMSG_LEN(num_recvfds * sizeof(int)) && - cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) - memcpy(recvfds, CMSG_DATA(cmsg), num_recvfds * sizeof(int)); + memset(recvfds, -1, num_recvfds * sizeof(int)); + if (cmsg && + cmsg->cmsg_len == CMSG_LEN(num_recvfds * sizeof(int)) && + cmsg->cmsg_level == SOL_SOCKET) + memcpy(recvfds, CMSG_DATA(cmsg), num_recvfds * sizeof(int)); + break; + } out: return ret; diff --git a/src/lxc/attach.c b/src/lxc/attach.c index f2a1339d3..ac0450187 100644 --- a/src/lxc/attach.c +++ b/src/lxc/attach.c @@ -853,11 +853,26 @@ static int attach_child_main(struct attach_clone_payload *payload) if (init_ctx->container && init_ctx->container->lxc_conf && init_ctx->container->lxc_conf->seccomp) { - ret = lxc_seccomp_load(init_ctx->container->lxc_conf); + struct lxc_conf *conf = init_ctx->container->lxc_conf; + + ret = lxc_seccomp_load(conf); if (ret < 0) goto on_error; TRACE("Loaded seccomp profile"); + +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD + if (conf->has_seccomp_notify) { + ret = lxc_abstract_unix_send_fds(payload->ipc_socket, + &conf->seccomp_notify_fd, + 1, NULL, 0); + close_prot_errno_disarm(conf->seccomp_notify_fd); + if (ret < 0) + goto on_error; + + TRACE("Sent seccomp listener fd to parent"); + } +#endif } close(payload->ipc_socket); @@ -1311,6 +1326,25 @@ int lxc_attach(const char *name, const char *lxcpath, TRACE("Sent LSM label file descriptor %d to child", labelfd); } +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD + if (conf->seccomp && conf->has_seccomp_notify) { + ret = lxc_abstract_unix_recv_fds(ipc_sockets[0], + &conf->seccomp_notify_fd, + 1, NULL, 0); + if (ret < 0) + goto close_mainloop; + + TRACE("Retrieved seccomp listener fd %d from child", + conf->seccomp_notify_fd); + ret = lxc_cmd_seccomp_notify_add_listener(name, lxcpath, + conf->seccomp_notify_fd, + -1, 0); + close_prot_errno_disarm(conf->seccomp_notify_fd); + if (ret < 0) + goto close_mainloop; + } +#endif + /* We're done, the child process should now execute whatever it * is that the user requested. The parent can now track it with * waitpid() or similar. diff --git a/src/lxc/commands.c b/src/lxc/commands.c index ad2c578e1..e42990465 100644 --- a/src/lxc/commands.c +++ b/src/lxc/commands.c @@ -47,6 +47,7 @@ #include "log.h" #include "lxc.h" #include "lxclock.h" +#include "lxcseccomp.h" #include "mainloop.h" #include "memory_utils.h" #include "monitor.h" @@ -97,6 +98,7 @@ static const char *lxc_cmd_str(lxc_cmd_t cmd) [LXC_CMD_ADD_STATE_CLIENT] = "add_state_client", [LXC_CMD_CONSOLE_LOG] = "console_log", [LXC_CMD_SERVE_STATE_CLIENTS] = "serve_state_clients", + [LXC_CMD_SECCOMP_NOTIFY_ADD_LISTENER] = "seccomp_notify_add_listener", }; if (cmd >= LXC_CMD_MAX) @@ -244,14 +246,21 @@ static int lxc_cmd_send(const char *name, struct lxc_cmd_rr *cmd, if (ret < 0 || (size_t)ret != sizeof(cmd->req)) return -1; - if (cmd->req.datalen <= 0) - return move_fd(client_fd); + if (cmd->req.cmd == LXC_CMD_SECCOMP_NOTIFY_ADD_LISTENER) { + int notify_fd = PTR_TO_INT(cmd->req.data); + ret = lxc_abstract_unix_send_fds(client_fd, ¬ify_fd, 1, NULL, 0); + if (ret <= 0) + return -1; + } else { + if (cmd->req.datalen <= 0) + return move_fd(client_fd); - errno = EMSGSIZE; - ret = lxc_send_nointr(client_fd, (void *)cmd->req.data, - cmd->req.datalen, MSG_NOSIGNAL); - if (ret < 0 || ret != (ssize_t)cmd->req.datalen) - return -1; + errno = EMSGSIZE; + ret = lxc_send_nointr(client_fd, (void *)cmd->req.data, + cmd->req.datalen, MSG_NOSIGNAL); + if (ret < 0 || ret != (ssize_t)cmd->req.datalen) + return -1; + } return move_fd(client_fd); } @@ -373,7 +382,8 @@ pid_t lxc_cmd_get_init_pid(const char *name, const char *lxcpath) } static int lxc_cmd_get_init_pid_callback(int fd, struct lxc_cmd_req *req, - struct lxc_handler *handler) + struct lxc_handler *handler, + struct lxc_epoll_descr *descr) { intmax_t pid = handler->pid; @@ -407,7 +417,8 @@ int lxc_cmd_get_clone_flags(const char *name, const char *lxcpath) } static int lxc_cmd_get_clone_flags_callback(int fd, struct lxc_cmd_req *req, - struct lxc_handler *handler) + struct lxc_handler *handler, + struct lxc_epoll_descr *descr) { struct lxc_cmd_rsp rsp = { .data = INT_TO_PTR(handler->ns_clone_flags) }; @@ -457,7 +468,8 @@ char *lxc_cmd_get_cgroup_path(const char *name, const char *lxcpath, } static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req, - struct lxc_handler *handler) + struct lxc_handler *handler, + struct lxc_epoll_descr *descr) { const char *path; struct lxc_cmd_rsp rsp; @@ -509,7 +521,8 @@ char *lxc_cmd_get_config_item(const char *name, const char *item, } static int lxc_cmd_get_config_item_callback(int fd, struct lxc_cmd_req *req, - struct lxc_handler *handler) + struct lxc_handler *handler, + struct lxc_epoll_descr *descr) { __do_free char *cidata = NULL; int cilen; @@ -575,7 +588,8 @@ int lxc_cmd_get_state(const char *name, const char *lxcpath) } static int lxc_cmd_get_state_callback(int fd, struct lxc_cmd_req *req, - struct lxc_handler *handler) + struct lxc_handler *handler, + struct lxc_epoll_descr *descr) { struct lxc_cmd_rsp rsp = { .data = INT_TO_PTR(handler->state) }; @@ -622,7 +636,8 @@ int lxc_cmd_stop(const char *name, const char *lxcpath) } static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req, - struct lxc_handler *handler) + struct lxc_handler *handler, + struct lxc_epoll_descr *descr) { struct lxc_cmd_rsp rsp; int stopsignal = SIGKILL; @@ -665,7 +680,8 @@ int lxc_cmd_terminal_winch(const char *name, const char *lxcpath) } static int lxc_cmd_terminal_winch_callback(int fd, struct lxc_cmd_req *req, - struct lxc_handler *handler) + struct lxc_handler *handler, + struct lxc_epoll_descr *descr) { /* should never be called */ return -1; @@ -720,7 +736,8 @@ int lxc_cmd_console(const char *name, int *ttynum, int *fd, const char *lxcpath) } static int lxc_cmd_console_callback(int fd, struct lxc_cmd_req *req, - struct lxc_handler *handler) + struct lxc_handler *handler, + struct lxc_epoll_descr *descr) { int masterfd, ret; struct lxc_cmd_rsp rsp; @@ -773,7 +790,8 @@ char *lxc_cmd_get_name(const char *hashed_sock_name) } static int lxc_cmd_get_name_callback(int fd, struct lxc_cmd_req *req, - struct lxc_handler *handler) + struct lxc_handler *handler, + struct lxc_epoll_descr *descr) { struct lxc_cmd_rsp rsp; @@ -811,7 +829,8 @@ char *lxc_cmd_get_lxcpath(const char *hashed_sock_name) } static int lxc_cmd_get_lxcpath_callback(int fd, struct lxc_cmd_req *req, - struct lxc_handler *handler) + struct lxc_handler *handler, + struct lxc_epoll_descr *descr) { struct lxc_cmd_rsp rsp; @@ -872,7 +891,8 @@ int lxc_cmd_add_state_client(const char *name, const char *lxcpath, } static int lxc_cmd_add_state_client_callback(int fd, struct lxc_cmd_req *req, - struct lxc_handler *handler) + struct lxc_handler *handler, + struct lxc_epoll_descr *descr) { int ret; struct lxc_cmd_rsp rsp = {0}; @@ -943,7 +963,8 @@ int lxc_cmd_console_log(const char *name, const char *lxcpath, } static int lxc_cmd_console_log_callback(int fd, struct lxc_cmd_req *req, - struct lxc_handler *handler) + struct lxc_handler *handler, + struct lxc_epoll_descr *descr) { struct lxc_cmd_rsp rsp; uint64_t buffer_size = handler->conf->console.buffer_size; @@ -1002,7 +1023,8 @@ int lxc_cmd_serve_state_clients(const char *name, const char *lxcpath, } static int lxc_cmd_serve_state_clients_callback(int fd, struct lxc_cmd_req *req, - struct lxc_handler *handler) + struct lxc_handler *handler, + struct lxc_epoll_descr *descr) { int ret; lxc_state_t state = PTR_TO_INT(req->data); @@ -1025,62 +1047,138 @@ reap_client_fd: return 1; } -static int lxc_cmd_process(int fd, struct lxc_cmd_req *req, - struct lxc_handler *handler) +int lxc_cmd_seccomp_notify_add_listener(const char *name, const char *lxcpath, + int fd, + /* unused */ unsigned int command, + /* unused */ unsigned int flags) { - typedef int (*callback)(int, struct lxc_cmd_req *, struct lxc_handler *); + +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD + int ret, stopped; + struct lxc_cmd_rr cmd = { + .req = { + .cmd = LXC_CMD_SECCOMP_NOTIFY_ADD_LISTENER, + .data = INT_TO_PTR(fd), + }, + }; + + ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); + if (ret < 0) { + SYSERROR("Failed to execute command"); + return -1; + } + + return cmd.rsp.ret; +#else + return minus_one_set_errno(ENOSYS); +#endif +} + +static int lxc_cmd_seccomp_notify_add_listener_callback(int fd, + struct lxc_cmd_req *req, + struct lxc_handler *handler, + struct lxc_epoll_descr *descr) +{ + struct lxc_cmd_rsp rsp = {0}; + int ret; + +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD + __do_close_prot_errno int recv_fd = -EBADF; + int notify_fd = -EBADF; + + if (!handler->conf->has_seccomp_notify || + handler->conf->seccomp_notify_proxy_fd < 0) { + rsp.ret = -EINVAL; + goto send_error; + } + + ret = lxc_abstract_unix_recv_fds(fd, &recv_fd, 1, NULL, 0); + if (ret <= 0) + goto reap_client_fd; + + ret = lxc_mainloop_add_handler(descr, notify_fd, seccomp_notify_handler, + handler); + notify_fd = move_fd(recv_fd); + if (ret < 0) + goto reap_client_fd; + +send_error: +#else + rsp.ret = -ENOSYS; +#endif + ret = lxc_cmd_rsp_send(fd, &rsp); + if (ret < 0) + goto reap_client_fd; + + return 0; + +reap_client_fd: + /* Special indicator to lxc_cmd_handler() to close the fd and do + * related cleanup. + */ + return 1; +} + +static int lxc_cmd_process(int fd, struct lxc_cmd_req *req, + struct lxc_handler *handler, + struct lxc_epoll_descr *descr) +{ + typedef int (*callback)(int, struct lxc_cmd_req *, struct lxc_handler *, + struct lxc_epoll_descr *); callback cb[LXC_CMD_MAX] = { - [LXC_CMD_CONSOLE] = lxc_cmd_console_callback, - [LXC_CMD_TERMINAL_WINCH] = lxc_cmd_terminal_winch_callback, - [LXC_CMD_STOP] = lxc_cmd_stop_callback, - [LXC_CMD_GET_STATE] = lxc_cmd_get_state_callback, - [LXC_CMD_GET_INIT_PID] = lxc_cmd_get_init_pid_callback, - [LXC_CMD_GET_CLONE_FLAGS] = lxc_cmd_get_clone_flags_callback, - [LXC_CMD_GET_CGROUP] = lxc_cmd_get_cgroup_callback, - [LXC_CMD_GET_CONFIG_ITEM] = lxc_cmd_get_config_item_callback, - [LXC_CMD_GET_NAME] = lxc_cmd_get_name_callback, - [LXC_CMD_GET_LXCPATH] = lxc_cmd_get_lxcpath_callback, - [LXC_CMD_ADD_STATE_CLIENT] = lxc_cmd_add_state_client_callback, - [LXC_CMD_CONSOLE_LOG] = lxc_cmd_console_log_callback, - [LXC_CMD_SERVE_STATE_CLIENTS] = lxc_cmd_serve_state_clients_callback, + [LXC_CMD_CONSOLE] = lxc_cmd_console_callback, + [LXC_CMD_TERMINAL_WINCH] = lxc_cmd_terminal_winch_callback, + [LXC_CMD_STOP] = lxc_cmd_stop_callback, + [LXC_CMD_GET_STATE] = lxc_cmd_get_state_callback, + [LXC_CMD_GET_INIT_PID] = lxc_cmd_get_init_pid_callback, + [LXC_CMD_GET_CLONE_FLAGS] = lxc_cmd_get_clone_flags_callback, + [LXC_CMD_GET_CGROUP] = lxc_cmd_get_cgroup_callback, + [LXC_CMD_GET_CONFIG_ITEM] = lxc_cmd_get_config_item_callback, + [LXC_CMD_GET_NAME] = lxc_cmd_get_name_callback, + [LXC_CMD_GET_LXCPATH] = lxc_cmd_get_lxcpath_callback, + [LXC_CMD_ADD_STATE_CLIENT] = lxc_cmd_add_state_client_callback, + [LXC_CMD_CONSOLE_LOG] = lxc_cmd_console_log_callback, + [LXC_CMD_SERVE_STATE_CLIENTS] = lxc_cmd_serve_state_clients_callback, + [LXC_CMD_SECCOMP_NOTIFY_ADD_LISTENER] = lxc_cmd_seccomp_notify_add_listener_callback, }; if (req->cmd >= LXC_CMD_MAX) { ERROR("Undefined command id %d", req->cmd); return -1; } - return cb[req->cmd](fd, req, handler); + return cb[req->cmd](fd, req, handler, descr); } static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler, - struct lxc_epoll_descr *descr, - const lxc_cmd_t cmd) + struct lxc_epoll_descr *descr, const lxc_cmd_t cmd) { struct lxc_list *cur, *next; lxc_terminal_free(handler->conf, fd); lxc_mainloop_del_handler(descr, fd); - if (cmd != LXC_CMD_ADD_STATE_CLIENT) { - close(fd); - return; - } - lxc_list_for_each_safe(cur, &handler->conf->state_clients, next) { - struct lxc_state_client *client = cur->elem; + switch (cmd) { + case LXC_CMD_ADD_STATE_CLIENT: + lxc_list_for_each_safe(cur, &handler->conf->state_clients, next) { + struct lxc_state_client *client = cur->elem; - if (client->clientfd != fd) - continue; + if (client->clientfd != fd) + continue; - /* kick client from list */ - lxc_list_del(cur); - close(client->clientfd); - free(cur->elem); - free(cur); - /* No need to walk the whole list. If we found the state client - * fd there can't be a second one. - */ + /* kick client from list */ + lxc_list_del(cur); + close(client->clientfd); + free(cur->elem); + free(cur); + /* No need to walk the whole list. If we found the state + * client fd there can't be a second one. + */ + break; + } break; + default: + close(fd); } } @@ -1139,7 +1237,7 @@ static int lxc_cmd_handler(int fd, uint32_t events, void *data, req.data = reqdata; } - ret = lxc_cmd_process(fd, &req, handler); + ret = lxc_cmd_process(fd, &req, handler, descr); if (ret) { /* This is not an error, but only a request to close fd. */ ret = LXC_MAINLOOP_CONTINUE; diff --git a/src/lxc/commands.h b/src/lxc/commands.h index 2c024b65d..d7d0c6096 100644 --- a/src/lxc/commands.h +++ b/src/lxc/commands.h @@ -46,6 +46,7 @@ typedef enum { LXC_CMD_ADD_STATE_CLIENT, LXC_CMD_CONSOLE_LOG, LXC_CMD_SERVE_STATE_CLIENTS, + LXC_CMD_SECCOMP_NOTIFY_ADD_LISTENER, LXC_CMD_MAX, } lxc_cmd_t; @@ -124,5 +125,10 @@ extern int lxc_cmd_mainloop_add(const char *name, struct lxc_epoll_descr *descr, extern int lxc_try_cmd(const char *name, const char *lxcpath); extern int lxc_cmd_console_log(const char *name, const char *lxcpath, struct lxc_console_log *log); +extern int lxc_cmd_seccomp_notify_add_listener(const char *name, + const char *lxcpath, + int fd, + /* unused */ unsigned int command, + /* unused */ unsigned int flags); #endif /* __commands_h */ diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 986cb89b0..453763243 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -2752,6 +2752,13 @@ struct lxc_conf *lxc_conf_init(void) new->lsm_aa_profile = NULL; lxc_list_init(&new->lsm_aa_raw); new->lsm_se_context = NULL; +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD + new->has_seccomp_notify = false; + new->seccomp_notify_fd = -EBADF; + new->seccomp_notify_proxy_fd = -EBADF; + new->seccomp_notify_req = NULL; + new->seccomp_notify_resp = NULL; +#endif new->tmp_umount_proc = false; new->tmp_umount_proc = 0; new->shmount.path_host = NULL; diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 85daf1b6a..f00f477c3 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -299,6 +299,13 @@ struct lxc_conf { unsigned int seccomp_allow_nesting; #if HAVE_SCMP_FILTER_CTX scmp_filter_ctx seccomp_ctx; +#endif +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD + bool has_seccomp_notify; + int seccomp_notify_fd; + int seccomp_notify_proxy_fd; + struct seccomp_notif *seccomp_notify_req; + struct seccomp_notif_resp *seccomp_notify_resp; #endif int maincmd_fd; unsigned int autodev; /* if 1, mount and fill a /dev at start */ diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 72d87410f..e16639d48 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -5227,6 +5227,28 @@ out: return ret; } +static int do_lxcapi_seccomp_notify(struct lxc_container *c, unsigned int cmd, int fd) +{ +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD + if (!c || !c->lxc_conf) + return minus_one_set_errno(-EINVAL); + + switch (cmd) { + case LXC_SECCOMP_NOTIFY_GET_FD: + if (fd) + return minus_one_set_errno(EINVAL); + + return c->lxc_conf->seccomp_notify_fd; + } + + return minus_one_set_errno(EINVAL); +#else + return minus_one_set_errno(ENOSYS); +#endif +} + +WRAP_API_2(int, lxcapi_seccomp_notify, unsigned int, int) + struct lxc_container *lxc_container_new(const char *name, const char *configpath) { struct lxc_container *c; @@ -5351,6 +5373,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath c->console_log = lxcapi_console_log; c->mount = lxcapi_mount; c->umount = lxcapi_umount; + c->seccomp_notify = lxcapi_seccomp_notify; return c; diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index c46be521e..2bd327114 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -31,6 +31,10 @@ #include +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD +#include +#endif + #ifdef __cplusplus extern "C" { #endif @@ -60,6 +64,21 @@ struct lxc_mount { int version; }; +enum { + LXC_SECCOMP_NOTIFY_GET_FD = 0, + LXC_SECCOMP_NOTIFY_MAX, +}; + +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD +struct seccomp_notify_proxy_msg { + uint32_t version; + struct seccomp_notif req; + struct seccomp_notif_resp resp; + pid_t monitor_pid; + pid_t init_pid; +}; +#endif + /*! * An LXC container. * @@ -867,6 +886,8 @@ struct lxc_container { */ int (*umount)(struct lxc_container *c, const char *target, unsigned long mountflags, struct lxc_mount *mnt); + + int (*seccomp_notify)(struct lxc_container *c, unsigned int cmd, int fd); }; /*! diff --git a/src/lxc/lxcseccomp.h b/src/lxc/lxcseccomp.h index 93d57bbdc..530fffd80 100644 --- a/src/lxc/lxcseccomp.h +++ b/src/lxc/lxcseccomp.h @@ -24,12 +24,20 @@ #ifndef __LXC_LXCSECCOMP_H #define __LXC_LXCSECCOMP_H +#include +#ifdef HAVE_SECCOMP +#include +#endif + #include "conf.h" #ifdef HAVE_SECCOMP extern int lxc_seccomp_load(struct lxc_conf *conf); extern int lxc_read_seccomp_config(struct lxc_conf *conf); extern void lxc_seccomp_free(struct lxc_conf *conf); +extern int seccomp_notify_handler(int fd, uint32_t events, void *data, + struct lxc_epoll_descr *descr); + #else static inline int lxc_seccomp_load(struct lxc_conf *conf) { @@ -46,6 +54,11 @@ static inline void lxc_seccomp_free(struct lxc_conf *conf) free(conf->seccomp); conf->seccomp = NULL; } +static inline int seccomp_notify_handler(int fd, uint32_t events, void *data, + struct lxc_epoll_descr *descr) +{ + return -ENOSYS; +} #endif #endif diff --git a/src/lxc/macro.h b/src/lxc/macro.h index b83e43911..7df3b56f0 100644 --- a/src/lxc/macro.h +++ b/src/lxc/macro.h @@ -407,4 +407,10 @@ enum { __internal_fd__; \ }) +#define minus_one_set_errno(__errno__) \ + ({ \ + errno = __errno__; \ + -1; \ + }) + #endif /* __LXC_MACRO_H */ diff --git a/src/lxc/seccomp.c b/src/lxc/seccomp.c index f90602e1f..10b634f2b 100644 --- a/src/lxc/seccomp.c +++ b/src/lxc/seccomp.c @@ -33,7 +33,9 @@ #include "config.h" #include "log.h" +#include "lxccontainer.h" #include "lxcseccomp.h" +#include "memory_utils.h" #include "utils.h" #ifdef __MIPSEL__ @@ -87,6 +89,10 @@ static const char *get_action_name(uint32_t action) return "trap"; case SCMP_ACT_ERRNO(0): return "errno"; +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD + case SCMP_ACT_USER_NOTIF: + return "notify"; +#endif } return "invalid action"; @@ -116,6 +122,10 @@ static uint32_t get_v2_default_action(char *line) ret_action = SCMP_ACT_ALLOW; } else if (strncmp(line, "trap", 4) == 0) { ret_action = SCMP_ACT_TRAP; +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD + } else if (strncmp(line, "notify", 6) == 0) { + ret_action = SCMP_ACT_USER_NOTIF; +#endif } else if (line[0]) { ERROR("Unrecognized seccomp action \"%s\"", line); return -2; @@ -928,6 +938,19 @@ static int parse_config_v2(FILE *f, char *line, size_t *line_bufsz, struct lxc_c goto bad_rule; } +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD + if ((rule.action == SCMP_ACT_USER_NOTIF) && + !conf->has_seccomp_notify) { + ret = seccomp_attr_set(conf->seccomp_ctx, + SCMP_FLTATR_NEW_LISTENER, 1); + if (ret) + goto bad_rule; + + conf->has_seccomp_notify = true; + TRACE("Set SCMP_FLTATR_NEW_LISTENER attribute"); + } +#endif + if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line, conf->seccomp_ctx, &rule)) goto bad_rule; @@ -1230,6 +1253,19 @@ int lxc_seccomp_load(struct lxc_conf *conf) } #endif +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD + if (conf->has_seccomp_notify) { + ret = seccomp_notif_get_fd(conf->seccomp_ctx); + if (ret < 0) { + errno = -ret; + return -1; + } + + conf->seccomp_notify_fd = ret; + TRACE("Retrieved new seccomp listener fd %d", ret); + } +#endif + return 0; } @@ -1244,4 +1280,56 @@ void lxc_seccomp_free(struct lxc_conf *conf) conf->seccomp_ctx = NULL; } #endif + +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD + close_prot_errno_disarm(conf->seccomp_notify_fd); + close_prot_errno_disarm(conf->seccomp_notify_proxy_fd); + seccomp_notif_free(conf->seccomp_notify_req, conf->seccomp_notify_resp); + conf->seccomp_notify_req = NULL; + conf->seccomp_notify_resp = NULL; +#endif +} + +int seccomp_notify_handler(int fd, uint32_t events, void *data, + struct lxc_epoll_descr *descr) +{ + +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD + int ret; + struct lxc_handler *hdlr = data; + struct lxc_conf *conf = hdlr->conf; + struct seccomp_notif *req = conf->seccomp_notify_req; + struct seccomp_notif_resp *resp = conf->seccomp_notify_resp; + int listener_proxy_fd = conf->seccomp_notify_proxy_fd; + struct seccomp_notify_proxy_msg msg; + + if (listener_proxy_fd < 0) + return minus_one_set_errno(EINVAL); + + ret = seccomp_notif_receive(fd, req); + if (ret) + return minus_one_set_errno(-ret); + + memcpy(&msg.req, req, sizeof(msg.req)); + msg.monitor_pid = hdlr->monitor_pid; + msg.init_pid = hdlr->pid; + + ret = lxc_send_nointr(listener_proxy_fd, &msg, sizeof(msg), MSG_NOSIGNAL); + if (ret < 0 || ret != (ssize_t)sizeof(msg)) + return -1; + + ret = lxc_recv_nointr(listener_proxy_fd, &msg, sizeof(msg), 0); + if (ret != (ssize_t)sizeof(msg)) + return -1; + + memcpy(resp, &msg.resp, sizeof(*resp)); + + ret = seccomp_notif_send_resp(fd, resp); + if (ret) + return minus_one_set_errno(-ret); + + return 0; +#else + return -ENOSYS; +#endif } diff --git a/src/lxc/start.c b/src/lxc/start.c index 46a92d3d4..b1b4a3af7 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -591,6 +591,20 @@ int lxc_poll(const char *name, struct lxc_handler *handler) goto out_mainloop_console; } +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD + if (handler->conf->has_seccomp_notify && + handler->conf->seccomp_notify_proxy_fd >= 0) { + ret = lxc_mainloop_add_handler(&descr, + handler->conf->seccomp_notify_fd, + seccomp_notify_handler, handler); + if (ret < 0) { + ERROR("Failed to add seccomp notify handler for %d to mainloop", + handler->conf->seccomp_notify_fd); + goto out_mainloop_console; + } + } +#endif + if (has_console) { struct lxc_terminal *console = &handler->conf->console; @@ -1094,6 +1108,9 @@ void lxc_abort(const char *name, struct lxc_handler *handler) static int do_start(void *data) { + struct lxc_handler *handler = data; + __do_close_prot_errno int data_sock0 = handler->data_sock[0], + data_sock1 = handler->data_sock[1]; int ret; char path[PATH_MAX]; uid_t new_uid; @@ -1102,7 +1119,6 @@ static int do_start(void *data) uid_t nsuid = 0; gid_t nsgid = 0; int devnull_fd = -1; - struct lxc_handler *handler = data; lxc_sync_fini_parent(handler); @@ -1278,8 +1294,6 @@ static int do_start(void *data) /* Setup the container, ip, names, utsname, ... */ ret = lxc_setup(handler); - close(handler->data_sock[1]); - close(handler->data_sock[0]); if (ret < 0) { ERROR("Failed to setup container \"%s\"", handler->name); goto out_warn_father; @@ -1330,6 +1344,20 @@ static int do_start(void *data) if (ret < 0) goto out_warn_father; +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD + if (handler->conf->has_seccomp_notify) { + ret = lxc_abstract_unix_send_fds(data_sock0, + &handler->conf->seccomp_notify_fd, + 1, NULL, 0); + if (ret < 0) { + SYSERROR("Failed to send seccomp notify fd to parent"); + goto out_warn_father; + } + close(handler->conf->seccomp_notify_fd); + handler->conf->seccomp_notify_fd = -EBADF; + } +#endif + ret = run_lxc_hooks(handler->name, "start", handler->conf, NULL); if (ret < 0) { ERROR("Failed to run lxc.hook.start for container \"%s\"", @@ -1592,6 +1620,7 @@ static inline int do_share_ns(void *arg) */ static int lxc_spawn(struct lxc_handler *handler) { + __do_close_prot_errno int data_sock0 = -EBADF, data_sock1 = -EBADF; int i, ret; char pidstr[20]; bool wants_to_map_ids; @@ -1624,6 +1653,8 @@ static int lxc_spawn(struct lxc_handler *handler) handler->data_sock); if (ret < 0) goto out_sync_fini; + data_sock0 = handler->data_sock[0]; + data_sock1 = handler->data_sock[1]; ret = resolve_clone_flags(handler); if (ret < 0) @@ -1888,6 +1919,26 @@ static int lxc_spawn(struct lxc_handler *handler) goto out_delete_net; } +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD + if (handler->conf->has_seccomp_notify) { + ret = lxc_abstract_unix_recv_fds(handler->data_sock[1], + &handler->conf->seccomp_notify_fd, + 1, NULL, 0); + if (ret < 0) { + SYSERROR("Failed to receive seccomp notify fd from child"); + goto out_delete_net; + } + + ret = seccomp_notif_alloc(&handler->conf->seccomp_notify_req, + &handler->conf->seccomp_notify_resp); + if (ret) { + errno = ret; + ret = -1; + goto out_delete_net; + } + } +#endif + ret = handler->ops->post_start(handler, handler->data); if (ret < 0) goto out_abort; @@ -1980,11 +2031,6 @@ int __lxc_start(const char *name, struct lxc_handler *handler, ERROR("Failed to spawn container \"%s\"", name); goto out_detach_blockdev; } - /* close parent side of data socket */ - close(handler->data_sock[0]); - handler->data_sock[0] = -1; - close(handler->data_sock[1]); - handler->data_sock[1] = -1; handler->conf->reboot = REBOOT_NONE; From 86ce1da153fddccc5ae6c1ab86447a96e2520d93 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 25 Apr 2019 14:51:51 +0200 Subject: [PATCH 2/6] confile: add lxc.seccomp.notify.proxy Signed-off-by: Christian Brauner --- src/lxc/af_unix.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++ src/lxc/af_unix.h | 5 ++++ src/lxc/conf.c | 1 + src/lxc/conf.h | 1 + src/lxc/confile.c | 50 +++++++++++++++++++++++++++++++++ src/lxc/start.c | 15 +++++++++- 6 files changed, 141 insertions(+), 1 deletion(-) diff --git a/src/lxc/af_unix.c b/src/lxc/af_unix.c index 06700fac1..328f891f0 100644 --- a/src/lxc/af_unix.c +++ b/src/lxc/af_unix.c @@ -316,3 +316,73 @@ int lxc_abstract_unix_rcv_credential(int fd, void *data, size_t size) out: return ret; } + +int lxc_unix_sockaddr(struct sockaddr_un *ret, const char *path) +{ + size_t len; + + len = strlen(path); + if (len == 0) + return minus_one_set_errno(EINVAL); + if (path[0] != '/' && path[0] != '@') + return minus_one_set_errno(EINVAL); + if (path[1] == '\0') + return minus_one_set_errno(EINVAL); + + if (len + 1 > sizeof(ret->sun_path)) + return minus_one_set_errno(EINVAL); + + *ret = (struct sockaddr_un){ + .sun_family = AF_UNIX, + }; + + if (path[0] == '@') { + memcpy(ret->sun_path + 1, path + 1, len); + return (int)(offsetof(struct sockaddr_un, sun_path) + len); + } + + memcpy(ret->sun_path, path, len + 1); + return (int)(offsetof(struct sockaddr_un, sun_path) + len + 1); +} + +int lxc_unix_connect(struct sockaddr_un *addr) +{ + __do_close_prot_errno int fd = -EBADF; + int ret; + ssize_t len; + + fd = socket(PF_UNIX, SOCK_STREAM, SOCK_CLOEXEC); + if (fd < 0) + return -1; + + if (addr->sun_path[0] == '\0') + len = strlen(&addr->sun_path[1]); + else + len = strlen(&addr->sun_path[0]); + ret = connect(fd, (struct sockaddr *)&addr, + offsetof(struct sockaddr_un, sun_path) + len + 1); + if (ret < 0) + return -1; + + return move_fd(fd); +} + +int lxc_socket_set_timeout(int fd, int rcv_timeout, int snd_timeout) +{ + struct timeval out = {0}; + int ret; + + out.tv_sec = snd_timeout; + ret = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (const void *)&out, + sizeof(out)); + if (ret < 0) + return -1; + + out.tv_sec = rcv_timeout; + ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const void *)&out, + sizeof(out)); + if (ret < 0) + return -1; + + return 0; +} diff --git a/src/lxc/af_unix.h b/src/lxc/af_unix.h index f2c2fdcc6..3ae595498 100644 --- a/src/lxc/af_unix.h +++ b/src/lxc/af_unix.h @@ -25,6 +25,8 @@ #define __LXC_AF_UNIX_H #include +#include +#include /* does not enforce \0-termination */ extern int lxc_abstract_unix_open(const char *path, int type, int flags); @@ -37,5 +39,8 @@ extern int lxc_abstract_unix_recv_fds(int fd, int *recvfds, int num_recvfds, void *data, size_t size); extern int lxc_abstract_unix_send_credential(int fd, void *data, size_t size); extern int lxc_abstract_unix_rcv_credential(int fd, void *data, size_t size); +extern int lxc_unix_sockaddr(struct sockaddr_un *ret, const char *path); +extern int lxc_unix_connect(struct sockaddr_un *addr); +extern int lxc_socket_set_timeout(int fd, int rcv_timeout, int snd_timeout); #endif /* __LXC_AF_UNIX_H */ diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 453763243..db00c70e9 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -2756,6 +2756,7 @@ struct lxc_conf *lxc_conf_init(void) new->has_seccomp_notify = false; new->seccomp_notify_fd = -EBADF; new->seccomp_notify_proxy_fd = -EBADF; + memset(&new->seccomp_notify_proxy_addr, 0, sizeof(new->seccomp_notify_proxy_addr)); new->seccomp_notify_req = NULL; new->seccomp_notify_resp = NULL; #endif diff --git a/src/lxc/conf.h b/src/lxc/conf.h index f00f477c3..f7b8e9724 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -304,6 +304,7 @@ struct lxc_conf { bool has_seccomp_notify; int seccomp_notify_fd; int seccomp_notify_proxy_fd; + struct sockaddr_un seccomp_notify_proxy_addr; struct seccomp_notif *seccomp_notify_req; struct seccomp_notif_resp *seccomp_notify_resp; #endif diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 8f94635d1..da5e45ce3 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -46,6 +46,7 @@ #include #include +#include "af_unix.h" #include "conf.h" #include "config.h" #include "confile.h" @@ -147,6 +148,7 @@ lxc_config_define(rootfs_options); lxc_config_define(rootfs_path); lxc_config_define(seccomp_profile); lxc_config_define(seccomp_allow_nesting); +lxc_config_define(seccomp_notify_proxy); lxc_config_define(selinux_context); lxc_config_define(signal_halt); lxc_config_define(signal_reboot); @@ -234,6 +236,7 @@ static struct lxc_config_t config_jump_table[] = { { "lxc.rootfs.options", set_config_rootfs_options, get_config_rootfs_options, clr_config_rootfs_options, }, { "lxc.rootfs.path", set_config_rootfs_path, get_config_rootfs_path, clr_config_rootfs_path, }, { "lxc.seccomp.allow_nesting", set_config_seccomp_allow_nesting, get_config_seccomp_allow_nesting, clr_config_seccomp_allow_nesting, }, + { "lxc.seccomp.notify.proxy", set_config_seccomp_notify_proxy, get_config_seccomp_notify_proxy, clr_config_seccomp_notify_proxy, }, { "lxc.seccomp.profile", set_config_seccomp_profile, get_config_seccomp_profile, clr_config_seccomp_profile, }, { "lxc.selinux.context", set_config_selinux_context, get_config_selinux_context, clr_config_selinux_context, }, { "lxc.signal.halt", set_config_signal_halt, get_config_signal_halt, clr_config_signal_halt, }, @@ -784,9 +787,31 @@ static int set_config_seccomp_allow_nesting(const char *key, const char *value, return -1; if (lxc_conf->seccomp_allow_nesting > 1) + return minus_one_set_errno(EINVAL); + + return 0; +} + +static int set_config_seccomp_notify_proxy(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD + const char *offset; + + if (lxc_config_value_empty(value)) + return clr_config_seccomp_notify_proxy(key, lxc_conf, NULL); + + if (strncmp(value, "unix:", 5) != 0) + return minus_one_set_errno(EINVAL); + + offset = value + 5; + if (lxc_unix_sockaddr(&lxc_conf->seccomp_notify_proxy_addr, offset) < 0) return -1; return 0; +#else + return minus_one_set_errno(ENOSYS); +#endif } static int set_config_seccomp_profile(const char *key, const char *value, @@ -3704,6 +3729,19 @@ static int get_config_seccomp_allow_nesting(const char *key, char *retv, return lxc_get_conf_int(c, retv, inlen, c->seccomp_allow_nesting); } +static int get_config_seccomp_notify_proxy(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD + return lxc_get_conf_str(retv, inlen, + (c->seccomp_notify_proxy_addr.sun_path[0]) == '/' + ? &c->seccomp_notify_proxy_addr.sun_path[0] + : &c->seccomp_notify_proxy_addr.sun_path[1]); +#else + return minus_one_set_errno(ENOSYS); +#endif +} + static int get_config_seccomp_profile(const char *key, char *retv, int inlen, struct lxc_conf *c, void *data) { @@ -4294,6 +4332,18 @@ static inline int clr_config_seccomp_allow_nesting(const char *key, return 0; } +static inline int clr_config_seccomp_notify_proxy(const char *key, + struct lxc_conf *c, void *data) +{ +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD + memset(&c->seccomp_notify_proxy_addr, 0, + sizeof(c->seccomp_notify_proxy_addr)); + return 0; +#else + return minus_one_set_errno(ENOSYS); +#endif +} + static inline int clr_config_seccomp_profile(const char *key, struct lxc_conf *c, void *data) { diff --git a/src/lxc/start.c b/src/lxc/start.c index b1b4a3af7..6d5913af6 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -593,7 +593,18 @@ int lxc_poll(const char *name, struct lxc_handler *handler) #if HAVE_DECL_SECCOMP_NOTIF_GET_FD if (handler->conf->has_seccomp_notify && - handler->conf->seccomp_notify_proxy_fd >= 0) { + handler->conf->seccomp_notify_proxy_addr.sun_path[1] != '\0') { + __do_close_prot_errno int notify_fd = -EBADF; + + notify_fd = lxc_unix_connect(&handler->conf->seccomp_notify_proxy_addr); + if (notify_fd < 0) + goto out_mainloop_console; + + /* 30 second timeout */ + ret = lxc_socket_set_timeout(notify_fd, 30, 30); + if (ret) + goto out_mainloop_console; + ret = lxc_mainloop_add_handler(&descr, handler->conf->seccomp_notify_fd, seccomp_notify_handler, handler); @@ -602,6 +613,8 @@ int lxc_poll(const char *name, struct lxc_handler *handler) handler->conf->seccomp_notify_fd); goto out_mainloop_console; } + + handler->conf->seccomp_notify_proxy_fd = move_fd(notify_fd); } #endif From e35b7bf81ca99e019d3957f422b15361473ea16d Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 25 Apr 2019 15:53:14 +0200 Subject: [PATCH 3/6] seccomp: send default answer and try to reconnect Signed-off-by: Christian Brauner --- src/lxc/seccomp.c | 81 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 12 deletions(-) diff --git a/src/lxc/seccomp.c b/src/lxc/seccomp.c index 10b634f2b..287da564d 100644 --- a/src/lxc/seccomp.c +++ b/src/lxc/seccomp.c @@ -31,6 +31,7 @@ #include #include +#include "af_unix.h" #include "config.h" #include "log.h" #include "lxccontainer.h" @@ -1290,12 +1291,51 @@ void lxc_seccomp_free(struct lxc_conf *conf) #endif } +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD +static int seccomp_notify_reconnect(struct lxc_handler *handler) +{ + __do_close_prot_errno int notify_fd = -EBADF; + + close_prot_errno_disarm(handler->conf->seccomp_notify_proxy_fd); + + notify_fd = lxc_unix_connect(&handler->conf->seccomp_notify_proxy_addr); + if (notify_fd < 0) { + SYSERROR("Failed to reconnect to seccomp proxy"); + return -1; + } + + /* 30 second timeout */ + if (lxc_socket_set_timeout(notify_fd, 30, 30)) { + SYSERROR("Failed to set socket timeout"); + return -1; + } + handler->conf->seccomp_notify_proxy_fd = move_fd(notify_fd); + return 0; +} +#endif + +#if HAVE_DECL_SECCOMP_NOTIF_GET_FD +static int seccomp_notify_default_answer(int fd, struct seccomp_notif *req, + struct seccomp_notif_resp *resp, + struct lxc_handler *handler) +{ + resp->id = req->id; + resp->error = -ENOSYS; + + if (seccomp_notif_send_resp(fd, resp)) + SYSERROR("Failed to send default message to seccomp"); + + return seccomp_notify_reconnect(handler); +} +#endif + int seccomp_notify_handler(int fd, uint32_t events, void *data, struct lxc_epoll_descr *descr) { #if HAVE_DECL_SECCOMP_NOTIF_GET_FD - int ret; + int reconnect_count, ret; + ssize_t bytes; struct lxc_handler *hdlr = data; struct lxc_conf *conf = hdlr->conf; struct seccomp_notif *req = conf->seccomp_notify_req; @@ -1303,31 +1343,48 @@ int seccomp_notify_handler(int fd, uint32_t events, void *data, int listener_proxy_fd = conf->seccomp_notify_proxy_fd; struct seccomp_notify_proxy_msg msg; - if (listener_proxy_fd < 0) + if (listener_proxy_fd < 0) { + ERROR("No seccomp proxy registered"); return minus_one_set_errno(EINVAL); + } ret = seccomp_notif_receive(fd, req); - if (ret) - return minus_one_set_errno(-ret); + if (ret) { + SYSERROR("Failed to read seccomp notification"); + goto out; + } memcpy(&msg.req, req, sizeof(msg.req)); msg.monitor_pid = hdlr->monitor_pid; msg.init_pid = hdlr->pid; - ret = lxc_send_nointr(listener_proxy_fd, &msg, sizeof(msg), MSG_NOSIGNAL); - if (ret < 0 || ret != (ssize_t)sizeof(msg)) - return -1; + reconnect_count = 0; + do { + bytes = lxc_send_nointr(listener_proxy_fd, &msg, sizeof(msg), + MSG_NOSIGNAL); + if (bytes != (ssize_t)sizeof(msg)) { + SYSERROR("Failed to forward message to seccomp proxy"); + if (seccomp_notify_default_answer(fd, req, resp, hdlr)) + goto out; + } + } while (reconnect_count++); - ret = lxc_recv_nointr(listener_proxy_fd, &msg, sizeof(msg), 0); - if (ret != (ssize_t)sizeof(msg)) - return -1; + reconnect_count = 0; + do { + bytes = lxc_recv_nointr(listener_proxy_fd, &msg, sizeof(msg), 0); + if (bytes != (ssize_t)sizeof(msg)) { + SYSERROR("Failed to receive message from seccomp proxy"); + if (seccomp_notify_default_answer(fd, req, resp, hdlr)) + goto out; + } + } while (reconnect_count++); memcpy(resp, &msg.resp, sizeof(*resp)); - ret = seccomp_notif_send_resp(fd, resp); if (ret) - return minus_one_set_errno(-ret); + SYSERROR("Failed to send seccomp notification"); +out: return 0; #else return -ENOSYS; From 8a6437599caa44264d671daf660bb0d40073000d Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 25 Apr 2019 16:40:40 +0200 Subject: [PATCH 4/6] doc: add lxc.seccomp.notify.proxy Signed-off-by: Christian Brauner --- doc/lxc.container.conf.sgml.in | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in index ee78e49a3..4ed65f63a 100644 --- a/doc/lxc.container.conf.sgml.in +++ b/doc/lxc.container.conf.sgml.in @@ -1873,8 +1873,27 @@ dev/null proc/kcore none bind,relative 0 0 2 blacklist mknod errno 0 + ioctl notify + + Specifying "errno" as action will cause LXC to register a seccomp filter + that will cause a specific errno to be returned ot the caller. The errno + value can be specified after the "errno" action word. + + + + Specifying "notify" as action will cause LXC to register a seccomp + listener and retrieve a listener file descriptor from the kernel. When a + syscall is made that is registered as "notify" the kernel will generate a + poll event and send a message over the file descriptor. The caller can + read this message, inspect the syscalls including its arguments. Based on + this information the caller is expected to send back a message informing + the kernel which action to take. Until that message is sent the kernel + will block the calling process. The format of the messages to read and + sent is documented in seccomp itself. + + @@ -1900,6 +1919,20 @@ dev/null proc/kcore none bind,relative 0 0 + + + + + + + Specify a unix socket to which LXC will connect and forward + seccomp events to. The path must by in the form + unix:/path/to/socket or unix:@socket. The former specifies a + path-bound unix domain socket while the latter specifies an + abstract unix domain socket. + + + From 6cc93f0ed6f45e1affa9cada2d1a7e84eb9d91c0 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 25 Apr 2019 15:25:23 +0200 Subject: [PATCH 5/6] api_extensions: add seccomp_notify Signed-off-by: Christian Brauner --- src/lxc/api_extensions.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lxc/api_extensions.h b/src/lxc/api_extensions.h index 03d4e25fd..e87c8e924 100644 --- a/src/lxc/api_extensions.h +++ b/src/lxc/api_extensions.h @@ -43,6 +43,7 @@ static char *api_extensions[] = { "cgroup_relative", "mount_injection_file", "seccomp_allow_nesting", + "seccomp_notify", }; static size_t nr_api_extensions = sizeof(api_extensions) / sizeof(*api_extensions); From eab649dc41b64f2897d6d9f2e5ba7ff66ca0423b Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 25 Apr 2019 17:20:39 +0200 Subject: [PATCH 6/6] start: silence clang Signed-off-by: Christian Brauner --- src/lxc/start.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lxc/start.c b/src/lxc/start.c index 6d5913af6..b26d0ce7a 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -1122,8 +1122,8 @@ void lxc_abort(const char *name, struct lxc_handler *handler) static int do_start(void *data) { struct lxc_handler *handler = data; - __do_close_prot_errno int data_sock0 = handler->data_sock[0], - data_sock1 = handler->data_sock[1]; + ATTR_UNUSED __do_close_prot_errno int data_sock0 = handler->data_sock[0], + data_sock1 = handler->data_sock[1]; int ret; char path[PATH_MAX]; uid_t new_uid;