/* This file is in the public domain. */ #include #include #include #include #include #include #include extern const char *__progname; #include "b64.h" #include "errf.h" #include "panic.h" #include "nested.h" #include "stdio-util.h" #include "keyfiles.h" static int writeit(int fd, const char *data, int len, const char *what, const char *fn) { int n; int first; first = 1; while (len > 0) { n = write(fd,data,len); if (n < 0) { fprintf(errf,"%s: error writing %s %s: %s\n",__progname,what,fn,strerror(errno)); return(0); } if (n == 0) { fprintf(errf,"%s: error writing %s %s: zero-length write\n",__progname,what,fn); return(0); } if (first && (n < len)) { fprintf(errf,"%s: warning: short write writing %s %s (wanted %d, wrote %d) - retrying remainder\n",__progname,what,fn,len,n); first = 0; } data += n; len -= n; } return(1); } static char *read_pubkey_comment(FILE *f, const char *def) { char *cbuf; int l; int n; int c; NESTED void savec(char c) { if (l >= n) cbuf = realloc(cbuf,n=l+16); cbuf[l++] = c; } cbuf = 0; l = 0; n = 0; while (1) { c = getc(f); switch (c) { case '\n': case EOF: if (l == 0) return(def?strdup(def):0); savec('\0'); return(cbuf); break; case ' ': if (l == 0) break; default: savec(c); if (l >= 80) { savec('.'); savec('.'); savec('.'); savec('\0'); return(cbuf); } break; } } } int load_key_pair( const char *fn_pub, const char *fn_priv, void **pubdp, int *publp, HKALG **algp, void **privdp, int *privlp, char **commentp, char *defcomm, FILE *ef ) { char *pubfn; FILE *pubf; char *privfn; FILE *privf; int rv; if (pubdp) { if (! fn_pub) panic("no fn_pub"); asprintf(&pubfn,"%s.pub",fn_pub); } else { pubfn = 0; } if (privdp) { if (! fn_priv) panic("no fn_priv"); asprintf(&privfn,"%s.priv",fn_priv); } else { privfn = 0; } pubf = 0; privf = 0; rv = 0; do <"ret"> { if (pubdp) { pubf = fopen(pubfn,"r"); if (pubf == 0) { fprintf(ef,"can't open public key file %s: %s",pubfn,strerror(errno)); break <"ret">; } } if (privdp) { privf = fopen(privfn,"r"); if (privf == 0) { fprintf(ef,"can't open private key file %s: %s",privfn,strerror(errno)); fclose(pubf); break <"ret">; } } rv = load_key_pair_stdio( pubf, pubfn, privf, privfn, pubdp, publp, algp, privdp, privlp, commentp, defcomm, ef ); } while (0); free(pubfn); free(privfn); if (pubf) fclose(pubf); if (privf) fclose(privf); return(rv); } int load_key_pair_stdio( FILE *pubf, const char *pubfn, FILE *privf, const char *privfn, void **pubdp, int *publp, HKALG **algp, void **privdp, int *privlp, char **commentp, char *defcomm, FILE *ef ) { char kname[64+1]; HKALG *alg; void *pub; int publen; void *priv; int privlen; char *comment; int rv; if (pubdp && !pubf) panic("no pubf"); if (privdp && !privf) panic("no privf"); pub = 0; priv = 0; comment = 0; do <"ret"> { do <"fail"> { if (pubdp) { if (fscanf(pubf,"%64s ",&kname[0]) != 1) { fprintf(ef,"can't use public key from %s (missing type)",pubfn); break <"fail">; } alg = (*at_hk.find_c)(&kname[0]); if (alg == 0) { fprintf(ef,"can't use public key from %s (unknown algorithm name)",pubfn); break <"fail">; } *algp = alg; if (! b64_read_blob(pubf,&pub,&publen)) { fprintf(ef,"can't use public key from %s (can't read public-key blob)",pubfn); break <"fail">; } if (! (*alg->checkpub)(pub,publen)) { fprintf(ef,"can't use public key from %s (corrupted public-key blob)",pubfn); break <"fail">; } comment = read_pubkey_comment(pubf,0); } if (privdp) { if (! b64_read_blob(privf,&priv,&privlen)) { fprintf(ef,"can't use private key from %s (blob unreadable)",privfn); break <"fail">; } } if (pubdp) { *pubdp = pub; *publp = publen; *commentp = comment ? : defcomm; } if (privdp) { *privdp = priv; *privlp = privlen; } rv = 1; break <"ret">; } while (0); free(pub); free(priv); free(comment); rv = 0; break <"ret">; } while (0); return(rv); } int write_pub_to_fd(const char *what, const char *to, int fd, HKALG *alg, const void *pubdata, int publen, const char *comment) { FILE *f; char *blob; int blen; int rv; f = fopen_alloc(&blob,&blen); fprintf(f,"%s ",alg->name); b64_write_blob(f,pubdata,publen); if (comment) fprintf(f," %s",comment); fprintf(f,"\n"); fclose(f); rv = writeit(fd,blob,blen,what,to); free(blob); return(rv); } int brief_pub_to_fd(const char *what, const char *to, int fd, HKALG *alg, const char *comment) { FILE *f; char *blob; int blen; int rv; f = fopen_alloc(&blob,&blen); fprintf(f,"%s",alg->name); if (comment) fprintf(f," %s",comment); fprintf(f,"\n"); fclose(f); rv = writeit(fd,blob,blen,what,to); free(blob); return(rv); } int write_priv_to_fd(const char *what, const char *to, int fd, const void *privdata, int privlen) { FILE *f; char *blob; int blen; int rv; f = fopen_alloc(&blob,&blen); b64_write_blob(f,privdata,privlen); fprintf(f,"\n"); fclose(f); rv = writeit(fd,blob,blen,what,to); free(blob); return(rv); } void write_key(HKALG *alg, const char *pubfile, void *pubdata, int publen, const char *comment, const char *privfile, void *privdata, int privlen) { char *pubtmp; char *pubfn; char *privtmp; char *privfn; int fd; sigset_t mask; sigset_t omask; NESTED void dofrees(void) { free(pubfn); free(pubtmp); free(privfn); free(privtmp); } pubfn = 0; pubtmp = 0; privfn = 0; privtmp = 0; do <"errexit"> { if (pubfile) { asprintf(&pubfn,"%s.pub",pubfile); asprintf(&pubtmp,"%s.PUB",pubfile); fd = open(pubtmp,O_WRONLY|O_CREAT|O_EXCL,0644); if (fd < 0) { fprintf(errf,"%s: can't create %s: %s\n",__progname,pubtmp,strerror(errno)); break <"errexit">; } if (! write_pub_to_fd("public key temporary file",pubtmp,fd,alg,pubdata,publen,comment)) break <"errexit">; if (fsync(fd) < 0) { fprintf(errf,"%s: error flushing public key temporary file %s: %s\n",__progname,pubtmp,strerror(errno)); break <"errexit">; } if (close(fd) < 0) { fprintf(errf,"%s: error closing public key temporary file %s: %s\n",__progname,pubtmp,strerror(errno)); break <"errexit">; } } if (privfile) { asprintf(&privfn,"%s.priv",privfile); asprintf(&privtmp,"%s.PRIV",privfile); fd = open(privtmp,O_WRONLY|O_CREAT|O_EXCL,0600); if (fd < 0) { fprintf(errf,"%s: can't create %s: %s\n",__progname,privtmp,strerror(errno)); break <"errexit">; } if (! write_priv_to_fd("private key temporary file",privtmp,fd,privdata,privlen)) break <"errexit">; if (fsync(fd) < 0) { fprintf(errf,"%s: error flushing private key temporary file %s: %s\n",__progname,privtmp,strerror(errno)); break <"errexit">; } if (close(fd) < 0) { fprintf(errf,"%s: error closing private key temporary file %s: %s\n",__progname,privtmp,strerror(errno)); break <"errexit">; } } sigemptyset(&mask); sigaddset(&mask,SIGHUP); sigaddset(&mask,SIGINT); sigaddset(&mask,SIGQUIT); sigaddset(&mask,SIGTERM); sigaddset(&mask,SIGTSTP); sigaddset(&mask,SIGTTIN); sigaddset(&mask,SIGTTOU); sigprocmask(SIG_BLOCK,&mask,&omask); if (pubfile) { if (rename(pubtmp,pubfn) < 0) { fprintf(errf,"%s: can't rename %s -> %s: %s\n",__progname,pubtmp,pubfn,strerror(errno)); pubtmp = 0; break <"errexit">; } } if (privfile) { if (rename(privtmp,privfn) < 0) { fprintf(errf,"%s: can't rename %s -> %s: %s\n",__progname,privtmp,privfn,strerror(errno)); privtmp = 0; break <"errexit">; } } sigprocmask(SIG_SETMASK,&omask,0); dofrees(); return; } while (0); if (pubtmp) unlink(pubtmp); if (privtmp) unlink(privtmp); exit(1); } int read_pubkeys_file(const char *file, int (*eachkey)(HKALG *, const void *, int, const char *)) { FILE *f; char kname[64+1]; HKALG *alg; void *blob; int bloblen; char *comment; int v; int rv; NESTED void skip_to_nl(void) { int c; do c = getc(f); while ((c != '\n') && (c != EOF)); } f = fopen(file,"r"); if (f == 0) return(0); rv = 0; while (1) { if (fscanf(f,"%64s",&kname[0]) != 1) { fclose(f); return(rv); } alg = (*at_hk.find_c)(&kname[0]); if (alg == 0) { skip_to_nl(); continue; } fscanf(f," "); if ( !b64_read_blob(f,&blob,&bloblen) || !(*alg->checkpub)(blob,bloblen) ) { skip_to_nl(); continue; } comment = read_pubkey_comment(f,0); v = (*eachkey)(alg,blob,bloblen,comment); free(blob); free(comment); if (v < 0) { rv = v; break; } rv += v; } fclose(f); return(rv); }