common.c revision 56664548661c43ae04de4a32bce3510ed36aeaf9
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* 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.
*/
static CK_RV
init_pkcs11(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_pkcs11()) != 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_SHA1WithRSA;
*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_SHA1WithRSA;
*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 temptoken[32];
char *p = NULL;
char *t = NULL;
int len;
(void) memset(temptoken, 0, sizeof (temptoken));
if (kstype == KMF_KEYSTORE_PK11TOKEN) {
p = strchr(token_spec, ':');
if (p != NULL)
*p = 0;
}
len = strlen(token_spec);
if (len > sizeof (temptoken))
len = sizeof (temptoken);
(void) strncpy(temptoken, token_spec, len);
/*
* Strip trailing whitespace
*/
t = temptoken + (len - 1);
while (isspace(*t) && t >= temptoken) {
*t = 0x00;
t--;
}
/*
* Login to the token first.
*/
(void) snprintf(prompt, sizeof (prompt),
gettext(DEFAULT_TOKEN_PROMPT), temptoken);
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);
free(ekus);
}
}
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);
ekus = calloc(sizeof (EKU_LIST), 1);
if (ekus == NULL)
return (KMF_ERR_MEMORY);
/*
* 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);
}
KMF_RETURN
token_auth_needed(KMF_HANDLE_T handle, char *tokenlabel, int *auth)
{
CK_TOKEN_INFO info;
CK_SLOT_ID slot;
CK_RV ckrv;
KMF_RETURN rv;
*auth = 0;
rv = kmf_pk11_token_lookup(handle, tokenlabel, &slot);
if (rv != KMF_OK)
return (rv);
ckrv = C_GetTokenInfo(slot, &info);
if (ckrv != KMF_OK)
return (KMF_ERR_INTERNAL);
*auth = (info.flags & CKF_LOGIN_REQUIRED);
return (KMF_OK);
}