3 * $Id: pixie.c,v 1.13 2004/03/21 22:43:05 mdw Exp $
5 * Passphrase pixie for Catacomb
7 * (c) 1999 Straylight/Edgeware
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of Catacomb.
14 * Catacomb is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Library General Public License as
16 * published by the Free Software Foundation; either version 2 of the
17 * License, or (at your option) any later version.
19 * Catacomb is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
24 * You should have received a copy of the GNU Library General Public
25 * License along with Catacomb; if not, write to the Free
26 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
30 /*----- Revision history --------------------------------------------------*
33 * Revision 1.13 2004/03/21 22:43:05 mdw
34 * Keep quiet about expected errors on incoming connections.
36 * Revision 1.12 2002/01/13 13:50:42 mdw
37 * Various fixes tracking mLib changes.
39 * Revision 1.11 2002/01/13 13:43:05 mdw
40 * Fix bug in daemon mode.
42 * Revision 1.10 2001/02/21 20:03:54 mdw
43 * Handle select errors (by bombing out). Cosmetic tweak.
45 * Revision 1.9 2001/02/03 16:06:44 mdw
46 * Don't set a handler for @SIGINT@ if it's ignored at startup. Add some
47 * error handling for the @select@ loop.
49 * Revision 1.8 2001/01/25 22:19:31 mdw
50 * Make flags be unsigned.
52 * Revision 1.7 2000/12/06 20:33:27 mdw
53 * Make flags be macros rather than enumerations, to ensure that they're
56 * Revision 1.6 2000/10/08 12:06:46 mdw
57 * Change size passed to socket function to be a @size_t@. Insert missing
58 * type name for flag declaration.
60 * Revision 1.5 2000/07/29 22:05:22 mdw
61 * Miscellaneous tidyings:
63 * * Change the timeout to something more appropriate for real use.
65 * * Check assumptions about object types when binding the socket. In
66 * particular, don't zap the socket if it's really something else.
68 * * In @p_request@, return a failure if the shell command returned
69 * nonzero. Fix a bug in @p_get@ which incorrectly passes on a success
70 * code when this happens.
72 * * Dispose of the locked memory in client mode to avoid being
75 * * Also in client mode, don't report closure from the server if we're
76 * running noninteractively.
78 * * Insert a missing option letter into the usage string.
80 * * Change to the root directory after forking in daemon mode.
82 * Revision 1.4 2000/06/17 11:50:53 mdw
83 * New pixie protocol allowing application to request passphrases and send
84 * them to the pixie. Use the secure arena interface for the input
85 * buffer. Extend the input buffer. Other minor fixes.
87 * Revision 1.3 1999/12/22 22:14:40 mdw
88 * Only produce initialization message if verbose.
90 * Revision 1.2 1999/12/22 22:13:42 mdw
91 * Fix bug in passphrase flushing loop.
93 * Revision 1.1 1999/12/22 15:58:41 mdw
94 * Passphrase pixie support.
98 /*----- Header files ------------------------------------------------------*/
113 #include <sys/types.h>
114 #include <sys/time.h>
116 #include <sys/stat.h>
117 #include <sys/wait.h>
120 #include <sys/ioctl.h>
124 #include <sys/socket.h>
127 #include <mLib/alloc.h>
128 #include <mLib/dstr.h>
129 #include <mLib/fdflags.h>
130 #include <mLib/mdwopt.h>
131 #include <mLib/quis.h>
132 #include <mLib/report.h>
133 #include <mLib/sel.h>
134 #include <mLib/selbuf.h>
135 #include <mLib/sig.h>
136 #include <mLib/str.h>
137 #include <mLib/sub.h>
142 #include "passphrase.h"
145 /*----- Static variables --------------------------------------------------*/
147 static unsigned long timeout
= 900;
148 static sel_state sel
;
149 static unsigned verbose
= 1;
150 static const char *command
= 0;
152 static unsigned flags
= 0;
157 /*----- Event logging -----------------------------------------------------*/
161 * Arguments: @const char *p@ = @printf@-style format string
162 * @...@ = extra arguments to fill in
166 * Use: Writes out a timestamped log message.
169 static void log(const char *p
, ...)
174 if (!(flags
& F_SYSLOG
)) {
176 struct tm
*tm
= localtime(&t
);
178 d
.len
+= strftime(d
.buf
, d
.sz
, "%Y-%m-%d %H:%M:%S ", tm
);
181 dstr_vputf(&d
, p
, &ap
);
184 if (flags
& F_SYSLOG
)
185 syslog(LOG_NOTICE
, "%s", d
.buf
);
188 dstr_write(&d
, stderr
);
193 /*----- Passphrase management ---------------------------------------------*/
195 /* --- Data structures --- */
197 typedef struct phrase
{
207 /* --- Variables --- */
209 #define P_ROOT ((phrase *)&p_root)
210 static struct { phrase
*next
; phrase
*prev
; } p_root
= { P_ROOT
, P_ROOT
};
212 /* --- @p_free@ --- *
214 * Arguments: @phrase *p@ = pointer to phrase block
218 * Use: Frees a phrase block.
221 static void p_free(phrase
*p
)
224 sel_rmtimer(&p
->timer
);
227 p
->next
->prev
= p
->prev
;
228 p
->prev
->next
= p
->next
;
232 /* --- @p_timer@ --- *
234 * Arguments: @struct timeval *tv@ = current time
235 * @void *p@ = pointer to phrase
239 * Use: Expires a passphrase.
242 static void p_timer(struct timeval
*tv
, void *p
)
246 log("expiring passphrase `%s'", pp
->tag
);
250 /* --- @p_alloc@ --- *
252 * Arguments: @size_t sz@ = amount of memory required
254 * Returns: Pointer to allocated memory, or null.
256 * Use: Allocates some locked memory, flushing old passphrases if
257 * there's not enough space.
260 static void *p_alloc(size_t sz
)
264 if ((p
= l_alloc(&lm
, sz
)) != 0)
266 if (P_ROOT
->next
== P_ROOT
)
269 log("flushing passphrase `%s' to free up needed space",
272 p_free(P_ROOT
->next
);
276 /* --- @p_find@ --- *
278 * Arguments: @const char *tag@ = pointer to tag to find
280 * Returns: Pointer to passphrase block, or null.
282 * Use: Finds a passphrase with a given tag.
285 static phrase
*p_find(const char *tag
)
289 for (p
= P_ROOT
->next
; p
!= P_ROOT
; p
= p
->next
) {
290 if (strcmp(p
->tag
, tag
) == 0) {
293 sel_rmtimer(&p
->timer
);
294 gettimeofday(&tv
, 0);
296 sel_addtimer(&sel
, &p
->timer
, &tv
, p_timer
, p
);
298 p
->next
->prev
= p
->prev
;
299 p
->prev
->next
= p
->next
;
301 p
->prev
= P_ROOT
->prev
;
302 P_ROOT
->prev
->next
= p
;
312 * Arguments: @const char *tag@ = pointer to tag string
313 * @const char *p@ = pointer to passphrase
314 * @unsigned long t@ = expiry timeout
316 * Returns: Pointer to newly-added passphrase.
318 * Use: Adds a new passphrase. The tag must not already exist.
321 static phrase
*p_add(const char *tag
, const char *p
, unsigned long t
)
323 size_t sz
= strlen(p
) + 1;
324 char *l
= p_alloc(sz
);
327 /* --- Make sure the locked memory was allocated --- */
332 /* --- Fill in some other bits of the block --- */
337 pp
->tag
= xstrdup(tag
);
340 /* --- Set the timer --- */
345 gettimeofday(&tv
, 0);
347 sel_addtimer(&sel
, &pp
->timer
, &tv
, p_timer
, pp
);
350 /* --- Link the block into the chain --- */
353 pp
->prev
= P_ROOT
->prev
;
354 P_ROOT
->prev
->next
= pp
;
359 /* --- @p_flush@ --- *
361 * Arguments: @const char *tag@ = pointer to tag string, or zero for all
365 * Use: Immediately flushes either a single phrase or all of them.
368 static void p_flush(const char *tag
)
372 if (!tag
&& verbose
> 1)
373 log("flushing all passphrases");
375 while (p
!= P_ROOT
) {
376 phrase
*pp
= p
->next
;
379 else if (strcmp(p
->tag
, tag
) == 0) {
381 log("flushing passphrase `%s'", tag
);
389 /*----- Reading passphrases -----------------------------------------------*/
391 /* --- @p_request@ --- *
393 * Arguments: @const char *msg@ = message string
394 * @const char *tag@ = pointer to tag string
395 * @char *buf@ = pointer to (locked) buffer
396 * @size_t sz@ = size of buffer
398 * Returns: Zero if all went well, nonzero otherwise.
400 * Use: Requests a passphrase from the user.
403 static int p_request(const char *msg
, const char *tag
, char *buf
, size_t sz
)
405 /* --- If there's a passphrase-fetching command, run it --- */
415 /* --- Substitute the prompt string into the command --- */
419 const char *q
= strchr(p
, '%');
442 /* --- Create a pipe and start a child process --- */
446 if ((kid
= fork()) < 0)
449 /* --- Child process --- */
453 if (dup2(fd
[1], STDOUT_FILENO
) < 0)
456 execl("/bin/sh", "sh", "-c", d
.buf
, (void *)0);
460 /* --- Read the data back into my buffer --- */
463 if ((r
= read(fd
[0], buf
, sz
- 1)) >= 0) {
464 char *q
= memchr(buf
, '\n', r
);
470 waitpid(kid
, &rc
, 0);
472 if (r
< 0 || rc
!= 0)
476 /* --- Tidy up when things go wrong --- */
487 /* --- Read a passphrase from the terminal --- *
489 * Use the standard Catacomb passphrase-reading function, so it'll read the
490 * passphrase from a file descriptor or something if the appropriate
491 * environment variable is set.
497 dstr_putf(&d
, "%s %s: ", msg
, tag
);
498 rc
= pixie_getpass(d
.buf
, buf
, sz
);
505 /* --- Sort out the buffer --- *
507 * Strip leading spaces.
513 while (isspace((unsigned char)*p
))
516 memmove(buf
, p
, len
);
527 * Arguments: @const char **q@ = where to store the result
528 * @const char *tag@ = pointer to tag string
529 * @unsigned mode@ = reading mode (verify?)
530 * @time_t exp@ = expiry time suggestion
532 * Returns: Zero if successful, @-1@ on a read failure, or @+1@ if the
533 * passphrase is missing and there is no fetcher. (This will
534 * always happen if there is no fetcher and @mode@ is
537 * Use: Reads a passphrase from somewhere.
540 static int p_get(const char **q
, const char *tag
, unsigned mode
, time_t exp
)
547 /* --- Write a log message --- */
550 log("passphrase `%s' requested", tag
);
552 /* --- If there is no fetcher, life is simpler --- */
554 if (!(flags
& F_FETCH
)) {
555 if (mode
== PMODE_VERIFY
)
557 if ((p
= p_find(tag
)) == 0)
563 /* --- Try to find the phrase --- */
565 if (mode
== PMODE_VERIFY
)
567 if (mode
== PMODE_VERIFY
|| (p
= p_find(tag
)) == 0) {
568 if ((pp
= p_alloc(LBUFSZ
)) == 0)
570 if (p_request(mode
== PMODE_READ ?
"Passphrase" : "New passphrase",
571 tag
, pp
, LBUFSZ
) < 0)
573 p
= p_add(tag
, pp
, exp
);
578 /* --- If verification is requested, verify the passphrase --- */
580 if (mode
== PMODE_VERIFY
) {
581 if (!pp
&& (pp
= p_alloc(LBUFSZ
)) == 0)
583 if (p_request("Verify passphrase", tag
, pp
, LBUFSZ
) < 0)
585 if (strcmp(pp
, p
->p
) != 0) {
587 log("passphrases for `%s' don't match", tag
);
593 /* --- Tidy up and return the passphrase --- */
596 memset(pp
, 0, LBUFSZ
);
602 /* --- Tidy up if things went wrong --- */
606 memset(pp
, 0, LBUFSZ
);
614 /*----- Server command parsing --------------------------------------------*/
616 /* --- Data structures --- */
618 typedef struct pixserv
{
627 #define PIXSERV_TIMEOUT 30
629 /* --- @pixserv_expire@ --- *
631 * Arguments: @struct timeval *tv@ = pointer to current time
632 * @void *p@ = pointer to server block
636 * Use: Expires a pixie connection if the remote end decides he's not
637 * interested any more.
640 static void pixserv_expire(struct timeval
*tv
, void *p
)
643 if (px
->fd
!= px
->b
.reader
.fd
)
645 selbuf_destroy(&px
->b
);
646 close(px
->b
.reader
.fd
);
650 /* --- @pixserv_write@ --- *
652 * Arguments: @pixserv *px@ = pointer to server block
653 * @const char *p@ = pointer to skeleton string
654 * @...@ = other arguments to fill in
658 * Use: Formats a string and emits it to the output file.
661 static void pixserv_write(pixserv
*px
, const char *p
, ...)
667 dstr_vputf(&d
, p
, &ap
);
668 write(px
->fd
, d
.buf
, d
.len
);
673 /* --- @pixserv_timeout@ --- *
675 * Arguments: @const char *p@ = pointer to timeout string
677 * Returns: Timeout in seconds.
679 * Use: Translates a string to a timeout value in seconds.
682 static unsigned long pixserv_timeout(const char *p
)
690 t
= strtoul(p
, &q
, 0);
695 case 's': if (q
[1] != 0)
702 /* --- @pixserv_line@ --- *
704 * Arguments: @char *s@ = pointer to the line read
705 * @size_t len@ = length of the line
706 * @void *p@ = pointer to server block
710 * Use: Handles a line read from the client.
713 static void pixserv_line(char *s
, size_t len
, void *p
)
719 /* --- Handle an end-of-file --- */
721 if (!(px
->f
& px_stdin
))
722 sel_rmtimer(&px
->timer
);
724 if (px
->fd
!= px
->b
.reader
.fd
)
726 selbuf_destroy(&px
->b
);
727 close(px
->b
.reader
.fd
);
731 /* --- Fiddle the timeout --- */
733 if (!(px
->f
& px_stdin
)) {
735 gettimeofday(&tv
, 0);
736 tv
.tv_sec
+= PIXSERV_TIMEOUT
;
737 sel_addtimer(&sel
, &px
->timer
, &tv
, pixserv_expire
, px
);
740 /* --- Scan out the first word --- */
742 if ((q
= str_getword(&s
)) == 0)
744 for (qq
= q
; *qq
; qq
++)
745 *qq
= tolower((unsigned char)*qq
);
747 /* --- Handle a help request --- */
749 if (strcmp(q
, "help") == 0) {
751 INFO Commands supported:\n\
754 INFO PASS tag [expire]\n\
755 INFO VERIFY tag [expire]\n\
757 INFO SET tag [expire] -- phrase\n\
763 /* --- List the passphrases --- */
765 else if (strcmp(q
, "list") == 0) {
768 for (p
= P_ROOT
->next
; p
!= P_ROOT
; p
= p
->next
) {
770 pixserv_write(px
, "ITEM %s no-expire\n", p
->tag
);
773 gettimeofday(&tv
, 0);
774 TV_SUB(&tv
, &p
->timer
.tv
, &tv
);
775 pixserv_write(px
, "ITEM %s %i\n", p
->tag
, tv
.tv_sec
);
778 pixserv_write(px
, "OK\n");
781 /* --- Request a passphrase --- */
783 else if ((mode
= PMODE_READ
, strcmp(q
, "pass") == 0) ||
784 (mode
= PMODE_VERIFY
, strcmp(q
, "verify") == 0)) {
789 if ((q
= str_getword(&s
)) == 0)
790 pixserv_write(px
, "FAIL missing tag\n");
791 else if ((t
= pixserv_timeout(s
)) == 0)
792 pixserv_write(px
, "FAIL bad timeout\n");
794 rc
= p_get(&p
, q
, mode
, t
> timeout ? timeout
: t
);
797 pixserv_write(px
, "OK %s\n", p
);
800 pixserv_write(px
, "FAIL error reading passphrase\n");
803 pixserv_write(px
, "MISSING\n");
809 /* --- Flush existing passphrases --- */
811 else if (strcmp(q
, "flush") == 0) {
814 pixserv_write(px
, "OK\n");
817 /* --- Set a passphrase --- */
819 else if (strcmp(q
, "set") == 0) {
822 if ((tag
= str_getword(&s
)) == 0)
823 pixserv_write(px
, "FAIL missing tag\n");
824 else if ((q
= str_getword(&s
)) == 0)
825 pixserv_write(px
, "FAIL no passphrase\n");
827 if (strcmp(q
, "--") != 0) {
828 t
= pixserv_timeout(q
);
831 t
= pixserv_timeout(0);
833 pixserv_write(px
, "FAIL no passphrase\n");
834 else if (strcmp(q
, "--") != 0)
835 pixserv_write(px
, "FAIL rubbish found before passphrase\n");
839 pixserv_write(px
, "OK\n");
844 /* --- Shut the server down --- */
846 else if (strcmp(q
, "quit") == 0) {
848 log("%s client requested shutdown",
849 px
->f
& px_stdin ?
"local" : "remote");
850 pixserv_write(px
, "OK\n");
854 /* --- Report an error for other commands --- */
857 pixserv_write(px
, "FAIL unknown command `%s'\n", q
);
860 /* --- @pixserv_create@ --- *
862 * Arguments: @int fd@ = file descriptor to read from
863 * @int ofd@ = file descriptor to write to
865 * Returns: Pointer to the new connection.
867 * Use: Creates a new Pixie server instance for a new connection.
870 static pixserv
*pixserv_create(int fd
, int ofd
)
872 pixserv
*px
= CREATE(pixserv
);
874 fdflags(fd
, O_NONBLOCK
, O_NONBLOCK
, FD_CLOEXEC
, FD_CLOEXEC
);
876 fdflags(ofd
, O_NONBLOCK
, O_NONBLOCK
, FD_CLOEXEC
, FD_CLOEXEC
);
878 selbuf_init(&px
->b
, &sel
, fd
, pixserv_line
, px
);
879 px
->b
.b
.a
= arena_secure
;
880 selbuf_setsize(&px
->b
, 1024);
881 gettimeofday(&tv
, 0);
882 tv
.tv_sec
+= PIXSERV_TIMEOUT
;
883 sel_addtimer(&sel
, &px
->timer
, &tv
, pixserv_expire
, px
);
888 /* --- @pixserv_accept@ --- *
890 * Arguments: @int fd@ = file descriptor
891 * @unsigned mode@ = what's happened
892 * @void *p@ = an uninteresting argument
896 * Use: Accepts a new connection.
899 static void pixserv_accept(int fd
, unsigned mode
, void *p
)
902 struct sockaddr_un sun
;
903 size_t sunsz
= sizeof(sun
);
905 if (mode
!= SEL_READ
)
907 if ((nfd
= accept(fd
, (struct sockaddr
*)&sun
, &sunsz
)) < 0) {
908 if (verbose
&& errno
!= EAGAIN
&& errno
!= EWOULDBLOCK
&&
909 errno
!= ECONNABORTED
&& errno
!= EPROTO
&& errno
!= EINTR
)
910 log("new connection failed: %s", strerror(errno
));
913 pixserv_create(nfd
, nfd
);
916 /*----- Setting up the server ---------------------------------------------*/
918 /* --- @unlinksocket@ --- *
924 * Use: Tidies up the socket when it's finished with.
927 static char *sockpath
;
929 static void unlinksocket(void)
935 /* --- @pix_sigdie@ --- *
937 * Arguments: @int sig@ = signal number
938 * @void *p@ = uninteresting argument
942 * Use: Shuts down the program after a fatal signal.
945 static void pix_sigdie(int sig
, void *p
)
952 case SIGTERM
: p
= "SIGTERM"; break;
953 case SIGINT
: p
= "SIGINT"; break;
955 sprintf(buf
, "signal %i", sig
);
959 log("shutting down on %s", p
);
964 /* --- @pix_sigflush@ --- *
966 * Arguments: @int sig@ = signal number
967 * @void *p@ = uninteresting argument
971 * Use: Flushes the passphrase cache on receipt of a signal.
974 static void pix_sigflush(int sig
, void *p
)
981 case SIGHUP
: p
= "SIGHUP"; break;
982 case SIGQUIT
: p
= "SIGQUIT"; break;
984 sprintf(buf
, "signal %i", sig
);
988 log("received %s; flushing passphrases", p
);
993 /* --- @pix_setup@ --- *
995 * Arguments: @struct sockaddr_un *sun@ = pointer to address to use
996 * @size_t sz@ = size of socket address
1000 * Use: Sets up the pixie's Unix-domain socket.
1003 static void pix_setup(struct sockaddr_un
*sun
, size_t sz
)
1007 /* --- Set up the parent directory --- */
1010 char *p
= sun
->sun_path
;
1011 char *q
= strrchr(p
, '/');
1017 DPUTM(&d
, p
, q
- p
);
1021 if (stat(d
.buf
, &st
))
1022 die(1, "couldn't stat `%s': %s", d
.buf
, strerror(errno
));
1023 if (!S_ISDIR(st
.st_mode
))
1024 die(1, "object `%s' isn't a directory", d
.buf
);
1025 if (st
.st_mode
& 0077)
1026 die(1, "parent directory `%s' has group or world access", d
.buf
);
1031 /* --- Initialize the socket --- */
1039 if ((fd
= socket(PF_UNIX
, SOCK_STREAM
, 0)) < 0)
1040 die(1, "couldn't create socket: %s", strerror(errno
));
1041 if (bind(fd
, (struct sockaddr
*)sun
, sz
) < 0) {
1043 if (errno
!= EADDRINUSE
)
1044 die(1, "couldn't bind to address: %s", strerror(e
));
1046 die(1, "too many retries; giving up");
1048 if (connect(fd
, (struct sockaddr
*)sun
, sz
)) {
1050 if (errno
!= ECONNREFUSED
)
1051 die(1, "couldn't bind to address: %s", strerror(e
));
1052 if (stat(sun
->sun_path
, &st
))
1053 die(1, "couldn't stat `%s': %s", sun
->sun_path
, strerror(errno
));
1054 if (!S_ISSOCK(st
.st_mode
))
1055 die(1, "object `%s' isn't a socket", sun
->sun_path
);
1057 log("stale socket found; removing it");
1058 unlink(sun
->sun_path
);
1062 log("server already running; shutting it down");
1063 write(fd
, "QUIT\n", 5);
1069 chmod(sun
->sun_path
, 0600);
1070 fdflags(fd
, O_NONBLOCK
, O_NONBLOCK
, FD_CLOEXEC
, FD_CLOEXEC
);
1072 die(1, "couldn't listen on socket: %s", strerror(errno
));
1075 /* --- Set up the rest of the server --- */
1078 static sel_file serv
;
1079 sockpath
= sun
->sun_path
;
1080 atexit(unlinksocket
);
1081 sel_initfile(&sel
, &serv
, fd
, SEL_READ
, pixserv_accept
, 0);
1086 /*----- Client support code -----------------------------------------------*/
1088 /* --- Variables --- */
1090 static selbuf c_server
, c_client
;
1091 static unsigned c_flags
= 0;
1093 #define cf_uclose 1u
1094 #define cf_sclose 2u
1096 /* --- Line handler functions --- */
1098 static void c_uline(char *s
, size_t len
, void *p
)
1101 selbuf_destroy(&c_client
);
1102 shutdown(c_server
.reader
.fd
, 1);
1103 c_flags
|= cf_uclose
;
1106 write(c_server
.reader
.fd
, s
, len
);
1110 static void c_sline(char *s
, size_t len
, void *p
)
1113 selbuf_destroy(&c_server
);
1114 if (!(c_flags
& cf_uclose
)) {
1115 moan("server closed the connection");
1116 selbuf_destroy(&c_client
);
1123 /* --- @pix_client@ --- *
1125 * Arguments: @struct sockaddr_un *sun@ = pointer to socket address
1126 * @size_t sz@ = size of socket address
1127 * @char *argv[]@ = pointer to arguments to send
1131 * Use: Performs client-side actions for the passphrase pixie.
1134 static void pix_client(struct sockaddr_un
*sun
, size_t sz
, char *argv
[])
1138 /* --- Dispose of locked memory --- */
1142 /* --- Open the socket --- */
1144 if ((fd
= socket(PF_UNIX
, SOCK_STREAM
, 0)) < 0)
1145 die(1, "couldn't create socket: %s", strerror(errno
));
1146 if (connect(fd
, (struct sockaddr
*)sun
, sz
))
1147 die(1, "couldn't connect to server: %s", strerror(errno
));
1148 selbuf_init(&c_server
, &sel
, fd
, c_sline
, 0);
1150 /* --- If there are any arguments, turn them into a string --- */
1153 selbuf_init(&c_client
, &sel
, STDIN_FILENO
, c_uline
, 0);
1162 write(fd
, d
.buf
, d
.len
);
1164 c_flags
|= cf_uclose
;
1168 /* --- And repeat --- */
1171 if (sel_select(&sel
))
1172 die(EXIT_FAILURE
, "select error: %s", strerror(errno
));
1176 /*----- Main code ---------------------------------------------------------*/
1178 /* --- @help@, @version@, @usage@ --- *
1180 * Arguments: @FILE *fp@ = stream to write on
1184 * Use: Emit helpful messages.
1187 static void usage(FILE *fp
)
1191 $ [-qvfidl] [-c command] [-t timeout] [-s socket]\n\
1192 $ [-s socket] -C [command args...]\n\
1196 static void version(FILE *fp
)
1198 pquis(fp
, "$, Catacomb version " VERSION
"\n");
1201 static void help(FILE *fp
)
1207 The Catacomb passphrase pixie collects and caches passphrases used to\n\
1208 protect important keys. Options provided:\n\
1210 -h, --help Show this help text.\n\
1211 -V, --version Show the program's version number.\n\
1212 -u, --usage Show a (very) terse usage summary.\n\
1214 -C, --client Connect to a running pixie as a client.\n\
1216 -q, --quiet Emit fewer log messages.\n\
1217 -v, --version Emit more log messages.\n\
1218 -s, --socket=FILE Name the pixie's socket.\n\
1219 -c, --command=COMMAND Shell command to read a passphrase.\n\
1220 -f, --fetch Fetch passphrases from the terminal.\n\
1221 -t, --timeout=TIMEOUT Length of time to retain a passphrase in memory.\n\
1222 -i, --interactive Allow commands to be typed interactively.\n\
1223 -d, --daemon Fork into the background after initialization.\n\
1224 -l, --syslog Emit log messages to the system log.\n\
1226 The COMMAND may contain `%m' and `%t' markers which are replaced by a\n\
1227 prompt message and the passphrase tag respectively. The TIMEOUT is an\n\
1228 integer, optionally followed by `d', `h', `m' or `s' to specify units of\n\
1229 days, hours, minutes or seconds respectively.\n\
1231 In client mode, if a command is specified on the command line, it is sent\n\
1232 to the running server; otherwise the program reads requests from stdin.\n\
1233 Responses from the pixie are written to stdout. Send a HELP request for\n\
1234 a quick summary of the pixie communication protocol.\n\
1240 * Arguments: @int argc@ = number of arguments
1241 * @char *argv[]@ = vector of argument values
1243 * Returns: Zero if OK.
1245 * Use: Main program. Listens on a socket and responds with a PGP
1246 * passphrase when asked.
1249 int main(int argc
, char *argv
[])
1252 struct sockaddr_un
*sun
;
1260 #define f_syslog 16u
1262 /* --- Initialize libraries --- */
1267 /* --- Set up the locked memory area --- */
1272 /* --- Parse command line arguments --- */
1275 static struct option opts
[] = {
1277 /* --- Standard GNUy help options --- */
1279 { "help", 0, 0, 'h' },
1280 { "version", 0, 0, 'V' },
1281 { "usage", 0, 0, 'u' },
1283 /* --- Other options --- */
1285 { "quiet", 0, 0, 'q' },
1286 { "verbose", 0, 0, 'v' },
1287 { "client", 0, 0, 'C' },
1288 { "socket", OPTF_ARGREQ
, 0, 's' },
1289 { "command", OPTF_ARGREQ
, 0, 'c' },
1290 { "fetch", 0, 0, 'f' },
1291 { "timeout", OPTF_ARGREQ
, 0, 't' },
1292 { "interactive", 0, 0, 'i' },
1293 { "stdin", 0, 0, 'i' },
1294 { "daemon", 0, 0, 'd' },
1295 { "log", 0, 0, 'l' },
1296 { "syslog", 0, 0, 'l' },
1298 /* --- Magic terminator --- */
1303 int i
= mdwopt(argc
, argv
, "hVuqvCs:c:ft:idl", opts
, 0, 0, 0);
1309 /* --- GNUy help options --- */
1321 /* --- Other interesting things --- */
1337 if ((timeout
= pixserv_timeout(optarg
)) == 0)
1338 die(1, "bad timeout `%s'", optarg
);
1357 /* --- Something else --- */
1365 if (f
& f_bogus
|| (optind
< argc
&& !(f
& f_client
))) {
1370 /* --- Set up the socket address --- */
1372 sun
= pixie_address(path
, &sz
);
1374 /* --- Initialize selectory --- */
1377 signal(SIGPIPE
, SIG_IGN
);
1379 /* --- Be a client if a client's wanted --- */
1382 pix_client(sun
, sz
, argv
+ optind
);
1384 /* --- Open the syslog if requested --- */
1388 openlog(QUIS
, 0, LOG_DAEMON
);
1391 /* --- Check on the locked memory area --- */
1395 int rc
= l_report(&lm
, &d
);
1397 die(EXIT_FAILURE
, d
.buf
);
1398 else if (rc
&& verbose
) {
1400 log("couldn't lock passphrase buffer");
1403 arena_setsecure(&lm
.a
);
1406 /* --- Set signal behaviours --- */
1409 static sig sigint
, sigterm
, sigquit
, sighup
;
1410 struct sigaction sa
;
1412 sigaction(SIGINT
, 0, &sa
);
1413 if (sa
.sa_handler
!= SIG_IGN
)
1414 sig_add(&sigint
, SIGINT
, pix_sigdie
, 0);
1415 sig_add(&sigterm
, SIGTERM
, pix_sigdie
, 0);
1416 sig_add(&sigquit
, SIGQUIT
, pix_sigflush
, 0);
1417 sig_add(&sighup
, SIGHUP
, pix_sigflush
, 0);
1420 /* --- Set up the server --- */
1424 pixserv
*px
= pixserv_create(STDIN_FILENO
, STDOUT_FILENO
);
1425 sel_rmtimer(&px
->timer
);
1429 /* --- Fork into the background if requested --- */
1434 if (((f
& f_stdin
) &&
1435 (isatty(STDIN_FILENO
) || isatty(STDOUT_FILENO
))) ||
1436 (!command
&& (flags
& F_FETCH
)))
1437 die(1, "can't become a daemon if terminal required");
1439 if ((kid
= fork()) < 0)
1440 die(1, "fork failed: %s", strerror(errno
));
1446 if ((fd
= open("/dev/tty", O_RDONLY
)) >= 0) {
1447 ioctl(fd
, TIOCNOTTY
);
1460 log("initialized ok");
1465 if (!sel_select(&sel
))
1467 else if (errno
!= EINTR
&& errno
!= EAGAIN
) {
1468 log("error from select: %s", strerror(errno
));
1471 log("too many consecutive select errors: bailing out");
1480 /*----- That's all, folks -------------------------------------------------*/