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