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