]> git.meshlink.io Git - catta/blob - avahi-daemon/chroot.c
make use of setproctitle() to change the process title of the daemon processes. This...
[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 = daemon_fork()) < 0) {
302         close(sock[0]);
303         close(sock[1]);
304         avahi_log_error(__FILE__": Failed to fork()");
305         return -1;
306     } else if (pid == 0) {
307         
308         /* Drop all remaining capabilities */
309         avahi_caps_drop_all();
310
311         avahi_set_proc_title("%s: chroot helper process", argv0);
312         
313         close(sock[0]);
314         helper_main(sock[1]);
315         _exit(0);
316     }
317
318     close(sock[1]);
319     helper_fd = sock[0];
320
321     return 0;
322 }
323
324 void avahi_chroot_helper_shutdown(void) {
325
326     if (helper_fd <= 0)
327         return;
328
329     close(helper_fd);
330     helper_fd = -1;
331 }
332
333 int avahi_chroot_helper_get_fd(const char *fname) {
334
335     if (helper_fd >= 0) {
336         uint8_t command;
337         
338         for (command = 2; command < AVAHI_CHROOT_MAX; command++)
339             if (get_file_name_table[(int) command] &&
340                 strcmp(fname, get_file_name_table[(int) command]) == 0)
341                 break;
342
343         if (command >= AVAHI_CHROOT_MAX) {
344             avahi_log_error("chroot() helper accessed for invalid file name");
345             errno = EACCES;
346             return -1;
347         }
348
349         assert(get_file_name_table[(int) command]);
350         
351         if (write(helper_fd, &command, sizeof(command)) < 0) {
352             avahi_log_error("write() failed: %s\n", strerror(errno));
353             return -1;
354         }
355
356         return recv_fd(helper_fd);
357
358     } else
359         return open(fname, O_RDONLY);
360 }
361
362
363 FILE *avahi_chroot_helper_get_file(const char *fname) {
364     FILE *f;
365     int fd;
366
367     if ((fd = avahi_chroot_helper_get_fd(fname)) < 0)
368         return NULL;
369
370     f = fdopen(fd, "r");
371     assert(f);
372
373     return f;
374 }
375
376 int avahi_chroot_helper_unlink(const char *fname) {
377
378     if (helper_fd >= 0) {
379         uint8_t c, command;
380         ssize_t r;
381         
382         for (command = 2; command < AVAHI_CHROOT_MAX; command++)
383             if (unlink_file_name_table[(int) command] &&
384                 strcmp(fname, unlink_file_name_table[(int) command]) == 0)
385                 break;
386
387         if (command >= AVAHI_CHROOT_MAX) {
388             avahi_log_error("chroot() helper accessed for invalid file name");
389             errno = EACCES;
390             return -1;
391         }
392
393         if (write(helper_fd, &command, sizeof(command)) < 0) {
394             avahi_log_error("write() failed: %s\n", strerror(errno));
395             return -1;
396         }
397
398         if ((r = read(helper_fd, &c, sizeof(c))) < 0) {
399             avahi_log_error("read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
400             return -1;
401         }
402         
403         return 0;
404         
405     } else
406         
407         return unlink(fname);
408     
409 }