chiark / gitweb /
mon/tripemon.in: Show per-peer crypto details in peer info sheet.
[tripe] / priv / helper.c
1 /* -*-c-*-
2  *
3  * Privilege-separated helper
4  *
5  * (c) 2008 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of Trivial IP Encryption (TrIPE).
11  *
12  * TrIPE is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * TrIPE is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with TrIPE; if not, write to the Free Software Foundation,
24  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25  */
26
27 /*----- Header files ------------------------------------------------------*/
28
29 #include "priv.h"
30
31 /*----- Helper-side utilities ---------------------------------------------*/
32
33 /* --- @lose@ --- *
34  *
35  * Arguments:   @const char *excuse@ = what went wrong
36  *
37  * Returns:     Doesn't.
38  *
39  * Use:         Reports a fatal error and quits.
40  */
41
42 static void lose(const char *excuse)
43 {
44   moan("helper process bailing out: %s; error: %s",
45        excuse,
46        errno == -1 ? "Unexpected EOF" : strerror(errno));
47   _exit(127);
48 }
49
50 /*----- Diagnostic functions ----------------------------------------------*/
51
52 /* --- @itrace@ --- *
53  *
54  * Arguments:   @unsigned mask@ = trace mask to check
55  *              @const char *fmt@ = message format
56  *              @...@ = values for placeholders
57  *
58  * Returns:     ---
59  *
60  * Use:         Writes a trace message.
61  */
62
63 #ifndef NTRACE
64
65 static void PRINTF_LIKE(2, 3) itrace(unsigned mask, const char *fmt, ...)
66 {
67   va_list ap;
68   dstr d = DSTR_INIT;
69
70   va_start(ap, fmt);
71   dstr_vputf(&d, fmt, &ap);
72   if (pc_putuint(PS_TRACE) ||
73       pc_putuint(mask) ||
74       pc_putsz(d.len) ||
75       pc_put(d.buf, d.len))
76     lose("write (trace)");
77   va_end(ap);
78   dstr_destroy(&d);
79 }
80
81 #endif
82
83 /* --- @warn@ --- *
84  *
85  * Arguments:   @const char *fmt@ = message format
86  *              @...@ = values for placeholders
87  *
88  * Returns:     ---
89  *
90  * Use:         Writes a warning message.
91  */
92
93 #define A_END ((char *)0)
94
95 static void EXECL_LIKE(0) IGNORABLE warn(const char *fmt, ...)
96 {
97   va_list ap;
98   dstr d = DSTR_INIT, dd = DSTR_INIT;
99
100   va_start(ap, fmt);
101   while (fmt) {
102     if (*fmt == '?') {
103       if (strcmp(fmt, "?ERRNO") == 0) {
104         dstr_putf(&d, " E%d", errno);
105         u_quotify(&d, strerror(errno));
106       } else
107         abort();
108     } else {
109       DRESET(&dd);
110       dstr_vputf(&dd, fmt, &ap);
111       u_quotify(&d, dd.buf);
112     }
113     fmt = va_arg(ap, const char *);
114   }
115   va_end(ap);
116
117   if (pc_putuint(PS_WARN) ||
118       pc_putsz(d.len) ||
119       pc_put(d.buf, d.len))
120     lose("write (warn)");
121
122   dstr_destroy(&d);
123   dstr_destroy(&dd);
124 }
125
126 /*----- Tunnel drivers ----------------------------------------------------*/
127
128 /* --- @topen_DRIVER@ --- *
129  *
130  * Arguments:   @char **ifn@ = where to put the interface name
131  *
132  * Returns:     A file descriptor, or @-1@ on failure.
133  *
134  * Use:         Opens a tunnel device.
135  */
136
137 #ifdef TUN_LINUX
138
139 #include <sys/ioctl.h>
140 #include <linux/if.h>
141 #include <linux/if_tun.h>
142
143 static int topen_linux(char **ifn)
144 {
145   int fd;
146   struct ifreq iff;
147
148   if ((fd = open("/dev/net/tun", O_RDWR)) < 0) {
149     warn("TUN", "-", "linux",
150          "open-error", "/dev/net/tun", "?ERRNO",
151          A_END);
152     return (-1);
153   }
154   memset(&iff, 0, sizeof(iff));
155   iff.ifr_name[0] = 0;
156   iff.ifr_flags = IFF_TUN | IFF_NO_PI;
157   if (ioctl(fd, TUNSETIFF, &iff) < 0) {
158     warn("TUN", "-", "linux", "config-error", "?ERRNO", A_END);
159     close(fd);
160     return (-1);
161   }
162   iff.ifr_name[IFNAMSIZ - 1] = 0;
163   *ifn = xstrdup(iff.ifr_name);
164   return (fd);
165 }
166
167 #endif
168
169 #ifdef TUN_BSD
170
171 static int topen_bsd(char **ifn)
172 {
173   int fd;
174   unsigned n;
175   char buf[16];
176
177   n = 0;
178   for (;;) {
179     sprintf(buf, "/dev/tun%u", n);
180     if ((fd = open(buf, O_RDWR)) >= 0)
181       break;
182     switch (errno) {
183       case EBUSY:
184         T( itrace(T_PRIVSEP, "tunnel device %u busy: skipping", n); )
185         break;
186       case ENOENT:
187         warn("TUN", "-", "bsd", "no-tunnel-devices", A_END);
188        return (-1);
189       default:
190         warn("TUN", "-", "open-error", "%s", buf, "?ERRNO", A_END);
191         break;
192     }
193     n++;
194   }
195   return (fd);
196 }
197
198 #endif
199
200 #ifdef TUN_UNET
201
202 #include <sys/ioctl.h>
203 #include <linux/if.h>
204 #include <unet.h>
205
206 static int topen_unet(char **ifn)
207 {
208   int fd;
209   int f;
210   struct unet_info uni;
211
212   if ((fd = open("/dev/unet", O_RDWR)) < 0) {
213     warn("TUN", "-", "unet", "open-error", "/dev/unet", "?ERRNO", A_END);
214     goto fail_0;
215   }
216   if ((f = ioctl(fd, UNIOCGIFFLAGS)) < 0 ||
217       ioctl(fd, UNIOCSIFFLAGS, f | IFF_POINTOPOINT)) {
218     warn("TUN", "-", "unet", "config-error", "?ERRNO", A_END);
219     goto fail_1;
220   }
221   if (ioctl(fd, UNIOCGINFO, &uni)) {
222     warn("TUN", "-", "unet", "getinfo-error", "?ERRNO", A_END);
223     goto fail_1;
224   }
225   *ifn = xstrdup(uni.uni_ifname);
226   return (fd);
227
228 fail_1:
229   close(fd);
230 fail_0:
231   return (-1);
232 }
233
234 #endif
235
236 static const struct tunnel {
237   const char *name;
238   int (*open)(char **);
239 } tunnels[] = {
240 #ifdef TUN_LINUX
241   { "linux", topen_linux },
242 #endif
243 #ifdef TUN_BSD
244   { "bsd", topen_bsd },
245 #endif
246 #ifdef TUN_UNET
247   { "unet", topen_unet },
248 #endif
249   { 0, 0 }
250 };
251
252 /*----- Helper process core -----------------------------------------------*/
253
254 int main(int argc, char *argv[])
255 {
256   struct sockaddr_un sun;
257   socklen_t slen = sizeof(sun);
258   unsigned rq;
259   dstr d = DSTR_INIT;
260   const struct tunnel *t;
261   char *ifn = 0;
262   int fd;
263   ssize_t n;
264
265   ego(argv[0]);
266   if (argc != 1 ||
267       getpeername(0, (struct sockaddr *)&sun, &slen) ||
268       sun.sun_family != AF_UNIX)
269     die(EXIT_FAILURE, "please do not run this program again.");
270
271   for (;;) {
272     if (pc_getuint(&rq)) {
273       if (errno == -1) break;
274       else lose("read (main)");
275     }
276     switch (rq) {
277       case PS_TUNRQ:
278         DRESET(&d);
279         if (pc_getstring(&d)) lose("read (tunnel)");
280         for (t = tunnels;; t++) {
281           if (!t->name) lose("unknown tunnel");
282           if (strcmp(d.buf, t->name) == 0) break;
283         }
284         T( itrace(T_PRIVSEP,
285                     "privsep: received request for %s tunnel",
286                     t->name); )
287         if ((fd = t->open(&ifn)) < 0)
288           goto err;
289         rq = PS_TUNFD;
290         n = fdpass_send(pc_fd, fd, &rq, sizeof(rq)); close(fd);
291         if (n < 0) { xfree(ifn); goto err; }
292         else if (n < sizeof(rq)) lose("partial write (fd-pass)");
293         if (pc_putstring(ifn)) lose("write (ifname)");
294         xfree(ifn);
295         break;
296       err:
297         if (pc_putuint(PS_TUNERR) || pc_puterr(errno)) lose("write (error)");
298         break;
299       default:
300         lose("bad request");
301         break;
302     }
303   }
304   _exit(0);
305 }
306
307 /*----- That's all, folks -------------------------------------------------*/