]> git.meshlink.io Git - catta/blob - avahi-daemon/chroot.c
5478bce6ff332fc676c5c989429310e4f6686143
[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 #endif
58     AVAHI_CHROOT_UNLINK_PID,
59     AVAHI_CHROOT_UNLINK_SOCKET,
60     AVAHI_CHROOT_MAX
61 };
62
63 static const char* const get_file_name_table[AVAHI_CHROOT_MAX] = {
64     NULL,
65     NULL,
66     "/etc/resolv.conf",
67 #ifdef HAVE_DBUS
68     AVAHI_DBUS_INTROSPECTION_DIR"/Server.introspect",
69     AVAHI_DBUS_INTROSPECTION_DIR"/EntryGroup.introspect",
70     AVAHI_DBUS_INTROSPECTION_DIR"/AddressResolver.introspect",
71     AVAHI_DBUS_INTROSPECTION_DIR"/DomainBrowser.introspect",
72     AVAHI_DBUS_INTROSPECTION_DIR"/HostNameResolver.introspect",
73     AVAHI_DBUS_INTROSPECTION_DIR"/ServiceBrowser.introspect",
74     AVAHI_DBUS_INTROSPECTION_DIR"/ServiceResolver.introspect",
75     AVAHI_DBUS_INTROSPECTION_DIR"/ServiceTypeBrowser.introspect",
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     NULL,
87     NULL,
88     NULL,
89     NULL,
90     NULL,
91     NULL,
92     NULL,
93     NULL,
94 #endif
95     AVAHI_DAEMON_RUNTIME_DIR"/pid",
96     AVAHI_SOCKET
97 };
98
99 static int helper_fd = -1;
100
101 static int send_fd(int fd, int payload_fd) {
102     uint8_t dummy = AVAHI_CHROOT_SUCCESS;
103     struct iovec iov;
104     struct msghdr msg;
105     union {
106         struct cmsghdr hdr;
107         char buf[CMSG_SPACE(sizeof(int))];
108     } cmsg;
109
110     /* Send a file descriptor over the socket */
111     
112     memset(&iov, 0, sizeof(iov));
113     memset(&msg, 0, sizeof(msg));
114     memset(&cmsg, 0, sizeof(cmsg));
115         
116     iov.iov_base = &dummy;  
117     iov.iov_len = sizeof(dummy);
118         
119     msg.msg_iov = &iov;
120     msg.msg_iovlen = 1;
121     msg.msg_name = NULL;
122     msg.msg_namelen = 0;
123
124     msg.msg_control = &cmsg;
125     msg.msg_controllen = sizeof(cmsg);
126     msg.msg_flags = 0;
127                 
128     cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(int));
129     cmsg.hdr.cmsg_level = SOL_SOCKET;
130     cmsg.hdr.cmsg_type = SCM_RIGHTS;
131     *((int*) CMSG_DATA(&cmsg.hdr)) = payload_fd;
132
133     if (sendmsg(fd, &msg, 0) < 0) {
134         avahi_log_error("sendmsg() failed: %s", strerror(errno));
135         return -1;
136     }
137
138     return 0;
139 }
140
141 static int recv_fd(int fd) {
142     uint8_t dummy;
143     struct iovec iov;
144     struct msghdr msg;
145     union {
146         struct cmsghdr hdr;
147         char buf[CMSG_SPACE(sizeof(int))];
148     } cmsg;
149
150     /* Receive a file descriptor from a socket */
151
152     memset(&iov, 0, sizeof(iov));
153     memset(&msg, 0, sizeof(msg));
154     memset(&cmsg, 0, sizeof(cmsg));
155     
156     iov.iov_base = &dummy;
157     iov.iov_len = sizeof(dummy);
158     
159     msg.msg_iov = &iov;
160     msg.msg_iovlen = 1;
161     msg.msg_name = NULL;
162     msg.msg_namelen = 0;
163
164     msg.msg_control = cmsg.buf;
165     msg.msg_controllen = sizeof(cmsg);
166     msg.msg_flags = 0;
167                 
168     cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(int));
169     cmsg.hdr.cmsg_level = SOL_SOCKET;
170     cmsg.hdr.cmsg_type = SCM_RIGHTS;
171     *((int*) CMSG_DATA(&cmsg.hdr)) = -1;
172                 
173     if (recvmsg(fd, &msg, 0) <= 0) {
174         avahi_log_error("recvmsg() failed: %s", strerror(errno));
175         return -1;
176     } else {
177         struct cmsghdr* h;
178
179         if (dummy != AVAHI_CHROOT_SUCCESS) {
180             errno = EINVAL;
181             return -1;
182         }
183         
184         if (!(h = CMSG_FIRSTHDR(&msg))) {
185             avahi_log_error("recvmsg() sent no fd.");
186             errno = EINVAL;
187             return -1;
188         }
189
190         assert(h->cmsg_len = CMSG_LEN(sizeof(int)));
191         assert(h->cmsg_level = SOL_SOCKET);
192         assert(h->cmsg_type == SCM_RIGHTS);
193
194         return *((int*)CMSG_DATA(h));
195     }
196 }
197                 
198 static int helper_main(int fd) {
199     int ret = 1;
200     assert(fd >= 0);
201
202     /* This is the main function of our helper process which is forked
203      * off to access files outside the chroot environment. Keep in
204      * mind that this code is security sensitive! */
205
206     avahi_log_debug(__FILE__": chroot() helper started");
207     
208     for (;;) {
209         uint8_t command;
210         ssize_t r;
211
212         if ((r = read(fd, &command, sizeof(command))) <= 0) {
213
214             /* EOF? */
215             if (r == 0)
216                 break;
217             
218             avahi_log_error(__FILE__": read() failed: %s", strerror(errno));
219             goto fail;
220         }
221
222         assert(r == sizeof(command));
223
224         avahi_log_debug(__FILE__": chroot() helper got command %02x", command);
225
226         switch (command) {
227 #ifdef HAVE_DBUS
228             case AVAHI_CHROOT_GET_SERVER_INTROSPECT:
229             case AVAHI_CHROOT_GET_ENTRY_GROUP_INTROSPECT:
230             case AVAHI_CHROOT_GET_ADDRESS_RESOLVER_INTROSPECT:
231             case AVAHI_CHROOT_GET_DOMAIN_BROWSER_INTROSPECT:
232             case AVAHI_CHROOT_GET_HOST_NAME_RESOLVER_INTROSPECT:
233             case AVAHI_CHROOT_GET_SERVICE_BROWSER_INTROSPECT:
234             case AVAHI_CHROOT_GET_SERVICE_RESOLVER_INTROSPECT:
235             case AVAHI_CHROOT_GET_SERVICE_TYPE_BROWSER_INTROSPECT:
236 #endif
237             case AVAHI_CHROOT_GET_RESOLV_CONF: {
238                 int payload;
239
240                 if ((payload = open(get_file_name_table[(int) command], O_RDONLY)) < 0) {
241                     uint8_t c = AVAHI_CHROOT_FAILURE;
242
243                     avahi_log_error(__FILE__": open() failed: %s", strerror(errno));
244
245                     if (write(fd, &c, sizeof(c)) != sizeof(c)) {
246                         avahi_log_error(__FILE__": write() failed: %s\n", strerror(errno));
247                         goto fail;
248                     }
249                     
250                     break;
251                 }
252
253                 if (send_fd(fd, payload) < 0)
254                     goto fail;
255
256                 close(payload);
257                 
258                 break;
259             }
260
261             case AVAHI_CHROOT_UNLINK_SOCKET:
262             case AVAHI_CHROOT_UNLINK_PID: {
263                 uint8_t c = AVAHI_CHROOT_SUCCESS;
264                 
265                 unlink(unlink_file_name_table[(int) command]);
266
267                 if (write(fd, &c, sizeof(c)) != sizeof(c)) {
268                     avahi_log_error(__FILE__": write() failed: %s\n", strerror(errno));
269                     goto fail;
270                 }
271                 
272                 break;
273             }
274                 
275             default:
276                 avahi_log_error(__FILE__": Unknown command %02x.", command);
277                 break;
278         }
279     }
280
281     ret = 0;
282     
283 fail:
284
285     avahi_log_debug(__FILE__": chroot() helper exiting with return value %i", ret);
286     
287     return ret;
288 }
289
290 int avahi_chroot_helper_start(const char *argv0) {
291     int sock[2];
292     pid_t pid;
293
294     assert(helper_fd < 0);
295     
296     if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock) < 0) {
297         avahi_log_error("socketpair() failed: %s", strerror(errno));
298         return -1;
299     }
300     
301     if ((pid = fork()) < 0) {
302         close(sock[0]);
303         close(sock[1]);
304         avahi_log_error(__FILE__": fork() failed: %s", strerror(errno));
305         return -1;
306     } else if (pid == 0) {
307
308         setsid();
309         
310         /* Drop all remaining capabilities */
311         avahi_caps_drop_all();
312
313         avahi_set_proc_title("%s: chroot helper process", argv0);
314         
315         close(sock[0]);
316         helper_main(sock[1]);
317         _exit(0);
318     }
319
320     close(sock[1]);
321     helper_fd = sock[0];
322
323     return 0;
324 }
325
326 void avahi_chroot_helper_shutdown(void) {
327
328     if (helper_fd <= 0)
329         return;
330
331     close(helper_fd);
332     helper_fd = -1;
333 }
334
335 int avahi_chroot_helper_get_fd(const char *fname) {
336
337     if (helper_fd >= 0) {
338         uint8_t command;
339         
340         for (command = 2; command < AVAHI_CHROOT_MAX; command++)
341             if (get_file_name_table[(int) command] &&
342                 strcmp(fname, get_file_name_table[(int) command]) == 0)
343                 break;
344
345         if (command >= AVAHI_CHROOT_MAX) {
346             avahi_log_error("chroot() helper accessed for invalid file name");
347             errno = EACCES;
348             return -1;
349         }
350
351         assert(get_file_name_table[(int) command]);
352         
353         if (write(helper_fd, &command, sizeof(command)) < 0) {
354             avahi_log_error("write() failed: %s\n", strerror(errno));
355             return -1;
356         }
357
358         return recv_fd(helper_fd);
359
360     } else
361         return open(fname, O_RDONLY);
362 }
363
364
365 FILE *avahi_chroot_helper_get_file(const char *fname) {
366     FILE *f;
367     int fd;
368
369     if ((fd = avahi_chroot_helper_get_fd(fname)) < 0)
370         return NULL;
371
372     f = fdopen(fd, "r");
373     assert(f);
374
375     return f;
376 }
377
378 int avahi_chroot_helper_unlink(const char *fname) {
379
380     if (helper_fd >= 0) {
381         uint8_t c, command;
382         ssize_t r;
383         
384         for (command = 2; command < AVAHI_CHROOT_MAX; command++)
385             if (unlink_file_name_table[(int) command] &&
386                 strcmp(fname, unlink_file_name_table[(int) command]) == 0)
387                 break;
388
389         if (command >= AVAHI_CHROOT_MAX) {
390             avahi_log_error("chroot() helper accessed for invalid file name");
391             errno = EACCES;
392             return -1;
393         }
394
395         if (write(helper_fd, &command, sizeof(command)) < 0) {
396             avahi_log_error("write() failed: %s\n", strerror(errno));
397             return -1;
398         }
399
400         if ((r = read(helper_fd, &c, sizeof(c))) < 0) {
401             avahi_log_error("read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
402             return -1;
403         }
404         
405         return 0;
406         
407     } else
408         
409         return unlink(fname);
410     
411 }