Home | History | Annotate | Download | only in avahi-daemon
      1 /***
      2   This file is part of avahi.
      3 
      4   avahi is free software; you can redistribute it and/or modify it
      5   under the terms of the GNU Lesser General Public License as
      6   published by the Free Software Foundation; either version 2.1 of the
      7   License, or (at your option) any later version.
      8 
      9   avahi is distributed in the hope that it will be useful, but WITHOUT
     10   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     11   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
     12   Public License for more details.
     13 
     14   You should have received a copy of the GNU Lesser General Public
     15   License along with avahi; if not, write to the Free Software
     16   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
     17   USA.
     18 ***/
     19 
     20 #ifdef HAVE_CONFIG_H
     21 #include <config.h>
     22 #endif
     23 
     24 #include <inttypes.h>
     25 #include <sys/socket.h>
     26 #include <sys/types.h>
     27 #include <fcntl.h>
     28 #include <stdlib.h>
     29 #include <unistd.h>
     30 #include <sys/un.h>
     31 #include <string.h>
     32 #include <errno.h>
     33 #include <assert.h>
     34 
     35 #include <avahi-core/log.h>
     36 #include <libdaemon/dfork.h>
     37 
     38 #include "chroot.h"
     39 #include "caps.h"
     40 #include "setproctitle.h"
     41 
     42 enum {
     43     AVAHI_CHROOT_SUCCESS = 0,
     44     AVAHI_CHROOT_FAILURE,
     45     AVAHI_CHROOT_GET_RESOLV_CONF,
     46 #ifdef HAVE_DBUS
     47     AVAHI_CHROOT_GET_SERVER_INTROSPECT,
     48     AVAHI_CHROOT_GET_ENTRY_GROUP_INTROSPECT,
     49     AVAHI_CHROOT_GET_ADDRESS_RESOLVER_INTROSPECT,
     50     AVAHI_CHROOT_GET_DOMAIN_BROWSER_INTROSPECT,
     51     AVAHI_CHROOT_GET_HOST_NAME_RESOLVER_INTROSPECT,
     52     AVAHI_CHROOT_GET_SERVICE_BROWSER_INTROSPECT,
     53     AVAHI_CHROOT_GET_SERVICE_RESOLVER_INTROSPECT,
     54     AVAHI_CHROOT_GET_SERVICE_TYPE_BROWSER_INTROSPECT,
     55     AVAHI_CHROOT_GET_RECORD_BROWSER_INTROSPECT,
     56 #endif
     57     AVAHI_CHROOT_UNLINK_PID,
     58     AVAHI_CHROOT_UNLINK_SOCKET,
     59     AVAHI_CHROOT_MAX
     60 };
     61 
     62 static const char* const get_file_name_table[AVAHI_CHROOT_MAX] = {
     63     NULL,
     64     NULL,
     65     "/etc/resolv.conf",
     66 #ifdef HAVE_DBUS
     67     AVAHI_DBUS_INTROSPECTION_DIR"/org.freedesktop.Avahi.Server.xml",
     68     AVAHI_DBUS_INTROSPECTION_DIR"/org.freedesktop.Avahi.EntryGroup.xml",
     69     AVAHI_DBUS_INTROSPECTION_DIR"/org.freedesktop.Avahi.AddressResolver.xml",
     70     AVAHI_DBUS_INTROSPECTION_DIR"/org.freedesktop.Avahi.DomainBrowser.xml",
     71     AVAHI_DBUS_INTROSPECTION_DIR"/org.freedesktop.Avahi.HostNameResolver.xml",
     72     AVAHI_DBUS_INTROSPECTION_DIR"/org.freedesktop.Avahi.ServiceBrowser.xml",
     73     AVAHI_DBUS_INTROSPECTION_DIR"/org.freedesktop.Avahi.ServiceResolver.xml",
     74     AVAHI_DBUS_INTROSPECTION_DIR"/org.freedesktop.Avahi.ServiceTypeBrowser.xml",
     75     AVAHI_DBUS_INTROSPECTION_DIR"/org.freedesktop.Avahi.RecordBrowser.xml",
     76 #endif
     77     NULL,
     78     NULL
     79 };
     80 
     81 static const char *const unlink_file_name_table[AVAHI_CHROOT_MAX] = {
     82     NULL,
     83     NULL,
     84     NULL
     85 #ifdef HAVE_DBUS
     86     ,
     87     NULL,
     88     NULL,
     89     NULL,
     90     NULL,
     91     NULL,
     92     NULL,
     93     NULL,
     94     NULL,
     95     NULL
     96 #endif
     97 #ifdef AVAHI_DAEMON_RUNTIME_DIR
     98     ,
     99     AVAHI_DAEMON_RUNTIME_DIR"/pid"
    100 #endif
    101 #ifdef AVAHI_SOCKET
    102     ,
    103     AVAHI_SOCKET
    104 #endif
    105 };
    106 
    107 static int helper_fd = -1;
    108 
    109 static int send_fd(int fd, int payload_fd) {
    110     uint8_t dummy = AVAHI_CHROOT_SUCCESS;
    111     struct iovec iov;
    112     struct msghdr msg;
    113     union {
    114         struct cmsghdr hdr;
    115         char buf[CMSG_SPACE(sizeof(int))];
    116     } cmsg;
    117 
    118     /* Send a file descriptor over the socket */
    119 
    120     memset(&iov, 0, sizeof(iov));
    121     memset(&msg, 0, sizeof(msg));
    122     memset(&cmsg, 0, sizeof(cmsg));
    123 
    124     iov.iov_base = &dummy;
    125     iov.iov_len = sizeof(dummy);
    126 
    127     msg.msg_iov = &iov;
    128     msg.msg_iovlen = 1;
    129     msg.msg_name = NULL;
    130     msg.msg_namelen = 0;
    131 
    132     msg.msg_control = &cmsg;
    133     msg.msg_controllen = sizeof(cmsg);
    134     msg.msg_flags = 0;
    135 
    136     cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(int));
    137     cmsg.hdr.cmsg_level = SOL_SOCKET;
    138     cmsg.hdr.cmsg_type = SCM_RIGHTS;
    139     *((int*) CMSG_DATA(&cmsg.hdr)) = payload_fd;
    140 
    141     if (sendmsg(fd, &msg, 0) < 0) {
    142         avahi_log_error("sendmsg() failed: %s", strerror(errno));
    143         return -1;
    144     }
    145 
    146     return 0;
    147 }
    148 
    149 static int recv_fd(int fd) {
    150     uint8_t dummy;
    151     struct iovec iov;
    152     struct msghdr msg;
    153     union {
    154         struct cmsghdr hdr;
    155         char buf[CMSG_SPACE(sizeof(int))];
    156     } cmsg;
    157 
    158     /* Receive a file descriptor from a socket */
    159 
    160     memset(&iov, 0, sizeof(iov));
    161     memset(&msg, 0, sizeof(msg));
    162     memset(&cmsg, 0, sizeof(cmsg));
    163 
    164     iov.iov_base = &dummy;
    165     iov.iov_len = sizeof(dummy);
    166 
    167     msg.msg_iov = &iov;
    168     msg.msg_iovlen = 1;
    169     msg.msg_name = NULL;
    170     msg.msg_namelen = 0;
    171 
    172     msg.msg_control = cmsg.buf;
    173     msg.msg_controllen = sizeof(cmsg);
    174     msg.msg_flags = 0;
    175 
    176     cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(int));
    177     cmsg.hdr.cmsg_level = SOL_SOCKET;
    178     cmsg.hdr.cmsg_type = SCM_RIGHTS;
    179     *((int*) CMSG_DATA(&cmsg.hdr)) = -1;
    180 
    181     if (recvmsg(fd, &msg, 0) <= 0) {
    182         avahi_log_error("recvmsg() failed: %s", strerror(errno));
    183         return -1;
    184     } else {
    185         struct cmsghdr* h;
    186 
    187         if (dummy != AVAHI_CHROOT_SUCCESS) {
    188             errno = EINVAL;
    189             return -1;
    190         }
    191 
    192         if (!(h = CMSG_FIRSTHDR(&msg))) {
    193             avahi_log_error("recvmsg() sent no fd.");
    194             errno = EINVAL;
    195             return -1;
    196         }
    197 
    198         assert(h->cmsg_len = CMSG_LEN(sizeof(int)));
    199         assert(h->cmsg_level = SOL_SOCKET);
    200         assert(h->cmsg_type == SCM_RIGHTS);
    201 
    202         return *((int*)CMSG_DATA(h));
    203     }
    204 }
    205 
    206 static int helper_main(int fd) {
    207     int ret = 1;
    208     assert(fd >= 0);
    209 
    210     /* This is the main function of our helper process which is forked
    211      * off to access files outside the chroot environment. Keep in
    212      * mind that this code is security sensitive! */
    213 
    214     avahi_log_debug(__FILE__": chroot() helper started");
    215 
    216     for (;;) {
    217         uint8_t command;
    218         ssize_t r;
    219 
    220         if ((r = read(fd, &command, sizeof(command))) <= 0) {
    221 
    222             /* EOF? */
    223             if (r == 0)
    224                 break;
    225 
    226             avahi_log_error(__FILE__": read() failed: %s", strerror(errno));
    227             goto fail;
    228         }
    229 
    230         assert(r == sizeof(command));
    231 
    232         avahi_log_debug(__FILE__": chroot() helper got command %02x", command);
    233 
    234         switch (command) {
    235 #ifdef HAVE_DBUS
    236             case AVAHI_CHROOT_GET_SERVER_INTROSPECT:
    237             case AVAHI_CHROOT_GET_ENTRY_GROUP_INTROSPECT:
    238             case AVAHI_CHROOT_GET_ADDRESS_RESOLVER_INTROSPECT:
    239             case AVAHI_CHROOT_GET_DOMAIN_BROWSER_INTROSPECT:
    240             case AVAHI_CHROOT_GET_HOST_NAME_RESOLVER_INTROSPECT:
    241             case AVAHI_CHROOT_GET_SERVICE_BROWSER_INTROSPECT:
    242             case AVAHI_CHROOT_GET_SERVICE_RESOLVER_INTROSPECT:
    243             case AVAHI_CHROOT_GET_SERVICE_TYPE_BROWSER_INTROSPECT:
    244             case AVAHI_CHROOT_GET_RECORD_BROWSER_INTROSPECT:
    245 #endif
    246             case AVAHI_CHROOT_GET_RESOLV_CONF: {
    247                 int payload;
    248 
    249                 if ((payload = open(get_file_name_table[(int) command], O_RDONLY)) < 0) {
    250                     uint8_t c = AVAHI_CHROOT_FAILURE;
    251 
    252                     avahi_log_error(__FILE__": open() failed: %s", strerror(errno));
    253 
    254                     if (write(fd, &c, sizeof(c)) != sizeof(c)) {
    255                         avahi_log_error(__FILE__": write() failed: %s\n", strerror(errno));
    256                         goto fail;
    257                     }
    258 
    259                     break;
    260                 }
    261 
    262                 if (send_fd(fd, payload) < 0)
    263                     goto fail;
    264 
    265                 close(payload);
    266 
    267                 break;
    268             }
    269 
    270             case AVAHI_CHROOT_UNLINK_SOCKET:
    271             case AVAHI_CHROOT_UNLINK_PID: {
    272                 uint8_t c = AVAHI_CHROOT_SUCCESS;
    273 
    274                 unlink(unlink_file_name_table[(int) command]);
    275 
    276                 if (write(fd, &c, sizeof(c)) != sizeof(c)) {
    277                     avahi_log_error(__FILE__": write() failed: %s\n", strerror(errno));
    278                     goto fail;
    279                 }
    280 
    281                 break;
    282             }
    283 
    284             default:
    285                 avahi_log_error(__FILE__": Unknown command %02x.", command);
    286                 break;
    287         }
    288     }
    289 
    290     ret = 0;
    291 
    292 fail:
    293 
    294     avahi_log_debug(__FILE__": chroot() helper exiting with return value %i", ret);
    295 
    296     return ret;
    297 }
    298 
    299 int avahi_chroot_helper_start(const char *argv0) {
    300     int sock[2];
    301     pid_t pid;
    302 
    303     assert(helper_fd < 0);
    304 
    305     if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock) < 0) {
    306         avahi_log_error("socketpair() failed: %s", strerror(errno));
    307         return -1;
    308     }
    309 
    310     if ((pid = fork()) < 0) {
    311         close(sock[0]);
    312         close(sock[1]);
    313         avahi_log_error(__FILE__": fork() failed: %s", strerror(errno));
    314         return -1;
    315     } else if (pid == 0) {
    316 
    317         /* Drop all remaining capabilities */
    318         avahi_caps_drop_all();
    319 
    320         avahi_set_proc_title(argv0, "%s: chroot helper", argv0);
    321 
    322         daemon_retval_done();
    323 
    324         close(sock[0]);
    325         helper_main(sock[1]);
    326         _exit(0);
    327     }
    328 
    329     close(sock[1]);
    330     helper_fd = sock[0];
    331 
    332     return 0;
    333 }
    334 
    335 void avahi_chroot_helper_shutdown(void) {
    336 
    337     if (helper_fd <= 0)
    338         return;
    339 
    340     close(helper_fd);
    341     helper_fd = -1;
    342 }
    343 
    344 int avahi_chroot_helper_get_fd(const char *fname) {
    345 
    346     if (helper_fd >= 0) {
    347         uint8_t command;
    348 
    349         for (command = 2; command < AVAHI_CHROOT_MAX; command++)
    350             if (get_file_name_table[(int) command] &&
    351                 strcmp(fname, get_file_name_table[(int) command]) == 0)
    352                 break;
    353 
    354         if (command >= AVAHI_CHROOT_MAX) {
    355             avahi_log_error("chroot() helper accessed for invalid file name");
    356             errno = EACCES;
    357             return -1;
    358         }
    359 
    360         assert(get_file_name_table[(int) command]);
    361 
    362         if (write(helper_fd, &command, sizeof(command)) < 0) {
    363             avahi_log_error("write() failed: %s\n", strerror(errno));
    364             return -1;
    365         }
    366 
    367         return recv_fd(helper_fd);
    368 
    369     } else
    370         return open(fname, O_RDONLY);
    371 }
    372 
    373 
    374 FILE *avahi_chroot_helper_get_file(const char *fname) {
    375     FILE *f;
    376     int fd;
    377 
    378     if ((fd = avahi_chroot_helper_get_fd(fname)) < 0)
    379         return NULL;
    380 
    381     f = fdopen(fd, "r");
    382     assert(f);
    383 
    384     return f;
    385 }
    386 
    387 int avahi_chroot_helper_unlink(const char *fname) {
    388 
    389     if (helper_fd >= 0) {
    390         uint8_t c, command;
    391         ssize_t r;
    392 
    393         for (command = 2; command < AVAHI_CHROOT_MAX; command++)
    394             if (unlink_file_name_table[(int) command] &&
    395                 strcmp(fname, unlink_file_name_table[(int) command]) == 0)
    396                 break;
    397 
    398         if (command >= AVAHI_CHROOT_MAX) {
    399             avahi_log_error("chroot() helper accessed for invalid file name");
    400             errno = EACCES;
    401             return -1;
    402         }
    403 
    404         if (write(helper_fd, &command, sizeof(command)) < 0 &&
    405             (errno != EPIPE && errno != ECONNRESET)) {
    406             avahi_log_error("write() failed: %s\n", strerror(errno));
    407             return -1;
    408         }
    409 
    410         if ((r = read(helper_fd, &c, sizeof(c))) < 0 &&
    411             (errno != EPIPE && errno != ECONNRESET)) {
    412             avahi_log_error("read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
    413             return -1;
    414         }
    415 
    416         return 0;
    417 
    418     } else
    419 
    420         return unlink(fname);
    421 
    422 }
    423