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