safe

Password protected secret keeper
git clone git://git.z3bra.org/safe.git
Log | Files | Refs | README | LICENSE

commit 2d6626a1f3a9b271d3e10724254f102de1958013
parent 0f19ce9b57089e25c33edd01d9779b18d3affd41
Author: Willy Goiffon <dev@z3bra.org>
Date:   Fri,  7 Jun 2019 12:34:10 +0200

Make agent a standalone program

Diffstat:
makefile | 23+++++++++++++++--------
mkfile | 12++++++++++--
safe-agent.c | 224+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
safe.c | 70++++++++++++++++------------------------------------------------------
4 files changed, 265 insertions(+), 64 deletions(-)

diff --git a/makefile b/makefile @@ -9,19 +9,26 @@ CFLAGS = -Wall -Wextra -pedantic LDFLAGS = -L/usr/local/lib LDLIBS = -lsodium +all: safe safe-agent + safe: safe.o readpassphrase.o +safe-agent: safe-agent.o clean: - rm -f *.o safe + rm -f *.o safe safe-agent + +install: safe safe-agent + mkdir -p ${DESTDIR}${PREFIX}/bin + cp safe ${DESTDIR}${PREFIX}/bin/safe + cp safe-agent ${DESTDIR}${PREFIX}/bin/safe-agent + chmod 755 ${DESTDIR}${PREFIX}/bin/safe + chmod 755 ${DESTDIR}${PREFIX}/bin/safe-agent + mkdir -p ${DESTDIR}${MANPREFIX}/man1 + cp safe.1 ${DESTDIR}${MANPREFIX}/man1/safe.1 + chmod 644 ${DESTDIR}${MANPREFIX}/man1/safe.1 -install: safe - mkdir -p ${DESTDIR}${PREFIX}/bin - cp safe ${DESTDIR}${PREFIX}/bin/safe - chmod 755 ${DESTDIR}${PREFIX}/bin/safe - mkdir -p ${DESTDIR}${MANPREFIX}/man1 - cp safe.1 ${DESTDIR}${MANPREFIX}/man1/safe.1 - chmod 644 ${DESTDIR}${MANPREFIX}/man1/safe.1 uninstall:: rm ${DESTDIR}${PREFIX}/bin/safe + rm ${DESTDIR}${PREFIX}/bin/safe-agent rm ${DESTDIR}${MANPREFIX}/man1/safe.1 diff --git a/mkfile b/mkfile @@ -9,23 +9,31 @@ CFLAGS = -g -Wall -Wextra -pedantic LDFLAGS = -L/usr/local/lib LDLIBS = -lsodium +all:V: safe safe-agent + safe: safe.o readpassphrase.o $LD -o $target $prereq $LDFLAGS $LDLIBS +safe-agent: safe-agent.o + $LD -o $target $prereq $LDFLAGS $LDLIBS + %.o: %.c $CC $CPPFLAGS $CFLAGS -c $stem.c clean:V: - rm -f *.o safe + rm -f *.o safe safe-agent -install:V: safe +install:V: safe safe-agent mkdir -p ${DESTDIR}${PREFIX}/bin cp safe ${DESTDIR}${PREFIX}/bin/safe + cp safe-agent ${DESTDIR}${PREFIX}/bin/safe-agent chmod 755 ${DESTDIR}${PREFIX}/bin/safe + chmod 755 ${DESTDIR}${PREFIX}/bin/safe-agent mkdir -p ${DESTDIR}${MANPREFIX}/man1 cp safe.1 ${DESTDIR}${MANPREFIX}/man1/safe.1 chmod 644 ${DESTDIR}${MANPREFIX}/man1/safe.1 uninstall:V: rm ${DESTDIR}${PREFIX}/bin/safe + rm ${DESTDIR}${PREFIX}/bin/safe-agent rm ${DESTDIR}${MANPREFIX}/man1/safe.1 diff --git a/safe-agent.c b/safe-agent.c @@ -0,0 +1,224 @@ +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/un.h> + +#include <err.h> +#include <fcntl.h> +#include <limits.h> +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sodium.h> + +#include "arg.h" + +#define SOCKDIR "/tmp/safe-XXXXXX" +#define SOCKET "agent" + +struct safe { + int loaded; + uint8_t saltkey[crypto_secretstream_xchacha20poly1305_KEYBYTES + crypto_pwhash_SALTBYTES]; +}; + +int loop = 1; +char *argv0; +struct safe s; +char *sockp = NULL; + +void +usage(void) +{ + fprintf(stderr, "usage: %s [-hd] [-t timeout] [-f socket]\n", argv0); + exit(1); +} + +char * +dirname(char *path) +{ + static char tmp[PATH_MAX]; + char *p = NULL; + size_t len; + snprintf(tmp, sizeof(tmp), "%s", path); + len = strlen(tmp); + for(p = tmp + len; p > tmp; p--) + if(*p == '/') + break; + + *p = 0; + return tmp; +} + +ssize_t +xread(int fd, void *buf, size_t nbytes) +{ + uint8_t *bp = buf; + ssize_t total = 0; + + while (nbytes > 0) { + ssize_t n; + + n = read(fd, &bp[total], nbytes); + if (n < 0) + err(1, "read"); + else if (n == 0) + return total; + total += n; + nbytes -= n; + } + return total; +} + +ssize_t +xwrite(int fd, const void *buf, size_t nbytes) +{ + const uint8_t *bp = buf; + ssize_t total = 0; + + while (nbytes > 0) { + ssize_t n; + + n = write(fd, &bp[total], nbytes); + if (n < 0) + err(1, "write"); + else if (n == 0) + return total; + total += n; + nbytes -= n; + } + return total; +} + +int +creatsock(char *sockpath) +{ + int sfd; + struct sockaddr_un addr; + + sfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sfd < 0) + return -1; + + umask(0077); + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, sockpath); + + if (bind(sfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) + return -1; + + if (listen(sfd, 10) < 0) + return -1; + + return sfd; +} + +void +forgetkey() +{ + memset(s.saltkey, 0, sizeof(s.saltkey)); + s.loaded = 0; + alarm(0); + fprintf(stderr, "memory cleared\n"); +} + +void +sighandler(int signal) +{ + switch (signal) { + case SIGINT: + case SIGTERM: + unlink(sockp); + rmdir(dirname(sockp)); + exit(0); + /* FALLTHROUGH */ + case SIGALRM: + case SIGUSR1: + forgetkey(); + break; + } +} + +int +servekey(int timeout) +{ + int cfd, sfd; + ssize_t n; + + sfd = creatsock(sockp); + if (sfd < 0) + err(1, "%s", sockp); + + s.loaded = 0; + + for (;;) { + cfd = accept(sfd, NULL, NULL); + if (cfd < 0) + err(1, "%s", sockp); + + n = 0; + if (s.loaded) { + xwrite(cfd, s.saltkey, sizeof(s.saltkey)); + } else { + n = xread(cfd, s.saltkey, sizeof(s.saltkey)); + if (n == sizeof(s.saltkey)) { + s.loaded = 1; + alarm(timeout); + } + } + close(cfd); + } + + /* NOTREACHED */ + close(sfd); + + return 0; +} + +int +main(int argc, char *argv[]) +{ + pid_t pid; + int timeout; + size_t dirlen; + char path[PATH_MAX] = SOCKDIR; + + pid = getpid(); + + ARGBEGIN { + case 'f': + sockp = EARGF(usage()); + break; + case 't': + timeout = atoi(EARGF(usage())); + break; + default: + usage(); + } ARGEND + + if (sockp) { + strncpy(path, sockp, sizeof(path)); + } else { + if (!mkdtemp(path)) + err(1, "mkdtemp: %s", path); + + dirlen = strnlen(path, sizeof(path)); + snprintf(path + dirlen, PATH_MAX - dirlen, "/%s.%d", SOCKET, pid); + sockp = path; + } + + printf("SAFE_PID=%d\n", pid); + printf("SAFE_SOCK=%s\n", sockp); + printf("export SAFE_PID SAFE_SOCK\n"); + + signal(SIGINT, sighandler); + signal(SIGTERM, sighandler); + signal(SIGUSR1, sighandler); + signal(SIGALRM, sighandler); + + return servekey(timeout); +} diff --git a/safe.c b/safe.c @@ -18,8 +18,6 @@ #include "arg.h" #include "readpassphrase.h" -#define SOCKDIR "/tmp/safe-XXXXXX" -#define SOCKET "agent" #define MASTER "master" #define SAFE ".secrets" @@ -40,7 +38,7 @@ char *argv0; void usage(void) { - fprintf(stderr, "usage: %s [-hd] [-s safe] [[-a] entry]\n", argv0); + fprintf(stderr, "usage: %s [-h] [-s safe] [[-a] entry]\n", argv0); exit(1); } @@ -156,61 +154,25 @@ deriv(char *pw, struct safe *s) } int -creatsock(char *sockpath) +pushkey(struct safe *s, char *path) { int sfd; struct sockaddr_un addr; - sfd = socket(AF_UNIX, SOCK_STREAM, 0); - if (sfd < 0) - return -1; - - umask(0077); - memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - strcpy(addr.sun_path, sockpath); + strcpy(addr.sun_path, path); - if (bind(sfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) + if ((sfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) return -1; - if (listen(sfd, 10) < 0) + if (connect(sfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) return -1; - return sfd; -} - -int -agent(struct safe *s, char *sockp) -{ - int cfd, sfd; - pid_t pid; - size_t dirlen; - char path[PATH_MAX] = SOCKDIR; - - pid = getpid(); - - if (sockp) { - strncpy(path, sockp, sizeof(path)); - } else { - if (!mkdtemp(path)) - err(1, "mkdtemp: %s", path); - - dirlen = strnlen(path, sizeof(path)); - snprintf(path + dirlen, PATH_MAX - dirlen, "/%s.%d", SOCKET, pid); - } - - sfd = creatsock(path); - if (sfd < 0) - err(1, "%s", path); - - printf("SAFE_PID=%d\n", pid); - printf("SAFE_SOCK=%s\n", path); + if (xwrite(sfd, s->salt, sizeof(s->salt)) < 0) + return -1; - while ((cfd = accept(sfd, NULL, NULL)) > 0) { - xwrite(cfd, s->salt, sizeof(s->salt)); - xwrite(cfd, s->key, sizeof(s->key)); - close(cfd); - } + if (write(sfd, s->key, sizeof(s->key)) < 0) + return -1; close(sfd); @@ -354,7 +316,7 @@ readsecret(struct safe *s, int in, int out) int main(int argc, char *argv[]) { - int fd, aflag = 0, dflag = 0; + int fd, aflag = 0, pflag = 0; char *secret = NULL, *sockp = NULL, *safe = SAFE; struct safe s; @@ -364,8 +326,8 @@ main(int argc, char *argv[]) case 'a': aflag = 1; break; - case 'd': - dflag = 1; + case 'p': + pflag = 1; break; case 's': safe = EARGF(usage()); @@ -374,7 +336,7 @@ main(int argc, char *argv[]) usage(); } ARGEND - if (argc != 1 && !dflag) + if (argc != 1 && !pflag) usage(); if (sodium_init() < 0) @@ -391,7 +353,7 @@ main(int argc, char *argv[]) if (fd < 0 && errno != ENOENT) err(1, "%s", MASTER); - if (sockp) { + if (sockp && !pflag) { if (readkey(&s, sockp) < 0) err(1, "%s", sockp); } else { @@ -423,8 +385,8 @@ main(int argc, char *argv[]) } close(fd); - if (dflag) - return agent(&s, sockp); + if (pflag) + return pushkey(&s, sockp); secret = argv[0];