tsafe.c - safe - password protected secret keeper
 (HTM) git clone git://git.z3bra.org/safe.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       tsafe.c (13201B)
       ---
            1 #include <netinet/in.h>
            2 #include <sys/resource.h>
            3 #include <sys/socket.h>
            4 #include <sys/stat.h>
            5 #include <sys/types.h>
            6 #include <sys/un.h>
            7 #include <sys/wait.h>
            8 
            9 #include <err.h>
           10 #include <errno.h>
           11 #include <fcntl.h>
           12 #include <limits.h>
           13 #include <paths.h>
           14 #include <stdint.h>
           15 #include <stdio.h>
           16 #include <stdlib.h>
           17 #include <string.h>
           18 #include <unistd.h>
           19 
           20 #include <sodium.h>
           21 
           22 #include "arg.h"
           23 #include "readpassphrase.h"
           24 #include "config.h"
           25 
           26 struct crypto {
           27         uint8_t  magic[6];
           28         uint16_t version;
           29         uint32_t xchacha20poly1305_bufsiz;
           30         uint32_t argon2id_memory;
           31         uint32_t argon2id_time;
           32         uint32_t argon2id_threads;
           33         uint8_t  salt[crypto_pwhash_SALTBYTES];
           34 };
           35 
           36 struct crypto crypto_defaults = {
           37         .magic = {'C','R','E','A','M','\1'},
           38         .version = 0x10,
           39         .xchacha20poly1305_bufsiz = BUFSIZ, /* must match size in readsecret() and writesecret() */
           40         .argon2id_memory          = crypto_pwhash_argon2id_MEMLIMIT_INTERACTIVE/1024,
           41         .argon2id_time            = crypto_pwhash_argon2id_OPSLIMIT_INTERACTIVE,
           42         .argon2id_threads         = 1, /* not used, hardcoded in libsodium */
           43 };
           44 
           45 struct safe {
           46         uint8_t key[crypto_secretstream_xchacha20poly1305_KEYBYTES];
           47         struct crypto crypto;
           48 };
           49 
           50 enum {
           51         SAFE_INIT  = 1 << 1,
           52         SAFE_FINAL = 1 << 2,
           53 };
           54 
           55 uint8_t cleartext[BUFSIZ]; // Fixed-size memory chunk for decrypting messages. Suitable for sodium_mlock()
           56 char *argv0;
           57 
           58 void
           59 usage(void)
           60 {
           61         fprintf(stderr, "usage: %s [-bhr] [-s safe] [-p prompt] [[-af] entry]\n", argv0);
           62         exit(1);
           63 }
           64 
           65 char *
           66 dirname(char *path)
           67 {
           68         static char tmp[PATH_MAX];
           69         char *p = NULL;
           70         size_t len;
           71         snprintf(tmp, sizeof(tmp), "%s", path);
           72         len = strlen(tmp);
           73         for(p = tmp + len; p > tmp; p--)
           74                 if(*p == '/')
           75                         break;
           76 
           77         *p = 0;
           78         return tmp;
           79 }
           80 
           81 int
           82 mkdir_p(char *path, mode_t mode)
           83 {
           84         char tmp[PATH_MAX] = "";
           85         char *p = NULL;
           86         size_t len;
           87 
           88         snprintf(tmp, sizeof(tmp), "%s", path);
           89         len = strlen(tmp);
           90         if(len && tmp[len - 1] == '/')
           91                 tmp[len - 1] = 0;
           92         for(p = tmp + 1; *p; p++)
           93                 if(*p == '/') {
           94                         *p = 0;
           95                         mkdir(tmp, mode);
           96                         *p = '/';
           97                 }
           98         return mkdir(tmp, mode);
           99 }
          100 
          101 ssize_t
          102 xread(int fd, void *buf, size_t nbytes, int *eof)
          103 {
          104         uint8_t *bp = buf;
          105         ssize_t total = 0;
          106 
          107         if (eof) *eof = 0;
          108         while (nbytes > 0) {
          109                 ssize_t n;
          110 
          111                 n = read(fd, &bp[total], nbytes);
          112                 if (n < 0) {
          113                         err(1, "read");
          114                 } else if (n == 0) {
          115                         if (eof) *eof = 1;
          116                         return total;
          117                 }
          118                 total += n;
          119                 nbytes -= n;
          120         }
          121         return total;
          122 }
          123 
          124 ssize_t
          125 xwrite(int fd, const void *buf, size_t nbytes)
          126 {
          127         const uint8_t *bp = buf;
          128         ssize_t total = 0;
          129 
          130         while (nbytes > 0) {
          131                 ssize_t n;
          132 
          133                 n = write(fd, &bp[total], nbytes);
          134                 if (n < 0)
          135                         err(1, "write");
          136                 else if (n == 0)
          137                         return total;
          138                 total += n;
          139                 nbytes -= n;
          140         }
          141         return total;
          142 }
          143 
          144 char *
          145 spawn_askpass(const char *askpass, const char *msg, char *buf, size_t bufsiz)
          146 {
          147         pid_t pid, ret;
          148         int p[2], eof, status;
          149 
          150         if (!askpass) {
          151                 sodium_memzero(buf, bufsiz);
          152                 return buf;
          153         }
          154 
          155         if (pipe(p) < 0)
          156                 return NULL;
          157 
          158         pid = fork();
          159         if (pid < 0)
          160                 return NULL;
          161 
          162         if (!pid) {
          163                 close(p[0]);
          164                 if (dup2(p[1], STDOUT_FILENO) < 0)
          165                         return NULL;
          166 
          167                 execlp(askpass, askpass, msg, NULL);
          168                 err(1, "execlp(%s)", askpass); /* NOTREACHED */
          169         }
          170         close(p[1]);
          171 
          172         xread(p[0], buf, bufsiz - 1, &eof);
          173         close(p[0]);
          174 
          175         ret = waitpid(pid, &status, 0);
          176 
          177         if (ret < 0 || !WIFEXITED(status) || WEXITSTATUS(status)) {
          178                 sodium_memzero(buf, bufsiz);
          179                 return buf;
          180         }
          181 
          182         buf[strcspn(buf, "\r\n")] = '\0';
          183         return buf;
          184 }
          185 
          186 int
          187 readpass(const char *prompt, char *buf, size_t bufsiz, int askflag, int stdinflag)
          188 {
          189         char *askpass;
          190         if (askflag) {
          191                 askpass = askpass_path;
          192                 if (getenv("SAFE_ASKPASS"))
          193                         askpass = getenv("SAFE_ASKPASS");
          194                 if (!spawn_askpass(askpass, prompt, buf, bufsiz))
          195                         err(1, "askpass:");
          196         } else {
          197                 int flags = 0;
          198                 flags |= RPP_ECHO_OFF;
          199                 flags |= stdinflag ? RPP_STDIN : RPP_REQUIRE_TTY;
          200                 if (!readpassphrase(prompt, buf, bufsiz, flags))
          201                         err(1, "readpassphrase:");
          202         }
          203 
          204         if (buf[0] == '\0')
          205                 return -1;
          206 
          207         return strnlen(buf, bufsiz);
          208 }
          209 
          210 void
          211 deriv(char *pw, struct safe *s)
          212 {
          213         if (crypto_pwhash(s->key, sizeof(s->key), pw, strlen(pw),
          214                         s->crypto.salt, s->crypto.argon2id_time,
          215                         s->crypto.argon2id_memory * 1024,
          216                         crypto_pwhash_ALG_ARGON2ID13))
          217                 err(1, "crypto_pwhash:");
          218 }
          219 
          220 int
          221 pushkey(struct safe *s, char *path)
          222 {
          223         int sfd;
          224         struct sockaddr_un addr;
          225 
          226         addr.sun_family = AF_UNIX;
          227         strcpy(addr.sun_path, path);
          228 
          229         if ((sfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
          230                 return -1;
          231 
          232         if (connect(sfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
          233                 return -1;
          234 
          235         if (xwrite(sfd, s->crypto.salt, sizeof(s->crypto.salt)) < 0)
          236                 return -1;
          237 
          238         if (write(sfd, s->key, sizeof(s->key)) < 0)
          239                 return -1;
          240 
          241         close(sfd);
          242 
          243         return 0;
          244 }
          245 
          246 int
          247 readkey(struct safe *s, char *path)
          248 {
          249         int sfd;
          250         ssize_t n;
          251         struct sockaddr_un addr;
          252 
          253         addr.sun_family = AF_UNIX;
          254         strcpy(addr.sun_path, path);
          255 
          256         if ((sfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
          257                 return -1;
          258 
          259         if (connect(sfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
          260                 goto err;
          261 
          262         if ((n = xread(sfd, &s->crypto.salt, sizeof(s->crypto.salt), NULL)) <= 0)
          263                 goto err;
          264 
          265         if (xread(sfd, s->key, sizeof(s->key), NULL) < 0)
          266                 goto err;
          267 
          268         close(sfd);
          269         return 0;
          270 
          271 err:
          272         close(sfd);
          273         return -1;
          274 }
          275 
          276 int
          277 trydecrypt(struct safe *s, int fd)
          278 {
          279         int r = 0, eof = 0;
          280         ssize_t n;
          281         uint8_t tag;
          282         uint8_t *m = cleartext;
          283         uint8_t c[BUFSIZ + crypto_secretstream_xchacha20poly1305_ABYTES];
          284         uint8_t h[crypto_secretstream_xchacha20poly1305_HEADERBYTES];
          285         crypto_secretstream_xchacha20poly1305_state st;
          286         unsigned long long mlen;
          287 
          288         xread(fd, h, sizeof(h), NULL);
          289         if (crypto_secretstream_xchacha20poly1305_init_pull(&st, h, s->key))
          290                 return -1;
          291 
          292         while ((n = xread(fd, c, sizeof(c), &eof)) > 0) {
          293                 if (crypto_secretstream_xchacha20poly1305_pull(&st, m, &mlen, &tag, c, n, NULL, 0))
          294                         r--;
          295 
          296                 if (eof && tag != crypto_secretstream_xchacha20poly1305_TAG_FINAL)
          297                         r--;
          298         }
          299         return r;
          300 }
          301 
          302 int
          303 readheader(int fd, struct crypto *hdr)
          304 {
          305         uint8_t buf[40];
          306         uint16_t s;
          307         uint32_t l;
          308 
          309         xread(fd, buf, sizeof(buf), NULL);
          310 
          311         memcpy(hdr->magic, buf +  0, 6);
          312         memcpy(&s, buf +  6, 2); hdr->version = ntohs(s);
          313         memcpy(&l, buf +  8, 4); hdr->xchacha20poly1305_bufsiz = ntohl(l);
          314         memcpy(&l, buf + 12, 4); hdr->argon2id_memory = ntohl(l);
          315         memcpy(&l, buf + 16, 4); hdr->argon2id_time = ntohl(l);
          316         memcpy(&l, buf + 20, 4); hdr->argon2id_threads = ntohl(l);
          317         memcpy(hdr->salt, buf + 24, 16);
          318 
          319         return 0;
          320 }
          321 
          322 int
          323 writeheader(int fd, struct crypto hdr)
          324 {
          325         uint8_t buf[40];
          326         uint16_t s;
          327         uint32_t l;
          328 
          329         memcpy(buf +  0, hdr.magic, 6);
          330         s = htons(hdr.version);                  memcpy(buf +  6, (uint8_t *) &s, 2);
          331         l = htonl(hdr.xchacha20poly1305_bufsiz); memcpy(buf +  8, (uint8_t *) &l, 4);
          332         l = htonl(hdr.argon2id_memory);          memcpy(buf + 12, (uint8_t *) &l, 4);
          333         l = htonl(hdr.argon2id_time);            memcpy(buf + 16, (uint8_t *) &l, 4);
          334         l = htonl(hdr.argon2id_threads);         memcpy(buf + 20, (uint8_t *) &l, 4);
          335         memcpy(buf + 24, hdr.salt, 16);
          336 
          337         return xwrite(fd, buf, sizeof(buf));
          338 }
          339 
          340 int
          341 writepass(struct safe *s, char *m, size_t mlen, int fd)
          342 {
          343         uint8_t *c, h[crypto_secretstream_xchacha20poly1305_HEADERBYTES];
          344         crypto_secretstream_xchacha20poly1305_state st;
          345         unsigned long long clen;
          346 
          347         c = malloc(mlen + crypto_secretstream_xchacha20poly1305_ABYTES);
          348         if (!c)
          349                 err(1, "malloc");
          350 
          351         if (crypto_secretstream_xchacha20poly1305_init_push(&st, h, s->key))
          352                 return -1;
          353 
          354         if (crypto_secretstream_xchacha20poly1305_push(&st, c, &clen, (uint8_t *)m, mlen, NULL, 0, crypto_secretstream_xchacha20poly1305_TAG_FINAL))
          355                 return -1;
          356 
          357         xwrite(fd, h, sizeof(h));
          358         xwrite(fd, c, clen);
          359 
          360         free(c);
          361 
          362         return 0;
          363 }
          364 
          365 int
          366 writesecret(struct safe *s, int in, int out)
          367 {
          368         int eof;
          369         ssize_t n;
          370         uint8_t tag;
          371         uint8_t m[BUFSIZ];
          372         uint8_t c[BUFSIZ + crypto_secretstream_xchacha20poly1305_ABYTES];
          373         uint8_t h[crypto_secretstream_xchacha20poly1305_HEADERBYTES];
          374         crypto_secretstream_xchacha20poly1305_state st;
          375         unsigned long long clen;
          376 
          377         if (crypto_secretstream_xchacha20poly1305_init_push(&st, h, s->key))
          378                 return -1;
          379 
          380         xwrite(out, h, sizeof(h));
          381 
          382         while ((n = xread(in, m, sizeof(m), &eof)) > 0) {
          383                 tag = eof ? crypto_secretstream_xchacha20poly1305_TAG_FINAL : 0;
          384                 if (crypto_secretstream_xchacha20poly1305_push(&st, c, &clen, m, n, NULL, 0, tag))
          385                         return -1;
          386 
          387                 xwrite(out, c, clen);
          388         }
          389         return 0;
          390 }
          391 
          392 int
          393 readsecret(struct safe *s, int in, int out)
          394 {
          395         int eof = 0;
          396         ssize_t n;
          397         uint8_t tag;
          398         uint8_t m[BUFSIZ];
          399         uint8_t c[BUFSIZ + crypto_secretstream_xchacha20poly1305_ABYTES];
          400         uint8_t h[crypto_secretstream_xchacha20poly1305_HEADERBYTES];
          401         crypto_secretstream_xchacha20poly1305_state st;
          402         unsigned long long mlen;
          403 
          404         xread(in, h, sizeof(h), NULL);
          405         if (crypto_secretstream_xchacha20poly1305_init_pull(&st, h, s->key))
          406                 return -1;
          407 
          408         while ((n = xread(in, c, sizeof(c), &eof)) > 0) {
          409                 if (crypto_secretstream_xchacha20poly1305_pull(&st, m, &mlen, &tag, c, n, NULL, 0))
          410                         return -1;
          411 
          412                 if (eof && tag != crypto_secretstream_xchacha20poly1305_TAG_FINAL)
          413                         return -1;
          414 
          415                 xwrite(out, m, mlen);
          416         }
          417         return 0;
          418 }
          419 
          420 int
          421 main(int argc, char *argv[])
          422 {
          423         int aflag = 0, bflag = 0, rflag = 0, kflag = 0, fflag = 0;
          424         int fd, haskey = 0, hasmaster = 1, ttyfd;
          425         char *prompt, *secret, *sockp, *safe = safe_dir;
          426         char passphrase[BUFSIZ], verifyphrase[BUFSIZ];
          427         ssize_t pplen, vplen = 0;
          428         struct safe s;
          429 
          430         safe   = getenv("SAFE_DIR");
          431         sockp  = getenv("SAFE_SOCK");
          432         prompt = "password:";
          433 
          434         /* set default cream parameters */
          435         memcpy(&s.crypto, &crypto_defaults, sizeof(s.crypto));
          436 
          437         ARGBEGIN {
          438         case 'f':
          439                 fflag = 1;
          440                 /* FALLTHROUGH */
          441         case 'a':
          442                 aflag = 1;
          443                 break;
          444         case 'b':
          445                 bflag = 1;
          446                 break;
          447         case 'p':
          448                 prompt = EARGF(usage());
          449                 break;
          450         case 'r':
          451                 rflag = 1;
          452                 break;
          453         case 's':
          454                 safe = EARGF(usage());
          455                 break;
          456         case 'k':
          457                 kflag = 1;
          458                 break;
          459         default:
          460                 usage();
          461         } ARGEND
          462 
          463         if (!safe)
          464                 safe = safe_dir;
          465 
          466         if (argc != 1 && !rflag)
          467                 usage();
          468 
          469         if (sodium_init() < 0)
          470                 return -1;
          471 
          472         sodium_mlock(&s, sizeof(s));
          473         sodium_mlock(&cleartext, sizeof(cleartext));
          474         sodium_mlock(&passphrase, sizeof(passphrase));
          475         sodium_mlock(&verifyphrase, sizeof(verifyphrase));
          476 
          477 #ifndef _DEBUG
          478         /* deny core dump as memory contains passwords and keys */
          479         struct rlimit rlim;
          480         rlim.rlim_cur = rlim.rlim_max = 0;
          481         if (setrlimit(RLIMIT_CORE, &rlim) < 0)
          482                 err(1, "setrlimit RLIMIT_CORE");
          483 #endif
          484 
          485         mkdir(safe, 0700);
          486 
          487 #ifdef __OpenBSD__
          488         if (unveil(_PATH_TTY, "rw") == -1)
          489                 err(1, "unveil %s", _PATH_TTY);
          490         if (unveil(safe, "rwc") == -1)
          491                 err(1, "unveil %s", safe);
          492         if (sockp)
          493                 if (unveil(sockp, "rw") == -1)
          494                         err(1, "unveil %s", sockp);
          495         if (pledge("stdio rpath wpath cpath unix tty", NULL) < 0)
          496                 err(1, "pledge");
          497 #endif
          498 
          499         if (chdir(safe) < 0)
          500                 err(1, "chdir: %s", safe);
          501 
          502         /* open master password as read only to retrieve salt */
          503         fd = open(master_entry, O_RDONLY);
          504         if (fd < 0) {
          505                 if (errno != ENOENT)
          506                         err(1, "%s", master_entry);
          507                 hasmaster = 0;
          508         }
          509 
          510         if (sockp && !readkey(&s, sockp))
          511                 haskey = 1;
          512 
          513         /*
          514          * read passphrase from an ASKPASS program stdout if there is
          515          * no tty available
          516          */
          517         if ((ttyfd = open(_PATH_TTY, O_RDWR)) < 0)
          518                 kflag = 1;
          519         else
          520                 close(ttyfd);
          521 
          522         /* write master password entry if not present */
          523         if (!hasmaster) {
          524                 pplen = readpass(prompt, passphrase, sizeof(passphrase), kflag, bflag);
          525                 if (pplen < 0)
          526                         return -1;
          527 
          528                 /* input for master password again to check */
          529                 vplen = readpass("verify:", verifyphrase, sizeof(verifyphrase), kflag, bflag);
          530                 if (vplen < 0)
          531                         return -1;
          532 
          533                 if (pplen != vplen || memcmp(passphrase, verifyphrase, pplen)) {
          534                         fprintf(stderr, "password mismatch\n");
          535                         return -1;
          536                 }
          537 
          538                 fd = open(master_entry, O_RDWR | O_CREAT | O_EXCL, 0600);
          539                 if (fd < 0)
          540                         err(1, "%s", master_entry);
          541 
          542                 randombytes_buf(s.crypto.salt, sizeof(s.crypto.salt));
          543                 deriv((char *)passphrase, &s);
          544 
          545                 writeheader(fd, s.crypto);
          546                 writepass(&s, passphrase, pplen, fd);
          547                 haskey = 1;
          548         }
          549 
          550         if (!haskey) {
          551                 pplen = readpass(prompt, passphrase, sizeof(passphrase), kflag, bflag);
          552                 if (pplen < 0)
          553                         return -1;
          554 
          555                 //xread(fd, &s.crypto, sizeof(s.crypto), NULL);
          556                 readheader(fd, &s.crypto);
          557                 deriv(passphrase, &s);
          558                 haskey = 1;
          559         }
          560 
          561         /* try to decrypt master password first, to ensure passphrase match */
          562         lseek(fd, sizeof(s.crypto), SEEK_SET);
          563         if (trydecrypt(&s, fd) < 0) {
          564                 fprintf(stderr, "incorrect master password\n");
          565                 close(fd);
          566                 return -1;
          567         }
          568         close(fd);
          569 
          570         /* push the key to a running agent */
          571         if (rflag) {
          572                 if (!sockp) {
          573                         fprintf(stderr, "SAFE_SOCK variable is not set\n");
          574                         return -1;
          575                 }
          576                 pushkey(&s, sockp);
          577         }
          578 
          579         secret = argv[0];
          580 
          581         if (!secret)
          582                 return 0;
          583 
          584         if (aflag) {
          585                 mkdir_p(dirname(secret), 0700);
          586 
          587                 /* Prevent overwriting unless fflag is set */
          588                 fd = open(secret, O_WRONLY | O_CREAT | (fflag ? 0 : O_EXCL), 0600);
          589                 if (fd < 0)
          590                         err(1, "%s", secret);
          591 
          592                 writeheader(fd, s.crypto);
          593                 writesecret(&s, STDIN_FILENO, fd);
          594                 close(fd);
          595         } else {
          596                 fd = open(secret, O_RDONLY);
          597                 if (fd < 0)
          598                         err(1, "%s", secret);
          599 
          600                 /* Read salt from the beginning of the file */
          601                 lseek(fd, sizeof(s.crypto), SEEK_SET);
          602                 readsecret(&s, fd, STDOUT_FILENO);
          603                 close(fd);
          604         }
          605 
          606         sodium_memzero(&s, sizeof(s));
          607         sodium_memzero(&cleartext, sizeof(cleartext));
          608         sodium_memzero(&passphrase, sizeof(passphrase));
          609         sodium_memzero(&verifyphrase, sizeof(verifyphrase));
          610 
          611         return 0;
          612 }