progs/perftest.c: Use from Glibc syscall numbers.
[catacomb] / progs / catsign.c
CommitLineData
fa54fe1e 1/* -*-c-*-
2 *
fa54fe1e 3 * Sign files
4 *
5 * (c) 2005 Straylight/Edgeware
6 */
7
45c0fd36 8/*----- Licensing notice --------------------------------------------------*
fa54fe1e 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.
45c0fd36 16 *
fa54fe1e 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.
45c0fd36 21 *
fa54fe1e 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
cd6eca43
MW
30#define _FILE_OFFSET_BITS 64
31
fa54fe1e 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#include <time.h>
40
41#include <mLib/base64.h>
42#include <mLib/dstr.h>
b94dfe3c 43#include <mLib/macros.h>
fa54fe1e 44#include <mLib/mdwopt.h>
45#include <mLib/quis.h>
46#include <mLib/report.h>
47#include <mLib/sub.h>
48
49#include "buf.h"
50#include "rand.h"
51#include "noise.h"
52#include "mprand.h"
53#include "key.h"
9cea6911 54#include "getdate.h"
fa54fe1e 55#include "cc.h"
56
57#include "ectab.h"
58#include "ptab.h"
59
fa54fe1e 60/*----- Static variables --------------------------------------------------*/
61
62static const char *keyring = "keyring";
63
64/*----- Data formats ------------------------------------------------------*
65 *
66 * Our crypto stuff is split into three parts: a header describing the key,
67 * the message itself, and the signature. In a detached signature, the
68 * message is separate, and the header and signature are combined into one
69 * encoded chunk. In an attached signature, the message is included. There
70 * are two forms of message: binary and text. Binary messages are divided
71 * into chunks, each preceded by a 2-octet length, and terminated by a
72 * zero-length chunk. Text messages are byte-stuffed, as for RFC821, and
73 * trailing whitespace before a newline is ignored.
74 */
75
76typedef struct sigmsg {
77 unsigned f; /* Flags */
78#define F_DETACH 1u
79#define F_BINARY 2u
80#define F_SIGMASK 3u
81#define F_HASHMASK (F_BINARY)
82 uint32 keyid; /* Key identifier */
83 time_t t; /* When the signature was made */
84 dstr kh; /* Key fingerprint (sanity check) */
85 dstr sig; /* Signature */
86 sig *s; /* Signature algorithm */
87} sigmsg;
88
89#define F_MIDLINE 16u
90#define F_EOF 32u
91#define F_NOCLOSE 64u
92#define F_BOGUS 128u
93#define F_BUFFER 256u
94#define F_UTC 512u
946c3f72 95#define F_NOCHECK 1024u
cd6eca43 96#define F_PROGRESS 2048u
fa54fe1e 97
98/*----- Chunk I/O ---------------------------------------------------------*/
99
100static void chunk_write(enc *e, const void *p, size_t n)
101{
102 octet b[2];
103
104 assert(n <= 0xffff);
105 STORE16(b, n);
106 if (e->ops->write(e, b, 2) || e->ops->write(e, p, n))
107 die(EXIT_FAILURE, "error writing output: %s", strerror(errno));
108}
109
110static size_t chunk_read(enc *e, void *p)
111{
112 octet b[2];
113 size_t n;
114
115 if (e->ops->read(e, b, 2) != 2)
116 goto err;
117 n = LOAD16(b);
118 if (n && e->ops->read(e, p, n) != n)
119 goto err;
120 return (n);
121
122err:
123 if (!errno) die(EXIT_FAILURE, "unexpected end-of-file on input");
124 else die(EXIT_FAILURE, "error reading input: %s", strerror(errno));
125 return (0);
126}
127
128/*----- Message canonification --------------------------------------------*/
129
130#define MSGBUFSZ 65536
131#define MSGBUFTHRESH 32768
132
133typedef struct msgcanon {
134 unsigned f;
135 FILE *fp;
136 enc *e;
cd6eca43 137 fprogress ff;
fa54fe1e 138 size_t (*read)(struct msgcanon *, void *);
139 void (*write)(struct msgcanon *, const void *, size_t);
140 void (*close)(struct msgcanon *);
141} msgcanon;
142
143#define MC_INIT { 0 }
144
145static size_t textread(msgcanon *m, void *bp)
146{
147 size_t n = 0, nsp = 0;
148 int ch;
149 char *b = bp;
150 unsigned f = m->f;
151
152 if (f & F_EOF) return (0);
153 for (;;) {
154 if ((ch = getc(m->fp)) == EOF) goto eof;
155 if (!(f & (F_DETACH | F_MIDLINE)) && ch == '.') {
156 ch = getc(m->fp);
157 if (ch == '\n') goto eof;
158 }
159 if (ch == '\n') {
160 n -= nsp; nsp = 0;
161 if (n >= MSGBUFTHRESH) goto full;
162 b[n++] = ch;
163 f &= ~F_MIDLINE;
141c1284 164 } else if (ISSPACE(ch)) {
fa54fe1e 165 f |= F_MIDLINE;
166 if (n >= MSGBUFSZ) goto full;
167 b[n++] = ch; nsp++;
168 } else {
169 f |= F_MIDLINE;
170 nsp = 0;
171 if (n >= MSGBUFTHRESH) goto full;
172 b[n++] = ch;
173 }
174 }
175eof:
176 f |= F_EOF;
177 goto done;
178full:
179 ungetc(ch, m->fp);
180done:
181 m->f = f;
cd6eca43 182 if (m->f & F_PROGRESS) fprogress_update(&m->ff, n);
fa54fe1e 183 return (n);
184}
185
186static void textwrite(msgcanon *m, const void *bp, size_t n)
187{
188 const char *p = bp, *l = p + n;
189 unsigned f = m->f;
190
191 while (p < l) {
192 if (!(f & (F_MIDLINE | F_DETACH)) &&
193 (*p == '.' || *p == '-'))
194 putc('.', m->fp);
195 if (*p == '\n') f &= ~F_MIDLINE;
196 else f |= F_MIDLINE;
197 putc(*p, m->fp);
198 p++;
199 }
200 m->f = f;
201}
202
203static size_t binreadembed(msgcanon *m, void *bp)
cd6eca43
MW
204{
205 size_t n = chunk_read(m->e, bp);
206 if (m->f & F_PROGRESS) fprogress_update(&m->ff, n);
207 return (n);
208}
209
fa54fe1e 210static size_t binreaddetach(msgcanon *m, void *bp)
cd6eca43
MW
211{
212 size_t n = fread(bp, 1, MSGBUFSZ - 1, m->fp);
213 if (m->f & F_PROGRESS) fprogress_update(&m->ff, n);
214 return (n);
215}
fa54fe1e 216
217static void binwriteembed(msgcanon *m, const void *bp, size_t n)
218 { chunk_write(m->e, bp, n); }
219
220static void nullwrite(msgcanon *m, const void *bp, size_t n) { ; }
221
222static void mcsetup_readfile(msgcanon *m, unsigned f, const char *fn)
223{
cd6eca43 224 m->f = F_DETACH | (f & (F_BINARY | F_PROGRESS));
141c1284 225 if (!fn || STRCMP(fn, ==, "-")) {
fa54fe1e 226 m->fp = stdin;
227 m->f |= F_NOCLOSE;
228 } else if ((m->fp = fopen(fn, (f & F_BINARY) ? "rb" : "r")) == 0)
229 die(EXIT_FAILURE, "couldn't open file `%s': %s", fn, strerror(errno));
cd6eca43
MW
230 if (m->f & F_PROGRESS) {
231 if (fprogress_init(&m->ff, fn, m->fp)) {
232 die(EXIT_FAILURE, "failed to initialize progress indicator: %s",
233 strerror(errno));
234 }
235 }
fa54fe1e 236 m->read = (f & F_BINARY) ? binreaddetach : textread;
237}
238
239static void mcsetup_writenull(msgcanon *m) { m->write = nullwrite; }
240
cd6eca43
MW
241static void mcsetup_read(msgcanon *m, unsigned f, enc **ee,
242 const char *fn, const char *dfn)
fa54fe1e 243{
244 enc *e = *ee;
245
246 m->f = f | F_NOCLOSE;
247
248 if (dfn && !(f & F_DETACH)) die(EXIT_FAILURE, "signature is not detached");
249 if (f & F_BINARY) {
250 if (!(f & F_DETACH)) {
251 m->e = e;
252 *ee = 0;
253 m->fp = e->fp;
254 m->read = binreadembed;
cd6eca43
MW
255 if (m->f & F_PROGRESS) {
256 if (fprogress_init(&m->ff, fn, m->fp)) {
257 die(EXIT_FAILURE, "failed to initialize progress indicator: %s",
258 strerror(errno));
259 }
260 }
fa54fe1e 261 } else {
262 m->read = binreaddetach;
141c1284 263 if (!dfn || STRCMP(dfn, ==, "-"))
fa54fe1e 264 m->fp = stdin;
265 else if ((m->fp = fopen(dfn, "rb")) == 0)
266 die(EXIT_FAILURE, "can't open `%s': %s", dfn, strerror(errno));
267 else
268 m->f &= ~F_NOCLOSE;
cd6eca43
MW
269 if (m->f & F_PROGRESS) {
270 if (fprogress_init(&m->ff, dfn, m->fp)) {
271 die(EXIT_FAILURE, "failed to initialize progress indicator: %s",
272 strerror(errno));
273 }
274 }
fa54fe1e 275 }
276 } else {
277 m->read = textread;
278 if (!(f & F_DETACH)) {
279 if (e->ops->decdone(e) || ferror(e->fp))
280 die(EXIT_FAILURE, "error at end of signature header");
281 m->fp = e->fp;
282 e->ops->destroy(e);
283 *ee = 0;
cd6eca43
MW
284 if (m->f & F_PROGRESS) {
285 if (fprogress_init(&m->ff, fn, m->fp)) {
286 die(EXIT_FAILURE, "failed to initialize progress indicator: %s",
287 strerror(errno));
288 }
289 }
290 } else {
141c1284 291 if (!dfn || STRCMP(dfn, ==, "-"))
cd6eca43
MW
292 m->fp = stdin;
293 else if ((m->fp = fopen(dfn, "r")) == 0)
294 die(EXIT_FAILURE, "can't read file `%s': %s", dfn, strerror(errno));
295 else
296 m->f &= ~F_NOCLOSE;
297 if (m->f & F_PROGRESS) {
298 if (fprogress_init(&m->ff, dfn, m->fp)) {
299 die(EXIT_FAILURE, "failed to initialize progress indicator: %s",
300 strerror(errno));
301 }
302 }
303 }
fa54fe1e 304 }
305}
306
307static void mcsetup_write(msgcanon *m, unsigned f, enc **ee)
308{
309 enc *e = *ee;
310
311 m->f = f | F_NOCLOSE;
312
313 if (f & F_DETACH)
314 m->write = nullwrite;
315 else if (f & F_BINARY) {
316 m->e = e;
317 *ee = 0;
318 m->fp = e->fp;
319 m->write = binwriteembed;
320 } else {
321 if (e->ops->encdone(e) || ferror(e->fp))
322 die(EXIT_FAILURE, "error at end of signature header");
323 m->fp = e->fp;
324 e->ops->destroy(e);
325 *ee = 0;
326 m->write = textwrite;
327 }
328}
329
330static void mc_endread(msgcanon *m, const encops *eops, enc **ee)
331{
332 if (!(m->f & F_DETACH)) {
333 if (m->f & F_BINARY)
334 *ee = m->e;
335 else
336 *ee = initdec(eops, m->fp, checkbdry, "CATSIGN SIGNATURE");
337 }
338 if (m->fp && !(m->f & F_NOCLOSE)) {
339 if (ferror(m->fp) || fclose(m->fp))
340 die(EXIT_FAILURE, "error closing message file: %s", strerror(errno));
45c0fd36 341 }
cd6eca43 342 if (m->f & F_PROGRESS) fprogress_done(&m->ff);
fa54fe1e 343}
344
345static void mc_endwrite(msgcanon *m, const encops *eops, enc **ee)
346{
347 if (!(m->f & F_BINARY)) {
348 if (m->f & F_MIDLINE) putc('\n', m->fp);
349 if (!(m->f & F_DETACH)) {
350 putc('.', m->fp); putc('\n', m->fp);
351 *ee = initenc(eops, m->fp, "CATSIGN SIGNATURE");
352 }
353 } else if (!(m->f & F_DETACH)) {
354 chunk_write(m->e, 0, 0);
355 *ee = m->e;
356 }
357 if (m->fp && !(m->f & F_NOCLOSE)) {
358 if (fflush(m->fp) || ferror(m->fp) || fclose(m->fp))
359 die(EXIT_FAILURE, "error closing message file: %s", strerror(errno));
45c0fd36 360 }
fa54fe1e 361}
362
363/*----- Signature reading and writing -------------------------------------*/
364
365static void sig_init(sigmsg *s, unsigned f, uint32 keyid)
366{
367 s->f = f & F_SIGMASK;
368 s->keyid = keyid;
369 time(&s->t);
370 s->s = 0;
371 dstr_create(&s->kh);
372 dstr_create(&s->sig);
373}
374
375static void sig_destroy(sigmsg *s)
376{
377 dstr_destroy(&s->kh);
378 dstr_destroy(&s->sig);
379 if (s->s) freesig(s->s);
380}
381
382static void sigtobuffer(sigmsg *s, buf *b, int hashp)
383{
384 kludge64 t;
385
386 ASSIGN64(t, s->t);
387 if (hashp) buf_putu16(b, s->f & F_HASHMASK);
388 else buf_putu16(b, s->f & F_SIGMASK);
389 buf_putu32(b, s->keyid);
390 buf_putu32(b, HI64(t));
391 buf_putu32(b, LO64(t));
f387fcb1 392 buf_putdstr16(b, &s->kh);
fa54fe1e 393 assert(BOK(b));
394}
395
396static void dohash(ghash *h, const void *p, size_t n)
397{
fa54fe1e 398 GH_HASH(h, p, n);
399}
400
401static void sig_hash(sigmsg *s)
402{
403 octet bb[16384];
404 buf b;
405
406 buf_init(&b, bb, sizeof(bb));
407 sigtobuffer(s, &b, 1);
408 dohash(s->s->h, BBASE(&b), BLEN(&b));
409}
410
411static void keyhash(key *k, sig *s, dstr *d)
412{
413 ghash *h;
414 key_filter kf;
415
02dfbd5b 416 h = GH_INIT(s->ch);
fa54fe1e 417 kf.f = KCAT_PUB;
418 kf.m = KF_CATMASK;
419 key_fingerprint(k, h, &kf);
420 dstr_ensure(d, GH_CLASS(h)->hashsz);
421 GH_DONE(h, d->buf + d->len);
422 d->len += GH_CLASS(h)->hashsz;
423 GH_DESTROY(h);
424}
425
426static void sig_writeheader(enc *e, sigmsg *s)
427{
428 octet bb[16384];
429 buf b;
430
431 buf_init(&b, bb, sizeof(bb));
432 sigtobuffer(s, &b, 0);
433 chunk_write(e, BBASE(&b), BLEN(&b));
434}
435
436static void sig_writesig(enc *e, sigmsg *s)
437 { chunk_write(e, s->sig.buf, s->sig.len); }
438
b94dfe3c 439static void NORETURN diechoke(const char *m, void *p)
9b1663a5 440 { die(EXIT_FAILURE, "%s%s%s", (const char *)p, p ? ": " : "", m); }
fa54fe1e 441
442static void sig_readheader(enc *e, sigmsg *s,
b94dfe3c
MW
443 void NORETURN (*choke)(const char *, void *),
444 void *p)
fa54fe1e 445{
446 uint16 f;
447 octet bb[MSGBUFSZ];
448 uint32 x, y;
449 kludge64 t;
450 buf b;
451 size_t n;
452
453 n = chunk_read(e, bb);
454 buf_init(&b, bb, n);
455 if (buf_getu16(&b, &f)) choke("missing flags", p);
456 if (buf_getu32(&b, &x)) choke("missing keyid", p);
457 sig_init(s, f, x);
458 if (buf_getu32(&b, &x) || buf_getu32(&b, &y))
459 choke("missing datestamp", p);
460 SET64(t, x, y); s->t = GET64(time_t, t);
f387fcb1 461 if (buf_getdstr16(&b, &s->kh))
fa54fe1e 462 choke("missing key hash", p);
463 if (BLEFT(&b))
464 choke("junk at end", p);
465}
466
467static void sig_readsig(enc *e, sigmsg *s)
468{
469 octet bb[MSGBUFSZ];
470 size_t n;
471
472 n = chunk_read(e, bb);
473 dstr_putm(&s->sig, bb, n);
474}
475
476/*----- Signing -----------------------------------------------------------*/
477
478static int sign(int argc, char *argv[])
479{
480 const char *ef = "binary", *fn = 0, *of = 0, *kn = "ccsig", *err;
481 unsigned f = 0;
482 key_file kf;
483 key *k;
484 sigmsg s;
485 FILE *ofp = 0;
486 int i;
487 char bb[MSGBUFSZ];
488 size_t n;
45c0fd36 489 dstr d = DSTR_INIT;
fa54fe1e 490 const encops *eo;
491 msgcanon mc_in = MC_INIT, mc_out = MC_INIT;
492 enc *e;
493
494 for (;;) {
495 static const struct option opt[] = {
496 { "armour", 0, 0, 'a' },
497 { "armor", 0, 0, 'a' },
498 { "binary", 0, 0, 'b' },
499 { "detach", 0, 0, 'd' },
500 { "key", OPTF_ARGREQ, 0, 'k' },
501 { "format", OPTF_ARGREQ, 0, 'f' },
502 { "output", OPTF_ARGREQ, 0, 'o' },
cd6eca43 503 { "progress", 0, 0, 'p' },
fa54fe1e 504 { "text", 0, 0, 't' },
946c3f72 505 { "nocheck", 0, 0, 'C' },
fa54fe1e 506 { 0, 0, 0, 0 }
507 };
cd6eca43 508 i = mdwopt(argc, argv, "k:f:o:abdptC", opt, 0, 0, 0);
fa54fe1e 509 if (i < 0) break;
510 switch (i) {
511 case 'k': kn = optarg; break;
512 case 'f': ef = optarg; break;
513 case 'o': of = optarg; break;
514 case 'a': ef = "pem"; break;
515 case 't': f &= ~F_BINARY; break;
516 case 'b': f |= F_BINARY; break;
517 case 'd': f |= F_DETACH; break;
946c3f72 518 case 'C': f |= F_NOCHECK; break;
cd6eca43 519 case 'p': f |= F_PROGRESS; break;
fa54fe1e 520 default: f |= F_BOGUS; break;
521 }
522 }
523 if (argc - optind > 1 || (f & F_BOGUS))
524 die(EXIT_FAILURE, "Usage: sign [-OPTIONS] [FILE]");
525
213e565f 526 if (key_open(&kf, keyring, KOPEN_READ, key_moan, 0))
fa54fe1e 527 die(EXIT_FAILURE, "can't open keyring `%s'", keyring);
528 if ((k = key_bytag(&kf, kn)) == 0)
529 die(EXIT_FAILURE, "key `%s' not found", kn);
530
531 if ((eo = getenc(ef)) == 0)
532 die(EXIT_FAILURE, "encoding `%s' not found", ef);
533
534 fn = (optind >= argc) ? 0 : argv[optind++];
535
141c1284 536 if (!of || STRCMP(of, ==, "-"))
fa54fe1e 537 ofp = stdout;
538 else if ((ofp = fopen(of, eo->wmode)) == 0) {
539 die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
9b1663a5 540 of, strerror(errno));
fa54fe1e 541 }
542
543 /* --- Start the work --- */
544
545 sig_init(&s, f, k->id);
546 dstr_reset(&d);
547 key_fulltag(k, &d);
548 s.s = getsig(k, "ccsig", 1);
946c3f72 549 if (!(f & F_NOCHECK) && (err = s.s->ops->check(s.s)) != 0)
fa54fe1e 550 moan("key %s fails check: %s", d.buf, err);
551 keyhash(k, s.s, &s.kh);
552 e = initenc(eo, ofp,
553 (f & F_DETACH) ? "CATSIGN SIGNATURE" :
554 (f & F_BINARY) ? "CATSIGN MESSAGE" :
555 "CATSIGN MESSAGE HEADER");
556 sig_writeheader(e, &s);
557
558 /* --- Hash the message --- */
559
560 mcsetup_readfile(&mc_in, f, fn);
561 mcsetup_write(&mc_out, f, &e);
562 sig_hash(&s);
563 for (;;) {
564 n = mc_in.read(&mc_in, bb);
565 if (!n) break;
566 dohash(s.s->h, bb, n);
567 mc_out.write(&mc_out, bb, n);
568 }
569 mc_endread(&mc_in, 0, 0);
570 mc_endwrite(&mc_out, eo, &e);
571
572 /* --- Write the signature --- */
573
574 if (s.s->ops->doit(s.s, &s.sig))
575 die(EXIT_FAILURE, "signature failed");
576 sig_writesig(e, &s);
577 e->ops->encdone(e);
578 if (fflush(ofp) || ferror(ofp) || fclose(ofp))
579 die(EXIT_FAILURE, "error writing signature: %s", strerror(errno));
580
581 /* --- All done --- */
582
583 freeenc(e);
584 key_close(&kf);
585 sig_destroy(&s);
586 dstr_destroy(&d);
587 return (0);
588}
589
590/*----- Verifying ---------------------------------------------------------*/
591
592typedef struct vrfctx {
593 unsigned f, m;
594 int verb;
595 const char *what;
596} vrfctx;
597
598static int vrfbdry(const char *b, void *p)
599{
600 vrfctx *v = p;
601
141c1284 602 if (STRCMP(b, ==, "CATSIGN MESSAGE")) {
fa54fe1e 603 v->f |= F_BINARY;
604 v->m |= F_BINARY | F_DETACH;
605 return (1);
141c1284 606 } else if (STRCMP(b, ==, "CATSIGN MESSAGE HEADER")) {
fa54fe1e 607 v->m |= F_BINARY | F_DETACH;
608 return (1);
141c1284 609 } else if (STRCMP(b, ==, "CATSIGN SIGNATURE")) {
fa54fe1e 610 v->f |= F_DETACH;
611 v->m |= F_DETACH;
612 return (1);
613 } else
614 return (0);
615}
616
b94dfe3c 617static void NORETURN vrfchoke(const char *m, void *p)
fa54fe1e 618{
619 vrfctx *v = p;
620 if (v->verb) printf("FAIL %s: %s\n", v->what, m);
621 exit(EXIT_FAILURE);
622}
623
624static int verify(int argc, char *argv[])
625{
cd6eca43 626 const char *ef = "binary", *of = 0, *fn, *dfn = 0, *kn = 0, *err;
fa54fe1e 627 vrfctx v = { 0, 0, 1 };
628 key_file kf;
629 key *k, *kk = 0;
630 sigmsg s;
631 FILE *fp, *ofp = 0, *rfp = 0;
cd6eca43 632 fprogress ff;
fa54fe1e 633 struct tm *tm;
634 int i;
635 char bb[MSGBUFSZ];
636 size_t n;
9cea6911 637 time_t t_fresh = 0;
fa54fe1e 638 dstr d = DSTR_INIT, dd = DSTR_INIT;
639 const encops *eo;
640 msgcanon mc_in = MC_INIT;
641 enc *e;
642
643 for (;;) {
644 static const struct option opt[] = {
645 { "armour", 0, 0, 'a' },
646 { "armor", 0, 0, 'a' },
647 { "buffer", 0, 0, 'b' },
648 { "key", OPTF_ARGREQ, 0, 'k' },
649 { "format", OPTF_ARGREQ, 0, 'f' },
650 { "output", OPTF_ARGREQ, 0, 'o' },
cd6eca43 651 { "progress", 0, 0, 'p' },
fa54fe1e 652 { "quiet", 0, 0, 'q' },
653 { "utc", 0, 0, 'u' },
9cea6911 654 { "fresh-time", 0, 0, 't' },
fa54fe1e 655 { "gmt", 0, 0, 'u' },
656 { "verbose", 0, 0, 'v' },
946c3f72 657 { "nocheck", 0, 0, 'C' },
fa54fe1e 658 { 0, 0, 0, 0 }
659 };
cd6eca43 660 i = mdwopt(argc, argv, "k:f:o:abpqt:uvC", opt, 0, 0, 0);
fa54fe1e 661 if (i < 0) break;
662 switch (i) {
663 case 'a': ef = "pem"; break;
664 case 'b': v.f |= F_BUFFER; break;
665 case 'k': kn = optarg; break;
666 case 'f': ef = optarg; break;
667 case 'o': of = optarg; break;
668 case 'u': v.f |= F_UTC; break;
946c3f72 669 case 'C': v.f |= F_NOCHECK; break;
9cea6911 670 case 't':
141c1284 671 if (STRCMP(optarg, ==, "always")) t_fresh = 0;
45c0fd36 672 else if ((t_fresh = get_date(optarg, 0)) < 0)
9cea6911 673 die(EXIT_FAILURE, "bad freshness time");
674 break;
fa54fe1e 675 case 'q': if (v.verb > 0) v.verb--; break;
676 case 'v': if (v.verb < 10) v.verb++; break;
cd6eca43 677 case 'p': v.f |= F_PROGRESS; break;
fa54fe1e 678 default: v.f |= F_BOGUS; break;
679 }
680 }
681 if (argc - optind > 2 || (v.f & F_BOGUS))
682 die(EXIT_FAILURE, "Usage: verify [-OPTIONS] [FILE [MESSAGE]]");
683
684 if ((eo = getenc(ef)) == 0)
685 die(EXIT_FAILURE, "encoding `%s' not found", ef);
686
cd6eca43 687 fn = optind < argc ? argv[optind++] : "-";
141c1284 688 if (STRCMP(fn, ==, "-"))
fa54fe1e 689 fp = stdin;
cd6eca43 690 else if ((fp = fopen(fn, eo->rmode)) == 0) {
fa54fe1e 691 die(EXIT_FAILURE, "couldn't open file `%s': %s",
cd6eca43
MW
692 fn, strerror(errno));
693 }
fa54fe1e 694
213e565f 695 if (key_open(&kf, keyring, KOPEN_READ, key_moan, 0))
fa54fe1e 696 die(EXIT_FAILURE, "can't open keyring `%s'", keyring);
697 if (kn && (kk = key_bytag(&kf, kn)) == 0)
698 die(EXIT_FAILURE, "key `%s' not found", kn);
699
700 e = initdec(eo, fp, vrfbdry, &v);
701
702 /* --- Read the header chunk --- */
703
704 v.what = "malformed header";
705 sig_readheader(e, &s, vrfchoke, &v);
706
707 if (((s.f ^ v.f) & v.m) != 0) {
708 if (v.verb) printf("FAIL boundary string inconsistent with contents\n");
709 exit(EXIT_FAILURE);
710 }
711 v.f |= s.f;
712
713 if ((k = key_byid(&kf, s.keyid)) == 0) {
714 if (v.verb) printf("FAIL key id %08lx not found\n",
715 (unsigned long)s.keyid);
716 exit(EXIT_FAILURE);
717 }
d31eb5ca 718 if (kk && k != kk) {
fa54fe1e 719 if (v.verb) {
720 dstr_reset(&d); key_fulltag(k, &d);
721 dstr_reset(&dd); key_fulltag(kk, &dd);
722 printf("FAIL signing key is %s; expected key %s\n", d.buf, dd.buf);
723 }
724 exit(EXIT_FAILURE);
725 }
726
727 s.s = getsig(k, "ccsig", 0);
728 dstr_reset(&d); key_fulltag(k, &d);
946c3f72 729 if (!(v.f & F_NOCHECK) && v.verb && (err = s.s->ops->check(s.s)) != 0)
fa54fe1e 730 printf("WARN verification key %s fails check: %s\n", d.buf, err);
731
732 dstr_reset(&dd); keyhash(k, s.s, &dd);
141c1284 733 if (dd.len != s.kh.len || MEMCMP(dd.buf, !=, s.kh.buf, dd.len)) {
fa54fe1e 734 if (v.verb) printf("FAIL key hash mismatch\n");
735 exit(EXIT_FAILURE);
45c0fd36 736 }
fa54fe1e 737
738 /* --- Now a merry dance --- */
739
740 if (v.f & F_DETACH)
741 sig_readsig(e, &s);
742 if (optind < argc)
743 dfn = argv[optind++];
cd6eca43 744 mcsetup_read(&mc_in, v.f, &e, fn, dfn);
fa54fe1e 745
746 if (!of && (v.f & F_DETACH)) {
747 rfp = ofp = 0;
748 v.f &= ~F_BUFFER;
141c1284 749 } else if (!of || STRCMP(of, ==, "-")) {
fa54fe1e 750 v.f |= F_BUFFER;
751 ofp = stdout;
29219919 752 } else if (of && !(v.f & F_BUFFER)) {
fa54fe1e 753 if ((ofp = fopen(of, (v.f & F_BINARY) ? "wb" : "w")) == 0) {
754 die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
755 of, strerror(errno));
756 }
757 rfp = ofp;
29219919
MW
758 }
759 if ((v.f & F_BUFFER) && (rfp = tmpfile()) == 0)
fa54fe1e 760 die(EXIT_FAILURE, "couldn't create temporary file: %s", strerror(errno));
761
762 /* --- Read the message and verify the signature --- */
763
764 sig_hash(&s);
765 for (;;) {
766 n = mc_in.read(&mc_in, bb);
767 if (!n) break;
768 dohash(s.s->h, bb, n);
769 if (rfp) fwrite(bb, 1, n, rfp);
770 }
771 mc_endread(&mc_in, eo, &e);
772 if (!(v.f & F_DETACH))
773 sig_readsig(e, &s);
774 if (rfp && (ferror(rfp) || fflush(rfp))) {
775 if (v.verb) printf("FAIL error writing message: %s\n", strerror(errno));
776 exit(EXIT_FAILURE);
777 }
778
779 /* --- Check the signature --- */
780
781 if (s.s->ops->doit(s.s, &s.sig)) {
782 if (v.verb) printf("FAIL signature verification failed\n");
783 exit(EXIT_FAILURE);
784 }
9cea6911 785 if (t_fresh && s.t < t_fresh) {
786 if (v.verb) printf("FAIL signature is stale\n");
787 exit(EXIT_FAILURE);
788 }
789 if (s.t > time(0)) {
790 if (v.verb) printf("FAIL signature timestamp in the future\n");
791 exit(EXIT_FAILURE);
792 }
fa54fe1e 793 if (v.verb) {
794 tm = (v.f & F_UTC) ? gmtime(&s.t) : localtime(&s.t);
795 strftime(bb, sizeof(bb), "%Y-%m-%d %H:%M:%S %Z", tm);
796 printf("INFO good-signature %s\n", d.buf);
797 printf("INFO date %s\n", bb);
798 }
799
800 /* --- Unbuffer buffered input --- */
801
802 if (v.f & F_BUFFER) {
803 if (!ofp && (ofp = fopen(of, "wb")) == 0) {
804 die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
805 of, strerror(errno));
806 }
807 rewind(rfp);
cd6eca43 808 if (v.f & F_PROGRESS) fprogress_init(&ff, "copying buffer", rfp);
fa54fe1e 809 if (v.verb && ofp == stdout) printf("DATA\n");
810 for (;;) {
811 n = fread(bb, 1, sizeof(bb), rfp);
812 if (!n) break;
cd6eca43
MW
813 if (v.f & F_PROGRESS) fprogress_update(&ff, n);
814 if (fwrite(bb, 1, n, ofp) < n) {
815 if (v.f & F_PROGRESS) fprogress_done(&ff);
fa54fe1e 816 die(EXIT_FAILURE, "error writing output: %s", strerror(errno));
cd6eca43 817 }
fa54fe1e 818 }
cd6eca43 819 if (v.f & F_PROGRESS) fprogress_done(&ff);
fa54fe1e 820 if (ferror(rfp) || fclose(rfp))
821 die(EXIT_FAILURE, "error unbuffering output: %s", strerror(errno));
822 }
823 if (ofp && (fflush(ofp) || ferror(ofp) || fclose(ofp)))
45c0fd36 824 die(EXIT_FAILURE, "error writing output: %s", strerror(errno));
fa54fe1e 825
826 /* --- Tidy up --- */
827
828 e->ops->decdone(e);
829 if (v.verb && ofp != stdout)
830 printf("OK verified successfully\n");
831 freeenc(e);
832 key_close(&kf);
833 sig_destroy(&s);
834 dstr_destroy(&d);
835 dstr_destroy(&dd);
45c0fd36 836 return (0);
fa54fe1e 837}
838
839/*----- Reformatting ------------------------------------------------------*/
840
841static int format(int argc, char *argv[])
842{
843 const char *ief = "binary", *oef = "binary";
cd6eca43 844 const char *fn, *dfn = 0, *of = 0, *mf = 0;
fa54fe1e 845 sigmsg s;
846 FILE *fp, *ofp = 0, *mfp = 0;
847 int i;
848 size_t n;
849 msgcanon mc_in = MC_INIT, mc_out = MC_INIT;
850 char bb[MSGBUFSZ];
851 vrfctx v = { 0, 0, 1 };
852 unsigned f = 0, fm = ~F_SIGMASK, sf;
853 const encops *ieo, *oeo;
854 enc *ie, *oe;
855
856 for (;;) {
857 static const struct option opt[] = {
858 { "armour-in", 0, 0, 'a' },
859 { "armor-in", 0, 0, 'a' },
860 { "armour-out", 0, 0, 'A' },
861 { "armor-out", 0, 0, 'A' },
862 { "detach", 0, 0, 'D' },
863 { "embed", 0, 0, 'E' },
864 { "format-in", OPTF_ARGREQ, 0, 'f' },
865 { "format-out", OPTF_ARGREQ, 0, 'F' },
45c0fd36
MW
866 { "message", OPTF_ARGREQ, 0, 'm' },
867 { "output", OPTF_ARGREQ, 0, 'o' },
cd6eca43 868 { "progress", 0, 0, 'p' },
fa54fe1e 869 { 0, 0, 0, 0 }
870 };
cd6eca43 871 i = mdwopt(argc, argv, "f:F:m:o:apADE", opt, 0, 0, 0);
fa54fe1e 872 if (i < 0) break;
873 switch (i) {
874 case 'a': ief = "pem"; break;
875 case 'A': oef = "pem"; break;
876 case 'f': ief = optarg; break;
877 case 'F': oef = optarg; break;
878 case 'D': f |= F_DETACH; fm |= F_DETACH; break;
879 case 'E': f &= ~F_DETACH; fm |= F_DETACH; break;
880 case 'm': mf = optarg; break;
881 case 'o': of = optarg; break;
cd6eca43 882 case 'p': f |= F_PROGRESS; break;
fa54fe1e 883 default: f |= F_BOGUS; break;
884 }
885 }
886
887 if (argc - optind > 2 || (f & F_BOGUS))
888 die(EXIT_FAILURE, "Usage: format [-OPTIONS] [FILE [MESSAGE]]");
889
890 if ((ieo = getenc(ief)) == 0)
891 die(EXIT_FAILURE, "encoding `%s' not found", ief);
892 if ((oeo = getenc(oef)) == 0)
893 die(EXIT_FAILURE, "encoding `%s' not found", oef);
894
cd6eca43 895 fn = optind < argc ? argv[optind++] : "-";
141c1284 896 if (STRCMP(fn, ==, "-"))
fa54fe1e 897 fp = stdin;
cd6eca43 898 else if ((fp = fopen(fn, ieo->rmode)) == 0) {
fa54fe1e 899 die(EXIT_FAILURE, "couldn't open file `%s': %s",
cd6eca43
MW
900 fn, strerror(errno));
901 }
fa54fe1e 902
903 if (optind < argc)
904 dfn = argv[optind++];
905
906 ie = initdec(ieo, fp, vrfbdry, &v);
907
908 /* --- Read the header chunk --- */
909
910 sig_readheader(ie, &s, diechoke, "malformed header");
911
912 if (((s.f ^ v.f) & v.m) != 0)
913 moan("boundary string inconsistent with contents (ignoring)");
45c0fd36 914
cd6eca43 915 mcsetup_read(&mc_in, s.f, &ie, fn, dfn);
fa54fe1e 916
917 /* --- Prepare the output stuff --- */
918
919 if (!of && !mf) of = "-";
920 sf = s.f;
921 f = (f & fm) | (s.f & ~fm);
922 s.f = f & F_SIGMASK;
923
924 if (sf & F_DETACH)
925 sig_readsig(ie, &s);
926
927 if (!of)
928 mcsetup_writenull(&mc_out);
929 else {
141c1284 930 if (STRCMP(of, ==, "-"))
fa54fe1e 931 ofp = stdout;
932 else if ((ofp = fopen(of, oeo->wmode)) == 0) {
933 die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
934 of, strerror(errno));
935 }
936 oe = initenc(oeo, ofp,
937 (f & F_DETACH) ? "CATSIGN SIGNATURE" :
938 (f & F_BINARY) ? "CATSIGN MESSAGE" :
939 "CATSIGN MESSAGE HEADER");
940 sig_writeheader(oe, &s);
941 mcsetup_write(&mc_out, f, &oe);
942 }
943
944 if (mf) {
141c1284 945 if (STRCMP(mf, ==, "-"))
fa54fe1e 946 mfp = stdout;
947 else if ((mfp = fopen(mf, (f & F_BINARY) ? "wb" : "w")) == 0) {
948 die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
949 mf, strerror(errno));
950 }
951 }
952
953 /* --- Wade through the message body --- */
954
955 for (;;) {
956 n = mc_in.read(&mc_in, bb);
957 if (!n) break;
958 mc_out.write(&mc_out, bb, n);
959 if (mfp) fwrite(bb, 1, n, mfp);
960 }
961 mc_endread(&mc_in, ieo, &ie);
962 if (of) mc_endwrite(&mc_out, oeo, &oe);
963
964 /* --- Write the signature --- */
965
966 if (!(sf & F_DETACH))
967 sig_readsig(ie, &s);
968 if (of) {
969 sig_writesig(oe, &s);
970 oe->ops->encdone(oe);
971 }
972
973 /* --- All done --- */
974
975 ie->ops->decdone(ie);
976 if (ferror(fp) || fclose(fp))
977 die(EXIT_FAILURE, "error reading input signature: %s", strerror(errno));
978 if (ofp && (fflush(ofp) || ferror(ofp) || fclose(ofp)))
979 die(EXIT_FAILURE, "error writing output signature: %s", strerror(errno));
980 if (mfp && (fflush(mfp) || ferror(mfp) || fclose(mfp)))
981 die(EXIT_FAILURE, "error writing output message: %s", strerror(errno));
982 freeenc(ie);
983 if (of) freeenc(oe);
984 sig_destroy(&s);
985 return (0);
986}
987
b94dfe3c 988static void NORETURN infochoke(const char *m, void *p)
fa54fe1e 989{
990 vrfctx *v = p;
991 printf("BAD %s: %s\n", v->what, m);
992 exit(EXIT_FAILURE);
993}
994
995static void infokeyreport(const char *file, int line,
996 const char *err, void *p)
997{ /*whatever*/; }
998
999static int info(int argc, char *argv[])
1000{
1001 const char *ef = "binary";
1002 vrfctx v = { 0, 0, 1 };
1003 key_file kf;
1004 key *k;
1005 sigmsg s;
1006 FILE *fp;
1007 int i;
1008 struct tm *tm;
1009 char bb[256];
1010 dstr d = DSTR_INIT;
1011 const encops *eo;
1012 enc *e;
1013
1014 for (;;) {
1015 static const struct option opt[] = {
1016 { "armour", 0, 0, 'a' },
1017 { "armor", 0, 0, 'a' },
1018 { "format", OPTF_ARGREQ, 0, 'f' },
1019 { "gmt", 0, 0, 'u' },
1020 { "utc", 0, 0, 'u' },
1021 { 0, 0, 0, 0 }
1022 };
1023 i = mdwopt(argc, argv, "f:au", opt, 0, 0, 0);
1024 if (i < 0) break;
1025 switch (i) {
1026 case 'a': ef = "pem"; break;
1027 case 'f': ef = optarg; break;
1028 case 'u': v.f |= F_UTC; break;
1029 default: v.f |= F_BOGUS; break;
1030 }
1031 }
1032 if (argc - optind > 1 || (v.f & F_BOGUS))
1033 die(EXIT_FAILURE, "Usage: info [-OPTIONS] [FILE]");
1034
1035 if ((eo = getenc(ef)) == 0)
1036 die(EXIT_FAILURE, "encoding `%s' not found", ef);
1037
1038 if (optind >= argc)
1039 fp = stdin;
141c1284 1040 else if (STRCMP(argv[optind], ==, "-")) {
fa54fe1e 1041 fp = stdin;
1042 optind++;
1043 } else if ((fp = fopen(argv[optind], eo->rmode)) == 0) {
1044 die(EXIT_FAILURE, "couldn't open file `%s': %s",
1045 argv[optind], strerror(errno));
1046 } else
1047 optind++;
1048
1049 if (key_open(&kf, keyring, KOPEN_READ, infokeyreport, 0)) {
1050 printf("NOTE can't open keyring `%s'\n", keyring);
1051 keyring = 0;
1052 }
1053 e = initdec(eo, fp, vrfbdry, &v);
1054
1055 v.what = "malformed header";
1056 sig_readheader(e, &s, infochoke, &v);
1057
1058 printf("INFO flags %sdetach %sbinary\n",
1059 (s.f & F_DETACH) ? "" : "!",
1060 (s.f & F_BINARY) ? "" : "!");
1061
1062 if (((s.f ^ v.f) & v.m) != 0) {
1063 printf("WARN boundary string inconsistent with contents\n");
1064 printf("INFO expected-flags");
1065 if (v.m & F_DETACH) printf(" %sdetach", (v.f & F_DETACH) ? "" : "!");
1066 if (v.m & F_BINARY) printf(" %sbinary", (v.f & F_BINARY) ? "" : "!");
1067 putchar('\n');
1068 }
1069 v.f |= s.f;
1070
1071 tm = (v.f & F_UTC) ? gmtime(&s.t) : localtime(&s.t);
1072 strftime(bb, sizeof(bb), "%Y-%m-%d %H:%M:%S %Z", tm);
1073 printf("INFO date %s\n", bb);
1074
1075 if (keyring && (k = key_byid(&kf, s.keyid)) != 0) {
1076 dstr_reset(&d); key_fulltag(k, &d);
1077 printf("INFO key %s\n", d.buf);
1078 } else
1079 printf("INFO unknown-key %08lx\n", (unsigned long)s.keyid);
1080
1081 if (keyring) key_close(&kf);
1082 dstr_destroy(&d);
1083 sig_destroy(&s);
1084 return (0);
1085}
1086
1087/*----- Main code ---------------------------------------------------------*/
1088
1089#define LISTS(LI) \
1090 LI("Lists", list, \
1091 listtab[i].name, listtab[i].name) \
1092 LI("Signature schemes", sig, \
1093 sigtab[i].name, sigtab[i].name) \
1094 LI("Encodings", enc, \
1095 enctab[i].name, enctab[i].name) \
1096 LI("Hash functions", hash, \
1097 ghashtab[i], ghashtab[i]->name)
1098
1099MAKELISTTAB(listtab, LISTS)
1100
1101int cmd_show(int argc, char *argv[])
1102{
1103 return (displaylists(listtab, argv + 1));
1104}
1105
1106static int cmd_help(int, char **);
1107
1108static cmd cmdtab[] = {
1109 { "help", cmd_help, "help [COMMAND...]" },
1110 { "show", cmd_show, "show [ITEM...]" },
1111 CMD_ENCODE,
1112 CMD_DECODE,
1113 { "sign", sign,
cd6eca43 1114 "sign [-adptC] [-k TAG] [-f FORMAT] [-o OUTPUT] [FILE]", "\
fa54fe1e 1115Options:\n\
1116\n\
1117-a, --armour Same as `-f pem'.\n\
1118-b, --binary Treat the input message as binary data.\n\
1119-d, --detach Produce a detached signature.\n\
1120-f, --format=FORMAT Encode as FORMAT.\n\
1121-k, --key=TAG Use public encryption key named by TAG.\n\
1122-o, --output=FILE Write output to FILE.\n\
cd6eca43 1123-p, --progress Show progress on large files.\n\
fa54fe1e 1124-t, --text Canonify input message as a text file.\n\
946c3f72 1125-C, --nocheck Don't check the private key.\n\
fa54fe1e 1126" },
1127 { "verify", verify,
cd6eca43 1128 "verify [-abpquvC] [-f FORMAT] [-k TAG] [-o OUTPUT]\n\t\
fa54fe1e 1129[FILE [MESSAGE]]", "\
1130Options:\n\
1131\n\
1132-a, --armour Same as `-f pem'.\n\
1133-b, --buffer Buffer message until signature is verified.\n\
1134-f, --format=FORMAT Decode as FORMAT.\n\
1135-k, --key=TAG Require that the message be signed by key TAG.\n\
1136-o, --output=FILE Write message to FILE.\n\
cd6eca43 1137-p, --progress Show progress on large files.\n\
fa54fe1e 1138-q, --quiet Produce fewer messages.\n\
9cea6911 1139-t, --freshtime=TIME Only accept signatures made after this time.\n\
fa54fe1e 1140-u, --utc Show dates in UTC rather than local time.\n\
1141-v, --verbose Produce more verbose messages.\n\
946c3f72 1142-C, --nocheck Don't check the public key.\n\
fa54fe1e 1143" },
1144 { "info", info,
1145 "info [-au] [-f FORMAT] [FILE]", "\
1146Options:\n\
1147\n\
1148-a, --armour Same as `-f pem'.\n\
1149-f, --format=FORMAT Decode as FORMAT.\n\
1150-u, --utc Show dates in UTC rather than local time.\n\
1151"},
1152 { "format", format,
cd6eca43 1153 "format [-apuADE] [-f FORMAT] [-F format] [-m FILE] [-o FILE]\n\t\
fa54fe1e 1154[FILE [MESSAGE]]", "\
1155Options:\n\
1156\n\
1157-a, --armour-in Same as `-f pem'.\n\
1158-A, --armour-out Same as `-F pem'.\n\
1159-D, --detach Create detached signature.\n\
1160-E, --embed Create signature with embedded message.\n\
1161-f, --format-in=FORMAT Decode input as FORMAT.\n\
1162-F, --format-out=FORMAT Encode output as FORMAT.\n\
1163-m, --message=FILE Write message to FILE.\n\
1164-o, --output=FILE Write new signature to FILE.\n\
cd6eca43 1165-p, --progress Show progress on large files.\n\
fa54fe1e 1166"},
1167 { 0, 0, 0 }
1168}; /* " Emacs seems confused. */
1169
1170static int cmd_help(int argc, char **argv)
1171{
1172 sc_help(cmdtab, stdout, argv + 1);
1173 return (0);
1174}
1175
1176void version(FILE *fp)
1177{
1178 pquis(fp, "$, Catacomb version " VERSION "\n");
1179}
1180
1181static void usage(FILE *fp)
1182{
1183 pquis(fp, "Usage: $ [-k KEYRING] COMMAND [ARGS]\n");
1184}
1185
1186void help_global(FILE *fp)
1187{
1188 usage(fp);
1189 fputs("\n\
1190Sign and verify data.\n\
1191\n\
1192Global command-line options:\n\
1193\n\
1194-h, --help [COMMAND...] Show this help message, or help for COMMANDs.\n\
1195-v, --version Show program version number.\n\
1196-u, --usage Show a terse usage message.\n\
1197\n\
1198-k, --keyring=FILE Read keys from FILE.\n",
1199 fp);
1200}
1201
1202/* --- @main@ --- *
1203 *
1204 * Arguments: @int argc@ = number of command line arguments
1205 * @char *argv[]@ = vector of command line arguments
1206 *
1207 * Returns: Zero if successful, nonzero otherwise.
1208 *
1209 * Use: Encrypts or decrypts files.
1210 */
1211
1212int main(int argc, char *argv[])
1213{
1214 unsigned f = 0;
1215
1216#define f_bogus 1u
1217
1218 /* --- Initialize the library --- */
1219
1220 ego(argv[0]);
1221 sub_init();
1222 rand_noisesrc(RAND_GLOBAL, &noise_source);
1223 rand_seed(RAND_GLOBAL, 160);
fa54fe1e 1224
1225 /* --- Parse options --- */
1226
1227 for (;;) {
1228 static struct option opts[] = {
1229 { "help", 0, 0, 'h' },
1230 { "version", 0, 0, 'v' },
1231 { "usage", 0, 0, 'u' },
1232 { "keyring", OPTF_ARGREQ, 0, 'k' },
1233 { 0, 0, 0, 0 }
1234 };
1235 int i = mdwopt(argc, argv, "+hvu k:", opts, 0, 0, 0);
1236 if (i < 0)
1237 break;
1238 switch (i) {
1239 case 'h':
1240 sc_help(cmdtab, stdout, argv + optind);
1241 exit(0);
1242 break;
1243 case 'v':
1244 version(stdout);
1245 exit(0);
1246 break;
1247 case 'u':
1248 usage(stdout);
1249 exit(0);
1250 case 'k':
1251 keyring = optarg;
1252 break;
1253 default:
1254 f |= f_bogus;
1255 break;
1256 }
1257 }
1258
1259 argc -= optind;
1260 argv += optind;
1261 optind = 0;
1262 if (f & f_bogus || argc < 1) {
1263 usage(stderr);
1264 exit(EXIT_FAILURE);
1265 }
1266
1267 /* --- Dispatch to the correct subcommand handler --- */
1268
1269 return (findcmd(cmdtab, argv[0])->cmd(argc, argv));
1270
1271#undef f_bogus
1272}
1273
1274/*----- That's all, folks -------------------------------------------------*/