1 /* $NetBSD: netpgp.c,v 1.17 2010/11/29 04:20:12 agc Exp $ */
2
3 /*-
4 * Copyright (c) 2009 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Alistair Crooks (agc@NetBSD.org)
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /* Command line program to perform netpgp operations */
33 #include <sys/types.h>
34 #include <sys/param.h>
35 #include <sys/stat.h>
36
37 #include <getopt.h>
38 #include <regex.h>
39 #include <netpgp.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44
45 /*
46 * SHA1 is now looking as though it should not be used. Let's
47 * pre-empt this by specifying SHA256 - gpg interoperates just fine
48 * with SHA256 - agc, 20090522
49 */
50 #define DEFAULT_HASH_ALG "SHA256"
51
52 static const char *usage =
53 " --help OR\n"
54 "\t--encrypt [--output=file] [options] files... OR\n"
55 "\t--decrypt [--output=file] [options] files... OR\n\n"
56 "\t--sign [--armor] [--detach] [--hash=alg] [--output=file]\n"
57 "\t\t[options] files... OR\n"
58 "\t--verify [options] files... OR\n"
59 "\t--cat [--output=file] [options] files... OR\n"
60 "\t--clearsign [--output=file] [options] files... OR\n"
61 "\t--list-packets [options] OR\n"
62 "\t--version\n"
63 "where options are:\n"
64 "\t[--cipher=<ciphername>] AND/OR\n"
65 "\t[--coredumps] AND/OR\n"
66 "\t[--homedir=<homedir>] AND/OR\n"
67 "\t[--keyring=<keyring>] AND/OR\n"
68 "\t[--numtries=<attempts>] AND/OR\n"
69 "\t[--userid=<userid>] AND/OR\n"
70 "\t[--maxmemalloc=<number of bytes>] AND/OR\n"
71 "\t[--verbose]\n";
72
73 enum optdefs {
74 /* commands */
75 ENCRYPT = 260,
76 DECRYPT,
77 SIGN,
78 CLEARSIGN,
79 VERIFY,
80 VERIFY_CAT,
81 LIST_PACKETS,
82 SHOW_KEYS,
83 VERSION_CMD,
84 HELP_CMD,
85
86 /* options */
87 SSHKEYS,
88 KEYRING,
89 USERID,
90 ARMOUR,
91 HOMEDIR,
92 DETACHED,
93 HASH_ALG,
94 OUTPUT,
95 RESULTS,
96 VERBOSE,
97 COREDUMPS,
98 PASSWDFD,
99 SSHKEYFILE,
100 MAX_MEM_ALLOC,
101 DURATION,
102 BIRTHTIME,
103 CIPHER,
104 NUMTRIES,
105
106 /* debug */
107 OPS_DEBUG
108 };
109
110 #define EXIT_ERROR 2
111
112 static struct option options[] = {
113 /* file manipulation commands */
114 {"encrypt", no_argument, NULL, ENCRYPT},
115 {"decrypt", no_argument, NULL, DECRYPT},
116 {"sign", no_argument, NULL, SIGN},
117 {"clearsign", no_argument, NULL, CLEARSIGN},
118 {"verify", no_argument, NULL, VERIFY},
119 {"cat", no_argument, NULL, VERIFY_CAT},
120 {"vericat", no_argument, NULL, VERIFY_CAT},
121 {"verify-cat", no_argument, NULL, VERIFY_CAT},
122 {"verify-show", no_argument, NULL, VERIFY_CAT},
123 {"verifyshow", no_argument, NULL, VERIFY_CAT},
124 /* file listing commands */
125 {"list-packets", no_argument, NULL, LIST_PACKETS},
126 /* debugging commands */
127 {"help", no_argument, NULL, HELP_CMD},
128 {"version", no_argument, NULL, VERSION_CMD},
129 {"debug", required_argument, NULL, OPS_DEBUG},
130 {"show-keys", no_argument, NULL, SHOW_KEYS},
131 {"showkeys", no_argument, NULL, SHOW_KEYS},
132 /* options */
133 {"ssh", no_argument, NULL, SSHKEYS},
134 {"ssh-keys", no_argument, NULL, SSHKEYS},
135 {"sshkeyfile", required_argument, NULL, SSHKEYFILE},
136 {"coredumps", no_argument, NULL, COREDUMPS},
137 {"keyring", required_argument, NULL, KEYRING},
138 {"userid", required_argument, NULL, USERID},
139 {"home", required_argument, NULL, HOMEDIR},
140 {"homedir", required_argument, NULL, HOMEDIR},
141 {"ascii", no_argument, NULL, ARMOUR},
142 {"armor", no_argument, NULL, ARMOUR},
143 {"armour", no_argument, NULL, ARMOUR},
144 {"detach", no_argument, NULL, DETACHED},
145 {"detached", no_argument, NULL, DETACHED},
146 {"hash-alg", required_argument, NULL, HASH_ALG},
147 {"hash", required_argument, NULL, HASH_ALG},
148 {"algorithm", required_argument, NULL, HASH_ALG},
149 {"verbose", no_argument, NULL, VERBOSE},
150 {"pass-fd", required_argument, NULL, PASSWDFD},
151 {"output", required_argument, NULL, OUTPUT},
152 {"results", required_argument, NULL, RESULTS},
153 {"maxmemalloc", required_argument, NULL, MAX_MEM_ALLOC},
154 {"max-mem", required_argument, NULL, MAX_MEM_ALLOC},
155 {"max-alloc", required_argument, NULL, MAX_MEM_ALLOC},
156 {"from", required_argument, NULL, BIRTHTIME},
157 {"birth", required_argument, NULL, BIRTHTIME},
158 {"birthtime", required_argument, NULL, BIRTHTIME},
159 {"creation", required_argument, NULL, BIRTHTIME},
160 {"duration", required_argument, NULL, DURATION},
161 {"expiry", required_argument, NULL, DURATION},
162 {"cipher", required_argument, NULL, CIPHER},
163 {"num-tries", required_argument, NULL, NUMTRIES},
164 {"numtries", required_argument, NULL, NUMTRIES},
165 {"attempts", required_argument, NULL, NUMTRIES},
166 { NULL, 0, NULL, 0},
167 };
168
169 /* gather up program variables into one struct */
170 typedef struct prog_t {
171 char keyring[MAXPATHLEN + 1]; /* name of keyring */
172 char *progname; /* program name */
173 char *output; /* output file name */
174 int overwrite; /* overwrite files? */
175 int armour; /* ASCII armor */
176 int detached; /* use separate file */
177 int cmd; /* netpgp command */
178 } prog_t;
179
180
181 /* print a usage message */
182 static void
print_usage(const char * usagemsg,char * progname)183 print_usage(const char *usagemsg, char *progname)
184 {
185 (void) fprintf(stderr,
186 "%s\nAll bug reports, praise and chocolate, please, to:\n%s\n",
187 netpgp_get_info("version"),
188 netpgp_get_info("maintainer"));
189 (void) fprintf(stderr, "Usage: %s COMMAND OPTIONS:\n%s %s",
190 progname, progname, usagemsg);
191 }
192
193 /* read all of stdin into memory */
194 static int
stdin_to_mem(netpgp_t * netpgp,char ** temp,char ** out,unsigned * maxsize)195 stdin_to_mem(netpgp_t *netpgp, char **temp, char **out, unsigned *maxsize)
196 {
197 unsigned newsize;
198 unsigned size;
199 char buf[BUFSIZ * 8];
200 char *loc;
201 int n;
202
203 *maxsize = (unsigned)atoi(netpgp_getvar(netpgp, "max mem alloc"));
204 size = 0;
205 *temp = NULL;
206 while ((n = read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
207 /* round up the allocation */
208 newsize = size + ((n / BUFSIZ) + 1) * BUFSIZ;
209 if (newsize > *maxsize) {
210 (void) fprintf(stderr, "bounds check\n");
211 return size;
212 }
213 loc = realloc(*temp, newsize);
214 if (loc == NULL) {
215 (void) fprintf(stderr, "short read\n");
216 return size;
217 }
218 *temp = loc;
219 (void) memcpy(&(*temp)[size], buf, n);
220 size += n;
221 }
222 if ((*out = calloc(1, *maxsize)) == NULL) {
223 (void) fprintf(stderr, "Bad alloc\n");
224 return 0;
225 }
226 return (int)size;
227 }
228
229 /* output the text to stdout */
230 static int
show_output(char * out,int size,const char * header)231 show_output(char *out, int size, const char *header)
232 {
233 int cc;
234 int n;
235
236 if (size <= 0) {
237 (void) fprintf(stderr, "%s\n", header);
238 return 0;
239 }
240 for (cc = 0 ; cc < size ; cc += n) {
241 if ((n = write(STDOUT_FILENO, &out[cc], size - cc)) <= 0) {
242 break;
243 }
244 }
245 if (cc < size) {
246 (void) fprintf(stderr, "Short write\n");
247 return 0;
248 }
249 return cc == size;
250 }
251
252 /* do a command once for a specified file 'f' */
253 static int
netpgp_cmd(netpgp_t * netpgp,prog_t * p,char * f)254 netpgp_cmd(netpgp_t *netpgp, prog_t *p, char *f)
255 {
256 const int cleartext = 1;
257 unsigned maxsize;
258 char *out;
259 char *in;
260 int ret;
261 int cc;
262
263 switch (p->cmd) {
264 case ENCRYPT:
265 if (f == NULL) {
266 cc = stdin_to_mem(netpgp, &in, &out, &maxsize);
267 ret = netpgp_encrypt_memory(netpgp,
268 netpgp_getvar(netpgp, "userid"),
269 in, cc, out, maxsize, p->armour);
270 ret = show_output(out, ret, "Bad memory encryption");
271 free(in);
272 free(out);
273 return ret;
274 }
275 return netpgp_encrypt_file(netpgp,
276 netpgp_getvar(netpgp, "userid"),
277 f, p->output,
278 p->armour);
279 case DECRYPT:
280 if (f == NULL) {
281 cc = stdin_to_mem(netpgp, &in, &out, &maxsize);
282 ret = netpgp_decrypt_memory(netpgp, in, cc, out,
283 maxsize, 0);
284 ret = show_output(out, ret, "Bad memory decryption");
285 free(in);
286 free(out);
287 return ret;
288 }
289 return netpgp_decrypt_file(netpgp, f, p->output, p->armour);
290 case CLEARSIGN:
291 case SIGN:
292 if (f == NULL) {
293 cc = stdin_to_mem(netpgp, &in, &out, &maxsize);
294 ret = netpgp_sign_memory(netpgp,
295 netpgp_getvar(netpgp, "userid"),
296 in, cc, out,
297 maxsize, p->armour,
298 (p->cmd == CLEARSIGN) ? cleartext :
299 !cleartext);
300 ret = show_output(out, ret, "Bad memory signature");
301 free(in);
302 free(out);
303 return ret;
304 }
305 return netpgp_sign_file(netpgp,
306 netpgp_getvar(netpgp, "userid"),
307 f, p->output,
308 p->armour,
309 (p->cmd == CLEARSIGN) ? cleartext :
310 !cleartext,
311 p->detached);
312 case VERIFY:
313 case VERIFY_CAT:
314 if (f == NULL) {
315 cc = stdin_to_mem(netpgp, &in, &out, &maxsize);
316 ret = netpgp_verify_memory(netpgp, in, cc,
317 (p->cmd == VERIFY_CAT) ? out : NULL,
318 (p->cmd == VERIFY_CAT) ? maxsize : 0,
319 p->armour);
320 ret = show_output(out, ret, "Bad memory verification");
321 free(in);
322 free(out);
323 return ret;
324 }
325 return netpgp_verify_file(netpgp, f,
326 (p->cmd == VERIFY) ? NULL :
327 (p->output) ? p->output : "-",
328 p->armour);
329 case LIST_PACKETS:
330 if (f == NULL) {
331 (void) fprintf(stderr, "%s: No filename provided\n",
332 p->progname);
333 return 0;
334 }
335 return netpgp_list_packets(netpgp, f, p->armour, NULL);
336 case SHOW_KEYS:
337 return netpgp_validate_sigs(netpgp);
338 case HELP_CMD:
339 default:
340 print_usage(usage, p->progname);
341 exit(EXIT_SUCCESS);
342 }
343 }
344
345 /* set an option */
346 static int
setoption(netpgp_t * netpgp,prog_t * p,int val,char * arg,int * homeset)347 setoption(netpgp_t *netpgp, prog_t *p, int val, char *arg, int *homeset)
348 {
349 switch (val) {
350 case COREDUMPS:
351 netpgp_setvar(netpgp, "coredumps", "allowed");
352 break;
353 case ENCRYPT:
354 /* for encryption, we need a userid */
355 netpgp_setvar(netpgp, "need userid", "1");
356 p->cmd = val;
357 break;
358 case SIGN:
359 case CLEARSIGN:
360 /* for signing, we need a userid and a seckey */
361 netpgp_setvar(netpgp, "need seckey", "1");
362 netpgp_setvar(netpgp, "need userid", "1");
363 p->cmd = val;
364 break;
365 case DECRYPT:
366 /* for decryption, we need a seckey */
367 netpgp_setvar(netpgp, "need seckey", "1");
368 p->cmd = val;
369 break;
370 case VERIFY:
371 case VERIFY_CAT:
372 case LIST_PACKETS:
373 case SHOW_KEYS:
374 case HELP_CMD:
375 p->cmd = val;
376 break;
377 case VERSION_CMD:
378 printf(
379 "%s\nAll bug reports, praise and chocolate, please, to:\n%s\n",
380 netpgp_get_info("version"),
381 netpgp_get_info("maintainer"));
382 exit(EXIT_SUCCESS);
383 /* options */
384 case SSHKEYS:
385 netpgp_setvar(netpgp, "ssh keys", "1");
386 break;
387 case KEYRING:
388 if (arg == NULL) {
389 (void) fprintf(stderr,
390 "No keyring argument provided\n");
391 exit(EXIT_ERROR);
392 }
393 snprintf(p->keyring, sizeof(p->keyring), "%s", arg);
394 break;
395 case USERID:
396 if (arg == NULL) {
397 (void) fprintf(stderr,
398 "No userid argument provided\n");
399 exit(EXIT_ERROR);
400 }
401 netpgp_setvar(netpgp, "userid", arg);
402 break;
403 case ARMOUR:
404 p->armour = 1;
405 break;
406 case DETACHED:
407 p->detached = 1;
408 break;
409 case VERBOSE:
410 netpgp_incvar(netpgp, "verbose", 1);
411 break;
412 case HOMEDIR:
413 if (arg == NULL) {
414 (void) fprintf(stderr,
415 "No home directory argument provided\n");
416 exit(EXIT_ERROR);
417 }
418 netpgp_set_homedir(netpgp, arg, NULL, 0);
419 *homeset = 1;
420 break;
421 case HASH_ALG:
422 if (arg == NULL) {
423 (void) fprintf(stderr,
424 "No hash algorithm argument provided\n");
425 exit(EXIT_ERROR);
426 }
427 netpgp_setvar(netpgp, "hash", arg);
428 break;
429 case PASSWDFD:
430 if (arg == NULL) {
431 (void) fprintf(stderr,
432 "No pass-fd argument provided\n");
433 exit(EXIT_ERROR);
434 }
435 netpgp_setvar(netpgp, "pass-fd", arg);
436 break;
437 case OUTPUT:
438 if (arg == NULL) {
439 (void) fprintf(stderr,
440 "No output filename argument provided\n");
441 exit(EXIT_ERROR);
442 }
443 if (p->output) {
444 (void) free(p->output);
445 }
446 p->output = strdup(arg);
447 break;
448 case RESULTS:
449 if (arg == NULL) {
450 (void) fprintf(stderr,
451 "No output filename argument provided\n");
452 exit(EXIT_ERROR);
453 }
454 netpgp_setvar(netpgp, "results", arg);
455 break;
456 case SSHKEYFILE:
457 netpgp_setvar(netpgp, "ssh keys", "1");
458 netpgp_setvar(netpgp, "sshkeyfile", arg);
459 break;
460 case MAX_MEM_ALLOC:
461 netpgp_setvar(netpgp, "max mem alloc", arg);
462 break;
463 case DURATION:
464 netpgp_setvar(netpgp, "duration", arg);
465 break;
466 case BIRTHTIME:
467 netpgp_setvar(netpgp, "birthtime", arg);
468 break;
469 case CIPHER:
470 netpgp_setvar(netpgp, "cipher", arg);
471 break;
472 case NUMTRIES:
473 netpgp_setvar(netpgp, "numtries", arg);
474 break;
475 case OPS_DEBUG:
476 netpgp_set_debug(arg);
477 break;
478 default:
479 p->cmd = HELP_CMD;
480 break;
481 }
482 return 1;
483 }
484
485 /* we have -o option=value -- parse, and process */
486 static int
parse_option(netpgp_t * netpgp,prog_t * p,const char * s,int * homeset)487 parse_option(netpgp_t *netpgp, prog_t *p, const char *s, int *homeset)
488 {
489 static regex_t opt;
490 struct option *op;
491 static int compiled;
492 regmatch_t matches[10];
493 char option[128];
494 char value[128];
495
496 if (!compiled) {
497 compiled = 1;
498 (void) regcomp(&opt, "([^=]{1,128})(=(.*))?", REG_EXTENDED);
499 }
500 if (regexec(&opt, s, 10, matches, 0) == 0) {
501 (void) snprintf(option, sizeof(option), "%.*s",
502 (int)(matches[1].rm_eo - matches[1].rm_so), &s[matches[1].rm_so]);
503 if (matches[2].rm_so > 0) {
504 (void) snprintf(value, sizeof(value), "%.*s",
505 (int)(matches[3].rm_eo - matches[3].rm_so), &s[matches[3].rm_so]);
506 } else {
507 value[0] = 0x0;
508 }
509 for (op = options ; op->name ; op++) {
510 if (strcmp(op->name, option) == 0) {
511 return setoption(netpgp, p, op->val, value, homeset);
512 }
513 }
514 }
515 return 0;
516 }
517
518 int
main(int argc,char ** argv)519 main(int argc, char **argv)
520 {
521 netpgp_t netpgp;
522 prog_t p;
523 int homeset;
524 int optindex;
525 int ret;
526 int ch;
527 int i;
528
529 (void) memset(&p, 0x0, sizeof(p));
530 (void) memset(&netpgp, 0x0, sizeof(netpgp));
531 p.progname = argv[0];
532 p.overwrite = 1;
533 p.output = NULL;
534 if (argc < 2) {
535 print_usage(usage, p.progname);
536 exit(EXIT_ERROR);
537 }
538 /* set some defaults */
539 netpgp_setvar(&netpgp, "hash", DEFAULT_HASH_ALG);
540 /* 4 MiB for a memory file */
541 netpgp_setvar(&netpgp, "max mem alloc", "4194304");
542 homeset = 0;
543 optindex = 0;
544 while ((ch = getopt_long(argc, argv, "S:Vdeo:sv", options, &optindex)) != -1) {
545 if (ch >= ENCRYPT) {
546 /* getopt_long returns 0 for long options */
547 if (!setoption(&netpgp, &p, options[optindex].val, optarg, &homeset)) {
548 (void) fprintf(stderr, "Bad option\n");
549 }
550 } else {
551 switch (ch) {
552 case 'S':
553 netpgp_setvar(&netpgp, "ssh keys", "1");
554 netpgp_setvar(&netpgp, "sshkeyfile", optarg);
555 break;
556 case 'V':
557 printf(
558 "%s\nAll bug reports, praise and chocolate, please, to:\n%s\n",
559 netpgp_get_info("version"),
560 netpgp_get_info("maintainer"));
561 exit(EXIT_SUCCESS);
562 case 'd':
563 /* for decryption, we need the seckey */
564 netpgp_setvar(&netpgp, "need seckey", "1");
565 p.cmd = DECRYPT;
566 break;
567 case 'e':
568 /* for encryption, we need a userid */
569 netpgp_setvar(&netpgp, "need userid", "1");
570 p.cmd = ENCRYPT;
571 break;
572 case 'o':
573 if (!parse_option(&netpgp, &p, optarg, &homeset)) {
574 (void) fprintf(stderr, "Bad option\n");
575 }
576 break;
577 case 's':
578 /* for signing, we need a userid and a seckey */
579 netpgp_setvar(&netpgp, "need seckey", "1");
580 netpgp_setvar(&netpgp, "need userid", "1");
581 p.cmd = SIGN;
582 break;
583 case 'v':
584 p.cmd = VERIFY;
585 break;
586 default:
587 p.cmd = HELP_CMD;
588 break;
589 }
590 }
591 }
592 if (!homeset) {
593 netpgp_set_homedir(&netpgp, getenv("HOME"),
594 netpgp_getvar(&netpgp, "ssh keys") ? "/.ssh" : "/.gnupg", 1);
595 }
596 /* initialise, and read keys from file */
597 if (!netpgp_init(&netpgp)) {
598 printf("can't initialise\n");
599 exit(EXIT_ERROR);
600 }
601 /* now do the required action for each of the command line args */
602 ret = EXIT_SUCCESS;
603 if (optind == argc) {
604 if (!netpgp_cmd(&netpgp, &p, NULL)) {
605 ret = EXIT_FAILURE;
606 }
607 } else {
608 for (i = optind; i < argc; i++) {
609 if (!netpgp_cmd(&netpgp, &p, argv[i])) {
610 ret = EXIT_FAILURE;
611 }
612 }
613 }
614 netpgp_end(&netpgp);
615 exit(ret);
616 }
617