xref: /illumos-gate/usr/src/cmd/cmd-crypto/pktool/common.c (revision d321a33cdd896e6b211d113a33698dd76e89b861)
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 contains the functions that are shared among
30  * the various services this tool will ultimately provide.
31  * The functions in this file return PKCS#11 CK_RV errors.
32  * Only one session and one login per token is supported
33  * at this time.
34  */
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <ctype.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <fcntl.h>
43 #include <tzfile.h>
44 #include <cryptoutil.h>
45 #include <security/cryptoki.h>
46 #include <kmfapi.h>
47 
48 #include "common.h"
49 
50 /* Local status variables. */
51 static boolean_t	initialized = B_FALSE;
52 static boolean_t	session_opened = B_FALSE;
53 static boolean_t	logged_in = B_FALSE;
54 
55 /* Supporting structures and global variables for getopt_av(). */
56 typedef struct	av_opts_s {
57 	int		shortnm;	/* short name character */
58 	char		*longnm;	/* long name string, NOT terminated */
59 	int		longnm_len;	/* length of long name string */
60 	boolean_t	has_arg;	/* takes optional argument */
61 } av_opts;
62 static av_opts		*opts_av = NULL;
63 static const char	*_save_optstr = NULL;
64 static int		_save_numopts = 0;
65 
66 int			optind_av = 1;
67 char			*optarg_av = NULL;
68 
69 static void close_sess(CK_SESSION_HANDLE);
70 static void logout_token(CK_SESSION_HANDLE);
71 
72 /*
73  * Perform PKCS#11 setup here.  Currently only C_Initialize is required,
74  * along with setting/resetting state variables.
75  */
76 CK_RV
77 init_pk11(void)
78 {
79 	CK_RV		rv = CKR_OK;
80 
81 	/* If C_Initialize() already called, nothing to do here. */
82 	if (initialized == B_TRUE)
83 		return (CKR_OK);
84 
85 	/* Reset state variables because C_Initialize() not yet done. */
86 	session_opened = B_FALSE;
87 	logged_in = B_FALSE;
88 
89 	/* Initialize PKCS#11 library. */
90 	if ((rv = C_Initialize(NULL_PTR)) != CKR_OK &&
91 	    rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) {
92 		return (rv);
93 	}
94 
95 	initialized = B_TRUE;
96 	return (CKR_OK);
97 }
98 
99 /*
100  * Finalize PKCS#11 library and reset state variables.  Open sessions,
101  * if any, are closed, and thereby any logins are logged out also.
102  */
103 void
104 final_pk11(CK_SESSION_HANDLE sess)
105 {
106 
107 	/* If the library wasn't initialized, nothing to do here. */
108 	if (!initialized)
109 		return;
110 
111 	/* Make sure the sesion is closed first. */
112 	close_sess(sess);
113 
114 	(void) C_Finalize(NULL);
115 	initialized = B_FALSE;
116 }
117 
118 /*
119  * Close PKCS#11 session and reset state variables.  Any logins are
120  * logged out.
121  */
122 static void
123 close_sess(CK_SESSION_HANDLE sess)
124 {
125 
126 	if (sess == NULL) {
127 		return;
128 	}
129 
130 	/* If session is already closed, nothing to do here. */
131 	if (!session_opened)
132 		return;
133 
134 	/* Make sure user is logged out of token. */
135 	logout_token(sess);
136 
137 	(void) C_CloseSession(sess);
138 	session_opened = B_FALSE;
139 }
140 
141 /*
142  * Log user out of token and reset status variable.
143  */
144 static void
145 logout_token(CK_SESSION_HANDLE sess)
146 {
147 
148 	if (sess == NULL) {
149 		return;
150 	}
151 
152 	/* If already logged out, nothing to do here. */
153 	if (!logged_in)
154 		return;
155 
156 	(void) C_Logout(sess);
157 	logged_in = B_FALSE;
158 }
159 
160 /*
161  * Gets PIN from user.  Caller needs to free the returned PIN when done.
162  * If two prompts are given, the PIN is confirmed with second prompt.
163  * Note that getphassphrase() may return data in static memory area.
164  */
165 CK_RV
166 get_pin(char *prompt1, char *prompt2, CK_UTF8CHAR_PTR *pin, CK_ULONG *pinlen)
167 {
168 	char		*save_phrase, *phrase1, *phrase2;
169 
170 
171 #ifdef DEBUG
172 	if (getenv("TOKENPIN") != NULL) {
173 		*pin = (CK_UTF8CHAR_PTR)strdup(getenv("TOKENPIN"));
174 		*pinlen = strlen((char *)(*pin));
175 		return (CKR_OK);
176 	}
177 #endif /* DEBUG */
178 
179 	/* Prompt user for a PIN. */
180 	if (prompt1 == NULL) {
181 		return (CKR_ARGUMENTS_BAD);
182 	}
183 	if ((phrase1 = getpassphrase(prompt1)) == NULL) {
184 		return (CKR_FUNCTION_FAILED);
185 	}
186 
187 	/* Duplicate 1st PIN in separate chunk of memory. */
188 	if ((save_phrase = strdup(phrase1)) == NULL)
189 		return (CKR_HOST_MEMORY);
190 
191 	/* If second prompt given, PIN confirmation is requested. */
192 	if (prompt2 != NULL) {
193 		if ((phrase2 = getpassphrase(prompt2)) == NULL) {
194 			free(save_phrase);
195 			return (CKR_FUNCTION_FAILED);
196 		}
197 		if (strcmp(save_phrase, phrase2) != 0) {
198 			free(save_phrase);
199 			return (CKR_PIN_INCORRECT);
200 		}
201 	}
202 
203 	*pin = (CK_UTF8CHAR_PTR)save_phrase;
204 	*pinlen = strlen(save_phrase);
205 	return (CKR_OK);
206 }
207 
208 /*
209  * Gets yes/no response from user.  If either no prompt is supplied, a
210  * default prompt is used.  If not message for invalid input is supplied,
211  * a default will not be provided.  If the user provides no response,
212  * the input default B_TRUE == yes, B_FALSE == no is returned.
213  * Otherwise, B_TRUE is returned for yes, and B_FALSE for no.
214  */
215 boolean_t
216 yesno(char *prompt, char *invalid, boolean_t dflt)
217 {
218 	char		*response, buf[1024];
219 	char		*yes = gettext("yes");
220 	char		*no = gettext("no");
221 
222 
223 #ifdef DEBUG
224 	/* If debugging or testing, return TRUE and avoid prompting */
225 	if (getenv("TOKENPIN") != NULL) {
226 		return (B_TRUE);
227 	}
228 #endif /* DEBUG */
229 
230 	if (prompt == NULL)
231 		prompt = gettext("Enter (y)es or (n)o? ");
232 
233 	for (;;) {
234 		/* Prompt user. */
235 		(void) printf("%s", prompt);
236 		(void) fflush(stdout);
237 
238 		/* Get the response. */
239 		if ((response = fgets(buf, sizeof (buf), stdin)) == NULL)
240 			break;		/* go to default response */
241 
242 		/* Skip any leading white space. */
243 		while (isspace(*response))
244 			response++;
245 		if (*response == '\0')
246 			break;		/* go to default response */
247 
248 		/* Is it valid input?  Return appropriately. */
249 		if (strncasecmp(response, yes, 1) == 0)
250 			return (B_TRUE);
251 		if (strncasecmp(response, no, 1) == 0)
252 			return (B_FALSE);
253 
254 		/* Indicate invalid input, and try again. */
255 		if (invalid != NULL)
256 			(void) printf("%s", invalid);
257 	}
258 	return (dflt);
259 }
260 
261 /*
262  * Gets the list of slots which have tokens in them.  Keeps adjusting
263  * the size of the slot list buffer until the call is successful or an
264  * irrecoverable error occurs.
265  */
266 CK_RV
267 get_token_slots(CK_SLOT_ID_PTR *slot_list, CK_ULONG *slot_count)
268 {
269 	CK_ULONG	tmp_count = 0;
270 	CK_SLOT_ID_PTR	tmp_list = NULL_PTR, tmp2_list = NULL_PTR;
271 	int		rv = CKR_OK;
272 
273 	if (!initialized)
274 		if ((rv = init_pk11()) != CKR_OK)
275 			return (rv);
276 
277 	/*
278 	 * Get the slot count first because we don't know how many
279 	 * slots there are and how many of those slots even have tokens.
280 	 * Don't specify an arbitrary buffer size for the slot list;
281 	 * it may be too small (see section 11.5 of PKCS#11 spec).
282 	 * Also select only those slots that have tokens in them,
283 	 * because this tool has no need to know about empty slots.
284 	 */
285 	if ((rv = C_GetSlotList(1, NULL_PTR, &tmp_count)) != CKR_OK)
286 		return (rv);
287 
288 	if (tmp_count == 0) {
289 		*slot_list = NULL_PTR;
290 		*slot_count = 0;
291 		return (CKR_OK);
292 	}
293 
294 	/* Allocate initial space for the slot list. */
295 	if ((tmp_list = (CK_SLOT_ID_PTR) malloc(tmp_count *
296 	    sizeof (CK_SLOT_ID))) == NULL)
297 		return (CKR_HOST_MEMORY);
298 
299 	/* Then get the slot list itself. */
300 	for (;;) {
301 		if ((rv = C_GetSlotList(1, tmp_list, &tmp_count)) == CKR_OK) {
302 			*slot_list = tmp_list;
303 			*slot_count = tmp_count;
304 			break;
305 		}
306 
307 		if (rv != CKR_BUFFER_TOO_SMALL) {
308 			free(tmp_list);
309 			break;
310 		}
311 
312 		/* If the number of slots grew, try again. */
313 		if ((tmp2_list = (CK_SLOT_ID_PTR) realloc(tmp_list,
314 		    tmp_count * sizeof (CK_SLOT_ID))) == NULL) {
315 			free(tmp_list);
316 			rv = CKR_HOST_MEMORY;
317 			break;
318 		}
319 		tmp_list = tmp2_list;
320 	}
321 
322 	return (rv);
323 }
324 
325 /*
326  * Breaks out the getopt-style option string into a structure that can be
327  * traversed later for calls to getopt_av().  Option string is NOT altered,
328  * but the struct fields point to locations within option string.
329  */
330 static int
331 populate_opts(char *optstring)
332 {
333 	int		i;
334 	av_opts		*temp;
335 	char		*marker;
336 
337 	if (optstring == NULL || *optstring == '\0')
338 		return (0);
339 
340 	/*
341 	 * This tries to imitate getopt(3c) Each option must conform to:
342 	 * <short name char> [ ':' ] [ '(' <long name string> ')' ]
343 	 * If long name is missing, the short name is used for long name.
344 	 */
345 	for (i = 0; *optstring != '\0'; i++) {
346 		if ((temp = (av_opts *)((i == 0) ? malloc(sizeof (av_opts)) :
347 		    realloc(opts_av, (i+1) * sizeof (av_opts)))) == NULL) {
348 			if (opts_av != NULL)
349 				free(opts_av);
350 			opts_av = NULL;
351 			return (0);
352 		} else {
353 			opts_av = (av_opts *)temp;
354 		}
355 
356 		(void) memset(&opts_av[i], 0, sizeof (av_opts));
357 		marker = optstring;		/* may need optstring later */
358 
359 		opts_av[i].shortnm = *marker++;	/* set short name */
360 
361 		if (*marker == ':') {		/* check for opt arg */
362 			marker++;
363 			opts_av[i].has_arg = B_TRUE;
364 		}
365 
366 		if (*marker == '(') {		/* check and set long name */
367 			marker++;
368 			opts_av[i].longnm = marker;
369 			opts_av[i].longnm_len = strcspn(marker, ")");
370 			optstring = marker + opts_av[i].longnm_len + 1;
371 		} else {
372 			/* use short name option character */
373 			opts_av[i].longnm = optstring;
374 			opts_av[i].longnm_len = 1;
375 			optstring = marker;
376 		}
377 	}
378 
379 	return (i);
380 }
381 
382 /*
383  * getopt_av() is very similar to getopt(3c) in that the takes an option
384  * string, compares command line arguments for matches, and returns a single
385  * letter option when a match is found.  However, getopt_av() differs from
386  * getopt(3c) by requiring that only longname options and values be found
387  * on the command line and all leading dashes are omitted.  In other words,
388  * it tries to enforce only longname "option=value" arguments on the command
389  * line.  Boolean options are not allowed either.
390  */
391 int
392 getopt_av(int argc, char * const *argv, const char *optstring)
393 {
394 	int	i;
395 	int	len;
396 	char   *cur_option;
397 
398 	if (optind_av >= argc)
399 		return (EOF);
400 
401 	/* First time or when optstring changes from previous one */
402 	if (_save_optstr != optstring) {
403 		if (opts_av != NULL)
404 			free(opts_av);
405 		opts_av = NULL;
406 		_save_optstr = optstring;
407 		_save_numopts = populate_opts((char *)optstring);
408 	}
409 
410 	for (i = 0; i < _save_numopts; i++) {
411 		cur_option = argv[optind_av];
412 
413 		if (strcmp(cur_option, "--") == 0) {
414 			optind_av++;
415 			break;
416 		}
417 
418 		if (cur_option[0] == '-' && strlen(cur_option) == 2) {
419 			len = 1;
420 			cur_option++; /* remove "-" */
421 		} else {
422 			len = strcspn(cur_option, "=");
423 		}
424 
425 		if (len == opts_av[i].longnm_len && strncmp(cur_option,
426 		    opts_av[i].longnm, opts_av[i].longnm_len) == 0) {
427 			/* matched */
428 			if (!opts_av[i].has_arg) {
429 				optind_av++;
430 				return (opts_av[i].shortnm);
431 			}
432 
433 			/* needs optarg */
434 			if (cur_option[len] == '=') {
435 				optarg_av = &(cur_option[len+1]);
436 				optind_av++;
437 				return (opts_av[i].shortnm);
438 			}
439 
440 			optarg_av = NULL;
441 			optind_av++;
442 			return ((int)'?');
443 		}
444 	}
445 
446 	return (EOF);
447 }
448 
449 KMF_KEYSTORE_TYPE
450 KS2Int(char *keystore_str)
451 {
452 	if (keystore_str == NULL)
453 		return (0);
454 	if (!strcasecmp(keystore_str, "pkcs11"))
455 		return (KMF_KEYSTORE_PK11TOKEN);
456 	else if (!strcasecmp(keystore_str, "nss"))
457 		return (KMF_KEYSTORE_NSS);
458 	else if (!strcasecmp(keystore_str, "file"))
459 		return (KMF_KEYSTORE_OPENSSL);
460 	else
461 		return (0);
462 }
463 
464 
465 int
466 Str2KeyType(char *algm, KMF_KEY_ALG *ktype, KMF_ALGORITHM_INDEX *sigAlg)
467 {
468 	if (algm == NULL) {
469 		*sigAlg = KMF_ALGID_MD5WithRSA;
470 		*ktype = KMF_RSA;
471 	} else if (strcasecmp(algm, "DSA") == 0) {
472 		*sigAlg = KMF_ALGID_SHA1WithDSA;
473 		*ktype = KMF_DSA;
474 	} else if (strcasecmp(algm, "RSA") == 0) {
475 		*sigAlg = KMF_ALGID_MD5WithRSA;
476 		*ktype = KMF_RSA;
477 	} else {
478 		return (-1);
479 	}
480 	return (0);
481 }
482 
483 int
484 Str2SymKeyType(char *algm, KMF_KEY_ALG *ktype)
485 {
486 	if (algm == NULL)
487 		*ktype = KMF_AES;
488 	else if (strcasecmp(algm, "aes") == 0)
489 		*ktype = KMF_AES;
490 	else if (strcasecmp(algm, "arcfour") == 0)
491 		*ktype = KMF_RC4;
492 	else if (strcasecmp(algm, "des") == 0)
493 		*ktype = KMF_DES;
494 	else if (strcasecmp(algm, "3des") == 0)
495 		*ktype = KMF_DES3;
496 	else if (strcasecmp(algm, "generic") == 0)
497 		*ktype = KMF_GENERIC_SECRET;
498 	else
499 		return (-1);
500 
501 	return (0);
502 }
503 
504 int
505 Str2Lifetime(char *ltimestr, uint32_t *ltime)
506 {
507 	int num;
508 	char timetok[6];
509 
510 	if (ltimestr == NULL || !strlen(ltimestr)) {
511 		/* default to 1 year lifetime */
512 		*ltime = SECSPERDAY * DAYSPERNYEAR;
513 		return (0);
514 	}
515 
516 	(void) memset(timetok, 0, sizeof (timetok));
517 	if (sscanf(ltimestr, "%d-%06s", &num, timetok) != 2)
518 		return (-1);
519 
520 	if (!strcasecmp(timetok, "day") ||
521 	    !strcasecmp(timetok, "days")) {
522 		*ltime = num * SECSPERDAY;
523 	} else if (!strcasecmp(timetok, "hour") ||
524 	    !strcasecmp(timetok, "hours")) {
525 		*ltime = num * SECSPERHOUR;
526 	} else if (!strcasecmp(timetok, "year") ||
527 	    !strcasecmp(timetok, "years")) {
528 		*ltime = num * SECSPERDAY * DAYSPERNYEAR;
529 	} else {
530 		*ltime = 0;
531 		return (-1);
532 	}
533 
534 	return (0);
535 }
536 
537 int
538 OT2Int(char *objclass)
539 {
540 	char *c = NULL;
541 	int retval = 0;
542 
543 	if (objclass == NULL)
544 		return (-1);
545 
546 	c = strchr(objclass, ':');
547 	if (c != NULL) {
548 		if (!strcasecmp(c, ":private"))
549 			retval = PK_PRIVATE_OBJ;
550 		else if (!strcasecmp(c, ":public"))
551 			retval = PK_PUBLIC_OBJ;
552 		else if (!strcasecmp(c, ":both"))
553 			retval = PK_PRIVATE_OBJ | PK_PUBLIC_OBJ;
554 		else /* unrecognized option */
555 			return (-1);
556 
557 		*c = '\0';
558 	}
559 
560 	if (!strcasecmp(objclass, "public")) {
561 		if (retval)
562 			return (-1);
563 		return (retval | PK_PUBLIC_OBJ | PK_CERT_OBJ | PK_PUBKEY_OBJ);
564 	} else if (!strcasecmp(objclass, "private")) {
565 		if (retval)
566 			return (-1);
567 		return (retval | PK_PRIKEY_OBJ | PK_PRIVATE_OBJ);
568 	} else if (!strcasecmp(objclass, "both")) {
569 		if (retval)
570 			return (-1);
571 		return (PK_KEY_OBJ | PK_PUBLIC_OBJ | PK_PRIVATE_OBJ);
572 	} else if (!strcasecmp(objclass, "cert")) {
573 		return (retval | PK_CERT_OBJ);
574 	} else if (!strcasecmp(objclass, "key")) {
575 		if (retval == 0) /* return all keys */
576 			return (retval | PK_KEY_OBJ);
577 		else if (retval == (PK_PRIVATE_OBJ | PK_PUBLIC_OBJ))
578 			/* return all keys */
579 			return (retval | PK_KEY_OBJ);
580 		else if (retval & PK_PUBLIC_OBJ)
581 			/* Only return public keys */
582 			return (retval | PK_PUBKEY_OBJ);
583 		else if (retval & PK_PRIVATE_OBJ)
584 			/* Only return private keys */
585 			return (retval | PK_PRIKEY_OBJ);
586 	} else if (!strcasecmp(objclass, "crl")) {
587 		if (retval)
588 			return (-1);
589 		return (retval | PK_CRL_OBJ);
590 	}
591 
592 	if (retval == 0) /* No matches found */
593 		retval = -1;
594 	return (retval);
595 }
596 
597 KMF_ENCODE_FORMAT
598 Str2Format(char *formstr)
599 {
600 	if (formstr == NULL || !strcasecmp(formstr, "der"))
601 		return (KMF_FORMAT_ASN1);
602 	if (!strcasecmp(formstr, "pem"))
603 		return (KMF_FORMAT_PEM);
604 	if (!strcasecmp(formstr, "pkcs12"))
605 		return (KMF_FORMAT_PKCS12);
606 	if (!strcasecmp(formstr, "raw"))
607 		return (KMF_FORMAT_RAWKEY);
608 
609 	return (KMF_FORMAT_UNDEF);
610 }
611 
612 
613 KMF_RETURN
614 select_token(void *kmfhandle, char *token,
615 	int readonly)
616 {
617 	KMF_ATTRIBUTE attlist[10];
618 	int i = 0;
619 	KMF_KEYSTORE_TYPE kstype = KMF_KEYSTORE_PK11TOKEN;
620 	KMF_RETURN rv = KMF_OK;
621 
622 	if (token == NULL)
623 		return (KMF_ERR_BAD_PARAMETER);
624 
625 	kmf_set_attr_at_index(attlist, i,
626 	    KMF_KEYSTORE_TYPE_ATTR, &kstype,
627 	    sizeof (kstype));
628 	i++;
629 
630 	if (token) {
631 		kmf_set_attr_at_index(attlist, i,
632 		    KMF_TOKEN_LABEL_ATTR, token,
633 		    strlen(token));
634 		i++;
635 	}
636 
637 	kmf_set_attr_at_index(attlist, i,
638 	    KMF_READONLY_ATTR, &readonly,
639 	    sizeof (readonly));
640 	i++;
641 
642 	rv = kmf_configure_keystore(kmfhandle, i, attlist);
643 	if (rv == KMF_ERR_TOKEN_SELECTED)
644 		rv = KMF_OK;
645 	return (rv);
646 }
647 
648 
649 KMF_RETURN
650 configure_nss(void *kmfhandle, char *dir, char *prefix)
651 {
652 
653 	KMF_ATTRIBUTE attlist[10];
654 	int i = 0;
655 	KMF_KEYSTORE_TYPE kstype = KMF_KEYSTORE_NSS;
656 	KMF_RETURN rv = KMF_OK;
657 
658 	kmf_set_attr_at_index(attlist, i,
659 	    KMF_KEYSTORE_TYPE_ATTR, &kstype,
660 	    sizeof (kstype));
661 	i++;
662 
663 	if (dir) {
664 		kmf_set_attr_at_index(attlist, i,
665 		    KMF_DIRPATH_ATTR, dir,
666 		    strlen(dir));
667 		i++;
668 	}
669 
670 	if (prefix) {
671 		kmf_set_attr_at_index(attlist, i,
672 		    KMF_CERTPREFIX_ATTR, prefix,
673 		    strlen(prefix));
674 		i++;
675 
676 		kmf_set_attr_at_index(attlist, i,
677 		    KMF_KEYPREFIX_ATTR, prefix,
678 		    strlen(prefix));
679 		i++;
680 	}
681 
682 	rv = kmf_configure_keystore(kmfhandle, i, attlist);
683 	if (rv == KMF_KEYSTORE_ALREADY_INITIALIZED)
684 		rv = KMF_OK;
685 
686 	return (rv);
687 }
688 
689 
690 KMF_RETURN
691 get_pk12_password(KMF_CREDENTIAL *cred)
692 {
693 	KMF_RETURN rv = KMF_OK;
694 	char prompt[1024];
695 
696 	/*
697 	 * Get the password to use for the PK12 encryption.
698 	 */
699 	(void) strlcpy(prompt,
700 	    gettext("Enter password to use for "
701 	    "accessing the PKCS12 file: "), sizeof (prompt));
702 
703 	if (get_pin(prompt, NULL, (uchar_t **)&cred->cred,
704 	    (ulong_t *)&cred->credlen) != CKR_OK) {
705 		cred->cred = NULL;
706 		cred->credlen = 0;
707 	}
708 
709 	return (rv);
710 }
711 
712 #define	FILENAME_PROMPT gettext("Filename:")
713 #define	FILENAME_MINLEN	1
714 #define	FILENAME_MAXLEN MAXPATHLEN
715 
716 #define	COUNTRY_PROMPT	gettext("Country Name (2 letter code) [US]:")
717 #define	STATE_PROMPT	gettext("State or Province Name (full name) " \
718 	"[Some-State]:")
719 #define	LOCALITY_PROMPT	gettext("Locality Name (eg, city) []:")
720 #define	ORG_PROMPT	gettext("Organization Name (eg, company) []:")
721 #define	UNIT_PROMPT	gettext("Organizational Unit Name (eg, section) []:")
722 #define	NAME_PROMPT	gettext("Common Name (eg, YOUR name) []:")
723 #define	EMAIL_PROMPT	gettext("Email Address []:")
724 
725 #define	SERNO_PROMPT	gettext("Serial Number (hex value, example: " \
726 	"0x01020304):")
727 #define	SERNO_MINLEN	3
728 #define	SERNO_MAXLEN	42
729 
730 #define	LABEL_PROMPT	gettext("Enter a label for the certificate:")
731 #define	LABEL_MINLEN	1
732 #define	LABEL_MAXLEN	1024
733 
734 #define	COUNTRY_DEFAULT "US"
735 #define	STATE_DEFAULT	NULL
736 #define	INVALID_INPUT 	gettext("Invalid input; please re-enter ...")
737 
738 #define	SUBNAMESIZ	1024
739 #define	RDN_MIN		1
740 #define	RDN_MAX		64
741 #define	COUNTRYNAME_MIN	2
742 #define	COUNTRYNAME_MAX	2
743 
744 static char *
745 get_input_string(char *prompt, char *default_str, int min_len, int max_len)
746 {
747 	char buf[1024];
748 	char *response = NULL;
749 	char *ret = NULL;
750 	int len;
751 
752 	for (;;) {
753 		(void) printf("\t%s", prompt);
754 		(void) fflush(stdout);
755 
756 		response = fgets(buf, sizeof (buf), stdin);
757 		if (response == NULL) {
758 			if (default_str != NULL) {
759 				ret = strdup(default_str);
760 			}
761 			break;
762 		}
763 
764 		/* Skip any leading white space. */
765 		while (isspace(*response))
766 			response++;
767 		if (*response == '\0') {
768 			if (default_str != NULL) {
769 				ret = strdup(default_str);
770 			}
771 			break;
772 		}
773 
774 		len = strlen(response);
775 		response[len-1] = '\0'; /* get rid of "LF" */
776 		len--;
777 		if (len >= min_len && len <= max_len) {
778 			ret = strdup(response);
779 			break;
780 		}
781 
782 		(void) printf("%s\n", INVALID_INPUT);
783 
784 	}
785 
786 	return (ret);
787 }
788 
789 int
790 get_filename(char *txt, char **result)
791 {
792 	char prompt[1024];
793 	char *fname = NULL;
794 
795 	(void) snprintf(prompt, sizeof (prompt),
796 	    gettext("Enter filename for the %s: "),
797 	    txt);
798 	fname = get_input_string(prompt, NULL,
799 	    FILENAME_MINLEN, FILENAME_MAXLEN);
800 	*result = fname;
801 	return (0);
802 }
803 
804 int
805 get_certlabel(char **result)
806 {
807 	char *label = NULL;
808 
809 	label = get_input_string(LABEL_PROMPT, NULL,
810 	    LABEL_MINLEN, LABEL_MAXLEN);
811 	*result = label;
812 	return (0);
813 }
814 
815 int
816 get_serial(char **result)
817 {
818 	char *serial = NULL;
819 
820 	serial = get_input_string(SERNO_PROMPT, NULL, SERNO_MINLEN,
821 	    SERNO_MAXLEN);
822 
823 	*result = serial;
824 	return (0);
825 }
826 
827 int
828 get_subname(char **result)
829 {
830 	char *country = NULL;
831 	char *state = NULL;
832 	char *locality = NULL;
833 	char *org = NULL;
834 	char *unit = NULL;
835 	char *name = NULL;
836 	char *email = NULL;
837 	char *subname = NULL;
838 
839 	(void) printf("Entering following fields for subject (a DN) ...\n");
840 	country = get_input_string(COUNTRY_PROMPT, COUNTRY_DEFAULT,
841 	    COUNTRYNAME_MIN, COUNTRYNAME_MAX);
842 	if (country == NULL)
843 		return (-1);
844 
845 	state = get_input_string(STATE_PROMPT, STATE_DEFAULT,
846 	    RDN_MIN, RDN_MAX);
847 
848 	locality = get_input_string(LOCALITY_PROMPT, NULL, RDN_MIN, RDN_MAX);
849 	org = get_input_string(ORG_PROMPT, NULL, RDN_MIN, RDN_MAX);
850 	unit = get_input_string(UNIT_PROMPT, NULL, RDN_MIN, RDN_MAX);
851 	name = get_input_string(NAME_PROMPT, NULL, RDN_MIN, RDN_MAX);
852 	email = get_input_string(EMAIL_PROMPT, NULL, RDN_MIN, RDN_MAX);
853 
854 	/* Now create a subject name from the input strings */
855 	if ((subname = malloc(SUBNAMESIZ)) == NULL)
856 		goto out;
857 
858 	(void) memset(subname, 0, SUBNAMESIZ);
859 	(void) strlcpy(subname, "C=", SUBNAMESIZ);
860 	(void) strlcat(subname, country, SUBNAMESIZ);
861 	if (state != NULL) {
862 		(void) strlcat(subname, ", ST=", SUBNAMESIZ);
863 		(void) strlcat(subname, state, SUBNAMESIZ);
864 	}
865 
866 	if (locality != NULL) {
867 		(void) strlcat(subname, ", L=", SUBNAMESIZ);
868 		(void) strlcat(subname, locality, SUBNAMESIZ);
869 	}
870 
871 	if (org != NULL) {
872 		(void) strlcat(subname, ", O=", SUBNAMESIZ);
873 		(void) strlcat(subname, org, SUBNAMESIZ);
874 	}
875 
876 	if (unit != NULL) {
877 		(void) strlcat(subname, ", OU=", SUBNAMESIZ);
878 		(void) strlcat(subname, unit, SUBNAMESIZ);
879 	}
880 
881 	if (name != NULL) {
882 		(void) strlcat(subname, ", CN=", SUBNAMESIZ);
883 		(void) strlcat(subname, name, SUBNAMESIZ);
884 	}
885 
886 	if (email != NULL) {
887 		(void) strlcat(subname, ", E=", SUBNAMESIZ);
888 		(void) strlcat(subname, email, SUBNAMESIZ);
889 	}
890 
891 out:
892 	if (country)
893 		free(country);
894 	if (state)
895 		free(state);
896 	if (locality)
897 		free(locality);
898 	if (org)
899 		free(org);
900 	if (unit)
901 		free(unit);
902 	if (name)
903 		free(name);
904 	if (email)
905 		free(email);
906 
907 	if (subname == NULL)
908 		return (-1);
909 	else {
910 		*result = subname;
911 		return (0);
912 	}
913 }
914 
915 /*
916  * Parse a string of KeyUsage values and convert
917  * them to the correct KU Bits.
918  * The field may be marked "critical" by prepending
919  * "critical:" to the list.
920  * EX:  critical:digitialSignature,keyEncipherment
921  */
922 KMF_RETURN
923 verify_keyusage(char *kustr, uint16_t *kubits, int *critical)
924 {
925 	KMF_RETURN ret = KMF_OK;
926 	uint16_t kuval;
927 	char *k;
928 
929 	*kubits = 0;
930 	if (kustr == NULL || !strlen(kustr))
931 		return (KMF_ERR_BAD_PARAMETER);
932 
933 	/* Check to see if this is critical */
934 	if (!strncasecmp(kustr, "critical:", strlen("critical:"))) {
935 		*critical = TRUE;
936 		kustr += strlen("critical:");
937 	} else {
938 		*critical = FALSE;
939 	}
940 
941 	k = strtok(kustr, ",");
942 	while (k != NULL) {
943 		kuval = kmf_string_to_ku(k);
944 		if (kuval == 0) {
945 			*kubits = 0;
946 			return (KMF_ERR_BAD_PARAMETER);
947 		}
948 		*kubits |= kuval;
949 		k = strtok(NULL, ",");
950 	}
951 
952 	return (ret);
953 }
954 
955 /*
956  * Verify the alternate subject label is real or invalid.
957  *
958  * The field may be marked "critical" by prepending
959  * "critical:" to the list.
960  * EX:  "critical:IP=1.2.3.4"
961  */
962 KMF_RETURN
963 verify_altname(char *arg, KMF_GENERALNAMECHOICES *type, int *critical)
964 {
965 	char *p;
966 	KMF_RETURN rv = KMF_OK;
967 
968 	/* Check to see if this is critical */
969 	if (!strncasecmp(arg, "critical:", strlen("critical:"))) {
970 		*critical = TRUE;
971 		arg += strlen("critical:");
972 	} else {
973 		*critical = FALSE;
974 	}
975 
976 	/* Make sure there is an "=" sign */
977 	p = strchr(arg, '=');
978 	if (p == NULL)
979 		return (KMF_ERR_BAD_PARAMETER);
980 
981 	p[0] = '\0';
982 
983 	if (strcmp(arg, "IP") == 0)
984 		*type = GENNAME_IPADDRESS;
985 	else if (strcmp(arg, "DNS") == 0)
986 		*type = GENNAME_DNSNAME;
987 	else if (strcmp(arg, "EMAIL") == 0)
988 		*type = GENNAME_RFC822NAME;
989 	else if (strcmp(arg, "URI") == 0)
990 		*type = GENNAME_URI;
991 	else if (strcmp(arg, "DN") == 0)
992 		*type = GENNAME_DIRECTORYNAME;
993 	else if (strcmp(arg, "RID") == 0)
994 		*type = GENNAME_REGISTEREDID;
995 	else
996 		rv = KMF_ERR_BAD_PARAMETER;
997 
998 	p[0] = '=';
999 
1000 	return (rv);
1001 }
1002 
1003 int
1004 get_token_password(KMF_KEYSTORE_TYPE kstype,
1005 	char *token_spec, KMF_CREDENTIAL *cred)
1006 {
1007 	char	prompt[1024];
1008 	char	*p = NULL;
1009 
1010 	if (kstype == KMF_KEYSTORE_PK11TOKEN) {
1011 		p = strchr(token_spec, ':');
1012 		if (p != NULL)
1013 		*p = 0;
1014 	}
1015 	/*
1016 	 * Login to the token first.
1017 	 */
1018 	(void) snprintf(prompt, sizeof (prompt),
1019 	    gettext(DEFAULT_TOKEN_PROMPT), token_spec);
1020 
1021 	if (get_pin(prompt, NULL, (uchar_t **)&cred->cred,
1022 	    (ulong_t *)&cred->credlen) != CKR_OK) {
1023 		cred->cred = NULL;
1024 		cred->credlen = 0;
1025 	}
1026 
1027 	if (kstype == KMF_KEYSTORE_PK11TOKEN && p != NULL)
1028 		*p = ':';
1029 	return (KMF_OK);
1030 }
1031 
1032 KMF_RETURN
1033 verify_file(char *filename)
1034 {
1035 	KMF_RETURN ret = KMF_OK;
1036 	int fd;
1037 
1038 	/*
1039 	 * Attempt to open with  the EXCL flag so that if
1040 	 * it already exists, the open will fail.  It will
1041 	 * also fail if the file cannot be created due to
1042 	 * permissions on the parent directory, or if the
1043 	 * parent directory itself does not exist.
1044 	 */
1045 	fd = open(filename, O_CREAT | O_EXCL, 0600);
1046 	if (fd == -1)
1047 		return (KMF_ERR_OPEN_FILE);
1048 
1049 	/* If we were able to create it, delete it. */
1050 	(void) close(fd);
1051 	(void) unlink(filename);
1052 
1053 	return (ret);
1054 }
1055 
1056 void
1057 display_error(void *handle, KMF_RETURN errcode, char *prefix)
1058 {
1059 	KMF_RETURN rv1, rv2;
1060 	char *plugin_errmsg = NULL;
1061 	char *kmf_errmsg = NULL;
1062 
1063 	rv1 = kmf_get_plugin_error_str(handle, &plugin_errmsg);
1064 	rv2 = kmf_get_kmf_error_str(errcode, &kmf_errmsg);
1065 
1066 	cryptoerror(LOG_STDERR, "%s:", prefix);
1067 	if (rv1 == KMF_OK && plugin_errmsg) {
1068 		cryptoerror(LOG_STDERR, gettext("keystore error: %s"),
1069 		    plugin_errmsg);
1070 		kmf_free_str(plugin_errmsg);
1071 	}
1072 
1073 	if (rv2 == KMF_OK && kmf_errmsg) {
1074 		cryptoerror(LOG_STDERR, gettext("libkmf error: %s"),
1075 		    kmf_errmsg);
1076 		kmf_free_str(kmf_errmsg);
1077 	}
1078 
1079 	if (rv1 != KMF_OK && rv2 != KMF_OK)
1080 		cryptoerror(LOG_STDERR, gettext("<unknown error>\n"));
1081 
1082 }
1083