summaryrefslogtreecommitdiffstats
path: root/avahi-daemon/chroot.c
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2005-10-30 18:21:57 +0000
committerLennart Poettering <lennart@poettering.net>2005-10-30 18:21:57 +0000
commitaedd4e87362371d83dd64d0bfb03ea3e5526607f (patch)
treecbe35acdb665b1ffeb0fa81220cb0d44549dfc4c /avahi-daemon/chroot.c
parentd8260fdf4310e135f301d0b8296aeffa02f5f494 (diff)
* add chroot() support on Linux
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@907 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
Diffstat (limited to 'avahi-daemon/chroot.c')
-rw-r--r--avahi-daemon/chroot.c406
1 files changed, 406 insertions, 0 deletions
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 <config.h>
+#endif
+
+#include <inttypes.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/un.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <avahi-core/log.h>
+#include <libdaemon/dfork.h>
+
+#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);
+
+}