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