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