chiark / gitweb /
Merge branch 'privsep'
[tripe] / server / tun-slip.c
1 /* -*-c-*-
2  *
3  * Tunnel packets via SLIP
4  *
5  * (c) 2005 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 #define TUN_INTERNALS
30
31 #include "tripe.h"
32
33 /*----- Data structures ---------------------------------------------------*/
34
35 typedef struct slipif {
36   struct slipif *next;                  /* Next one in the list */
37   int ifd, ofd;                         /* File descriptors to talk on */
38   char *name;                           /* Interface name */
39   pid_t kid;                            /* Child process id */
40   unsigned f;                           /* Various flags */
41 #   define F_INUSE 1u                   /*   Interface is in use */
42 #   define F_DYNAMIC 2u                 /*   Interface found dynamically */
43 } slipif;
44
45 struct tunnel {
46   const tunnel_ops *ops;                /* Pointer to operations */
47   slipif *sl;                           /* My interface record */
48   sel_file f;                           /* Selector for SLIP tty */
49   struct peer *p;                       /* Pointer to my peer */
50   unsigned st;                          /* Current parser state */
51 #   define ST_ESC 1u                    /*   Last saw an escape character */
52 #   define ST_BAD 2u                    /*   This packet is malformed */
53 #   define ST_EOF 4u                    /*   File descriptor reported EOF */
54   size_t n;                             /* Number of bytes used in buffer */
55   octet buf[PKBUFSZ];                   /* Buffer for incoming data */
56 };
57
58 /*----- Static variables --------------------------------------------------*/
59
60 static slipif *slipifs;                 /* List of available interfaces */
61 static const char *slipcmd;             /* Script to make new interfaces */
62
63 /*----- Main code ---------------------------------------------------------*/
64
65 /* --- @t_read@ --- *
66  *
67  * Arguments:   @int fd@ = file descriptor to read
68  *              @unsigned mode@ = what's happened
69  *              @void *v@ = pointer to tunnel block
70  *
71  * Returns:     ---
72  *
73  * Use:         Reads data from the tunnel.
74  */
75
76 static void t_read(int fd, unsigned mode, void *v)
77 {
78   tunnel *t = v;
79   ssize_t n;
80   const octet *p, *l, *ll;
81   octet *q;
82   unsigned st;
83   octet o;
84   buf b;
85
86   /* --- Read the input data --- */
87
88   n = read(fd, buf_t, sizeof(buf_t));
89   if (n < 0) {
90     if (errno == EINTR ||
91 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
92         errno == EWOULDBLOCK ||
93 #endif
94         errno == EAGAIN)
95       return;
96     a_warn("TUN", "%s", p_ifname(t->p), "read-error", "?ERRNO", A_END);
97     return;
98   }
99   if (!n) {
100     a_warn("TUN", "%s", p_ifname(t->p), "slip", "eof", A_END);
101     t->st = ST_EOF;
102     sel_rmfile(&t->f);
103     return;
104   }
105   IF_TRACING(T_TUNNEL, {
106     trace_block(T_PACKET, "tun-slip: SLIP-encapsulated data",
107                 buf_t, n);
108   })
109
110   /* --- Decapsulate the packet --- */
111
112   for (p = buf_t, l = p + n, st = t->st,
113        q = t->buf + t->n, ll = t->buf + sizeof(t->buf);
114        p < l;
115        p++) {
116     o = *p;
117     switch (o) {
118       case SL_END:
119         if (st & ST_BAD)
120           ;
121         else if (st & ST_ESC)
122           a_warn("TUN", "%s", p_ifname(t->p), "slip", "escape-end", A_END);
123         else if (q == t->buf) {
124           T( trace(T_TUNNEL, "tun-slip: empty packet"); )
125         } else {
126           IF_TRACING(T_TUNNEL, {
127             trace(T_TUNNEL, "tun-slip: packet arrived");
128             trace_block(T_PACKET, "tun-slip: packet contents",
129                         t->buf, q - t->buf);
130           })
131           buf_init(&b, t->buf, q - t->buf);
132           p_tun(t->p, &b);
133         }
134         q = t->buf;
135         st &= ~(ST_ESC | ST_BAD);
136         break;
137       case SL_ESC:
138         if ((st & ST_ESC) && !(st & ST_BAD)) {
139           a_warn("TUN", "%s", p_ifname(t->p), "slip", "bad-escape", A_END);
140           st |= ST_BAD;
141         } else
142           st |= ST_ESC;
143         break;
144       case SL_ESCEND:
145         if (st & ST_ESC)
146           o = SL_END;
147         goto emit;
148       case SL_ESCESC:
149         if (st & ST_ESC)
150           o = SL_ESC;
151         goto emit;
152       default:
153         if ((st & ST_ESC) && !(st & ST_BAD)) {
154           a_warn("TUN", "%s", p_ifname(t->p), "slip", "bad-escape", A_END);
155           st |= ST_BAD;
156         }
157       emit:
158         if (!(st & ST_BAD)) {
159           if (q < ll)
160             *q++ = o;
161           else {
162             a_warn("TUN", "%s", p_ifname(t->p), "slip", "overflow", A_END);
163             st |= ST_BAD;
164           }
165         }
166         st &= ~ST_ESC;
167         break;
168     }
169   }
170
171   t->n = q - t->buf;
172   t->st = st;
173 }
174
175 /* --- @t_init@ --- *
176  *
177  * Arguments:   ---
178  *
179  * Returns:     ---
180  *
181  * Use:         Initializes the tunneling system.  Maybe this will require
182  *              opening file descriptors or something.
183  */
184
185 static void t_init(void)
186 {
187   char *p, *q;
188   dstr d = DSTR_INIT;
189   slipif *sl, **tail = &slipifs;
190   unsigned long uli, ulo;
191   size_t n;
192
193   if ((p = getenv("TRIPE_SLIPIF")) == 0)
194     return;
195
196   /* --- Build the list of available interfaces --- */
197
198   dstr_puts(&d, p);
199
200   p = d.buf;
201   for (;;) {
202     if (*p == '/' || *p == '.') {
203       slipcmd = p;
204       T( trace(T_TUNNEL, "tun-slip: declared slip command `%s'", slipcmd); )
205       break;
206     }
207     uli = strtoul(p, &q, 0);
208     if (uli > INT_MAX || q == p)
209       goto whine;
210     if (*q != ',')
211       ulo = uli;
212     else {
213       p = q + 1;
214       ulo = strtoul(p, &q, 0);
215       if (ulo > INT_MAX || q == p)
216         goto whine;
217     }
218     if (*q != '=' || (n = strcspn(q + 1, ":")) == 0)
219       goto whine;
220     sl = CREATE(slipif);
221     sl->next = 0;
222     sl->ifd = uli;
223     fdflags(sl->ifd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
224     fdflags(sl->ofd, O_NONBLOCK, 0, FD_CLOEXEC, FD_CLOEXEC);
225     sl->ofd = ulo;
226     sl->name = xmalloc(n + 1);
227     sl->kid = -1;
228     sl->f = 0;
229     memcpy(sl->name, q + 1, n);
230     sl->name[n] = 0;
231     *tail = sl;
232     tail = &sl->next;
233     T( trace(T_TUNNEL, "tun-slip: declared slipif %d,%d=%s",
234              sl->ifd, sl->ofd, sl->name); )
235     p = q + n + 1;
236     if (!*p)
237       break;
238     p++;
239   }
240   return;
241
242 whine:
243   moan("bad slip interface list");
244 }
245
246 /* --- @t_create@ --- *
247  *
248  * Arguments:   @peer *p@ = pointer to peer block
249  *              @int fd@ = file descriptor of tunnel device (unused)
250  *              @char **ifn@ = where to put the interface name
251  *
252  * Returns:     A tunnel block if it worked, or null on failure.
253  *
254  * Use:         Initializes a new tunnel.
255  */
256
257 static tunnel *t_create(peer *p, int fd, char **ifn)
258 {
259   slipif *sl = 0;
260   int pin[2] = { -1, -1 }, pout[2] = { -1, -1 };
261   pid_t kid = -1;
262   dstr d = DSTR_INIT;
263   unsigned char ch;
264   tunnel *t;
265   static const char end[] = { SL_END, SL_END };
266
267   /* --- Try to find a spare static interface --- */
268
269   for (sl = slipifs; sl; sl = sl->next) {
270     if (!(sl->f & F_INUSE)) {
271       T( trace(T_TUNNEL, "tun-slip: %s using static slipif %s",
272                p_name(p), sl->name); )
273       goto found;
274     }
275   }
276
277   /* --- If no dynamic interfaces are available, give up --- */
278
279   if (!slipcmd) {
280     a_warn("TUN", "-", "slip", "no-slip-interfaces", A_END);
281     goto fail;
282   }
283
284   /* --- Fork off a child process to create a dynamic SLIP interface --- */
285
286   if (pipe(pin) || pipe(pout)) {
287     a_warn("TUN", "-", "slip", "pipe-error", "?ERRNO", A_END);
288     goto fail;
289   }
290   if ((kid = fork()) < 0) {
291     a_warn("TUN", "-", "slip", "fork-error", "?ERRNO", A_END);
292     goto fail;
293   }
294   if (!kid) {
295     close(pin[1]);
296     close(pout[0]);
297     dup2(pin[0], STDIN_FILENO);
298     dup2(pout[1], STDOUT_FILENO);
299     execlp(slipcmd, slipcmd, p_name(p), (char *)0);
300     _exit(127);
301   }
302
303   /* --- Read the interface name --- */
304
305   sl = CREATE(slipif);
306   close(pin[0]); pin[0] = -1;
307   close(pout[1]); pout[1] = -1;
308   for (;;) {
309     errno = EIO;
310     if (read(pout[0], &ch, 1) != 1 || ch == SL_END) {
311       a_warn("TUN", "-", "slip", "read-ifname-failed", "?ERRNO", A_END);
312       goto fail;
313     }
314     if (ch == '\n')
315       break;
316     DPUTC(&d, (char)ch);
317   }
318   DPUTZ(&d);
319   sl->name = xstrdup(d.buf);
320   sl->ifd = pout[0];
321   sl->ofd = pin[1];
322   sl->kid = kid;
323   sl->next = 0;
324   sl->f = F_DYNAMIC;
325   T( trace(T_TUNNEL, "tun-slip: %s using dynamic slipif %s",
326            p_name(p), sl->name); )
327   fdflags(pout[0], O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
328   fdflags(pin[1], O_NONBLOCK, 0, FD_CLOEXEC, FD_CLOEXEC);
329
330   /* --- Set up the new tunnel --- */
331
332 found:
333   t = CREATE(tunnel);
334   t->ops = &tun_slip;
335   t->p = p;
336   t->sl = sl;
337   t->st = 0;
338   t->n = 0;
339   sl->f |= F_INUSE;
340   sel_initfile(&sel, &t->f, sl->ifd, SEL_READ, t_read, t);
341   sel_addfile(&t->f);
342   write(sl->ofd, end, sizeof(end));
343   *ifn = xstrdup(sl->name);
344   dstr_destroy(&d);
345   return (t);
346
347   /* --- Tidy up after a failure --- */
348
349 fail:
350 #define CLOSE(fd) do if (fd != -1) close(fd); while (0)
351   CLOSE(pin[0]); CLOSE(pout[0]);
352   CLOSE(pin[1]); CLOSE(pout[1]);
353 #undef CLOSE
354   if (kid != -1) kill(kid, SIGTERM);
355   if (sl && (sl->f & F_DYNAMIC)) DESTROY(sl);
356   dstr_destroy(&d);
357   return (0);
358 }
359
360 /* --- @t_setifname@ --- *
361  *
362  * Arguments:   @tunnel *t@ = pointer to tunnel block
363  *              @const char *ifn@ = new interface name
364  *
365  * Returns:     ---
366  *
367  * Use:         Updates the interface name of a slip interface.
368  */
369
370 static void t_setifname(tunnel *t, const char *ifn)
371   { xfree(t->sl->name); t->sl->name = xstrdup(ifn); }
372
373 /* --- @t_inject@ --- *
374  *
375  * Arguments:   @tunnel *t@ = pointer to tunnel block
376  *              @buf *b@ = buffer to send
377  *
378  * Returns:     ---
379  *
380  * Use:         Injects a packet into the local network stack.
381  */
382
383 static void t_inject(tunnel *t, buf *b)
384 {
385   octet buf[PKBUFSZ * 2 + 2];
386   const octet *p, *l;
387   octet *q;
388
389   IF_TRACING(T_TUNNEL, {
390     trace(T_TUNNEL, "tun-slip: inject decrypted packet");
391     trace_block(T_PACKET, "tun-slip: packet contents", BBASE(b), BLEN(b));
392   })
393
394   q = buf;
395   *q++ = SL_END;
396   for (p = BBASE(b), l = BCUR(b); p < l; p++) {
397     switch (*p) {
398       case SL_END: *q++ = SL_ESC; *q++ = SL_ESCEND; break;
399       case SL_ESC: *q++ = SL_ESC; *q++ = SL_ESCESC; break;
400       default: *q++ = *p; break;
401     }
402   }
403   *q++ = SL_END;
404   IF_TRACING(T_TUNNEL, {
405     trace_block(T_PACKET, "tun-slip: SLIP-encapsulated contents",
406                 buf, q - buf);
407   })
408   write(t->sl->ofd, buf, q - buf);
409 }
410
411 /* --- @t_destroy@ --- *
412  *
413  * Arguments:   @tunnel *t@ = pointer to tunnel block
414  *
415  * Returns:     ---
416  *
417  * Use:         Destroys a tunnel.
418  */
419
420 static void t_destroy(tunnel *t)
421 {
422   slipif *sl = t->sl;
423
424   /* --- If it reported EOF, leave it out-of-action --- */
425
426   if (!(t->st & ST_EOF)) {
427     sel_rmfile(&t->f);
428     sl->f &= ~F_INUSE;
429   }
430   if (sl && (sl->f & F_DYNAMIC)) {
431     T( trace(T_TUNNEL, "tun-slip: releasing dynamic slipif %s", sl->name); )
432     close(sl->ofd);
433     close(sl->ifd);
434     kill(sl->kid, SIGTERM);
435     xfree(sl->name);
436     DESTROY(sl);
437   }
438   DESTROY(t);
439 }
440
441 const tunnel_ops tun_slip = {
442   "slip",
443   0,
444   t_init,
445   t_create,
446   t_setifname,
447   t_inject,
448   t_destroy
449 };
450
451 /*----- That's all, folks -------------------------------------------------*/