Fix reading of timeouts
[pixie] / pixie.c
CommitLineData
9d2e2c65 1/* -*-c-*-
2 *
cfd10afa 3 * $Id: pixie.c,v 1.2 2000/07/07 18:33:16 mdw Exp $
9d2e2c65 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 $
cfd10afa 30 * Revision 1.2 2000/07/07 18:33:16 mdw
31 * Fix reading of timeouts
32 *
33 * Revision 1.1.1.1 1999/10/23 10:58:49 mdw
34 * New import.
9d2e2c65 35 *
36 */
37
38/*----- Header files ------------------------------------------------------*/
39
40#include <errno.h>
41#include <signal.h>
42#include <stdarg.h>
43#include <stddef.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <time.h>
48
49#include <sys/types.h>
50#include <sys/time.h>
51#include <unistd.h>
52#include <sys/stat.h>
53#include <sys/wait.h>
54#include <pwd.h>
55#include <fcntl.h>
56#include <termios.h>
57
58#ifdef HAVE_MLOCK
59# include <sys/mman.h>
60#endif
61
62#include <sys/socket.h>
63#include <sys/un.h>
64
65#include "mdwopt.h"
66
67/*----- Magic constants ---------------------------------------------------*/
68
69#define PIXIE_BUFSZ 1024 /* Passphrase buffer size */
70#define PIXIE_TIMEOUT 300 /* Default timeout (in seconds) */
71
72#define PIXIE_SOCKET "pass-socket"
73
74/*----- Static variables --------------------------------------------------*/
75
76static char *pass;
77static size_t passlen = 0;
78static int sigfd_out;
79static unsigned flags;
80
81enum {
82 f_pass = 1u,
83 f_xgetline = 2u,
84 f_getpass = 4u,
85 f_goodbuf = 8u,
86 f_bogus = 128u
87};
88
89/*----- Library code ------------------------------------------------------*/
90
91const char *pn__name = "<UNNAMED>"; /* Program name */
92#define QUIS pn__name
93
94/* --- @quis@ --- *
95 *
96 * Arguments: ---
97 *
98 * Returns: Pointer to the program name.
99 *
100 * Use: Returns the program name.
101 */
102
103const char *quis(void) { return (QUIS); }
104
105/* --- @ego@ --- *
106 *
107 * Arguments: @const char *p@ = pointer to program name
108 *
109 * Returns: ---
110 *
111 * Use: Tells mLib what the program's name is.
112 */
113
114#ifndef PATHSEP
115# if defined(__riscos)
116# define PATHSEP '.'
117# elif defined(__unix) || defined(unix)
118# define PATHSEP '/'
119# else
120# define PATHSEP '\\'
121# endif
122#endif
123
124void ego(const char *p)
125{
126 const char *q = p;
127 while (*q) {
128 if (*q++ == PATHSEP)
129 p = q;
130 }
131 if (*p == '-')
132 p++;
133 pn__name = p;
134}
135
136#undef PATHSEP
137
138/* --- @pquis@ --- *
139 *
140 * Arguments: @FILE *fp@ = output stream to write on
141 * @const char *p@ = pointer to string to write
142 *
143 * Returns: Zero if everything worked, EOF if not.
144 *
145 * Use: Writes the string @p@ to the output stream @fp@. Occurrences
146 * of the character `$' in @p@ are replaced by the program name
147 * as reported by @quis@. A `$$' is replaced by a single `$'
148 * sign.
149 */
150
151int pquis(FILE *fp, const char *p)
152{
153 size_t sz;
154
155 while (*p) {
156 sz = strcspn(p, "$");
157 if (sz) {
158 if (fwrite(p, 1, sz, fp) < sz)
159 return (EOF);
160 p += sz;
161 }
162 if (*p == '$') {
163 p++;
164 if (*p == '$') {
165 if (fputc('$', fp) == EOF)
166 return (EOF);
167 p++;
168 } else {
169 if (fputs(pn__name, fp) == EOF)
170 return (EOF);
171 }
172 }
173 }
174 return (0);
175}
176
177/* --- @die@ --- *
178 *
179 * Arguments: @int status@ = exit status to return
180 * @const char *f@ = a @printf@-style format string
181 * @...@ = other arguments
182 *
183 * Returns: Never.
184 *
185 * Use: Reports an error and exits. Like @moan@ above, only more
186 * permanent.
187 */
188
189void die(int status, const char *f, ...)
190{
191 va_list ap;
192 va_start(ap, f);
193 fprintf(stderr, "%s: ", QUIS);
194 vfprintf(stderr, f, ap);
195 va_end(ap);
196 putc('\n', stderr);
197 exit(status);
198}
199
200/* --- @fdflags@ --- *
201 *
202 * Arguments: @int fd@ = file descriptor to fiddle with
203 * @unsigned fbic, fxor@ = file flags to set and clear
204 * @unsigned fdbic, fdxor@ = descriptor flags to set and clear
205 *
206 * Returns: Zero if successful, @-1@ if not.
207 *
208 * Use: Sets file descriptor flags in what is, I hope, an obvious
209 * way.
210 */
211
212int fdflags(int fd, unsigned fbic, unsigned fxor,
213 unsigned fdbic, unsigned fdxor)
214{
215 int f;
216
217 if ((f = fcntl(fd, F_GETFL)) == -1 ||
218 fcntl(fd, F_SETFL, (f & ~fbic) ^ fxor) == -1 ||
219 (f = fcntl(fd, F_GETFD)) == -1 ||
220 fcntl(fd, F_SETFD, (f & ~fdbic) ^ fdxor) == -1)
221 return (-1);
222 return (0);
223}
224
225/* --- Timeval manipulation macros --- */
226
227#define MILLION 1000000
228
229#define TV_ADD(dst, a, b) TV_ADDL(dst, a, (b)->tv_sec, (b)->tv_usec)
230
231#define TV_ADDL(dst, a, sec, usec) do { \
232 (dst)->tv_sec = (a)->tv_sec + (sec); \
233 (dst)->tv_usec = (a)->tv_usec + (usec); \
234 if ((dst)->tv_usec >= MILLION) { \
235 (dst)->tv_usec -= MILLION; \
236 (dst)->tv_sec++; \
237 } \
238} while (0)
239
240#define TV_SUB(dst, a, b) TV_SUBL(dst, a, (b)->tv_sec, (b)->tv_usec)
241
242#define TV_SUBL(dst, a, sec, usec) do { \
243 (dst)->tv_sec = (a)->tv_sec - (sec); \
244 if ((a)->tv_usec >= (usec)) \
245 (dst)->tv_usec = (a)->tv_usec - (usec); \
246 else { \
247 (dst)->tv_usec = (a)->tv_usec + MILLION - (usec); \
248 (dst)->tv_sec--; \
249 } \
250} while (0)
251
252#define TV_CMP(a, op, b) ((a)->tv_sec == (b)->tv_sec ? \
253 (a)->tv_usec op (b)->tv_usec : \
254 (a)->tv_sec op (b)->tv_sec)
255
256/*----- Main code ---------------------------------------------------------*/
257
258/* --- @log@ --- *
259 *
260 * Arguments: @const char *p@ = @printf@-style format string
261 * @...@ = extra arguments to fill in
262 *
263 * Returns: ---
264 *
265 * Use: Writes out a timestamped log message.
266 */
267
268static void log(const char *p, ...)
269{
270 char b[32];
271 va_list ap;
272 time_t t = time(0);
273 struct tm *tm = localtime(&t);
274
275 strftime(b, sizeof(b), "%Y-%m-%d %H:%M:%S", tm);
276 fprintf(stderr, "%s: %s ", QUIS, b);
277 va_start(ap, p);
278 vfprintf(stderr, p, ap);
279 va_end(ap);
280 fputc('\n', stderr);
281}
282
283/* --- @sigwrite@ --- *
284 *
285 * Arguments: @int sig@ = signal number
286 *
287 * Returns: ---
288 *
289 * Use: Handles signals. It writes the signal number to a pipe and
290 * exits. It's possible for signals to be lost if the pipe is
291 * full. This isn't likely enough to be worth caring about.
292 * The implementation in mLib's `sig.c' does the job right but
293 * it's rather more effort.
294 */
295
296static void sigwrite(int sig)
297{
298 int e = errno;
299 char c = sig;
300 write(sigfd_out, &c, 1);
301 errno = e;
302}
303
304/* --- @readpass@ --- *
305 *
306 * Arguments: @int fd@ = file descriptor to read from
307 *
308 * Returns: 0 if OK, -1 if not.
309 *
310 * Use: Reads a line from a file descriptor. It continues reading
311 * buffers until it gets a newline character. If the buffer
312 * becomes full, a newline is inserted and no more data is
313 * read. This might cause confusion.
314 */
315
316static int readpass(int fd)
317{
318 int r;
319 char *p = pass;
320 char *q;
321 size_t sz = PIXIE_BUFSZ;
322
323 for (;;) {
324 r = read(fd, p, sz);
325 if (r < 0)
326 return (-1);
327 if (r == 0) {
328 q = p + r;
329 break;
330 }
331 if ((q = memchr(p, '\n', r)) != 0) {
332 q++;
333 break;
334 }
335 sz -= r;
336 p += r;
337 if (!sz) {
338 q = p;
339 p[-1] = '\n';
340 break;
341 }
342 }
343
344 passlen = q - pass;
345 return (0);
346}
347
348/* --- @get_pass@ --- *
349 *
350 * Arguments: ---
351 *
352 * Returns: 0 if OK, -1 if it failed.
353 *
354 * Use: Reads a passphrase from somewhere. The data from the
355 * passphrase goes straight into the @pass@ buffer without
356 * touching any other memory. Of course, if @xgetline@ is used,
357 * it might end up in unprotected memory there. That's a shame,
358 * but @xgetline@ is a much shorter-lived process than this one
359 * so it shouldn't matter as much.
360 */
361
362static int get_pass(void)
363{
364#ifdef PATH_XGETLINE
365 if (flags & f_xgetline) {
366 int fd[2];
367 pid_t kid;
368 int r;
369
370 /* --- Do everything by hand --- *
371 *
372 * I could, I suppose, use @popen@. However, (a) that involves a shell
373 * which is extra overhead and makes passing arguments with spaces a
374 * little trickier; and (b) it uses @stdio@ buffers, which might get
375 * swapped to disk.
376 */
377
378 if (pipe(fd))
379 return (-1);
380 kid = fork();
381 if (kid < 0)
382 return (-1);
383 if (kid == 0) {
384 dup2(fd[1], STDOUT_FILENO);
385 close(fd[0]);
386 close(fd[1]);
387 execlp(PATH_XGETLINE, "xgetline",
388 "-i", "-tPGP pixie", "-pPGP passphrase:", (char *)0);
389 _exit(127);
390 }
391 close(fd[1]);
392 r = readpass(fd[0]);
393 close(fd[0]);
394 waitpid(kid, 0, 0);
395 return (r);
396 } else
397#endif
398 {
399 struct termios o, n;
400 int fd;
401 int r;
402 char prompt[] = "PGP passphrase: ";
403 char nl = '\n';
404
405 /* --- Do this by hand --- *
406 *
407 * I could use @getpass@, but that puts the passphrase in its own memory
408 * rather than mine, so I'd have to scrub it out manually. This is
409 * probably just as good if you don't mind fiddling with @termios@.
410 * Also, the GNU version uses @stdio@ streams to read from the terminal,
411 * which might be considered a Bad Thing.
412 */
413
414 if ((fd = open("/dev/tty", O_RDWR)) < 0)
415 return (-1);
416 if (tcgetattr(fd, &o))
417 return (-1);
418 n = o;
419 n.c_lflag &= ~(ECHO | ISIG);
420 if (tcsetattr(fd, TCSAFLUSH, &n))
421 return (-1);
422 write(fd, prompt, sizeof(prompt) - 1);
423 r = readpass(fd);
424 tcsetattr(fd, TCSAFLUSH, &o);
425 write(fd, &nl, 1);
426 close(fd);
427 return (r);
428 }
429}
430
431/* --- @help@, @version@ @usage@ --- *
432 *
433 * Arguments: @FILE *fp@ = stream to write on
434 *
435 * Returns: ---
436 *
437 * Use: Emit helpful messages.
438 */
439
440static void usage(FILE *fp)
441{
442 pquis(fp, "Usage: $ [-xqv] [-t timeout] [-d dir] [socket]\n");
443}
444
445static void version(FILE *fp)
446{
447 pquis(fp, "$ version " VERSION "\n");
448}
449
450static void help(FILE *fp)
451{
452 version(fp);
453 fputc('\n', fp);
454 usage(fp);
455 pquis(fp, "\n\
456The passphrase pixie remembers a PGP passphrase and passes it on to\n\
457clients which connect to a Unix-domain socket.\n\
458\n\
459The pixie will forget a passphrase after a certain amount of time. The\n\
460duration of the pixie's memory is configurable using the `-t' option, and\n\
461the default is 5 minutes. By giving a timeout of zero, the pixie can be\n\
462endowed with a perfect memory.\n\
463\n\
464The pixie attempts to lock its passphrase buffer into physical memory. If\n\
465this doesn't work (e.g., your operating system doesn't support this\n\
466feature, or you have insufficient privilege) a warning is emitted.\n\
467\n\
468Options available are:\n\
469\n\
470-h, --help Show this help text.\n\
471-V, --version Show the pixie's version number.\n\
472-u, --usage Show a uselessly terse usage message.\n\
473\n\
474"
475#ifdef PATH_XGETLINE
476"\
477-x, --x11 Run `xgetline' to read a passphrase.\n\
478+x, --no-x11 Don't run `xgetline' to read a passphrase.\n\
479"
480#endif
481"\
482-d, --directory=DIR Make secure directory DIR and change to it.\n\
483-q, --quiet Don't emit so many messages.\n\
484-v, --verbose Emit more messages.\n\
485");
486}
487
488/* --- @main@ --- *
489 *
490 * Arguments: @int argc@ = number of arguments
491 * @char *argv[]@ = vector of argument values
492 *
493 * Returns: Zero if OK.
494 *
495 * Use: Main program. Listens on a socket and responds with a PGP
496 * passphrase when asked.
497 */
498
499int main(int argc, char *argv[])
500{
501 char *dir = 0;
502 int fd;
503 int sigfd_in;
504 unsigned verbose = 1;
505 char *sock = 0;
506 unsigned long timeout = PIXIE_TIMEOUT;
507 char *emsg = 0;
508 int elock = 0;
509
510 ego(argv[0]);
511
512 /* --- Try making a secure locked passphrase buffer --- *
513 *
514 * Drop privileges before emitting diagnostic messages.
515 */
516
517#ifdef HAVE_MLOCK
518
519 /* --- Memory-map a page from somewhere --- */
520
521# ifdef MAP_ANON
9d2e2c65 522 pass = mmap(0, PIXIE_BUFSZ, PROT_READ | PROT_WRITE,
523 MAP_PRIVATE | MAP_ANON, -1, 0);
9d2e2c65 524# else
9d2e2c65 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 }
9d2e2c65 533# endif
534
535 /* --- Lock the page in memory --- *
536 *
537 * Why does @mmap@ return such a stupid result if it fails?
538 */
539
540 if (pass == 0 || pass == MAP_FAILED) {
541 emsg = "couldn't map a passphrase buffer: %s";
542 elock = errno;
543 pass = 0;
544 } else if (mlock(pass, PIXIE_BUFSZ)) {
545 emsg = "couldn't lock passphrase buffer: %s";
546 elock = errno;
547 munmap(pass, PIXIE_BUFSZ);
548 pass = 0;
549 } else
550 flags |= f_goodbuf;
551
552#endif
553
554 /* --- Make a standard passphrase buffer --- */
555
556 setuid(getuid());
557
558#ifdef HAVE_MLOCK
559 if (!pass)
560#endif
561 {
562 if ((pass = malloc(PIXIE_BUFSZ)) == 0)
563 die(1, "not enough memory for passphrase buffer");
564 }
565
566 /* --- Parse options --- */
567
568 for (;;) {
569 static struct option opts[] = {
570
571 /* --- GNUey help options --- */
572
573 { "help", 0, 0, 'h' },
574 { "usage", 0, 0, 'u' },
575 { "version", 0, 0, 'V' },
576
577 /* --- Other options --- */
578
579 { "timeout", OPTF_ARGREQ, 0, 't' },
580#ifdef PATH_XGETLINE
581 { "xgetline", OPTF_NEGATE, 0, 'x' },
582 { "x11", OPTF_NEGATE, 0, 'x' },
583#endif
584 { "directory", OPTF_ARGREQ, 0, 'd' },
585 { "quiet", 0, 0, 'q' },
586 { "verbose", 0, 0, 'v' },
587
588 /* --- Magic end marker --- */
589
590 { 0, 0, 0, 0 }
591 };
592
593#ifdef PATH_XGETLINE
594# define XOPTS "x+"
595#else
596# define XOPTS
597#endif
598
599 int i = mdwopt(argc, argv, "huV" XOPTS "t:d:qv",
600 opts, 0, 0, OPTF_NEGATION);
601
602#undef XOPTS
603
604 if (i < 0)
605 break;
606 switch (i) {
607 case 'h':
608 help(stdout);
609 exit(0);
610 case 'V':
611 version(stdout);
612 exit(0);
613 case 'u':
614 usage(stdout);
615 exit(0);
616 case 't': {
617 char *p;
618 timeout = strtoul(optarg, &p, 0);
619 switch (*p) {
620 case 'd': timeout *= 24;
621 case 'h': timeout *= 60;
622 case 'm': timeout *= 60;
cfd10afa 623 case 's': if (p[1] != 0)
624 default: timeout = 0;
625 case 0: break;
9d2e2c65 626 }
cfd10afa 627 if (!timeout)
628 die(1, "bad time specification `%s'", optarg);
9d2e2c65 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 -------------------------------------------------*/