7e419d4d8b04700df3b6f06803706f5be3ba6185
[project/ucert.git] / ucert.c
1 /*
2 * Copyright (C) 2018 Daniel Golle <daniel@makrotopia.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3
6 * as published by the Free Software Foundation
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14 #define _GNU_SOURCE
15
16 #include <fcntl.h>
17 #include <dlfcn.h>
18 #include <stdio.h>
19 #include <stdbool.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <getopt.h>
23 #include <stdint.h>
24 #include <unistd.h>
25 #include <inttypes.h>
26 #include <sys/stat.h>
27 #include <sys/wait.h>
28
29 #include <json-c/json.h>
30 #include <libubox/blob.h>
31 #include <libubox/utils.h>
32 #include <libubox/list.h>
33 #include <libubox/vlist.h>
34 #include <libubox/blobmsg_json.h>
35
36 #include "usign.h"
37
38 #define CERT_BUF_LEN 4096
39
40 static enum {
41 CMD_APPEND,
42 CMD_DUMP,
43 CMD_ISSUE,
44 CMD_REVOKE,
45 CMD_VERIFY,
46 CMD_NONE,
47 } cmd = CMD_NONE;
48
49 static bool quiet;
50
51 /*
52 * ucert structure
53 * | BLOB |
54 * | SIGNATURE | PAYLOAD |
55 * | |[ BLOBMSG CONTAINER ]|
56 * | |[[T,i,v,e,f,pubkey ]]|
57 */
58 enum cert_attr {
59 CERT_ATTR_SIGNATURE,
60 CERT_ATTR_PAYLOAD,
61 CERT_ATTR_MAX
62 };
63
64 static const struct blob_attr_info cert_policy[CERT_ATTR_MAX] = {
65 [CERT_ATTR_SIGNATURE] = { .type = BLOB_ATTR_BINARY },
66 [CERT_ATTR_PAYLOAD] = { .type = BLOB_ATTR_NESTED },
67 };
68
69 enum cert_cont_attr {
70 CERT_CT_ATTR_PAYLOAD,
71 CERT_CT_ATTR_MAX
72 };
73
74 static const struct blobmsg_policy cert_cont_policy[CERT_CT_ATTR_MAX] = {
75 [CERT_CT_ATTR_PAYLOAD] = { .name = "ucert", .type = BLOBMSG_TYPE_TABLE },
76 };
77
78 enum cert_payload_attr {
79 CERT_PL_ATTR_CERTTYPE,
80 CERT_PL_ATTR_CERTID,
81 CERT_PL_ATTR_VALIDFROMTIME,
82 CERT_PL_ATTR_EXPIRETIME,
83 CERT_PL_ATTR_PUBKEY,
84 CERT_PL_ATTR_KEY_FINGERPRINT,
85 CERT_PL_ATTR_MAX
86 };
87
88 enum certtype_id {
89 CERTTYPE_UNSPEC,
90 CERTTYPE_AUTH,
91 CERTTYPE_REVOKE
92 };
93
94 static const struct blobmsg_policy cert_payload_policy[CERT_PL_ATTR_MAX] = {
95 [CERT_PL_ATTR_CERTTYPE] = { .name = "certtype", .type = BLOBMSG_TYPE_INT32 },
96 [CERT_PL_ATTR_CERTID] = { .name = "certid", .type = BLOBMSG_TYPE_INT32 },
97 [CERT_PL_ATTR_VALIDFROMTIME] = { .name = "validfrom", .type = BLOBMSG_TYPE_INT64 },
98 [CERT_PL_ATTR_EXPIRETIME] = { .name = "expiresat", .type = BLOBMSG_TYPE_INT64 },
99 [CERT_PL_ATTR_PUBKEY] = { .name = "pubkey", .type = BLOBMSG_TYPE_STRING },
100 [CERT_PL_ATTR_KEY_FINGERPRINT] = { .name = "fingerprint", .type = BLOBMSG_TYPE_STRING },
101 };
102
103 /* list to store certificate chain at runtime */
104 struct cert_object {
105 struct list_head list;
106 struct blob_attr *cert[CERT_ATTR_MAX];
107 };
108
109 /* write buffer to file */
110 static int write_file(const char *filename, void *buf, size_t len, bool append) {
111 FILE *f;
112 size_t outlen;
113
114 f = fopen(filename, append?"a":"w");
115 if (!f)
116 return 1;
117
118 outlen = fwrite(buf, 1, len, f);
119 fclose(f);
120 return (outlen == len);
121 }
122
123 /* load certfile into list */
124 static int cert_load(const char *certfile, struct list_head *chain) {
125 FILE *f;
126 struct blob_attr *certtb[CERT_ATTR_MAX];
127 struct blob_attr *bufpt;
128 struct cert_object *cobj;
129 char filebuf[CERT_BUF_LEN];
130 int ret = 0, pret = 0;
131 int len, pos = 0;
132
133 f = fopen(certfile, "r");
134 if (!f)
135 return 1;
136
137 len = fread(&filebuf, 1, CERT_BUF_LEN - 1, f);
138 if (len < 64)
139 return 1;
140
141 ret = ferror(f) || !feof(f);
142 fclose(f);
143 if (ret)
144 return 1;
145
146 bufpt = (struct blob_attr *)filebuf;
147 do {
148 pret = blob_parse(bufpt, certtb, cert_policy, CERT_ATTR_MAX);
149 if (pret <= 0)
150 /* no attributes found */
151 break;
152
153 if (pos + blob_pad_len(bufpt) > len)
154 /* blob exceeds filebuffer */
155 break;
156 else
157 pos += blob_pad_len(bufpt);
158
159 cobj = calloc(1, sizeof(*cobj));
160 memcpy(cobj->cert, &certtb, sizeof(certtb));
161 list_add_tail(&cobj->list, chain);
162 ret += pret;
163 bufpt = blob_next(bufpt);
164 /* repeat parsing while there is still enough remaining data in buffer */
165 } while(len > pos + sizeof(struct blob_attr));
166
167 return (ret <= 0);
168 }
169
170 /* append signature to certfile */
171 static int cert_append(const char *certfile, const char *sigfile) {
172 FILE *fs;
173 char filebuf[CERT_BUF_LEN];
174 struct blob_buf sigbuf;
175 int len;
176 int ret;
177
178 fs = fopen(sigfile, "r");
179 if (!fs)
180 return 1;
181
182 len = fread(&filebuf, 1, CERT_BUF_LEN - 1, fs);
183 ret = ferror(fs) || !feof(fs) || (len < 64);
184 fclose(fs);
185 if (ret)
186 return 1;
187
188 blob_buf_init(&sigbuf, 0);
189 blob_put(&sigbuf, CERT_ATTR_SIGNATURE, filebuf, len);
190 ret = write_file(certfile, sigbuf.head, blob_raw_len(sigbuf.head), true);
191 blob_buf_free(&sigbuf);
192 return ret;
193 }
194
195 /* verify the signature of a single chain element */
196 static int cert_verify_blob(struct blob_attr *cert[CERT_ATTR_MAX],
197 const char *pubkeyfile, const char *pubkeydir) {
198 int i;
199 char msgfname[256], sigfname[256];
200 int ret;
201 char tmpdir[] = "/tmp/ucert-XXXXXX";
202
203 if (mkdtemp(tmpdir) == NULL)
204 return errno;
205
206 snprintf(msgfname, sizeof(msgfname) - 1, "%s/%s", tmpdir, "payload");
207 snprintf(sigfname, sizeof(sigfname) - 1, "%s/%s", tmpdir, "payload.sig");
208
209 for (i = 0; i < CERT_ATTR_MAX; i++) {
210 struct blob_attr *v = cert[i];
211
212 if (!v)
213 break;
214
215 switch(cert_policy[i].type) {
216 case BLOB_ATTR_BINARY:
217 write_file(sigfname, blob_data(v), blob_len(v), false);
218 break;
219 case BLOB_ATTR_NESTED:
220 write_file(msgfname, blob_data(v), blob_len(v), false);
221 break;
222 }
223 }
224
225 ret = usign_v(msgfname, pubkeyfile, pubkeydir, sigfname, quiet);
226
227 unlink(msgfname);
228 unlink(sigfname);
229 rmdir(tmpdir);
230
231 return ret;
232 }
233
234 /* verify cert chain (and message) */
235 static int chain_verify(const char *msgfile, const char *pubkeyfile,
236 const char *pubkeydir, struct list_head *chain) {
237 struct cert_object *cobj;
238 struct blob_attr *containertb[CERT_CT_ATTR_MAX];
239 struct blob_attr *payloadtb[CERT_PL_ATTR_MAX];
240 char tmpdir[] = "/tmp/ucert-XXXXXX";
241 char chainedpubkey[256] = {0};
242 char chainedfp[17] = {0};
243 char extsigfile[256] = {0};
244 int ret = 1;
245 int checkmsg = 0;
246
247 if (mkdtemp(tmpdir) == NULL)
248 return errno;
249
250 if (msgfile)
251 checkmsg = -1;
252
253 list_for_each_entry(cobj, chain, list) {
254 /* blob has payload, verify that using signature */
255 if (cobj->cert[CERT_ATTR_PAYLOAD]) {
256 struct timeval tv;
257 uint64_t validfrom;
258 uint64_t expiresat;
259 uint32_t certtype;
260
261 ret = cert_verify_blob(cobj->cert, chainedpubkey[0]?chainedpubkey:pubkeyfile, pubkeydir);
262 if (ret)
263 goto clean_and_return;
264
265 blobmsg_parse(cert_cont_policy,
266 ARRAY_SIZE(cert_cont_policy),
267 containertb,
268 blob_data(cobj->cert[CERT_ATTR_PAYLOAD]),
269 blob_len(cobj->cert[CERT_ATTR_PAYLOAD]));
270 if (!containertb[CERT_CT_ATTR_PAYLOAD]) {
271 ret = 1;
272 fprintf(stderr, "no ucert in signed payload\n");
273 goto clean_and_return;
274 }
275 blobmsg_parse(cert_payload_policy,
276 ARRAY_SIZE(cert_payload_policy),
277 payloadtb,
278 blobmsg_data(containertb[CERT_CT_ATTR_PAYLOAD]),
279 blobmsg_data_len(containertb[CERT_CT_ATTR_PAYLOAD]));
280
281 if (!payloadtb[CERT_PL_ATTR_CERTTYPE] ||
282 !payloadtb[CERT_PL_ATTR_VALIDFROMTIME] ||
283 !payloadtb[CERT_PL_ATTR_EXPIRETIME] ||
284 !payloadtb[CERT_PL_ATTR_PUBKEY]) {
285 ret = 1;
286 fprintf(stderr, "missing mandatory ucert attributes\n");
287 goto clean_and_return;
288 }
289 certtype = blobmsg_get_u32(payloadtb[CERT_PL_ATTR_CERTTYPE]);
290 validfrom = blobmsg_get_u64(payloadtb[CERT_PL_ATTR_VALIDFROMTIME]);
291 expiresat = blobmsg_get_u64(payloadtb[CERT_PL_ATTR_EXPIRETIME]);
292
293 if (certtype != CERTTYPE_AUTH) {
294 ret = 2;
295 fprintf(stderr, "wrong certificate type\n");
296 goto clean_and_return;
297 }
298
299 gettimeofday(&tv, NULL);
300 if (tv.tv_sec < validfrom ||
301 tv.tv_sec >= expiresat) {
302 ret = 3;
303 fprintf(stderr, "certificate expired\n");
304 goto clean_and_return;
305 }
306
307 snprintf(chainedpubkey, sizeof(chainedpubkey) - 1, "%s/%s", tmpdir, "chained-pubkey");
308 write_file(chainedpubkey,
309 blobmsg_data(payloadtb[CERT_PL_ATTR_PUBKEY]),
310 blobmsg_data_len(payloadtb[CERT_PL_ATTR_PUBKEY]),
311 false);
312
313 if (usign_f_pubkey(chainedfp, chainedpubkey)) {
314 if (!quiet)
315 fprintf(stderr, "cannot get fingerprint for chained key\n");
316 ret = 2;
317 goto clean_and_return;
318 }
319 if (pubkeydir && _usign_key_is_revoked(chainedfp, pubkeydir)) {
320 if (!quiet)
321 fprintf(stderr, "key %s has been revoked!\n", chainedfp);
322 ret = 4;
323 goto clean_and_return;
324 }
325 } else {
326 /* blob doesn't have payload, verify message using signature */
327 if (msgfile) {
328 snprintf(extsigfile, sizeof(extsigfile) - 1, "%s/%s", tmpdir, "ext-sig");
329 write_file(extsigfile,
330 blob_data(cobj->cert[CERT_ATTR_SIGNATURE]),
331 blob_len(cobj->cert[CERT_ATTR_SIGNATURE]),
332 false);
333 checkmsg = ret = usign_v(msgfile,
334 chainedpubkey[0]?chainedpubkey:pubkeyfile,
335 pubkeydir, extsigfile, quiet);
336 unlink(extsigfile);
337 } else {
338 fprintf(stderr, "stray trailing signature without anything to verify!\n");
339 ret = 1;
340 };
341 }
342 }
343
344 if (checkmsg == -1)
345 fprintf(stderr, "missing signature to verify message!\n");
346
347 clean_and_return:
348 if (chainedpubkey[0])
349 unlink(chainedpubkey);
350 rmdir(tmpdir);
351 return ret | checkmsg;
352 }
353
354 /* dump single chain element to console */
355 static void cert_dump_blob(struct blob_attr *cert[CERT_ATTR_MAX]) {
356 int i;
357
358 for (i = 0; i < CERT_ATTR_MAX; i++) {
359 struct blob_attr *v = cert[i];
360
361 if (!v)
362 continue;
363
364 switch(cert_policy[i].type) {
365 case BLOB_ATTR_BINARY:
366 fprintf(stdout, "signature:\n---\n%s---\n", (char *) blob_data(v));
367 break;
368 case BLOB_ATTR_NESTED:
369 fprintf(stdout, "payload:\n---\n%s\n---\n", blobmsg_format_json_indent(blob_data(v), false, 0));
370 break;
371 }
372 }
373 }
374
375 /* dump certfile to console */
376 static int cert_dump(const char *certfile) {
377 struct cert_object *cobj;
378 static LIST_HEAD(certchain);
379 unsigned int count = 0;
380
381 if (cert_load(certfile, &certchain)) {
382 fprintf(stderr, "cannot parse cert\n");
383 return 1;
384 }
385
386 list_for_each_entry(cobj, &certchain, list) {
387 fprintf(stderr, "=== CHAIN ELEMENT %02u ===\n", ++count);
388 cert_dump_blob(cobj->cert);
389 fprintf(stderr, "========================\n");
390 }
391
392 return 0;
393 }
394
395 /* issue an auth certificate for pubkey */
396 static int cert_issue(const char *certfile, const char *pubkeyfile, const char *seckeyfile) {
397 struct blob_buf certbuf;
398 struct blob_buf payloadbuf;
399 struct timeval tv;
400 struct stat st;
401 int pklen, siglen;
402 int revoker = 1;
403 void *c;
404 FILE *pkf, *sigf;
405 char pkb[512];
406 char sigb[512];
407 char fname[256], sfname[256];
408 char pkfp[17];
409 char tmpdir[] = "/tmp/ucert-XXXXXX";
410
411 if (stat(certfile, &st) == 0) {
412 fprintf(stderr, "certfile %s exists, won't overwrite.\n", certfile);
413 return -1;
414 }
415
416 pkf = fopen(pubkeyfile, "r");
417 if (!pkf)
418 return -1;
419
420 pklen = fread(pkb, 1, 512, pkf);
421 pkb[pklen] = '\0';
422
423 if (pklen < 32)
424 return -1;
425
426 fclose(pkf);
427
428 if (usign_f_pubkey(pkfp, pubkeyfile))
429 return -1;
430
431 gettimeofday(&tv, NULL);
432
433 if (mkdtemp(tmpdir) == NULL)
434 return errno;
435
436 while (revoker >= 0) {
437 blob_buf_init(&payloadbuf, 0);
438 c = blobmsg_open_table(&payloadbuf, "ucert");
439 blobmsg_add_u32(&payloadbuf, "certtype", revoker?CERTTYPE_REVOKE:CERTTYPE_AUTH);
440 blobmsg_add_u64(&payloadbuf, "validfrom", tv.tv_sec);
441 if (!revoker) {
442 blobmsg_add_u64(&payloadbuf, "expiresat", tv.tv_sec + 60 * 60 * 24 * 365);
443 blobmsg_add_string(&payloadbuf, "pubkey", pkb);
444 } else {
445 blobmsg_add_string(&payloadbuf, "fingerprint", pkfp);
446 }
447
448 blobmsg_close_table(&payloadbuf, c);
449
450 snprintf(fname, sizeof(fname) - 1, "%s/%s", tmpdir, revoker?"revoker":"payload");
451 write_file(fname, blob_data(payloadbuf.head), blob_len(payloadbuf.head), false);
452
453 snprintf(sfname, sizeof(sfname) - 1, "%s/%s", tmpdir, revoker?"revoker.sig":"payload.sig");
454 if (usign_s(fname, seckeyfile, sfname, quiet))
455 return 1;
456
457 sigf = fopen(sfname, "r");
458 if (!sigf)
459 return 1;
460
461 siglen = fread(sigb, 1, 1024, sigf);
462 if (siglen < 1)
463 return 1;
464
465 sigb[siglen] = '\0';
466 fclose(sigf);
467
468 unlink(fname);
469 unlink(sfname);
470
471 blob_buf_init(&certbuf, 0);
472 blob_put(&certbuf, CERT_ATTR_SIGNATURE, sigb, siglen);
473 blob_put(&certbuf, CERT_ATTR_PAYLOAD, blob_data(payloadbuf.head), blob_len(payloadbuf.head));
474 snprintf(fname, sizeof(fname) - 1, "%s%s", certfile, revoker?".revoke":"");
475 write_file(fname, certbuf.head, blob_raw_len(certbuf.head), false);
476 blob_buf_free(&certbuf);
477 blob_buf_free(&payloadbuf);
478
479 revoker--;
480 }
481
482 rmdir(tmpdir);
483
484 return 0;
485 }
486
487 /* process revoker certificate */
488 static int cert_process_revoker(const char *certfile, const char *pubkeydir) {
489 static LIST_HEAD(certchain);
490 struct cert_object *cobj;
491 struct blob_attr *containertb[CERT_CT_ATTR_MAX];
492 struct blob_attr *payloadtb[CERT_PL_ATTR_MAX];
493 struct stat st;
494 struct timeval tv;
495 uint64_t validfrom;
496 uint32_t certtype;
497 char *fingerprint;
498 char rfname[512];
499
500 int ret;
501
502 if (cert_load(certfile, &certchain)) {
503 fprintf(stderr, "cannot parse cert\n");
504 return 1;
505 }
506
507 list_for_each_entry(cobj, &certchain, list) {
508 /* blob has payload, verify that using signature */
509 if (!cobj->cert[CERT_ATTR_PAYLOAD])
510 return 2;
511 ret = cert_verify_blob(cobj->cert, NULL, pubkeydir);
512 if (ret)
513 return ret;
514
515 blobmsg_parse(cert_cont_policy,
516 ARRAY_SIZE(cert_cont_policy),
517 containertb,
518 blob_data(cobj->cert[CERT_ATTR_PAYLOAD]),
519 blob_len(cobj->cert[CERT_ATTR_PAYLOAD]));
520 if (!containertb[CERT_CT_ATTR_PAYLOAD]) {
521 fprintf(stderr, "no ucert in signed payload\n");
522 return 2;
523 }
524
525 blobmsg_parse(cert_payload_policy,
526 ARRAY_SIZE(cert_payload_policy),
527 payloadtb,
528 blobmsg_data(containertb[CERT_CT_ATTR_PAYLOAD]),
529 blobmsg_data_len(containertb[CERT_CT_ATTR_PAYLOAD]));
530
531 if (!payloadtb[CERT_PL_ATTR_CERTTYPE] ||
532 !payloadtb[CERT_PL_ATTR_VALIDFROMTIME] ||
533 !payloadtb[CERT_PL_ATTR_KEY_FINGERPRINT]) {
534 fprintf(stderr, "missing mandatory ucert attributes\n");
535 return 2;
536 }
537
538 certtype = blobmsg_get_u32(payloadtb[CERT_PL_ATTR_CERTTYPE]);
539 validfrom = blobmsg_get_u64(payloadtb[CERT_PL_ATTR_VALIDFROMTIME]);
540 fingerprint = blobmsg_get_string(payloadtb[CERT_PL_ATTR_KEY_FINGERPRINT]);
541
542 if (certtype != CERTTYPE_REVOKE) {
543 fprintf(stderr, "wrong certificate type\n");
544 return 2;
545 }
546
547 gettimeofday(&tv, NULL);
548 if (tv.tv_sec < validfrom) {
549 return 3;
550 }
551
552 snprintf(rfname, sizeof(rfname)-1, "%s/%s", pubkeydir, fingerprint);
553 /* check if entry in pubkeydir exists */
554 if (stat(rfname, &st) == 0) {
555 if (_usign_key_is_revoked(fingerprint, pubkeydir)) {
556 if (!quiet)
557 fprintf(stdout, "existing revoker deadlink for key %s\n", fingerprint);
558 continue;
559 };
560
561 /* remove any other entry */
562 if (unlink(rfname))
563 return -1;
564 }
565
566 ret = symlink(".revoked.", rfname);
567 if (ret)
568 return ret;
569
570 if (!quiet)
571 fprintf(stdout, "created revoker deadlink for key %s\n", fingerprint);
572 };
573
574 return ret;
575 }
576
577 /* load and verify certfile (and message) */
578 static int cert_verify(const char *certfile, const char *pubkeyfile, const char *pubkeydir, const char *msgfile) {
579 static LIST_HEAD(certchain);
580
581 if (cert_load(certfile, &certchain)) {
582 fprintf(stderr, "cannot parse cert\n");
583 return 1;
584 }
585
586 return chain_verify(msgfile, pubkeyfile, pubkeydir, &certchain);
587 }
588
589 /* output help */
590 static int usage(const char *cmd)
591 {
592 fprintf(stderr,
593 "Usage: %s <command> <options>\n"
594 "Commands:\n"
595 " -A: append signature (needs -c and -x)\n"
596 " -D: dump (needs -c)\n"
597 " -I: issue cert and revoker (needs -c and -p and -s)\n"
598 " -R: process revoker certificate (needs -c and -P)\n"
599 " -V: verify (needs -c and -p|-P, may have -m)\n"
600 "Options:\n"
601 " -c <file>: certificate file\n"
602 " -m <file>: message file (verify only)\n"
603 " -p <file>: public key file\n"
604 " -P <path>: public key directory (verify only)\n"
605 " -q: quiet (do not print verification result, use return code only)\n"
606 " -s <file>: secret key file (issue only)\n"
607 " -x <file>: signature file\n"
608 "\n",
609 cmd);
610 return 1;
611 }
612
613 /* parse command line options and call functions */
614 int main(int argc, char *argv[]) {
615 int ch;
616 const char *msgfile = NULL;
617 const char *sigfile = NULL;
618 const char *pubkeyfile = NULL;
619 const char *pubkeydir = NULL;
620 const char *certfile = NULL;
621 const char *seckeyfile = NULL;
622
623 quiet = false;
624 while ((ch = getopt(argc, argv, "ADIRVc:m:p:P:qs:x:")) != -1) {
625 switch (ch) {
626 case 'A':
627 if (cmd != CMD_NONE)
628 return usage(argv[0]);
629 cmd = CMD_APPEND;
630 break;
631 case 'D':
632 if (cmd != CMD_NONE)
633 return usage(argv[0]);
634 cmd = CMD_DUMP;
635 break;
636 case 'I':
637 if (cmd != CMD_NONE)
638 return usage(argv[0]);
639 cmd = CMD_ISSUE;
640 break;
641 case 'R':
642 if (cmd != CMD_NONE)
643 return usage(argv[0]);
644 cmd = CMD_REVOKE;
645 break;
646 case 'V':
647 if (cmd != CMD_NONE)
648 return usage(argv[0]);
649 cmd = CMD_VERIFY;
650 break;
651 case 'c':
652 if (certfile || cmd == CMD_NONE)
653 return usage(argv[0]);
654 certfile = optarg;
655 break;
656 case 'm':
657 if (msgfile || cmd != CMD_VERIFY)
658 return usage(argv[0]);
659 msgfile = optarg;
660 break;
661 case 'p':
662 if (pubkeyfile || (cmd != CMD_VERIFY && cmd != CMD_ISSUE) || cmd == CMD_NONE)
663 return usage(argv[0]);
664 pubkeyfile = optarg;
665 break;
666 case 'P':
667 if (pubkeydir || (cmd != CMD_VERIFY && cmd != CMD_REVOKE) || cmd == CMD_NONE)
668 return usage(argv[0]);
669 pubkeydir = optarg;
670 break;
671 case 'q':
672 if (quiet || cmd == CMD_NONE)
673 return usage(argv[0]);
674 quiet = true;
675 break;
676 case 's':
677 if (seckeyfile || cmd != CMD_ISSUE || cmd == CMD_NONE)
678 return usage(argv[0]);
679 seckeyfile = optarg;
680 break;
681 case 'x':
682 if (sigfile || cmd != CMD_APPEND || cmd == CMD_NONE)
683 return usage(argv[0]);
684 sigfile = optarg;
685 break;
686 default:
687 return usage(argv[0]);
688 }
689 }
690
691 switch (cmd) {
692 case CMD_APPEND:
693 if (certfile && sigfile)
694 return cert_append(certfile, sigfile);
695 else
696 return usage(argv[0]);
697 case CMD_DUMP:
698 if (certfile)
699 return cert_dump(certfile);
700 else
701 return usage(argv[0]);
702 case CMD_ISSUE:
703 if (certfile && pubkeyfile && seckeyfile)
704 return cert_issue(certfile, pubkeyfile, seckeyfile);
705 else
706 return usage(argv[0]);
707 case CMD_REVOKE:
708 if (certfile && pubkeydir)
709 return cert_process_revoker(certfile, pubkeydir);
710 else
711 return usage(argv[0]);
712 case CMD_VERIFY:
713 if (certfile && (pubkeyfile || pubkeydir))
714 return cert_verify(certfile, pubkeyfile, pubkeydir, msgfile);
715 else
716 return usage(argv[0]);
717 case CMD_NONE:
718 return usage(argv[0]);
719 }
720
721 /* unreachable */
722 return usage(argv[0]);
723 }