Unverified Commit 26077e91 by Christian Brauner Committed by GitHub

Merge pull request #3080 from Blub/seccomp-notify-api

Seccomp notify api update
parents cfc3b342 b9dab9ef
......@@ -367,6 +367,7 @@ OLD_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $SECCOMP_CFLAGS"
AC_CHECK_TYPES([scmp_filter_ctx], [], [], [[#include <seccomp.h>]])
AC_CHECK_DECLS([seccomp_notify_fd], [], [], [[#include <seccomp.h>]])
AC_CHECK_TYPES([struct seccomp_notif_sizes], [], [], [[#include <seccomp.h>]])
AC_CHECK_DECLS([seccomp_syscall_resolve_name_arch], [], [], [[#include <seccomp.h>]])
CFLAGS="$OLD_CFLAGS"
......
......@@ -1997,11 +1997,22 @@ dev/null proc/kcore none bind,relative 0 0
<listitem>
<para>
Specify a unix socket to which LXC will connect and forward
seccomp events to. The path must by in the form
seccomp events to. The path must be 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.
</para>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>lxc.seccomp.notify.cookie</option>
</term>
<listitem>
<para>
An additional string sent along with proxied seccomp notification
requests.
</para>
</listitem>
</varlistentry>
</variablelist>
......
......@@ -153,19 +153,16 @@ int lxc_abstract_unix_connect(const char *path)
return fd;
}
int lxc_abstract_unix_send_fds(int fd, int *sendfds, int num_sendfds,
void *data, size_t size)
int lxc_abstract_unix_send_fds_iov(int fd, int *sendfds, int num_sendfds,
struct iovec *iov, size_t iovlen)
{
__do_free char *cmsgbuf = NULL;
int ret;
struct msghdr msg;
struct iovec iov;
struct cmsghdr *cmsg = NULL;
char buf[1] = {0};
size_t cmsgbufsize = CMSG_SPACE(num_sendfds * sizeof(int));
memset(&msg, 0, sizeof(msg));
memset(&iov, 0, sizeof(iov));
cmsgbuf = malloc(cmsgbufsize);
if (!cmsgbuf) {
......@@ -185,10 +182,8 @@ int lxc_abstract_unix_send_fds(int fd, int *sendfds, int num_sendfds,
memcpy(CMSG_DATA(cmsg), sendfds, num_sendfds * sizeof(int));
iov.iov_base = data ? data : buf;
iov.iov_len = data ? size : sizeof(buf);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_iov = iov;
msg.msg_iovlen = iovlen;
again:
ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
......@@ -199,26 +194,35 @@ again:
return ret;
}
int lxc_abstract_unix_send_fds(int fd, int *sendfds, int num_sendfds,
void *data, size_t size)
{
char buf[1] = {0};
struct iovec iov = {
.iov_base = data ? data : buf,
.iov_len = data ? size : sizeof(buf),
};
return lxc_abstract_unix_send_fds_iov(fd, sendfds, num_sendfds, &iov,
1);
}
int lxc_unix_send_fds(int fd, int *sendfds, int num_sendfds, void *data,
size_t size)
{
return lxc_abstract_unix_send_fds(fd, sendfds, num_sendfds, data, size);
}
int lxc_abstract_unix_recv_fds(int fd, int *recvfds, int num_recvfds,
void *data, size_t size)
static int lxc_abstract_unix_recv_fds_iov(int fd, int *recvfds, int num_recvfds,
struct iovec *iov, size_t iovlen)
{
__do_free char *cmsgbuf = NULL;
int ret;
struct msghdr msg;
struct iovec iov;
struct cmsghdr *cmsg = NULL;
char buf[1] = {0};
size_t cmsgbufsize = CMSG_SPACE(sizeof(struct ucred)) +
CMSG_SPACE(num_recvfds * sizeof(int));
memset(&msg, 0, sizeof(msg));
memset(&iov, 0, sizeof(iov));
cmsgbuf = malloc(cmsgbufsize);
if (!cmsgbuf) {
......@@ -229,10 +233,8 @@ int lxc_abstract_unix_recv_fds(int fd, int *recvfds, int num_recvfds,
msg.msg_control = cmsgbuf;
msg.msg_controllen = cmsgbufsize;
iov.iov_base = data ? data : buf;
iov.iov_len = data ? size : sizeof(buf);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_iov = iov;
msg.msg_iovlen = iovlen;
again:
ret = recvmsg(fd, &msg, 0);
......@@ -264,6 +266,17 @@ out:
return ret;
}
int lxc_abstract_unix_recv_fds(int fd, int *recvfds, int num_recvfds,
void *data, size_t size)
{
char buf[1] = {0};
struct iovec iov = {
.iov_base = data ? data : buf,
.iov_len = data ? size : sizeof(buf),
};
return lxc_abstract_unix_recv_fds_iov(fd, recvfds, num_recvfds, &iov, 1);
}
int lxc_abstract_unix_send_credential(int fd, void *data, size_t size)
{
struct msghdr msg = {0};
......@@ -365,13 +378,13 @@ int lxc_unix_sockaddr(struct sockaddr_un *ret, const char *path)
return (int)(offsetof(struct sockaddr_un, sun_path) + len + 1);
}
int lxc_unix_connect(struct sockaddr_un *addr)
int lxc_unix_connect_type(struct sockaddr_un *addr, int type)
{
__do_close_prot_errno int fd = -EBADF;
int ret;
ssize_t len;
fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
fd = socket(AF_UNIX, type | SOCK_CLOEXEC, 0);
if (fd < 0) {
SYSERROR("Failed to open new AF_UNIX socket");
return -1;
......@@ -392,6 +405,11 @@ int lxc_unix_connect(struct sockaddr_un *addr)
return move_fd(fd);
}
int lxc_unix_connect(struct sockaddr_un *addr, int type)
{
return lxc_unix_connect_type(addr, SOCK_STREAM);
}
int lxc_socket_set_timeout(int fd, int rcv_timeout, int snd_timeout)
{
struct timeval out = {0};
......
......@@ -35,6 +35,9 @@ extern void lxc_abstract_unix_close(int fd);
extern int lxc_abstract_unix_connect(const char *path);
extern int lxc_abstract_unix_send_fds(int fd, int *sendfds, int num_sendfds,
void *data, size_t size);
extern int lxc_abstract_unix_send_fds_iov(int fd, int *sendfds,
int num_sendfds, struct iovec *iov,
size_t iovlen);
extern int lxc_unix_send_fds(int fd, int *sendfds, int num_sendfds, void *data,
size_t size);
extern int lxc_abstract_unix_recv_fds(int fd, int *recvfds, int num_recvfds,
......@@ -43,6 +46,7 @@ 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_unix_connect_type(struct sockaddr_un *addr, int type);
extern int lxc_socket_set_timeout(int fd, int rcv_timeout, int snd_timeout);
#endif /* __LXC_AF_UNIX_H */
......@@ -153,6 +153,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_cookie);
lxc_config_define(seccomp_notify_proxy);
lxc_config_define(selinux_context);
lxc_config_define(signal_halt);
......@@ -246,6 +247,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.cookie", set_config_seccomp_notify_cookie, get_config_seccomp_notify_cookie, clr_config_seccomp_notify_cookie, },
{ "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, },
......@@ -1013,6 +1015,16 @@ static int set_config_seccomp_allow_nesting(const char *key, const char *value,
#endif
}
static int set_config_seccomp_notify_cookie(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
{
#ifdef HAVE_SECCOMP_NOTIFY
return set_config_string_item(&lxc_conf->seccomp.notifier.cookie, value);
#else
return minus_one_set_errno(ENOSYS);
#endif
}
static int set_config_seccomp_notify_proxy(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
{
......@@ -3955,6 +3967,16 @@ static int get_config_seccomp_allow_nesting(const char *key, char *retv,
#endif
}
static int get_config_seccomp_notify_cookie(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data)
{
#ifdef HAVE_SECCOMP_NOTIFY
return lxc_get_conf_str(retv, inlen, c->seccomp.notifier.cookie);
#else
return minus_one_set_errno(ENOSYS);
#endif
}
static int get_config_seccomp_notify_proxy(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data)
{
......@@ -4563,6 +4585,18 @@ static inline int clr_config_seccomp_allow_nesting(const char *key,
#endif
}
static inline int clr_config_seccomp_notify_cookie(const char *key,
struct lxc_conf *c, void *data)
{
#ifdef HAVE_SECCOMP_NOTIFY
free(c->seccomp.notifier.cookie);
c->seccomp.notifier.cookie = NULL;
return 0;
#else
return minus_one_set_errno(ENOSYS);
#endif
}
static inline int clr_config_seccomp_notify_proxy(const char *key,
struct lxc_conf *c, void *data)
{
......
......@@ -142,6 +142,24 @@ again:
return ret;
}
ssize_t lxc_recvmsg_nointr_iov(int sockfd, struct iovec *iov, size_t iovlen,
int flags)
{
ssize_t ret;
struct msghdr msg;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = iov;
msg.msg_iovlen = iovlen;
again:
ret = recvmsg(sockfd, &msg, flags);
if (ret < 0 && errno == EINTR)
goto again;
return ret;
}
ssize_t lxc_read_nointr_expect(int fd, void *buf, size_t count, const void *expected_buf)
{
ssize_t ret;
......
......@@ -26,6 +26,7 @@
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/vfs.h>
#include <unistd.h>
......@@ -43,6 +44,8 @@ extern ssize_t lxc_read_nointr_expect(int fd, void *buf, size_t count,
extern ssize_t lxc_read_file_expect(const char *path, void *buf, size_t count,
const void *expected_buf);
extern ssize_t lxc_recv_nointr(int sockfd, void *buf, size_t len, int flags);
ssize_t lxc_recvmsg_nointr_iov(int sockfd, struct iovec *iov, size_t iovlen,
int flags);
extern bool file_exists(const char *f);
extern int print_to_file(const char *file, const char *content);
......
......@@ -54,12 +54,21 @@ struct lxc_handler;
#if HAVE_DECL_SECCOMP_NOTIFY_FD
#if !HAVE_STRUCT_SECCOMP_NOTIF_SIZES
struct seccomp_notif_sizes {
__u16 seccomp_notif;
__u16 seccomp_notif_resp;
__u16 seccomp_data;
};
#endif
struct seccomp_notify_proxy_msg {
uint32_t version;
struct seccomp_notif req;
struct seccomp_notif_resp resp;
uint64_t __reserved;
pid_t monitor_pid;
pid_t init_pid;
struct seccomp_notif_sizes sizes;
uint64_t cookie_len;
/* followed by: seccomp_notif, seccomp_notif_resp, cookie */
};
struct seccomp_notify {
......@@ -67,8 +76,10 @@ struct seccomp_notify {
int notify_fd;
int proxy_fd;
struct sockaddr_un proxy_addr;
struct seccomp_notif_sizes sizes;
struct seccomp_notif *req_buf;
struct seccomp_notif_resp *rsp_buf;
char *cookie;
};
#define HAVE_SECCOMP_NOTIFY 1
......
......@@ -49,8 +49,25 @@
#define MIPS_ARCH_N64 lxc_seccomp_arch_mips64
#endif
#ifndef SECCOMP_GET_NOTIF_SIZES
#define SECCOMP_GET_NOTIF_SIZES 3
#endif
lxc_log_define(seccomp, lxc);
#if HAVE_DECL_SECCOMP_NOTIFY_FD
static inline int __seccomp(unsigned int operation, unsigned int flags,
void *args)
{
#ifdef __NR_seccomp
return syscall(__NR_seccomp, operation, flags, args);
#else
errno = ENOSYS;
return -1;
#endif
}
#endif
static int parse_config_v1(FILE *f, char *line, size_t *line_bufsz, struct lxc_conf *conf)
{
int ret = 0;
......@@ -1294,7 +1311,8 @@ static int seccomp_notify_reconnect(struct lxc_handler *handler)
close_prot_errno_disarm(handler->conf->seccomp.notifier.proxy_fd);
notify_fd = lxc_unix_connect(&handler->conf->seccomp.notifier.proxy_addr);
notify_fd = lxc_unix_connect_type(
&handler->conf->seccomp.notifier.proxy_addr, SOCK_SEQPACKET);
if (notify_fd < 0) {
SYSERROR("Failed to reconnect to seccomp proxy");
return -1;
......@@ -1311,17 +1329,15 @@ static int seccomp_notify_reconnect(struct lxc_handler *handler)
#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)
static void 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
......@@ -1330,24 +1346,26 @@ int seccomp_notify_handler(int fd, uint32_t events, void *data,
{
#if HAVE_DECL_SECCOMP_NOTIFY_FD
__do_close_prot_errno int fd_pid = -EBADF;
__do_close_prot_errno int fd_mem = -EBADF;
int reconnect_count, ret;
int ret;
ssize_t bytes;
int send_fd_list[2];
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 */];
bool reconnected = false;
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);
}
char *cookie = conf->seccomp.notifier.cookie;
uint64_t req_id;
ret = seccomp_notify_receive(fd, req);
if (ret) {
......@@ -1355,10 +1373,36 @@ int seccomp_notify_handler(int fd, uint32_t events, void *data,
goto out;
}
if (listener_proxy_fd < 0) {
ret = -1;
/* Same condition as for the initial setup_proxy() */
if (conf->seccomp.notifier.wants_supervision &&
conf->seccomp.notifier.proxy_addr.sun_path[1] != '\0') {
ret = seccomp_notify_reconnect(hdlr);
}
if (ret) {
ERROR("No seccomp proxy registered");
seccomp_notify_default_answer(fd, req, resp, hdlr);
goto out;
}
listener_proxy_fd = conf->seccomp.notifier.proxy_fd;
}
/* 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", req->pid);
fd_pid = open(mem_path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
if (fd_pid < 0) {
seccomp_notify_default_answer(fd, req, resp, hdlr);
SYSERROR("Failed to open process pidfd for seccomp notify request");
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);
seccomp_notify_default_answer(fd, req, resp, hdlr);
SYSERROR("Failed to open process memory for seccomp notify request");
goto out;
}
......@@ -1369,39 +1413,80 @@ int seccomp_notify_handler(int fd, uint32_t events, void *data,
*/
ret = seccomp_notify_id_valid(fd, req->id);
if (ret < 0) {
(void)seccomp_notify_default_answer(fd, req, resp, hdlr);
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;
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;
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;
iov[3].iov_base = cookie;
msg_full_size += (iov[3].iov_len = len);
iov_len = 4;
} else {
iov_len = 3;
}
send_fd_list[0] = fd_pid;
send_fd_list[1] = fd_mem;
retry:
bytes = lxc_abstract_unix_send_fds_iov(listener_proxy_fd, send_fd_list,
2, iov, iov_len);
if (bytes != (ssize_t)msg_full_size) {
SYSERROR("Failed to forward message to seccomp proxy");
if (!reconnected) {
ret = seccomp_notify_reconnect(hdlr);
if (ret == 0) {
reconnected = true;
goto retry;
}
}
} while (reconnect_count++);
seccomp_notify_default_answer(fd, req, resp, hdlr);
goto out;
}
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++);
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");
seccomp_notify_default_answer(fd, req, resp, hdlr);
goto out;
}
bytes = lxc_recvmsg_nointr_iov(listener_proxy_fd, iov,iov_len,
MSG_TRUNC);
if (bytes != (ssize_t)msg_base_size) {
SYSERROR("Failed to receive message from seccomp proxy");
seccomp_notify_default_answer(fd, req, resp, hdlr);
goto out;
}
memcpy(resp, &msg.resp, sizeof(*resp));
ret = seccomp_notify_respond(fd, resp);
if (ret)
SYSERROR("Failed to send seccomp notification");
......@@ -1441,7 +1526,8 @@ int lxc_seccomp_setup_proxy(struct lxc_seccomp *seccomp,
__do_close_prot_errno int notify_fd = -EBADF;
int ret;
notify_fd = lxc_unix_connect(&seccomp->notifier.proxy_addr);
notify_fd = lxc_unix_connect_type(&seccomp->notifier.proxy_addr,
SOCK_SEQPACKET);
if (notify_fd < 0) {
SYSERROR("Failed to connect to seccomp proxy");
return -1;
......@@ -1454,6 +1540,13 @@ int lxc_seccomp_setup_proxy(struct lxc_seccomp *seccomp,
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) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment