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