chiark / gitweb /
progs/pixie.c: Rename `log' function to `pxlog'.
[catacomb] / progs / pixie.c
1 /* -*-c-*-
2  *
3  * Passphrase pixie for Catacomb
4  *
5  * (c) 1999 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of Catacomb.
11  *
12  * Catacomb is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU Library General Public License as
14  * published by the Free Software Foundation; either version 2 of the
15  * License, or (at your option) any later version.
16  *
17  * Catacomb 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 Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public
23  * License along with Catacomb; if not, write to the Free
24  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25  * MA 02111-1307, USA.
26  */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 #include "config.h"
31
32 #include <assert.h>
33 #include <ctype.h>
34 #include <errno.h>
35 #include <signal.h>
36 #include <stdarg.h>
37 #include <stddef.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <time.h>
42
43 #include <sys/types.h>
44 #include <sys/time.h>
45 #include <unistd.h>
46 #include <sys/stat.h>
47 #include <sys/wait.h>
48 #include <pwd.h>
49 #include <fcntl.h>
50 #include <sys/ioctl.h>
51 #include <termios.h>
52 #include <syslog.h>
53
54 #include <sys/socket.h>
55 #include <sys/un.h>
56
57 #include <mLib/alloc.h>
58 #include <mLib/dstr.h>
59 #include <mLib/fdflags.h>
60 #include <mLib/mdwopt.h>
61 #include <mLib/quis.h>
62 #include <mLib/report.h>
63 #include <mLib/sel.h>
64 #include <mLib/selbuf.h>
65 #include <mLib/sig.h>
66 #include <mLib/str.h>
67 #include <mLib/sub.h>
68 #include <mLib/tv.h>
69
70 #include "arena.h"
71 #include "lmem.h"
72 #include "passphrase.h"
73 #include "pixie.h"
74
75 /*----- Static variables --------------------------------------------------*/
76
77 static unsigned long timeout = 900;
78 static sel_state sel;
79 static unsigned verbose = 1;
80 static const char *command = 0;
81 static lmem lm;
82 static unsigned flags = 0;
83
84 #define F_SYSLOG 1u
85 #define F_FETCH 2u
86
87 /*----- Event logging -----------------------------------------------------*/
88
89 /* --- @pxlog@ --- *
90  *
91  * Arguments:   @const char *p@ = @printf@-style format string
92  *              @...@ = extra arguments to fill in
93  *
94  * Returns:     ---
95  *
96  * Use:         Writes out a timestamped log message.
97  */
98
99 static void pxlog(const char *p, ...)
100 {
101   dstr d = DSTR_INIT;
102   va_list ap;
103
104   if (!(flags & F_SYSLOG)) {
105     time_t t = time(0);
106     struct tm *tm = localtime(&t);
107     DENSURE(&d, 64);
108     d.len += strftime(d.buf, d.sz, "%Y-%m-%d %H:%M:%S ", tm);
109   }
110   va_start(ap, p);
111   dstr_vputf(&d, p, &ap);
112   va_end(ap);
113
114   if (flags & F_SYSLOG)
115     syslog(LOG_NOTICE, "%s", d.buf);
116   else {
117     DPUTC(&d, '\n');
118     dstr_write(&d, stderr);
119   }
120   DDESTROY(&d);
121 }
122
123 /*----- Passphrase management ---------------------------------------------*/
124
125 /* --- Data structures --- */
126
127 typedef struct phrase {
128   struct phrase *next;
129   struct phrase *prev;
130   char *tag;
131   char *p;
132   unsigned long t;
133   sel_timer timer;
134   unsigned f;
135 } phrase;
136
137 /* --- Variables --- */
138
139 #define P_ROOT ((phrase *)&p_root)
140 static struct { phrase *next; phrase *prev; } p_root = { P_ROOT, P_ROOT };
141
142 /* --- @p_free@ --- *
143  *
144  * Arguments:   @phrase *p@ = pointer to phrase block
145  *
146  * Returns:     ---
147  *
148  * Use:         Frees a phrase block.
149  */
150
151 static void p_free(phrase *p)
152 {
153   if (p->t)
154     sel_rmtimer(&p->timer);
155   xfree(p->tag);
156   l_free(&lm, p->p);
157   p->next->prev = p->prev;
158   p->prev->next = p->next;
159   DESTROY(p);
160 }
161
162 /* --- @p_timer@ --- *
163  *
164  * Arguments:   @struct timeval *tv@ = current time
165  *              @void *p@ = pointer to phrase
166  *
167  * Returns:     ---
168  *
169  * Use:         Expires a passphrase.
170  */
171
172 static void p_timer(struct timeval *tv, void *p)
173 {
174   phrase *pp = p;
175   if (verbose)
176     pxlog("expiring passphrase `%s'", pp->tag);
177   p_free(pp);
178 }
179
180 /* --- @p_alloc@ --- *
181  *
182  * Arguments:   @size_t sz@ = amount of memory required
183  *
184  * Returns:     Pointer to allocated memory, or null.
185  *
186  * Use:         Allocates some locked memory, flushing old passphrases if
187  *              there's not enough space.
188  */
189
190 static void *p_alloc(size_t sz)
191 {
192   for (;;) {
193     char *p;
194     if ((p = l_alloc(&lm, sz)) != 0)
195       return (p);
196     if (P_ROOT->next == P_ROOT)
197       return (0);
198     if (verbose) {
199       pxlog("flushing passphrase `%s' to free up needed space",
200             p_head->tag);
201     }
202     p_free(P_ROOT->next);
203   }
204 }
205
206 /* --- @p_find@ --- *
207  *
208  * Arguments:   @const char *tag@ = pointer to tag to find
209  *
210  * Returns:     Pointer to passphrase block, or null.
211  *
212  * Use:         Finds a passphrase with a given tag.
213  */
214
215 static phrase *p_find(const char *tag)
216 {
217   phrase *p;
218
219   for (p = P_ROOT->next; p != P_ROOT; p = p->next) {
220     if (strcmp(p->tag, tag) == 0) {
221       if (p->t) {
222         struct timeval tv;
223         sel_rmtimer(&p->timer);
224         gettimeofday(&tv, 0);
225         tv.tv_sec += p->t;
226         sel_addtimer(&sel, &p->timer, &tv, p_timer, p);
227       }
228       p->next->prev = p->prev;
229       p->prev->next = p->next;
230       p->next = P_ROOT;
231       p->prev = P_ROOT->prev;
232       P_ROOT->prev->next = p;
233       P_ROOT->prev = p;
234       return (p);
235     }
236   }
237   return (0);
238 }
239
240 /* --- @p_add@ --- *
241  *
242  * Arguments:   @const char *tag@ = pointer to tag string
243  *              @const char *p@ = pointer to passphrase
244  *              @unsigned long t@ = expiry timeout
245  *
246  * Returns:     Pointer to newly-added passphrase.
247  *
248  * Use:         Adds a new passphrase.  The tag must not already exist.
249  */
250
251 static phrase *p_add(const char *tag, const char *p, unsigned long t)
252 {
253   size_t sz = strlen(p) + 1;
254   char *l = p_alloc(sz);
255   phrase *pp;
256
257   /* --- Make sure the locked memory was allocated --- */
258
259   if (!l)
260     return (0);
261
262   /* --- Fill in some other bits of the block --- */
263
264   pp = CREATE(phrase);
265   memcpy(l, p, sz);
266   pp->p = l;
267   pp->tag = xstrdup(tag);
268   pp->f = 0;
269
270   /* --- Set the timer --- */
271
272   pp->t = t;
273   if (t) {
274     struct timeval tv;
275     gettimeofday(&tv, 0);
276     tv.tv_sec += t;
277     sel_addtimer(&sel, &pp->timer, &tv, p_timer, pp);
278   }
279
280   /* --- Link the block into the chain --- */
281
282   pp->next = P_ROOT;
283   pp->prev = P_ROOT->prev;
284   P_ROOT->prev->next = pp;
285   P_ROOT->prev = pp;
286   return (pp);
287 }
288
289 /* --- @p_flush@ --- *
290  *
291  * Arguments:   @const char *tag@ = pointer to tag string, or zero for all
292  *
293  * Returns:     ---
294  *
295  * Use:         Immediately flushes either a single phrase or all of them.
296  */
297
298 static void p_flush(const char *tag)
299 {
300   phrase *p;
301
302   if (!tag && verbose > 1)
303     pxlog("flushing all passphrases");
304   p = P_ROOT->next;
305   while (p != P_ROOT) {
306     phrase *pp = p->next;
307     if (!tag)
308       p_free(p);
309     else if (strcmp(p->tag, tag) == 0) {
310       if (verbose > 1)
311         pxlog("flushing passphrase `%s'", tag);
312       p_free(p);
313       break;
314     }
315     p = pp;
316   }
317 }
318
319 /*----- Reading passphrases -----------------------------------------------*/
320
321 /* --- @p_request@ --- *
322  *
323  * Arguments:   @const char *msg@ = message string
324  *              @const char *tag@ = pointer to tag string
325  *              @char *buf@ = pointer to (locked) buffer
326  *              @size_t sz@ = size of buffer
327  *
328  * Returns:     Zero if all went well, nonzero otherwise.
329  *
330  * Use:         Requests a passphrase from the user.
331  */
332
333 static int p_request(const char *msg, const char *tag, char *buf, size_t sz)
334 {
335   /* --- If there's a passphrase-fetching command, run it --- */
336
337   if (command) {
338     dstr d = DSTR_INIT;
339     const char *p;
340     int fd[2];
341     pid_t kid;
342     int r;
343     int rc;
344
345     /* --- Substitute the prompt string into the command --- */
346
347     p = command;
348     for (;;) {
349       const char *q = strchr(p, '%');
350       if (!q || !q[1]) {
351         DPUTS(&d, p);
352         break;
353       }
354       DPUTM(&d, p, q - p);
355       p = q + 1;
356       switch (*p) {
357         case 'm':
358           DPUTS(&d, msg);
359           break;
360         case 't':
361           DPUTS(&d, tag);
362           break;
363         default:
364           DPUTC(&d, '%');
365           DPUTC(&d, *p);
366           break;
367       }
368       p++;
369     }
370     DPUTZ(&d);
371
372     /* --- Create a pipe and start a child process --- */
373
374     if (pipe(fd))
375       goto fail_1;
376     if ((kid = fork()) < 0)
377       goto fail_2;
378
379     /* --- Child process --- */
380
381     fflush(0);
382     if (kid == 0) {
383       if (dup2(fd[1], STDOUT_FILENO) < 0)
384         _exit(127);
385       close(fd[0]);
386       execl("/bin/sh", "sh", "-c", d.buf, (char *)0);
387       _exit(127);
388     }
389
390     /* --- Read the data back into my buffer --- */
391
392     close(fd[1]);
393     if ((r = read(fd[0], buf, sz - 1)) >= 0) {
394       char *q = memchr(buf, '\n', r);
395       if (!q)
396         q = buf + r;
397       *q = 0;
398     }
399     close(fd[0]);
400     waitpid(kid, &rc, 0);
401     dstr_destroy(&d);
402     if (r < 0 || rc != 0)
403       goto fail_0;
404     goto ok;
405
406     /* --- Tidy up when things go wrong --- */
407
408   fail_2:
409     close(fd[0]);
410     close(fd[1]);
411   fail_1:
412     dstr_destroy(&d);
413   fail_0:
414     return (-1);
415   }
416
417   /* --- Read a passphrase from the terminal --- *
418    *
419    * Use the standard Catacomb passphrase-reading function, so it'll read the
420    * passphrase from a file descriptor or something if the appropriate
421    * environment variable is set.
422    */
423
424   {
425     dstr d = DSTR_INIT;
426     int rc;
427     dstr_putf(&d, "%s %s: ", msg, tag);
428     rc = pixie_getpass(d.buf, buf, sz);
429     dstr_destroy(&d);
430     if (rc)
431       return (rc);
432     goto ok;
433   }
434
435   /* --- Sort out the buffer --- *
436    *
437    * Strip leading spaces.
438    */
439
440 ok: {
441     char *p = buf;
442     size_t len;
443     while (isspace((unsigned char)*p))
444       p++;
445     len = strlen(p);
446     memmove(buf, p, len);
447     p[len] = 0;
448   }
449
450   /* --- Done --- */
451
452   return (0);
453 }
454
455 /* --- @p_get@ --- *
456  *
457  * Arguments:   @const char **q@ = where to store the result
458  *              @const char *tag@ = pointer to tag string
459  *              @unsigned mode@ = reading mode (verify?)
460  *              @time_t exp@ = expiry time suggestion
461  *
462  * Returns:     Zero if successful, @-1@ on a read failure, or @+1@ if the
463  *              passphrase is missing and there is no fetcher.  (This will
464  *              always happen if there is no fetcher and @mode@ is
465  *              @PMODE_VERIFY@.
466  *
467  * Use:         Reads a passphrase from somewhere.
468  */
469
470 static int p_get(const char **q, const char *tag, unsigned mode, time_t exp)
471 {
472 #define LBUFSZ 1024
473
474   phrase *p;
475   char *pp = 0;
476
477   /* --- Write a log message --- */
478
479   if (verbose > 1)
480     pxlog("passphrase `%s' requested", tag);
481
482   /* --- If there is no fetcher, life is simpler --- */
483
484   if (!(flags & F_FETCH)) {
485     if (mode == PMODE_VERIFY)
486       return (+1);
487     if ((p = p_find(tag)) == 0)
488       return (+1);
489     *q = p->p;
490     return (0);
491   }
492
493   /* --- Try to find the phrase --- */
494
495   if (mode == PMODE_VERIFY)
496     p_flush(tag);
497   if (mode == PMODE_VERIFY || (p = p_find(tag)) == 0) {
498     if ((pp = p_alloc(LBUFSZ)) == 0)
499       goto fail;
500     if (p_request(mode == PMODE_READ ? "Passphrase" : "New passphrase",
501                   tag, pp, LBUFSZ) < 0)
502       goto fail;
503     p = p_add(tag, pp, exp);
504     if (!p)
505       goto fail;
506   }
507
508   /* --- If verification is requested, verify the passphrase --- */
509
510   if (mode == PMODE_VERIFY) {
511     if (!pp && (pp = p_alloc(LBUFSZ)) == 0)
512       goto fail;
513     if (p_request("Verify passphrase", tag, pp, LBUFSZ) < 0)
514       goto fail;
515     if (strcmp(pp, p->p) != 0) {
516       if (verbose)
517         pxlog("passphrases for `%s' don't match", tag);
518       p_free(p);
519       goto fail;
520     }
521   }
522
523   /* --- Tidy up and return the passphrase --- */
524
525   if (pp) {
526     memset(pp, 0, LBUFSZ);
527     l_free(&lm, pp);
528   }
529   *q = p->p;
530   return (0);
531
532   /* --- Tidy up if things went wrong --- */
533
534 fail:
535   if (pp) {
536     memset(pp, 0, LBUFSZ);
537     l_free(&lm, pp);
538   }
539   return (-1);
540
541 #undef LBUFSZ
542 }
543
544 /*----- Server command parsing --------------------------------------------*/
545
546 /* --- Data structures --- */
547
548 typedef struct pixserv {
549   selbuf b;
550   int fd;
551   sel_timer timer;
552   unsigned f;
553 } pixserv;
554
555 #define px_stdin 1u
556
557 #define PIXSERV_TIMEOUT 30
558
559 /* --- @pixserv_expire@ --- *
560  *
561  * Arguments:   @struct timeval *tv@ = pointer to current time
562  *              @void *p@ = pointer to server block
563  *
564  * Returns:     ---
565  *
566  * Use:         Expires a pixie connection if the remote end decides he's not
567  *              interested any more.
568  */
569
570 static void pixserv_expire(struct timeval *tv, void *p)
571 {
572   pixserv *px = p;
573   if (px->fd != px->b.reader.fd)
574     close(px->fd);
575   selbuf_destroy(&px->b);
576   close(px->b.reader.fd);
577   DESTROY(px);
578 }
579
580 /* --- @pixserv_write@ --- *
581  *
582  * Arguments:   @pixserv *px@ = pointer to server block
583  *              @const char *p@ = pointer to skeleton string
584  *              @...@ = other arguments to fill in
585  *
586  * Returns:     ---
587  *
588  * Use:         Formats a string and emits it to the output file.
589  */
590
591 static void pixserv_write(pixserv *px, const char *p, ...)
592 {
593   dstr d = DSTR_INIT;
594   va_list ap;
595
596   va_start(ap, p);
597   dstr_vputf(&d, p, &ap);
598   write(px->fd, d.buf, d.len);
599   va_end(ap);
600   dstr_destroy(&d);
601 }
602
603 /* --- @pixserv_timeout@ --- *
604  *
605  * Arguments:   @const char *p@ = pointer to timeout string
606  *
607  * Returns:     Timeout in seconds.
608  *
609  * Use:         Translates a string to a timeout value in seconds.
610  */
611
612 static unsigned long pixserv_timeout(const char *p)
613 {
614   unsigned long t;
615   char *q;
616
617   if (!p)
618     return (timeout);
619
620   t = strtoul(p, &q, 0);
621   switch (*q) {
622     case 'd': t *= 24;
623     case 'h': t *= 60;
624     case 'm': t *= 60;
625     case 's': if (q[1] != 0)
626       default:    t = 0;
627     case 0:   break;
628   }
629   return (t);
630 }
631
632 /* --- @pixserv_line@ --- *
633  *
634  * Arguments:   @char *s@ = pointer to the line read
635  *              @size_t len@ = length of the line
636  *              @void *p@ = pointer to server block
637  *
638  * Returns:     ---
639  *
640  * Use:         Handles a line read from the client.
641  */
642
643 static void pixserv_line(char *s, size_t len, void *p)
644 {
645   pixserv *px = p;
646   char *q, *qq;
647   unsigned mode;
648
649   /* --- Handle an end-of-file --- */
650
651   if (!(px->f & px_stdin))
652     sel_rmtimer(&px->timer);
653   if (!s) {
654     if (px->fd != px->b.reader.fd)
655       close(px->fd);
656     selbuf_destroy(&px->b);
657     close(px->b.reader.fd);
658     return;
659   }
660
661   /* --- Fiddle the timeout --- */
662
663   if (!(px->f & px_stdin)) {
664     struct timeval tv;
665     gettimeofday(&tv, 0);
666     tv.tv_sec += PIXSERV_TIMEOUT;
667     sel_addtimer(&sel, &px->timer, &tv, pixserv_expire, px);
668   }
669
670   /* --- Scan out the first word --- */
671
672   if ((q = str_getword(&s)) == 0)
673     return;
674   for (qq = q; *qq; qq++)
675     *qq = tolower((unsigned char)*qq);
676
677   /* --- Handle a help request --- */
678
679   if (strcmp(q, "help") == 0) {
680     pixserv_write(px, "\
681 INFO Commands supported:\n\
682 INFO HELP\n\
683 INFO LIST\n\
684 INFO PASS tag [expire]\n\
685 INFO VERIFY tag [expire]\n\
686 INFO FLUSH [tag]\n\
687 INFO SET tag [expire] -- phrase\n\
688 INFO QUIT\n\
689 OK\n\
690 ");
691   }
692
693   /* --- List the passphrases --- */
694
695   else if (strcmp(q, "list") == 0) {
696     phrase *p;
697
698     for (p = P_ROOT->next; p != P_ROOT; p = p->next) {
699       if (!p->t)
700         pixserv_write(px, "ITEM %s no-expire\n", p->tag);
701       else {
702         struct timeval tv;
703         gettimeofday(&tv, 0);
704         TV_SUB(&tv, &p->timer.tv, &tv);
705         pixserv_write(px, "ITEM %s %i\n", p->tag, tv.tv_sec);
706       }
707     }
708     pixserv_write(px, "OK\n");
709   }
710
711   /* --- Request a passphrase --- */
712
713   else if ((mode = PMODE_READ, strcmp(q, "pass") == 0) ||
714            (mode = PMODE_VERIFY, strcmp(q, "verify") == 0)) {
715     unsigned long t;
716     const char *p;
717     int rc;
718
719     if ((q = str_getword(&s)) == 0)
720       pixserv_write(px, "FAIL missing tag\n");
721     else if ((t = pixserv_timeout(s)) == 0)
722       pixserv_write(px, "FAIL bad timeout\n");
723     else {
724       rc = p_get(&p, q, mode, t > timeout ? timeout : t);
725       switch (rc) {
726         case 0:
727           pixserv_write(px, "OK %s\n", p);
728           break;
729         case -1:
730           pixserv_write(px, "FAIL error reading passphrase\n");
731           break;
732         case +1:
733           pixserv_write(px, "MISSING\n");
734           break;
735       }
736     }
737   }
738
739   /* --- Flush existing passphrases --- */
740
741   else if (strcmp(q, "flush") == 0) {
742     q = str_getword(&s);
743     p_flush(q);
744     pixserv_write(px, "OK\n");
745   }
746
747   /* --- Set a passphrase --- */
748
749   else if (strcmp(q, "set") == 0) {
750     char *tag;
751     unsigned long t;
752     if ((tag = str_getword(&s)) == 0)
753       pixserv_write(px, "FAIL missing tag\n");
754     else if ((q = str_getword(&s)) == 0)
755       pixserv_write(px, "FAIL no passphrase\n");
756     else {
757       if (strcmp(q, "--") != 0) {
758         t = pixserv_timeout(q);
759         q = str_getword(&s);
760       } else
761         t = pixserv_timeout(0);
762       if (!q)
763         pixserv_write(px, "FAIL no passphrase\n");
764       else if (strcmp(q, "--") != 0)
765         pixserv_write(px, "FAIL rubbish found before passphrase\n");
766       else {
767         p_flush(tag);
768         p_add(tag, s, t);
769         pixserv_write(px, "OK\n");
770       }
771     }
772   }
773
774   /* --- Shut the server down --- */
775
776   else if (strcmp(q, "quit") == 0) {
777     if (verbose)
778       pxlog("%s client requested shutdown",
779             px->f & px_stdin ? "local" : "remote");
780     pixserv_write(px, "OK\n");
781     exit(0);
782   }
783
784   /* --- Report an error for other commands --- */
785
786   else
787     pixserv_write(px, "FAIL unknown command `%s'\n", q);
788 }
789
790 /* --- @pixserv_create@ --- *
791  *
792  * Arguments:   @int fd@ = file descriptor to read from
793  *              @int ofd@ = file descriptor to write to
794  *
795  * Returns:     Pointer to the new connection.
796  *
797  * Use:         Creates a new Pixie server instance for a new connection.
798  */
799
800 static pixserv *pixserv_create(int fd, int ofd)
801 {
802   pixserv *px = CREATE(pixserv);
803   struct timeval tv;
804   fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
805   if (ofd != fd)
806     fdflags(ofd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
807   px->fd = ofd;
808   selbuf_init(&px->b, &sel, fd, pixserv_line, px);
809   px->b.b.a = arena_secure;
810   selbuf_setsize(&px->b, 1024);
811   gettimeofday(&tv, 0);
812   tv.tv_sec += PIXSERV_TIMEOUT;
813   sel_addtimer(&sel, &px->timer, &tv, pixserv_expire, px);
814   px->f = 0;
815   return (px);
816 }
817
818 /* --- @pixserv_accept@ --- *
819  *
820  * Arguments:   @int fd@ = file descriptor
821  *              @unsigned mode@ = what's happened
822  *              @void *p@ = an uninteresting argument
823  *
824  * Returns:     ---
825  *
826  * Use:         Accepts a new connection.
827  */
828
829 static void pixserv_accept(int fd, unsigned mode, void *p)
830 {
831   int nfd;
832   struct sockaddr_un sun;
833   size_t sunsz = sizeof(sun);
834
835   if (mode != SEL_READ)
836     return;
837   if ((nfd = accept(fd, (struct sockaddr *)&sun, &sunsz)) < 0) {
838     if (verbose && errno != EAGAIN && errno != EWOULDBLOCK &&
839         errno != ECONNABORTED && errno != EPROTO && errno != EINTR)
840       pxlog("new connection failed: %s", strerror(errno));
841     return;
842   }
843   pixserv_create(nfd, nfd);
844 }
845
846 /*----- Setting up the server ---------------------------------------------*/
847
848 /* --- @unlinksocket@ --- *
849  *
850  * Arguments:   ---
851  *
852  * Returns:     ---
853  *
854  * Use:         Tidies up the socket when it's finished with.
855  */
856
857 static char *sockpath;
858
859 static void unlinksocket(void)
860 {
861   unlink(sockpath);
862   l_purge(&lm);
863 }
864
865 /* --- @pix_sigdie@ --- *
866  *
867  * Arguments:   @int sig@ = signal number
868  *              @void *p@ = uninteresting argument
869  *
870  * Returns:     ---
871  *
872  * Use:         Shuts down the program after a fatal signal.
873  */
874
875 static void pix_sigdie(int sig, void *p)
876 {
877   if (verbose) {
878     char *p;
879     char buf[20];
880
881     switch (sig) {
882       case SIGTERM:     p = "SIGTERM"; break;
883       case SIGINT:      p = "SIGINT"; break;
884       default:
885         sprintf(buf, "signal %i", sig);
886         p = buf;
887         break;
888     }
889     pxlog("shutting down on %s", p);
890   }
891   exit(0);
892 }
893
894 /* --- @pix_sigflush@ --- *
895  *
896  * Arguments:   @int sig@ = signal number
897  *              @void *p@ = uninteresting argument
898  *
899  * Returns:     ---
900  *
901  * Use:         Flushes the passphrase cache on receipt of a signal.
902  */
903
904 static void pix_sigflush(int sig, void *p)
905 {
906   if (verbose) {
907     char *p;
908     char buf[20];
909
910     switch (sig) {
911       case SIGHUP:      p = "SIGHUP"; break;
912       case SIGQUIT:     p = "SIGQUIT"; break;
913       default:
914         sprintf(buf, "signal %i", sig);
915         p = buf;
916         break;
917     }
918     pxlog("received %s; flushing passphrases", p);
919   }
920   p_flush(0);
921 }
922
923 /* --- @pix_setup@ --- *
924  *
925  * Arguments:   @struct sockaddr_un *sun@ = pointer to address to use
926  *              @size_t sz@ = size of socket address
927  *
928  * Returns:     ---
929  *
930  * Use:         Sets up the pixie's Unix-domain socket.
931  */
932
933 static void pix_setup(struct sockaddr_un *sun, size_t sz)
934 {
935   int fd;
936
937   /* --- Set up the parent directory --- */
938
939   {
940     char *p = sun->sun_path;
941     char *q = strrchr(p, '/');
942
943     if (q) {
944       dstr d = DSTR_INIT;
945       struct stat st;
946
947       DPUTM(&d, p, q - p);
948       DPUTZ(&d);
949
950       mkdir(d.buf, 0700);
951       if (stat(d.buf, &st))
952         die(1, "couldn't stat `%s': %s", d.buf, strerror(errno));
953       if (!S_ISDIR(st.st_mode))
954         die(1, "object `%s' isn't a directory", d.buf);
955       if (st.st_mode & 0077)
956         die(1, "parent directory `%s' has group or world access", d.buf);
957       dstr_destroy(&d);
958     }
959   }
960
961   /* --- Initialize the socket --- */
962
963   {
964     int n = 5;
965     int e;
966
967     umask(0077);
968   again:
969     if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
970       die(1, "couldn't create socket: %s", strerror(errno));
971     if (bind(fd, (struct sockaddr *)sun, sz) < 0) {
972       e = errno;
973       if (errno != EADDRINUSE)
974         die(1, "couldn't bind to address: %s", strerror(e));
975       if (!n)
976         die(1, "too many retries; giving up");
977       n--;
978       if (connect(fd, (struct sockaddr *)sun, sz)) {
979         struct stat st;
980         if (errno != ECONNREFUSED)
981           die(1, "couldn't bind to address: %s", strerror(e));
982         if (stat(sun->sun_path, &st))
983           die(1, "couldn't stat `%s': %s", sun->sun_path, strerror(errno));
984         if (!S_ISSOCK(st.st_mode))
985           die(1, "object `%s' isn't a socket", sun->sun_path);
986         if (verbose)
987           pxlog("stale socket found; removing it");
988         unlink(sun->sun_path);
989         close(fd);
990       } else {
991         if (verbose)
992           pxlog("server already running; shutting it down");
993         write(fd, "QUIT\n", 5);
994         sleep(1);
995         close(fd);
996       }
997       goto again;
998     }
999     chmod(sun->sun_path, 0600);
1000     fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
1001     if (listen(fd, 5))
1002       die(1, "couldn't listen on socket: %s", strerror(errno));
1003   }
1004
1005   /* --- Set up the rest of the server --- */
1006
1007   {
1008     static sel_file serv;
1009     sockpath = sun->sun_path;
1010     atexit(unlinksocket);
1011     sel_initfile(&sel, &serv, fd, SEL_READ, pixserv_accept, 0);
1012     sel_addfile(&serv);
1013   }
1014 }
1015
1016 /*----- Client support code -----------------------------------------------*/
1017
1018 /* --- Variables --- */
1019
1020 static selbuf c_server, c_client;
1021 static unsigned c_flags = 0;
1022
1023 #define cf_uclose 1u
1024 #define cf_sclose 2u
1025 #define cf_cooked 4u
1026
1027 /* --- Line handler functions --- */
1028
1029 static void c_uline(char *s, size_t len, void *p)
1030 {
1031   if (!s) {
1032     selbuf_destroy(&c_client);
1033     shutdown(c_server.reader.fd, 1);
1034     c_flags |= cf_uclose;
1035   } else {
1036     s[len++] = '\n';
1037     write(c_server.reader.fd, s, len);
1038   }
1039 }
1040
1041 static void c_sline(char *s, size_t len, void *p)
1042 {
1043   if (!s) {
1044     selbuf_destroy(&c_server);
1045     if (!(c_flags & cf_uclose)) {
1046       moan("server closed the connection");
1047       selbuf_destroy(&c_client);
1048     }
1049     exit(0);
1050   }
1051   if (!(c_flags & cf_cooked))
1052     puts(s);
1053   else {
1054     char *q = str_getword(&s);
1055     if (strcmp(q, "FAIL") == 0)
1056       die(1, "%s", s);
1057     else if (strcmp(q, "INFO") == 0 ||
1058              strcmp(q, "ITEM") == 0)
1059       puts(s);
1060     else if (strcmp(q, "OK") == 0) {
1061       if (s && *s) puts(s);
1062     } else if (strcmp(q, "MISSING") == 0)
1063       ;
1064     else
1065       moan("unexpected output: %s %s", q, s);
1066   }
1067 }
1068
1069 /* --- @pix_client@ --- *
1070  *
1071  * Arguments:   @struct sockaddr_un *sun@ = pointer to socket address
1072  *              @size_t sz@ = size of socket address
1073  *              @char *argv[]@ = pointer to arguments to send
1074  *
1075  * Returns:     ---
1076  *
1077  * Use:         Performs client-side actions for the passphrase pixie.
1078  */
1079
1080 static void pix_client(struct sockaddr_un *sun, size_t sz, char *argv[])
1081 {
1082   int fd;
1083
1084   /* --- Dispose of locked memory --- */
1085
1086   l_destroy(&lm);
1087
1088   /* --- Open the socket --- */
1089
1090   if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
1091     die(1, "couldn't create socket: %s", strerror(errno));
1092   if (connect(fd, (struct sockaddr *)sun, sz))
1093     die(1, "couldn't connect to server: %s", strerror(errno));
1094   selbuf_init(&c_server, &sel, fd, c_sline, 0);
1095
1096   /* --- If there are any arguments, turn them into a string --- */
1097
1098   if (!*argv)
1099     selbuf_init(&c_client, &sel, STDIN_FILENO, c_uline, 0);
1100   else {
1101     dstr d = DSTR_INIT;
1102     DPUTS(&d, *argv++);
1103     while (*argv) {
1104       DPUTC(&d, ' ');
1105       DPUTS(&d, *argv++);
1106     }
1107     DPUTC(&d, '\n');
1108     write(fd, d.buf, d.len);
1109     shutdown(fd, 1);
1110     c_flags |= cf_uclose | cf_cooked;
1111     dstr_destroy(&d);
1112   }
1113
1114   /* --- And repeat --- */
1115
1116   for (;;) {
1117     if (sel_select(&sel))
1118       die(EXIT_FAILURE, "select error: %s", strerror(errno));
1119   }
1120 }
1121
1122 /*----- Main code ---------------------------------------------------------*/
1123
1124 /* --- @help@, @version@, @usage@ --- *
1125  *
1126  * Arguments:   @FILE *fp@ = stream to write on
1127  *
1128  * Returns:     ---
1129  *
1130  * Use:         Emit helpful messages.
1131  */
1132
1133 static void usage(FILE *fp)
1134 {
1135   pquis(fp, "\
1136 Usage:\n\
1137         $ [-qvfidl] [-c COMMAND] [-t TIMEOUT] [-s SOCKET]\n\
1138         $ [-s SOCKET] -C [COMMAND ARGS...]\n\
1139         $ [-s SOCKET] -P[P] TAG\n\
1140 ");
1141 }
1142
1143 static void version(FILE *fp)
1144 {
1145   pquis(fp, "$, Catacomb version " VERSION "\n");
1146 }
1147
1148 static void help(FILE *fp)
1149 {
1150   version(fp);
1151   fputc('\n', fp);
1152   usage(fp);
1153   pquis(fp, "\n\
1154 The Catacomb passphrase pixie collects and caches passphrases used to\n\
1155 protect important keys.  Options provided:\n\
1156 \n\
1157 -h, --help              Show this help text.\n\
1158 -V, --version           Show the program's version number.\n\
1159 -u, --usage             Show a (very) terse usage summary.\n\
1160 \n\
1161 -C, --client            Connect to a running pixie as a client.\n\
1162 -P, --passphrase        Request passphrase TAG and print to stdout.\n\
1163 -PP, --verify-passphrase\n\
1164                         Verify passphrase TAG and print to stdout.\n\
1165 \n\
1166 -q, --quiet             Emit fewer log messages.\n\
1167 -v, --version           Emit more log messages.\n\
1168 -s, --socket=FILE       Name the pixie's socket.\n\
1169 -c, --command=COMMAND   Shell command to read a passphrase.\n\
1170 -f, --fetch             Fetch passphrases from the terminal.\n\
1171 -t, --timeout=TIMEOUT   Length of time to retain a passphrase in memory.\n\
1172 -i, --interactive       Allow commands to be typed interactively.\n\
1173 -d, --daemon            Fork into the background after initialization.\n\
1174 -l, --syslog            Emit log messages to the system log.\n\
1175 \n\
1176 The COMMAND may contain `%m' and `%t' markers which are replaced by a\n\
1177 prompt message and the passphrase tag respectively.  The TIMEOUT is an\n\
1178 integer, optionally followed by `d', `h', `m' or `s' to specify units of\n\
1179 days, hours, minutes or seconds respectively.\n\
1180 \n\
1181 In client mode, if a command is specified on the command line, it is sent\n\
1182 to the running server; otherwise the program reads requests from stdin.\n\
1183 Responses from the pixie are written to stdout.  Send a HELP request for\n\
1184 a quick summary of the pixie communication protocol.\n\
1185 ");
1186 }
1187
1188 /* --- @main@ --- *
1189  *
1190  * Arguments:   @int argc@ = number of arguments
1191  *              @char *argv[]@ = vector of argument values
1192  *
1193  * Returns:     Zero if OK.
1194  *
1195  * Use:         Main program.  Listens on a socket and responds with a PGP
1196  *              passphrase when asked.
1197  */
1198
1199 int main(int argc, char *argv[])
1200 {
1201   char *path = 0;
1202   struct sockaddr_un *sun;
1203   size_t sz;
1204   unsigned f = 0;
1205
1206 #define f_bogus 1u
1207 #define f_client 2u
1208 #define f_stdin 4u
1209 #define f_daemon 8u
1210 #define f_syslog 16u
1211 #define f_fetch 32u
1212 #define f_verify 64u
1213
1214   /* --- Initialize libraries --- */
1215
1216   ego(argv[0]);
1217   sub_init();
1218
1219   /* --- Set up the locked memory area --- */
1220
1221   l_init(&lm, 16384);
1222   setuid(getuid());
1223
1224   /* --- Parse command line arguments --- */
1225
1226   for (;;) {
1227     static struct option opts[] = {
1228
1229       /* --- Standard GNUy help options --- */
1230
1231       { "help",         0,              0,      'h' },
1232       { "version",      0,              0,      'V' },
1233       { "usage",        0,              0,      'u' },
1234
1235       /* --- Other options --- */
1236
1237       { "quiet",        0,              0,      'q' },
1238       { "verbose",      0,              0,      'v' },
1239       { "client",       0,              0,      'C' },
1240       { "passphrase",   0,              0,      'P' },
1241       { "verify-passphrase",    0,      0,      '+' },
1242       { "socket",       OPTF_ARGREQ,    0,      's' },
1243       { "command",      OPTF_ARGREQ,    0,      'c' },
1244       { "fetch",        0,              0,      'f' },
1245       { "timeout",      OPTF_ARGREQ,    0,      't' },
1246       { "interactive",  0,              0,      'i' },
1247       { "stdin",        0,              0,      'i' },
1248       { "daemon",       0,              0,      'd' },
1249       { "log",          0,              0,      'l' },
1250       { "syslog",       0,              0,      'l' },
1251
1252       /* --- Magic terminator --- */
1253
1254       { 0,              0,              0,      0 }
1255     };
1256
1257     int i = mdwopt(argc, argv, "hVuqvCPs:c:ft:idl", opts, 0, 0, 0);
1258     if (i < 0)
1259       break;
1260
1261     switch (i) {
1262
1263       /* --- GNUy help options --- */
1264
1265       case 'h':
1266         help(stdout);
1267         exit(0);
1268       case 'V':
1269         version(stdout);
1270         exit(0);
1271       case 'u':
1272         usage(stdout);
1273         exit(0);
1274
1275       /* --- Other interesting things --- */
1276
1277       case 'q':
1278         if (verbose)
1279           verbose--;
1280         break;
1281       case 'v':
1282         verbose++;
1283         break;
1284       case 'C':
1285         f |= f_client;
1286         f &= ~f_fetch;
1287         break;
1288       case 'P':
1289         if (!(f & f_fetch))
1290           f |= f_fetch;
1291         else
1292           f |= f_verify;
1293         break;
1294       case '+':
1295         f |= f_fetch | f_verify;
1296         f &= ~f_client;
1297         break;
1298       case 's':
1299         path = optarg;
1300         break;
1301       case 't':
1302         if ((timeout = pixserv_timeout(optarg)) == 0)
1303           die(1, "bad timeout `%s'", optarg);
1304         break;
1305       case 'c':
1306         command = optarg;
1307         flags |= F_FETCH;
1308         break;
1309       case 'f':
1310         flags |= F_FETCH;
1311         break;
1312       case 'i':
1313         f |= f_stdin;
1314         break;
1315       case 'd':
1316         f |= f_daemon;
1317         break;
1318       case 'l':
1319         f |= f_syslog;
1320         break;
1321
1322       /* --- Something else --- */
1323
1324       default:
1325         f |= f_bogus;
1326         break;
1327     }
1328   }
1329
1330   if (f & f_bogus ||
1331       (optind < argc && !(f & (f_client|f_fetch))) ||
1332       ((f & f_fetch) && optind != argc - 1)) {
1333     usage(stderr);
1334     exit(1);
1335   }
1336
1337   /* --- Handle request for a passphrase --- */
1338
1339   if (f & f_fetch) {
1340     char *buf = l_alloc(&lm, 1024);
1341     passphrase_connect(path);
1342     if (passphrase_read(argv[optind],
1343                         (f & f_verify) ? PMODE_VERIFY : PMODE_READ,
1344                         buf, 1024))
1345       die(1, "failed to read passphrase: %s", strerror(errno));
1346     puts(buf);
1347     return (0);
1348   }
1349
1350   /* --- Set up the socket address --- */
1351
1352   sun = pixie_address(path, &sz);
1353
1354   /* --- Initialize selectory --- */
1355
1356   sel_init(&sel);
1357   signal(SIGPIPE, SIG_IGN);
1358
1359   /* --- Be a client if a client's wanted --- */
1360
1361   if (f & f_client)
1362     pix_client(sun, sz, argv + optind);
1363
1364   /* --- Open the syslog if requested --- */
1365
1366   if (f & f_syslog) {
1367     flags |= F_SYSLOG;
1368     openlog(QUIS, 0, LOG_DAEMON);
1369   }
1370
1371   /* --- Check on the locked memory area --- */
1372
1373   {
1374     dstr d = DSTR_INIT;
1375     int rc = l_report(&lm, &d);
1376     if (rc < 0)
1377       die(EXIT_FAILURE, d.buf);
1378     else if (rc && verbose) {
1379       pxlog(d.buf);
1380       pxlog("couldn't lock passphrase buffer");
1381     }
1382     dstr_destroy(&d);
1383     arena_setsecure(&lm.a);
1384   }
1385
1386   /* --- Set signal behaviours --- */
1387
1388   {
1389     static sig sigint, sigterm, sigquit, sighup;
1390     struct sigaction sa;
1391     sig_init(&sel);
1392     sigaction(SIGINT, 0, &sa);
1393     if (sa.sa_handler != SIG_IGN)
1394       sig_add(&sigint, SIGINT, pix_sigdie, 0);
1395     sig_add(&sigterm, SIGTERM, pix_sigdie, 0);
1396     sig_add(&sigquit, SIGQUIT, pix_sigflush, 0);
1397     sig_add(&sighup, SIGHUP, pix_sigflush, 0);
1398   }
1399
1400   /* --- Set up the server --- */
1401
1402   pix_setup(sun, sz);
1403   if (f & f_stdin) {
1404     pixserv *px = pixserv_create(STDIN_FILENO, STDOUT_FILENO);
1405     sel_rmtimer(&px->timer);
1406     px->f |= px_stdin;
1407   }
1408
1409   /* --- Fork into the background if requested --- */
1410
1411   if (f & f_daemon) {
1412     pid_t kid;
1413
1414     if (((f & f_stdin) &&
1415          (isatty(STDIN_FILENO) || isatty(STDOUT_FILENO))) ||
1416         (!command && (flags & F_FETCH)))
1417       die(1, "can't become a daemon if terminal required");
1418
1419     if ((kid = fork()) < 0)
1420       die(1, "fork failed: %s", strerror(errno));
1421     if (kid)
1422       _exit(0);
1423 #ifdef TIOCNOTTY
1424     {
1425       int fd;
1426       if ((fd = open("/dev/tty", O_RDONLY)) >= 0) {
1427         ioctl(fd, TIOCNOTTY);
1428         close(fd);
1429       }
1430     }
1431 #endif
1432     chdir("/");
1433     setsid();
1434
1435     if (fork() != 0)
1436       _exit(0);
1437   }
1438
1439   if (verbose)
1440     pxlog("initialized ok");
1441
1442   {
1443     int selerr = 0;
1444     for (;;) {
1445       if (!sel_select(&sel))
1446         selerr = 0;
1447       else if (errno != EINTR && errno != EAGAIN) {
1448         pxlog("error from select: %s", strerror(errno));
1449         selerr++;
1450         if (selerr > 8) {
1451           pxlog("too many consecutive select errors: bailing out");
1452           exit(EXIT_FAILURE);
1453         }
1454       }
1455     }
1456   }
1457   return (0);
1458 }
1459
1460 /*----- That's all, folks -------------------------------------------------*/