chiark / gitweb /
Initial support for BSD tunnel devices.
[tripe] / admin.c
1 /* -*-c-*-
2  *
3  * $Id: admin.c,v 1.3 2001/02/04 01:17:12 mdw Exp $
4  *
5  * Admin interface for configuration
6  *
7  * (c) 2001 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of Trivial IP Encryption (TrIPE).
13  *
14  * TrIPE is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  * 
19  * TrIPE is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * 
24  * You should have received a copy of the GNU General Public License
25  * along with TrIPE; if not, write to the Free Software Foundation,
26  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 /*----- Revision history --------------------------------------------------* 
30  *
31  * $Log: admin.c,v $
32  * Revision 1.3  2001/02/04 01:17:12  mdw
33  * The `DAEMON' notification to stdout is replaced by a warning.  The
34  * `DAEMON' and `QUIT' command send `OK' on successful completion.  Put
35  * assignment of sequence number in a T(...) guard.
36  *
37  * Revision 1.2  2001/02/03 22:40:29  mdw
38  * Put timer information into the entropy pool when packets are received
39  * and on similar events.  Reseed the generator on the interval timer.
40  *
41  * Revision 1.1  2001/02/03 20:26:37  mdw
42  * Initial checkin.
43  *
44  */
45
46 /*----- Header files ------------------------------------------------------*/
47
48 #include "tripe.h"
49
50 /*----- Global variables --------------------------------------------------*/
51
52 #ifndef NTRACE
53
54 const trace_opt tr_opts[] = {
55   { 't',        T_TUNNEL,       "tunnel events" },
56   { 'r',        T_PEER,         "peer events" },
57   { 'a',        T_ADMIN,        "admin interface" },
58   { 'p',        T_PACKET,       "packet contents" },
59   { 'c',        T_CRYPTO,       "crypto details" },
60   { 's',        T_KEYSET,       "symmetric keyset management" },
61   { 'x',        T_KEYEXCH,      "key exchange" },
62   { 'm',        T_KEYMGMT,      "key management" },
63   { 'A',        T_ALL,          "all of the above" },
64   { 0,          0,              0 }
65 };
66
67 unsigned tr_flags = 0;
68 #endif
69
70 /*----- Static variables --------------------------------------------------*/
71
72 static admin *admins;
73 static sel_file sock;
74 static const char *sockname;
75 static unsigned flags = 0;
76 static admin *a_stdin = 0;
77 static sig s_term, s_int, s_hup;
78
79 #define F_DAEMON 1u
80 #define F_INIT 2u
81
82 #define T_RESOLVE SEC(30)
83
84 /*----- Utility functions -------------------------------------------------*/
85
86 /* --- @a_write@ --- *
87  *
88  * Arguments:   @admin *a@ = admin connection to write to
89  *              @const char *fmt@ = pointer to format string
90  *              @...@ = other arguments
91  *
92  * Returns:     ---
93  *
94  * Use:         Sends a message to an admin connection.
95  */
96
97 static void a_write(admin *a, const char *fmt, ...)
98 {
99   va_list ap;
100   dstr d = DSTR_INIT;
101   va_start(ap, fmt);
102   dstr_vputf(&d, fmt, ap);
103   va_end(ap);
104   write(a->fd, d.buf, d.len);
105   dstr_destroy(&d);
106 }
107
108 /* --- @a_warn@ --- *
109  *
110  * Arguments:   @const char *fmt@ = pointer to format string
111  *              @...@ = other arguments
112  *
113  * Returns:     ---
114  *
115  * Use:         Informs all admin connections of a warning.
116  */
117
118 void a_warn(const char *fmt, ...)
119 {
120   va_list ap;
121   admin *a;
122   dstr d = DSTR_INIT;
123
124   if (flags & F_INIT)
125     dstr_puts(&d, "WARN ");
126   va_start(ap, fmt);
127   dstr_vputf(&d, fmt, ap);
128   va_end(ap);
129   if (!(flags & F_INIT))
130     moan("%s", d.buf);
131   else {
132     dstr_putc(&d, '\n');
133     for (a = admins; a; a = a->next)
134       write(a->fd, d.buf, d.len);
135   }
136   dstr_destroy(&d);
137 }
138
139 /* --- @a_trace@ --- *
140  *
141  * Arguments:   @const char *p@ = pointer to a buffer
142  *              @size_t sz@ = size of the buffer
143  *              @void *v@ = uninteresting pointer
144  *
145  * Returns:     ---
146  *
147  * Use:         Custom trace output handler.
148  */
149
150 #ifndef NTRACE
151 static void a_trace(const char *p, size_t sz, void *v)
152 {
153   dstr d = DSTR_INIT;
154   admin *a;
155
156   dstr_puts(&d, "TRACE ");
157   dstr_putm(&d, p, sz);
158   dstr_putc(&d, '\n');
159   for (a = admins; a; a = a->next)
160     write(a->fd, d.buf, d.len);
161   dstr_destroy(&d);  
162 }
163 #endif
164
165 /* --- @a_quit@ --- *
166  *
167  * Arguments:   ---
168  *
169  * Returns:     ---
170  *
171  * Use:         Shuts things down nicely.
172  */
173
174 void a_quit(void)
175 {
176   close(sock.fd);
177   unlink(sockname);
178   exit(0);
179 }
180
181 /* --- @a_sigdie@ --- *
182  *
183  * Arguments:   @int sig@ = signal number
184  *              @void *v@ = an uninteresting argument
185  *
186  * Returns:     ---
187  *
188  * Use          Shuts down on receipt of a fatal signal.
189  */
190
191 static void a_sigdie(int sig, void *v)
192 {
193   char *p;
194   char buf[20];
195
196   switch (sig) {
197     case SIGTERM:       p = "SIGTERM"; break;
198     case SIGINT:        p = "SIGINT"; break;
199     default:
200       sprintf(buf, "signal %i", sig);
201       p = buf;
202       break;
203   }
204   a_warn("shutting down on %s", p);
205   a_quit();
206 }
207
208 /* --- @a_sighup@ --- *
209  *
210  * Arguments:   @int sig@ = signal number
211  *              @void *v@ = an uninteresting argument
212  *
213  * Returns:     ---
214  *
215  * Use          Logs a message about SIGHUP not being useful.
216  */
217
218 static void a_sighup(int sig, void *v)
219 {
220   a_warn("received SIGHUP: ignoring");
221 }
222
223 /*----- Adding peers ------------------------------------------------------*/
224  
225 /* --- @a_resolve@ --- *
226  *
227  * Arguments:   @struct hostent *h@ = pointer to resolved hostname
228  *              @void *v@ = pointer to admin block
229  *
230  * Returns:     ---
231  *
232  * Use:         Handles a completed name resolution.
233  */
234
235 static void a_resolve(struct hostent *h, void *v)
236 {
237   admin *a = v;
238   T( trace(T_ADMIN, "admin: %u resolved", a->seq); )
239   TIMER;
240   sel_rmtimer(&a->t);
241   if (!h)
242     a_write(a, "ERR couldn't resolve hostname `%s'\n", a->paddr);
243   else if (p_find(a->pname))
244     a_write(a, "ERR peer `%s' already registered\n", a->pname);
245   else {
246     memcpy(&a->peer.sin.sin_addr, h->h_addr, sizeof(struct in_addr));
247     if (!p_create(a->pname, &a->peer.sa, a->sasz))
248       a_write(a, "ERR couldn't create peer\n");
249     else
250       a_write(a, "OK\n");
251   }
252   xfree(a->pname);
253   xfree(a->paddr);
254   a->pname = 0;
255   selbuf_enable(&a->b);
256 }
257
258 /* --- @a_timer@ --- *
259  *
260  * Arguments:   @struct timeval *tv@ = timer
261  *              @void *v@ = pointer to admin block
262  *
263  * Returns:     ---
264  *
265  * Use:         Times out a resolver.
266  */
267
268 static void a_timer(struct timeval *tv, void *v)
269 {
270   admin *a = v;
271   T( trace(T_ADMIN, "admin: %u resolver timeout", a->seq); )
272   bres_abort(&a->r);
273   a_write(a, "ERR timeout resolving `%s'\n", a->paddr);
274   xfree(a->pname);
275   xfree(a->paddr);
276   a->pname = 0;
277   selbuf_enable(&a->b);
278 }
279
280 /* --- @acmd_add@ --- *
281  *
282  * Arguments:   @admin *a@ = connection which requested the addition
283  *              @unsigned ac@ = argument count
284  *              @char *av[]@ = pointer to the argument list
285  *
286  * Returns:     ---
287  *
288  * Use:         Adds a new peer.
289  */
290
291 static void acmd_add(admin *a, unsigned ac, char *av[])
292 {
293   unsigned long pt;
294   struct timeval tv;
295   char *p;
296
297   /* --- Make sure someone's not got there already --- */
298
299   if (p_find(av[0])) {
300     a_write(a, "ERR peer `%s' already registered\n", av[0]);
301     return;
302   }
303
304   /* --- Fill in the easy bits of address --- */
305
306   BURN(a->peer);
307   a->peer.sin.sin_family = AF_INET;
308   a->sasz = sizeof(a->peer.sin);
309   pt = strtoul(av[2], &p, 0);
310   if (*p) {
311     struct servent *s = getservbyname(av[2], "udp");
312     if (!s) {
313       a_write(a, "ERR service `%s' not known\n", av[2]);
314       return;
315     }
316     pt = ntohs(s->s_port);
317   }
318   if (pt == 0 || pt >= 65536) {
319     a_write(a, "ERR bad port number %lu\n", pt);
320     return;
321   }
322   a->peer.sin.sin_port = htons(pt);
323
324   /* --- If the name is numeric, do it the easy way --- */
325   
326   if (inet_aton(av[1], &a->peer.sin.sin_addr)) {
327     if (!p_create(av[0], &a->peer.sa, a->sasz))
328       a_write(a, "ERR couldn't create peer\n");
329     else
330       a_write(a, "OK\n");
331     return;
332   }
333
334   /* --- Store everything for later and crank up the resolver --- *
335    *
336    * We disable the line buffer until the resolver completes (or times out).
337    * This prevents other commands on the same connection (though the rest of
338    * the system continues regardless), but makes life simpler for the client.
339    */
340
341   a->pname = xstrdup(av[0]);
342   a->paddr = xstrdup(av[1]);
343   selbuf_disable(&a->b);
344   gettimeofday(&tv, 0);
345   tv.tv_sec += T_RESOLVE;
346   sel_addtimer(&sel, &a->t, &tv, a_timer, a);
347   bres_byname(&a->r, a->paddr, a_resolve, a);
348   T( trace(T_ADMIN, "admin: %u resolving hostname `%s'",
349            a->seq, a->paddr); )
350 }
351
352 /*----- Administration commands -------------------------------------------*/
353
354 /* --- Miscellaneous commands --- */
355
356 #ifndef NTRACE
357
358 static void acmd_trace(admin *a, unsigned ac, char *av[])
359 {
360   if (!ac || strcmp(av[0], "?") == 0) {
361     const trace_opt *t;
362     a_write(a, "INFO Trace options:\n");
363     for (t = tr_opts; t->ch; t++) {
364       a_write(a, "INFO %c %c  %s\n",
365               t->ch, (tr_flags & t->f) == t->f ? '*' : ' ', t->help);
366     }
367   } else {
368     unsigned sense = 1;
369     unsigned f = tr_flags;
370     const trace_opt *tt;
371     char *p = av[0];
372
373     while (*p) {
374       switch (*p) {
375         case '+': sense = 1; break;
376         case '-': sense = 0; break;
377         default:
378           for (tt = tr_opts; tt->ch; tt++) {
379             if (tt->ch == *p) {
380               if (sense) f |= tt->f;
381               else f &= ~tt->f;
382               goto tropt_ok;
383             }
384           }
385           a_write(a, "ERR unknown trace option `%c'\n", *p);
386           return;
387         tropt_ok:;
388           break;
389       }
390       p++;
391     }
392     tr_flags = f;
393     trace_level(tr_flags);
394   }
395   a_write(a, "OK\n");
396 }
397
398 #endif
399
400 static void acmd_port(admin *a, unsigned ac, char *av[])
401 {
402   a_write(a, "INFO %u\nOK\n", p_port());
403 }
404
405 static void a_destroy(admin */*a*/);
406
407 static void acmd_daemon(admin *a, unsigned ac, char *av[])
408 {
409   if (flags & F_DAEMON)
410     a_write(a, "ERR already running as a daemon\n");
411   else {
412     a_warn("becoming a daemon");
413     if (a_stdin)
414       a_destroy(a_stdin);
415     if (u_daemon())
416       a_write(a, "ERR error becoming a daemon: %s", strerror(errno));
417     else {
418       flags |= F_DAEMON;
419       a_write(a, "OK\n");
420     }
421   }
422 }
423
424 static void acmd_list(admin *a, unsigned ac, char *av[])
425 {
426   peer *p;
427   for (p = p_first(); p; p = p_next(p))
428     a_write(a, "INFO %s\n", p_name(p));
429   a_write(a, "OK\n");
430 }
431
432 static void acmd_ifname(admin *a, unsigned ac, char *av[])
433 {
434   peer *p;
435
436   if ((p = p_find(av[0])) == 0)
437     a_write(a, "ERR peer `%s' not found\n", av[0]);
438   else
439     a_write(a, "INFO %s\nOK\n", p_ifname(p));
440 }
441
442 static void acmd_addr(admin *a, unsigned ac, char *av[])
443 {
444   peer *p;
445   const addr *ad;
446
447   if ((p = p_find(av[0])) == 0)
448     a_write(a, "ERR peer `%s' not found\n", av[0]);
449   else {
450     ad = p_addr(p);
451     assert(ad->sa.sa_family == AF_INET);
452     a_write(a, "INFO %s %u\nOK\n",
453             inet_ntoa(ad->sin.sin_addr),
454             (unsigned)ntohs(ad->sin.sin_port));
455   }
456 }
457
458 static void acmd_kill(admin *a, unsigned ac, char *av[])
459 {
460   peer *p;
461   if ((p = p_find(av[0])) == 0)
462     a_write(a, "ERR peer `%s' not found\n", av[0]);
463   else {
464     p_destroy(p);
465     a_write(a, "OK\n");
466   }
467 }
468
469 static void acmd_quit(admin *a, unsigned ac, char *av[])
470 {
471   a_warn("closing down on admin request");
472   a_write(a, "OK\n");
473   a_quit();
474 }
475
476 /* --- The command table and help --- */
477
478 typedef struct acmd {
479   const char *name;
480   const char *help;
481   unsigned argmin, argmax;
482   void (*func)(admin */*a*/, unsigned /*ac*/, char */*av*/[]);
483 } acmd;
484
485 static void acmd_help(admin */*a*/, unsigned /*ac*/, char */*av*/[]);
486
487 static const acmd acmdtab[] = {
488   { "help",     "HELP",                 0,      0,      acmd_help },
489 #ifndef NTRACE
490   { "trace",    "TRACE [options]",      0,      1,      acmd_trace },
491 #endif
492   { "port",     "PORT",                 0,      0,      acmd_port },
493   { "daemon",   "DAEMON",               0,      0,      acmd_daemon },
494   { "list",     "LIST",                 0,      0,      acmd_list },
495   { "ifname",   "IFNAME peer",          1,      1,      acmd_ifname },
496   { "addr",     "ADDR peer",            1,      1,      acmd_addr },
497   { "kill",     "KILL peer",            1,      1,      acmd_kill },
498   { "add",      "ADD peer addr port",   3,      3,      acmd_add },
499   { "quit",     "QUIT",                 0,      0,      acmd_quit },
500   { 0,          0,                      0,      0,      0 }
501 };
502
503 static void acmd_help(admin *a, unsigned ac, char *av[])
504 {
505   const acmd *c;
506   for (c = acmdtab; c->name; c++)
507     a_write(a, "INFO %s\n", c->help);
508   a_write(a, "OK\n");
509 }
510
511 /*----- Connection handling -----------------------------------------------*/
512
513 /* --- @a_destroy@ --- *
514  *
515  * Arguments:   @admin *a@ = pointer to an admin block
516  *
517  * Returns:     ---
518  *
519  * Use:         Destroys an admin block.
520  */
521
522 static void a_destroy(admin *a)
523 {
524   T( trace(T_ADMIN, "admin: destroying connection %u", a->seq); )
525   selbuf_destroy(&a->b);
526   if (a->b.reader.fd != a->fd)
527     close(a->b.reader.fd);
528   close(a->fd);
529   if (a->pname) {
530     xfree(a->pname);
531     xfree(a->paddr);
532     bres_abort(&a->r);
533     sel_rmtimer(&a->t);
534   }
535   if (a->next)
536     a->next->prev = a->prev;
537   if (a->prev)
538     a->prev->next = a->next;
539   else
540     admins = a->next;
541   if (a_stdin == a)
542     a_stdin = 0;
543   DESTROY(a);
544 }
545
546 /* --- @a_line@ --- *
547  *
548  * Arguments:   @char *p@ = pointer to the line read
549  *              @void *vp@ = pointer to my admin block
550  *
551  * Returns:     ---
552  *
553  * Use:         Handles a line of input.
554  */
555
556 static void a_line(char *p, void *vp)
557 {
558   admin *a = vp;
559   const acmd *c;
560   char *av[4];
561   size_t ac;
562
563   TIMER;
564   if (!p) {
565     a_destroy(a);
566     return;
567   }
568   ac = str_qsplit(p, av, 4, 0, STRF_QUOTE);
569   if (!ac)
570     return;
571   for (p = av[0]; *p; p++) *p = tolower((unsigned char)*p);
572   for (c = acmdtab; c->name; c++) {
573     if (strcmp(av[0], c->name) == 0) {
574       ac--;
575       if (c->argmin > ac || ac > c->argmax)
576         a_write(a, "ERR syntax: %s\n", c->help);
577       else
578         c->func(a, ac, av + 1);
579       return;
580     }
581   }
582   a_write(a, "ERR unknown command `%s'\n", av[0]);
583 }
584
585 /* --- @a_create@ --- *
586  *
587  * Arguments:   @int fd_in, fd_out@ = file descriptors to use
588  *
589  * Returns:     ---
590  *
591  * Use:         Creates a new admin connection.
592  */
593
594 void a_create(int fd_in, int fd_out)
595 {
596   admin *a = CREATE(admin);
597   T( static unsigned seq = 0;
598      a->seq = seq++; )
599   T( trace(T_ADMIN, "admin: accepted connection %u", a->seq); )
600   a->pname = 0;
601   if (fd_in == STDIN_FILENO)
602     a_stdin = a;
603   fdflags(fd_in, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
604   if (fd_out != fd_in)
605     fdflags(fd_out, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
606   a->fd = fd_out;
607   selbuf_init(&a->b, &sel, fd_in, a_line, a);
608   a->next = admins;
609   a->prev = 0;
610   if (admins)
611     admins->prev = a;
612   admins = a;
613 }
614
615 /* --- @a_accept@ --- *
616  *
617  * Arguments:   @int fd@ = file descriptor to accept
618  *              @unsigned mode@ = what to do
619  *              @void *v@ = uninteresting pointer
620  *
621  * Returns:     ---
622  *
623  * Use:         Accepts a new admin connection.
624  */
625
626 static void a_accept(int fd, unsigned mode, void *v)
627 {
628   int nfd;
629   struct sockaddr_un sun;
630   size_t sz = sizeof(sun);
631
632   if ((nfd = accept(fd, (struct sockaddr *)&sun, &sz)) < 0) {
633     a_warn("accept admin connection failed: %s", strerror(errno));
634     return;
635   }
636   a_create(nfd, nfd);
637 }
638
639 /* --- @a_daemon@ --- *
640  *
641  * Arguments:   ---
642  *
643  * Returns:     ---
644  *
645  * Use:         Informs the admin module that it's a daemon.
646  */
647
648 void a_daemon(void)
649 {
650   flags |= F_DAEMON;
651 }
652
653 /* --- @a_init@ --- *
654  *
655  * Arguments:   @const char *name@ = socket name to create
656  *
657  * Returns:     ---
658  *
659  * Use:         Creates the admin listening socket.
660  */
661
662 void a_init(const char *name)
663 {
664   int fd;
665   int n = 5;
666   struct sockaddr_un sun;
667   struct sigaction sa;
668   size_t sz;
669
670   /* --- Set up the socket address --- */
671
672   sz = strlen(name) + 1;
673   if (sz > sizeof(sun.sun_path))
674     die(EXIT_FAILURE, "socket name `%s' too long", name);
675   BURN(sun);
676   sun.sun_family = AF_UNIX;
677   memcpy(sun.sun_path, name, sz);
678   sz += offsetof(struct sockaddr_un, sun_path);
679
680   /* --- Attempt to bind to the socket --- */
681
682   umask(0077);
683 again:
684   if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
685     die(EXIT_FAILURE, "couldn't create socket: %s", strerror(errno));
686   if (bind(fd, (struct sockaddr *)&sun, sz) < 0) {
687     struct stat st;
688     int e = errno;
689     if (errno != EADDRINUSE) {
690       die(EXIT_FAILURE, "couldn't bind to address `%s': %s",
691           sun.sun_path, strerror(e));
692     }
693     if (!n)
694       die(EXIT_FAILURE, "too many retries; giving up");
695     n--;
696     if (!connect(fd, (struct sockaddr *)&sun, sz)) {
697       die(EXIT_FAILURE, "server already listening on admin socket `%s'",
698           sun.sun_path);
699     }
700     if (errno != ECONNREFUSED)
701       die(EXIT_FAILURE, "couldn't bind to address: %s", strerror(e));
702     if (stat(sun.sun_path, &st)) {
703       die(EXIT_FAILURE, "couldn't stat `%s': %s",
704           sun.sun_path, strerror(errno));
705     }
706     if (!S_ISSOCK(st.st_mode))
707       die(EXIT_FAILURE, "object `%s' isn't a socket", sun.sun_path);
708     T( trace(T_ADMIN, "stale socket found; removing it"); )
709     unlink(sun.sun_path);
710     close(fd);
711     goto again;
712   }
713   chmod(sun.sun_path, 0600);
714   fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
715   if (listen(fd, 5))
716     die(EXIT_FAILURE, "couldn't listen on socket: %s", strerror(errno));
717
718   /* --- Listen to the socket --- */
719
720   sel_initfile(&sel, &sock, fd, SEL_READ, a_accept, 0);
721   sel_addfile(&sock);
722   sockname = name;
723   bres_init(&sel);
724   T( trace_custom(a_trace, 0);
725      trace(T_ADMIN, "admin: enabled custom tracing"); )
726   flags |= F_INIT;
727
728   /* --- Set up signal handlers --- */
729
730   sig_add(&s_term, SIGTERM, a_sigdie, 0);
731   sig_add(&s_hup, SIGHUP, a_sighup, 0);
732   sigaction(SIGINT, 0, &sa);
733   if (sa.sa_handler != SIG_IGN)
734     sig_add(&s_int, SIGINT, a_sigdie, 0);
735 }
736
737 /*----- That's all, folks -------------------------------------------------*/