chiark / gitweb /
Initial revision
[pixie] / pixie.c
... / ...
CommitLineData
1/* -*-c-*-
2 *
3 * $Id: pixie.c,v 1.1 1999/10/23 10:58:49 mdw Exp $
4 *
5 * New, improved PGP pixie for auto-pgp
6 *
7 * (c) 1999 Mark Wooding
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * PGP pixie is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * PGP pixie is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with PGP pixie; if not, write to the Free Software Foundation,
24 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26
27/*----- Revision history --------------------------------------------------*
28 *
29 * $Log: pixie.c,v $
30 * Revision 1.1 1999/10/23 10:58:49 mdw
31 * Initial revision
32 *
33 */
34
35/*----- Header files ------------------------------------------------------*/
36
37#include <errno.h>
38#include <signal.h>
39#include <stdarg.h>
40#include <stddef.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <time.h>
45
46#include <sys/types.h>
47#include <sys/time.h>
48#include <unistd.h>
49#include <sys/stat.h>
50#include <sys/wait.h>
51#include <pwd.h>
52#include <fcntl.h>
53#include <termios.h>
54
55#ifdef HAVE_MLOCK
56# include <sys/mman.h>
57#endif
58
59#include <sys/socket.h>
60#include <sys/un.h>
61
62#include "mdwopt.h"
63
64/*----- Magic constants ---------------------------------------------------*/
65
66#define PIXIE_BUFSZ 1024 /* Passphrase buffer size */
67#define PIXIE_TIMEOUT 300 /* Default timeout (in seconds) */
68
69#define PIXIE_SOCKET "pass-socket"
70
71/*----- Static variables --------------------------------------------------*/
72
73static char *pass;
74static size_t passlen = 0;
75static int sigfd_out;
76static unsigned flags;
77
78enum {
79 f_pass = 1u,
80 f_xgetline = 2u,
81 f_getpass = 4u,
82 f_goodbuf = 8u,
83 f_bogus = 128u
84};
85
86/*----- Library code ------------------------------------------------------*/
87
88const char *pn__name = "<UNNAMED>"; /* Program name */
89#define QUIS pn__name
90
91/* --- @quis@ --- *
92 *
93 * Arguments: ---
94 *
95 * Returns: Pointer to the program name.
96 *
97 * Use: Returns the program name.
98 */
99
100const char *quis(void) { return (QUIS); }
101
102/* --- @ego@ --- *
103 *
104 * Arguments: @const char *p@ = pointer to program name
105 *
106 * Returns: ---
107 *
108 * Use: Tells mLib what the program's name is.
109 */
110
111#ifndef PATHSEP
112# if defined(__riscos)
113# define PATHSEP '.'
114# elif defined(__unix) || defined(unix)
115# define PATHSEP '/'
116# else
117# define PATHSEP '\\'
118# endif
119#endif
120
121void ego(const char *p)
122{
123 const char *q = p;
124 while (*q) {
125 if (*q++ == PATHSEP)
126 p = q;
127 }
128 if (*p == '-')
129 p++;
130 pn__name = p;
131}
132
133#undef PATHSEP
134
135/* --- @pquis@ --- *
136 *
137 * Arguments: @FILE *fp@ = output stream to write on
138 * @const char *p@ = pointer to string to write
139 *
140 * Returns: Zero if everything worked, EOF if not.
141 *
142 * Use: Writes the string @p@ to the output stream @fp@. Occurrences
143 * of the character `$' in @p@ are replaced by the program name
144 * as reported by @quis@. A `$$' is replaced by a single `$'
145 * sign.
146 */
147
148int pquis(FILE *fp, const char *p)
149{
150 size_t sz;
151
152 while (*p) {
153 sz = strcspn(p, "$");
154 if (sz) {
155 if (fwrite(p, 1, sz, fp) < sz)
156 return (EOF);
157 p += sz;
158 }
159 if (*p == '$') {
160 p++;
161 if (*p == '$') {
162 if (fputc('$', fp) == EOF)
163 return (EOF);
164 p++;
165 } else {
166 if (fputs(pn__name, fp) == EOF)
167 return (EOF);
168 }
169 }
170 }
171 return (0);
172}
173
174/* --- @die@ --- *
175 *
176 * Arguments: @int status@ = exit status to return
177 * @const char *f@ = a @printf@-style format string
178 * @...@ = other arguments
179 *
180 * Returns: Never.
181 *
182 * Use: Reports an error and exits. Like @moan@ above, only more
183 * permanent.
184 */
185
186void die(int status, const char *f, ...)
187{
188 va_list ap;
189 va_start(ap, f);
190 fprintf(stderr, "%s: ", QUIS);
191 vfprintf(stderr, f, ap);
192 va_end(ap);
193 putc('\n', stderr);
194 exit(status);
195}
196
197/* --- @fdflags@ --- *
198 *
199 * Arguments: @int fd@ = file descriptor to fiddle with
200 * @unsigned fbic, fxor@ = file flags to set and clear
201 * @unsigned fdbic, fdxor@ = descriptor flags to set and clear
202 *
203 * Returns: Zero if successful, @-1@ if not.
204 *
205 * Use: Sets file descriptor flags in what is, I hope, an obvious
206 * way.
207 */
208
209int fdflags(int fd, unsigned fbic, unsigned fxor,
210 unsigned fdbic, unsigned fdxor)
211{
212 int f;
213
214 if ((f = fcntl(fd, F_GETFL)) == -1 ||
215 fcntl(fd, F_SETFL, (f & ~fbic) ^ fxor) == -1 ||
216 (f = fcntl(fd, F_GETFD)) == -1 ||
217 fcntl(fd, F_SETFD, (f & ~fdbic) ^ fdxor) == -1)
218 return (-1);
219 return (0);
220}
221
222/* --- Timeval manipulation macros --- */
223
224#define MILLION 1000000
225
226#define TV_ADD(dst, a, b) TV_ADDL(dst, a, (b)->tv_sec, (b)->tv_usec)
227
228#define TV_ADDL(dst, a, sec, usec) do { \
229 (dst)->tv_sec = (a)->tv_sec + (sec); \
230 (dst)->tv_usec = (a)->tv_usec + (usec); \
231 if ((dst)->tv_usec >= MILLION) { \
232 (dst)->tv_usec -= MILLION; \
233 (dst)->tv_sec++; \
234 } \
235} while (0)
236
237#define TV_SUB(dst, a, b) TV_SUBL(dst, a, (b)->tv_sec, (b)->tv_usec)
238
239#define TV_SUBL(dst, a, sec, usec) do { \
240 (dst)->tv_sec = (a)->tv_sec - (sec); \
241 if ((a)->tv_usec >= (usec)) \
242 (dst)->tv_usec = (a)->tv_usec - (usec); \
243 else { \
244 (dst)->tv_usec = (a)->tv_usec + MILLION - (usec); \
245 (dst)->tv_sec--; \
246 } \
247} while (0)
248
249#define TV_CMP(a, op, b) ((a)->tv_sec == (b)->tv_sec ? \
250 (a)->tv_usec op (b)->tv_usec : \
251 (a)->tv_sec op (b)->tv_sec)
252
253/*----- Main code ---------------------------------------------------------*/
254
255/* --- @log@ --- *
256 *
257 * Arguments: @const char *p@ = @printf@-style format string
258 * @...@ = extra arguments to fill in
259 *
260 * Returns: ---
261 *
262 * Use: Writes out a timestamped log message.
263 */
264
265static void log(const char *p, ...)
266{
267 char b[32];
268 va_list ap;
269 time_t t = time(0);
270 struct tm *tm = localtime(&t);
271
272 strftime(b, sizeof(b), "%Y-%m-%d %H:%M:%S", tm);
273 fprintf(stderr, "%s: %s ", QUIS, b);
274 va_start(ap, p);
275 vfprintf(stderr, p, ap);
276 va_end(ap);
277 fputc('\n', stderr);
278}
279
280/* --- @sigwrite@ --- *
281 *
282 * Arguments: @int sig@ = signal number
283 *
284 * Returns: ---
285 *
286 * Use: Handles signals. It writes the signal number to a pipe and
287 * exits. It's possible for signals to be lost if the pipe is
288 * full. This isn't likely enough to be worth caring about.
289 * The implementation in mLib's `sig.c' does the job right but
290 * it's rather more effort.
291 */
292
293static void sigwrite(int sig)
294{
295 int e = errno;
296 char c = sig;
297 write(sigfd_out, &c, 1);
298 errno = e;
299}
300
301/* --- @readpass@ --- *
302 *
303 * Arguments: @int fd@ = file descriptor to read from
304 *
305 * Returns: 0 if OK, -1 if not.
306 *
307 * Use: Reads a line from a file descriptor. It continues reading
308 * buffers until it gets a newline character. If the buffer
309 * becomes full, a newline is inserted and no more data is
310 * read. This might cause confusion.
311 */
312
313static int readpass(int fd)
314{
315 int r;
316 char *p = pass;
317 char *q;
318 size_t sz = PIXIE_BUFSZ;
319
320 for (;;) {
321 r = read(fd, p, sz);
322 if (r < 0)
323 return (-1);
324 if (r == 0) {
325 q = p + r;
326 break;
327 }
328 if ((q = memchr(p, '\n', r)) != 0) {
329 q++;
330 break;
331 }
332 sz -= r;
333 p += r;
334 if (!sz) {
335 q = p;
336 p[-1] = '\n';
337 break;
338 }
339 }
340
341 passlen = q - pass;
342 return (0);
343}
344
345/* --- @get_pass@ --- *
346 *
347 * Arguments: ---
348 *
349 * Returns: 0 if OK, -1 if it failed.
350 *
351 * Use: Reads a passphrase from somewhere. The data from the
352 * passphrase goes straight into the @pass@ buffer without
353 * touching any other memory. Of course, if @xgetline@ is used,
354 * it might end up in unprotected memory there. That's a shame,
355 * but @xgetline@ is a much shorter-lived process than this one
356 * so it shouldn't matter as much.
357 */
358
359static int get_pass(void)
360{
361#ifdef PATH_XGETLINE
362 if (flags & f_xgetline) {
363 int fd[2];
364 pid_t kid;
365 int r;
366
367 /* --- Do everything by hand --- *
368 *
369 * I could, I suppose, use @popen@. However, (a) that involves a shell
370 * which is extra overhead and makes passing arguments with spaces a
371 * little trickier; and (b) it uses @stdio@ buffers, which might get
372 * swapped to disk.
373 */
374
375 if (pipe(fd))
376 return (-1);
377 kid = fork();
378 if (kid < 0)
379 return (-1);
380 if (kid == 0) {
381 dup2(fd[1], STDOUT_FILENO);
382 close(fd[0]);
383 close(fd[1]);
384 execlp(PATH_XGETLINE, "xgetline",
385 "-i", "-tPGP pixie", "-pPGP passphrase:", (char *)0);
386 _exit(127);
387 }
388 close(fd[1]);
389 r = readpass(fd[0]);
390 close(fd[0]);
391 waitpid(kid, 0, 0);
392 return (r);
393 } else
394#endif
395 {
396 struct termios o, n;
397 int fd;
398 int r;
399 char prompt[] = "PGP passphrase: ";
400 char nl = '\n';
401
402 /* --- Do this by hand --- *
403 *
404 * I could use @getpass@, but that puts the passphrase in its own memory
405 * rather than mine, so I'd have to scrub it out manually. This is
406 * probably just as good if you don't mind fiddling with @termios@.
407 * Also, the GNU version uses @stdio@ streams to read from the terminal,
408 * which might be considered a Bad Thing.
409 */
410
411 if ((fd = open("/dev/tty", O_RDWR)) < 0)
412 return (-1);
413 if (tcgetattr(fd, &o))
414 return (-1);
415 n = o;
416 n.c_lflag &= ~(ECHO | ISIG);
417 if (tcsetattr(fd, TCSAFLUSH, &n))
418 return (-1);
419 write(fd, prompt, sizeof(prompt) - 1);
420 r = readpass(fd);
421 tcsetattr(fd, TCSAFLUSH, &o);
422 write(fd, &nl, 1);
423 close(fd);
424 return (r);
425 }
426}
427
428/* --- @help@, @version@ @usage@ --- *
429 *
430 * Arguments: @FILE *fp@ = stream to write on
431 *
432 * Returns: ---
433 *
434 * Use: Emit helpful messages.
435 */
436
437static void usage(FILE *fp)
438{
439 pquis(fp, "Usage: $ [-xqv] [-t timeout] [-d dir] [socket]\n");
440}
441
442static void version(FILE *fp)
443{
444 pquis(fp, "$ version " VERSION "\n");
445}
446
447static void help(FILE *fp)
448{
449 version(fp);
450 fputc('\n', fp);
451 usage(fp);
452 pquis(fp, "\n\
453The passphrase pixie remembers a PGP passphrase and passes it on to\n\
454clients which connect to a Unix-domain socket.\n\
455\n\
456The pixie will forget a passphrase after a certain amount of time. The\n\
457duration of the pixie's memory is configurable using the `-t' option, and\n\
458the default is 5 minutes. By giving a timeout of zero, the pixie can be\n\
459endowed with a perfect memory.\n\
460\n\
461The pixie attempts to lock its passphrase buffer into physical memory. If\n\
462this doesn't work (e.g., your operating system doesn't support this\n\
463feature, or you have insufficient privilege) a warning is emitted.\n\
464\n\
465Options available are:\n\
466\n\
467-h, --help Show this help text.\n\
468-V, --version Show the pixie's version number.\n\
469-u, --usage Show a uselessly terse usage message.\n\
470\n\
471"
472#ifdef PATH_XGETLINE
473"\
474-x, --x11 Run `xgetline' to read a passphrase.\n\
475+x, --no-x11 Don't run `xgetline' to read a passphrase.\n\
476"
477#endif
478"\
479-d, --directory=DIR Make secure directory DIR and change to it.\n\
480-q, --quiet Don't emit so many messages.\n\
481-v, --verbose Emit more messages.\n\
482");
483}
484
485/* --- @main@ --- *
486 *
487 * Arguments: @int argc@ = number of arguments
488 * @char *argv[]@ = vector of argument values
489 *
490 * Returns: Zero if OK.
491 *
492 * Use: Main program. Listens on a socket and responds with a PGP
493 * passphrase when asked.
494 */
495
496int main(int argc, char *argv[])
497{
498 char *dir = 0;
499 int fd;
500 int sigfd_in;
501 unsigned verbose = 1;
502 char *sock = 0;
503 unsigned long timeout = PIXIE_TIMEOUT;
504 char *emsg = 0;
505 int elock = 0;
506
507 ego(argv[0]);
508
509 /* --- Try making a secure locked passphrase buffer --- *
510 *
511 * Drop privileges before emitting diagnostic messages.
512 */
513
514#ifdef HAVE_MLOCK
515
516 /* --- Memory-map a page from somewhere --- */
517
518# ifdef MAP_ANON
519
520 pass = mmap(0, PIXIE_BUFSZ, PROT_READ | PROT_WRITE,
521 MAP_PRIVATE | MAP_ANON, -1, 0);
522
523# else
524
525 if ((fd = open("/dev/zero", O_RDWR)) < 0) {
526 emsg = "couldn't open `/dev/zero': %s";
527 elock = errno;
528 } else {
529 pass = mmap(0, PIXIE_BUFSZ, PROT_READ | PROT_WRITE,
530 MAP_PRIVATE, fd, 0);
531 close(fd);
532 }
533
534# endif
535
536 /* --- Lock the page in memory --- *
537 *
538 * Why does @mmap@ return such a stupid result if it fails?
539 */
540
541 if (pass == 0 || pass == MAP_FAILED) {
542 emsg = "couldn't map a passphrase buffer: %s";
543 elock = errno;
544 pass = 0;
545 } else if (mlock(pass, PIXIE_BUFSZ)) {
546 emsg = "couldn't lock passphrase buffer: %s";
547 elock = errno;
548 munmap(pass, PIXIE_BUFSZ);
549 pass = 0;
550 } else
551 flags |= f_goodbuf;
552
553#endif
554
555 /* --- Make a standard passphrase buffer --- */
556
557 setuid(getuid());
558
559#ifdef HAVE_MLOCK
560 if (!pass)
561#endif
562 {
563 if ((pass = malloc(PIXIE_BUFSZ)) == 0)
564 die(1, "not enough memory for passphrase buffer");
565 }
566
567 /* --- Parse options --- */
568
569 for (;;) {
570 static struct option opts[] = {
571
572 /* --- GNUey help options --- */
573
574 { "help", 0, 0, 'h' },
575 { "usage", 0, 0, 'u' },
576 { "version", 0, 0, 'V' },
577
578 /* --- Other options --- */
579
580 { "timeout", OPTF_ARGREQ, 0, 't' },
581#ifdef PATH_XGETLINE
582 { "xgetline", OPTF_NEGATE, 0, 'x' },
583 { "x11", OPTF_NEGATE, 0, 'x' },
584#endif
585 { "directory", OPTF_ARGREQ, 0, 'd' },
586 { "quiet", 0, 0, 'q' },
587 { "verbose", 0, 0, 'v' },
588
589 /* --- Magic end marker --- */
590
591 { 0, 0, 0, 0 }
592 };
593
594#ifdef PATH_XGETLINE
595# define XOPTS "x+"
596#else
597# define XOPTS
598#endif
599
600 int i = mdwopt(argc, argv, "huV" XOPTS "t:d:qv",
601 opts, 0, 0, OPTF_NEGATION);
602
603#undef XOPTS
604
605 if (i < 0)
606 break;
607 switch (i) {
608 case 'h':
609 help(stdout);
610 exit(0);
611 case 'V':
612 version(stdout);
613 exit(0);
614 case 'u':
615 usage(stdout);
616 exit(0);
617 case 't': {
618 char *p;
619 timeout = strtoul(optarg, &p, 0);
620 switch (*p) {
621 case 'd': timeout *= 24;
622 case 'h': timeout *= 60;
623 case 'm': timeout *= 60;
624 case 's': case 0: break;
625 default:
626 die(1, "unrecognized suffix character `%c'", *p);
627 break;
628 }
629 } break;
630#ifdef PATH_XGETLINE
631 case 'x':
632 flags |= f_xgetline;
633 flags &= ~f_getpass;
634 break;
635 case 'x' | OPTF_NEGATED:
636 flags |= f_getpass;
637 flags &= ~f_xgetline;
638 break;
639#endif
640 case 'd':
641 dir = optarg;
642 break;
643 case 'q':
644 if (verbose > 0)
645 verbose--;
646 break;
647 case 'v':
648 verbose++;
649 break;
650 default:
651 flags |= f_bogus;
652 break;
653 }
654 }
655
656 if (optind < argc)
657 sock = argv[optind++];
658
659 if (optind < argc)
660 flags |= f_bogus;
661
662 if (flags & f_bogus) {
663 usage(stderr);
664 exit(1);
665 }
666
667 /* --- Sort out how to request the passphrase --- */
668
669#ifdef PATH_XGETLINE
670 if ((flags & (f_xgetline | f_getpass)) == 0) {
671 if (isatty(STDIN_FILENO))
672 flags |= f_getpass;
673 else
674 flags |= f_xgetline;
675 }
676#endif
677
678 /* --- Make the socket directory --- *
679 *
680 * Be very paranoid about the directory. Very paranoid indeed.
681 */
682
683 if (dir) {
684 struct stat st;
685
686 if (chdir(dir)) {
687 if (errno != ENOENT) {
688 die(1, "couldn't change directory to `%s': %s",
689 dir, strerror(errno));
690 }
691 if (mkdir(dir, 0700))
692 die(1, "couldn't create directory `%s': %s", dir, strerror(errno));
693 if (chdir(dir)) {
694 die(1, "couldn't change directory to `%s': %s",
695 dir, strerror(errno));
696 }
697 if (verbose > 1)
698 log("created directory `%s'", dir);
699 }
700
701 if (stat(".", &st))
702 die(1, "couldn't stat directory `%s': %s", dir, strerror(errno));
703 if ((st.st_mode & 07777) != 0700) {
704 die(1, "directory `%s' has mode %04o; should be 0700",
705 dir, st.st_mode & 07777);
706 }
707 if (st.st_uid != getuid()) {
708 struct passwd *pw = getpwuid(st.st_uid);
709 char b[16];
710 char *p;
711
712 if (pw)
713 p = pw->pw_name;
714 else {
715 sprintf(b, "uid `%i'", st.st_uid);
716 p = b;
717 }
718 die(1, "directory `%s' owned by %s; should be you", dir, p);
719 }
720
721 if (verbose > 2)
722 log("directory `%s' checked out OK", dir);
723 }
724
725 /* --- A little argument checking --- */
726
727 if (!sock) {
728 if (dir)
729 sock = PIXIE_SOCKET;
730 else
731 die(1, "no socket filename given");
732 }
733
734 /* --- Create and bind the socket --- */
735
736 {
737 size_t len = strlen(sock) + 1;
738 size_t sz = offsetof(struct sockaddr_un, sun_path) + len;
739 struct sockaddr_un *sun = malloc(sz);
740 unsigned u = umask(077);
741
742 /* --- Create the file descriptor --- */
743
744 if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
745 die(1, "couldn't create socket: %s", strerror(errno));
746 if (fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC))
747 die(1, "couldn't configure socket: %s", strerror(errno));
748
749 /* --- Set up the address --- */
750
751 memset(sun, 0, sz);
752 sun->sun_family = AF_UNIX;
753 strcpy(sun->sun_path, sock);
754
755 /* --- Bind to the address --- */
756
757 if (bind(fd, (struct sockaddr *)sun, sz))
758 die(1, "couldn't bind to socket `%s': %s", sock, strerror(errno));
759 free(sun);
760 if (listen(fd, 5))
761 die(1, "couldn't listen on socket: %s", strerror(errno));
762 umask(u);
763 }
764
765 /* --- Set signals up --- *
766 *
767 * I'm using Dan Bernstein's self-pipe trick to catch signals in the main
768 * code. See http://pobox.com/~djb/docs/selfpipe.html
769 */
770
771 {
772 static int sig[] = { SIGINT, SIGTERM, SIGHUP, SIGQUIT, 0 };
773 struct sigaction sa;
774 int i;
775 int pfd[2];
776
777 /* --- Create the signal pipe --- */
778
779 if (pipe(pfd))
780 die(1, "couldn't create pipe: %s", strerror(errno));
781
782 if (fdflags(pfd[0], O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC) ||
783 fdflags(pfd[1], O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC))
784 die(1, "couldn't configure pipe attributes: %s", strerror(errno));
785
786 sigfd_in = pfd[0];
787 sigfd_out = pfd[1];
788
789 /* --- Set up the signal handlers --- */
790
791 sa.sa_handler = sigwrite;
792 sa.sa_flags = 0;
793#ifdef SA_RESTART
794 sa.sa_flags |= SA_RESTART;
795#endif
796 sigemptyset(&sa.sa_mask);
797
798 for (i = 0; sig[i]; i++) {
799 struct sigaction osa;
800 if (sigaction(sig[i], 0, &osa) == 0 &&
801 osa.sa_handler != SIG_IGN)
802 sigaction(sig[i], &sa, 0);
803 }
804 }
805
806 /* --- Now listen, and wait --- */
807
808 {
809 int maxfd;
810 fd_set fds;
811 struct timeval tv, now, when, *tvp;
812
813 if (fd > sigfd_in)
814 maxfd = fd + 1;
815 else
816 maxfd = sigfd_in + 1;
817
818 if (flags & f_goodbuf) {
819 if (verbose > 1)
820 log("passphrase buffer created and locked OK");
821 } else {
822 if (emsg && verbose > 1)
823 log(emsg, strerror(elock));
824 else if (verbose)
825 log("couldn't create locked passphrase buffer");
826 }
827
828 if (verbose > 1)
829 log("passphrase pixie initialized OK");
830
831 for (;;) {
832
833 /* --- Set up the file descriptors --- */
834
835 FD_ZERO(&fds);
836 FD_SET(fd, &fds);
837 FD_SET(sigfd_in, &fds);
838
839 /* --- Set up the timeout --- */
840
841 if (!timeout || !(flags & f_pass))
842 tvp = 0;
843 else {
844 gettimeofday(&now, 0);
845 TV_SUB(&tv, &when, &now);
846 tvp = &tv;
847 }
848
849 /* --- Wait for something interesting to happen --- */
850
851 if (select(maxfd, &fds, 0, 0, tvp) < 0) {
852 if (errno == EINTR)
853 continue;
854 die(1, "error from select: %s", strerror(errno));
855 }
856
857 /* --- Act on a signal --- */
858
859 if (FD_ISSET(sigfd_in, &fds)) {
860 char buf[256];
861 int r;
862 sigset_t ss;
863
864 /* --- Go through each signal in turn --- *
865 *
866 * Don't try to respond to duplicates.
867 */
868
869 sigemptyset(&ss);
870 while ((r = read(sigfd_in, buf, sizeof(buf))) > 0) {
871 char *p = buf;
872
873 /* --- A buffer of signals has arrived; grind through it --- */
874
875 for (p = buf; r; r--, p++) {
876
877 /* --- If this signal has been seen, skip on to the next --- */
878
879 if (sigismember(&ss, *p))
880 continue;
881 sigaddset(&ss, *p);
882
883 switch (*p) {
884 const char *s;
885
886 /* --- Various interesting signals --- */
887
888 case SIGINT:
889 s = "SIGINT";
890 goto closedown;
891 case SIGTERM:
892 s = "SIGTERM";
893 goto closedown;
894 case SIGHUP:
895 s = "SIGHUP";
896 goto clear;
897 case SIGQUIT:
898 s = "SIGQUIT";
899 goto clear;
900
901 /* --- Shut down the program if requested --- */
902
903 closedown:
904 if (verbose > 1)
905 log("closing down on %s", s);
906 goto done;
907
908 /* --- Clear the passphrase if requested --- */
909
910 clear:
911 if (flags & f_pass) {
912 memset(pass, 0, PIXIE_BUFSZ);
913 passlen = 0;
914 flags &= ~f_pass;
915 if (verbose)
916 log("caught %s: passphrase cleared", s);
917 } else if (verbose > 1)
918 log("caught %s: passphrase not set", s);
919 break;
920
921 /* --- Other signals which aren't so interesting --- */
922
923 default:
924 if (verbose > 2)
925 log("caught unexpected signal %i: ignoring it", *p);
926 break;
927 }
928 }
929 }
930 }
931
932 /* --- Act on a passphrase timeout --- */
933
934 if (timeout && (flags & f_pass)) {
935 gettimeofday(&now, 0);
936 if (TV_CMP(&now, >, &when)) {
937 memset(pass, 0, PIXIE_BUFSZ);
938 passlen = 0;
939 flags &= ~f_pass;
940 if (verbose > 1)
941 log("passphrase timed out");
942 }
943 }
944
945 /* --- Act on a new connection --- */
946
947 if (FD_ISSET(fd, &fds)) {
948 int nfd;
949
950 {
951 struct sockaddr_un sun;
952 int sunsz = sizeof(sun);
953
954 if ((nfd = accept(fd, (struct sockaddr *)&sun, &sunsz)) < 0) {
955 if (verbose > 1)
956 log("accept failed: %s", strerror(errno));
957 goto fail_0;
958 }
959 }
960
961 if (!(flags & f_pass)) {
962 if (get_pass()) {
963 if (verbose)
964 log("couldn't get passphrase: %s", strerror(errno));
965 goto fail_1;
966 }
967 flags |= f_pass;
968 if (timeout) {
969 gettimeofday(&when, 0);
970 when.tv_sec += timeout;
971 }
972 }
973 write(nfd, pass, passlen);
974 if (verbose)
975 log("responded to passphrase request");
976 fail_1:
977 close(nfd);
978 fail_0:
979 ;
980 }
981 }
982 }
983
984done:
985 memset(pass, 0, PIXIE_BUFSZ);
986 passlen = 0;
987 unlink(sock);
988 return (0);
989}
990
991/*----- That's all, folks -------------------------------------------------*/