key-data.c (key_struct{set,steal}): Assert no other references.
[u/mdw/catacomb] / catsign.c
index dc96e55..3bbd7b2 100644 (file)
--- a/catsign.c
+++ b/catsign.c
@@ -7,7 +7,7 @@
  * (c) 2005 Straylight/Edgeware
  */
 
-/*----- Licensing notice --------------------------------------------------* 
+/*----- Licensing notice --------------------------------------------------*
  *
  * This file is part of Catacomb.
  *
  * 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 <ctype.h>
 #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. */