xref: /illumos-gate/usr/src/cmd/cmd-crypto/pktool/pktool.c (revision cb6207858a9fcc2feaee22e626912fba281ac969)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * This file comprises the main driver for this tool.
30  * Upon parsing the command verbs from user input, it
31  * branches to the appropriate modules to perform the
32  * requested task.
33  */
34 
35 #include <stdio.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <malloc.h>
39 #include <libgen.h>
40 #include <errno.h>
41 #include <cryptoutil.h>
42 #include <security/cryptoki.h>
43 #include "common.h"
44 
45 /*
46  * The verbcmd construct allows genericizing information about a verb so
47  * that it is easier to manipulate.  Makes parsing code easier to read,
48  * fix, and extend with new verbs.
49  */
50 typedef struct verbcmd_s {
51 	char	*verb;
52 	int	(*action)(int, char *[]);
53 	int	mode;
54 	char	*summary;
55 	char	*synopsis;
56 } verbcmd;
57 
58 /* External declarations for supported verb actions. */
59 extern int	pk_setpin(int argc, char *argv[]);
60 extern int	pk_list(int argc, char *argv[]);
61 extern int	pk_delete(int argc, char *argv[]);
62 extern int	pk_import(int argc, char *argv[]);
63 extern int	pk_export(int argc, char *argv[]);
64 extern int	pk_tokens(int argc, char *argv[]);
65 extern int	pk_gencert(int argc, char *argv[]);
66 extern int	pk_gencsr(int argc, char *argv[]);
67 extern int	pk_download(int argc, char *argv[]);
68 extern int	pk_genkey(int argc, char *argv[]);
69 
70 /* Forward declarations for "built-in" verb actions. */
71 static int	pk_help(int argc, char *argv[]);
72 
73 /* Command structure for verbs and their actions.  Do NOT i18n/l10n. */
74 static verbcmd	cmds[] = {
75 	{ "tokens",	pk_tokens,	0,
76 		"lists all visible PKCS#11 tokens", "tokens" },
77 	{ "setpin",	pk_setpin,	0,
78 		"changes user authentication passphrase for keystore access",
79 		"setpin [ keystore=pkcs11 ]\n\t\t"
80 		"[ token=token[:manuf[:serial]]]\n\t"
81 
82 		"setpin keystore=nss\n\t\t"
83 		"[ token=token ]\n\t\t"
84 		"[ dir=directory-path ]\n\t\t"
85 		"[ prefix=DBprefix ]\n\t"
86 	},
87 	{ "list",	pk_list,	0,
88 		"lists a summary of objects in the keystore",
89 	"list [ token=token[:manuf[:serial]]]\n\t\t"
90 		"[ objtype=private|public|both ]\n\t\t"
91 		"[ label=label ]\n\t"
92 
93 	"list objtype=cert[:[public | private | both ]]\n\t\t"
94 		"[ subject=subject-DN ]\n\t\t"
95 		"[ keystore=pkcs11 ]\n\t\t"
96 		"[ issuer=issuer-DN ]\n\t\t"
97 		"[ serial=serial number]\n\t\t"
98 		"[ label=cert-label ]\n\t\t"
99 		"[ token=token[:manuf[:serial]]]\n\t\t"
100 		"[ criteria=valid|expired|both ]\n\t"
101 
102 	"list objtype=key[:[public | private | both ]]\n\t\t"
103 		"[ keystore=pkcs11 ]\n\t\t"
104 		"[ subject=subject-DN ]\n\t\t"
105 		"[ label=key-label ]\n\t\t"
106 		"[ token=token[:manuf[:serial]]]\n\t"
107 
108 	"list keystore=pkcs11 objtype=crl\n\t\t"
109 		"infile=crl-fn\n\t\t"
110 		"[ dir=directory-path ]\n\t"
111 
112 	"list keystore=nss objtype=cert\n\t\t"
113 		"[ subject=subject-DN ]\n\t\t"
114 		"[ issuer=issuer-DN ]\n\t\t"
115 		"[ serial=serial number]\n\t\t"
116 		"[ nickname=cert-nickname ]\n\t\t"
117 		"[ token=token[:manuf[:serial]]]\n\t\t"
118 		"[ dir=directory-path ]\n\t\t"
119 		"[ prefix=DBprefix ]\n\t\t"
120 		"[ criteria=valid|expired|both ]\n\t"
121 
122 	"list keystore=nss objtype=key\n\t\t"
123 		"[ token=token[:manuf[:serial]]]\n\t\t"
124 		"[ dir=directory-path ]\n\t\t"
125 		"[ prefix=DBprefix ]\n\t\t"
126 		"[ nickname=key-nickname ]\n\t"
127 
128 	"list keystore=file objtype=cert\n\t\t"
129 		"[ subject=subject-DN ]\n\t\t"
130 		"[ issuer=issuer-DN ]\n\t\t"
131 		"[ serial=serial number]\n\t\t"
132 		"[ infile=cert-fn ]\n\t\t"
133 		"[ dir=directory-path ]\n\t\t"
134 		"[ criteria=valid|expired|both ]\n\t"
135 
136 	"list keystore=file objtype=key\n\t\t"
137 		"[ infile=key-fn ]\n\t\t"
138 		"[ dir=directory-path ]\n\t"
139 
140 	"list keystore=file objtype=crl\n\t\t"
141 		"infile=crl-fn\n\t\t"
142 		"[ dir=directory-path ]\n\t"
143 	},
144 
145 	{ "delete",	pk_delete,	0,
146 		"deletes objects in the keystore",
147 
148 	"delete [ token=token[:manuf[:serial]]]\n\t\t"
149 		"[ objtype=private|public|both ]\n\t\t"
150 		"[ label=object-label ]\n\t"
151 
152 	"delete keystore=nss objtype=cert\n\t\t"
153 		"[ subject=subject-DN ]\n\t\t"
154 		"[ issuer=issuer-DN ]\n\t\t"
155 		"[ serial=serial number]\n\t\t"
156 		"[ nickname=cert-nickname ]\n\t\t"
157 		"[ token=token[:manuf[:serial]]]\n\t\t"
158 		"[ dir=directory-path ]\n\t\t"
159 		"[ prefix=DBprefix ]\n\t\t"
160 		"[ criteria=valid|expired|both ]\n\t"
161 
162 	"delete keystore=nss objtype=key\n\t\t"
163 		"[ token=token[:manuf[:serial]]]\n\t\t"
164 		"[ dir=directory-path ]\n\t\t"
165 		"[ prefix=DBprefix ]\n\t\t"
166 		"[ nickname=key-nickname ]\n\t\t"
167 
168 	"delete keystore=nss objtype=crl\n\t\t"
169 		"[ nickname=issuer-nickname ]\n\t\t"
170 		"[ subject=subject-DN ]\n\t\t"
171 		"[ token=token[:manuf[:serial]]]\n\t\t"
172 		"[ dir=directory-path ]\n\t\t"
173 		"[ prefix=DBprefix ]\n\t"
174 
175 	"delete keystore=pkcs11 objtype=cert[:[public | private | both]]\n\t\t"
176 		"[ subject=subject-DN ]\n\t\t"
177 		"[ issuer=issuer-DN ]\n\t\t"
178 		"[ serial=serial number]\n\t\t"
179 		"[ label=cert-label ]\n\t\t"
180 		"[ token=token[:manuf[:serial]]]\n\t\t"
181 		"[ criteria=valid|expired|both ]\n\t"
182 
183 	"delete keystore=pkcs11 objtype=key[:[public | private | both]]\n\t\t"
184 		"[ subject=subject-DN ]\n\t\t"
185 		"[ label=key-label ]\n\t\t"
186 		"[ token=token[:manuf[:serial]]]\n\t"
187 
188 	"delete keystore=pkcs11 objtype=crl\n\t\t"
189 		"infile=crl-fn\n\t\t"
190 		"[ dir=directory-path ]\n\t"
191 
192 	"delete keystore=file objtype=cert\n\t\t"
193 		"[ subject=subject-DN ]\n\t\t"
194 		"[ issuer=issuer-DN ]\n\t\t"
195 		"[ serial=serial number]\n\t\t"
196 		"[ infile=cert-fn ]\n\t\t"
197 		"[ dir=directory-path ]\n\t\t"
198 		"[ criteria=valid|expired|both ]\n\t"
199 
200 	"delete keystore=file objtype=key\n\t\t"
201 		"[ infile=key-fn ]\n\t\t"
202 		"[ dir=directory-path ]\n\t"
203 
204 	"delete keystore=file objtype=crl\n\t\t"
205 		"infile=crl-fn\n\t\t"
206 		"[ dir=directory-path ]\n\t"
207 	},
208 	{ "import",	pk_import,	0,
209 		"imports objects from an external source",
210 
211 	"import [token=token[:manuf[:serial]]]\n\t\t"
212 	"infile=input-fn\n\t"
213 
214 	"import keystore=nss objtype=cert\n\t\t"
215 		"infile=input-fn\n\t\t"
216 		"nickname=cert-nickname\n\t\t"
217 		"[ trust=trust-value ]\n\t\t"
218 		"[ token=token[:manuf[:serial]]]\n\t\t"
219 		"[ dir=directory-path ]\n\t\t"
220 		"[ prefix=DBprefix ]\n\t"
221 
222 	"import keystore=nss objtype=crl\n\t\t"
223 		"infile=input-fn\n\t\t"
224 		"[ verifycrl=y|n ]\n\t\t"
225 		"[ token=token[:manuf[:serial]]]\n\t\t"
226 		"[ dir=directory-path ]\n\t\t"
227 		"[ prefix=DBprefix ]\n\t"
228 
229 	"import keystore=pkcs11\n\t\t"
230 		"infile=input-fn\n\t\t"
231 		"label=cert-label\n\t\t"
232 		"[ token=token[:manuf[:serial]]]\n\t"
233 
234 	"import keystore=pkcs11 objtype=crl\n\t\t"
235 		"infile=input-crl-fn\n\t\t"
236 		"outcrl=output-crl-fn\n\t\t"
237 		"outformat=pem|der\n\t\t"
238 		"[ dir=output-crl-directory-path ]\n\t"
239 
240 	"import keystore=file\n\t\t"
241 		"infile=input-fn\n\t\t"
242 		"outkey=output-key-fn\n\t\t"
243 		"outcert=output-cert-fn\n\t\t"
244 		"[ dir=output-cert-dir-path ]\n\t\t"
245 		"[ keydir=output-key-dir-path ]\n\t\t"
246 		"[ outformat=pem|der|pkcs12 ]\n\t"
247 
248 	"import keystore=file objtype=crl\n\t\t"
249 		"infile=input-crl-fn\n\t\t"
250 		"outcrl=output-crl-fn\n\t\t"
251 		"outformat=pem|der\n\t\t"
252 		"[ dir=output-crl-directory-path ]\n\t"
253 	},
254 
255 	{ "export",	pk_export,	0,
256 		"exports objects from the keystore to a file",
257 
258 	"export [token=token[:manuf[:serial]]]\n\t\t"
259 	"outfile=output-fn\n\t"
260 
261 	"export keystore=nss\n\t\t"
262 		"outfile=output-fn\n\t\t"
263 		"[ objtype=cert|key ]\n\t\t"
264 		"[ subject=subject-DN ]\n\t\t"
265 		"[ issuer=issuer-DN ]\n\t\t"
266 		"[ serial=serial number]\n\t\t"
267 		"[ nickname=cert-nickname]\n\t\t"
268 		"[ token=token[:manuf[:serial]]]\n\t\t"
269 		"[ dir=directory-path ]\n\t\t"
270 		"[ prefix=DBPrefix ]\n\t\t"
271 		"[ outformat=pem|der|pkcs12 ]\n\t"
272 
273 	"export keystore=pkcs11\n\t\t"
274 		"outfile=output-fn\n\t\t"
275 		"[ label=cert-label]\n\t\t"
276 		"[ subject=subject-DN ]\n\t\t"
277 		"[ issuer=issuer-DN ]\n\t\t"
278 		"[ serial=serial number]\n\t\t"
279 		"[ outformat=pem|der|pkcs12]\n\t\t"
280 		"[ token=token[:manuf[:serial]]]\n\t"
281 
282 	"export keystore=file\n\t\t"
283 		"certfile=cert-input-fn\n\t\t"
284 		"keyfile=key-input-fn\n\t\t"
285 		"outfile=output-pkcs12-fn\n\t\t"
286 		"[ dir=directory-path ]\n\t"
287 	},
288 
289 	{ "gencert",	pk_gencert,	0,
290 		"creates a self-signed X.509v3 certificate",
291 
292 	"gencert [-i] keystore=nss\n\t\t"
293 		"label=cert-nickname\n\t\t"
294 		"serial=serial number hex string]\n\t\t"
295 		"subject=subject-DN\n\t\t"
296 		"[ altname=[critical:]SubjectAltName ]\n\t\t"
297 		"[ keyusage=[critical:]usage,usage,...]\n\t\t"
298 		"[ token=token[:manuf[:serial]]]\n\t\t"
299 		"[ dir=directory-path ]\n\t\t"
300 		"[ prefix=DBprefix ]\n\t\t"
301 		"[ keytype=rsa|dsa ]\n\t\t"
302 		"[ keylen=key-size ]\n\t\t"
303 		"[ trust=trust-value ]\n\t\t"
304 		"[ lifetime=number-hour|number-day|number-year ]\n\t"
305 
306 	"gencert [-i] [ keystore=pkcs11 ]\n\t\t"
307 		"label=key/cert-label\n\t\t"
308 		"subject=subject-DN\n\t\t"
309 		"serial=serial number hex string\n\t\t"
310 		"[ altname=[critical:]SubjectAltName ]\n\t\t"
311 		"[ keyusage=[critical:]usage,usage,...]\n\t\t"
312 		"[ token=token[:manuf[:serial]]]\n\t\t"
313 		"[ keytype=rsa|dsa ]\n\t\t"
314 		"[ keylen=key-size ]\n\t\t"
315 		"[ lifetime=number-hour|number-day|number-year ]\n\t"
316 
317 	"gencert [-i] keystore=file\n\t\t"
318 		"outcert=cert_filename\n\t\t"
319 		"outkey=key_filename\n\t\t"
320 		"subject=subject-DN\n\t\t"
321 		"serial=serial number hex string\n\t\t"
322 		"[ altname=[critical:]SubjectAltName ]\n\t\t"
323 		"[ keyusage=[critical:]usage,usage,...]\n\t\t"
324 		"[ format=der|pem ]\n\t\t"
325 		"[ dir=directory-path ]\n\t\t"
326 		"[ prefix=DBprefix ]\n\t\t"
327 		"[ keytype=rsa|dsa ]\n\t\t"
328 		"[ keylen=key-size ]\n\t\t"
329 		"[ lifetime=number-hour|number-day|number-year ]\n\t"
330 	},
331 	{ "gencsr",	pk_gencsr,	0,
332 		"creates a PKCS#10 certificate signing request file",
333 	"gencsr [-i] keystore=nss \n\t\t"
334 		"nickname=cert-nickname\n\t\t"
335 		"outcsr=csr-fn\n\t\t"
336 		"subject=subject-DN\n\t\t"
337 		"[ altname=[critical:]SubjectAltName ]\n\t\t"
338 		"[ keyusage=[critical:]usage,usage,...]\n\t\t"
339 		"[ token=token[:manuf[:serial]]]\n\t\t"
340 		"[ dir=directory-path ]\n\t\t"
341 		"[ prefix=DBprefix ]\n\t\t"
342 		"[ keytype=rsa|dsa ]\n\t\t"
343 		"[ keylen=key-size ]\n\t\t"
344 		"[ format=pem|der]\n\t"
345 	"gencsr [-i] [ keystore=pkcs11 ]\n\t\t"
346 		"label=key-label\n\t\t"
347 		"outcsr=csr-fn\n\t\t"
348 		"subject=subject-DN\n\t\t"
349 		"[ altname=[critical:]SubjectAltName ]\n\t\t"
350 		"[ keyusage=[critical:]usage,usage,...]\n\t\t"
351 		"[ token=token[:manuf[:serial]]]\n\t\t"
352 		"[ keytype=rsa|dsa ]\n\t\t"
353 		"[ keylen=key-size ]\n\t\t"
354 		"[ format=pem|der]\n\t"
355 	"gencsr [-i] keystore=file\n\t\t"
356 		"outcsr=csr-fn\n\t\t"
357 		"outkey=key-fn\n\t\t"
358 		"subject=subject-DN\n\t\t"
359 		"[ altname=[critical:]SubjectAltName ]\n\t\t"
360 		"[ keyusage=[critical:]usage,usage,...]\n\t\t"
361 		"[ keytype=rsa|dsa ]\n\t\t"
362 		"[ keylen=key-size ]\n\t\t"
363 		"[ dir=directory-path ]\n\t\t"
364 		"[ format=pem|der]\n\t"
365 	},
366 
367 	{ "download",	pk_download,	0,
368 		"downloads a CRL or certificate file from an external source",
369 
370 	"download url=url_str\n\t\t"
371 		"[ objtype=crl|cert ]\n\t\t"
372 		"[ http_proxy=proxy_str ]\n\t\t"
373 		"[ outfile = outfile ]\n\t\t"
374 	},
375 
376 	{ "genkey",	pk_genkey,	0,
377 		"creates a symmetric key in the keystore",
378 
379 	"genkey [ keystore=pkcs11 ]\n\t\t"
380 		"label=key-label\n\t\t"
381 		"[ keytype=aes|arcfour|des|3des|generic ]\n\t\t"
382 		"[ keylen=key-size (AES, ARCFOUR or GENERIC only)]\n\t\t"
383 		"[ token=token[:manuf[:serial]]]\n\t\t"
384 		"[ sensitive=y|n ]\n\t\t"
385 		"[ extractable=y|n ]\n\t\t"
386 		"[ print=y|n ]\n\t"
387 
388 	"genkey keystore=nss\n\t\t"
389 		"label=key-label\n\t\t"
390 		"[ keytype=aes|arcfour|des|3des|generic ]\n\t\t"
391 		"[ keylen=key-size (AES, ARCFOUR or GENERIC only)]\n\t\t"
392 		"[ token=token[:manuf[:serial]]]\n\t\t"
393 		"[ dir=directory-path ]\n\t\t"
394 		"[ prefix=DBprefix ]\n\t"
395 
396 	"genkey keystore=file\n\t\t"
397 		"outkey=key-fn\n\t\t"
398 		"[ keytype=aes|arcfour|des|3des|generic ]\n\t\t"
399 		"[ keylen=key-size (AES, ARCFOUR or GENERIC only)]\n\t\t"
400 		"[ dir=directory-path ]\n\t\t"
401 		"[ print=y|n ]\n\t"
402 	},
403 
404 	{ "help",	pk_help,	0,
405 		"displays help message",
406 		"help\t(help and usage)" }
407 };
408 
409 static int	num_cmds = sizeof (cmds) / sizeof (verbcmd);
410 
411 static char	*prog;
412 static void	usage(int);
413 
414 /*
415  * Usage information.  This function must be updated when new verbs or
416  * options are added.
417  */
418 static void
419 usage(int idx)
420 {
421 	int	i;
422 
423 	/* Display this block only in command-line mode. */
424 	(void) fprintf(stdout, gettext("Usage:\n"));
425 	(void) fprintf(stdout, gettext("   %s -?\t(help and usage)\n"),
426 		prog);
427 	(void) fprintf(stdout, gettext("   %s -f option_file\n"), prog);
428 	(void) fprintf(stdout, gettext("   %s subcommand [options...]\n"),
429 		prog);
430 	(void) fprintf(stdout, gettext("where subcommands may be:\n"));
431 
432 	/* Display only those verbs that match the current tool mode. */
433 	if (idx == -1) {
434 		for (i = 0; i < num_cmds; i++) {
435 			/* Do NOT i18n/l10n. */
436 			(void) fprintf(stdout, "   %-8s	- %s\n",
437 				cmds[i].verb, cmds[i].summary);
438 		}
439 		(void) fprintf(stdout, gettext("\nFurther details on the "
440 			"subcommands can be found by adding \'help\'.\n"
441 			"Ex: pktool gencert help\n\n"));
442 	} else {
443 		(void) fprintf(stdout, "\t%s\n", cmds[idx].synopsis);
444 	}
445 }
446 
447 /*
448  * Provide help, in the form of displaying the usage.
449  */
450 static int
451 pk_help(int argc, char *argv[])
452 /* ARGSUSED */
453 {
454 	usage(-1);
455 	return (0);
456 }
457 
458 /*
459  * Process arguments from the argfile and create a new
460  * argv/argc list to be processed later.
461  */
462 static int
463 process_arg_file(char *argfile, char ***argv, int *argc)
464 {
465 	FILE *fp;
466 	char argline[2 * BUFSIZ]; /* 2048 bytes should be plenty */
467 	char *p;
468 	int nargs = 0;
469 
470 	if ((fp = fopen(argfile, "rF")) == NULL) {
471 		(void) fprintf(stderr,
472 			gettext("Cannot read argfile %s: %s\n"),
473 			argfile, strerror(errno));
474 		return (errno);
475 	}
476 
477 	while (fgets(argline, sizeof (argline), fp) != NULL) {
478 		int j;
479 		/* remove trailing whitespace */
480 		j = strlen(argline) - 1;
481 		while (j >= 0 && isspace(argline[j])) {
482 			argline[j] = 0;
483 			j--;
484 		}
485 		/* If it was a blank line, get the next one. */
486 		if (!strlen(argline))
487 			continue;
488 
489 		(*argv) = realloc((*argv),
490 			(nargs + 1) * sizeof (char *));
491 		if ((*argv) == NULL) {
492 			perror("memory error");
493 			(void) fclose(fp);
494 			return (errno);
495 		}
496 		p = (char *)strdup(argline);
497 		if (p == NULL) {
498 			perror("memory error");
499 			(void) fclose(fp);
500 			return (errno);
501 		}
502 		(*argv)[nargs] = p;
503 		nargs++;
504 	}
505 	*argc = nargs;
506 	(void) fclose(fp);
507 	return (0);
508 }
509 
510 /*
511  * MAIN() -- where all the action is
512  */
513 int
514 main(int argc, char *argv[], char *envp[])
515 /* ARGSUSED2 */
516 {
517 	int	i, found = -1;
518 	int	rv;
519 	int	pk_argc = 0;
520 	char	**pk_argv = NULL;
521 	int	save_errno = 0;
522 
523 	/* Set up for i18n/l10n. */
524 	(void) setlocale(LC_ALL, "");
525 #if !defined(TEXT_DOMAIN)		/* Should be defined by cc -D. */
526 #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it isn't. */
527 #endif
528 	(void) textdomain(TEXT_DOMAIN);
529 
530 	/* Get program base name and move pointer over 0th arg. */
531 	prog = basename(argv[0]);
532 	argv++, argc--;
533 
534 	/* Set up for debug and error output. */
535 	if (argc == 0) {
536 		usage(-1);
537 		return (1);
538 	}
539 
540 	/* Check for help options.  For CLIP-compliance. */
541 	if (strcmp(argv[0], "-?") == 0) {
542 		return (pk_help(argc, argv));
543 	} else if (strcmp(argv[0], "-f") == 0 && argc == 2) {
544 		rv = process_arg_file(argv[1], &pk_argv, &pk_argc);
545 		if (rv)
546 			return (rv);
547 	} else if (argc >= 1 && argv[0][0] == '-') {
548 		usage(-1);
549 		return (1);
550 	}
551 
552 	/* Always turns off Metaslot so that we can see softtoken. */
553 	if (setenv("METASLOT_ENABLED", "false", 1) < 0) {
554 		save_errno = errno;
555 		cryptoerror(LOG_STDERR,
556 		    gettext("Disabling Metaslot failed (%s)."),
557 		    strerror(save_errno));
558 		return (1);
559 	}
560 
561 	/* Begin parsing command line. */
562 	if (pk_argc == 0 && pk_argv == NULL) {
563 		pk_argc = argc;
564 		pk_argv = argv;
565 	}
566 
567 	/* Check for valid verb (or an abbreviation of it). */
568 	found = -1;
569 	for (i = 0; i < num_cmds; i++) {
570 		if (strcmp(cmds[i].verb, pk_argv[0]) == 0) {
571 			if (found < 0) {
572 				found = i;
573 				break;
574 			}
575 		}
576 	}
577 	/* Stop here if no valid verb found. */
578 	if (found < 0) {
579 		cryptoerror(LOG_STDERR, gettext("Invalid verb: %s"),
580 		    pk_argv[0]);
581 		return (1);
582 	}
583 
584 	/* Get to work! */
585 	rv = (*cmds[found].action)(pk_argc, pk_argv);
586 	switch (rv) {
587 	case PK_ERR_NONE:
588 		break;		/* Command succeeded, do nothing. */
589 	case PK_ERR_USAGE:
590 		usage(found);
591 		break;
592 	case PK_ERR_QUIT:
593 		exit(0);
594 		/* NOTREACHED */
595 	case PK_ERR_PK11:
596 	case PK_ERR_SYSTEM:
597 	case PK_ERR_OPENSSL:
598 	case PK_ERR_NSS:
599 	default:
600 		break;
601 	}
602 	return (rv);
603 }
604