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