From aedd4e87362371d83dd64d0bfb03ea3e5526607f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 30 Oct 2005 18:21:57 +0000 Subject: * add chroot() support on Linux git-svn-id: file:///home/lennart/svn/public/avahi/trunk@907 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe --- avahi-daemon/chroot.c | 406 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 406 insertions(+) create mode 100644 avahi-daemon/chroot.c (limited to 'avahi-daemon/chroot.c') diff --git a/avahi-daemon/chroot.c b/avahi-daemon/chroot.c new file mode 100644 index 0000000..5504cbd --- /dev/null +++ b/avahi-daemon/chroot.c @@ -0,0 +1,406 @@ +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "chroot.h" +#include "caps.h" + +enum { + AVAHI_CHROOT_SUCCESS = 0, + AVAHI_CHROOT_FAILURE, + AVAHI_CHROOT_GET_RESOLV_CONF, +#ifdef HAVE_DBUS + AVAHI_CHROOT_GET_SERVER_INTROSPECT, + AVAHI_CHROOT_GET_ENTRY_GROUP_INTROSPECT, + AVAHI_CHROOT_GET_ADDRESS_RESOLVER_INTROSPECT, + AVAHI_CHROOT_GET_DOMAIN_BROWSER_INTROSPECT, + AVAHI_CHROOT_GET_HOST_NAME_RESOLVER_INTROSPECT, + AVAHI_CHROOT_GET_SERVICE_BROWSER_INTROSPECT, + AVAHI_CHROOT_GET_SERVICE_RESOLVER_INTROSPECT, + AVAHI_CHROOT_GET_SERVICE_TYPE_BROWSER_INTROSPECT, +#endif + AVAHI_CHROOT_UNLINK_PID, + AVAHI_CHROOT_UNLINK_SOCKET, + AVAHI_CHROOT_MAX +}; + +static const char* const get_file_name_table[AVAHI_CHROOT_MAX] = { + NULL, + NULL, + "/etc/resolv.conf", +#ifdef HAVE_DBUS + AVAHI_DBUS_INTROSPECTION_DIR"/Server.introspect", + AVAHI_DBUS_INTROSPECTION_DIR"/EntryGroup.introspect", + AVAHI_DBUS_INTROSPECTION_DIR"/AddressResolver.introspect", + AVAHI_DBUS_INTROSPECTION_DIR"/DomainBrowser.introspect", + AVAHI_DBUS_INTROSPECTION_DIR"/HostNameResolver.introspect", + AVAHI_DBUS_INTROSPECTION_DIR"/ServiceBrowser.introspect", + AVAHI_DBUS_INTROSPECTION_DIR"/ServiceResolver.introspect", + AVAHI_DBUS_INTROSPECTION_DIR"/ServiceTypeBrowser.introspect", +#endif + NULL, + NULL +}; + +static const char *const unlink_file_name_table[AVAHI_CHROOT_MAX] = { + NULL, + NULL, + NULL, +#ifdef HAVE_DBUS + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +#endif + AVAHI_DAEMON_RUNTIME_DIR"/pid", + AVAHI_SOCKET +}; + +static int helper_fd = -1; + +static int send_fd(int fd, int payload_fd) { + uint8_t dummy = AVAHI_CHROOT_SUCCESS; + struct iovec iov; + struct msghdr msg; + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(sizeof(int))]; + } cmsg; + + /* Send a file descriptor over the socket */ + + memset(&iov, 0, sizeof(iov)); + memset(&msg, 0, sizeof(msg)); + memset(&cmsg, 0, sizeof(cmsg)); + + iov.iov_base = &dummy; + iov.iov_len = sizeof(dummy); + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; + + msg.msg_control = &cmsg; + msg.msg_controllen = sizeof(cmsg); + msg.msg_flags = 0; + + cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(int)); + cmsg.hdr.cmsg_level = SOL_SOCKET; + cmsg.hdr.cmsg_type = SCM_RIGHTS; + *((int*) CMSG_DATA(&cmsg.hdr)) = payload_fd; + + if (sendmsg(fd, &msg, 0) < 0) { + avahi_log_error("sendmsg() failed: %s", strerror(errno)); + return -1; + } + + return 0; +} + +static int recv_fd(int fd) { + uint8_t dummy; + struct iovec iov; + struct msghdr msg; + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(sizeof(int))]; + } cmsg; + + /* Receive a file descriptor from a socket */ + + memset(&iov, 0, sizeof(iov)); + memset(&msg, 0, sizeof(msg)); + memset(&cmsg, 0, sizeof(cmsg)); + + iov.iov_base = &dummy; + iov.iov_len = sizeof(dummy); + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; + + msg.msg_control = cmsg.buf; + msg.msg_controllen = sizeof(cmsg); + msg.msg_flags = 0; + + cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(int)); + cmsg.hdr.cmsg_level = SOL_SOCKET; + cmsg.hdr.cmsg_type = SCM_RIGHTS; + *((int*) CMSG_DATA(&cmsg.hdr)) = -1; + + if (recvmsg(fd, &msg, 0) <= 0) { + avahi_log_error("recvmsg() failed: %s", strerror(errno)); + return -1; + } else { + struct cmsghdr* h; + + if (dummy != AVAHI_CHROOT_SUCCESS) { + errno = EINVAL; + return -1; + } + + if (!(h = CMSG_FIRSTHDR(&msg))) { + avahi_log_error("recvmsg() sent no fd."); + errno = EINVAL; + return -1; + } + + assert(h->cmsg_len = CMSG_LEN(sizeof(int))); + assert(h->cmsg_level = SOL_SOCKET); + assert(h->cmsg_type == SCM_RIGHTS); + + return *((int*)CMSG_DATA(h)); + } +} + +static int helper_main(int fd) { + int ret = 1; + assert(fd >= 0); + + /* This is the main function of our helper process which is forked + * off to access files outside the chroot environment. Keep in + * mind that this code is security sensitive! */ + + avahi_log_debug(__FILE__": chroot() helper started"); + + for (;;) { + uint8_t command; + ssize_t r; + + if ((r = read(fd, &command, sizeof(command))) <= 0) { + + /* EOF? */ + if (r == 0) + break; + + avahi_log_error(__FILE__": read() failed: %s", strerror(errno)); + goto fail; + } + + assert(r == sizeof(command)); + + avahi_log_debug(__FILE__": chroot() helper got command %02x", command); + + switch (command) { +#ifdef HAVE_DBUS + case AVAHI_CHROOT_GET_SERVER_INTROSPECT: + case AVAHI_CHROOT_GET_ENTRY_GROUP_INTROSPECT: + case AVAHI_CHROOT_GET_ADDRESS_RESOLVER_INTROSPECT: + case AVAHI_CHROOT_GET_DOMAIN_BROWSER_INTROSPECT: + case AVAHI_CHROOT_GET_HOST_NAME_RESOLVER_INTROSPECT: + case AVAHI_CHROOT_GET_SERVICE_BROWSER_INTROSPECT: + case AVAHI_CHROOT_GET_SERVICE_RESOLVER_INTROSPECT: + case AVAHI_CHROOT_GET_SERVICE_TYPE_BROWSER_INTROSPECT: +#endif + case AVAHI_CHROOT_GET_RESOLV_CONF: { + int payload; + + if ((payload = open(get_file_name_table[(int) command], O_RDONLY)) < 0) { + uint8_t c = AVAHI_CHROOT_FAILURE; + + avahi_log_error(__FILE__": open() failed: %s", strerror(errno)); + + if (write(fd, &c, sizeof(c)) != sizeof(c)) { + avahi_log_error(__FILE__": write() failed: %s\n", strerror(errno)); + goto fail; + } + + break; + } + + if (send_fd(fd, payload) < 0) + goto fail; + + close(payload); + + break; + } + + case AVAHI_CHROOT_UNLINK_SOCKET: + case AVAHI_CHROOT_UNLINK_PID: { + uint8_t c = AVAHI_CHROOT_SUCCESS; + + unlink(unlink_file_name_table[(int) command]); + + if (write(fd, &c, sizeof(c)) != sizeof(c)) { + avahi_log_error(__FILE__": write() failed: %s\n", strerror(errno)); + goto fail; + } + + break; + } + + default: + avahi_log_error(__FILE__": Unknown command %02x.", command); + break; + } + } + + ret = 0; + +fail: + + avahi_log_debug(__FILE__": chroot() helper exiting with return value %i", ret); + + return ret; +} + +int avahi_chroot_helper_start(void) { + int sock[2]; + pid_t pid; + + assert(helper_fd < 0); + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock) < 0) { + avahi_log_error("socketpair() failed: %s", strerror(errno)); + return -1; + } + + if ((pid = daemon_fork()) < 0) { + close(sock[0]); + close(sock[1]); + avahi_log_error(__FILE__": Failed to fork()"); + return -1; + } else if (pid == 0) { + + /* Drop all remaining capabilities */ + avahi_caps_drop_all(); + + close(sock[0]); + helper_main(sock[1]); + _exit(0); + } + + close(sock[1]); + helper_fd = sock[0]; + + return 0; +} + +void avahi_chroot_helper_shutdown(void) { + + if (helper_fd <= 0) + return; + + close(helper_fd); + helper_fd = -1; +} + +int avahi_chroot_helper_get_fd(const char *fname) { + + if (helper_fd >= 0) { + uint8_t command; + + for (command = 2; command < AVAHI_CHROOT_MAX; command++) + if (get_file_name_table[(int) command] && + strcmp(fname, get_file_name_table[(int) command]) == 0) + break; + + if (command >= AVAHI_CHROOT_MAX) { + avahi_log_error("chroot() helper accessed for invalid file name"); + errno = EACCES; + return -1; + } + + assert(get_file_name_table[(int) command]); + + if (write(helper_fd, &command, sizeof(command)) < 0) { + avahi_log_error("write() failed: %s\n", strerror(errno)); + return -1; + } + + return recv_fd(helper_fd); + + } else + return open(fname, O_RDONLY); +} + + +FILE *avahi_chroot_helper_get_file(const char *fname) { + FILE *f; + int fd; + + if ((fd = avahi_chroot_helper_get_fd(fname)) < 0) + return NULL; + + f = fdopen(fd, "r"); + assert(f); + + return f; +} + +int avahi_chroot_helper_unlink(const char *fname) { + + if (helper_fd >= 0) { + uint8_t c, command; + ssize_t r; + + for (command = 2; command < AVAHI_CHROOT_MAX; command++) + if (unlink_file_name_table[(int) command] && + strcmp(fname, unlink_file_name_table[(int) command]) == 0) + break; + + if (command >= AVAHI_CHROOT_MAX) { + avahi_log_error("chroot() helper accessed for invalid file name"); + errno = EACCES; + return -1; + } + + if (write(helper_fd, &command, sizeof(command)) < 0) { + avahi_log_error("write() failed: %s\n", strerror(errno)); + return -1; + } + + if ((r = read(helper_fd, &c, sizeof(c))) < 0) { + avahi_log_error("read() failed: %s\n", r < 0 ? strerror(errno) : "EOF"); + return -1; + } + + return 0; + + } else + + return unlink(fname); + +} -- cgit