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