+
+#if HAVE_DECL_SECCOMP_NOTIFY_FD
+ close_prot_errno_disarm(seccomp->notifier.notify_fd);
+ close_prot_errno_disarm(seccomp->notifier.proxy_fd);
+ seccomp_notify_free(seccomp->notifier.req_buf, seccomp->notifier.rsp_buf);
+ seccomp->notifier.req_buf = NULL;
+ seccomp->notifier.rsp_buf = NULL;
+#endif
+}
+
+#if HAVE_DECL_SECCOMP_NOTIFY_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
+
+#if HAVE_DECL_SECCOMP_NOTIFY_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_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_NOTIFY_FD
+ __do_close_prot_errno int fd_mem = -EBADF;
+ int reconnect_count, ret;
+ ssize_t bytes;
+ struct iovec iov[4];
+ size_t iov_len, msg_base_size, msg_full_size;
+ 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};
+ char *cookie = conf->seccomp.notifier.cookie;
+ uint64_t req_id;
+
+ 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;
+ }
+
+ /* remember the ID in case we receive garbage from the proxy */
+ resp->id = req_id = req->id;
+
+ 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;
+ }
+
+ msg.monitor_pid = hdlr->monitor_pid;
+ msg.init_pid = hdlr->pid;
+ memcpy(&msg.sizes, &conf->seccomp.notifier.sizes, sizeof(msg.sizes));
+
+ msg_base_size = 0;
+ iov[0].iov_base = &msg;
+ msg_base_size += (iov[0].iov_len = sizeof(msg));
+ iov[1].iov_base = req;
+ msg_base_size += (iov[1].iov_len = msg.sizes.seccomp_notif);
+ iov[2].iov_base = resp;
+ msg_base_size += (iov[2].iov_len = msg.sizes.seccomp_notif_resp);
+ msg_full_size = msg_base_size;
+
+ if (cookie) {
+ size_t len = strlen(cookie);
+
+ msg.cookie_len = (uint64_t)len;
+
+ iov[3].iov_base = cookie;
+ msg_full_size += (iov[3].iov_len = len);
+
+ iov_len = 4;
+ } else {
+ iov_len = 3;
+ }
+
+ reconnect_count = 0;
+ do {
+ bytes = lxc_abstract_unix_send_fds_iov(listener_proxy_fd,
+ &fd_mem, 1, iov,
+ iov_len);
+ if (bytes != (ssize_t)msg_full_size) {
+ 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);
+
+ if (msg.__reserved != 0) {
+ ERROR("Proxy filled reserved data in response");
+ seccomp_notify_default_answer(fd, req, resp, hdlr);
+ goto out;
+ }
+
+ if (resp->id != req_id) {
+ resp->id = req_id;
+ ERROR("Proxy returned response with illegal id");
+ (void)seccomp_notify_default_answer(fd, req, resp, hdlr);
+ goto out;
+ }
+
+ reconnect_count = 0;
+ do {
+ bytes = lxc_recvmsg_nointr_iov(listener_proxy_fd, iov,iov_len,
+ 0);
+ if (bytes != (ssize_t)msg_base_size) {
+ SYSERROR("Failed to receive message from seccomp proxy");
+ if (seccomp_notify_default_answer(fd, req, resp, hdlr))
+ goto out;
+ }
+ } while (reconnect_count++);
+
+ 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
+ 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_NOTIFY_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_NOTIFY_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(SECCOMP_GET_NOTIF_SIZES, 0,
+ &seccomp->notifier.sizes);
+ if (ret) {
+ SYSERROR("Failed to query seccomp notify struct sizes");
+ 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_NOTIFY_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_NOTIFY_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_NOTIFY_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;