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/macros.h>
61 #include <mLib/mdwopt.h>
62 #include <mLib/quis.h>
63 #include <mLib/report.h>
65 #include <mLib/selbuf.h>
73 #include "passphrase.h"
76 /*----- Static variables --------------------------------------------------*/
78 static unsigned long timeout
= 900;
80 static unsigned verbose
= 1;
81 static const char *command
= 0;
83 static unsigned flags
= 0;
89 /*----- Event logging -----------------------------------------------------*/
93 * Arguments: @const char *p@ = @printf@-style format string
94 * @...@ = extra arguments to fill in
98 * Use: Writes out a timestamped log message.
101 static void PRINTF_LIKE(1, 2) pxlog(const char *p
, ...)
106 if (!(flags
& F_SYSLOG
)) {
108 struct tm
*tm
= localtime(&t
);
110 d
.len
+= strftime(d
.buf
, d
.sz
, "%Y-%m-%d %H:%M:%S ", tm
);
113 dstr_vputf(&d
, p
, &ap
);
116 if (flags
& F_SYSLOG
)
117 syslog(LOG_NOTICE
, "%s", d
.buf
);
120 dstr_write(&d
, stderr
);
125 /*----- Passphrase management ---------------------------------------------*/
127 /* --- Data structures --- */
129 typedef struct phrase
{
130 struct phrase
*next
, *prev
;
138 /* --- Variables --- */
140 static phrase
*p_head
= 0, *p_tail
= 0;
142 /* --- Utility macros --- */
144 #define P_LINKTAIL(p) do { \
146 (p)->prev = p_tail; \
147 *(p_tail ? &p_tail->next : &p_head) = (p); \
151 #define P_UNLINK(p) do { \
152 *((p)->next ? &(p)->next->prev : &p_tail) = (p)->prev; \
153 *((p)->prev ? &(p)->prev->next : &p_head) = (p)->next; \
156 /* --- @p_free@ --- *
158 * Arguments: @phrase *p@ = pointer to phrase block
162 * Use: Frees a phrase block.
165 static void p_free(phrase
*p
)
168 sel_rmtimer(&p
->timer
);
175 /* --- @p_timer@ --- *
177 * Arguments: @struct timeval *tv@ = current time
178 * @void *p@ = pointer to phrase
182 * Use: Expires a passphrase.
185 static void p_timer(struct timeval
*tv
, void *p
)
189 pxlog("expiring passphrase `%s'", pp
->tag
);
193 /* --- @p_alloc@ --- *
195 * Arguments: @size_t sz@ = amount of memory required
197 * Returns: Pointer to allocated memory, or null.
199 * Use: Allocates some locked memory, flushing old passphrases if
200 * there's not enough space.
203 static void *p_alloc(size_t sz
)
207 if ((p
= l_alloc(&lm
, sz
)) != 0)
212 pxlog("flushing passphrase `%s' to free up needed space",
219 /* --- @p_find@ --- *
221 * Arguments: @const char *tag@ = pointer to tag to find
223 * Returns: Pointer to passphrase block, or null.
225 * Use: Finds a passphrase with a given tag.
228 static phrase
*p_find(const char *tag
)
232 for (p
= p_head
; p
; p
= p
->next
) {
233 if (STRCMP(p
->tag
, ==, tag
)) {
236 sel_rmtimer(&p
->timer
);
237 gettimeofday(&tv
, 0);
239 sel_addtimer(&sel
, &p
->timer
, &tv
, p_timer
, p
);
251 * Arguments: @const char *tag@ = pointer to tag string
252 * @const char *p@ = pointer to passphrase
253 * @unsigned long t@ = expiry timeout
255 * Returns: Pointer to newly-added passphrase.
257 * Use: Adds a new passphrase. The tag must not already exist.
260 static phrase
*p_add(const char *tag
, const char *p
, unsigned long t
)
262 size_t sz
= strlen(p
) + 1;
263 char *l
= p_alloc(sz
);
266 /* --- Make sure the locked memory was allocated --- */
271 /* --- Fill in some other bits of the block --- */
276 pp
->tag
= xstrdup(tag
);
279 /* --- Set the timer --- */
284 gettimeofday(&tv
, 0);
286 sel_addtimer(&sel
, &pp
->timer
, &tv
, p_timer
, pp
);
289 /* --- Link the block into the chain --- */
295 /* --- @p_flush@ --- *
297 * Arguments: @const char *tag@ = pointer to tag string, or zero for all
301 * Use: Immediately flushes either a single phrase or all of them.
304 static void p_flush(const char *tag
)
308 if (!tag
&& verbose
> 1)
309 pxlog("flushing all passphrases");
312 phrase
*pp
= p
->next
;
315 else if (STRCMP(p
->tag
, ==, tag
)) {
317 pxlog("flushing passphrase `%s'", tag
);
325 /*----- Reading passphrases -----------------------------------------------*/
327 /* --- @p_request@ --- *
329 * Arguments: @const char *msg@ = message string
330 * @const char *tag@ = pointer to tag string
331 * @char *buf@ = pointer to (locked) buffer
332 * @size_t sz@ = size of buffer
334 * Returns: Zero if all went well, nonzero otherwise.
336 * Use: Requests a passphrase from the user.
339 static int p_request(const char *msg
, const char *tag
, char *buf
, size_t sz
)
341 /* --- If there's a passphrase-fetching command, run it --- */
351 /* --- Substitute the prompt string into the command --- */
355 const char *q
= strchr(p
, '%');
378 /* --- Create a pipe and start a child process --- */
382 if ((kid
= fork()) < 0)
385 /* --- Child process --- */
389 if (dup2(fd
[1], STDOUT_FILENO
) < 0)
392 execl("/bin/sh", "sh", "-c", d
.buf
, (char *)0);
396 /* --- Read the data back into my buffer --- */
399 if ((r
= read(fd
[0], buf
, sz
- 1)) >= 0) {
400 char *q
= memchr(buf
, '\n', r
);
406 waitpid(kid
, &rc
, 0);
408 if (r
< 0 || rc
!= 0)
412 /* --- Tidy up when things go wrong --- */
423 /* --- Read a passphrase from the terminal --- *
425 * Use the standard Catacomb passphrase-reading function, so it'll read the
426 * passphrase from a file descriptor or something if the appropriate
427 * environment variable is set.
433 dstr_putf(&d
, "%s %s: ", msg
, tag
);
434 rc
= pixie_getpass(d
.buf
, buf
, sz
);
441 /* --- Sort out the buffer --- *
443 * Strip leading spaces.
452 memmove(buf
, p
, len
);
463 * Arguments: @const char **q@ = where to store the result
464 * @const char *tag@ = pointer to tag string
465 * @unsigned mode@ = reading mode (verify?)
466 * @time_t exp@ = expiry time suggestion
468 * Returns: Zero if successful, @-1@ on a read failure, or @+1@ if the
469 * passphrase is missing and there is no fetcher. (This will
470 * always happen if there is no fetcher and @mode@ is
473 * Use: Reads a passphrase from somewhere.
476 static int p_get(const char **q
, const char *tag
, unsigned mode
, time_t exp
)
483 /* --- Write a log message --- */
486 pxlog("passphrase `%s' requested", tag
);
488 /* --- If there is no fetcher, life is simpler --- */
490 if (!(flags
& F_FETCH
)) {
491 if (mode
== PMODE_VERIFY
)
493 if ((p
= p_find(tag
)) == 0)
499 /* --- Try to find the phrase --- */
501 if (mode
== PMODE_VERIFY
)
503 if (mode
== PMODE_VERIFY
|| (p
= p_find(tag
)) == 0) {
504 if ((pp
= p_alloc(LBUFSZ
)) == 0)
506 if (p_request(mode
== PMODE_READ ?
"Passphrase" : "New passphrase",
507 tag
, pp
, LBUFSZ
) < 0)
509 p
= p_add(tag
, pp
, exp
);
514 /* --- If verification is requested, verify the passphrase --- */
516 if (mode
== PMODE_VERIFY
) {
517 if (!pp
&& (pp
= p_alloc(LBUFSZ
)) == 0)
519 if (p_request("Verify passphrase", tag
, pp
, LBUFSZ
) < 0)
521 if (STRCMP(pp
, !=, p
->p
)) {
523 pxlog("passphrases for `%s' don't match", tag
);
529 /* --- Tidy up and return the passphrase --- */
532 memset(pp
, 0, LBUFSZ
);
538 /* --- Tidy up if things went wrong --- */
542 memset(pp
, 0, LBUFSZ
);
550 /*----- Server command parsing --------------------------------------------*/
552 /* --- Data structures --- */
554 typedef struct pixserv
{
563 #define PIXSERV_TIMEOUT 30
565 /* --- @pixserv_expire@ --- *
567 * Arguments: @struct timeval *tv@ = pointer to current time
568 * @void *p@ = pointer to server block
572 * Use: Expires a pixie connection if the remote end decides he's not
573 * interested any more.
576 static void pixserv_expire(struct timeval
*tv
, void *p
)
579 if (px
->fd
!= px
->b
.reader
.fd
)
581 selbuf_destroy(&px
->b
);
582 close(px
->b
.reader
.fd
);
586 /* --- @pixserv_write@ --- *
588 * Arguments: @pixserv *px@ = pointer to server block
589 * @const char *p@ = pointer to skeleton string
590 * @...@ = other arguments to fill in
592 * Returns: Zero on success, @-1@ on error.
594 * Use: Formats a string and emits it to the output file.
597 static int PRINTF_LIKE(2, 3) pixserv_write(pixserv
*px
, const char *p
, ...)
604 dstr_vputf(&d
, p
, &ap
);
605 rc
= write(px
->fd
, d
.buf
, d
.len
);
609 pxlog("failed to write to client: %s (closing)", strerror(errno
));
615 /* --- @pixserv_timeout@ --- *
617 * Arguments: @const char *p@ = pointer to timeout string
619 * Returns: Timeout in seconds.
621 * Use: Translates a string to a timeout value in seconds.
624 static unsigned long pixserv_timeout(const char *p
)
632 t
= strtoul(p
, &q
, 0);
637 case 's': if (q
[1] != 0)
644 /* --- @pixserv_line@ --- *
646 * Arguments: @char *s@ = pointer to the line read
647 * @size_t len@ = length of the line
648 * @void *p@ = pointer to server block
652 * Use: Handles a line read from the client.
655 static void pixserv_line(char *s
, size_t len
, void *p
)
661 /* --- Handle an end-of-file --- */
663 if (!(px
->f
& px_stdin
))
664 sel_rmtimer(&px
->timer
);
667 /* --- Fiddle the timeout --- */
669 if (!(px
->f
& px_stdin
)) {
671 gettimeofday(&tv
, 0);
672 tv
.tv_sec
+= PIXSERV_TIMEOUT
;
673 sel_addtimer(&sel
, &px
->timer
, &tv
, pixserv_expire
, px
);
676 /* --- Scan out the first word --- */
678 if ((q
= str_getword(&s
)) == 0)
680 for (qq
= q
; *qq
; qq
++)
683 /* --- Handle a help request --- */
685 if (STRCMP(q
, ==, "help")) {
686 if (pixserv_write(px
, "\
687 INFO Commands supported:\n\
691 INFO pass TAG [EXPIRE]\n\
693 INFO set TAG [EXPIRE] -- PHRASE\n\
694 INFO verify TAG [EXPIRE]\n\
700 /* --- List the passphrases --- */
702 else if (STRCMP(q
, ==, "list")) {
705 for (p
= p_head
; p
; p
= p
->next
) {
707 if (pixserv_write(px
, "ITEM %s no-expire\n", p
->tag
)) goto close
;
710 gettimeofday(&tv
, 0);
711 TV_SUB(&tv
, &p
->timer
.tv
, &tv
);
712 if (pixserv_write(px
, "ITEM %s %lu\n",
713 p
->tag
, (unsigned long)tv
.tv_sec
))
717 if (pixserv_write(px
, "OK\n")) goto close
;
720 /* --- Request a passphrase --- */
722 else if ((mode
= PMODE_READ
, STRCMP(q
, ==, "pass")) ||
723 (mode
= PMODE_VERIFY
, STRCMP(q
, ==, "verify"))) {
728 if ((q
= str_getword(&s
)) == 0)
729 pixserv_write(px
, "FAIL missing tag\n");
730 else if ((t
= pixserv_timeout(s
)) == 0)
731 pixserv_write(px
, "FAIL bad timeout\n");
733 rc
= p_get(&p
, q
, mode
, t
> timeout ? timeout
: t
);
736 if (pixserv_write(px
, "OK %s\n", p
)) goto close
;
739 if (pixserv_write(px
, "FAIL error reading passphrase\n"))
743 if (pixserv_write(px
, "MISSING\n")) goto close
;
749 /* --- Flush existing passphrases --- */
751 else if (STRCMP(q
, ==, "flush")) {
754 if (pixserv_write(px
, "OK\n")) goto close
;
757 /* --- Set a passphrase --- */
759 else if (STRCMP(q
, ==, "set")) {
762 if ((tag
= str_getword(&s
)) == 0) {
763 if (pixserv_write(px
, "FAIL missing tag\n")) goto close
;
764 } else if ((q
= str_getword(&s
)) == 0) {
765 if (pixserv_write(px
, "FAIL no passphrase\n")) goto close
;
767 if (STRCMP(q
, !=, "--")) {
768 t
= pixserv_timeout(q
);
771 t
= pixserv_timeout(0);
773 if (pixserv_write(px
, "FAIL no passphrase\n")) goto close
;
774 } else if (STRCMP(q
, !=, "--")) {
775 if (pixserv_write(px
, "FAIL rubbish found before passphrase\n"))
779 p_add(tag
, s ? s
: "", t
);
780 if (pixserv_write(px
, "OK\n")) goto close
;
785 /* --- Shut the server down --- */
787 else if (STRCMP(q
, ==, "quit")) {
789 pxlog("%s client requested shutdown",
790 px
->f
& px_stdin ?
"local" : "remote");
792 if (pixserv_write(px
, "OK\n")) goto close
;
796 /* --- Report an error for other commands --- */
799 if (pixserv_write(px
, "FAIL unknown command `%s'\n", q
)) goto close
;
804 if (px
->fd
!= px
->b
.reader
.fd
)
806 selbuf_destroy(&px
->b
);
807 close(px
->b
.reader
.fd
);
810 /* --- @pixserv_create@ --- *
812 * Arguments: @int fd@ = file descriptor to read from
813 * @int ofd@ = file descriptor to write to
815 * Returns: Pointer to the new connection.
817 * Use: Creates a new Pixie server instance for a new connection.
820 static pixserv
*pixserv_create(int fd
, int ofd
)
822 pixserv
*px
= CREATE(pixserv
);
824 fdflags(fd
, O_NONBLOCK
, O_NONBLOCK
, FD_CLOEXEC
, FD_CLOEXEC
);
826 fdflags(ofd
, O_NONBLOCK
, O_NONBLOCK
, FD_CLOEXEC
, FD_CLOEXEC
);
828 selbuf_init(&px
->b
, &sel
, fd
, pixserv_line
, px
);
829 px
->b
.b
.a
= arena_secure
;
830 selbuf_setsize(&px
->b
, 1024);
831 gettimeofday(&tv
, 0);
832 tv
.tv_sec
+= PIXSERV_TIMEOUT
;
833 sel_addtimer(&sel
, &px
->timer
, &tv
, pixserv_expire
, px
);
838 /* --- @pixserv_accept@ --- *
840 * Arguments: @int fd@ = file descriptor
841 * @unsigned mode@ = what's happened
842 * @void *p@ = an uninteresting argument
846 * Use: Accepts a new connection.
849 static void pixserv_accept(int fd
, unsigned mode
, void *p
)
852 struct sockaddr_un sun
;
853 socklen_t sunsz
= sizeof(sun
);
855 if (mode
!= SEL_READ
)
857 if ((nfd
= accept(fd
, (struct sockaddr
*)&sun
, &sunsz
)) < 0) {
858 if (verbose
&& errno
!= EAGAIN
&& errno
!= EWOULDBLOCK
&&
859 errno
!= ECONNABORTED
&& errno
!= EPROTO
&& errno
!= EINTR
)
860 pxlog("new connection failed: %s", strerror(errno
));
863 pixserv_create(nfd
, nfd
);
866 /*----- Setting up the server ---------------------------------------------*/
868 /* --- @unlinksocket@ --- *
874 * Use: Tidies up the socket when it's finished with.
877 static char *sockpath
;
879 static void unlinksocket(void)
885 /* --- @pix_sigdie@ --- *
887 * Arguments: @int sig@ = signal number
888 * @void *p@ = uninteresting argument
892 * Use: Shuts down the program after a fatal signal.
895 static void pix_sigdie(int sig
, void *p
)
902 case SIGTERM
: p
= "SIGTERM"; break;
903 case SIGINT
: p
= "SIGINT"; break;
905 sprintf(buf
, "signal %i", sig
);
909 pxlog("shutting down on %s", p
);
914 /* --- @pix_sigflush@ --- *
916 * Arguments: @int sig@ = signal number
917 * @void *p@ = uninteresting argument
921 * Use: Flushes the passphrase cache on receipt of a signal.
924 static void pix_sigflush(int sig
, void *p
)
931 case SIGHUP
: p
= "SIGHUP"; break;
932 case SIGQUIT
: p
= "SIGQUIT"; break;
934 sprintf(buf
, "signal %i", sig
);
938 pxlog("received %s; flushing passphrases", p
);
943 /* --- @pix_setup@ --- *
945 * Arguments: @struct sockaddr_un *sun@ = pointer to address to use
946 * @size_t sz@ = size of socket address
950 * Use: Sets up the pixie's Unix-domain socket.
953 static void pix_setup(struct sockaddr_un
*sun
, size_t sz
)
957 /* --- Set up the parent directory --- */
960 char *p
= sun
->sun_path
;
961 char *q
= strrchr(p
, '/');
971 if (stat(d
.buf
, &st
))
972 die(1, "couldn't stat `%s': %s", d
.buf
, strerror(errno
));
973 if (!S_ISDIR(st
.st_mode
))
974 die(1, "object `%s' isn't a directory", d
.buf
);
975 if (st
.st_mode
& 0077)
976 die(1, "parent directory `%s' has group or world access", d
.buf
);
981 /* --- Initialize the socket --- */
989 if ((fd
= socket(PF_UNIX
, SOCK_STREAM
, 0)) < 0)
990 die(1, "couldn't create socket: %s", strerror(errno
));
991 if (bind(fd
, (struct sockaddr
*)sun
, sz
) < 0) {
993 if (errno
!= EADDRINUSE
)
994 die(1, "couldn't bind to address: %s", strerror(e
));
996 die(1, "too many retries; giving up");
998 if (connect(fd
, (struct sockaddr
*)sun
, sz
)) {
1000 if (errno
!= ECONNREFUSED
)
1001 die(1, "couldn't bind to address: %s", strerror(e
));
1002 if (stat(sun
->sun_path
, &st
))
1003 die(1, "couldn't stat `%s': %s", sun
->sun_path
, strerror(errno
));
1004 if (!S_ISSOCK(st
.st_mode
))
1005 die(1, "object `%s' isn't a socket", sun
->sun_path
);
1007 pxlog("stale socket found; removing it");
1008 unlink(sun
->sun_path
);
1010 } else if (flags
& F_REPLACE
) {
1012 pxlog("server already running; shutting it down");
1013 if (write(fd
, "QUIT\n", 5) < 0) {
1014 die(EXIT_FAILURE
, "failed to shut down old server: %s",
1020 die(EXIT_FAILURE
, "pixie already running; not starting");
1023 chmod(sun
->sun_path
, 0600);
1024 fdflags(fd
, O_NONBLOCK
, O_NONBLOCK
, FD_CLOEXEC
, FD_CLOEXEC
);
1026 die(1, "couldn't listen on socket: %s", strerror(errno
));
1029 /* --- Set up the rest of the server --- */
1032 static sel_file serv
;
1033 sockpath
= sun
->sun_path
;
1034 atexit(unlinksocket
);
1035 sel_initfile(&sel
, &serv
, fd
, SEL_READ
, pixserv_accept
, 0);
1040 /*----- Client support code -----------------------------------------------*/
1042 /* --- Variables --- */
1044 static selbuf c_server
, c_client
;
1045 static unsigned c_flags
= 0;
1047 #define cf_uclose 1u
1048 #define cf_sclose 2u
1049 #define cf_cooked 4u
1051 /* --- Line handler functions --- */
1053 static void c_uline(char *s
, size_t len
, void *p
)
1056 selbuf_destroy(&c_client
);
1057 shutdown(c_server
.reader
.fd
, 1);
1058 c_flags
|= cf_uclose
;
1061 if (write(c_server
.reader
.fd
, s
, len
) < 0)
1062 die(EXIT_FAILURE
, "failed to read from stdin: %s", strerror(errno
));
1066 static void c_sline(char *s
, size_t len
, void *p
)
1069 selbuf_destroy(&c_server
);
1070 if (!(c_flags
& cf_uclose
)) {
1071 moan("server closed the connection");
1072 selbuf_destroy(&c_client
);
1076 if (!(c_flags
& cf_cooked
))
1079 char *q
= str_getword(&s
);
1080 if (STRCMP(q
, ==, "FAIL"))
1082 else if (STRCMP(q
, ==, "INFO") ||
1083 STRCMP(q
, ==, "ITEM"))
1085 else if (STRCMP(q
, ==, "OK")) {
1086 if (s
&& *s
) puts(s
);
1087 } else if (STRCMP(q
, ==, "MISSING"))
1090 moan("unexpected output: %s %s", q
, s
);
1094 /* --- @pix_client@ --- *
1096 * Arguments: @struct sockaddr_un *sun@ = pointer to socket address
1097 * @size_t sz@ = size of socket address
1098 * @char *argv[]@ = pointer to arguments to send
1102 * Use: Performs client-side actions for the passphrase pixie.
1105 static void pix_client(struct sockaddr_un
*sun
, size_t sz
, char *argv
[])
1109 /* --- Dispose of locked memory --- */
1113 /* --- Open the socket --- */
1115 if ((fd
= socket(PF_UNIX
, SOCK_STREAM
, 0)) < 0)
1116 die(1, "couldn't create socket: %s", strerror(errno
));
1117 if (connect(fd
, (struct sockaddr
*)sun
, sz
))
1118 die(1, "couldn't connect to server: %s", strerror(errno
));
1119 selbuf_init(&c_server
, &sel
, fd
, c_sline
, 0);
1121 /* --- If there are any arguments, turn them into a string --- */
1124 selbuf_init(&c_client
, &sel
, STDIN_FILENO
, c_uline
, 0);
1133 if (write(fd
, d
.buf
, d
.len
) < 0 || shutdown(fd
, 1))
1134 die(EXIT_FAILURE
, "failed to write command: %s", strerror(errno
));
1135 c_flags
|= cf_uclose
| cf_cooked
;
1139 /* --- And repeat --- */
1142 if (sel_select(&sel
))
1143 die(EXIT_FAILURE
, "select error: %s", strerror(errno
));
1147 /*----- Main code ---------------------------------------------------------*/
1149 /* --- @help@, @version@, @usage@ --- *
1151 * Arguments: @FILE *fp@ = stream to write on
1155 * Use: Emit helpful messages.
1158 static void usage(FILE *fp
)
1162 $ [-qvfidl] [-c COMMAND] [-t TIMEOUT] [-s SOCKET]\n\
1163 $ [-s SOCKET] -C [COMMAND ARGS...]\n\
1164 $ [-s SOCKET] -P[P] TAG\n\
1168 static void version(FILE *fp
)
1170 pquis(fp
, "$, Catacomb version " VERSION
"\n");
1173 static void help(FILE *fp
)
1179 The Catacomb passphrase pixie collects and caches passphrases used to\n\
1180 protect important keys. Options provided:\n\
1182 -h, --help Show this help text.\n\
1183 -V, --version Show the program's version number.\n\
1184 -u, --usage Show a (very) terse usage summary.\n\
1186 -C, --client Connect to a running pixie as a client.\n\
1187 -P, --passphrase Request passphrase TAG and print to stdout.\n\
1188 -PP, --verify-passphrase\n\
1189 Verify passphrase TAG and print to stdout.\n\
1191 -q, --quiet Emit fewer log messages.\n\
1192 -v, --version Emit more log messages.\n\
1193 -s, --socket=FILE Name the pixie's socket.\n\
1194 -r, --replace Replace existing pixie, if one is running.\n\
1195 -c, --command=COMMAND Shell command to read a passphrase.\n\
1196 -f, --fetch Fetch passphrases from the terminal.\n\
1197 -t, --timeout=TIMEOUT Length of time to retain a passphrase in memory.\n\
1198 -i, --interactive Allow commands to be typed interactively.\n\
1199 -d, --daemon Fork into the background after initialization.\n\
1200 -l, --syslog Emit log messages to the system log.\n\
1202 The COMMAND may contain `%m' and `%t' markers which are replaced by a\n\
1203 prompt message and the passphrase tag respectively. The TIMEOUT is an\n\
1204 integer, optionally followed by `d', `h', `m' or `s' to specify units of\n\
1205 days, hours, minutes or seconds respectively.\n\
1207 In client mode, if a command is specified on the command line, it is sent\n\
1208 to the running server; otherwise the program reads requests from stdin.\n\
1209 Responses from the pixie are written to stdout. Send a HELP request for\n\
1210 a quick summary of the pixie communication protocol.\n\
1216 * Arguments: @int argc@ = number of arguments
1217 * @char *argv[]@ = vector of argument values
1219 * Returns: Zero if OK.
1221 * Use: Main program. Listens on a socket and responds with a PGP
1222 * passphrase when asked.
1225 int main(int argc
, char *argv
[])
1228 struct sockaddr_un
*sun
;
1236 #define f_syslog 16u
1238 #define f_verify 64u
1240 /* --- Initialize libraries --- */
1245 /* --- Set up the locked memory area --- */
1248 if (setuid(getuid())) _exit(125);
1250 /* --- Parse command line arguments --- */
1253 static struct option opts
[] = {
1255 /* --- Standard GNUy help options --- */
1257 { "help", 0, 0, 'h' },
1258 { "version", 0, 0, 'V' },
1259 { "usage", 0, 0, 'u' },
1261 /* --- Other options --- */
1263 { "quiet", 0, 0, 'q' },
1264 { "verbose", 0, 0, 'v' },
1265 { "client", 0, 0, 'C' },
1266 { "passphrase", 0, 0, 'P' },
1267 { "verify-passphrase", 0, 0, '+' },
1268 { "socket", OPTF_ARGREQ
, 0, 's' },
1269 { "replace", 0, 0, 'r' },
1270 { "command", OPTF_ARGREQ
, 0, 'c' },
1271 { "fetch", 0, 0, 'f' },
1272 { "timeout", OPTF_ARGREQ
, 0, 't' },
1273 { "interactive", 0, 0, 'i' },
1274 { "stdin", 0, 0, 'i' },
1275 { "daemon", 0, 0, 'd' },
1276 { "log", 0, 0, 'l' },
1277 { "syslog", 0, 0, 'l' },
1279 /* --- Magic terminator --- */
1284 int i
= mdwopt(argc
, argv
, "hVuqvCPs:rc:ft:idl", opts
, 0, 0, 0);
1290 /* --- GNUy help options --- */
1302 /* --- Other interesting things --- */
1322 f
|= f_fetch
| f_verify
;
1332 if ((timeout
= pixserv_timeout(optarg
)) == 0)
1333 die(1, "bad timeout `%s'", optarg
);
1352 /* --- Something else --- */
1361 (optind
< argc
&& !(f
& (f_client
|f_fetch
))) ||
1362 ((f
& f_fetch
) && optind
!= argc
- 1)) {
1367 /* --- Handle request for a passphrase --- */
1370 char *buf
= l_alloc(&lm
, 1024);
1371 passphrase_connect(path
);
1372 if (passphrase_read(argv
[optind
],
1373 (f
& f_verify
) ? PMODE_VERIFY
: PMODE_READ
,
1375 die(1, "failed to read passphrase: %s", strerror(errno
));
1380 /* --- Set up the socket address --- */
1382 sun
= pixie_address(path
, &sz
);
1384 /* --- Initialize selectory --- */
1387 signal(SIGPIPE
, SIG_IGN
);
1389 /* --- Be a client if a client's wanted --- */
1392 pix_client(sun
, sz
, argv
+ optind
);
1394 /* --- Open the syslog if requested --- */
1398 openlog(QUIS
, 0, LOG_DAEMON
);
1401 /* --- Check on the locked memory area --- */
1405 int rc
= l_report(&lm
, &d
);
1407 die(EXIT_FAILURE
, "%s", d
.buf
);
1408 else if (rc
&& verbose
) {
1410 pxlog("couldn't lock passphrase buffer");
1413 arena_setsecure(&lm
.a
);
1416 /* --- Set signal behaviours --- */
1419 static sig sigint
, sigterm
, sigquit
, sighup
;
1420 struct sigaction sa
;
1422 sigaction(SIGINT
, 0, &sa
);
1423 if (sa
.sa_handler
!= SIG_IGN
)
1424 sig_add(&sigint
, SIGINT
, pix_sigdie
, 0);
1425 sig_add(&sigterm
, SIGTERM
, pix_sigdie
, 0);
1426 sig_add(&sigquit
, SIGQUIT
, pix_sigflush
, 0);
1427 sig_add(&sighup
, SIGHUP
, pix_sigflush
, 0);
1430 /* --- Set up the server --- */
1434 pixserv
*px
= pixserv_create(STDIN_FILENO
, STDOUT_FILENO
);
1435 sel_rmtimer(&px
->timer
);
1439 /* --- Fork into the background if requested --- */
1444 if (((f
& f_stdin
) &&
1445 (isatty(STDIN_FILENO
) || isatty(STDOUT_FILENO
))) ||
1446 (!command
&& (flags
& F_FETCH
)))
1447 die(1, "can't become a daemon if terminal required");
1449 if ((kid
= fork()) < 0)
1450 die(1, "fork failed: %s", strerror(errno
));
1456 if ((fd
= open("/dev/tty", O_RDONLY
)) >= 0) {
1457 ioctl(fd
, TIOCNOTTY
);
1462 DISCARD(chdir("/"));
1470 pxlog("initialized ok");
1475 if (!sel_select(&sel
))
1477 else if (errno
!= EINTR
&& errno
!= EAGAIN
) {
1478 pxlog("error from select: %s", strerror(errno
));
1481 pxlog("too many consecutive select errors: bailing out");
1490 /*----- That's all, folks -------------------------------------------------*/