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