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 		"[ label=cert-label ]\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 		"label=cert-label\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=label\n\t\t"
232 		"[ objtype=cert|key ]\n\t\t"
233 		"[ keytype=aes|arcfour|des|3des|generic ]\n\t\t"
234 		"[ sensitive=y|n ]\n\t\t"
235 		"[ extractable=y|n ]\n\t\t"
236 		"[ token=token[:manuf[:serial]]]\n\t"
237 
238 	"import keystore=pkcs11 objtype=crl\n\t\t"
239 		"infile=input-crl-fn\n\t\t"
240 		"outcrl=output-crl-fn\n\t\t"
241 		"outformat=pem|der\n\t\t"
242 		"[ dir=output-crl-directory-path ]\n\t"
243 
244 	"import keystore=file\n\t\t"
245 		"infile=input-fn\n\t\t"
246 		"outkey=output-key-fn\n\t\t"
247 		"outcert=output-cert-fn\n\t\t"
248 		"[ dir=output-cert-dir-path ]\n\t\t"
249 		"[ keydir=output-key-dir-path ]\n\t\t"
250 		"[ outformat=pem|der|pkcs12 ]\n\t"
251 
252 	"import keystore=file objtype=crl\n\t\t"
253 		"infile=input-crl-fn\n\t\t"
254 		"outcrl=output-crl-fn\n\t\t"
255 		"outformat=pem|der\n\t\t"
256 		"[ dir=output-crl-directory-path ]\n\t"
257 	},
258 
259 	{ "export",	pk_export,	0,
260 		"exports objects from the keystore to a file",
261 
262 	"export [token=token[:manuf[:serial]]]\n\t\t"
263 	"outfile=output-fn\n\t"
264 
265 	"export keystore=nss\n\t\t"
266 		"outfile=output-fn\n\t\t"
267 		"[ objtype=cert|key ]\n\t\t"
268 		"[ subject=subject-DN ]\n\t\t"
269 		"[ issuer=issuer-DN ]\n\t\t"
270 		"[ serial=serial number ]\n\t\t"
271 		"[ nickname=cert-nickname ]\n\t\t"
272 		"[ token=token[:manuf[:serial]]]\n\t\t"
273 		"[ dir=directory-path ]\n\t\t"
274 		"[ prefix=DBPrefix ]\n\t\t"
275 		"[ outformat=pem|der|pkcs12 ]\n\t"
276 
277 	"export keystore=pkcs11\n\t\t"
278 		"outfile=output-fn\n\t\t"
279 		"[ objtype=cert|key ]\n\t\t"
280 		"[ label=label ]\n\t\t"
281 		"[ subject=subject-DN ]\n\t\t"
282 		"[ issuer=issuer-DN ]\n\t\t"
283 		"[ serial=serial number ]\n\t\t"
284 		"[ outformat=pem|der|pkcs12|raw ]\n\t\t"
285 		"[ token=token[:manuf[:serial]]]\n\t"
286 
287 	"export keystore=file\n\t\t"
288 		"certfile=cert-input-fn\n\t\t"
289 		"keyfile=key-input-fn\n\t\t"
290 		"outfile=output-pkcs12-fn\n\t\t"
291 		"[ dir=directory-path ]\n\t"
292 	},
293 
294 	{ "gencert",	pk_gencert,	0,
295 		"creates a self-signed X.509v3 certificate",
296 
297 	"gencert [-i] keystore=nss\n\t\t"
298 		"label=cert-nickname\n\t\t"
299 		"serial=serial number hex string]\n\t\t"
300 		"subject=subject-DN\n\t\t"
301 		"[ altname=[critical:]SubjectAltName ]\n\t\t"
302 		"[ keyusage=[critical:]usage,usage,...]\n\t\t"
303 		"[ token=token[:manuf[:serial]]]\n\t\t"
304 		"[ dir=directory-path ]\n\t\t"
305 		"[ prefix=DBprefix ]\n\t\t"
306 		"[ keytype=rsa|dsa ]\n\t\t"
307 		"[ keylen=key-size ]\n\t\t"
308 		"[ trust=trust-value ]\n\t\t"
309 		"[ lifetime=number-hour|number-day|number-year ]\n\t"
310 
311 	"gencert [-i] [ keystore=pkcs11 ]\n\t\t"
312 		"label=key/cert-label\n\t\t"
313 		"subject=subject-DN\n\t\t"
314 		"serial=serial number hex string\n\t\t"
315 		"[ altname=[critical:]SubjectAltName ]\n\t\t"
316 		"[ keyusage=[critical:]usage,usage,...]\n\t\t"
317 		"[ token=token[:manuf[:serial]]]\n\t\t"
318 		"[ keytype=rsa|dsa ]\n\t\t"
319 		"[ keylen=key-size ]\n\t\t"
320 		"[ lifetime=number-hour|number-day|number-year ]\n\t"
321 
322 	"gencert [-i] keystore=file\n\t\t"
323 		"outcert=cert_filename\n\t\t"
324 		"outkey=key_filename\n\t\t"
325 		"subject=subject-DN\n\t\t"
326 		"serial=serial number hex string\n\t\t"
327 		"[ altname=[critical:]SubjectAltName ]\n\t\t"
328 		"[ keyusage=[critical:]usage,usage,...]\n\t\t"
329 		"[ format=der|pem ]\n\t\t"
330 		"[ dir=directory-path ]\n\t\t"
331 		"[ prefix=DBprefix ]\n\t\t"
332 		"[ keytype=rsa|dsa ]\n\t\t"
333 		"[ keylen=key-size ]\n\t\t"
334 		"[ lifetime=number-hour|number-day|number-year ]\n\t"
335 	},
336 	{ "gencsr",	pk_gencsr,	0,
337 		"creates a PKCS#10 certificate signing request file",
338 	"gencsr [-i] keystore=nss \n\t\t"
339 		"nickname=cert-nickname\n\t\t"
340 		"outcsr=csr-fn\n\t\t"
341 		"subject=subject-DN\n\t\t"
342 		"[ altname=[critical:]SubjectAltName ]\n\t\t"
343 		"[ keyusage=[critical:]usage,usage,...]\n\t\t"
344 		"[ token=token[:manuf[:serial]]]\n\t\t"
345 		"[ dir=directory-path ]\n\t\t"
346 		"[ prefix=DBprefix ]\n\t\t"
347 		"[ keytype=rsa|dsa ]\n\t\t"
348 		"[ keylen=key-size ]\n\t\t"
349 		"[ format=pem|der ]\n\t"
350 	"gencsr [-i] [ keystore=pkcs11 ]\n\t\t"
351 		"label=key-label\n\t\t"
352 		"outcsr=csr-fn\n\t\t"
353 		"subject=subject-DN\n\t\t"
354 		"[ altname=[critical:]SubjectAltName ]\n\t\t"
355 		"[ keyusage=[critical:]usage,usage,...]\n\t\t"
356 		"[ token=token[:manuf[:serial]]]\n\t\t"
357 		"[ keytype=rsa|dsa ]\n\t\t"
358 		"[ keylen=key-size ]\n\t\t"
359 		"[ format=pem|der ]]\n\t"
360 	"gencsr [-i] keystore=file\n\t\t"
361 		"outcsr=csr-fn\n\t\t"
362 		"outkey=key-fn\n\t\t"
363 		"subject=subject-DN\n\t\t"
364 		"[ altname=[critical:]SubjectAltName ]\n\t\t"
365 		"[ keyusage=[critical:]usage,usage,...]\n\t\t"
366 		"[ keytype=rsa|dsa ]\n\t\t"
367 		"[ keylen=key-size ]\n\t\t"
368 		"[ dir=directory-path ]\n\t\t"
369 		"[ format=pem|der ]\n\t"
370 	},
371 
372 	{ "download",	pk_download,	0,
373 		"downloads a CRL or certificate file from an external source",
374 
375 	"download url=url_str\n\t\t"
376 		"[ objtype=crl|cert ]\n\t\t"
377 		"[ http_proxy=proxy_str ]\n\t\t"
378 		"[ outfile = outfile ]\n\t\t"
379 	},
380 
381 	{ "genkey",	pk_genkey,	0,
382 		"creates a symmetric key in the keystore",
383 
384 	"genkey [ keystore=pkcs11 ]\n\t\t"
385 		"label=key-label\n\t\t"
386 		"[ keytype=aes|arcfour|des|3des|generic ]\n\t\t"
387 		"[ keylen=key-size (AES, ARCFOUR or GENERIC only)]\n\t\t"
388 		"[ token=token[:manuf[:serial]]]\n\t\t"
389 		"[ sensitive=y|n ]\n\t\t"
390 		"[ extractable=y|n ]\n\t\t"
391 		"[ print=y|n ]\n\t"
392 
393 	"genkey keystore=nss\n\t\t"
394 		"label=key-label\n\t\t"
395 		"[ keytype=aes|arcfour|des|3des|generic ]\n\t\t"
396 		"[ keylen=key-size (AES, ARCFOUR or GENERIC only)]\n\t\t"
397 		"[ token=token[:manuf[:serial]]]\n\t\t"
398 		"[ dir=directory-path ]\n\t\t"
399 		"[ prefix=DBprefix ]\n\t"
400 
401 	"genkey keystore=file\n\t\t"
402 		"outkey=key-fn\n\t\t"
403 		"[ keytype=aes|arcfour|des|3des|generic ]\n\t\t"
404 		"[ keylen=key-size (AES, ARCFOUR or GENERIC only)]\n\t\t"
405 		"[ dir=directory-path ]\n\t\t"
406 		"[ print=y|n ]\n\t"
407 	},
408 
409 	{ "help",	pk_help,	0,
410 		"displays help message",
411 		"help\t(help and usage)" }
412 };
413 
414 static int	num_cmds = sizeof (cmds) / sizeof (verbcmd);
415 
416 static char	*prog;
417 static void	usage(int);
418 
419 /*
420  * Usage information.  This function must be updated when new verbs or
421  * options are added.
422  */
423 static void
424 usage(int idx)
425 {
426 	int	i;
427 
428 	/* Display this block only in command-line mode. */
429 	(void) fprintf(stdout, gettext("Usage:\n"));
430 	(void) fprintf(stdout, gettext("   %s -?\t(help and usage)\n"),
431 	    prog);
432 	(void) fprintf(stdout, gettext("   %s -f option_file\n"), prog);
433 	(void) fprintf(stdout, gettext("   %s subcommand [options...]\n"),
434 	    prog);
435 	(void) fprintf(stdout, gettext("where subcommands may be:\n"));
436 
437 	/* Display only those verbs that match the current tool mode. */
438 	if (idx == -1) {
439 		for (i = 0; i < num_cmds; i++) {
440 			/* Do NOT i18n/l10n. */
441 			(void) fprintf(stdout, "   %-8s	- %s\n",
442 			    cmds[i].verb, cmds[i].summary);
443 		}
444 		(void) fprintf(stdout, gettext("\nFurther details on the "
445 		    "subcommands can be found by adding \'help\'.\n"
446 		    "Ex: pktool gencert help\n\n"));
447 	} else {
448 		(void) fprintf(stdout, "\t%s\n", cmds[idx].synopsis);
449 	}
450 }
451 
452 /*
453  * Provide help, in the form of displaying the usage.
454  */
455 static int
456 pk_help(int argc, char *argv[])
457 /* ARGSUSED */
458 {
459 	usage(-1);
460 	return (0);
461 }
462 
463 /*
464  * Process arguments from the argfile and create a new
465  * argv/argc list to be processed later.
466  */
467 static int
468 process_arg_file(char *argfile, char ***argv, int *argc)
469 {
470 	FILE *fp;
471 	char argline[2 * BUFSIZ]; /* 2048 bytes should be plenty */
472 	char *p;
473 	int nargs = 0;
474 
475 	if ((fp = fopen(argfile, "rF")) == NULL) {
476 		(void) fprintf(stderr,
477 		    gettext("Cannot read argfile %s: %s\n"),
478 		    argfile, strerror(errno));
479 		return (errno);
480 	}
481 
482 	while (fgets(argline, sizeof (argline), fp) != NULL) {
483 		int j;
484 		/* remove trailing whitespace */
485 		j = strlen(argline) - 1;
486 		while (j >= 0 && isspace(argline[j])) {
487 			argline[j] = 0;
488 			j--;
489 		}
490 		/* If it was a blank line, get the next one. */
491 		if (!strlen(argline))
492 			continue;
493 
494 		(*argv) = realloc((*argv),
495 		    (nargs + 1) * sizeof (char *));
496 		if ((*argv) == NULL) {
497 			perror("memory error");
498 			(void) fclose(fp);
499 			return (errno);
500 		}
501 		p = (char *)strdup(argline);
502 		if (p == NULL) {
503 			perror("memory error");
504 			(void) fclose(fp);
505 			return (errno);
506 		}
507 		(*argv)[nargs] = p;
508 		nargs++;
509 	}
510 	*argc = nargs;
511 	(void) fclose(fp);
512 	return (0);
513 }
514 
515 /*
516  * MAIN() -- where all the action is
517  */
518 int
519 main(int argc, char *argv[], char *envp[])
520 /* ARGSUSED2 */
521 {
522 	int	i, found = -1;
523 	int	rv;
524 	int	pk_argc = 0;
525 	char	**pk_argv = NULL;
526 	int	save_errno = 0;
527 
528 	/* Set up for i18n/l10n. */
529 	(void) setlocale(LC_ALL, "");
530 #if !defined(TEXT_DOMAIN)		/* Should be defined by cc -D. */
531 #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it isn't. */
532 #endif
533 	(void) textdomain(TEXT_DOMAIN);
534 
535 	/* Get program base name and move pointer over 0th arg. */
536 	prog = basename(argv[0]);
537 	argv++, argc--;
538 
539 	/* Set up for debug and error output. */
540 	if (argc == 0) {
541 		usage(-1);
542 		return (1);
543 	}
544 
545 	/* Check for help options.  For CLIP-compliance. */
546 	if (strcmp(argv[0], "-?") == 0) {
547 		return (pk_help(argc, argv));
548 	} else if (strcmp(argv[0], "-f") == 0 && argc == 2) {
549 		rv = process_arg_file(argv[1], &pk_argv, &pk_argc);
550 		if (rv)
551 			return (rv);
552 	} else if (argc >= 1 && argv[0][0] == '-') {
553 		usage(-1);
554 		return (1);
555 	}
556 
557 	/* Always turns off Metaslot so that we can see softtoken. */
558 	if (setenv("METASLOT_ENABLED", "false", 1) < 0) {
559 		save_errno = errno;
560 		cryptoerror(LOG_STDERR,
561 		    gettext("Disabling Metaslot failed (%s)."),
562 		    strerror(save_errno));
563 		return (1);
564 	}
565 
566 	/* Begin parsing command line. */
567 	if (pk_argc == 0 && pk_argv == NULL) {
568 		pk_argc = argc;
569 		pk_argv = argv;
570 	}
571 
572 	/* Check for valid verb (or an abbreviation of it). */
573 	found = -1;
574 	for (i = 0; i < num_cmds; i++) {
575 		if (strcmp(cmds[i].verb, pk_argv[0]) == 0) {
576 			if (found < 0) {
577 				found = i;
578 				break;
579 			}
580 		}
581 	}
582 	/* Stop here if no valid verb found. */
583 	if (found < 0) {
584 		cryptoerror(LOG_STDERR, gettext("Invalid verb: %s"),
585 		    pk_argv[0]);
586 		return (1);
587 	}
588 
589 	/* Get to work! */
590 	rv = (*cmds[found].action)(pk_argc, pk_argv);
591 	switch (rv) {
592 	case PK_ERR_NONE:
593 		break;		/* Command succeeded, do nothing. */
594 	case PK_ERR_USAGE:
595 		usage(found);
596 		break;
597 	case PK_ERR_QUIT:
598 		exit(0);
599 		/* NOTREACHED */
600 	case PK_ERR_PK11:
601 	case PK_ERR_SYSTEM:
602 	case PK_ERR_OPENSSL:
603 	case PK_ERR_NSS:
604 	default:
605 		break;
606 	}
607 	return (rv);
608 }
609