]> git.meshlink.io Git - catta/commitdiff
* add chroot() support on Linux
authorLennart Poettering <lennart@poettering.net>
Sun, 30 Oct 2005 18:21:57 +0000 (18:21 +0000)
committerLennart Poettering <lennart@poettering.net>
Sun, 30 Oct 2005 18:21:57 +0000 (18:21 +0000)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@907 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe

13 files changed:
avahi-daemon/Makefile.am
avahi-daemon/avahi-daemon.conf
avahi-daemon/caps.c [new file with mode: 0644]
avahi-daemon/caps.h [new file with mode: 0644]
avahi-daemon/chroot.c [new file with mode: 0644]
avahi-daemon/chroot.h [new file with mode: 0644]
avahi-daemon/dbus-util.c
avahi-daemon/main.c
avahi-daemon/main.h
avahi-daemon/simple-protocol.c
avahi-daemon/static-services.c
avahi-daemon/static-services.h
configure.ac

index 70817524aec11179ebfe3b137699390b4473a504..0dadede2d2981ad81adad3193579a95b0d95c008 100644 (file)
@@ -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 \
index ed154bebb9ea750edfd0c873e3ba25dad2b98d2c..b812d371e3a0bfb046e24fff13fc38730929d1a0 100644 (file)
@@ -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 (file)
index 0000000..2d0f39a
--- /dev/null
@@ -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 (file)
index 0000000..788aceb
--- /dev/null
@@ -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 (file)
index 0000000..5504cbd
--- /dev/null
@@ -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 (file)
index 0000000..1255739
--- /dev/null
@@ -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
index 09826d335afecd070e0011ad40deb72186303760..00d8a9d4e0efc4b8d348fda9a5d7e6f31086d1b0 100644 (file)
 #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);
index 6f0624d3ae96389ce30c5dcefa308331a9cbf429..7ebfcd776f195a3121245d5a0d7ef46dbe82d8e6 100644 (file)
 #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;
 }
index 0c933379233b687507eb3b2f5f95281760a6d2fd..951b5f683ff6a17aa427a861338d277119e8206a 100644 (file)
@@ -28,5 +28,4 @@
 extern AvahiServer *avahi_server;
 extern AvahiSimplePoll *simple_poll_api;
 
-
 #endif
index 2d806dfc9f1841961e5fb24673dd31c9687e65c7..8aa427b86639d1fd7f1a6b683acb656e2e446902 100644 (file)
 #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);
index 129063c02e99b8630a75bf55ffe1560bbc79634a..7f910b0f720eb504e23c32e51b52129fab7f6bb9 100644 (file)
@@ -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++)
index 1c503727ca9a2e7c206a3154d8cd29d77d0b9335..a2bc355abf1f7254d7b1ad644e405003d6b36a65 100644 (file)
@@ -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);
index 356a0adfdcf37ace7b83c986ac4521de32ab7d5f..4e9a98a4382ddaf634a60fd4b1f61c31c0c0ab01 100644 (file)
@@ -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 (!)"