diff usr/src/cmd/cmd-crypto/pktool/common.c @ 0:c9caec207d52 b86

Initial porting based on b86
author Koji Uno <koji.uno@sun.com>
date Tue, 02 Jun 2009 18:56:50 +0900
parents
children 1a15d5aaf794
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-crypto/pktool/common.c	Tue Jun 02 18:56:50 2009 +0900
@@ -0,0 +1,1176 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"@(#)common.c	1.10	08/02/15 SMI"
+
+/*
+ * This file contains the functions that are shared among
+ * the various services this tool will ultimately provide.
+ * The functions in this file return PKCS#11 CK_RV errors.
+ * Only one session and one login per token is supported
+ * at this time.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <tzfile.h>
+#include <cryptoutil.h>
+#include <security/cryptoki.h>
+#include <kmfapi.h>
+
+#include "common.h"
+
+/* Local status variables. */
+static boolean_t	initialized = B_FALSE;
+static boolean_t	session_opened = B_FALSE;
+static boolean_t	logged_in = B_FALSE;
+
+/* Supporting structures and global variables for getopt_av(). */
+typedef struct	av_opts_s {
+	int		shortnm;	/* short name character */
+	char		*longnm;	/* long name string, NOT terminated */
+	int		longnm_len;	/* length of long name string */
+	boolean_t	has_arg;	/* takes optional argument */
+} av_opts;
+static av_opts		*opts_av = NULL;
+static const char	*_save_optstr = NULL;
+static int		_save_numopts = 0;
+
+int			optind_av = 1;
+char			*optarg_av = NULL;
+
+static void close_sess(CK_SESSION_HANDLE);
+static void logout_token(CK_SESSION_HANDLE);
+
+/*
+ * Perform PKCS#11 setup here.  Currently only C_Initialize is required,
+ * along with setting/resetting state variables.
+ */
+CK_RV
+init_pk11(void)
+{
+	CK_RV		rv = CKR_OK;
+
+	/* If C_Initialize() already called, nothing to do here. */
+	if (initialized == B_TRUE)
+		return (CKR_OK);
+
+	/* Reset state variables because C_Initialize() not yet done. */
+	session_opened = B_FALSE;
+	logged_in = B_FALSE;
+
+	/* Initialize PKCS#11 library. */
+	if ((rv = C_Initialize(NULL_PTR)) != CKR_OK &&
+	    rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) {
+		return (rv);
+	}
+
+	initialized = B_TRUE;
+	return (CKR_OK);
+}
+
+/*
+ * Finalize PKCS#11 library and reset state variables.  Open sessions,
+ * if any, are closed, and thereby any logins are logged out also.
+ */
+void
+final_pk11(CK_SESSION_HANDLE sess)
+{
+
+	/* If the library wasn't initialized, nothing to do here. */
+	if (!initialized)
+		return;
+
+	/* Make sure the sesion is closed first. */
+	close_sess(sess);
+
+	(void) C_Finalize(NULL);
+	initialized = B_FALSE;
+}
+
+/*
+ * Close PKCS#11 session and reset state variables.  Any logins are
+ * logged out.
+ */
+static void
+close_sess(CK_SESSION_HANDLE sess)
+{
+
+	if (sess == NULL) {
+		return;
+	}
+
+	/* If session is already closed, nothing to do here. */
+	if (!session_opened)
+		return;
+
+	/* Make sure user is logged out of token. */
+	logout_token(sess);
+
+	(void) C_CloseSession(sess);
+	session_opened = B_FALSE;
+}
+
+/*
+ * Log user out of token and reset status variable.
+ */
+static void
+logout_token(CK_SESSION_HANDLE sess)
+{
+
+	if (sess == NULL) {
+		return;
+	}
+
+	/* If already logged out, nothing to do here. */
+	if (!logged_in)
+		return;
+
+	(void) C_Logout(sess);
+	logged_in = B_FALSE;
+}
+
+/*
+ * Gets PIN from user.  Caller needs to free the returned PIN when done.
+ * If two prompts are given, the PIN is confirmed with second prompt.
+ * Note that getphassphrase() may return data in static memory area.
+ */
+CK_RV
+get_pin(char *prompt1, char *prompt2, CK_UTF8CHAR_PTR *pin, CK_ULONG *pinlen)
+{
+	char *save_phrase, *phrase1, *phrase2;
+
+	/* Prompt user for a PIN. */
+	if (prompt1 == NULL) {
+		return (CKR_ARGUMENTS_BAD);
+	}
+	if ((phrase1 = getpassphrase(prompt1)) == NULL) {
+		return (CKR_FUNCTION_FAILED);
+	}
+
+	/* Duplicate 1st PIN in separate chunk of memory. */
+	if ((save_phrase = strdup(phrase1)) == NULL)
+		return (CKR_HOST_MEMORY);
+
+	/* If second prompt given, PIN confirmation is requested. */
+	if (prompt2 != NULL) {
+		if ((phrase2 = getpassphrase(prompt2)) == NULL) {
+			free(save_phrase);
+			return (CKR_FUNCTION_FAILED);
+		}
+		if (strcmp(save_phrase, phrase2) != 0) {
+			free(save_phrase);
+			return (CKR_PIN_INCORRECT);
+		}
+	}
+
+	*pin = (CK_UTF8CHAR_PTR)save_phrase;
+	*pinlen = strlen(save_phrase);
+	return (CKR_OK);
+}
+
+int
+yn_to_int(char *ynstr)
+{
+	char *y = gettext("yes");
+	char *n = gettext("no");
+	if (ynstr == NULL)
+		return (-1);
+
+	if (strncasecmp(ynstr, y, 1) == 0)
+		return (1);
+	else if (strncasecmp(ynstr, n, 1) == 0)
+		return (0);
+	else
+		return (-1);
+}
+
+/*
+ * Gets yes/no response from user.  If either no prompt is supplied, a
+ * default prompt is used.  If not message for invalid input is supplied,
+ * a default will not be provided.  If the user provides no response,
+ * the input default B_TRUE == yes, B_FALSE == no is returned.
+ * Otherwise, B_TRUE is returned for yes, and B_FALSE for no.
+ */
+boolean_t
+yesno(char *prompt, char *invalid, boolean_t dflt)
+{
+	char	*response, buf[1024];
+	int	ans;
+
+	if (prompt == NULL)
+		prompt = gettext("Enter (y)es or (n)o? ");
+
+	for (;;) {
+		/* Prompt user. */
+		(void) printf("%s", prompt);
+		(void) fflush(stdout);
+
+		/* Get the response. */
+		if ((response = fgets(buf, sizeof (buf), stdin)) == NULL)
+			break;		/* go to default response */
+
+		/* Skip any leading white space. */
+		while (isspace(*response))
+			response++;
+		if (*response == '\0')
+			break;		/* go to default response */
+
+		ans = yn_to_int(response);
+		if (ans == 1)
+			return (B_TRUE);
+		else if (ans == 0)
+			return (B_FALSE);
+
+		/* Indicate invalid input, and try again. */
+		if (invalid != NULL)
+			(void) printf("%s", invalid);
+	}
+	return (dflt);
+}
+
+/*
+ * Gets the list of slots which have tokens in them.  Keeps adjusting
+ * the size of the slot list buffer until the call is successful or an
+ * irrecoverable error occurs.
+ */
+CK_RV
+get_token_slots(CK_SLOT_ID_PTR *slot_list, CK_ULONG *slot_count)
+{
+	CK_ULONG	tmp_count = 0;
+	CK_SLOT_ID_PTR	tmp_list = NULL_PTR, tmp2_list = NULL_PTR;
+	int		rv = CKR_OK;
+
+	if (!initialized)
+		if ((rv = init_pk11()) != CKR_OK)
+			return (rv);
+
+	/*
+	 * Get the slot count first because we don't know how many
+	 * slots there are and how many of those slots even have tokens.
+	 * Don't specify an arbitrary buffer size for the slot list;
+	 * it may be too small (see section 11.5 of PKCS#11 spec).
+	 * Also select only those slots that have tokens in them,
+	 * because this tool has no need to know about empty slots.
+	 */
+	if ((rv = C_GetSlotList(1, NULL_PTR, &tmp_count)) != CKR_OK)
+		return (rv);
+
+	if (tmp_count == 0) {
+		*slot_list = NULL_PTR;
+		*slot_count = 0;
+		return (CKR_OK);
+	}
+
+	/* Allocate initial space for the slot list. */
+	if ((tmp_list = (CK_SLOT_ID_PTR) malloc(tmp_count *
+	    sizeof (CK_SLOT_ID))) == NULL)
+		return (CKR_HOST_MEMORY);
+
+	/* Then get the slot list itself. */
+	for (;;) {
+		if ((rv = C_GetSlotList(1, tmp_list, &tmp_count)) == CKR_OK) {
+			*slot_list = tmp_list;
+			*slot_count = tmp_count;
+			break;
+		}
+
+		if (rv != CKR_BUFFER_TOO_SMALL) {
+			free(tmp_list);
+			break;
+		}
+
+		/* If the number of slots grew, try again. */
+		if ((tmp2_list = (CK_SLOT_ID_PTR) realloc(tmp_list,
+		    tmp_count * sizeof (CK_SLOT_ID))) == NULL) {
+			free(tmp_list);
+			rv = CKR_HOST_MEMORY;
+			break;
+		}
+		tmp_list = tmp2_list;
+	}
+
+	return (rv);
+}
+
+/*
+ * Breaks out the getopt-style option string into a structure that can be
+ * traversed later for calls to getopt_av().  Option string is NOT altered,
+ * but the struct fields point to locations within option string.
+ */
+static int
+populate_opts(char *optstring)
+{
+	int		i;
+	av_opts		*temp;
+	char		*marker;
+
+	if (optstring == NULL || *optstring == '\0')
+		return (0);
+
+	/*
+	 * This tries to imitate getopt(3c) Each option must conform to:
+	 * <short name char> [ ':' ] [ '(' <long name string> ')' ]
+	 * If long name is missing, the short name is used for long name.
+	 */
+	for (i = 0; *optstring != '\0'; i++) {
+		if ((temp = (av_opts *)((i == 0) ? malloc(sizeof (av_opts)) :
+		    realloc(opts_av, (i+1) * sizeof (av_opts)))) == NULL) {
+			if (opts_av != NULL)
+				free(opts_av);
+			opts_av = NULL;
+			return (0);
+		} else {
+			opts_av = (av_opts *)temp;
+		}
+
+		(void) memset(&opts_av[i], 0, sizeof (av_opts));
+		marker = optstring;		/* may need optstring later */
+
+		opts_av[i].shortnm = *marker++;	/* set short name */
+
+		if (*marker == ':') {		/* check for opt arg */
+			marker++;
+			opts_av[i].has_arg = B_TRUE;
+		}
+
+		if (*marker == '(') {		/* check and set long name */
+			marker++;
+			opts_av[i].longnm = marker;
+			opts_av[i].longnm_len = strcspn(marker, ")");
+			optstring = marker + opts_av[i].longnm_len + 1;
+		} else {
+			/* use short name option character */
+			opts_av[i].longnm = optstring;
+			opts_av[i].longnm_len = 1;
+			optstring = marker;
+		}
+	}
+
+	return (i);
+}
+
+/*
+ * getopt_av() is very similar to getopt(3c) in that the takes an option
+ * string, compares command line arguments for matches, and returns a single
+ * letter option when a match is found.  However, getopt_av() differs from
+ * getopt(3c) by requiring that only longname options and values be found
+ * on the command line and all leading dashes are omitted.  In other words,
+ * it tries to enforce only longname "option=value" arguments on the command
+ * line.  Boolean options are not allowed either.
+ */
+int
+getopt_av(int argc, char * const *argv, const char *optstring)
+{
+	int	i;
+	int	len;
+	char   *cur_option;
+
+	if (optind_av >= argc)
+		return (EOF);
+
+	/* First time or when optstring changes from previous one */
+	if (_save_optstr != optstring) {
+		if (opts_av != NULL)
+			free(opts_av);
+		opts_av = NULL;
+		_save_optstr = optstring;
+		_save_numopts = populate_opts((char *)optstring);
+	}
+
+	for (i = 0; i < _save_numopts; i++) {
+		cur_option = argv[optind_av];
+
+		if (strcmp(cur_option, "--") == 0) {
+			optind_av++;
+			break;
+		}
+
+		if (cur_option[0] == '-' && strlen(cur_option) == 2) {
+			len = 1;
+			cur_option++; /* remove "-" */
+		} else {
+			len = strcspn(cur_option, "=");
+		}
+
+		if (len == opts_av[i].longnm_len && strncmp(cur_option,
+		    opts_av[i].longnm, opts_av[i].longnm_len) == 0) {
+			/* matched */
+			if (!opts_av[i].has_arg) {
+				optind_av++;
+				return (opts_av[i].shortnm);
+			}
+
+			/* needs optarg */
+			if (cur_option[len] == '=') {
+				optarg_av = &(cur_option[len+1]);
+				optind_av++;
+				return (opts_av[i].shortnm);
+			}
+
+			optarg_av = NULL;
+			optind_av++;
+			return ((int)'?');
+		}
+	}
+
+	return (EOF);
+}
+
+KMF_KEYSTORE_TYPE
+KS2Int(char *keystore_str)
+{
+	if (keystore_str == NULL)
+		return (0);
+	if (strcasecmp(keystore_str, "pkcs11") == 0)
+		return (KMF_KEYSTORE_PK11TOKEN);
+	else if (strcasecmp(keystore_str, "nss") == 0)
+		return (KMF_KEYSTORE_NSS);
+	else if (strcasecmp(keystore_str, "file") == 0)
+		return (KMF_KEYSTORE_OPENSSL);
+	else
+		return (0);
+}
+
+
+int
+Str2KeyType(char *algm, KMF_KEY_ALG *ktype, KMF_ALGORITHM_INDEX *sigAlg)
+{
+	if (algm == NULL) {
+		*sigAlg = KMF_ALGID_MD5WithRSA;
+		*ktype = KMF_RSA;
+	} else if (strcasecmp(algm, "DSA") == 0) {
+		*sigAlg = KMF_ALGID_SHA1WithDSA;
+		*ktype = KMF_DSA;
+	} else if (strcasecmp(algm, "RSA") == 0) {
+		*sigAlg = KMF_ALGID_MD5WithRSA;
+		*ktype = KMF_RSA;
+	} else {
+		return (-1);
+	}
+	return (0);
+}
+
+int
+Str2SymKeyType(char *algm, KMF_KEY_ALG *ktype)
+{
+	if (algm == NULL)
+		*ktype = KMF_AES;
+	else if (strcasecmp(algm, "aes") == 0)
+		*ktype = KMF_AES;
+	else if (strcasecmp(algm, "arcfour") == 0)
+		*ktype = KMF_RC4;
+	else if (strcasecmp(algm, "des") == 0)
+		*ktype = KMF_DES;
+	else if (strcasecmp(algm, "3des") == 0)
+		*ktype = KMF_DES3;
+	else if (strcasecmp(algm, "generic") == 0)
+		*ktype = KMF_GENERIC_SECRET;
+	else
+		return (-1);
+
+	return (0);
+}
+
+int
+Str2Lifetime(char *ltimestr, uint32_t *ltime)
+{
+	int num;
+	char timetok[6];
+
+	if (ltimestr == NULL || strlen(ltimestr) == 0) {
+		/* default to 1 year lifetime */
+		*ltime = SECSPERDAY * DAYSPERNYEAR;
+		return (0);
+	}
+
+	(void) memset(timetok, 0, sizeof (timetok));
+	if (sscanf(ltimestr, "%d-%06s", &num, timetok) != 2)
+		return (-1);
+
+	if (strcasecmp(timetok, "day") == 0||
+	    strcasecmp(timetok, "days") == 0) {
+		*ltime = num * SECSPERDAY;
+	} else if (strcasecmp(timetok, "hour") == 0||
+	    strcasecmp(timetok, "hours") == 0) {
+		*ltime = num * SECSPERHOUR;
+	} else if (strcasecmp(timetok, "year") == 0 ||
+	    strcasecmp(timetok, "years") == 0) {
+		*ltime = num * SECSPERDAY * DAYSPERNYEAR;
+	} else {
+		*ltime = 0;
+		return (-1);
+	}
+
+	return (0);
+}
+
+int
+OT2Int(char *objclass)
+{
+	char *c = NULL;
+	int retval = 0;
+
+	if (objclass == NULL)
+		return (-1);
+
+	c = strchr(objclass, ':');
+	if (c != NULL) {
+		if (strcasecmp(c, ":private") == 0)
+			retval = PK_PRIVATE_OBJ;
+		else if (strcasecmp(c, ":public") == 0)
+			retval = PK_PUBLIC_OBJ;
+		else if (strcasecmp(c, ":both") == 0)
+			retval = PK_PRIVATE_OBJ | PK_PUBLIC_OBJ;
+		else /* unrecognized option */
+			return (-1);
+
+		*c = '\0';
+	}
+
+	if (strcasecmp(objclass, "public") == 0) {
+		if (retval)
+			return (-1);
+		return (retval | PK_PUBLIC_OBJ | PK_CERT_OBJ | PK_PUBKEY_OBJ);
+	} else if (strcasecmp(objclass, "private") == 0) {
+		if (retval)
+			return (-1);
+		return (retval | PK_PRIKEY_OBJ | PK_PRIVATE_OBJ);
+	} else if (strcasecmp(objclass, "both") == 0) {
+		if (retval)
+			return (-1);
+		return (PK_KEY_OBJ | PK_PUBLIC_OBJ | PK_PRIVATE_OBJ);
+	} else if (strcasecmp(objclass, "cert") == 0) {
+		return (retval | PK_CERT_OBJ);
+	} else if (strcasecmp(objclass, "key") == 0) {
+		if (retval == 0) /* return all keys */
+			return (retval | PK_KEY_OBJ);
+		else if (retval == (PK_PRIVATE_OBJ | PK_PUBLIC_OBJ))
+			/* return all keys */
+			return (retval | PK_KEY_OBJ);
+		else if (retval & PK_PUBLIC_OBJ)
+			/* Only return public keys */
+			return (retval | PK_PUBKEY_OBJ);
+		else if (retval & PK_PRIVATE_OBJ)
+			/* Only return private keys */
+			return (retval | PK_PRIKEY_OBJ);
+	} else if (strcasecmp(objclass, "crl") == 0) {
+		if (retval)
+			return (-1);
+		return (retval | PK_CRL_OBJ);
+	}
+
+	if (retval == 0) /* No matches found */
+		retval = -1;
+	return (retval);
+}
+
+KMF_ENCODE_FORMAT
+Str2Format(char *formstr)
+{
+	if (formstr == NULL || strcasecmp(formstr, "der") == 0)
+		return (KMF_FORMAT_ASN1);
+	if (strcasecmp(formstr, "pem") == 0)
+		return (KMF_FORMAT_PEM);
+	if (strcasecmp(formstr, "pkcs12") == 0)
+		return (KMF_FORMAT_PKCS12);
+	if (strcasecmp(formstr, "raw") == 0)
+		return (KMF_FORMAT_RAWKEY);
+
+	return (KMF_FORMAT_UNDEF);
+}
+
+KMF_RETURN
+select_token(void *kmfhandle, char *token, int readonly)
+{
+	KMF_ATTRIBUTE attlist[10];
+	int i = 0;
+	KMF_KEYSTORE_TYPE kstype = KMF_KEYSTORE_PK11TOKEN;
+	KMF_RETURN rv = KMF_OK;
+
+	if (token == NULL)
+		return (KMF_ERR_BAD_PARAMETER);
+
+	kmf_set_attr_at_index(attlist, i,
+	    KMF_KEYSTORE_TYPE_ATTR, &kstype,
+	    sizeof (kstype));
+	i++;
+
+	if (token) {
+		kmf_set_attr_at_index(attlist, i,
+		    KMF_TOKEN_LABEL_ATTR, token,
+		    strlen(token));
+		i++;
+	}
+
+	kmf_set_attr_at_index(attlist, i,
+	    KMF_READONLY_ATTR, &readonly,
+	    sizeof (readonly));
+	i++;
+
+	rv = kmf_configure_keystore(kmfhandle, i, attlist);
+	if (rv == KMF_ERR_TOKEN_SELECTED)
+		rv = KMF_OK;
+	return (rv);
+}
+
+KMF_RETURN
+configure_nss(void *kmfhandle, char *dir, char *prefix)
+{
+	KMF_ATTRIBUTE attlist[10];
+	int i = 0;
+	KMF_KEYSTORE_TYPE kstype = KMF_KEYSTORE_NSS;
+	KMF_RETURN rv = KMF_OK;
+
+	kmf_set_attr_at_index(attlist, i,
+	    KMF_KEYSTORE_TYPE_ATTR, &kstype,
+	    sizeof (kstype));
+	i++;
+
+	if (dir) {
+		kmf_set_attr_at_index(attlist, i,
+		    KMF_DIRPATH_ATTR, dir,
+		    strlen(dir));
+		i++;
+	}
+
+	if (prefix) {
+		kmf_set_attr_at_index(attlist, i,
+		    KMF_CERTPREFIX_ATTR, prefix,
+		    strlen(prefix));
+		i++;
+
+		kmf_set_attr_at_index(attlist, i,
+		    KMF_KEYPREFIX_ATTR, prefix,
+		    strlen(prefix));
+		i++;
+	}
+
+	rv = kmf_configure_keystore(kmfhandle, i, attlist);
+	if (rv == KMF_KEYSTORE_ALREADY_INITIALIZED)
+		rv = KMF_OK;
+
+	return (rv);
+}
+
+KMF_RETURN
+get_pk12_password(KMF_CREDENTIAL *cred)
+{
+	KMF_RETURN rv = KMF_OK;
+	char prompt[1024];
+
+	/*
+	 * Get the password to use for the PK12 encryption.
+	 */
+	(void) strlcpy(prompt,
+	    gettext("Enter password to use for "
+	    "accessing the PKCS12 file: "), sizeof (prompt));
+
+	if (get_pin(prompt, NULL, (uchar_t **)&cred->cred,
+	    (ulong_t *)&cred->credlen) != CKR_OK) {
+		cred->cred = NULL;
+		cred->credlen = 0;
+	}
+
+	return (rv);
+}
+
+#define	FILENAME_PROMPT gettext("Filename:")
+#define	FILENAME_MINLEN	1
+#define	FILENAME_MAXLEN MAXPATHLEN
+
+#define	COUNTRY_PROMPT	gettext("Country Name (2 letter code) [US]:")
+#define	STATE_PROMPT	gettext("State or Province Name (full name) " \
+	"[Some-State]:")
+#define	LOCALITY_PROMPT	gettext("Locality Name (eg, city) []:")
+#define	ORG_PROMPT	gettext("Organization Name (eg, company) []:")
+#define	UNIT_PROMPT	gettext("Organizational Unit Name (eg, section) []:")
+#define	NAME_PROMPT	gettext("Common Name (eg, YOUR name) []:")
+#define	EMAIL_PROMPT	gettext("Email Address []:")
+
+#define	SERNO_PROMPT	gettext("Serial Number (hex value, example: " \
+	"0x01020304):")
+#define	SERNO_MINLEN	3
+#define	SERNO_MAXLEN	42
+
+#define	LABEL_PROMPT	gettext("Enter a label for the certificate:")
+#define	LABEL_MINLEN	1
+#define	LABEL_MAXLEN	1024
+
+#define	COUNTRY_DEFAULT "US"
+#define	STATE_DEFAULT	NULL
+#define	INVALID_INPUT 	gettext("Invalid input; please re-enter ...")
+
+#define	SUBNAMESIZ	1024
+#define	RDN_MIN		1
+#define	RDN_MAX		64
+#define	COUNTRYNAME_MIN	2
+#define	COUNTRYNAME_MAX	2
+
+static char *
+get_input_string(char *prompt, char *default_str, int min_len, int max_len)
+{
+	char buf[1024];
+	char *response = NULL;
+	char *ret = NULL;
+	int len;
+
+	for (;;) {
+		(void) printf("\t%s", prompt);
+		(void) fflush(stdout);
+
+		response = fgets(buf, sizeof (buf), stdin);
+		if (response == NULL) {
+			if (default_str != NULL) {
+				ret = strdup(default_str);
+			}
+			break;
+		}
+
+		/* Skip any leading white space. */
+		while (isspace(*response))
+			response++;
+		if (*response == '\0') {
+			if (default_str != NULL) {
+				ret = strdup(default_str);
+			}
+			break;
+		}
+
+		len = strlen(response);
+		response[len-1] = '\0'; /* get rid of "LF" */
+		len--;
+		if (len >= min_len && len <= max_len) {
+			ret = strdup(response);
+			break;
+		}
+
+		(void) printf("%s\n", INVALID_INPUT);
+
+	}
+
+	return (ret);
+}
+
+int
+get_filename(char *txt, char **result)
+{
+	char prompt[1024];
+	char *fname = NULL;
+
+	(void) snprintf(prompt, sizeof (prompt),
+	    gettext("Enter filename for the %s: "),
+	    txt);
+	fname = get_input_string(prompt, NULL,
+	    FILENAME_MINLEN, FILENAME_MAXLEN);
+	*result = fname;
+	return (0);
+}
+
+int
+get_certlabel(char **result)
+{
+	char *label = NULL;
+
+	label = get_input_string(LABEL_PROMPT, NULL,
+	    LABEL_MINLEN, LABEL_MAXLEN);
+	*result = label;
+	return (0);
+}
+
+int
+get_serial(char **result)
+{
+	char *serial = NULL;
+
+	serial = get_input_string(SERNO_PROMPT, NULL, SERNO_MINLEN,
+	    SERNO_MAXLEN);
+
+	*result = serial;
+	return (0);
+}
+
+int
+get_subname(char **result)
+{
+	char *country = NULL;
+	char *state = NULL;
+	char *locality = NULL;
+	char *org = NULL;
+	char *unit = NULL;
+	char *name = NULL;
+	char *email = NULL;
+	char *subname = NULL;
+
+	(void) printf("Entering following fields for subject (a DN) ...\n");
+	country = get_input_string(COUNTRY_PROMPT, COUNTRY_DEFAULT,
+	    COUNTRYNAME_MIN, COUNTRYNAME_MAX);
+	if (country == NULL)
+		return (-1);
+
+	state = get_input_string(STATE_PROMPT, STATE_DEFAULT,
+	    RDN_MIN, RDN_MAX);
+
+	locality = get_input_string(LOCALITY_PROMPT, NULL, RDN_MIN, RDN_MAX);
+	org = get_input_string(ORG_PROMPT, NULL, RDN_MIN, RDN_MAX);
+	unit = get_input_string(UNIT_PROMPT, NULL, RDN_MIN, RDN_MAX);
+	name = get_input_string(NAME_PROMPT, NULL, RDN_MIN, RDN_MAX);
+	email = get_input_string(EMAIL_PROMPT, NULL, RDN_MIN, RDN_MAX);
+
+	/* Now create a subject name from the input strings */
+	if ((subname = malloc(SUBNAMESIZ)) == NULL)
+		goto out;
+
+	(void) memset(subname, 0, SUBNAMESIZ);
+	(void) strlcpy(subname, "C=", SUBNAMESIZ);
+	(void) strlcat(subname, country, SUBNAMESIZ);
+	if (state != NULL) {
+		(void) strlcat(subname, ", ST=", SUBNAMESIZ);
+		(void) strlcat(subname, state, SUBNAMESIZ);
+	}
+
+	if (locality != NULL) {
+		(void) strlcat(subname, ", L=", SUBNAMESIZ);
+		(void) strlcat(subname, locality, SUBNAMESIZ);
+	}
+
+	if (org != NULL) {
+		(void) strlcat(subname, ", O=", SUBNAMESIZ);
+		(void) strlcat(subname, org, SUBNAMESIZ);
+	}
+
+	if (unit != NULL) {
+		(void) strlcat(subname, ", OU=", SUBNAMESIZ);
+		(void) strlcat(subname, unit, SUBNAMESIZ);
+	}
+
+	if (name != NULL) {
+		(void) strlcat(subname, ", CN=", SUBNAMESIZ);
+		(void) strlcat(subname, name, SUBNAMESIZ);
+	}
+
+	if (email != NULL) {
+		(void) strlcat(subname, ", E=", SUBNAMESIZ);
+		(void) strlcat(subname, email, SUBNAMESIZ);
+	}
+
+out:
+	if (country)
+		free(country);
+	if (state)
+		free(state);
+	if (locality)
+		free(locality);
+	if (org)
+		free(org);
+	if (unit)
+		free(unit);
+	if (name)
+		free(name);
+	if (email)
+		free(email);
+
+	if (subname == NULL)
+		return (-1);
+	else {
+		*result = subname;
+		return (0);
+	}
+}
+
+/*
+ * Parse a string of KeyUsage values and convert
+ * them to the correct KU Bits.
+ * The field may be marked "critical" by prepending
+ * "critical:" to the list.
+ * EX:  critical:digitialSignature,keyEncipherment
+ */
+KMF_RETURN
+verify_keyusage(char *kustr, uint16_t *kubits, int *critical)
+{
+	KMF_RETURN ret = KMF_OK;
+	uint16_t kuval;
+	char *k;
+
+	*kubits = 0;
+	if (kustr == NULL || strlen(kustr) == 0)
+		return (KMF_ERR_BAD_PARAMETER);
+
+	/* Check to see if this is critical */
+	if (strncasecmp(kustr, "critical:", strlen("critical:")) == 0) {
+		*critical = TRUE;
+		kustr += strlen("critical:");
+	} else {
+		*critical = FALSE;
+	}
+
+	k = strtok(kustr, ",");
+	while (k != NULL) {
+		kuval = kmf_string_to_ku(k);
+		if (kuval == 0) {
+			*kubits = 0;
+			return (KMF_ERR_BAD_PARAMETER);
+		}
+		*kubits |= kuval;
+		k = strtok(NULL, ",");
+	}
+
+	return (ret);
+}
+
+/*
+ * Verify the alternate subject label is real or invalid.
+ *
+ * The field may be marked "critical" by prepending
+ * "critical:" to the list.
+ * EX:  "critical:IP=1.2.3.4"
+ */
+KMF_RETURN
+verify_altname(char *arg, KMF_GENERALNAMECHOICES *type, int *critical)
+{
+	char *p;
+	KMF_RETURN rv = KMF_OK;
+
+	/* Check to see if this is critical */
+	if (strncasecmp(arg, "critical:", strlen("critical:")) == 0) {
+		*critical = TRUE;
+		arg += strlen("critical:");
+	} else {
+		*critical = FALSE;
+	}
+
+	/* Make sure there is an "=" sign */
+	p = strchr(arg, '=');
+	if (p == NULL)
+		return (KMF_ERR_BAD_PARAMETER);
+
+	p[0] = '\0';
+
+	if (strcmp(arg, "IP") == 0)
+		*type = GENNAME_IPADDRESS;
+	else if (strcmp(arg, "DNS") == 0)
+		*type = GENNAME_DNSNAME;
+	else if (strcmp(arg, "EMAIL") == 0)
+		*type = GENNAME_RFC822NAME;
+	else if (strcmp(arg, "URI") == 0)
+		*type = GENNAME_URI;
+	else if (strcmp(arg, "DN") == 0)
+		*type = GENNAME_DIRECTORYNAME;
+	else if (strcmp(arg, "RID") == 0)
+		*type = GENNAME_REGISTEREDID;
+	else if (strcmp(arg, "KRB") == 0)
+		*type = GENNAME_KRB5PRINC;
+	else if (strcmp(arg, "UPN") == 0)
+		*type = GENNAME_SCLOGON_UPN;
+	else
+		rv = KMF_ERR_BAD_PARAMETER;
+
+	p[0] = '=';
+
+	return (rv);
+}
+
+int
+get_token_password(KMF_KEYSTORE_TYPE kstype,
+	char *token_spec, KMF_CREDENTIAL *cred)
+{
+	char	prompt[1024];
+	char	*p = NULL;
+
+	if (kstype == KMF_KEYSTORE_PK11TOKEN) {
+		p = strchr(token_spec, ':');
+		if (p != NULL)
+		*p = 0;
+	}
+	/*
+	 * Login to the token first.
+	 */
+	(void) snprintf(prompt, sizeof (prompt),
+	    gettext(DEFAULT_TOKEN_PROMPT), token_spec);
+
+	if (get_pin(prompt, NULL, (uchar_t **)&cred->cred,
+	    (ulong_t *)&cred->credlen) != CKR_OK) {
+		cred->cred = NULL;
+		cred->credlen = 0;
+	}
+
+	if (kstype == KMF_KEYSTORE_PK11TOKEN && p != NULL)
+		*p = ':';
+	return (KMF_OK);
+}
+
+KMF_RETURN
+verify_file(char *filename)
+{
+	KMF_RETURN ret = KMF_OK;
+	int fd;
+
+	/*
+	 * Attempt to open with  the EXCL flag so that if
+	 * it already exists, the open will fail.  It will
+	 * also fail if the file cannot be created due to
+	 * permissions on the parent directory, or if the
+	 * parent directory itself does not exist.
+	 */
+	fd = open(filename, O_CREAT | O_EXCL, 0600);
+	if (fd == -1)
+		return (KMF_ERR_OPEN_FILE);
+
+	/* If we were able to create it, delete it. */
+	(void) close(fd);
+	(void) unlink(filename);
+
+	return (ret);
+}
+
+void
+display_error(void *handle, KMF_RETURN errcode, char *prefix)
+{
+	KMF_RETURN rv1, rv2;
+	char *plugin_errmsg = NULL;
+	char *kmf_errmsg = NULL;
+
+	rv1 = kmf_get_plugin_error_str(handle, &plugin_errmsg);
+	rv2 = kmf_get_kmf_error_str(errcode, &kmf_errmsg);
+
+	cryptoerror(LOG_STDERR, "%s:", prefix);
+	if (rv1 == KMF_OK && plugin_errmsg) {
+		cryptoerror(LOG_STDERR, gettext("keystore error: %s"),
+		    plugin_errmsg);
+		kmf_free_str(plugin_errmsg);
+	}
+
+	if (rv2 == KMF_OK && kmf_errmsg) {
+		cryptoerror(LOG_STDERR, gettext("libkmf error: %s"),
+		    kmf_errmsg);
+		kmf_free_str(kmf_errmsg);
+	}
+
+	if (rv1 != KMF_OK && rv2 != KMF_OK)
+		cryptoerror(LOG_STDERR, gettext("<unknown error>\n"));
+
+}
+
+static KMF_RETURN
+addToEKUList(EKU_LIST *ekus, int critical, KMF_OID *newoid)
+{
+	if (newoid != NULL && ekus != NULL) {
+		ekus->eku_count++;
+
+		ekus->critlist = realloc(ekus->critlist,
+		    ekus->eku_count * sizeof (int));
+		if (ekus->critlist != NULL)
+			ekus->critlist[ekus->eku_count-1] = critical;
+		else
+			return (KMF_ERR_MEMORY);
+
+		ekus->ekulist = realloc(
+		    ekus->ekulist, ekus->eku_count * sizeof (KMF_OID));
+		if (ekus->ekulist != NULL)
+			ekus->ekulist[ekus->eku_count-1] = *newoid;
+		else
+			return (KMF_ERR_MEMORY);
+	}
+	return (KMF_OK);
+}
+
+void
+free_eku_list(EKU_LIST *ekus)
+{
+	if (ekus != NULL && ekus->eku_count > 0) {
+		int i;
+		for (i = 0; i < ekus->eku_count; i++) {
+			kmf_free_data(&ekus->ekulist[i]);
+		}
+		free(ekus->ekulist);
+		free(ekus->critlist);
+	}
+}
+
+static KMF_RETURN
+parse_ekus(char *ekustr, EKU_LIST *ekus)
+{
+	KMF_RETURN rv = KMF_OK;
+	KMF_OID *newoid;
+	int critical;
+
+	if (strncasecmp(ekustr, "critical:",
+	    strlen("critical:")) == 0) {
+		critical = TRUE;
+		ekustr += strlen("critical:");
+	} else {
+		critical = FALSE;
+	}
+	newoid = kmf_ekuname_to_oid(ekustr);
+	if (newoid != NULL) {
+		rv = addToEKUList(ekus, critical, newoid);
+		free(newoid);
+	} else {
+		rv = PK_ERR_USAGE;
+	}
+
+	return (rv);
+}
+
+KMF_RETURN
+verify_ekunames(char *ekuliststr, EKU_LIST **ekulist)
+{
+	KMF_RETURN rv = KMF_OK;
+	char *p;
+	EKU_LIST *ekus = NULL;
+
+	if (ekuliststr == NULL || strlen(ekuliststr) == 0)
+		return (0);
+
+	/*
+	 * The list should be comma separated list of EKU Names.
+	 */
+	p = strtok(ekuliststr, ",");
+
+	/* If no tokens found, then maybe it's just a single EKU value */
+	if (p == NULL) {
+		rv = parse_ekus(ekuliststr, ekus);
+	}
+
+	while (p != NULL) {
+		rv = parse_ekus(p, ekus);
+
+		if (rv != KMF_OK)
+			break;
+		p = strtok(NULL, ",");
+	}
+
+	if (rv != KMF_OK)
+		free_eku_list(ekus);
+	else
+		*ekulist = ekus;
+
+	return (rv);
+}