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;
87 /*----- Event logging -----------------------------------------------------*/
91 * Arguments: @const char *p@ = @printf@-style format string
92 * @...@ = extra arguments to fill in
96 * Use: Writes out a timestamped log message.
99 static void PRINTF_LIKE(1, 2) pxlog(const char *p, ...)
104 if (!(flags & F_SYSLOG)) {
106 struct tm *tm = localtime(&t);
108 d.len += strftime(d.buf, d.sz, "%Y-%m-%d %H:%M:%S ", tm);
111 dstr_vputf(&d, p, &ap);
114 if (flags & F_SYSLOG)
115 syslog(LOG_NOTICE, "%s", d.buf);
118 dstr_write(&d, stderr);
123 /*----- Passphrase management ---------------------------------------------*/
125 /* --- Data structures --- */
127 typedef struct phrase {
128 struct phrase *next, *prev;
136 /* --- Variables --- */
138 static phrase *p_head = 0, *p_tail = 0;
140 /* --- Utility macros --- */
142 #define P_LINKTAIL(p) do { \
144 (p)->prev = p_tail; \
145 *(p_tail ? &p_tail->next : &p_head) = (p); \
149 #define P_UNLINK(p) do { \
150 *((p)->next ? &(p)->next->prev : &p_tail) = (p)->prev; \
151 *((p)->prev ? &(p)->prev->next : &p_head) = (p)->next; \
154 /* --- @p_free@ --- *
156 * Arguments: @phrase *p@ = pointer to phrase block
160 * Use: Frees a phrase block.
163 static void p_free(phrase *p)
166 sel_rmtimer(&p->timer);
173 /* --- @p_timer@ --- *
175 * Arguments: @struct timeval *tv@ = current time
176 * @void *p@ = pointer to phrase
180 * Use: Expires a passphrase.
183 static void p_timer(struct timeval *tv, void *p)
187 pxlog("expiring passphrase `%s'", pp->tag);
191 /* --- @p_alloc@ --- *
193 * Arguments: @size_t sz@ = amount of memory required
195 * Returns: Pointer to allocated memory, or null.
197 * Use: Allocates some locked memory, flushing old passphrases if
198 * there's not enough space.
201 static void *p_alloc(size_t sz)
205 if ((p = l_alloc(&lm, sz)) != 0)
210 pxlog("flushing passphrase `%s' to free up needed space",
217 /* --- @p_find@ --- *
219 * Arguments: @const char *tag@ = pointer to tag to find
221 * Returns: Pointer to passphrase block, or null.
223 * Use: Finds a passphrase with a given tag.
226 static phrase *p_find(const char *tag)
230 for (p = p_head; p; p = p->next) {
231 if (strcmp(p->tag, tag) == 0) {
234 sel_rmtimer(&p->timer);
235 gettimeofday(&tv, 0);
237 sel_addtimer(&sel, &p->timer, &tv, p_timer, p);
249 * Arguments: @const char *tag@ = pointer to tag string
250 * @const char *p@ = pointer to passphrase
251 * @unsigned long t@ = expiry timeout
253 * Returns: Pointer to newly-added passphrase.
255 * Use: Adds a new passphrase. The tag must not already exist.
258 static phrase *p_add(const char *tag, const char *p, unsigned long t)
260 size_t sz = strlen(p) + 1;
261 char *l = p_alloc(sz);
264 /* --- Make sure the locked memory was allocated --- */
269 /* --- Fill in some other bits of the block --- */
274 pp->tag = xstrdup(tag);
277 /* --- Set the timer --- */
282 gettimeofday(&tv, 0);
284 sel_addtimer(&sel, &pp->timer, &tv, p_timer, pp);
287 /* --- Link the block into the chain --- */
293 /* --- @p_flush@ --- *
295 * Arguments: @const char *tag@ = pointer to tag string, or zero for all
299 * Use: Immediately flushes either a single phrase or all of them.
302 static void p_flush(const char *tag)
306 if (!tag && verbose > 1)
307 pxlog("flushing all passphrases");
310 phrase *pp = p->next;
313 else if (strcmp(p->tag, tag) == 0) {
315 pxlog("flushing passphrase `%s'", tag);
323 /*----- Reading passphrases -----------------------------------------------*/
325 /* --- @p_request@ --- *
327 * Arguments: @const char *msg@ = message string
328 * @const char *tag@ = pointer to tag string
329 * @char *buf@ = pointer to (locked) buffer
330 * @size_t sz@ = size of buffer
332 * Returns: Zero if all went well, nonzero otherwise.
334 * Use: Requests a passphrase from the user.
337 static int p_request(const char *msg, const char *tag, char *buf, size_t sz)
339 /* --- If there's a passphrase-fetching command, run it --- */
349 /* --- Substitute the prompt string into the command --- */
353 const char *q = strchr(p, '%');
376 /* --- Create a pipe and start a child process --- */
380 if ((kid = fork()) < 0)
383 /* --- Child process --- */
387 if (dup2(fd[1], STDOUT_FILENO) < 0)
390 execl("/bin/sh", "sh", "-c", d.buf, (char *)0);
394 /* --- Read the data back into my buffer --- */
397 if ((r = read(fd[0], buf, sz - 1)) >= 0) {
398 char *q = memchr(buf, '\n', r);
404 waitpid(kid, &rc, 0);
406 if (r < 0 || rc != 0)
410 /* --- Tidy up when things go wrong --- */
421 /* --- Read a passphrase from the terminal --- *
423 * Use the standard Catacomb passphrase-reading function, so it'll read the
424 * passphrase from a file descriptor or something if the appropriate
425 * environment variable is set.
431 dstr_putf(&d, "%s %s: ", msg, tag);
432 rc = pixie_getpass(d.buf, buf, sz);
439 /* --- Sort out the buffer --- *
441 * Strip leading spaces.
447 while (isspace((unsigned char)*p))
450 memmove(buf, p, len);
461 * Arguments: @const char **q@ = where to store the result
462 * @const char *tag@ = pointer to tag string
463 * @unsigned mode@ = reading mode (verify?)
464 * @time_t exp@ = expiry time suggestion
466 * Returns: Zero if successful, @-1@ on a read failure, or @+1@ if the
467 * passphrase is missing and there is no fetcher. (This will
468 * always happen if there is no fetcher and @mode@ is
471 * Use: Reads a passphrase from somewhere.
474 static int p_get(const char **q, const char *tag, unsigned mode, time_t exp)
481 /* --- Write a log message --- */
484 pxlog("passphrase `%s' requested", tag);
486 /* --- If there is no fetcher, life is simpler --- */
488 if (!(flags & F_FETCH)) {
489 if (mode == PMODE_VERIFY)
491 if ((p = p_find(tag)) == 0)
497 /* --- Try to find the phrase --- */
499 if (mode == PMODE_VERIFY)
501 if (mode == PMODE_VERIFY || (p = p_find(tag)) == 0) {
502 if ((pp = p_alloc(LBUFSZ)) == 0)
504 if (p_request(mode == PMODE_READ ? "Passphrase" : "New passphrase",
505 tag, pp, LBUFSZ) < 0)
507 p = p_add(tag, pp, exp);
512 /* --- If verification is requested, verify the passphrase --- */
514 if (mode == PMODE_VERIFY) {
515 if (!pp && (pp = p_alloc(LBUFSZ)) == 0)
517 if (p_request("Verify passphrase", tag, pp, LBUFSZ) < 0)
519 if (strcmp(pp, p->p) != 0) {
521 pxlog("passphrases for `%s' don't match", tag);
527 /* --- Tidy up and return the passphrase --- */
530 memset(pp, 0, LBUFSZ);
536 /* --- Tidy up if things went wrong --- */
540 memset(pp, 0, LBUFSZ);
548 /*----- Server command parsing --------------------------------------------*/
550 /* --- Data structures --- */
552 typedef struct pixserv {
561 #define PIXSERV_TIMEOUT 30
563 /* --- @pixserv_expire@ --- *
565 * Arguments: @struct timeval *tv@ = pointer to current time
566 * @void *p@ = pointer to server block
570 * Use: Expires a pixie connection if the remote end decides he's not
571 * interested any more.
574 static void pixserv_expire(struct timeval *tv, void *p)
577 if (px->fd != px->b.reader.fd)
579 selbuf_destroy(&px->b);
580 close(px->b.reader.fd);
584 /* --- @pixserv_write@ --- *
586 * Arguments: @pixserv *px@ = pointer to server block
587 * @const char *p@ = pointer to skeleton string
588 * @...@ = other arguments to fill in
590 * Returns: Zero on success, @-1@ on error.
592 * Use: Formats a string and emits it to the output file.
595 static int PRINTF_LIKE(2, 3) pixserv_write(pixserv *px, const char *p, ...)
602 dstr_vputf(&d, p, &ap);
603 rc = write(px->fd, d.buf, d.len);
607 pxlog("failed to write to client: %s (closing)", strerror(errno));
613 /* --- @pixserv_timeout@ --- *
615 * Arguments: @const char *p@ = pointer to timeout string
617 * Returns: Timeout in seconds.
619 * Use: Translates a string to a timeout value in seconds.
622 static unsigned long pixserv_timeout(const char *p)
630 t = strtoul(p, &q, 0);
635 case 's': if (q[1] != 0)
642 /* --- @pixserv_line@ --- *
644 * Arguments: @char *s@ = pointer to the line read
645 * @size_t len@ = length of the line
646 * @void *p@ = pointer to server block
650 * Use: Handles a line read from the client.
653 static void pixserv_line(char *s, size_t len, void *p)
659 /* --- Handle an end-of-file --- */
661 if (!(px->f & px_stdin))
662 sel_rmtimer(&px->timer);
665 /* --- Fiddle the timeout --- */
667 if (!(px->f & px_stdin)) {
669 gettimeofday(&tv, 0);
670 tv.tv_sec += PIXSERV_TIMEOUT;
671 sel_addtimer(&sel, &px->timer, &tv, pixserv_expire, px);
674 /* --- Scan out the first word --- */
676 if ((q = str_getword(&s)) == 0)
678 for (qq = q; *qq; qq++)
679 *qq = tolower((unsigned char)*qq);
681 /* --- Handle a help request --- */
683 if (strcmp(q, "help") == 0) {
684 if (pixserv_write(px, "\
685 INFO Commands supported:\n\
688 INFO PASS tag [expire]\n\
689 INFO VERIFY tag [expire]\n\
691 INFO SET tag [expire] -- phrase\n\
698 /* --- List the passphrases --- */
700 else if (strcmp(q, "list") == 0) {
703 for (p = p_head; p; p = p->next) {
705 if (pixserv_write(px, "ITEM %s no-expire\n", p->tag)) goto close;
708 gettimeofday(&tv, 0);
709 TV_SUB(&tv, &p->timer.tv, &tv);
710 if (pixserv_write(px, "ITEM %s %lu\n",
711 p->tag, (unsigned long)tv.tv_sec))
715 if (pixserv_write(px, "OK\n")) goto close;
718 /* --- Request a passphrase --- */
720 else if ((mode = PMODE_READ, strcmp(q, "pass") == 0) ||
721 (mode = PMODE_VERIFY, strcmp(q, "verify") == 0)) {
726 if ((q = str_getword(&s)) == 0)
727 pixserv_write(px, "FAIL missing tag\n");
728 else if ((t = pixserv_timeout(s)) == 0)
729 pixserv_write(px, "FAIL bad timeout\n");
731 rc = p_get(&p, q, mode, t > timeout ? timeout : t);
734 if (pixserv_write(px, "OK %s\n", p)) goto close;
737 if (pixserv_write(px, "FAIL error reading passphrase\n"))
741 if (pixserv_write(px, "MISSING\n")) goto close;
747 /* --- Flush existing passphrases --- */
749 else if (strcmp(q, "flush") == 0) {
752 if (pixserv_write(px, "OK\n")) goto close;
755 /* --- Set a passphrase --- */
757 else if (strcmp(q, "set") == 0) {
760 if ((tag = str_getword(&s)) == 0) {
761 if (pixserv_write(px, "FAIL missing tag\n")) goto close;
762 } else if ((q = str_getword(&s)) == 0) {
763 if (pixserv_write(px, "FAIL no passphrase\n")) goto close;
765 if (strcmp(q, "--") != 0) {
766 t = pixserv_timeout(q);
769 t = pixserv_timeout(0);
771 if (pixserv_write(px, "FAIL no passphrase\n")) goto close;
772 } else if (strcmp(q, "--") != 0) {
773 if (pixserv_write(px, "FAIL rubbish found before passphrase\n"))
778 if (pixserv_write(px, "OK\n")) goto close;
783 /* --- Shut the server down --- */
785 else if (strcmp(q, "quit") == 0) {
787 pxlog("%s client requested shutdown",
788 px->f & px_stdin ? "local" : "remote");
790 if (pixserv_write(px, "OK\n")) goto close;
794 /* --- Report an error for other commands --- */
797 if (pixserv_write(px, "FAIL unknown command `%s'\n", q)) goto close;
802 if (px->fd != px->b.reader.fd)
804 selbuf_destroy(&px->b);
805 close(px->b.reader.fd);
808 /* --- @pixserv_create@ --- *
810 * Arguments: @int fd@ = file descriptor to read from
811 * @int ofd@ = file descriptor to write to
813 * Returns: Pointer to the new connection.
815 * Use: Creates a new Pixie server instance for a new connection.
818 static pixserv *pixserv_create(int fd, int ofd)
820 pixserv *px = CREATE(pixserv);
822 fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
824 fdflags(ofd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
826 selbuf_init(&px->b, &sel, fd, pixserv_line, px);
827 px->b.b.a = arena_secure;
828 selbuf_setsize(&px->b, 1024);
829 gettimeofday(&tv, 0);
830 tv.tv_sec += PIXSERV_TIMEOUT;
831 sel_addtimer(&sel, &px->timer, &tv, pixserv_expire, px);
836 /* --- @pixserv_accept@ --- *
838 * Arguments: @int fd@ = file descriptor
839 * @unsigned mode@ = what's happened
840 * @void *p@ = an uninteresting argument
844 * Use: Accepts a new connection.
847 static void pixserv_accept(int fd, unsigned mode, void *p)
850 struct sockaddr_un sun;
851 size_t sunsz = sizeof(sun);
853 if (mode != SEL_READ)
855 if ((nfd = accept(fd, (struct sockaddr *)&sun, &sunsz)) < 0) {
856 if (verbose && errno != EAGAIN && errno != EWOULDBLOCK &&
857 errno != ECONNABORTED && errno != EPROTO && errno != EINTR)
858 pxlog("new connection failed: %s", strerror(errno));
861 pixserv_create(nfd, nfd);
864 /*----- Setting up the server ---------------------------------------------*/
866 /* --- @unlinksocket@ --- *
872 * Use: Tidies up the socket when it's finished with.
875 static char *sockpath;
877 static void unlinksocket(void)
883 /* --- @pix_sigdie@ --- *
885 * Arguments: @int sig@ = signal number
886 * @void *p@ = uninteresting argument
890 * Use: Shuts down the program after a fatal signal.
893 static void pix_sigdie(int sig, void *p)
900 case SIGTERM: p = "SIGTERM"; break;
901 case SIGINT: p = "SIGINT"; break;
903 sprintf(buf, "signal %i", sig);
907 pxlog("shutting down on %s", p);
912 /* --- @pix_sigflush@ --- *
914 * Arguments: @int sig@ = signal number
915 * @void *p@ = uninteresting argument
919 * Use: Flushes the passphrase cache on receipt of a signal.
922 static void pix_sigflush(int sig, void *p)
929 case SIGHUP: p = "SIGHUP"; break;
930 case SIGQUIT: p = "SIGQUIT"; break;
932 sprintf(buf, "signal %i", sig);
936 pxlog("received %s; flushing passphrases", p);
941 /* --- @pix_setup@ --- *
943 * Arguments: @struct sockaddr_un *sun@ = pointer to address to use
944 * @size_t sz@ = size of socket address
948 * Use: Sets up the pixie's Unix-domain socket.
951 static void pix_setup(struct sockaddr_un *sun, size_t sz)
955 /* --- Set up the parent directory --- */
958 char *p = sun->sun_path;
959 char *q = strrchr(p, '/');
969 if (stat(d.buf, &st))
970 die(1, "couldn't stat `%s': %s", d.buf, strerror(errno));
971 if (!S_ISDIR(st.st_mode))
972 die(1, "object `%s' isn't a directory", d.buf);
973 if (st.st_mode & 0077)
974 die(1, "parent directory `%s' has group or world access", d.buf);
979 /* --- Initialize the socket --- */
987 if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
988 die(1, "couldn't create socket: %s", strerror(errno));
989 if (bind(fd, (struct sockaddr *)sun, sz) < 0) {
991 if (errno != EADDRINUSE)
992 die(1, "couldn't bind to address: %s", strerror(e));
994 die(1, "too many retries; giving up");
996 if (connect(fd, (struct sockaddr *)sun, sz)) {
998 if (errno != ECONNREFUSED)
999 die(1, "couldn't bind to address: %s", strerror(e));
1000 if (stat(sun->sun_path, &st))
1001 die(1, "couldn't stat `%s': %s", sun->sun_path, strerror(errno));
1002 if (!S_ISSOCK(st.st_mode))
1003 die(1, "object `%s' isn't a socket", sun->sun_path);
1005 pxlog("stale socket found; removing it");
1006 unlink(sun->sun_path);
1010 pxlog("server already running; shutting it down");
1011 if (write(fd, "QUIT\n", 5) < 0) {
1012 die(EXIT_FAILURE, "failed to shut down old server: %s",
1020 chmod(sun->sun_path, 0600);
1021 fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
1023 die(1, "couldn't listen on socket: %s", strerror(errno));
1026 /* --- Set up the rest of the server --- */
1029 static sel_file serv;
1030 sockpath = sun->sun_path;
1031 atexit(unlinksocket);
1032 sel_initfile(&sel, &serv, fd, SEL_READ, pixserv_accept, 0);
1037 /*----- Client support code -----------------------------------------------*/
1039 /* --- Variables --- */
1041 static selbuf c_server, c_client;
1042 static unsigned c_flags = 0;
1044 #define cf_uclose 1u
1045 #define cf_sclose 2u
1046 #define cf_cooked 4u
1048 /* --- Line handler functions --- */
1050 static void c_uline(char *s, size_t len, void *p)
1053 selbuf_destroy(&c_client);
1054 shutdown(c_server.reader.fd, 1);
1055 c_flags |= cf_uclose;
1058 if (write(c_server.reader.fd, s, len) < 0)
1059 die(EXIT_FAILURE, "failed to read from stdin: %s", strerror(errno));
1063 static void c_sline(char *s, size_t len, void *p)
1066 selbuf_destroy(&c_server);
1067 if (!(c_flags & cf_uclose)) {
1068 moan("server closed the connection");
1069 selbuf_destroy(&c_client);
1073 if (!(c_flags & cf_cooked))
1076 char *q = str_getword(&s);
1077 if (strcmp(q, "FAIL") == 0)
1079 else if (strcmp(q, "INFO") == 0 ||
1080 strcmp(q, "ITEM") == 0)
1082 else if (strcmp(q, "OK") == 0) {
1083 if (s && *s) puts(s);
1084 } else if (strcmp(q, "MISSING") == 0)
1087 moan("unexpected output: %s %s", q, s);
1091 /* --- @pix_client@ --- *
1093 * Arguments: @struct sockaddr_un *sun@ = pointer to socket address
1094 * @size_t sz@ = size of socket address
1095 * @char *argv[]@ = pointer to arguments to send
1099 * Use: Performs client-side actions for the passphrase pixie.
1102 static void pix_client(struct sockaddr_un *sun, size_t sz, char *argv[])
1106 /* --- Dispose of locked memory --- */
1110 /* --- Open the socket --- */
1112 if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
1113 die(1, "couldn't create socket: %s", strerror(errno));
1114 if (connect(fd, (struct sockaddr *)sun, sz))
1115 die(1, "couldn't connect to server: %s", strerror(errno));
1116 selbuf_init(&c_server, &sel, fd, c_sline, 0);
1118 /* --- If there are any arguments, turn them into a string --- */
1121 selbuf_init(&c_client, &sel, STDIN_FILENO, c_uline, 0);
1130 if (write(fd, d.buf, d.len) < 0 || shutdown(fd, 1))
1131 die(EXIT_FAILURE, "failed to write command: %s", strerror(errno));
1132 c_flags |= cf_uclose | cf_cooked;
1136 /* --- And repeat --- */
1139 if (sel_select(&sel))
1140 die(EXIT_FAILURE, "select error: %s", strerror(errno));
1144 /*----- Main code ---------------------------------------------------------*/
1146 /* --- @help@, @version@, @usage@ --- *
1148 * Arguments: @FILE *fp@ = stream to write on
1152 * Use: Emit helpful messages.
1155 static void usage(FILE *fp)
1159 $ [-qvfidl] [-c COMMAND] [-t TIMEOUT] [-s SOCKET]\n\
1160 $ [-s SOCKET] -C [COMMAND ARGS...]\n\
1161 $ [-s SOCKET] -P[P] TAG\n\
1165 static void version(FILE *fp)
1167 pquis(fp, "$, Catacomb version " VERSION "\n");
1170 static void help(FILE *fp)
1176 The Catacomb passphrase pixie collects and caches passphrases used to\n\
1177 protect important keys. Options provided:\n\
1179 -h, --help Show this help text.\n\
1180 -V, --version Show the program's version number.\n\
1181 -u, --usage Show a (very) terse usage summary.\n\
1183 -C, --client Connect to a running pixie as a client.\n\
1184 -P, --passphrase Request passphrase TAG and print to stdout.\n\
1185 -PP, --verify-passphrase\n\
1186 Verify passphrase TAG and print to stdout.\n\
1188 -q, --quiet Emit fewer log messages.\n\
1189 -v, --version Emit more log messages.\n\
1190 -s, --socket=FILE Name the pixie's socket.\n\
1191 -c, --command=COMMAND Shell command to read a passphrase.\n\
1192 -f, --fetch Fetch passphrases from the terminal.\n\
1193 -t, --timeout=TIMEOUT Length of time to retain a passphrase in memory.\n\
1194 -i, --interactive Allow commands to be typed interactively.\n\
1195 -d, --daemon Fork into the background after initialization.\n\
1196 -l, --syslog Emit log messages to the system log.\n\
1198 The COMMAND may contain `%m' and `%t' markers which are replaced by a\n\
1199 prompt message and the passphrase tag respectively. The TIMEOUT is an\n\
1200 integer, optionally followed by `d', `h', `m' or `s' to specify units of\n\
1201 days, hours, minutes or seconds respectively.\n\
1203 In client mode, if a command is specified on the command line, it is sent\n\
1204 to the running server; otherwise the program reads requests from stdin.\n\
1205 Responses from the pixie are written to stdout. Send a HELP request for\n\
1206 a quick summary of the pixie communication protocol.\n\
1212 * Arguments: @int argc@ = number of arguments
1213 * @char *argv[]@ = vector of argument values
1215 * Returns: Zero if OK.
1217 * Use: Main program. Listens on a socket and responds with a PGP
1218 * passphrase when asked.
1221 int main(int argc, char *argv[])
1224 struct sockaddr_un *sun;
1232 #define f_syslog 16u
1234 #define f_verify 64u
1236 /* --- Initialize libraries --- */
1241 /* --- Set up the locked memory area --- */
1246 /* --- Parse command line arguments --- */
1249 static struct option opts[] = {
1251 /* --- Standard GNUy help options --- */
1253 { "help", 0, 0, 'h' },
1254 { "version", 0, 0, 'V' },
1255 { "usage", 0, 0, 'u' },
1257 /* --- Other options --- */
1259 { "quiet", 0, 0, 'q' },
1260 { "verbose", 0, 0, 'v' },
1261 { "client", 0, 0, 'C' },
1262 { "passphrase", 0, 0, 'P' },
1263 { "verify-passphrase", 0, 0, '+' },
1264 { "socket", OPTF_ARGREQ, 0, 's' },
1265 { "command", OPTF_ARGREQ, 0, 'c' },
1266 { "fetch", 0, 0, 'f' },
1267 { "timeout", OPTF_ARGREQ, 0, 't' },
1268 { "interactive", 0, 0, 'i' },
1269 { "stdin", 0, 0, 'i' },
1270 { "daemon", 0, 0, 'd' },
1271 { "log", 0, 0, 'l' },
1272 { "syslog", 0, 0, 'l' },
1274 /* --- Magic terminator --- */
1279 int i = mdwopt(argc, argv, "hVuqvCPs:c:ft:idl", opts, 0, 0, 0);
1285 /* --- GNUy help options --- */
1297 /* --- Other interesting things --- */
1317 f |= f_fetch | f_verify;
1324 if ((timeout = pixserv_timeout(optarg)) == 0)
1325 die(1, "bad timeout `%s'", optarg);
1344 /* --- Something else --- */
1353 (optind < argc && !(f & (f_client|f_fetch))) ||
1354 ((f & f_fetch) && optind != argc - 1)) {
1359 /* --- Handle request for a passphrase --- */
1362 char *buf = l_alloc(&lm, 1024);
1363 passphrase_connect(path);
1364 if (passphrase_read(argv[optind],
1365 (f & f_verify) ? PMODE_VERIFY : PMODE_READ,
1367 die(1, "failed to read passphrase: %s", strerror(errno));
1372 /* --- Set up the socket address --- */
1374 sun = pixie_address(path, &sz);
1376 /* --- Initialize selectory --- */
1379 signal(SIGPIPE, SIG_IGN);
1381 /* --- Be a client if a client's wanted --- */
1384 pix_client(sun, sz, argv + optind);
1386 /* --- Open the syslog if requested --- */
1390 openlog(QUIS, 0, LOG_DAEMON);
1393 /* --- Check on the locked memory area --- */
1397 int rc = l_report(&lm, &d);
1399 die(EXIT_FAILURE, "%s", d.buf);
1400 else if (rc && verbose) {
1402 pxlog("couldn't lock passphrase buffer");
1405 arena_setsecure(&lm.a);
1408 /* --- Set signal behaviours --- */
1411 static sig sigint, sigterm, sigquit, sighup;
1412 struct sigaction sa;
1414 sigaction(SIGINT, 0, &sa);
1415 if (sa.sa_handler != SIG_IGN)
1416 sig_add(&sigint, SIGINT, pix_sigdie, 0);
1417 sig_add(&sigterm, SIGTERM, pix_sigdie, 0);
1418 sig_add(&sigquit, SIGQUIT, pix_sigflush, 0);
1419 sig_add(&sighup, SIGHUP, pix_sigflush, 0);
1422 /* --- Set up the server --- */
1426 pixserv *px = pixserv_create(STDIN_FILENO, STDOUT_FILENO);
1427 sel_rmtimer(&px->timer);
1431 /* --- Fork into the background if requested --- */
1436 if (((f & f_stdin) &&
1437 (isatty(STDIN_FILENO) || isatty(STDOUT_FILENO))) ||
1438 (!command && (flags & F_FETCH)))
1439 die(1, "can't become a daemon if terminal required");
1441 if ((kid = fork()) < 0)
1442 die(1, "fork failed: %s", strerror(errno));
1448 if ((fd = open("/dev/tty", O_RDONLY)) >= 0) {
1449 ioctl(fd, TIOCNOTTY);
1454 DISCARD(chdir("/"));
1462 pxlog("initialized ok");
1467 if (!sel_select(&sel))
1469 else if (errno != EINTR && errno != EAGAIN) {
1470 pxlog("error from select: %s", strerror(errno));
1473 pxlog("too many consecutive select errors: bailing out");
1482 /*----- That's all, folks -------------------------------------------------*/