3 * Passphrase pixie for Catacomb
5 * (c) 1999 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of Catacomb.
12 * Catacomb is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Library General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) any later version.
17 * Catacomb 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 Library General Public License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License along with Catacomb; if not, write to the Free
24 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
28 /*----- Header files ------------------------------------------------------*/
43 #include <sys/types.h>
50 #include <sys/ioctl.h>
54 #include <sys/socket.h>
57 #include <mLib/alloc.h>
58 #include <mLib/dstr.h>
59 #include <mLib/fdflags.h>
60 #include <mLib/mdwopt.h>
61 #include <mLib/quis.h>
62 #include <mLib/report.h>
64 #include <mLib/selbuf.h>
72 #include "passphrase.h"
75 /*----- Static variables --------------------------------------------------*/
77 static unsigned long timeout
= 900;
79 static unsigned verbose
= 1;
80 static const char *command
= 0;
82 static unsigned flags
= 0;
88 /*----- Event logging -----------------------------------------------------*/
92 * Arguments: @const char *p@ = @printf@-style format string
93 * @...@ = extra arguments to fill in
97 * Use: Writes out a timestamped log message.
100 static void PRINTF_LIKE(1, 2) pxlog(const char *p
, ...)
105 if (!(flags
& F_SYSLOG
)) {
107 struct tm
*tm
= localtime(&t
);
109 d
.len
+= strftime(d
.buf
, d
.sz
, "%Y-%m-%d %H:%M:%S ", tm
);
112 dstr_vputf(&d
, p
, &ap
);
115 if (flags
& F_SYSLOG
)
116 syslog(LOG_NOTICE
, "%s", d
.buf
);
119 dstr_write(&d
, stderr
);
124 /*----- Passphrase management ---------------------------------------------*/
126 /* --- Data structures --- */
128 typedef struct phrase
{
129 struct phrase
*next
, *prev
;
137 /* --- Variables --- */
139 static phrase
*p_head
= 0, *p_tail
= 0;
141 /* --- Utility macros --- */
143 #define P_LINKTAIL(p) do { \
145 (p)->prev = p_tail; \
146 *(p_tail ? &p_tail->next : &p_head) = (p); \
150 #define P_UNLINK(p) do { \
151 *((p)->next ? &(p)->next->prev : &p_tail) = (p)->prev; \
152 *((p)->prev ? &(p)->prev->next : &p_head) = (p)->next; \
155 /* --- @p_free@ --- *
157 * Arguments: @phrase *p@ = pointer to phrase block
161 * Use: Frees a phrase block.
164 static void p_free(phrase
*p
)
167 sel_rmtimer(&p
->timer
);
174 /* --- @p_timer@ --- *
176 * Arguments: @struct timeval *tv@ = current time
177 * @void *p@ = pointer to phrase
181 * Use: Expires a passphrase.
184 static void p_timer(struct timeval
*tv
, void *p
)
188 pxlog("expiring passphrase `%s'", pp
->tag
);
192 /* --- @p_alloc@ --- *
194 * Arguments: @size_t sz@ = amount of memory required
196 * Returns: Pointer to allocated memory, or null.
198 * Use: Allocates some locked memory, flushing old passphrases if
199 * there's not enough space.
202 static void *p_alloc(size_t sz
)
206 if ((p
= l_alloc(&lm
, sz
)) != 0)
211 pxlog("flushing passphrase `%s' to free up needed space",
218 /* --- @p_find@ --- *
220 * Arguments: @const char *tag@ = pointer to tag to find
222 * Returns: Pointer to passphrase block, or null.
224 * Use: Finds a passphrase with a given tag.
227 static phrase
*p_find(const char *tag
)
231 for (p
= p_head
; p
; p
= p
->next
) {
232 if (strcmp(p
->tag
, tag
) == 0) {
235 sel_rmtimer(&p
->timer
);
236 gettimeofday(&tv
, 0);
238 sel_addtimer(&sel
, &p
->timer
, &tv
, p_timer
, p
);
250 * Arguments: @const char *tag@ = pointer to tag string
251 * @const char *p@ = pointer to passphrase
252 * @unsigned long t@ = expiry timeout
254 * Returns: Pointer to newly-added passphrase.
256 * Use: Adds a new passphrase. The tag must not already exist.
259 static phrase
*p_add(const char *tag
, const char *p
, unsigned long t
)
261 size_t sz
= strlen(p
) + 1;
262 char *l
= p_alloc(sz
);
265 /* --- Make sure the locked memory was allocated --- */
270 /* --- Fill in some other bits of the block --- */
275 pp
->tag
= xstrdup(tag
);
278 /* --- Set the timer --- */
283 gettimeofday(&tv
, 0);
285 sel_addtimer(&sel
, &pp
->timer
, &tv
, p_timer
, pp
);
288 /* --- Link the block into the chain --- */
294 /* --- @p_flush@ --- *
296 * Arguments: @const char *tag@ = pointer to tag string, or zero for all
300 * Use: Immediately flushes either a single phrase or all of them.
303 static void p_flush(const char *tag
)
307 if (!tag
&& verbose
> 1)
308 pxlog("flushing all passphrases");
311 phrase
*pp
= p
->next
;
314 else if (strcmp(p
->tag
, tag
) == 0) {
316 pxlog("flushing passphrase `%s'", tag
);
324 /*----- Reading passphrases -----------------------------------------------*/
326 /* --- @p_request@ --- *
328 * Arguments: @const char *msg@ = message string
329 * @const char *tag@ = pointer to tag string
330 * @char *buf@ = pointer to (locked) buffer
331 * @size_t sz@ = size of buffer
333 * Returns: Zero if all went well, nonzero otherwise.
335 * Use: Requests a passphrase from the user.
338 static int p_request(const char *msg
, const char *tag
, char *buf
, size_t sz
)
340 /* --- If there's a passphrase-fetching command, run it --- */
350 /* --- Substitute the prompt string into the command --- */
354 const char *q
= strchr(p
, '%');
377 /* --- Create a pipe and start a child process --- */
381 if ((kid
= fork()) < 0)
384 /* --- Child process --- */
388 if (dup2(fd
[1], STDOUT_FILENO
) < 0)
391 execl("/bin/sh", "sh", "-c", d
.buf
, (char *)0);
395 /* --- Read the data back into my buffer --- */
398 if ((r
= read(fd
[0], buf
, sz
- 1)) >= 0) {
399 char *q
= memchr(buf
, '\n', r
);
405 waitpid(kid
, &rc
, 0);
407 if (r
< 0 || rc
!= 0)
411 /* --- Tidy up when things go wrong --- */
422 /* --- Read a passphrase from the terminal --- *
424 * Use the standard Catacomb passphrase-reading function, so it'll read the
425 * passphrase from a file descriptor or something if the appropriate
426 * environment variable is set.
432 dstr_putf(&d
, "%s %s: ", msg
, tag
);
433 rc
= pixie_getpass(d
.buf
, buf
, sz
);
440 /* --- Sort out the buffer --- *
442 * Strip leading spaces.
448 while (isspace((unsigned char)*p
))
451 memmove(buf
, p
, len
);
462 * Arguments: @const char **q@ = where to store the result
463 * @const char *tag@ = pointer to tag string
464 * @unsigned mode@ = reading mode (verify?)
465 * @time_t exp@ = expiry time suggestion
467 * Returns: Zero if successful, @-1@ on a read failure, or @+1@ if the
468 * passphrase is missing and there is no fetcher. (This will
469 * always happen if there is no fetcher and @mode@ is
472 * Use: Reads a passphrase from somewhere.
475 static int p_get(const char **q
, const char *tag
, unsigned mode
, time_t exp
)
482 /* --- Write a log message --- */
485 pxlog("passphrase `%s' requested", tag
);
487 /* --- If there is no fetcher, life is simpler --- */
489 if (!(flags
& F_FETCH
)) {
490 if (mode
== PMODE_VERIFY
)
492 if ((p
= p_find(tag
)) == 0)
498 /* --- Try to find the phrase --- */
500 if (mode
== PMODE_VERIFY
)
502 if (mode
== PMODE_VERIFY
|| (p
= p_find(tag
)) == 0) {
503 if ((pp
= p_alloc(LBUFSZ
)) == 0)
505 if (p_request(mode
== PMODE_READ ?
"Passphrase" : "New passphrase",
506 tag
, pp
, LBUFSZ
) < 0)
508 p
= p_add(tag
, pp
, exp
);
513 /* --- If verification is requested, verify the passphrase --- */
515 if (mode
== PMODE_VERIFY
) {
516 if (!pp
&& (pp
= p_alloc(LBUFSZ
)) == 0)
518 if (p_request("Verify passphrase", tag
, pp
, LBUFSZ
) < 0)
520 if (strcmp(pp
, p
->p
) != 0) {
522 pxlog("passphrases for `%s' don't match", tag
);
528 /* --- Tidy up and return the passphrase --- */
531 memset(pp
, 0, LBUFSZ
);
537 /* --- Tidy up if things went wrong --- */
541 memset(pp
, 0, LBUFSZ
);
549 /*----- Server command parsing --------------------------------------------*/
551 /* --- Data structures --- */
553 typedef struct pixserv
{
562 #define PIXSERV_TIMEOUT 30
564 /* --- @pixserv_expire@ --- *
566 * Arguments: @struct timeval *tv@ = pointer to current time
567 * @void *p@ = pointer to server block
571 * Use: Expires a pixie connection if the remote end decides he's not
572 * interested any more.
575 static void pixserv_expire(struct timeval
*tv
, void *p
)
578 if (px
->fd
!= px
->b
.reader
.fd
)
580 selbuf_destroy(&px
->b
);
581 close(px
->b
.reader
.fd
);
585 /* --- @pixserv_write@ --- *
587 * Arguments: @pixserv *px@ = pointer to server block
588 * @const char *p@ = pointer to skeleton string
589 * @...@ = other arguments to fill in
591 * Returns: Zero on success, @-1@ on error.
593 * Use: Formats a string and emits it to the output file.
596 static int PRINTF_LIKE(2, 3) pixserv_write(pixserv
*px
, const char *p
, ...)
603 dstr_vputf(&d
, p
, &ap
);
604 rc
= write(px
->fd
, d
.buf
, d
.len
);
608 pxlog("failed to write to client: %s (closing)", strerror(errno
));
614 /* --- @pixserv_timeout@ --- *
616 * Arguments: @const char *p@ = pointer to timeout string
618 * Returns: Timeout in seconds.
620 * Use: Translates a string to a timeout value in seconds.
623 static unsigned long pixserv_timeout(const char *p
)
631 t
= strtoul(p
, &q
, 0);
636 case 's': if (q
[1] != 0)
643 /* --- @pixserv_line@ --- *
645 * Arguments: @char *s@ = pointer to the line read
646 * @size_t len@ = length of the line
647 * @void *p@ = pointer to server block
651 * Use: Handles a line read from the client.
654 static void pixserv_line(char *s
, size_t len
, void *p
)
660 /* --- Handle an end-of-file --- */
662 if (!(px
->f
& px_stdin
))
663 sel_rmtimer(&px
->timer
);
666 /* --- Fiddle the timeout --- */
668 if (!(px
->f
& px_stdin
)) {
670 gettimeofday(&tv
, 0);
671 tv
.tv_sec
+= PIXSERV_TIMEOUT
;
672 sel_addtimer(&sel
, &px
->timer
, &tv
, pixserv_expire
, px
);
675 /* --- Scan out the first word --- */
677 if ((q
= str_getword(&s
)) == 0)
679 for (qq
= q
; *qq
; qq
++)
680 *qq
= tolower((unsigned char)*qq
);
682 /* --- Handle a help request --- */
684 if (strcmp(q
, "help") == 0) {
685 if (pixserv_write(px
, "\
686 INFO Commands supported:\n\
690 INFO pass TAG [EXPIRE]\n\
692 INFO set TAG [EXPIRE] -- PHRASE\n\
693 INFO verify TAG [EXPIRE]\n\
699 /* --- List the passphrases --- */
701 else if (strcmp(q
, "list") == 0) {
704 for (p
= p_head
; p
; p
= p
->next
) {
706 if (pixserv_write(px
, "ITEM %s no-expire\n", p
->tag
)) goto close
;
709 gettimeofday(&tv
, 0);
710 TV_SUB(&tv
, &p
->timer
.tv
, &tv
);
711 if (pixserv_write(px
, "ITEM %s %lu\n",
712 p
->tag
, (unsigned long)tv
.tv_sec
))
716 if (pixserv_write(px
, "OK\n")) goto close
;
719 /* --- Request a passphrase --- */
721 else if ((mode
= PMODE_READ
, strcmp(q
, "pass") == 0) ||
722 (mode
= PMODE_VERIFY
, strcmp(q
, "verify") == 0)) {
727 if ((q
= str_getword(&s
)) == 0)
728 pixserv_write(px
, "FAIL missing tag\n");
729 else if ((t
= pixserv_timeout(s
)) == 0)
730 pixserv_write(px
, "FAIL bad timeout\n");
732 rc
= p_get(&p
, q
, mode
, t
> timeout ? timeout
: t
);
735 if (pixserv_write(px
, "OK %s\n", p
)) goto close
;
738 if (pixserv_write(px
, "FAIL error reading passphrase\n"))
742 if (pixserv_write(px
, "MISSING\n")) goto close
;
748 /* --- Flush existing passphrases --- */
750 else if (strcmp(q
, "flush") == 0) {
753 if (pixserv_write(px
, "OK\n")) goto close
;
756 /* --- Set a passphrase --- */
758 else if (strcmp(q
, "set") == 0) {
761 if ((tag
= str_getword(&s
)) == 0) {
762 if (pixserv_write(px
, "FAIL missing tag\n")) goto close
;
763 } else if ((q
= str_getword(&s
)) == 0) {
764 if (pixserv_write(px
, "FAIL no passphrase\n")) goto close
;
766 if (strcmp(q
, "--") != 0) {
767 t
= pixserv_timeout(q
);
770 t
= pixserv_timeout(0);
772 if (pixserv_write(px
, "FAIL no passphrase\n")) goto close
;
773 } else if (strcmp(q
, "--") != 0) {
774 if (pixserv_write(px
, "FAIL rubbish found before passphrase\n"))
779 if (pixserv_write(px
, "OK\n")) goto close
;
784 /* --- Shut the server down --- */
786 else if (strcmp(q
, "quit") == 0) {
788 pxlog("%s client requested shutdown",
789 px
->f
& px_stdin ?
"local" : "remote");
791 if (pixserv_write(px
, "OK\n")) goto close
;
795 /* --- Report an error for other commands --- */
798 if (pixserv_write(px
, "FAIL unknown command `%s'\n", q
)) goto close
;
803 if (px
->fd
!= px
->b
.reader
.fd
)
805 selbuf_destroy(&px
->b
);
806 close(px
->b
.reader
.fd
);
809 /* --- @pixserv_create@ --- *
811 * Arguments: @int fd@ = file descriptor to read from
812 * @int ofd@ = file descriptor to write to
814 * Returns: Pointer to the new connection.
816 * Use: Creates a new Pixie server instance for a new connection.
819 static pixserv
*pixserv_create(int fd
, int ofd
)
821 pixserv
*px
= CREATE(pixserv
);
823 fdflags(fd
, O_NONBLOCK
, O_NONBLOCK
, FD_CLOEXEC
, FD_CLOEXEC
);
825 fdflags(ofd
, O_NONBLOCK
, O_NONBLOCK
, FD_CLOEXEC
, FD_CLOEXEC
);
827 selbuf_init(&px
->b
, &sel
, fd
, pixserv_line
, px
);
828 px
->b
.b
.a
= arena_secure
;
829 selbuf_setsize(&px
->b
, 1024);
830 gettimeofday(&tv
, 0);
831 tv
.tv_sec
+= PIXSERV_TIMEOUT
;
832 sel_addtimer(&sel
, &px
->timer
, &tv
, pixserv_expire
, px
);
837 /* --- @pixserv_accept@ --- *
839 * Arguments: @int fd@ = file descriptor
840 * @unsigned mode@ = what's happened
841 * @void *p@ = an uninteresting argument
845 * Use: Accepts a new connection.
848 static void pixserv_accept(int fd
, unsigned mode
, void *p
)
851 struct sockaddr_un sun
;
852 socklen_t sunsz
= sizeof(sun
);
854 if (mode
!= SEL_READ
)
856 if ((nfd
= accept(fd
, (struct sockaddr
*)&sun
, &sunsz
)) < 0) {
857 if (verbose
&& errno
!= EAGAIN
&& errno
!= EWOULDBLOCK
&&
858 errno
!= ECONNABORTED
&& errno
!= EPROTO
&& errno
!= EINTR
)
859 pxlog("new connection failed: %s", strerror(errno
));
862 pixserv_create(nfd
, nfd
);
865 /*----- Setting up the server ---------------------------------------------*/
867 /* --- @unlinksocket@ --- *
873 * Use: Tidies up the socket when it's finished with.
876 static char *sockpath
;
878 static void unlinksocket(void)
884 /* --- @pix_sigdie@ --- *
886 * Arguments: @int sig@ = signal number
887 * @void *p@ = uninteresting argument
891 * Use: Shuts down the program after a fatal signal.
894 static void pix_sigdie(int sig
, void *p
)
901 case SIGTERM
: p
= "SIGTERM"; break;
902 case SIGINT
: p
= "SIGINT"; break;
904 sprintf(buf
, "signal %i", sig
);
908 pxlog("shutting down on %s", p
);
913 /* --- @pix_sigflush@ --- *
915 * Arguments: @int sig@ = signal number
916 * @void *p@ = uninteresting argument
920 * Use: Flushes the passphrase cache on receipt of a signal.
923 static void pix_sigflush(int sig
, void *p
)
930 case SIGHUP
: p
= "SIGHUP"; break;
931 case SIGQUIT
: p
= "SIGQUIT"; break;
933 sprintf(buf
, "signal %i", sig
);
937 pxlog("received %s; flushing passphrases", p
);
942 /* --- @pix_setup@ --- *
944 * Arguments: @struct sockaddr_un *sun@ = pointer to address to use
945 * @size_t sz@ = size of socket address
949 * Use: Sets up the pixie's Unix-domain socket.
952 static void pix_setup(struct sockaddr_un
*sun
, size_t sz
)
956 /* --- Set up the parent directory --- */
959 char *p
= sun
->sun_path
;
960 char *q
= strrchr(p
, '/');
970 if (stat(d
.buf
, &st
))
971 die(1, "couldn't stat `%s': %s", d
.buf
, strerror(errno
));
972 if (!S_ISDIR(st
.st_mode
))
973 die(1, "object `%s' isn't a directory", d
.buf
);
974 if (st
.st_mode
& 0077)
975 die(1, "parent directory `%s' has group or world access", d
.buf
);
980 /* --- Initialize the socket --- */
988 if ((fd
= socket(PF_UNIX
, SOCK_STREAM
, 0)) < 0)
989 die(1, "couldn't create socket: %s", strerror(errno
));
990 if (bind(fd
, (struct sockaddr
*)sun
, sz
) < 0) {
992 if (errno
!= EADDRINUSE
)
993 die(1, "couldn't bind to address: %s", strerror(e
));
995 die(1, "too many retries; giving up");
997 if (connect(fd
, (struct sockaddr
*)sun
, sz
)) {
999 if (errno
!= ECONNREFUSED
)
1000 die(1, "couldn't bind to address: %s", strerror(e
));
1001 if (stat(sun
->sun_path
, &st
))
1002 die(1, "couldn't stat `%s': %s", sun
->sun_path
, strerror(errno
));
1003 if (!S_ISSOCK(st
.st_mode
))
1004 die(1, "object `%s' isn't a socket", sun
->sun_path
);
1006 pxlog("stale socket found; removing it");
1007 unlink(sun
->sun_path
);
1009 } else if (flags
& F_REPLACE
) {
1011 pxlog("server already running; shutting it down");
1012 if (write(fd
, "QUIT\n", 5) < 0) {
1013 die(EXIT_FAILURE
, "failed to shut down old server: %s",
1019 die(EXIT_FAILURE
, "pixie already running; not starting");
1022 chmod(sun
->sun_path
, 0600);
1023 fdflags(fd
, O_NONBLOCK
, O_NONBLOCK
, FD_CLOEXEC
, FD_CLOEXEC
);
1025 die(1, "couldn't listen on socket: %s", strerror(errno
));
1028 /* --- Set up the rest of the server --- */
1031 static sel_file serv
;
1032 sockpath
= sun
->sun_path
;
1033 atexit(unlinksocket
);
1034 sel_initfile(&sel
, &serv
, fd
, SEL_READ
, pixserv_accept
, 0);
1039 /*----- Client support code -----------------------------------------------*/
1041 /* --- Variables --- */
1043 static selbuf c_server
, c_client
;
1044 static unsigned c_flags
= 0;
1046 #define cf_uclose 1u
1047 #define cf_sclose 2u
1048 #define cf_cooked 4u
1050 /* --- Line handler functions --- */
1052 static void c_uline(char *s
, size_t len
, void *p
)
1055 selbuf_destroy(&c_client
);
1056 shutdown(c_server
.reader
.fd
, 1);
1057 c_flags
|= cf_uclose
;
1060 if (write(c_server
.reader
.fd
, s
, len
) < 0)
1061 die(EXIT_FAILURE
, "failed to read from stdin: %s", strerror(errno
));
1065 static void c_sline(char *s
, size_t len
, void *p
)
1068 selbuf_destroy(&c_server
);
1069 if (!(c_flags
& cf_uclose
)) {
1070 moan("server closed the connection");
1071 selbuf_destroy(&c_client
);
1075 if (!(c_flags
& cf_cooked
))
1078 char *q
= str_getword(&s
);
1079 if (strcmp(q
, "FAIL") == 0)
1081 else if (strcmp(q
, "INFO") == 0 ||
1082 strcmp(q
, "ITEM") == 0)
1084 else if (strcmp(q
, "OK") == 0) {
1085 if (s
&& *s
) puts(s
);
1086 } else if (strcmp(q
, "MISSING") == 0)
1089 moan("unexpected output: %s %s", q
, s
);
1093 /* --- @pix_client@ --- *
1095 * Arguments: @struct sockaddr_un *sun@ = pointer to socket address
1096 * @size_t sz@ = size of socket address
1097 * @char *argv[]@ = pointer to arguments to send
1101 * Use: Performs client-side actions for the passphrase pixie.
1104 static void pix_client(struct sockaddr_un
*sun
, size_t sz
, char *argv
[])
1108 /* --- Dispose of locked memory --- */
1112 /* --- Open the socket --- */
1114 if ((fd
= socket(PF_UNIX
, SOCK_STREAM
, 0)) < 0)
1115 die(1, "couldn't create socket: %s", strerror(errno
));
1116 if (connect(fd
, (struct sockaddr
*)sun
, sz
))
1117 die(1, "couldn't connect to server: %s", strerror(errno
));
1118 selbuf_init(&c_server
, &sel
, fd
, c_sline
, 0);
1120 /* --- If there are any arguments, turn them into a string --- */
1123 selbuf_init(&c_client
, &sel
, STDIN_FILENO
, c_uline
, 0);
1132 if (write(fd
, d
.buf
, d
.len
) < 0 || shutdown(fd
, 1))
1133 die(EXIT_FAILURE
, "failed to write command: %s", strerror(errno
));
1134 c_flags
|= cf_uclose
| cf_cooked
;
1138 /* --- And repeat --- */
1141 if (sel_select(&sel
))
1142 die(EXIT_FAILURE
, "select error: %s", strerror(errno
));
1146 /*----- Main code ---------------------------------------------------------*/
1148 /* --- @help@, @version@, @usage@ --- *
1150 * Arguments: @FILE *fp@ = stream to write on
1154 * Use: Emit helpful messages.
1157 static void usage(FILE *fp
)
1161 $ [-qvfidl] [-c COMMAND] [-t TIMEOUT] [-s SOCKET]\n\
1162 $ [-s SOCKET] -C [COMMAND ARGS...]\n\
1163 $ [-s SOCKET] -P[P] TAG\n\
1167 static void version(FILE *fp
)
1169 pquis(fp
, "$, Catacomb version " VERSION
"\n");
1172 static void help(FILE *fp
)
1178 The Catacomb passphrase pixie collects and caches passphrases used to\n\
1179 protect important keys. Options provided:\n\
1181 -h, --help Show this help text.\n\
1182 -V, --version Show the program's version number.\n\
1183 -u, --usage Show a (very) terse usage summary.\n\
1185 -C, --client Connect to a running pixie as a client.\n\
1186 -P, --passphrase Request passphrase TAG and print to stdout.\n\
1187 -PP, --verify-passphrase\n\
1188 Verify passphrase TAG and print to stdout.\n\
1190 -q, --quiet Emit fewer log messages.\n\
1191 -v, --version Emit more log messages.\n\
1192 -s, --socket=FILE Name the pixie's socket.\n\
1193 -r, --replace Replace existing pixie, if one is running.\n\
1194 -c, --command=COMMAND Shell command to read a passphrase.\n\
1195 -f, --fetch Fetch passphrases from the terminal.\n\
1196 -t, --timeout=TIMEOUT Length of time to retain a passphrase in memory.\n\
1197 -i, --interactive Allow commands to be typed interactively.\n\
1198 -d, --daemon Fork into the background after initialization.\n\
1199 -l, --syslog Emit log messages to the system log.\n\
1201 The COMMAND may contain `%m' and `%t' markers which are replaced by a\n\
1202 prompt message and the passphrase tag respectively. The TIMEOUT is an\n\
1203 integer, optionally followed by `d', `h', `m' or `s' to specify units of\n\
1204 days, hours, minutes or seconds respectively.\n\
1206 In client mode, if a command is specified on the command line, it is sent\n\
1207 to the running server; otherwise the program reads requests from stdin.\n\
1208 Responses from the pixie are written to stdout. Send a HELP request for\n\
1209 a quick summary of the pixie communication protocol.\n\
1215 * Arguments: @int argc@ = number of arguments
1216 * @char *argv[]@ = vector of argument values
1218 * Returns: Zero if OK.
1220 * Use: Main program. Listens on a socket and responds with a PGP
1221 * passphrase when asked.
1224 int main(int argc
, char *argv
[])
1227 struct sockaddr_un
*sun
;
1235 #define f_syslog 16u
1237 #define f_verify 64u
1239 /* --- Initialize libraries --- */
1244 /* --- Set up the locked memory area --- */
1247 if (setuid(getuid())) _exit(125);
1249 /* --- Parse command line arguments --- */
1252 static struct option opts
[] = {
1254 /* --- Standard GNUy help options --- */
1256 { "help", 0, 0, 'h' },
1257 { "version", 0, 0, 'V' },
1258 { "usage", 0, 0, 'u' },
1260 /* --- Other options --- */
1262 { "quiet", 0, 0, 'q' },
1263 { "verbose", 0, 0, 'v' },
1264 { "client", 0, 0, 'C' },
1265 { "passphrase", 0, 0, 'P' },
1266 { "verify-passphrase", 0, 0, '+' },
1267 { "socket", OPTF_ARGREQ
, 0, 's' },
1268 { "replace", 0, 0, 'r' },
1269 { "command", OPTF_ARGREQ
, 0, 'c' },
1270 { "fetch", 0, 0, 'f' },
1271 { "timeout", OPTF_ARGREQ
, 0, 't' },
1272 { "interactive", 0, 0, 'i' },
1273 { "stdin", 0, 0, 'i' },
1274 { "daemon", 0, 0, 'd' },
1275 { "log", 0, 0, 'l' },
1276 { "syslog", 0, 0, 'l' },
1278 /* --- Magic terminator --- */
1283 int i
= mdwopt(argc
, argv
, "hVuqvCPs:rc:ft:idl", opts
, 0, 0, 0);
1289 /* --- GNUy help options --- */
1301 /* --- Other interesting things --- */
1321 f
|= f_fetch
| f_verify
;
1331 if ((timeout
= pixserv_timeout(optarg
)) == 0)
1332 die(1, "bad timeout `%s'", optarg
);
1351 /* --- Something else --- */
1360 (optind
< argc
&& !(f
& (f_client
|f_fetch
))) ||
1361 ((f
& f_fetch
) && optind
!= argc
- 1)) {
1366 /* --- Handle request for a passphrase --- */
1369 char *buf
= l_alloc(&lm
, 1024);
1370 passphrase_connect(path
);
1371 if (passphrase_read(argv
[optind
],
1372 (f
& f_verify
) ? PMODE_VERIFY
: PMODE_READ
,
1374 die(1, "failed to read passphrase: %s", strerror(errno
));
1379 /* --- Set up the socket address --- */
1381 sun
= pixie_address(path
, &sz
);
1383 /* --- Initialize selectory --- */
1386 signal(SIGPIPE
, SIG_IGN
);
1388 /* --- Be a client if a client's wanted --- */
1391 pix_client(sun
, sz
, argv
+ optind
);
1393 /* --- Open the syslog if requested --- */
1397 openlog(QUIS
, 0, LOG_DAEMON
);
1400 /* --- Check on the locked memory area --- */
1404 int rc
= l_report(&lm
, &d
);
1406 die(EXIT_FAILURE
, "%s", d
.buf
);
1407 else if (rc
&& verbose
) {
1409 pxlog("couldn't lock passphrase buffer");
1412 arena_setsecure(&lm
.a
);
1415 /* --- Set signal behaviours --- */
1418 static sig sigint
, sigterm
, sigquit
, sighup
;
1419 struct sigaction sa
;
1421 sigaction(SIGINT
, 0, &sa
);
1422 if (sa
.sa_handler
!= SIG_IGN
)
1423 sig_add(&sigint
, SIGINT
, pix_sigdie
, 0);
1424 sig_add(&sigterm
, SIGTERM
, pix_sigdie
, 0);
1425 sig_add(&sigquit
, SIGQUIT
, pix_sigflush
, 0);
1426 sig_add(&sighup
, SIGHUP
, pix_sigflush
, 0);
1429 /* --- Set up the server --- */
1433 pixserv
*px
= pixserv_create(STDIN_FILENO
, STDOUT_FILENO
);
1434 sel_rmtimer(&px
->timer
);
1438 /* --- Fork into the background if requested --- */
1443 if (((f
& f_stdin
) &&
1444 (isatty(STDIN_FILENO
) || isatty(STDOUT_FILENO
))) ||
1445 (!command
&& (flags
& F_FETCH
)))
1446 die(1, "can't become a daemon if terminal required");
1448 if ((kid
= fork()) < 0)
1449 die(1, "fork failed: %s", strerror(errno
));
1455 if ((fd
= open("/dev/tty", O_RDONLY
)) >= 0) {
1456 ioctl(fd
, TIOCNOTTY
);
1461 DISCARD(chdir("/"));
1469 pxlog("initialized ok");
1474 if (!sel_select(&sel
))
1476 else if (errno
!= EINTR
&& errno
!= EAGAIN
) {
1477 pxlog("error from select: %s", strerror(errno
));
1480 pxlog("too many consecutive select errors: bailing out");
1489 /*----- That's all, folks -------------------------------------------------*/