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