X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=src%2Flxc%2Faf_unix.c;h=2ff98dc1b6e6de6cdc3f2ac430219d3dcf4c27a3;hb=970ef13dd2eed4c8dd24056045a1951d8c5f7696;hp=6debb07f12eec5087da610773eaabbbbe7278a0e;hpb=0059379ff44bd52cf17d72300e60432686f2fba0;p=mirror_lxc.git diff --git a/src/lxc/af_unix.c b/src/lxc/af_unix.c index 6debb07f1..2ff98dc1b 100644 --- a/src/lxc/af_unix.c +++ b/src/lxc/af_unix.c @@ -20,8 +20,10 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "config.h" +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif #include #include #include @@ -33,119 +35,140 @@ #include #include +#include "config.h" #include "log.h" +#include "memory_utils.h" +#include "raw_syscalls.h" #include "utils.h" -lxc_log_define(lxc_af_unix, lxc); +#ifndef HAVE_STRLCPY +#include "include/strlcpy.h" +#endif + +lxc_log_define(af_unix, lxc); + +static ssize_t lxc_abstract_unix_set_sockaddr(struct sockaddr_un *addr, + const char *path) +{ + size_t len; + + if (!addr || !path) { + errno = EINVAL; + return -1; + } + + /* Clear address structure */ + memset(addr, 0, sizeof(*addr)); + + addr->sun_family = AF_UNIX; + + len = strlen(&path[1]); + + /* do not enforce \0-termination */ + if (len >= INT_MAX || len >= sizeof(addr->sun_path)) { + errno = ENAMETOOLONG; + return -1; + } + + /* do not enforce \0-termination */ + memcpy(&addr->sun_path[1], &path[1], len); + return len; +} int lxc_abstract_unix_open(const char *path, int type, int flags) { int fd, ret; - size_t len; + ssize_t len; struct sockaddr_un addr; - fd = socket(PF_UNIX, type, 0); + fd = socket(PF_UNIX, type | SOCK_CLOEXEC, 0); if (fd < 0) return -1; - /* Clear address structure */ - memset(&addr, 0, sizeof(addr)); - if (!path) return fd; - addr.sun_family = AF_UNIX; - - len = strlen(&path[1]); - /* do not enforce \0-termination */ - if (len >= sizeof(addr.sun_path)) { + len = lxc_abstract_unix_set_sockaddr(&addr, path); + if (len < 0) { + int saved_errno = errno; close(fd); - errno = ENAMETOOLONG; + errno = saved_errno; return -1; } - /* addr.sun_path[0] has already been set to 0 by memset() */ - strncpy(&addr.sun_path[1], &path[1], len); ret = bind(fd, (struct sockaddr *)&addr, offsetof(struct sockaddr_un, sun_path) + len + 1); if (ret < 0) { - int tmp = errno; + int saved_errno = errno; close(fd); - errno = tmp; + errno = saved_errno; return -1; } if (type == SOCK_STREAM) { ret = listen(fd, 100); if (ret < 0) { - int tmp = errno; + int saved_errno = errno; close(fd); - errno = tmp; + errno = saved_errno; return -1; } - } return fd; } -int lxc_abstract_unix_close(int fd) +void lxc_abstract_unix_close(int fd) { close(fd); - return 0; } int lxc_abstract_unix_connect(const char *path) { int fd, ret; - size_t len; + ssize_t len; struct sockaddr_un addr; - fd = socket(PF_UNIX, SOCK_STREAM, 0); + fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); if (fd < 0) return -1; - memset(&addr, 0, sizeof(addr)); - - addr.sun_family = AF_UNIX; - - len = strlen(&path[1]); - /* do not enforce \0-termination */ - if (len >= sizeof(addr.sun_path)) { + len = lxc_abstract_unix_set_sockaddr(&addr, path); + if (len < 0) { + int saved_errno = errno; close(fd); - errno = ENAMETOOLONG; + errno = saved_errno; return -1; } - /* addr.sun_path[0] has already been set to 0 by memset() */ - strncpy(&addr.sun_path[1], &path[1], strlen(&path[1])); ret = connect(fd, (struct sockaddr *)&addr, offsetof(struct sockaddr_un, sun_path) + len + 1); if (ret < 0) { + int saved_errno = errno; close(fd); + errno = saved_errno; return -1; } 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}; - char *cmsgbuf; size_t cmsgbufsize = CMSG_SPACE(num_sendfds * sizeof(int)); memset(&msg, 0, sizeof(msg)); - memset(&iov, 0, sizeof(iov)); cmsgbuf = malloc(cmsgbufsize); - if (!cmsgbuf) + if (!cmsgbuf) { + errno = ENOMEM; return -1; + } msg.msg_control = cmsgbuf; msg.msg_controllen = cmsgbufsize; @@ -159,59 +182,102 @@ 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); - free(cmsgbuf); + if (ret < 0) + if (errno == EINTR) + goto again; + return ret; } -int lxc_abstract_unix_recv_fds(int fd, int *recvfds, int num_recvfds, +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); +} + +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}; - char *cmsgbuf; - 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)); cmsgbuf = malloc(cmsgbufsize); - if (!cmsgbuf) + if (!cmsgbuf) { + errno = ENOMEM; return -1; + } 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); - if (ret <= 0) - goto out; + if (ret < 0) { + if (errno == EINTR) + goto again; - cmsg = CMSG_FIRSTHDR(&msg); + goto out; + } + if (ret == 0) + goto out; - 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)); + /* + * 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) + memcpy(recvfds, CMSG_DATA(cmsg), num_recvfds * sizeof(int)); + break; } out: - free(cmsgbuf); 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}; @@ -275,10 +341,92 @@ int lxc_abstract_unix_rcv_credential(int fd, void *data, size_t size) memcpy(&cred, CMSG_DATA(cmsg), sizeof(cred)); if (cred.uid && (cred.uid != getuid() || cred.gid != getgid())) { - INFO("message denied for '%d/%d'", cred.uid, cred.gid); - return -EACCES; + INFO("Message denied for '%d/%d'", cred.uid, cred.gid); + errno = EACCES; + return -1; } } + 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_type(struct sockaddr_un *addr, int type) +{ + __do_close_prot_errno int fd = -EBADF; + int ret; + ssize_t len; + + fd = socket(AF_UNIX, type | SOCK_CLOEXEC, 0); + if (fd < 0) { + SYSERROR("Failed to open new AF_UNIX socket"); + 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); + if (ret < 0) { + SYSERROR("Failed to bind new AF_UNIX socket"); + return -1; + } + + 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}; + 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; +}