chiark / gitweb /
progs/perftest.c: Use from Glibc syscall numbers.
[catacomb] / progs / dsig.c
1 /* -*-c-*-
2  *
3  * Verify signatures on distribuitions of files
4  *
5  * (c) 2000 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of Catacomb.
11  *
12  * Catacomb is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU Library General Public License as
14  * published by the Free Software Foundation; either version 2 of the
15  * License, or (at your option) any later version.
16  *
17  * Catacomb is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public
23  * License along with Catacomb; if not, write to the Free
24  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25  * MA 02111-1307, USA.
26  */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 #define _FILE_OFFSET_BITS 64
31
32 #include "config.h"
33
34 #include <ctype.h>
35 #include <errno.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include <mLib/alloc.h>
41 #include <mLib/base64.h>
42 #include <mLib/macros.h>
43 #include <mLib/mdwopt.h>
44 #include <mLib/quis.h>
45 #include <mLib/report.h>
46 #include <mLib/sub.h>
47
48 #include "getdate.h"
49 #include "rand.h"
50 #include "ghash.h"
51 #include "key.h"
52 #include "key-data.h"
53 #include "noise.h"
54 #include "cc.h"
55
56 /*----- Data formatting ---------------------------------------------------*/
57
58 /* --- Binary data structure --- *
59  *
60  * The binary format, which is used for hashing and for the optional binary
61  * output, consists of a sequence of tagged blocks.  The tag describes the
62  * format and meaining of the following data.
63  */
64
65 enum {
66   /* --- Block tags --- */
67
68   T_IDENT = 0,                          /* An identifying marker */
69   T_KEYID,                              /* Key identifier */
70   T_BEGIN,                              /* Begin hashing here */
71   T_COMMENT = T_BEGIN,                  /* A textual comment */
72   T_DATE,                               /* Creation date of signature */
73   T_EXPIRE,                             /* Expiry date of signature */
74   T_FILE,                               /* File and corresponding hash */
75   T_SIGNATURE,                          /* Final signature block */
76
77   /* --- Error messages --- */
78
79   E_EOF = -1,
80   E_BIN = -2,
81   E_TAG = -3,
82   E_DATE = -4
83 };
84
85 /* --- Name translation table --- */
86
87 static const char *tagtab[] = {
88   "ident:", "keyid:",
89   "comment:", "date:", "expires:", "file:",
90   "signature:",
91   0
92 };
93
94 static const char *errtab[] = {
95   "Off-by-one bug",
96   "Unexpected end-of-file",
97   "Binary object too large",
98   "Unrecognized tag",
99   "Bad date string"
100 };
101
102 /* --- Memory representation of block types --- */
103
104 typedef struct block {
105   int tag;                              /* Type tag */
106   dstr d;                               /* String data */
107   dstr b;                               /* Binary data */
108   time_t t;                             /* Timestamp */
109   uint32 k;                             /* Keyid */
110 } block;
111
112 /* --- @timestring@ --- *
113  *
114  * Arguments:   @time_t t@ = a timestamp
115  *              @dstr *d@ = a string to write on
116  *
117  * Returns:     ---
118  *
119  * Use:         Writes a textual representation of the timestamp to the
120  *              string.
121  */
122
123 static void timestring(time_t t, dstr *d)
124 {
125   if (t == KEXP_FOREVER)
126     DPUTS(d, "forever");
127   else {
128     struct tm *tm = localtime(&t);
129     DENSURE(d, 32);
130     d->len += strftime(d->buf + d->len, 32, "%Y-%m-%d %H:%M:%S %Z", tm);
131     DPUTZ(d);
132   }
133 }
134
135 /* --- @breset@ --- *
136  *
137  * Arguments:   @block *b@ = block to reset
138  *
139  * Returns:     ---
140  *
141  * Use:         Resets a block so that more stuff can be put in it.
142  */
143
144 static void breset(block *b)
145 {
146   b->tag = 0;
147   DRESET(&b->d);
148   DRESET(&b->b);
149   b->k = 0;
150   b->t = KEXP_EXPIRE;
151 }
152
153 /* --- @binit@ --- *
154  *
155  * Arguments:   @block *b@ = block to initialize
156  *
157  * Returns:     ---
158  *
159  * Use:         Initializes a block as something to read into.
160  */
161
162 static void binit(block *b)
163 {
164   dstr_create(&b->d);
165   dstr_create(&b->b);
166   breset(b);
167 }
168
169 /* --- @bdestroy@ --- *
170  *
171  * Arguments:   @block *b@ = block to destroy
172  *
173  * Returns:     ---
174  *
175  * Use:         Destroys a block's contents.
176  */
177
178 static void bdestroy(block *b)
179 {
180   dstr_destroy(&b->d);
181   dstr_destroy(&b->b);
182 }
183
184 /* --- @bget@ --- *
185  *
186  * Arguments:   @block *b@ = pointer to block
187  *              @FILE *fp@ = stream to read from
188  *              @unsigned bin@ = binary switch
189  *
190  * Returns:     Tag of block, or an error tag.
191  *
192  * Use:         Reads a block from a stream.
193  */
194
195 static int bget(block *b, FILE *fp, unsigned bin)
196 {
197   int tag;
198
199   /* --- Read the tag --- */
200
201   if (bin)
202     tag = getc(fp);
203   else {
204     dstr d = DSTR_INIT;
205     if (getstring(fp, &d, GSF_FILE))
206       return (E_EOF);
207     for (tag = 0; tagtab[tag]; tag++) {
208       if (STRCMP(tagtab[tag], ==, d.buf))
209         goto done;
210     }
211     return (E_TAG);
212   done:;
213   }
214
215   /* --- Decide what to do next --- */
216
217   breset(b);
218   b->tag = tag;
219   switch (tag) {
220
221     /* --- Reading of strings --- */
222
223     case T_IDENT:
224     case T_COMMENT:
225       if (getstring(fp, &b->d, GSF_FILE | (bin ? GSF_RAW : 0)))
226         return (E_EOF);
227       break;
228
229     /* --- Timestamps --- */
230
231     case T_DATE:
232     case T_EXPIRE:
233       if (bin) {
234         octet buf[8];
235         if (fread(buf, sizeof(buf), 1, fp) < 1)
236           return (E_EOF);
237         b->t = ((time_t)(((LOAD32(buf + 0) << 16) << 16) &
238                          ~(unsigned long)MASK32) |
239                 (time_t)LOAD32(buf + 4));
240       } else {
241         if (getstring(fp, &b->d, GSF_FILE))
242           return (E_EOF);
243         if (STRCMP(b->d.buf, ==, "forever"))
244           b->t = KEXP_FOREVER;
245         else if ((b->t = get_date(b->d.buf, 0)) == -1)
246           return (E_DATE);
247       }
248       break;
249
250     /* --- Key ids --- */
251
252     case T_KEYID:
253       if (bin) {
254         octet buf[4];
255         if (fread(buf, sizeof(buf), 1, fp) < 1)
256           return (E_EOF);
257         b->k = LOAD32(buf);
258       } else {
259         if (getstring(fp, &b->d, GSF_FILE))
260           return (E_EOF);
261         b->k = strtoul(b->d.buf, 0, 16);
262       }
263       break;
264
265     /* --- Reading of binary data --- */
266
267     case T_FILE:
268     case T_SIGNATURE:
269       if (bin) {
270         octet buf[2];
271         uint32 sz;
272         if (fread(buf, sizeof(buf), 1, fp) < 1)
273           return (E_EOF);
274         sz = LOAD16(buf);
275         if (sz > 4096)
276           return (E_BIN);
277         DENSURE(&b->b, sz);
278         if (fread(b->b.buf + b->b.len, 1, sz, fp) < sz)
279           return (E_EOF);
280         b->b.len += sz;
281       } else {
282         base64_ctx b64;
283         if (getstring(fp, &b->d, GSF_FILE))
284           return (E_EOF);
285         base64_init(&b64);
286         base64_decode(&b64, b->d.buf, b->d.len, &b->b);
287         base64_decode(&b64, 0, 0, &b->b);
288         DRESET(&b->d);
289       }
290       if (tag == T_FILE &&
291           getstring(fp, &b->d, GSF_FILE | (bin ? GSF_RAW : 0)))
292         return (E_EOF);
293       break;
294
295       /* --- Anything else --- */
296
297     default:
298       return (E_TAG);
299   }
300
301   return (tag);
302 }
303
304 /* --- @blob@ --- *
305  *
306  * Arguments:   @block *b@ = pointer to block to emit
307  *              @dstr *d@ = output buffer
308  *
309  * Returns:     ---
310  *
311  * Use:         Encodes a block in a binary format.
312  */
313
314 static void blob(block *b, dstr *d)
315 {
316   DPUTC(d, b->tag);
317   switch (b->tag) {
318     case T_IDENT:
319     case T_COMMENT:
320       DPUTD(d, &b->d);
321       DPUTC(d, 0);
322       break;
323     case T_DATE:
324     case T_EXPIRE:
325       DENSURE(d, 8);
326       if (b->t == KEXP_FOREVER) {
327         STORE32(d->buf + d->len, 0xffffffff);
328         STORE32(d->buf + d->len + 4, 0xffffffff);
329       } else {
330         STORE32(d->buf + d->len,
331                 ((b->t & ~(unsigned long)MASK32) >> 16) >> 16);
332         STORE32(d->buf + d->len + 4, b->t);
333       }
334       d->len += 8;
335       break;
336     case T_KEYID:
337       DENSURE(d, 4);
338       STORE32(d->buf + d->len, b->k);
339       d->len += 4;
340       break;
341     case T_FILE:
342     case T_SIGNATURE:
343       DENSURE(d, 2);
344       STORE16(d->buf + d->len, b->b.len);
345       d->len += 2;
346       DPUTD(d, &b->b);
347       if (b->tag == T_FILE) {
348         DPUTD(d, &b->d);
349         DPUTC(d, 0);
350       }
351       break;
352   }
353 }
354
355 /* --- @bwrite@ --- *
356  *
357  * Arguments:   @block *b@ = pointer to block to write
358  *              @FILE *fp@ = stream to write on
359  *
360  * Returns:     ---
361  *
362  * Use:         Writes a block on a stream in a textual format.
363  */
364
365 static void bwrite(block *b, FILE *fp)
366 {
367   fputs(tagtab[b->tag], fp);
368   putc(' ', fp);
369   switch (b->tag) {
370     case T_IDENT:
371     case T_COMMENT:
372       putstring(fp, b->d.buf, 0);
373       break;
374     case T_DATE:
375     case T_EXPIRE: {
376       dstr d = DSTR_INIT;
377       timestring(b->t, &d);
378       putstring(fp, d.buf, 0);
379       dstr_destroy(&d);
380     } break;
381     case T_KEYID:
382       fprintf(fp, "%08lx", (unsigned long)b->k);
383       break;
384     case T_FILE:
385     case T_SIGNATURE: {
386       dstr d = DSTR_INIT;
387       base64_ctx b64;
388       base64_init(&b64);
389       b64.maxline = 0;
390       base64_encode(&b64, b->b.buf, b->b.len, &d);
391       base64_encode(&b64, 0, 0, &d);
392       dstr_write(&d, fp);
393       if (b->tag == T_FILE) {
394         putc(' ', fp);
395         putstring(fp, b->d.buf, 0);
396       }
397     } break;
398   }
399   putc('\n', fp);
400 }
401
402 /* --- @bemit@ --- *
403  *
404  * Arguments:   @block *b@ = pointer to block to write
405  *              @FILE *fp@ = file to write on
406  *              @ghash *h@ = pointer to hash function
407  *              @unsigned bin@ = binary/text flag
408  *
409  * Returns:     ---
410  *
411  * Use:         Spits out a block properly.
412  */
413
414 static void bemit(block *b, FILE *fp, ghash *h, unsigned bin)
415 {
416   if (h || (fp && bin)) {
417     dstr d = DSTR_INIT;
418     blob(b, &d);
419     if (h)
420       GH_HASH(h, d.buf, d.len);
421     if (fp && bin)
422       fwrite(d.buf, d.len, 1, fp);
423   }
424   if (fp && !bin)
425     bwrite(b, fp);
426 }
427
428 /*----- Static variables --------------------------------------------------*/
429
430 static const char *keyring = "keyring";
431
432 /*----- Other shared functions --------------------------------------------*/
433
434 /* --- @fhex@ --- *
435  *
436  * Arguments:   @FILE *fp@ = file to write on
437  *              @const void *p@ = pointer to data to be written
438  *              @size_t sz@ = size of the data to write
439  *
440  * Returns:     ---
441  *
442  * Use:         Emits a hex dump to a stream.
443  */
444
445 static void fhex(FILE *fp, const void *p, size_t sz)
446 {
447   const octet *q = p;
448   if (!sz)
449     return;
450   for (;;) {
451     fprintf(fp, "%02x", *q++);
452     sz--;
453     if (!sz)
454       break;
455   }
456 }
457
458 /*----- Signature generation ----------------------------------------------*/
459
460 static int sign(int argc, char *argv[])
461 {
462 #define f_bogus 1u
463 #define f_bin 2u
464 #define f_nocheck 4u
465
466   unsigned f = 0;
467   const char *ki = "dsig";
468   key_file kf;
469   key *k;
470   sig *s;
471   fhashstate fh;
472   time_t exp = KEXP_EXPIRE;
473   unsigned verb = 0;
474   const char *ifile = 0, *hfile = 0;
475   const char *ofile = 0;
476   const char *c = 0;
477   const char *err;
478   FILE *ifp, *ofp;
479   dstr d = DSTR_INIT;
480   hfpctx hfp;
481   block b;
482   int e, hf, n;
483
484   for (;;) {
485     static struct option opts[] = {
486       { "null",         0,              0,      '0' },
487       { "binary",       0,              0,      'b' },
488       { "verbose",      0,              0,      'v' },
489       { "progress",     0,              0,      'p' },
490       { "quiet",        0,              0,      'q' },
491       { "comment",      OPTF_ARGREQ,    0,      'c' },
492       { "file",         OPTF_ARGREQ,    0,      'f' },
493       { "hashes",       OPTF_ARGREQ,    0,      'h' },
494       { "output",       OPTF_ARGREQ,    0,      'o' },
495       { "key",          OPTF_ARGREQ,    0,      'k' },
496       { "expire",       OPTF_ARGREQ,    0,      'e' },
497       { "nocheck",      OPTF_ARGREQ,    0,      'C' },
498       { 0,              0,              0,      0 }
499     };
500     int i = mdwopt(argc, argv, "+0vpqbC" "c:" "f:h:o:" "k:e:",
501                    opts, 0, 0, 0);
502     if (i < 0)
503       break;
504     switch (i) {
505       case '0':
506         f |= GSF_RAW;
507         break;
508       case 'b':
509         f |= f_bin;
510         break;
511       case 'v':
512         verb++;
513         break;
514       case 'p':
515         f |= FHF_PROGRESS;
516         break;
517       case 'q':
518         if (verb > 0)
519           verb--;
520         break;
521       case 'C':
522         f |= f_nocheck;
523         break;
524       case 'c':
525         c = optarg;
526         break;
527       case 'f':
528         ifile = optarg;
529         break;
530       case 'h':
531         hfile = optarg;
532         break;
533       case 'o':
534         ofile = optarg;
535         break;
536       case 'k':
537         ki = optarg;
538         break;
539       case 'e':
540         if (STRCMP(optarg, ==, "forever"))
541           exp = KEXP_FOREVER;
542         else if ((exp = get_date(optarg, 0)) == -1)
543           die(EXIT_FAILURE, "bad expiry time");
544         break;
545       default:
546         f |= f_bogus;
547         break;
548     }
549   }
550   if (optind != argc || (f & f_bogus))
551     die(EXIT_FAILURE, "Usage: sign [-OPTIONS]");
552   if (hfile && ifile)
553     die(EXIT_FAILURE, "Inconsistent options `-h' and `-f'");
554
555   /* --- Locate the signing key --- */
556
557   if (key_open(&kf, keyring, KOPEN_WRITE, key_moan, 0))
558     die(EXIT_FAILURE, "couldn't open keyring `%s'", keyring);
559   if ((k = key_bytag(&kf, ki)) == 0)
560     die(EXIT_FAILURE, "couldn't find key `%s'", ki);
561   key_fulltag(k, &d);
562   if (exp == KEXP_FOREVER && k->exp != KEXP_FOREVER) {
563     die(EXIT_FAILURE, "key `%s' expires: can't create nonexpiring signature",
564         d.buf);
565   }
566   s = getsig(k, "dsig", 1);
567
568   /* --- Check the key --- */
569
570   if (!(f & f_nocheck) && (err = s->ops->check(s)) != 0)
571     moan("key `%s' fails check: %s", d.buf, err);
572
573   /* --- Open files --- */
574
575   if (hfile) ifile = hfile;
576   if (!ifile || STRCMP(ifile, ==, "-"))
577     ifp = stdin;
578   else if ((ifp = fopen(ifile, (f & f_bin) ? "rb" : "r")) == 0) {
579     die(EXIT_FAILURE, "couldn't open input file `%s': %s",
580         ifile, strerror(errno));
581   }
582
583   if (!ofile || STRCMP(ofile, ==, "-"))
584     ofp = stdout;
585   else if ((ofp = fopen(ofile, (f & f_bin) ? "wb" : "w")) == 0) {
586     die(EXIT_FAILURE, "couldn't open output file `%s': %s",
587         ofile, strerror(errno));
588   }
589
590   /* --- Emit the start of the output --- */
591
592   binit(&b); b.tag = T_IDENT;
593   dstr_putf(&b.d, "%s, Catacomb version " VERSION, QUIS);
594   bemit(&b, ofp, 0, f & f_bin);
595
596   breset(&b); b.tag = T_KEYID; b.k = k->id;
597   bemit(&b, ofp, 0, f & f_bin);
598
599   /* --- Start hashing, and emit the datestamps and things --- */
600
601   {
602     time_t now = time(0);
603
604     breset(&b); b.tag = T_DATE; b.t = now; bemit(&b, ofp, s->h, f & f_bin);
605     if (exp == KEXP_EXPIRE)
606       exp = now + 86400 * 28;
607     breset(&b); b.tag = T_EXPIRE; b.t = exp; bemit(&b, ofp, s->h, f & f_bin);
608     if (c) {
609       breset(&b); b.tag = T_COMMENT; DPUTS(&b.d, c);
610       bemit(&b, ofp, s->h, f & f_bin);
611     }
612
613     if (!(f & f_bin))
614       putc('\n', ofp);
615   }
616
617   /* --- Now hash the various files --- */
618
619   if (hfile) {
620     hfp.f = f;
621     hfp.fp = ifp;
622     hfp.ee = &encodingtab[ENC_HEX];
623     hfp.gch = GH_CLASS(s->h);
624     hfp.dline = &d;
625     hfp.dfile = &b.d;
626
627     n = 0;
628     for (;;) {
629       breset(&b);
630       DENSURE(&b.b, hfp.gch->hashsz);
631       hfp.hbuf = (octet *)b.b.buf;
632       if (ferror(ofp)) { f |= f_bogus; break; }
633       if ((hf = hfparse(&hfp)) == HF_EOF) break;
634       n++;
635
636       switch (hf) {
637         case HF_HASH:
638           if (hfp.gch != GH_CLASS(s->h)) {
639             moan("%s:%d: incorrect hash function `%s' (should be `%s')",
640                  hfile, n, hfp.gch->name, GH_CLASS(s->h)->name);
641             f |= f_bogus;
642           }
643           break;
644         case HF_BAD:
645           moan("%s:%d: invalid hash-file line", hfile, n);
646           f |= f_bogus;
647           break;
648         case HF_FILE:
649           b.tag = T_FILE;
650           b.b.len += hfp.gch->hashsz;
651           bemit(&b, ofp, s->h, f & f_bin);
652           break;
653       }
654     }
655   } else {
656     for (;;) {
657
658       /* --- Stop on an output error --- */
659
660       if (ferror(ofp)) {
661         f |= f_bogus;
662         break;
663       }
664
665       /* --- Read the next filename to hash --- */
666
667       fhash_init(&fh, GH_CLASS(s->h), f | FHF_BINARY);
668       breset(&b);
669       if (getstring(ifp, &b.d, GSF_FILE | f))
670         break;
671       b.tag = T_FILE;
672       DENSURE(&b.b, GH_CLASS(s->h)->hashsz);
673       if (fhash(&fh, b.d.buf, b.b.buf)) {
674         moan("error reading `%s': %s", b.d.buf, strerror(errno));
675         f |= f_bogus;
676       } else {
677         b.b.len += GH_CLASS(s->h)->hashsz;
678         if (verb) {
679           fhex(stderr, b.b.buf, b.b.len);
680           fprintf(stderr, " %s\n", b.d.buf);
681         }
682         bemit(&b, ofp, s->h, f & f_bin);
683       }
684       fhash_free(&fh);
685     }
686   }
687
688   /* --- Create the signature --- */
689
690   if (!(f & f_bogus)) {
691     breset(&b);
692     b.tag = T_SIGNATURE;
693     if ((e = s->ops->doit(s, &b.b)) != 0) {
694       moan("error creating signature: %s", key_strerror(e));
695       f |= f_bogus;
696     }
697     if (!(f & f_bogus)) {
698       bemit(&b, ofp, 0, f & f_bin);
699       key_used(&kf, k, exp);
700     }
701   }
702
703   /* --- Tidy up at the end --- */
704
705   freesig(s);
706   bdestroy(&b);
707   if (ifile)
708     fclose(ifp);
709   if (ofile) {
710     if (fclose(ofp))
711       f |= f_bogus;
712   } else {
713     if (fflush(ofp))
714       f |= f_bogus;
715   }
716   if ((e = key_close(&kf)) != 0) {
717     switch (e) {
718       case KWRITE_FAIL:
719         die(EXIT_FAILURE, "couldn't write file `%s': %s",
720             keyring, strerror(errno));
721       case KWRITE_BROKEN:
722         die(EXIT_FAILURE, "keyring file `%s' broken: %s (repair manually)",
723             keyring, strerror(errno));
724     }
725   }
726   if (f & f_bogus)
727     die(EXIT_FAILURE, "error(s) occurred while creating signature");
728   return (EXIT_SUCCESS);
729
730 #undef f_bin
731 #undef f_bogus
732 #undef f_nocheck
733 }
734
735 /*----- Signature verification --------------------------------------------*/
736
737 static int checkjunk(const char *path, const struct stat *st, void *p)
738 {
739   if (!st) printf("JUNK (error %s) %s\n", strerror(errno), path);
740   else printf("JUNK %s %s\n", describefile(st), path);
741   return (0);
742 }
743
744 static int verify(int argc, char *argv[])
745 {
746 #define f_bogus 1u
747 #define f_bin 2u
748 #define f_ok 4u
749 #define f_nocheck 8u
750
751   unsigned f = 0;
752   unsigned verb = 1;
753   key_file kf;
754   key *k = 0;
755   sig *s;
756   dstr d = DSTR_INIT;
757   const char *err;
758   fhashstate fh;
759   FILE *fp;
760   block b;
761   int e;
762
763   /* --- Parse the options --- */
764
765   for (;;) {
766     static struct option opts[] = {
767       { "verbose",      0,              0,      'v' },
768       { "progress",     0,              0,      'p' },
769       { "quiet",        0,              0,      'q' },
770       { "nocheck",      0,              0,      'C' },
771       { "junk",         0,              0,      'j' },
772       { 0,              0,              0,      0 }
773     };
774     int i = mdwopt(argc, argv, "+vpqCj", opts, 0, 0, 0);
775     if (i < 0)
776       break;
777     switch (i) {
778       case 'v':
779         verb++;
780         break;
781       case 'p':
782         f |= FHF_PROGRESS;
783         break;
784       case 'q':
785         if (verb)
786           verb--;
787         break;
788       case 'C':
789         f |= f_nocheck;
790         break;
791       case 'j':
792         f |= FHF_JUNK;
793         break;
794       default:
795         f |= f_bogus;
796         break;
797     }
798   }
799   argc -= optind;
800   argv += optind;
801   if ((f & f_bogus) || argc > 1)
802     die(EXIT_FAILURE, "Usage: verify [-qvC] [FILE]");
803
804   /* --- Open the key file, and start reading the input file --- */
805
806   if (key_open(&kf, keyring, KOPEN_READ, key_moan, 0))
807     die(EXIT_FAILURE, "couldn't open keyring `%s'\n", keyring);
808   if (argc < 1)
809     fp = stdin;
810   else {
811     if ((fp = fopen(argv[0], "rb")) == 0) {
812       die(EXIT_FAILURE, "couldn't open file `%s': %s\n",
813               argv[0], strerror(errno));
814     }
815     if (getc(fp) == 0) {
816       ungetc(0, fp);
817       f |= f_bin;
818     } else {
819       fclose(fp);
820       if ((fp = fopen(argv[0], "r")) == 0) {
821         die(EXIT_FAILURE, "couldn't open file `%s': %s\n",
822                 argv[0], strerror(errno));
823       }
824     }
825   }
826
827   /* --- Read the introductory matter --- */
828
829   binit(&b);
830   for (;;) {
831     breset(&b);
832     e = bget(&b, fp, f & f_bin);
833     if (e < 0)
834       die(EXIT_FAILURE, "error reading packet: %s", errtab[-e]);
835     if (e >= T_BEGIN)
836       break;
837     switch (e) {
838       case T_IDENT:
839         if (verb > 2)
840           printf("INFO ident: `%s'\n", b.d.buf);
841         break;
842       case T_KEYID:
843         if ((k = key_byid(&kf, b.k)) == 0) {
844           if (verb)
845             printf("FAIL key %08lx not found\n", (unsigned long)b.k);
846           exit(EXIT_FAILURE);
847         }
848         if (verb > 2) {
849           DRESET(&b.d);
850           key_fulltag(k, &b.d);
851           printf("INFO key: %s\n", b.d.buf);
852         }
853         break;
854       default:
855         die(EXIT_FAILURE, "(internal) unknown packet type\n");
856         break;
857     }
858   }
859
860   /* --- Initialize the hash function and start reading hashed packets --- */
861
862   if (!k) {
863     if (verb)
864       puts("FAIL no keyid packet found");
865     exit(EXIT_FAILURE);
866   }
867
868   s = getsig(k, "dsig", 0);
869   if (!(f & f_nocheck) && verb && (err = s->ops->check(s)) != 0)
870     printf("WARN public key fails check: %s", err);
871
872   fhash_init(&fh, GH_CLASS(s->h), f | FHF_BINARY);
873   for (;;) {
874     switch (e) {
875       case T_COMMENT:
876         if (verb > 1)
877           printf("INFO comment: `%s'\n", b.d.buf);
878         bemit(&b, 0, s->h, 0);
879         break;
880       case T_DATE:
881         if (verb > 2) {
882           DRESET(&b.d);
883           timestring(b.t, &b.d);
884           printf("INFO date: %s\n", b.d.buf);
885         }
886         bemit(&b, 0, s->h, 0);
887         break;
888       case T_EXPIRE: {
889         time_t now = time(0);
890         if (b.t != KEXP_FOREVER && b.t < now) {
891           if (verb > 1)
892             puts("BAD signature has expired");
893           f |= f_bogus;
894         }
895         if (verb > 2) {
896           DRESET(&b.d);
897           timestring(b.t, &b.d);
898           printf("INFO expires: %s\n", b.d.buf);
899         }
900         bemit(&b, 0, s->h, 0);
901       } break;
902       case T_FILE:
903         DRESET(&d);
904         DENSURE(&d, GH_CLASS(s->h)->hashsz);
905         if (fhash(&fh, b.d.buf, d.buf)) {
906           if (verb > 1) {
907             printf("BAD error reading file `%s': %s\n",
908                     b.d.buf, strerror(errno));
909           }
910           f |= f_bogus;
911         } else if (b.b.len != GH_CLASS(s->h)->hashsz ||
912                    MEMCMP(d.buf, !=, b.b.buf, b.b.len)) {
913           if (verb > 1)
914             printf("BAD file `%s' has incorrect hash\n", b.d.buf);
915           f |= f_bogus;
916         } else if (verb > 3) {
917           fputs("INFO hash: ", stdout);
918           fhex(stdout, b.b.buf, b.b.len);
919           printf(" %s\n", b.d.buf);
920         }
921         bemit(&b, 0, s->h, 0);
922         break;
923       case T_SIGNATURE:
924         if (s->ops->doit(s, &b.b)) {
925           if (verb > 1)
926             puts("BAD bad signature");
927           f |= f_bogus;
928         } else if (verb > 2)
929           puts("INFO good signature");
930         goto done;
931       default:
932         if (verb)
933           printf("FAIL invalid packet type %i\n", e);
934         exit(EXIT_FAILURE);
935         break;
936     }
937     breset(&b);
938     e = bget(&b, fp, f & f_bin);
939     if (e < 0) {
940       if (verb)
941         printf("FAIL error reading packet: %s\n", errtab[-e]);
942       exit(EXIT_FAILURE);
943     }
944   }
945 done:
946   if ((f & FHF_JUNK) && fhash_junk(&fh, checkjunk, 0))
947     f |= f_bogus;
948   fhash_free(&fh);
949   bdestroy(&b);
950   dstr_destroy(&d);
951   freesig(s);
952   key_close(&kf);
953   if (fp != stdin)
954     fclose(fp);
955   if (verb) {
956     if (f & f_bogus)
957       puts("FAIL signature invalid");
958     else
959       puts("OK signature verified");
960   }
961   return (f & f_bogus ? EXIT_FAILURE : EXIT_SUCCESS);
962
963 #undef f_bogus
964 #undef f_bin
965 #undef f_ok
966 #undef f_nocheck
967 }
968
969 /*----- Main code ---------------------------------------------------------*/
970
971 #define LISTS(LI)                                                       \
972   LI("Lists", list,                                                     \
973      listtab[i].name, listtab[i].name)                                  \
974   LI("Signature schemes", sig,                                          \
975      sigtab[i].name, sigtab[i].name)                                    \
976   LI("Hash functions", hash,                                            \
977      ghashtab[i], ghashtab[i]->name)
978
979 MAKELISTTAB(listtab, LISTS)
980
981 int cmd_show(int argc, char *argv[])
982 {
983   return (displaylists(listtab, argv + 1));
984 }
985
986 static int cmd_help(int, char **);
987
988 static cmd cmdtab[] = {
989   { "help", cmd_help, "help [COMMAND...]" },
990   { "show", cmd_show, "show [ITEM...]" },
991   { "sign", sign,
992     "sign [-0bpqvC] [-c COMMENT] [-k TAG] [-e EXPIRE]\n\t\
993 [-f FILE] [-h FILE] [-o OUTPUT]",
994     "\
995 Options:\n\
996 \n\
997 -0, --null              Read null-terminated filenames from stdin.\n\
998 -b, --binary            Produce a binary output file.\n\
999 -q, --quiet             Produce fewer messages while working.\n\
1000 -v, --verbose           Produce more messages while working.\n\
1001 -p, --progress          Show progress on large files.\n\
1002 -C, --nocheck           Don't check the private key.\n\
1003 -c, --comment=COMMENT   Include COMMENT in the output file.\n\
1004 -f, --file=FILE         Read filenames to hash from FILE.\n\
1005 -h, --hashes=FILE       Read precomputed hashes from FILE.\n\
1006 -o, --output=FILE       Write the signed result to FILE.\n\
1007 -k, --key=TAG           Use a key named by TAG.\n\
1008 -e, --expire=TIME       The signature should expire after TIME.\n\
1009 " },
1010   { "verify", verify,
1011     "verify [-pqvC] [FILE]", "\
1012 Options:\n\
1013 \n\
1014 -q, --quiet             Produce fewer messages while working.\n\
1015 -v, --verbose           Produce more messages while working.\n\
1016 -p, --progress          Show progress on large files.\n\
1017 -C, --nocheck           Don't check the public key.\n\
1018 " },
1019   { 0, 0, 0 }
1020 };
1021
1022 static int cmd_help(int argc, char **argv)
1023 {
1024   sc_help(cmdtab, stdout, argv + 1);
1025   return (0);
1026 }
1027
1028 void version(FILE *fp)
1029 {
1030   pquis(fp, "$, Catacomb version " VERSION "\n");
1031 }
1032
1033 static void usage(FILE *fp)
1034 {
1035   pquis(fp, "Usage: $ [-k KEYRING] COMMAND [ARGS]\n");
1036 }
1037
1038 void help_global(FILE *fp)
1039 {
1040   usage(fp);
1041   fputs("\n\
1042 Create and verify signatures on lists of files.\n\
1043 \n\
1044 Global command-line options:\n\
1045 \n\
1046 -h, --help [COMMAND...] Show this help message, or help for COMMANDs.\n\
1047 -v, --version           Show program version number.\n\
1048 -u, --usage             Show a terse usage message.\n\
1049 \n\
1050 -k, --keyring=FILE      Read keys from FILE.\n",
1051         fp);
1052 }
1053
1054 /* --- @main@ --- *
1055  *
1056  * Arguments:   @int argc@ = number of command line arguments
1057  *              @char *argv[]@ = vector of command line arguments
1058  *
1059  * Returns:     Zero if successful, nonzero otherwise.
1060  *
1061  * Use:         Signs or verifies signatures on lists of files.  Useful for
1062  *              ensuring that a distribution is unmolested.
1063  */
1064
1065 int main(int argc, char *argv[])
1066 {
1067   unsigned f = 0;
1068
1069 #define f_bogus 1u
1070
1071   /* --- Initialize the library --- */
1072
1073   ego(argv[0]);
1074   sub_init();
1075   rand_noisesrc(RAND_GLOBAL, &noise_source);
1076   rand_seed(RAND_GLOBAL, 160);
1077
1078   /* --- Parse options --- */
1079
1080   for (;;) {
1081     static struct option opts[] = {
1082       { "help",         0,              0,      'h' },
1083       { "version",      0,              0,      'v' },
1084       { "usage",        0,              0,      'u' },
1085       { "keyring",      OPTF_ARGREQ,    0,      'k' },
1086       { 0,              0,              0,      0 }
1087     };
1088     int i = mdwopt(argc, argv, "+hvu k:", opts, 0, 0, 0);
1089     if (i < 0)
1090       break;
1091     switch (i) {
1092       case 'h':
1093         sc_help(cmdtab, stdout, argv + optind);
1094         exit(0);
1095         break;
1096       case 'v':
1097         version(stdout);
1098         exit(0);
1099         break;
1100       case 'u':
1101         usage(stdout);
1102         exit(0);
1103       case 'k':
1104         keyring = optarg;
1105         break;
1106       default:
1107         f |= f_bogus;
1108         break;
1109     }
1110   }
1111
1112   argc -= optind;
1113   argv += optind;
1114   optind = 0;
1115   if (f & f_bogus || argc < 1) {
1116     usage(stderr);
1117     exit(EXIT_FAILURE);
1118   }
1119
1120   /* --- Dispatch to the correct subcommand handler --- */
1121
1122   return (findcmd(cmdtab, argv[0])->cmd(argc, argv));
1123
1124 #undef f_bogus
1125 }
1126
1127 /*----- That's all, folks -------------------------------------------------*/