#include <sys/mount.h>
#include <sys/utsname.h>
+#include "af_unix.h"
+#include "commands.h"
#include "config.h"
#include "log.h"
+#include "lxccontainer.h"
#include "lxcseccomp.h"
+#include "mainloop.h"
+#include "memory_utils.h"
#include "utils.h"
#ifdef __MIPSEL__
}
#if HAVE_SCMP_FILTER_CTX
- ret = seccomp_rule_add(conf->seccomp_ctx, SCMP_ACT_ALLOW, nr, 0);
+ ret = seccomp_rule_add(conf->seccomp.seccomp_ctx, SCMP_ACT_ALLOW, nr, 0);
#else
ret = seccomp_rule_add(SCMP_ACT_ALLOW, nr, 0);
#endif
return "trap";
case SCMP_ACT_ERRNO(0):
return "errno";
+#if HAVE_DECL_SECCOMP_NOTIF_GET_FD
+ case SCMP_ACT_NOTIFY:
+ return "notify";
+#endif
}
return "invalid action";
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_NOTIFY;
+#endif
} else if (line[0]) {
ERROR("Unrecognized seccomp action \"%s\"", line);
return -2;
}
if (default_policy_action != SCMP_ACT_KILL) {
- ret = seccomp_reset(conf->seccomp_ctx, default_policy_action);
+ ret = seccomp_reset(conf->seccomp.seccomp_ctx, default_policy_action);
if (ret != 0) {
ERROR("Error re-initializing Seccomp");
return -1;
}
- ret = seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_CTL_NNP, 0);
+ ret = seccomp_attr_set(conf->seccomp.seccomp_ctx, SCMP_FLTATR_CTL_NNP, 0);
if (ret < 0) {
errno = -ret;
SYSERROR("Failed to turn off no-new-privs");
}
#ifdef SCMP_FLTATR_ATL_TSKIP
- ret = seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_ATL_TSKIP, 1);
+ ret = seccomp_attr_set(conf->seccomp.seccomp_ctx, SCMP_FLTATR_ATL_TSKIP, 1);
if (ret < 0) {
errno = -ret;
SYSWARN("Failed to turn on seccomp nop-skip, continuing");
goto bad_rule;
}
+#if HAVE_DECL_SECCOMP_NOTIF_GET_FD
+ if ((rule.action == SCMP_ACT_NOTIFY) &&
+ !conf->seccomp.notifier.wants_supervision) {
+ ret = seccomp_attr_set(conf->seccomp.seccomp_ctx,
+ SCMP_FLTATR_NEW_LISTENER, 1);
+ if (ret)
+ goto bad_rule;
+
+ conf->seccomp.notifier.wants_supervision = true;
+ TRACE("Set SCMP_FLTATR_NEW_LISTENER attribute");
+ }
+#endif
+
if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line,
- conf->seccomp_ctx, &rule))
+ conf->seccomp.seccomp_ctx, &rule))
goto bad_rule;
INFO("Added native rule for arch %d for %s action %d(%s)",
INFO("Merging compat seccomp contexts into main context");
if (ctx.contexts[0]) {
if (ctx.needs_merge[0]) {
- ret = seccomp_merge(conf->seccomp_ctx, ctx.contexts[0]);
+ ret = seccomp_merge(conf->seccomp.seccomp_ctx, ctx.contexts[0]);
if (ret < 0) {
ERROR("Failed to merge first compat seccomp "
"context into main context");
if (ctx.contexts[1]) {
if (ctx.needs_merge[1]) {
- ret = seccomp_merge(conf->seccomp_ctx, ctx.contexts[1]);
+ ret = seccomp_merge(conf->seccomp.seccomp_ctx, ctx.contexts[1]);
if (ret < 0) {
ERROR("Failed to merge first compat seccomp "
"context into main context");
if (ctx.contexts[2]) {
if (ctx.needs_merge[2]) {
- ret = seccomp_merge(conf->seccomp_ctx, ctx.contexts[2]);
+ ret = seccomp_merge(conf->seccomp.seccomp_ctx, ctx.contexts[2]);
if (ret < 0) {
ERROR("Failed to merge third compat seccomp "
"context into main context");
char *line = NULL;
bool already_enabled = false, found = false;
- if (conf->seccomp_allow_nesting > 0)
+ if (conf->seccomp.allow_nesting > 0)
return true;
f = fopen("/proc/self/status", "r");
int ret;
FILE *f;
- if (!conf->seccomp)
+ if (!conf->seccomp.seccomp)
return 0;
if (!use_seccomp(conf))
#if HAVE_SCMP_FILTER_CTX
/* XXX for debug, pass in SCMP_ACT_TRAP */
- conf->seccomp_ctx = seccomp_init(SCMP_ACT_KILL);
- ret = !conf->seccomp_ctx;
+ conf->seccomp.seccomp_ctx = seccomp_init(SCMP_ACT_KILL);
+ ret = !conf->seccomp.seccomp_ctx;
#else
ret = seccomp_init(SCMP_ACT_KILL) < 0;
#endif
/* turn off no-new-privs. We don't want it in lxc, and it breaks
* with apparmor */
#if HAVE_SCMP_FILTER_CTX
- ret = seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_CTL_NNP, 0);
+ ret = seccomp_attr_set(conf->seccomp.seccomp_ctx, SCMP_FLTATR_CTL_NNP, 0);
#else
ret = seccomp_attr_set(SCMP_FLTATR_CTL_NNP, 0);
#endif
}
#ifdef SCMP_FLTATR_ATL_TSKIP
- ret = seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_ATL_TSKIP, 1);
+ ret = seccomp_attr_set(conf->seccomp.seccomp_ctx, SCMP_FLTATR_ATL_TSKIP, 1);
if (ret < 0) {
errno = -ret;
SYSWARN("Failed to turn on seccomp nop-skip, continuing");
}
#endif
- f = fopen(conf->seccomp, "r");
+ f = fopen(conf->seccomp.seccomp, "r");
if (!f) {
- SYSERROR("Failed to open seccomp policy file %s", conf->seccomp);
+ SYSERROR("Failed to open seccomp policy file %s", conf->seccomp.seccomp);
return -1;
}
{
int ret;
- if (!conf->seccomp)
+ if (!conf->seccomp.seccomp)
return 0;
if (!use_seccomp(conf))
return 0;
#if HAVE_SCMP_FILTER_CTX
- ret = seccomp_load(conf->seccomp_ctx);
+ ret = seccomp_load(conf->seccomp.seccomp_ctx);
#else
ret = seccomp_load();
#endif
if ((lxc_log_get_level() <= LXC_LOG_LEVEL_TRACE ||
conf->loglevel <= LXC_LOG_LEVEL_TRACE) &&
lxc_log_fd >= 0) {
- ret = seccomp_export_pfc(conf->seccomp_ctx, lxc_log_fd);
+ ret = seccomp_export_pfc(conf->seccomp.seccomp_ctx, lxc_log_fd);
/* Just give an warning when export error */
if (ret < 0) {
errno = -ret;
}
#endif
+#if HAVE_DECL_SECCOMP_NOTIF_GET_FD
+ if (conf->seccomp.notifier.wants_supervision) {
+ ret = seccomp_notify_fd(conf->seccomp.seccomp_ctx);
+ if (ret < 0) {
+ errno = -ret;
+ return -1;
+ }
+
+ conf->seccomp.notifier.notify_fd = ret;
+ TRACE("Retrieved new seccomp listener fd %d", ret);
+ }
+#endif
+
+ return 0;
+}
+
+void lxc_seccomp_free(struct lxc_seccomp *seccomp)
+{
+ free_disarm(seccomp->seccomp);
+
+#if HAVE_SCMP_FILTER_CTX
+ if (seccomp->seccomp_ctx) {
+ seccomp_release(seccomp->seccomp_ctx);
+ seccomp->seccomp_ctx = NULL;
+ }
+#endif
+
+#if HAVE_DECL_SECCOMP_NOTIF_GET_FD
+ close_prot_errno_disarm(seccomp->notifier.notify_fd);
+ close_prot_errno_disarm(seccomp->notifier.proxy_fd);
+ seccomp_notif_free(seccomp->notifier.req_buf, seccomp->notifier.rsp_buf);
+ seccomp->notifier.req_buf = NULL;
+ seccomp->notifier.rsp_buf = NULL;
+#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.notifier.proxy_fd);
+
+ notify_fd = lxc_unix_connect(&handler->conf->seccomp.notifier.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.notifier.proxy_fd = move_fd(notify_fd);
return 0;
}
+#endif
-void lxc_seccomp_free(struct lxc_conf *conf)
+#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)
{
- free(conf->seccomp);
- conf->seccomp = NULL;
+ resp->id = req->id;
+ resp->error = -ENOSYS;
+
+ if (seccomp_notify_respond(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
+ __do_close_prot_errno int fd_mem = -EBADF;
+ int reconnect_count, ret;
+ ssize_t bytes;
+ char mem_path[6 /* /proc/ */
+ + INTTYPE_TO_STRLEN(int64_t)
+ + 3 /* mem */
+ + 1 /* \0 */];
+ struct lxc_handler *hdlr = data;
+ struct lxc_conf *conf = hdlr->conf;
+ struct seccomp_notif *req = conf->seccomp.notifier.req_buf;
+ struct seccomp_notif_resp *resp = conf->seccomp.notifier.rsp_buf;
+ int listener_proxy_fd = conf->seccomp.notifier.proxy_fd;
+ struct seccomp_notify_proxy_msg msg = {0};
+
+ if (listener_proxy_fd < 0) {
+ ERROR("No seccomp proxy registered");
+ return minus_one_set_errno(EINVAL);
+ }
+
+ ret = seccomp_notify_receive(fd, req);
+ if (ret) {
+ SYSERROR("Failed to read seccomp notification");
+ goto out;
+ }
+
+ snprintf(mem_path, sizeof(mem_path), "/proc/%d/mem", req->pid);
+ fd_mem = open(mem_path, O_RDONLY | O_CLOEXEC);
+ if (fd_mem < 0) {
+ (void)seccomp_notify_default_answer(fd, req, resp, hdlr);
+ SYSERROR("Failed to open process memory for seccomp notify request");
+ goto out;
+ }
+
+ /*
+ * Make sure that the fd for /proc/<pid>/mem we just opened still
+ * refers to the correct process's memory.
+ */
+ ret = seccomp_notify_id_valid(fd, req->id);
+ if (ret < 0) {
+ (void)seccomp_notify_default_answer(fd, req, resp, hdlr);
+ SYSERROR("Invalid seccomp notify request id");
+ goto out;
+ }
+
+ memcpy(&msg.req, req, sizeof(msg.req));
+ msg.monitor_pid = hdlr->monitor_pid;
+ msg.init_pid = hdlr->pid;
+
+ reconnect_count = 0;
+ do {
+ bytes = lxc_unix_send_fds(listener_proxy_fd, &fd_mem, 1, &msg,
+ sizeof(msg));
+ 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++);
+ close_prot_errno_disarm(fd_mem);
+
+ 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_notify_respond(fd, resp);
+ if (ret)
+ SYSERROR("Failed to send seccomp notification");
+
+out:
+ return 0;
+#else
+ return -ENOSYS;
+#endif
+}
+
+void seccomp_conf_init(struct lxc_conf *conf)
+{
+ conf->seccomp.seccomp = NULL;
#if HAVE_SCMP_FILTER_CTX
- if (conf->seccomp_ctx) {
- seccomp_release(conf->seccomp_ctx);
- conf->seccomp_ctx = NULL;
+ conf->seccomp.allow_nesting = 0;
+ memset(&conf->seccomp.seccomp_ctx, 0, sizeof(conf->seccomp.seccomp_ctx));
+#endif /* HAVE_SCMP_FILTER_CTX */
+#if HAVE_DECL_SECCOMP_NOTIF_GET_FD
+ conf->seccomp.notifier.wants_supervision = false;
+ conf->seccomp.notifier.notify_fd = -EBADF;
+ conf->seccomp.notifier.proxy_fd = -EBADF;
+ memset(&conf->seccomp.notifier.proxy_addr, 0,
+ sizeof(conf->seccomp.notifier.proxy_addr));
+ conf->seccomp.notifier.req_buf = NULL;
+ conf->seccomp.notifier.rsp_buf = NULL;
+#endif
+}
+
+int lxc_seccomp_setup_proxy(struct lxc_seccomp *seccomp,
+ struct lxc_epoll_descr *descr,
+ struct lxc_handler *handler)
+{
+#if HAVE_DECL_SECCOMP_NOTIF_GET_FD
+ if (seccomp->notifier.wants_supervision &&
+ seccomp->notifier.proxy_addr.sun_path[1] != '\0') {
+ __do_close_prot_errno int notify_fd = -EBADF;
+ int ret;
+
+ notify_fd = lxc_unix_connect(&seccomp->notifier.proxy_addr);
+ if (notify_fd < 0) {
+ SYSERROR("Failed to connect to seccomp proxy");
+ return -1;
+ }
+
+ /* 30 second timeout */
+ ret = lxc_socket_set_timeout(notify_fd, 30, 30);
+ if (ret) {
+ SYSERROR("Failed to set timeouts for seccomp proxy");
+ return -1;
+ }
+
+ ret = seccomp_notify_alloc(&seccomp->notifier.req_buf,
+ &seccomp->notifier.rsp_buf);
+ if (ret) {
+ ERROR("Failed to allocate seccomp notify request and response buffers");
+ errno = ret;
+ return -1;
+ }
+
+ ret = lxc_mainloop_add_handler(descr,
+ seccomp->notifier.notify_fd,
+ seccomp_notify_handler, handler);
+ if (ret < 0) {
+ ERROR("Failed to add seccomp notify handler for %d to mainloop",
+ notify_fd);
+ return -1;
+ }
+
+ seccomp->notifier.proxy_fd = move_fd(notify_fd);
}
#endif
+ return 0;
+}
+
+int lxc_seccomp_send_notifier_fd(struct lxc_seccomp *seccomp, int socket_fd)
+{
+#if HAVE_DECL_SECCOMP_NOTIF_GET_FD
+ if (seccomp->notifier.wants_supervision) {
+ if (lxc_abstract_unix_send_fds(socket_fd,
+ &seccomp->notifier.notify_fd, 1,
+ NULL, 0) < 0)
+ return -1;
+ close_prot_errno_disarm(seccomp->notifier.notify_fd);
+ }
+#endif
+ return 0;
+}
+
+int lxc_seccomp_recv_notifier_fd(struct lxc_seccomp *seccomp, int socket_fd)
+{
+#if HAVE_DECL_SECCOMP_NOTIF_GET_FD
+ if (seccomp->notifier.wants_supervision) {
+ int ret;
+
+ ret = lxc_abstract_unix_recv_fds(socket_fd,
+ &seccomp->notifier.notify_fd,
+ 1, NULL, 0);
+ if (ret < 0)
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+int lxc_seccomp_add_notifier(const char *name, const char *lxcpath,
+ struct lxc_seccomp *seccomp)
+{
+
+#if HAVE_DECL_SECCOMP_NOTIF_GET_FD
+ if (seccomp->notifier.wants_supervision) {
+ int ret;
+
+ ret = lxc_cmd_seccomp_notify_add_listener(name, lxcpath,
+ seccomp->notifier.notify_fd,
+ -1, 0);
+ close_prot_errno_disarm(seccomp->notifier.notify_fd);
+ if (ret < 0)
+ return -1;
+ }
+#endif
+ return 0;
}