summaryrefslogtreecommitdiffstats
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
parentd8260fdf4310e135f301d0b8296aeffa02f5f494 (diff)
* add chroot() support on Linux
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@907 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
-rw-r--r--avahi-daemon/Makefile.am14
-rw-r--r--avahi-daemon/avahi-daemon.conf2
-rw-r--r--avahi-daemon/caps.c116
-rw-r--r--avahi-daemon/caps.h29
-rw-r--r--avahi-daemon/chroot.c406
-rw-r--r--avahi-daemon/chroot.h36
-rw-r--r--avahi-daemon/dbus-util.c23
-rw-r--r--avahi-daemon/main.c137
-rw-r--r--avahi-daemon/main.h1
-rw-r--r--avahi-daemon/simple-protocol.c8
-rw-r--r--avahi-daemon/static-services.c4
-rw-r--r--avahi-daemon/static-services.h2
-rw-r--r--configure.ac12
13 files changed, 757 insertions, 33 deletions
diff --git a/avahi-daemon/Makefile.am b/avahi-daemon/Makefile.am
index 7081752..0dadede 100644
--- a/avahi-daemon/Makefile.am
+++ b/avahi-daemon/Makefile.am
@@ -35,7 +35,8 @@ AM_CFLAGS+= \
-DAVAHI_SOCKET=\"$(avahi_socket)\" \
-DAVAHI_SERVICE_DIR=\"$(servicedir)\" \
-DAVAHI_CONFIG_FILE=\"$(pkgsysconfdir)/avahi-daemon.conf\" \
- -DAVAHI_DBUS_INTROSPECTION_DIR=\"$(introspectiondir)\"
+ -DAVAHI_DBUS_INTROSPECTION_DIR=\"$(introspectiondir)\" \
+ -DAVAHI_CONFIG_DIR=\"$(pkgsysconfdir)\"
sbin_PROGRAMS = \
avahi-daemon
@@ -70,6 +71,15 @@ service_DATA = \
pkgdata_DATA = \
avahi-service.dtd
+if ENABLE_CHROOT
+
+avahi_daemon_SOURCES += \
+ chroot.c chroot.h \
+ caps.c caps.h
+
+avahi_daemon_LDADD += -lcap
+
+endif
if HAVE_DBUS
@@ -86,7 +96,7 @@ avahi_daemon_SOURCES += \
dbus-service-type-browser.c \
dbus-sync-address-resolver.c \
dbus-sync-host-name-resolver.c \
- dbus-sync-service-resolver.c
+ dbus-sync-service-resolver.c
avahi_daemon_LDADD += \
../avahi-common/libdbus-common.la \
diff --git a/avahi-daemon/avahi-daemon.conf b/avahi-daemon/avahi-daemon.conf
index ed154be..b812d37 100644
--- a/avahi-daemon/avahi-daemon.conf
+++ b/avahi-daemon/avahi-daemon.conf
@@ -31,4 +31,4 @@ rlimit-data=4194304
rlimit-fsize=0
rlimit-nofile=30
rlimit-stack=4194304
-rlimit-nproc=1
+rlimit-nproc=3
diff --git a/avahi-daemon/caps.c b/avahi-daemon/caps.c
new file mode 100644
index 0000000..2d0f39a
--- /dev/null
+++ b/avahi-daemon/caps.c
@@ -0,0 +1,116 @@
+/* $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 <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+
+#include <avahi-core/log.h>
+
+#include "caps.h"
+
+int avahi_caps_reduce(void) {
+ int ret = 0;
+ cap_t caps;
+ static cap_value_t cap_values[] = { CAP_SYS_CHROOT, CAP_SETUID, CAP_SETGID };
+
+ /* Let's reduce our caps to the minimum set and tell Linux to keep
+ * them accross setuid(). This is called before we droppped
+ * priviliges. */
+
+ caps = cap_init();
+ assert(caps);
+ cap_clear(caps);
+
+ cap_set_flag(caps, CAP_EFFECTIVE, 3, cap_values, CAP_SET);
+ cap_set_flag(caps, CAP_PERMITTED, 3, cap_values, CAP_SET);
+
+ if (cap_set_proc(caps) < 0) {
+ avahi_log_error("cap_set_proc() failed: %s", strerror(errno));
+ ret = -1;
+ }
+ cap_free(caps);
+
+ /* Retain capabilities accros setuid() */
+ if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
+ avahi_log_error("prctl(PR_SET_KEEPCAPS) failed: %s", strerror(errno));
+ ret = -1;
+ }
+
+ return ret;
+}
+
+int avahi_caps_reduce2(void) {
+ int ret = 0;
+ cap_t caps;
+ static cap_value_t cap_values[] = { CAP_SYS_CHROOT };
+
+ /* Reduce our caps to the bare minimum and tell Linux not to keep
+ * them across setuid(). This is called after we dropped
+ * privilige. */
+
+ /* No longer retain caps across setuid() */
+ if (prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) < 0) {
+ avahi_log_error("prctl(PR_SET_KEEPCAPS) failed: %s", strerror(errno));
+ ret = -1;
+ }
+
+ caps = cap_init();
+ assert(caps);
+ cap_clear(caps);
+
+ /* setuid() zeroed our effective caps, let's get them back */
+ cap_set_flag(caps, CAP_EFFECTIVE, 1, cap_values, CAP_SET);
+ cap_set_flag(caps, CAP_PERMITTED, 1, cap_values, CAP_SET);
+
+ if (cap_set_proc(caps) < 0) {
+ avahi_log_error("cap_set_proc() failed: %s", strerror(errno));
+ ret = -1;
+ }
+ cap_free(caps);
+
+ return ret;
+}
+
+int avahi_caps_drop_all(void) {
+ cap_t caps;
+ int ret = 0;
+
+ /* Drop all capabilities and turn ourselves into a normal user process */
+
+ caps = cap_init();
+ assert(caps);
+ cap_clear(caps);
+
+ if (cap_set_proc(caps) < 0) {
+ avahi_log_error("cap_set_proc() failed: %s", strerror(errno));
+ ret = -1;
+ }
+ cap_free(caps);
+
+ return ret;
+}
diff --git a/avahi-daemon/caps.h b/avahi-daemon/caps.h
new file mode 100644
index 0000000..788aceb
--- /dev/null
+++ b/avahi-daemon/caps.h
@@ -0,0 +1,29 @@
+#ifndef foocapshfoo
+#define foocapshfoo
+
+/* $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.
+***/
+
+int avahi_caps_reduce(void);
+int avahi_caps_reduce2(void);
+int avahi_caps_drop_all(void);
+
+#endif
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);
+
+}
diff --git a/avahi-daemon/chroot.h b/avahi-daemon/chroot.h
new file mode 100644
index 0000000..1255739
--- /dev/null
+++ b/avahi-daemon/chroot.h
@@ -0,0 +1,36 @@
+#ifndef foochroothelperhfoo
+#define foochroothelperhfoo
+
+/* $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.
+***/
+
+#include <stdio.h>
+
+int avahi_chroot_helper_start(void);
+void avahi_chroot_helper_shutdown(void);
+int avahi_chroot_helper_get(const char *fname);
+
+int avahi_chroot_helper_get_fd(const char *fname);
+FILE *avahi_chroot_helper_get_file(const char *fname);
+
+int avahi_chroot_helper_unlink(const char *fname);
+
+#endif
diff --git a/avahi-daemon/dbus-util.c b/avahi-daemon/dbus-util.c
index 09826d3..00d8a9d 100644
--- a/avahi-daemon/dbus-util.c
+++ b/avahi-daemon/dbus-util.c
@@ -37,6 +37,10 @@
#include <avahi-core/log.h>
#include <avahi-core/core.h>
+#ifdef ENABLE_CHROOT
+#include "chroot.h"
+#endif
+
#include "main.h"
#include "dbus-util.h"
@@ -155,15 +159,21 @@ const char *avahi_dbus_map_resolve_signal_name(AvahiResolverEvent e) {
abort();
}
-static char *file_get_contents(char *fname) {
+static char *file_get_contents(const char *fname) {
int fd = -1;
struct stat st;
ssize_t size;
char *buf = NULL;
-
+
assert(fname);
+
+#ifdef ENABLE_CHROOT
+ fd = avahi_chroot_helper_get_fd(fname);
+#else
+ fd = open(fname, O_RDONLY);
+#endif
- if (!(fd = open(fname, O_RDONLY))) {
+ if (fd < 0) {
avahi_log_error("Failed to open %s: %s", fname, strerror(errno));
goto fail;
}
@@ -193,12 +203,13 @@ static char *file_get_contents(char *fname) {
buf[size] = 0;
close(fd);
+
return buf;
fail:
if (fd >= 0)
close(fd);
-
+
if (buf)
avahi_free(buf);
@@ -207,7 +218,7 @@ fail:
}
DBusHandlerResult avahi_dbus_handle_introspect(DBusConnection *c, DBusMessage *m, const char *fname) {
- char *path, *contents;
+ char *contents, *path;
DBusError error;
assert(c);
@@ -220,7 +231,7 @@ DBusHandlerResult avahi_dbus_handle_introspect(DBusConnection *c, DBusMessage *m
avahi_log_error("Error parsing Introspect message: %s", error.message);
goto fail;
}
-
+
path = avahi_strdup_printf("%s/%s", AVAHI_DBUS_INTROSPECTION_DIR, fname);
contents = file_get_contents(path);
avahi_free(path);
diff --git a/avahi-daemon/main.c b/avahi-daemon/main.c
index 6f0624d..7ebfcd7 100644
--- a/avahi-daemon/main.c
+++ b/avahi-daemon/main.c
@@ -56,6 +56,11 @@
#include <avahi-core/dns-srv-rr.h>
#include <avahi-core/log.h>
+#ifdef ENABLE_CHROOT
+#include "chroot.h"
+#include "caps.h"
+#endif
+
#include "main.h"
#include "simple-protocol.h"
#include "static-services.h"
@@ -83,12 +88,18 @@ typedef struct {
int daemonize;
int use_syslog;
char *config_file;
+#ifdef HAVE_DBUS
int enable_dbus;
int fail_on_missing_dbus;
+#endif
int drop_root;
+ int set_rlimits;
+#ifdef ENABLE_CHROOT
+ int use_chroot;
+#endif
+
int publish_resolv_conf;
char ** publish_dns_servers;
- int no_rlimits;
int debug;
int rlimit_as_set, rlimit_core_set, rlimit_data_set, rlimit_fsize_set, rlimit_nofile_set, rlimit_stack_set;
@@ -125,8 +136,14 @@ static int load_resolv_conf(void) {
avahi_strfreev(resolv_conf);
resolv_conf = NULL;
- if (!(f = fopen(RESOLV_CONF, "r"))) {
- avahi_log_warn("Failed to open "RESOLV_CONF".");
+#ifdef ENABLE_CHROOT
+ f = avahi_chroot_helper_get_file(RESOLV_CONF);
+#else
+ f = fopen(RESOLV_CONF, "r");
+#endif
+
+ if (!f) {
+ avahi_log_warn("Failed to open "RESOLV_CONF": %s", strerror(errno));
goto finish;
}
@@ -208,6 +225,11 @@ static void update_wide_area_servers(void) {
unsigned n = 0;
char **p;
+ if (!resolv_conf) {
+ avahi_server_set_wide_area_servers(avahi_server, NULL, 0);
+ return;
+ }
+
for (p = resolv_conf; *p && n < AVAHI_WIDE_AREA_SERVERS_MAX; p++) {
if (!avahi_address_parse(*p, AVAHI_PROTO_UNSPEC, &a[n]))
avahi_log_warn("Failed to parse address '%s', ignoring.", *p);
@@ -291,6 +313,9 @@ static void help(FILE *f, const char *argv0) {
" "AVAHI_CONFIG_FILE"\n"
" --no-rlimits Don't enforce resource limits\n"
" --no-drop-root Don't drop privileges\n"
+#ifdef ENABLE_CHROOT
+ " --no-chroot Don't chroot()\n"
+#endif
" --debug Increase verbosity\n",
argv0);
}
@@ -302,6 +327,9 @@ static int parse_command_line(DaemonConfig *c, int argc, char *argv[]) {
enum {
OPTION_NO_RLIMITS = 256,
OPTION_NO_DROP_ROOT,
+#ifdef ENABLE_CHROOT
+ OPTION_NO_CHROOT,
+#endif
OPTION_DEBUG
};
@@ -316,6 +344,9 @@ static int parse_command_line(DaemonConfig *c, int argc, char *argv[]) {
{ "syslog", no_argument, NULL, 's' },
{ "no-rlimits", no_argument, NULL, OPTION_NO_RLIMITS },
{ "no-drop-root", no_argument, NULL, OPTION_NO_DROP_ROOT },
+#ifdef ENABLE_CHROOT
+ { "no-chroot", no_argument, NULL, OPTION_NO_CHROOT },
+#endif
{ "debug", no_argument, NULL, OPTION_DEBUG },
{ NULL, 0, NULL, 0 }
};
@@ -352,11 +383,16 @@ static int parse_command_line(DaemonConfig *c, int argc, char *argv[]) {
c->command = DAEMON_CHECK;
break;
case OPTION_NO_RLIMITS:
- c->no_rlimits = 1;
+ c->set_rlimits = 0;
break;
case OPTION_NO_DROP_ROOT:
c->drop_root = 0;
break;
+#ifdef ENABLE_CHROOT
+ case OPTION_NO_CHROOT:
+ c->use_chroot = 0;
+ break;
+#endif
case OPTION_DEBUG:
c->debug = 1;
break;
@@ -588,7 +624,11 @@ static void signal_callback(AvahiWatch *watch, AVAHI_GCC_UNUSED int fd, AVAHI_GC
case SIGHUP:
avahi_log_info("Got SIGHUP, reloading.");
- static_service_load();
+#ifdef ENABLE_CHROOT
+ static_service_load(config.use_chroot);
+#else
+ static_service_load(0);
+#endif
static_service_add_to_server();
if (resolv_conf_entry_group)
@@ -614,6 +654,7 @@ static void signal_callback(AvahiWatch *watch, AVAHI_GCC_UNUSED int fd, AVAHI_GC
}
}
+
static int run_server(DaemonConfig *c) {
int r = -1;
int error;
@@ -641,8 +682,9 @@ static int run_server(DaemonConfig *c) {
if (simple_protocol_setup(poll_api) < 0)
goto finish;
- if (c->enable_dbus) {
+
#ifdef HAVE_DBUS
+ if (c->enable_dbus) {
if (dbus_protocol_setup(poll_api) < 0) {
if (c->fail_on_missing_dbus)
@@ -651,14 +693,35 @@ static int run_server(DaemonConfig *c) {
avahi_log_warn("WARNING: Failed to contact D-BUS daemon, disabling D-BUS support.");
c->enable_dbus = 0;
}
-#else
- avahi_log_warn("WARNING: We are configured to enable D-BUS but it was not compiled in.");
- c->enable_dbus = 0;
+ }
#endif
+
+#ifdef ENABLE_CHROOT
+
+ if (config.drop_root && config.use_chroot) {
+ if (chroot(AVAHI_CONFIG_DIR) < 0) {
+ avahi_log_error("Failed to chroot(): %s", strerror(errno));
+ goto finish;
+ }
+
+ chdir("/");
+
+ if (avahi_caps_drop_all() < 0) {
+ avahi_log_error("Failed to drop capabilities.");
+ goto finish;
+ }
+
+ avahi_log_info("chroot() successful.");
}
+#endif
+
load_resolv_conf();
- static_service_load();
+#ifdef ENABLE_CHROOT
+ static_service_load(config.use_chroot);
+#else
+ static_service_load(0);
+#endif
if (!(avahi_server = avahi_server_new(poll_api, &c->server_config, server_callback, c, &error))) {
avahi_log_error("Failed to create server: %s", avahi_strerror(error));
@@ -778,7 +841,7 @@ static int drop_root(void) {
set_env("USER", pw->pw_name);
set_env("LOGNAME", pw->pw_name);
set_env("HOME", pw->pw_dir);
-
+
avahi_log_info("Successfully dropped root privileges.");
return 0;
@@ -904,15 +967,17 @@ int main(int argc, char *argv[]) {
#ifdef HAVE_DBUS
config.enable_dbus = 1;
config.fail_on_missing_dbus = 1;
-#else
- config.enable_dbus = 0;
- config.fail_on_missing_dbus = 0;
#endif
+
config.drop_root = 1;
+ config.set_rlimits = 1;
+#ifdef ENABLE_CHROOT
+ config.use_chroot = 1;
+#endif
+
config.publish_dns_servers = NULL;
config.publish_resolv_conf = 0;
config.use_syslog = 0;
- config.no_rlimits = 0;
config.debug = 0;
config.rlimit_as_set = 0;
@@ -937,6 +1002,10 @@ int main(int argc, char *argv[]) {
if (parse_command_line(&config, argc, argv) < 0)
goto finish;
+#ifdef ENABLE_CHROOT
+ config.use_chroot = config.use_chroot && config.drop_root;
+#endif
+
if (config.command == DAEMON_HELP) {
help(stdout, argv0);
r = 0;
@@ -1005,8 +1074,20 @@ int main(int argc, char *argv[]) {
goto finish;
if (config.drop_root) {
+#ifdef ENABLE_CHROOT
+ if (config.use_chroot)
+ if (avahi_caps_reduce() < 0)
+ goto finish;
+#endif
+
if (drop_root() < 0)
goto finish;
+
+#ifdef ENABLE_CHROOT
+ if (config.use_chroot)
+ if (avahi_caps_reduce2() < 0)
+ goto finish;
+#endif
}
if (daemon_pid_file_create() < 0) {
@@ -1018,13 +1099,20 @@ int main(int argc, char *argv[]) {
} else
wrote_pid_file = 1;
- if (!config.no_rlimits)
+ if (config.set_rlimits)
enforce_rlimits();
chdir("/");
-
+
+#ifdef ENABLE_CHROOT
+ if (config.drop_root && config.use_chroot)
+ if (avahi_chroot_helper_start() < 0) {
+ avahi_log_error("failed to start chroot() helper daemon.");
+ goto finish;
+ }
+#endif
avahi_log_info("%s "PACKAGE_VERSION" starting up.", argv0);
-
+
if (run_server(&config) == 0)
r = 0;
}
@@ -1039,8 +1127,17 @@ finish:
avahi_strfreev(config.publish_dns_servers);
avahi_strfreev(resolv_conf);
- if (wrote_pid_file)
+ if (wrote_pid_file) {
+#ifdef ENABLE_CHROOT
+ avahi_chroot_helper_unlink(pid_file_proc());
+#else
daemon_pid_file_remove();
-
+#endif
+ }
+
+#if ENABLE_CHROOT
+ avahi_chroot_helper_shutdown();
+#endif
+
return r;
}
diff --git a/avahi-daemon/main.h b/avahi-daemon/main.h
index 0c93337..951b5f6 100644
--- a/avahi-daemon/main.h
+++ b/avahi-daemon/main.h
@@ -28,5 +28,4 @@
extern AvahiServer *avahi_server;
extern AvahiSimplePoll *simple_poll_api;
-
#endif
diff --git a/avahi-daemon/simple-protocol.c b/avahi-daemon/simple-protocol.c
index 2d806df..8aa427b 100644
--- a/avahi-daemon/simple-protocol.c
+++ b/avahi-daemon/simple-protocol.c
@@ -44,6 +44,10 @@
#include "simple-protocol.h"
#include "main.h"
+#ifdef ENABLE_CHROOT
+#include "chroot.h"
+#endif
+
#define BUFFER_SIZE (20*1024)
#define CLIENTS_MAX 50
@@ -497,7 +501,11 @@ void simple_protocol_shutdown(void) {
if (server) {
if (server->bind_successful)
+#ifdef ENABLE_CHROOT
+ avahi_chroot_helper_unlink(AVAHI_SOCKET);
+#else
unlink(AVAHI_SOCKET);
+#endif
while (server->clients)
client_free(server->clients);
diff --git a/avahi-daemon/static-services.c b/avahi-daemon/static-services.c
index 129063c..7f910b0 100644
--- a/avahi-daemon/static-services.c
+++ b/avahi-daemon/static-services.c
@@ -648,7 +648,7 @@ static void load_file(char *n) {
}
}
-void static_service_load(void) {
+void static_service_load(int in_chroot) {
StaticServiceGroup *g, *n;
glob_t globbuf;
char **p;
@@ -677,7 +677,7 @@ void static_service_load(void) {
}
memset(&globbuf, 0, sizeof(globbuf));
- if (glob(AVAHI_SERVICE_DIR "/*.service", GLOB_ERR, NULL, &globbuf) != 0)
+ if (glob(in_chroot ? "/services/*.service" : AVAHI_SERVICE_DIR "/*.service", GLOB_ERR, NULL, &globbuf) != 0)
avahi_log_error("Failed to read service directory.");
else {
for (p = globbuf.gl_pathv; *p; p++)
diff --git a/avahi-daemon/static-services.h b/avahi-daemon/static-services.h
index 1c50372..a2bc355 100644
--- a/avahi-daemon/static-services.h
+++ b/avahi-daemon/static-services.h
@@ -22,7 +22,7 @@
USA.
***/
-void static_service_load(void);
+void static_service_load(int in_chroot);
void static_service_free_all(void);
void static_service_add_to_server(void);
void static_service_remove_from_server(void);
diff --git a/configure.ac b/configure.ac
index 356a0ad..4e9a98a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -215,6 +215,17 @@ AC_TYPE_PID_T
AC_CHECK_DECLS(environ)
+enable_chroot=yes
+AC_CHECK_HEADERS([sys/capability.h],,enable_chroot=no)
+AC_CHECK_HEADERS([sys/prctl.h],,enable_chroot=no)
+AC_CHECK_FUNCS([chroot],,enable_chroot=no)
+
+AM_CONDITIONAL(ENABLE_CHROOT, test "x$enable_chroot" = "xyes")
+
+if test "x$enable_chroot" = "xyes" ; then
+ AC_DEFINE([ENABLE_CHROOT], 1, [Enable chroot() usage])
+fi
+
# Check for pkg-config manually first, as if its not installed the
# PKG_PROG_PKG_CONFIG macro won't be defined.
AC_CHECK_PROG(have_pkg_config, pkg-config, yes, no)
@@ -729,6 +740,7 @@ echo "
Linux Distro: ${with_distro}
User for Avahi: ${AVAHI_USER}
Group for Avahi: ${AVAHI_GROUP}
+ Enable chroot(): ${enable_chroot}
"
BUILD_DAEMON="no (!)"