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