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