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