X-Git-Url: https://git.distorted.org.uk/u/mdw/catacomb/blobdiff_plain/c725c20d3fdc06e623d788de4a4f73be20fde7db..902cbb33a6f9b90989a635d9e7d62996367d8b15:/progs/pixie.c diff --git a/progs/pixie.c b/progs/pixie.c index 1198fcb..1d2be67 100644 --- a/progs/pixie.c +++ b/progs/pixie.c @@ -83,6 +83,7 @@ static unsigned flags = 0; #define F_SYSLOG 1u #define F_FETCH 2u +#define F_REPLACE 4u /*----- Event logging -----------------------------------------------------*/ @@ -96,7 +97,7 @@ static unsigned flags = 0; * Use: Writes out a timestamped log message. */ -static void pxlog(const char *p, ...) +static void PRINTF_LIKE(1, 2) pxlog(const char *p, ...) { dstr d = DSTR_INIT; va_list ap; @@ -125,8 +126,7 @@ static void pxlog(const char *p, ...) /* --- Data structures --- */ typedef struct phrase { - struct phrase *next; - struct phrase *prev; + struct phrase *next, *prev; char *tag; char *p; unsigned long t; @@ -136,8 +136,21 @@ typedef struct phrase { /* --- Variables --- */ -#define P_ROOT ((phrase *)&p_root) -static struct { phrase *next; phrase *prev; } p_root = { P_ROOT, P_ROOT }; +static phrase *p_head = 0, *p_tail = 0; + +/* --- Utility macros --- */ + +#define P_LINKTAIL(p) do { \ + (p)->next = 0; \ + (p)->prev = p_tail; \ + *(p_tail ? &p_tail->next : &p_head) = (p); \ + p_tail = (p); \ +} while (0) + +#define P_UNLINK(p) do { \ + *((p)->next ? &(p)->next->prev : &p_tail) = (p)->prev; \ + *((p)->prev ? &(p)->prev->next : &p_head) = (p)->next; \ +} while (0) /* --- @p_free@ --- * * @@ -154,8 +167,7 @@ static void p_free(phrase *p) sel_rmtimer(&p->timer); xfree(p->tag); l_free(&lm, p->p); - p->next->prev = p->prev; - p->prev->next = p->next; + P_UNLINK(p); DESTROY(p); } @@ -193,13 +205,13 @@ static void *p_alloc(size_t sz) char *p; if ((p = l_alloc(&lm, sz)) != 0) return (p); - if (P_ROOT->next == P_ROOT) + if (!p_head) return (0); if (verbose) { pxlog("flushing passphrase `%s' to free up needed space", p_head->tag); } - p_free(P_ROOT->next); + p_free(p_head); } } @@ -216,7 +228,7 @@ static phrase *p_find(const char *tag) { phrase *p; - for (p = P_ROOT->next; p != P_ROOT; p = p->next) { + for (p = p_head; p; p = p->next) { if (strcmp(p->tag, tag) == 0) { if (p->t) { struct timeval tv; @@ -225,12 +237,8 @@ static phrase *p_find(const char *tag) tv.tv_sec += p->t; sel_addtimer(&sel, &p->timer, &tv, p_timer, p); } - p->next->prev = p->prev; - p->prev->next = p->next; - p->next = P_ROOT; - p->prev = P_ROOT->prev; - P_ROOT->prev->next = p; - P_ROOT->prev = p; + P_UNLINK(p); + P_LINKTAIL(p); return (p); } } @@ -279,10 +287,7 @@ static phrase *p_add(const char *tag, const char *p, unsigned long t) /* --- Link the block into the chain --- */ - pp->next = P_ROOT; - pp->prev = P_ROOT->prev; - P_ROOT->prev->next = pp; - P_ROOT->prev = pp; + P_LINKTAIL(pp); return (pp); } @@ -301,8 +306,8 @@ static void p_flush(const char *tag) if (!tag && verbose > 1) pxlog("flushing all passphrases"); - p = P_ROOT->next; - while (p != P_ROOT) { + p = p_head; + while (p) { phrase *pp = p->next; if (!tag) p_free(p); @@ -583,21 +588,27 @@ static void pixserv_expire(struct timeval *tv, void *p) * @const char *p@ = pointer to skeleton string * @...@ = other arguments to fill in * - * Returns: --- + * Returns: Zero on success, @-1@ on error. * * Use: Formats a string and emits it to the output file. */ -static void pixserv_write(pixserv *px, const char *p, ...) +static int PRINTF_LIKE(2, 3) pixserv_write(pixserv *px, const char *p, ...) { dstr d = DSTR_INIT; va_list ap; + int rc; va_start(ap, p); dstr_vputf(&d, p, &ap); - write(px->fd, d.buf, d.len); + rc = write(px->fd, d.buf, d.len); va_end(ap); dstr_destroy(&d); + if (rc < 0) { + pxlog("failed to write to client: %s (closing)", strerror(errno)); + return (-1); + } + return (0); } /* --- @pixserv_timeout@ --- * @@ -650,13 +661,7 @@ static void pixserv_line(char *s, size_t len, void *p) if (!(px->f & px_stdin)) sel_rmtimer(&px->timer); - if (!s) { - if (px->fd != px->b.reader.fd) - close(px->fd); - selbuf_destroy(&px->b); - close(px->b.reader.fd); - return; - } + if (!s) goto close; /* --- Fiddle the timeout --- */ @@ -677,7 +682,7 @@ static void pixserv_line(char *s, size_t len, void *p) /* --- Handle a help request --- */ if (strcmp(q, "help") == 0) { - pixserv_write(px, "\ + if (pixserv_write(px, "\ INFO Commands supported:\n\ INFO HELP\n\ INFO LIST\n\ @@ -687,7 +692,8 @@ INFO FLUSH [tag]\n\ INFO SET tag [expire] -- phrase\n\ INFO QUIT\n\ OK\n\ -"); +")) + goto close; } /* --- List the passphrases --- */ @@ -695,17 +701,19 @@ OK\n\ else if (strcmp(q, "list") == 0) { phrase *p; - for (p = P_ROOT->next; p != P_ROOT; p = p->next) { - if (!p->t) - pixserv_write(px, "ITEM %s no-expire\n", p->tag); - else { + for (p = p_head; p; p = p->next) { + if (!p->t) { + if (pixserv_write(px, "ITEM %s no-expire\n", p->tag)) goto close; + } else { struct timeval tv; gettimeofday(&tv, 0); TV_SUB(&tv, &p->timer.tv, &tv); - pixserv_write(px, "ITEM %s %i\n", p->tag, tv.tv_sec); + if (pixserv_write(px, "ITEM %s %lu\n", + p->tag, (unsigned long)tv.tv_sec)) + goto close; } } - pixserv_write(px, "OK\n"); + if (pixserv_write(px, "OK\n")) goto close; } /* --- Request a passphrase --- */ @@ -724,13 +732,14 @@ OK\n\ rc = p_get(&p, q, mode, t > timeout ? timeout : t); switch (rc) { case 0: - pixserv_write(px, "OK %s\n", p); + if (pixserv_write(px, "OK %s\n", p)) goto close; break; case -1: - pixserv_write(px, "FAIL error reading passphrase\n"); + if (pixserv_write(px, "FAIL error reading passphrase\n")) + goto close; break; case +1: - pixserv_write(px, "MISSING\n"); + if (pixserv_write(px, "MISSING\n")) goto close; break; } } @@ -741,7 +750,7 @@ OK\n\ else if (strcmp(q, "flush") == 0) { q = str_getword(&s); p_flush(q); - pixserv_write(px, "OK\n"); + if (pixserv_write(px, "OK\n")) goto close; } /* --- Set a passphrase --- */ @@ -749,24 +758,25 @@ OK\n\ else if (strcmp(q, "set") == 0) { char *tag; unsigned long t; - if ((tag = str_getword(&s)) == 0) - pixserv_write(px, "FAIL missing tag\n"); - else if ((q = str_getword(&s)) == 0) - pixserv_write(px, "FAIL no passphrase\n"); - else { + if ((tag = str_getword(&s)) == 0) { + if (pixserv_write(px, "FAIL missing tag\n")) goto close; + } else if ((q = str_getword(&s)) == 0) { + if (pixserv_write(px, "FAIL no passphrase\n")) goto close; + } else { if (strcmp(q, "--") != 0) { t = pixserv_timeout(q); q = str_getword(&s); } else t = pixserv_timeout(0); - if (!q) - pixserv_write(px, "FAIL no passphrase\n"); - else if (strcmp(q, "--") != 0) - pixserv_write(px, "FAIL rubbish found before passphrase\n"); - else { + if (!q) { + if (pixserv_write(px, "FAIL no passphrase\n")) goto close; + } else if (strcmp(q, "--") != 0) { + if (pixserv_write(px, "FAIL rubbish found before passphrase\n")) + goto close; + } else { p_flush(tag); p_add(tag, s, t); - pixserv_write(px, "OK\n"); + if (pixserv_write(px, "OK\n")) goto close; } } } @@ -774,17 +784,26 @@ OK\n\ /* --- Shut the server down --- */ else if (strcmp(q, "quit") == 0) { - if (verbose) + if (verbose) { pxlog("%s client requested shutdown", px->f & px_stdin ? "local" : "remote"); - pixserv_write(px, "OK\n"); + } + if (pixserv_write(px, "OK\n")) goto close; exit(0); } /* --- Report an error for other commands --- */ - else - pixserv_write(px, "FAIL unknown command `%s'\n", q); + else { + if (pixserv_write(px, "FAIL unknown command `%s'\n", q)) goto close; + } + return; + +close: + if (px->fd != px->b.reader.fd) + close(px->fd); + selbuf_destroy(&px->b); + close(px->b.reader.fd); } /* --- @pixserv_create@ --- * @@ -830,7 +849,7 @@ static void pixserv_accept(int fd, unsigned mode, void *p) { int nfd; struct sockaddr_un sun; - size_t sunsz = sizeof(sun); + socklen_t sunsz = sizeof(sun); if (mode != SEL_READ) return; @@ -987,13 +1006,17 @@ static void pix_setup(struct sockaddr_un *sun, size_t sz) pxlog("stale socket found; removing it"); unlink(sun->sun_path); close(fd); - } else { + } else if (flags & F_REPLACE) { if (verbose) pxlog("server already running; shutting it down"); - write(fd, "QUIT\n", 5); + if (write(fd, "QUIT\n", 5) < 0) { + die(EXIT_FAILURE, "failed to shut down old server: %s", + strerror(errno)); + } sleep(1); close(fd); - } + } else + die(EXIT_FAILURE, "pixie already running; not starting"); goto again; } chmod(sun->sun_path, 0600); @@ -1034,7 +1057,8 @@ static void c_uline(char *s, size_t len, void *p) c_flags |= cf_uclose; } else { s[len++] = '\n'; - write(c_server.reader.fd, s, len); + if (write(c_server.reader.fd, s, len) < 0) + die(EXIT_FAILURE, "failed to read from stdin: %s", strerror(errno)); } } @@ -1105,8 +1129,8 @@ static void pix_client(struct sockaddr_un *sun, size_t sz, char *argv[]) DPUTS(&d, *argv++); } DPUTC(&d, '\n'); - write(fd, d.buf, d.len); - shutdown(fd, 1); + if (write(fd, d.buf, d.len) < 0 || shutdown(fd, 1)) + die(EXIT_FAILURE, "failed to write command: %s", strerror(errno)); c_flags |= cf_uclose | cf_cooked; dstr_destroy(&d); } @@ -1166,6 +1190,7 @@ protect important keys. Options provided:\n\ -q, --quiet Emit fewer log messages.\n\ -v, --version Emit more log messages.\n\ -s, --socket=FILE Name the pixie's socket.\n\ +-r, --replace Replace existing pixie, if one is running.\n\ -c, --command=COMMAND Shell command to read a passphrase.\n\ -f, --fetch Fetch passphrases from the terminal.\n\ -t, --timeout=TIMEOUT Length of time to retain a passphrase in memory.\n\ @@ -1240,6 +1265,7 @@ int main(int argc, char *argv[]) { "passphrase", 0, 0, 'P' }, { "verify-passphrase", 0, 0, '+' }, { "socket", OPTF_ARGREQ, 0, 's' }, + { "replace", 0, 0, 'r' }, { "command", OPTF_ARGREQ, 0, 'c' }, { "fetch", 0, 0, 'f' }, { "timeout", OPTF_ARGREQ, 0, 't' }, @@ -1254,7 +1280,7 @@ int main(int argc, char *argv[]) { 0, 0, 0, 0 } }; - int i = mdwopt(argc, argv, "hVuqvCPs:c:ft:idl", opts, 0, 0, 0); + int i = mdwopt(argc, argv, "hVuqvCPs:rc:ft:idl", opts, 0, 0, 0); if (i < 0) break; @@ -1298,6 +1324,9 @@ int main(int argc, char *argv[]) case 's': path = optarg; break; + case 'r': + flags |= F_REPLACE; + break; case 't': if ((timeout = pixserv_timeout(optarg)) == 0) die(1, "bad timeout `%s'", optarg); @@ -1374,9 +1403,9 @@ int main(int argc, char *argv[]) dstr d = DSTR_INIT; int rc = l_report(&lm, &d); if (rc < 0) - die(EXIT_FAILURE, d.buf); + die(EXIT_FAILURE, "%s", d.buf); else if (rc && verbose) { - pxlog(d.buf); + pxlog("%s", d.buf); pxlog("couldn't lock passphrase buffer"); } dstr_destroy(&d); @@ -1429,7 +1458,7 @@ int main(int argc, char *argv[]) } } #endif - chdir("/"); + DISCARD(chdir("/")); setsid(); if (fork() != 0)