X-Git-Url: https://git.distorted.org.uk/u/mdw/catacomb/blobdiff_plain/f387fcb17a980fe165218d217b0187a8c279508a..a6864ad907239985bd1f6eab414cec6171930d46:/catsign.c diff --git a/catsign.c b/catsign.c index dc96e55..3bbd7b2 100644 --- a/catsign.c +++ b/catsign.c @@ -7,7 +7,7 @@ * (c) 2005 Straylight/Edgeware */ -/*----- Licensing notice --------------------------------------------------* +/*----- Licensing notice --------------------------------------------------* * * This file is part of Catacomb. * @@ -15,12 +15,12 @@ * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. - * + * * Catacomb is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. - * + * * You should have received a copy of the GNU Library General Public * License along with Catacomb; if not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, @@ -29,6 +29,8 @@ /*----- Header files ------------------------------------------------------*/ +#define _FILE_OFFSET_BITS 64 + #include "config.h" #include @@ -50,30 +52,12 @@ #include "noise.h" #include "mprand.h" #include "key.h" +#include "getdate.h" #include "cc.h" #include "ectab.h" #include "ptab.h" -/*----- Utilities ---------------------------------------------------------*/ - -/* --- @keyreport@ --- * - * - * Arguments: @const char *file@ = filename containing the error - * @int line@ = line number in file - * @const char *err@ = error text message - * @void *p@ = unimportant pointer - * - * Returns: --- - * - * Use: Reports errors during the opening of a key file. - */ - -static void keyreport(const char *file, int line, const char *err, void *p) -{ - moan("error in keyring `%s' at line `%s': %s", file, line, err); -} - /*----- Static variables --------------------------------------------------*/ static const char *keyring = "keyring"; @@ -109,6 +93,8 @@ typedef struct sigmsg { #define F_BOGUS 128u #define F_BUFFER 256u #define F_UTC 512u +#define F_NOCHECK 1024u +#define F_PROGRESS 2048u /*----- Chunk I/O ---------------------------------------------------------*/ @@ -149,6 +135,7 @@ typedef struct msgcanon { unsigned f; FILE *fp; enc *e; + fprogress ff; size_t (*read)(struct msgcanon *, void *); void (*write)(struct msgcanon *, const void *, size_t); void (*close)(struct msgcanon *); @@ -193,6 +180,7 @@ full: ungetc(ch, m->fp); done: m->f = f; + if (m->f & F_PROGRESS) fprogress_update(&m->ff, n); return (n); } @@ -214,9 +202,18 @@ static void textwrite(msgcanon *m, const void *bp, size_t n) } static size_t binreadembed(msgcanon *m, void *bp) - { return (chunk_read(m->e, bp)); } +{ + size_t n = chunk_read(m->e, bp); + if (m->f & F_PROGRESS) fprogress_update(&m->ff, n); + return (n); +} + static size_t binreaddetach(msgcanon *m, void *bp) - { return (fread(bp, 1, MSGBUFSZ, m->fp)); } +{ + size_t n = fread(bp, 1, MSGBUFSZ - 1, m->fp); + if (m->f & F_PROGRESS) fprogress_update(&m->ff, n); + return (n); +} static void binwriteembed(msgcanon *m, const void *bp, size_t n) { chunk_write(m->e, bp, n); } @@ -225,18 +222,25 @@ static void nullwrite(msgcanon *m, const void *bp, size_t n) { ; } static void mcsetup_readfile(msgcanon *m, unsigned f, const char *fn) { - m->f = F_DETACH | (f & F_BINARY); + m->f = F_DETACH | (f & (F_BINARY | F_PROGRESS)); if (!fn || strcmp(fn, "-") == 0) { m->fp = stdin; m->f |= F_NOCLOSE; } else if ((m->fp = fopen(fn, (f & F_BINARY) ? "rb" : "r")) == 0) die(EXIT_FAILURE, "couldn't open file `%s': %s", fn, strerror(errno)); + if (m->f & F_PROGRESS) { + if (fprogress_init(&m->ff, fn, m->fp)) { + die(EXIT_FAILURE, "failed to initialize progress indicator: %s", + strerror(errno)); + } + } m->read = (f & F_BINARY) ? binreaddetach : textread; } static void mcsetup_writenull(msgcanon *m) { m->write = nullwrite; } -static void mcsetup_read(msgcanon *m, unsigned f, enc **ee, const char *dfn) +static void mcsetup_read(msgcanon *m, unsigned f, enc **ee, + const char *fn, const char *dfn) { enc *e = *ee; @@ -249,6 +253,12 @@ static void mcsetup_read(msgcanon *m, unsigned f, enc **ee, const char *dfn) *ee = 0; m->fp = e->fp; m->read = binreadembed; + if (m->f & F_PROGRESS) { + if (fprogress_init(&m->ff, fn, m->fp)) { + die(EXIT_FAILURE, "failed to initialize progress indicator: %s", + strerror(errno)); + } + } } else { m->read = binreaddetach; if (!dfn || strcmp(dfn, "-") == 0) @@ -257,6 +267,12 @@ static void mcsetup_read(msgcanon *m, unsigned f, enc **ee, const char *dfn) die(EXIT_FAILURE, "can't open `%s': %s", dfn, strerror(errno)); else m->f &= ~F_NOCLOSE; + if (m->f & F_PROGRESS) { + if (fprogress_init(&m->ff, dfn, m->fp)) { + die(EXIT_FAILURE, "failed to initialize progress indicator: %s", + strerror(errno)); + } + } } } else { m->read = textread; @@ -266,12 +282,26 @@ static void mcsetup_read(msgcanon *m, unsigned f, enc **ee, const char *dfn) m->fp = e->fp; e->ops->destroy(e); *ee = 0; - } else if (!dfn || strcmp(dfn, "-") == 0) - m->fp = stdin; - else if ((m->fp = fopen(dfn, "r")) == 0) - die(EXIT_FAILURE, "can't read file `%s': %s", dfn, strerror(errno)); - else - m->f &= ~F_NOCLOSE; + if (m->f & F_PROGRESS) { + if (fprogress_init(&m->ff, fn, m->fp)) { + die(EXIT_FAILURE, "failed to initialize progress indicator: %s", + strerror(errno)); + } + } + } else { + if (!dfn || strcmp(dfn, "-") == 0) + m->fp = stdin; + else if ((m->fp = fopen(dfn, "r")) == 0) + die(EXIT_FAILURE, "can't read file `%s': %s", dfn, strerror(errno)); + else + m->f &= ~F_NOCLOSE; + if (m->f & F_PROGRESS) { + if (fprogress_init(&m->ff, dfn, m->fp)) { + die(EXIT_FAILURE, "failed to initialize progress indicator: %s", + strerror(errno)); + } + } + } } } @@ -309,7 +339,8 @@ static void mc_endread(msgcanon *m, const encops *eops, enc **ee) if (m->fp && !(m->f & F_NOCLOSE)) { if (ferror(m->fp) || fclose(m->fp)) die(EXIT_FAILURE, "error closing message file: %s", strerror(errno)); - } + } + if (m->f & F_PROGRESS) fprogress_done(&m->ff); } static void mc_endwrite(msgcanon *m, const encops *eops, enc **ee) @@ -327,7 +358,7 @@ static void mc_endwrite(msgcanon *m, const encops *eops, enc **ee) if (m->fp && !(m->f & F_NOCLOSE)) { if (fflush(m->fp) || ferror(m->fp) || fclose(m->fp)) die(EXIT_FAILURE, "error closing message file: %s", strerror(errno)); - } + } } /*----- Signature reading and writing -------------------------------------*/ @@ -384,7 +415,7 @@ static void keyhash(key *k, sig *s, dstr *d) ghash *h; key_filter kf; - h = GH_INIT(GH_CLASS(s->h)); + h = GH_INIT(s->ch); kf.f = KCAT_PUB; kf.m = KF_CATMASK; key_fingerprint(k, h, &kf); @@ -456,7 +487,7 @@ static int sign(int argc, char *argv[]) int i; char bb[MSGBUFSZ]; size_t n; - dstr d = DSTR_INIT; + dstr d = DSTR_INIT; const encops *eo; msgcanon mc_in = MC_INIT, mc_out = MC_INIT; enc *e; @@ -470,10 +501,12 @@ static int sign(int argc, char *argv[]) { "key", OPTF_ARGREQ, 0, 'k' }, { "format", OPTF_ARGREQ, 0, 'f' }, { "output", OPTF_ARGREQ, 0, 'o' }, + { "progress", 0, 0, 'p' }, { "text", 0, 0, 't' }, + { "nocheck", 0, 0, 'C' }, { 0, 0, 0, 0 } }; - i = mdwopt(argc, argv, "k:f:o:abdt", opt, 0, 0, 0); + i = mdwopt(argc, argv, "k:f:o:abdptC", opt, 0, 0, 0); if (i < 0) break; switch (i) { case 'k': kn = optarg; break; @@ -483,13 +516,15 @@ static int sign(int argc, char *argv[]) case 't': f &= ~F_BINARY; break; case 'b': f |= F_BINARY; break; case 'd': f |= F_DETACH; break; + case 'C': f |= F_NOCHECK; break; + case 'p': f |= F_PROGRESS; break; default: f |= F_BOGUS; break; } } if (argc - optind > 1 || (f & F_BOGUS)) die(EXIT_FAILURE, "Usage: sign [-OPTIONS] [FILE]"); - if (key_open(&kf, keyring, KOPEN_READ, keyreport, 0)) + if (key_open(&kf, keyring, KOPEN_READ, key_moan, 0)) die(EXIT_FAILURE, "can't open keyring `%s'", keyring); if ((k = key_bytag(&kf, kn)) == 0) die(EXIT_FAILURE, "key `%s' not found", kn); @@ -512,7 +547,7 @@ static int sign(int argc, char *argv[]) dstr_reset(&d); key_fulltag(k, &d); s.s = getsig(k, "ccsig", 1); - if ((err = s.s->ops->check(s.s)) != 0) + if (!(f & F_NOCHECK) && (err = s.s->ops->check(s.s)) != 0) moan("key %s fails check: %s", d.buf, err); keyhash(k, s.s, &s.kh); e = initenc(eo, ofp, @@ -589,16 +624,18 @@ static void vrfchoke(const char *m, void *p) static int verify(int argc, char *argv[]) { - const char *ef = "binary", *of = 0, *dfn = 0, *kn = 0, *err; + const char *ef = "binary", *of = 0, *fn, *dfn = 0, *kn = 0, *err; vrfctx v = { 0, 0, 1 }; key_file kf; key *k, *kk = 0; sigmsg s; FILE *fp, *ofp = 0, *rfp = 0; + fprogress ff; struct tm *tm; int i; char bb[MSGBUFSZ]; size_t n; + time_t t_fresh = 0; dstr d = DSTR_INIT, dd = DSTR_INIT; const encops *eo; msgcanon mc_in = MC_INIT; @@ -612,13 +649,16 @@ static int verify(int argc, char *argv[]) { "key", OPTF_ARGREQ, 0, 'k' }, { "format", OPTF_ARGREQ, 0, 'f' }, { "output", OPTF_ARGREQ, 0, 'o' }, + { "progress", 0, 0, 'p' }, { "quiet", 0, 0, 'q' }, { "utc", 0, 0, 'u' }, + { "fresh-time", 0, 0, 't' }, { "gmt", 0, 0, 'u' }, { "verbose", 0, 0, 'v' }, + { "nocheck", 0, 0, 'C' }, { 0, 0, 0, 0 } }; - i = mdwopt(argc, argv, "k:f:o:abquv", opt, 0, 0, 0); + i = mdwopt(argc, argv, "k:f:o:abpqt:uvC", opt, 0, 0, 0); if (i < 0) break; switch (i) { case 'a': ef = "pem"; break; @@ -627,8 +667,15 @@ static int verify(int argc, char *argv[]) case 'f': ef = optarg; break; case 'o': of = optarg; break; case 'u': v.f |= F_UTC; break; + case 'C': v.f |= F_NOCHECK; break; + case 't': + if (strcmp(optarg, "always") == 0) t_fresh = 0; + else if ((t_fresh = get_date(optarg, 0)) < 0) + die(EXIT_FAILURE, "bad freshness time"); + break; case 'q': if (v.verb > 0) v.verb--; break; case 'v': if (v.verb < 10) v.verb++; break; + case 'p': v.f |= F_PROGRESS; break; default: v.f |= F_BOGUS; break; } } @@ -638,18 +685,15 @@ static int verify(int argc, char *argv[]) if ((eo = getenc(ef)) == 0) die(EXIT_FAILURE, "encoding `%s' not found", ef); - if (optind >= argc) + fn = optind < argc ? argv[optind++] : "-"; + if (strcmp(fn, "-") == 0) fp = stdin; - else if (strcmp(argv[optind], "-") == 0) { - fp = stdin; - optind++; - } else if ((fp = fopen(argv[optind], eo->rmode)) == 0) { + else if ((fp = fopen(fn, eo->rmode)) == 0) { die(EXIT_FAILURE, "couldn't open file `%s': %s", - argv[optind], strerror(errno)); - } else - optind++; + fn, strerror(errno)); + } - if (key_open(&kf, keyring, KOPEN_READ, keyreport, 0)) + if (key_open(&kf, keyring, KOPEN_READ, key_moan, 0)) die(EXIT_FAILURE, "can't open keyring `%s'", keyring); if (kn && (kk = key_bytag(&kf, kn)) == 0) die(EXIT_FAILURE, "key `%s' not found", kn); @@ -683,14 +727,14 @@ static int verify(int argc, char *argv[]) s.s = getsig(k, "ccsig", 0); dstr_reset(&d); key_fulltag(k, &d); - if (v.verb && (err = s.s->ops->check(s.s)) != 0) + if (!(v.f & F_NOCHECK) && v.verb && (err = s.s->ops->check(s.s)) != 0) printf("WARN verification key %s fails check: %s\n", d.buf, err); dstr_reset(&dd); keyhash(k, s.s, &dd); if (dd.len != s.kh.len || memcmp(dd.buf, s.kh.buf, dd.len) != 0) { if (v.verb) printf("FAIL key hash mismatch\n"); exit(EXIT_FAILURE); - } + } /* --- Now a merry dance --- */ @@ -698,7 +742,7 @@ static int verify(int argc, char *argv[]) sig_readsig(e, &s); if (optind < argc) dfn = argv[optind++]; - mcsetup_read(&mc_in, v.f, &e, dfn); + mcsetup_read(&mc_in, v.f, &e, fn, dfn); if (!of && (v.f & F_DETACH)) { rfp = ofp = 0; @@ -739,6 +783,14 @@ static int verify(int argc, char *argv[]) if (v.verb) printf("FAIL signature verification failed\n"); exit(EXIT_FAILURE); } + if (t_fresh && s.t < t_fresh) { + if (v.verb) printf("FAIL signature is stale\n"); + exit(EXIT_FAILURE); + } + if (s.t > time(0)) { + if (v.verb) printf("FAIL signature timestamp in the future\n"); + exit(EXIT_FAILURE); + } if (v.verb) { tm = (v.f & F_UTC) ? gmtime(&s.t) : localtime(&s.t); strftime(bb, sizeof(bb), "%Y-%m-%d %H:%M:%S %Z", tm); @@ -754,18 +806,23 @@ static int verify(int argc, char *argv[]) of, strerror(errno)); } rewind(rfp); + if (v.f & F_PROGRESS) fprogress_init(&ff, "copying buffer", rfp); if (v.verb && ofp == stdout) printf("DATA\n"); for (;;) { n = fread(bb, 1, sizeof(bb), rfp); if (!n) break; - if (fwrite(bb, 1, n, ofp) < n) + if (v.f & F_PROGRESS) fprogress_update(&ff, n); + if (fwrite(bb, 1, n, ofp) < n) { + if (v.f & F_PROGRESS) fprogress_done(&ff); die(EXIT_FAILURE, "error writing output: %s", strerror(errno)); + } } + if (v.f & F_PROGRESS) fprogress_done(&ff); if (ferror(rfp) || fclose(rfp)) die(EXIT_FAILURE, "error unbuffering output: %s", strerror(errno)); } if (ofp && (fflush(ofp) || ferror(ofp) || fclose(ofp))) - die(EXIT_FAILURE, "error writing output: %s", strerror(errno)); + die(EXIT_FAILURE, "error writing output: %s", strerror(errno)); /* --- Tidy up --- */ @@ -777,7 +834,7 @@ static int verify(int argc, char *argv[]) sig_destroy(&s); dstr_destroy(&d); dstr_destroy(&dd); - return (0); + return (0); } /*----- Reformatting ------------------------------------------------------*/ @@ -785,7 +842,7 @@ static int verify(int argc, char *argv[]) static int format(int argc, char *argv[]) { const char *ief = "binary", *oef = "binary"; - const char *dfn = 0, *of = 0, *mf = 0; + const char *fn, *dfn = 0, *of = 0, *mf = 0; sigmsg s; FILE *fp, *ofp = 0, *mfp = 0; int i; @@ -807,11 +864,12 @@ static int format(int argc, char *argv[]) { "embed", 0, 0, 'E' }, { "format-in", OPTF_ARGREQ, 0, 'f' }, { "format-out", OPTF_ARGREQ, 0, 'F' }, - { "message", OPTF_ARGREQ, 0, 'm' }, - { "output", OPTF_ARGREQ, 0, 'o' }, + { "message", OPTF_ARGREQ, 0, 'm' }, + { "output", OPTF_ARGREQ, 0, 'o' }, + { "progress", 0, 0, 'p' }, { 0, 0, 0, 0 } }; - i = mdwopt(argc, argv, "f:F:m:o:aADE", opt, 0, 0, 0); + i = mdwopt(argc, argv, "f:F:m:o:apADE", opt, 0, 0, 0); if (i < 0) break; switch (i) { case 'a': ief = "pem"; break; @@ -822,6 +880,7 @@ static int format(int argc, char *argv[]) case 'E': f &= ~F_DETACH; fm |= F_DETACH; break; case 'm': mf = optarg; break; case 'o': of = optarg; break; + case 'p': f |= F_PROGRESS; break; default: f |= F_BOGUS; break; } } @@ -834,16 +893,13 @@ static int format(int argc, char *argv[]) if ((oeo = getenc(oef)) == 0) die(EXIT_FAILURE, "encoding `%s' not found", oef); - if (optind >= argc) - fp = stdin; - else if (strcmp(argv[optind], "-") == 0) { + fn = optind < argc ? argv[optind++] : "-"; + if (strcmp(fn, "-") == 0) fp = stdin; - optind++; - } else if ((fp = fopen(argv[optind], ieo->rmode)) == 0) { + else if ((fp = fopen(fn, ieo->rmode)) == 0) { die(EXIT_FAILURE, "couldn't open file `%s': %s", - argv[optind], strerror(errno)); - } else - optind++; + fn, strerror(errno)); + } if (optind < argc) dfn = argv[optind++]; @@ -856,8 +912,8 @@ static int format(int argc, char *argv[]) if (((s.f ^ v.f) & v.m) != 0) moan("boundary string inconsistent with contents (ignoring)"); - - mcsetup_read(&mc_in, s.f, &ie, dfn); + + mcsetup_read(&mc_in, s.f, &ie, fn, dfn); /* --- Prepare the output stuff --- */ @@ -1056,7 +1112,7 @@ static cmd cmdtab[] = { CMD_ENCODE, CMD_DECODE, { "sign", sign, - "sign [-adt] [-k TAG] [-f FORMAT] [-o OUTPUT] [FILE]", "\ + "sign [-adptC] [-k TAG] [-f FORMAT] [-o OUTPUT] [FILE]", "\ Options:\n\ \n\ -a, --armour Same as `-f pem'.\n\ @@ -1065,10 +1121,12 @@ Options:\n\ -f, --format=FORMAT Encode as FORMAT.\n\ -k, --key=TAG Use public encryption key named by TAG.\n\ -o, --output=FILE Write output to FILE.\n\ +-p, --progress Show progress on large files.\n\ -t, --text Canonify input message as a text file.\n\ +-C, --nocheck Don't check the private key.\n\ " }, { "verify", verify, - "verify [-abquv] [-f FORMAT] [-k TAG] [-o OUTPUT]\n\t\ + "verify [-abpquvC] [-f FORMAT] [-k TAG] [-o OUTPUT]\n\t\ [FILE [MESSAGE]]", "\ Options:\n\ \n\ @@ -1077,9 +1135,12 @@ Options:\n\ -f, --format=FORMAT Decode as FORMAT.\n\ -k, --key=TAG Require that the message be signed by key TAG.\n\ -o, --output=FILE Write message to FILE.\n\ +-p, --progress Show progress on large files.\n\ -q, --quiet Produce fewer messages.\n\ +-t, --freshtime=TIME Only accept signatures made after this time.\n\ -u, --utc Show dates in UTC rather than local time.\n\ -v, --verbose Produce more verbose messages.\n\ +-C, --nocheck Don't check the public key.\n\ " }, { "info", info, "info [-au] [-f FORMAT] [FILE]", "\ @@ -1090,7 +1151,7 @@ Options:\n\ -u, --utc Show dates in UTC rather than local time.\n\ "}, { "format", format, - "format [-auADE] [-f FORMAT] [-F format] [-m FILE] [-o FILE]\n\t\ + "format [-apuADE] [-f FORMAT] [-F format] [-m FILE] [-o FILE]\n\t\ [FILE [MESSAGE]]", "\ Options:\n\ \n\ @@ -1102,6 +1163,7 @@ Options:\n\ -F, --format-out=FORMAT Encode output as FORMAT.\n\ -m, --message=FILE Write message to FILE.\n\ -o, --output=FILE Write new signature to FILE.\n\ +-p, --progress Show progress on large files.\n\ "}, { 0, 0, 0 } }; /* " Emacs seems confused. */