common.c revision 49e212991a3065f7e499a4b29ae8d8eaf33f3135
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% 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 <cryptoutil.h>
#include <security/cryptoki.h>
#include "common.h"
#include "biginteger.h"
/* True and false for attribute templates. */
CK_BBOOL pk_true = B_TRUE;
CK_BBOOL pk_false = B_FALSE;
/* Local status variables. */
static boolean_t initialized = B_FALSE;
static boolean_t session_opened = B_FALSE;
static boolean_t session_writable = 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;
/*
* 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;
cryptodebug("inside init_pk11");
/* 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;
session_writable = B_FALSE;
logged_in = B_FALSE;
/* Initialize PKCS#11 library. */
cryptodebug("calling C_Initialize()");
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)
{
cryptodebug("inside final_pk11");
/* If the library wasn't initialized, nothing to do here. */
if (!initialized)
return;
/* Make sure the sesion is closed first. */
close_sess(sess);
cryptodebug("calling C_Finalize()");
(void) C_Finalize(NULL);
initialized = B_FALSE;
}
/*
* Create a PKCS#11 session on the given slot, and set state information.
* If session is already open, check that the read-only/read-write state
* requested matches that of the session. If it doesn't, make it so.
*/
CK_RV
open_sess(CK_SLOT_ID slot_id, CK_FLAGS sess_flags, CK_SESSION_HANDLE_PTR sess)
{
CK_RV rv = CKR_OK;
cryptodebug("inside open_sess");
/* If the session is already open, check the session flags. */
if (session_opened) {
/*
* If requesting R/W session and it is currently R/O,
* need to close the session and reopen it R/W. The
* other cases are considered acceptable:
* sess_flags current state
* ---------- -------------
* ~CKF_RW_SESSION !session_writable
* ~CKF_RW_SESSION session_writable
* CKF_RW_SESSION session_writable
*/
if ((sess_flags & CKF_RW_SESSION) && !session_writable)
close_sess(*sess);
else
return (CKR_OK);
}
/* Make sure the PKCS#11 is already initialized. */
if (!initialized)
if ((rv = init_pk11()) != CKR_OK)
return (rv);
/* Create a session for subsequent operations. */
cryptodebug("calling C_OpenSession()");
if ((rv = C_OpenSession(slot_id, CKF_SERIAL_SESSION|sess_flags,
NULL, NULL, sess)) != CKR_OK)
return (rv);
session_opened = B_TRUE;
session_writable = (sess_flags & CKF_RW_SESSION) ? B_TRUE : B_FALSE;
return (CKR_OK);
}
/*
* Close PKCS#11 session and reset state variables. Any logins are
* logged out.
*/
void
close_sess(CK_SESSION_HANDLE sess)
{
cryptodebug("inside close_sess");
if (sess == NULL) {
cryptodebug("session handle is null");
return;
}
/* If session is already closed, nothing to do here. */
session_writable = B_FALSE;
if (!session_opened)
return;
/* Make sure user is logged out of token. */
logout_token(sess);
cryptodebug("calling C_CloseSession()");
(void) C_CloseSession(sess);
session_opened = B_FALSE;
}
/*
* Log user into token in given slot. If this first login ever for this
* token, the initial PIN is "changeme", C_Login() will succeed, but all
* PKCS#11 calls following the C_Login() will fail with CKR_PIN_EXPIRED.
*/
CK_RV
login_token(CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR pin, CK_ULONG pinlen,
CK_SESSION_HANDLE_PTR sess)
{
CK_RV rv = CKR_OK;
cryptodebug("inside login_token");
/* If already logged in, nothing to do here. */
if (logged_in)
return (CKR_OK);
/* Make sure we have a session first, assume R/O is enough. */
if (!session_opened)
if ((rv = open_sess(slot_id, CKF_SERIAL_SESSION, sess)) !=
CKR_OK)
return (rv);
/* Log the user into the token. */
cryptodebug("calling C_Login()");
if ((rv = C_Login(*sess, CKU_USER, pin, pinlen)) != CKR_OK) {
cryptodebug("C_Login returns %s", pkcs11_strerror(rv));
return (rv);
}
logged_in = B_TRUE;
return (CKR_OK);
}
/*
* Log user out of token and reset status variable.
*/
void
logout_token(CK_SESSION_HANDLE sess)
{
cryptodebug("inside logout_token");
if (sess == NULL) {
cryptodebug("session handle is null");
return;
}
/* If already logged out, nothing to do here. */
if (!logged_in)
return;
cryptodebug("calling C_Logout()");
(void) C_Logout(sess);
logged_in = B_FALSE;
}
/*
* Shortcut function to get from an uninitialized state to user logged in.
* If the library is already initialized, the session is already opened,
* or the user is already logged in, those steps are skipped and the next
* step is checked.
*/
CK_RV
quick_start(CK_SLOT_ID slot_id, CK_FLAGS sess_flags, CK_UTF8CHAR_PTR pin,
CK_ULONG pinlen, CK_SESSION_HANDLE_PTR sess)
{
CK_RV rv = CKR_OK;
cryptodebug("inside quick_start");
/* Call open_sess() explicitly if R/W session is needed. */
if (sess_flags & CKF_RW_SESSION)
if ((rv = open_sess(slot_id, sess_flags, sess)) != CKR_OK)
return (rv);
if ((rv = login_token(slot_id, pin, pinlen, sess)) != CKR_OK)
return (rv);
return (CKR_OK);
}
/*
* Shortcut function to go from any state to uninitialized PKCS#11 library.
*/
void
quick_finish(CK_SESSION_HANDLE sess)
{
cryptodebug("inside quick_finish");
/* All the needed calls are done implicitly. */
final_pk11(sess);
}
/*
* 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;
cryptodebug("inside get_pin");
/* Prompt user for a PIN. */
if (prompt1 == NULL) {
cryptodebug("no passphrase prompt given");
return (CKR_ARGUMENTS_BAD);
}
if ((phrase1 = getpassphrase(prompt1)) == NULL) {
cryptodebug("getpassphrase() failed");
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) {
cryptodebug("getpassphrase() confirmation failed");
free(save_phrase);
return (CKR_FUNCTION_FAILED);
}
if (strcmp(save_phrase, phrase2) != 0) {
cryptodebug("passphrases do not match");
free(save_phrase);
return (CKR_PIN_INCORRECT);
}
}
*pin = (CK_UTF8CHAR_PTR)save_phrase;
*pinlen = strlen(save_phrase);
return (CKR_OK);
}
/*
* 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];
char *yes = gettext("yes");
char *no = gettext("no");
cryptodebug("inside yesno");
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 */
/* Is it valid input? Return appropriately. */
if (strncasecmp(response, yes, 1) == 0)
return (B_TRUE);
if (strncasecmp(response, no, 1) == 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;
cryptodebug("inside get_token_slots");
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.
*/
cryptodebug("calling C_GetSlotList() for slot count");
if ((rv = C_GetSlotList(1, NULL_PTR, &tmp_count)) != CKR_OK)
return (rv);
if (tmp_count == 0) {
cryptodebug("no slots with tokens found");
*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 (;;) {
cryptodebug("calling C_GetSlotList()");
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. */
cryptodebug("number of tokens present increased");
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);
}
/*
* memcmp_pad_max() is a specialized version of memcmp() which
* compares two pieces of data up to a maximum length. If the
* the two data match up the maximum length, they are considered
* matching. Trailing blanks do not cause the match to fail if
* one of the data is shorted.
*
* Examples of matches:
* "one" |
* "one " |
* ^maximum length
*
* "Number One | X" (X is beyond maximum length)
* "Number One " |
* ^maximum length
*
* Examples of mismatches:
* " one"
* "one"
*
* "Number One X|"
* "Number One |"
* ^maximum length
*/
static int
memcmp_pad_max(void *d1, uint_t d1_len, void *d2, uint_t d2_len, uint_t max_sz)
{
uint_t len, extra_len;
char *marker;
/* No point in comparing anything beyond max_sz */
if (d1_len > max_sz)
d1_len = max_sz;
if (d2_len > max_sz)
d2_len = max_sz;
/* Find shorter of the two data. */
if (d1_len <= d2_len) {
len = d1_len;
extra_len = d2_len;
marker = d2;
} else { /* d1_len > d2_len */
len = d2_len;
extra_len = d1_len;
marker = d1;
}
/* Have a match in the shortest length of data? */
if (memcmp(d1, d2, len) != 0)
/* CONSTCOND */
return (!0);
/* If the rest of longer data is nulls or blanks, call it a match. */
while (len < extra_len)
if (!isspace(marker[len++]))
/* CONSTCOND */
return (!0);
return (0);
}
/*
* Locate a token slot whose token matches the label, manufacturer ID, and
* serial number given. Token label must be specified, manufacturer ID and
* serial number are optional. When the token is located, the PIN state
* is also returned to determine if it still has the default PIN.
*/
CK_RV
find_token_slot(char *token_name, char *manuf_id, char *serial_no,
CK_SLOT_ID *slot_id, CK_FLAGS *pin_state)
{
CK_SLOT_ID_PTR slot_list;
CK_TOKEN_INFO token_info;
CK_ULONG slot_count = 0;
int rv = CKR_OK;
int i;
uint_t len, max_sz;
boolean_t tok_match = B_FALSE,
man_match = B_FALSE,
ser_match = B_FALSE;
cryptodebug("inside find_token_slot");
if (token_name == NULL)
return (CKR_ARGUMENTS_BAD);
/* Get a list of all slots with tokens present. */
if ((rv = get_token_slots(&slot_list, &slot_count)) != CKR_OK)
return (rv);
/* If there are no such slots, the desired token won't be found. */
if (slot_count == 0)
return (CKR_TOKEN_NOT_PRESENT);
/* Search the slot list for the token. */
for (i = 0; i < slot_count; i++) {
cryptodebug("calling C_GetTokenInfo()");
if ((rv = C_GetTokenInfo(slot_list[i], &token_info)) !=
CKR_OK) {
cryptodebug("token in slot %d returns %s", i,
pkcs11_strerror(rv));
continue;
}
/* See if the token label matches. */
len = strlen(token_name);
max_sz = sizeof (token_info.label);
if (memcmp_pad_max(&(token_info.label), max_sz, token_name, len,
max_sz) == 0)
tok_match = B_TRUE;
/*
* If manufacturer id was given, see if it actually matches.
* If no manufacturer id was given, assume match is true.
*/
if (manuf_id) {
len = strlen(manuf_id);
max_sz = sizeof ((char *)(token_info.manufacturerID));
if (memcmp_pad_max(&(token_info.manufacturerID), max_sz,
manuf_id, len, max_sz) == 0)
man_match = B_TRUE;
} else
man_match = B_TRUE;
/*
* If serial number was given, see if it actually matches.
* If no serial number was given, assume match is true.
*/
if (serial_no) {
len = strlen(serial_no);
max_sz = sizeof ((char *)(token_info.serialNumber));
if (memcmp_pad_max(&(token_info.serialNumber), max_sz,
serial_no, len, max_sz) == 0)
ser_match = B_TRUE;
} else
ser_match = B_TRUE;
cryptodebug("slot %d:", i);
cryptodebug("\tlabel = \"%.32s\"%s", token_info.label,
tok_match ? " match" : "");
cryptodebug("\tmanuf = \"%.32s\"%s", token_info.manufacturerID,
man_match ? " match" : "");
cryptodebug("\tserno = \"%.16s\"%s", token_info.serialNumber,
ser_match ? " match" : "");
cryptodebug("\tmodel = \"%.16s\"", token_info.model);
cryptodebug("\tCKF_USER_PIN_INITIALIZED = %s",
(token_info.flags & CKF_USER_PIN_INITIALIZED) ?
"true" : "false");
cryptodebug("\tCKF_USER_PIN_TO_BE_CHANGED = %s",
(token_info.flags & CKF_USER_PIN_TO_BE_CHANGED) ?
"true" : "false");
if (tok_match && man_match && ser_match)
break; /* found it! */
}
/* Scanned the whole list without finding the token. */
if (i == slot_count) {
cryptodebug("token not found");
free(slot_list);
return (CKR_TOKEN_NOT_PRESENT);
}
/* Return slot id where token was found and its PIN state. */
cryptodebug("token found at slot %d", i);
*slot_id = slot_list[i];
*pin_state = (token_info.flags & CKF_USER_PIN_TO_BE_CHANGED);
free(slot_list);
return (CKR_OK);
}
/*
* Returns pointer to either null-terminator or next unescaped colon. The
* string to be extracted starts at the beginning and goes until one character
* before this pointer. If NULL is returned, the string itself is NULL.
*/
static char *
find_unescaped_colon(char *str)
{
char *end;
if (str == NULL)
return (NULL);
while ((end = strchr(str, ':')) != NULL) {
if (end != str && *(end-1) != '\\')
return (end);
str = end + 1; /* could point to null-terminator */
}
if (end == NULL)
end = strchr(str, '\0');
return (end);
}
/*
* Compresses away any characters escaped with backslash from given string.
* The string is altered in-place. Example, "ab\:\\e" becomes "ab:\e".
*/
static void
unescape_str(char *str)
{
boolean_t escaped = B_FALSE;
char *mark;
if (str == NULL)
return;
for (mark = str; *str != '\0'; str++) {
if (*str != '\\' || escaped == B_TRUE) {
*mark++ = *str;
escaped = B_FALSE;
} else {
escaped = B_TRUE;
}
}
*mark = '\0';
}
/*
* Given a colon-separated token specifier, this functions splits it into
* its label, manufacturer ID (if any), and serial number (if any). Literal
* colons within the label/manuf/serial can be escaped with a backslash.
* Fields can left blank and trailing colons can be omitted, however leading
* colons are required as placeholders. For example, these are equivalent:
* (a) "lbl", "lbl:", "lbl::" (b) "lbl:man", "lbl:man:"
* but these are not:
* (c) "man", ":man" (d) "ser", "::ser"
* Furthermore, the token label is required always.
*
* The buffer containing the token specifier is altered by replacing the
* colons to null-terminators, and pointers returned are pointers into this
* string. No new memory is allocated.
*/
int
parse_token_spec(char *token_spec, char **token_name, char **manuf_id,
char **serial_no)
{
char *mark;
if (token_spec == NULL || *token_spec == '\0') {
cryptodebug("token specifier is empty");
return (-1);
}
*token_name = NULL;
*manuf_id = NULL;
*serial_no = NULL;
/* Token label (required) */
mark = find_unescaped_colon(token_spec);
*token_name = token_spec;
if (*mark != '\0')
*mark++ = '\0'; /* mark points to next field, if any */
unescape_str(*token_name);
if (*(*token_name) == '\0') { /* token label is required */
cryptodebug("no token label found");
return (-1);
}
if (*mark == '\0' || *(mark+1) == '\0') /* no more fields */
return (0);
token_spec = mark;
/* Manufacturer identifier (optional) */
mark = find_unescaped_colon(token_spec);
*manuf_id = token_spec;
if (*mark != '\0')
*mark++ = '\0'; /* mark points to next field, if any */
unescape_str(*manuf_id);
if (*mark == '\0' || *(mark+1) == '\0') /* no more fields */
return (0);
token_spec = mark;
/* Serial number (optional) */
mark = find_unescaped_colon(token_spec);
*serial_no = token_spec;
if (*mark != '\0')
*mark++ = '\0'; /* null-terminate, just in case */
unescape_str(*serial_no);
return (0);
}
/*
* Constructs a fully qualified token name from its label, manufacturer ID
* (if any), and its serial number (if any). Note that the given buf must
* be big enough. Do NOT i18n/l10n.
*
* FULL_NAME_LEN is defined in common.h to be 91 because a fully qualified
* token name adds up this way:
* =32(label) + 32(manuf) + 16(serial) + 4("", ) + 4("", ) + 3("" and nul)
*/
void
full_token_name(char *token_name, char *manuf_id, char *serial_no, char *buf)
{
char *marker = buf;
int n_written = 0;
int space_left = FULL_NAME_LEN;
if (!token_name)
return;
n_written = sprintf(buf, "\"%.32s\"", token_name);
marker += n_written;
space_left -= n_written;
n_written = sprintf(marker, ", \"%.32s\"", manuf_id ? manuf_id : "");
marker += n_written;
space_left -= n_written;
n_written = sprintf(marker, ", \"%.16s\"", serial_no ? serial_no : "");
marker += n_written;
space_left -= n_written;
/* space_left should always be >= 1 */
}
/*
* Find how many token objects with the given label.
*/
CK_RV
find_obj_count(CK_SESSION_HANDLE sess, int obj_type, CK_BYTE *label,
CK_ULONG *count)
{
CK_RV rv = CKR_OK;
CK_ATTRIBUTE attrs[4] = {
{ CKA_TOKEN, &pk_true, sizeof (pk_true) },
{ 0, NULL, 0 },
{ 0, NULL, 0 },
{ 0, NULL, 0 }
};
CK_ULONG num_attrs = sizeof (attrs) / sizeof (CK_ATTRIBUTE);
CK_ULONG cur_attr = 1; /* CKA_TOKEN already set */
CK_OBJECT_CLASS obj_class;
CK_OBJECT_HANDLE tmp_obj;
CK_ULONG obj_count = 0;
cryptodebug("inside find_obj_count");
if (!session_opened || sess == NULL) {
cryptodebug("session handle is null");
return (CKR_SESSION_HANDLE_INVALID);
}
if (label) {
cryptodebug("object label was specified");
attrs[cur_attr].type = CKA_LABEL;
attrs[cur_attr].pValue = label;
attrs[cur_attr].ulValueLen = strlen((char *)label);
cur_attr++;
}
if ((obj_type & PK_PRIVATE_OBJ) && !(obj_type & PK_PUBLIC_OBJ)) {
cryptodebug("only searching for private objects");
attrs[cur_attr].type = CKA_PRIVATE;
attrs[cur_attr].pValue = &pk_true;
attrs[cur_attr].ulValueLen = sizeof (pk_true);
cur_attr++;
}
/*
* If "certs and all keys" is not specified, but at least either
* "certs" or some "keys" is specified, then go into this block.
* If all certs and keys were specified, there's no point in
* putting that fact in the attribute template -- leave that open,
* and all certs and keys will be matched automatically.
* In other words, only if at least one of 0x10,0x20,0x40,0x80
* bits is off, go into this code block.
*
* NOTE: For now, only one of cert or key types is allowed.
* This needs to change in the future.
*/
if ((obj_type & (PK_CERT_OBJ|PK_KEY_OBJ)) != (PK_CERT_OBJ|PK_KEY_OBJ) &&
((obj_type & PK_CERT_OBJ) || (obj_type & PK_KEY_OBJ))) {
if (obj_type & PK_CERT_OBJ) {
cryptodebug("only searching for certificates");
obj_class = CKO_CERTIFICATE;
} else if (obj_type & PK_PRIKEY_OBJ) {
cryptodebug("only searching for private keys");
obj_class = CKO_PRIVATE_KEY;
} else if (obj_type & PK_PUBKEY_OBJ) {
cryptodebug("only searching for public keys");
obj_class = CKO_PUBLIC_KEY;
} else if (obj_type & PK_SECKEY_OBJ) {
cryptodebug("only searching for secret keys");
obj_class = CKO_SECRET_KEY;
}
attrs[cur_attr].type = CKA_CLASS;
attrs[cur_attr].pValue = &obj_class;
attrs[cur_attr].ulValueLen = sizeof (CK_OBJECT_CLASS);
cur_attr++;
}
/*
* This can't happen now. When finding objects is enhanced in the
* future. this could lead to buffer overruns.
*/
if (cur_attr > num_attrs)
cryptodebug("internal error: attr template overrun");
cryptodebug("calling C_FindObjectsInit");
if ((rv = C_FindObjectsInit(sess, attrs, cur_attr)) != CKR_OK)
return (rv);
/* Look for the object, checking if there are more than one. */
cryptodebug("calling C_FindObjects");
for (*count = 0; /* empty */; (*count)++) {
if ((rv = C_FindObjects(sess, &tmp_obj, 1, &obj_count)) !=
CKR_OK)
break;
/* No more found. */
if (obj_count == 0)
break;
}
cryptodebug("%d matching objects found", *count);
cryptodebug("calling C_FindObjectsFinal");
(void) C_FindObjectsFinal(sess);
return (rv);
}
/*
* Find the token object with the given label.
*/
CK_RV
find_objs(CK_SESSION_HANDLE sess, int obj_type, CK_BYTE *label,
CK_OBJECT_HANDLE_PTR *obj, CK_ULONG *count)
{
CK_RV rv = CKR_OK;
CK_ATTRIBUTE attrs[4] = {
{ CKA_TOKEN, &pk_true, sizeof (pk_true) },
{ 0, NULL, 0 },
{ 0, NULL, 0 },
{ 0, NULL, 0 }
};
CK_ULONG num_attrs = sizeof (attrs) / sizeof (CK_ATTRIBUTE);
CK_ULONG cur_attr = 1; /* CKA_TOKEN already set */
CK_OBJECT_CLASS obj_class;
CK_OBJECT_HANDLE tmp_obj;
CK_ULONG obj_count = 0;
int i;
cryptodebug("inside find_obj");
if ((rv = find_obj_count(sess, obj_type, label, count)) != CKR_OK)
return (rv);
if (*count == 0)
return (CKR_OK);
if ((*obj = (CK_OBJECT_HANDLE_PTR) malloc((*count) *
sizeof (CK_OBJECT_HANDLE))) == NULL) {
cryptodebug("no memory for found object");
return (CKR_HOST_MEMORY);
}
if (label) {
cryptodebug("object label was specified");
attrs[cur_attr].type = CKA_LABEL;
attrs[cur_attr].pValue = label;
attrs[cur_attr].ulValueLen = strlen((char *)label);
cur_attr++;
}
if ((obj_type & PK_PRIVATE_OBJ) && !(obj_type & PK_PUBLIC_OBJ)) {
cryptodebug("only searching for private objects");
attrs[cur_attr].type = CKA_PRIVATE;
attrs[cur_attr].pValue = &pk_true;
attrs[cur_attr].ulValueLen = sizeof (pk_true);
cur_attr++;
}
/*
* If "certs and all keys" is not specified, but at least either
* "certs" or some "keys" is specified, then go into this block.
* If all certs and keys were specified, there's no point in
* putting that fact in the attribute template -- leave that open,
* and all certs and keys will be matched automatically.
* In other words, only if at least one of 0x10,0x20,0x40,0x80
* bits is off, go into this code block.
*
* NOTE: For now, only one of cert or key types is allowed.
* This needs to change in the future.
*/
if ((obj_type & (PK_CERT_OBJ|PK_KEY_OBJ)) != (PK_CERT_OBJ|PK_KEY_OBJ) &&
((obj_type & PK_CERT_OBJ) || (obj_type & PK_KEY_OBJ))) {
if (obj_type & PK_CERT_OBJ) {
cryptodebug("only searching for certificates");
obj_class = CKO_CERTIFICATE;
} else if (obj_type & PK_PRIKEY_OBJ) {
cryptodebug("only searching for private keys");
obj_class = CKO_PRIVATE_KEY;
} else if (obj_type & PK_PUBKEY_OBJ) {
cryptodebug("only searching for public keys");
obj_class = CKO_PUBLIC_KEY;
} else if (obj_type & PK_SECKEY_OBJ) {
cryptodebug("only searching for secret keys");
obj_class = CKO_SECRET_KEY;
}
attrs[cur_attr].type = CKA_CLASS;
attrs[cur_attr].pValue = &obj_class;
attrs[cur_attr].ulValueLen = sizeof (CK_OBJECT_CLASS);
cur_attr++;
}
/*
* This can't happen now. When finding objects is enhanced in the
* future. this could lead to buffer overruns.
*/
if (cur_attr > num_attrs)
cryptodebug("internal error: attr template overrun");
cryptodebug("calling C_FindObjectsInit");
if ((rv = C_FindObjectsInit(sess, attrs, cur_attr)) != CKR_OK) {
free(*obj);
return (rv);
}
/*
* Find all the matching objects. The loop goes 1 more beyond
* the number of objects found to determine if any new objects
* were created since the time the object count was done.
*/
cryptodebug("calling C_FindObjects");
for (i = 0; i < (*count) + 1; i++) {
if ((rv = C_FindObjects(sess, &tmp_obj, 1, &obj_count)) !=
CKR_OK)
break;
/* No more found. */
if (obj_count == 0)
break;
/*
* Save the object in the list being created, as long as
* we don't overrun the size of the list.
*/
if (i < *count)
(*obj)[i] = tmp_obj;
else
cryptodebug("number of objects changed since last count");
}
if (rv != CKR_OK) {
free(*obj);
} else {
/*
* There are three cases to handle: (1) fewer objects were
* found than originally counted => change *count to the
* smaller number; (2) the number of objects found matches
* the number originally counted => do nothing; (3) more
* objects found than originally counted => list passed
* in is too small to contain the extra object(s), flag
* that in the debug output but don't change number of
* objects returned. The caller can double-check by
* calling find_obj_count() after this function to make
* sure the numbers match, if desired.
*/
/* Case 1: Fewer objects. */
if (i < *count) {
cryptodebug("%d objects found, expected %d", i, *count);
*count = i;
/* Case 3: More objects. */
} else if (i > *count) {
cryptodebug("at least %d objects found, expected %d",
i, *count);
}
/*
* Case 2: Same number of objects.
*
* else if (i == *count)
* ;
*/
}
cryptodebug("calling C_FindObjectsFinal");
(void) C_FindObjectsFinal(sess);
return (rv);
}
char *
class_str(CK_OBJECT_CLASS class)
{
switch (class) {
case CKO_DATA: return (gettext("data"));
case CKO_CERTIFICATE: return (gettext("certificate"));
case CKO_PUBLIC_KEY: return (gettext("public key"));
case CKO_PRIVATE_KEY: return (gettext("private key"));
case CKO_SECRET_KEY: return (gettext("secret key"));
case CKO_DOMAIN_PARAMETERS: return (gettext("domain parameter"));
default: return (gettext("unknown object"));
}
}
char *
keytype_str(CK_KEY_TYPE keytype)
{
switch (keytype) {
case CKK_RSA: return (gettext("RSA"));
case CKK_DSA: return (gettext("DSA"));
case CKK_DH: return (gettext("Diffie-Hellman"));
case CKK_X9_42_DH: return (gettext("X9.42 Diffie-Hellman"));
case CKK_GENERIC_SECRET: return (gettext("generic"));
case CKK_RC2: return (gettext("RC2"));
case CKK_RC4: return (gettext("RC4"));
case CKK_DES: return (gettext("DES"));
case CKK_DES2: return (gettext("Double-DES"));
case CKK_DES3: return (gettext("Triple-DES"));
case CKK_RC5: return (gettext("RC5"));
case CKK_AES: return (gettext("AES"));
default: return (gettext("typeless"));
}
}
char *
attr_str(CK_ATTRIBUTE_TYPE attrtype)
{
switch (attrtype) {
case CKA_PRIVATE: return (gettext("private"));
case CKA_LOCAL: return (gettext("local"));
case CKA_SENSITIVE: return (gettext("sensitive"));
case CKA_EXTRACTABLE: return (gettext("extractable"));
case CKA_ENCRYPT: return (gettext("encrypt"));
case CKA_DECRYPT: return (gettext("decrypt"));
case CKA_WRAP: return (gettext("wrap"));
case CKA_UNWRAP: return (gettext("unwrap"));
case CKA_SIGN: return (gettext("sign"));
case CKA_SIGN_RECOVER: return (gettext("sign-recover"));
case CKA_VERIFY: return (gettext("verify"));
case CKA_VERIFY_RECOVER: return (gettext("verify-recover"));
case CKA_DERIVE: return (gettext("derive"));
case CKA_ALWAYS_SENSITIVE: return (gettext("always sensitive"));
case CKA_NEVER_EXTRACTABLE: return (gettext("never extractable"));
default: return (gettext("unknown capability"));
}
}
/*
* Convert a byte string into a string of octets formatted like this:
* oo oo oo oo oo ... oo
* where each "oo" is an octet is space separated and in the form:
* [0-f][0-f] if the octet is a non-printable character
* <space><char> if the octet is a printable character
*
* Note: octets_sz must be 3 * str_sz + 1, or at least as long as "blank"
*/
void
octetify(CK_BYTE *str, CK_ULONG str_sz, char *octets, int octets_sz,
boolean_t stop_on_nul, boolean_t do_ascii, int limit, char *indent,
char *blank)
{
char *marker;
int nc;
int newline;
int indent_len;
boolean_t first = B_TRUE;
cryptodebug("inside octetify");
cryptodebug(stop_on_nul ? "stopping on first nul found" :
"continuing to full length of buffer");
cryptodebug(do_ascii ? "using ascii chars where printable" :
"using only hex octets");
cryptodebug("every %d characters indent with \"%s\"\n ", limit, indent);
cryptodebug("return \"%s\" if buffer is null or empty", blank);
/* If string is empty, write as much of the blank string and leave. */
if (str_sz == 0) {
(void) snprintf(octets, octets_sz, "%s", blank);
return;
}
/* If only limit or indent is set, pick default for the other. */
if (limit > 0 && indent == NULL)
indent = "\n";
if (indent != NULL && limit == 0)
limit = 60;
indent_len = strlen(indent);
for (marker = octets, newline = 0, first = B_TRUE;
(stop_on_nul && *str != '\0') ||
(!stop_on_nul && str_sz > 0 && octets_sz > 0);
str++, str_sz--, marker += nc, octets_sz -= nc) {
if (!first) {
if (limit > 0 && ((marker - octets) / limit) >
newline) {
nc = snprintf(marker, indent_len, "%s", indent);
newline++;
continue;
}
nc = sprintf(marker,
((do_ascii && isprint(*str) && !isspace(*str)) ?
"%s%c" : "%s%02x"), (do_ascii ? " " : ":"), *str);
} else {
nc = sprintf(marker,
((do_ascii && isprint(*str) && !isspace(*str)) ?
"%c" : "%02x"), *str);
first = B_FALSE;
}
}
*marker = '\0';
}
/*
* Copies a biginteger_t to a template attribute.
* Should be a macro instead of a function.
*/
void
copy_bigint_to_attr(biginteger_t big, CK_ATTRIBUTE_PTR attr)
{
attr->pValue = big.big_value;
attr->ulValueLen = big.big_value_len;
}
/*
* Copies a string and its length to a template attribute.
* Should be a macro instead of a function.
*/
void
copy_string_to_attr(CK_BYTE *buf, CK_ULONG buflen, CK_ATTRIBUTE_PTR attr)
{
attr->pValue = buf;
attr->ulValueLen = buflen;
}
/*
* Copies a template attribute to a biginteger_t.
* Should be a macro instead of a function.
*/
void
copy_attr_to_bigint(CK_ATTRIBUTE_PTR attr, biginteger_t *big)
{
big->big_value = attr->pValue;
big->big_value_len = attr->ulValueLen;
}
/*
* Copies a template attribute to a string and its length.
* Should be a macro instead of a function.
*/
void
copy_attr_to_string(CK_ATTRIBUTE_PTR attr, CK_BYTE **buf, CK_ULONG *buflen)
{
*buf = attr->pValue;
*buflen = attr->ulValueLen;
}
/*
* Copies a template attribute to a date and its length.
* Should be a macro instead of a function.
*/
void
copy_attr_to_date(CK_ATTRIBUTE_PTR attr, CK_DATE **buf, CK_ULONG *buflen)
{
*buf = (CK_DATE *)attr->pValue;
*buflen = attr->ulValueLen;
}
/*
* 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) {
free(opts_av);
opts_av = NULL;
return (0);
} else
opts_av = (av_opts *)temp;
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;
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++) {
if (strcmp(argv[optind_av], "--") == 0) {
optind_av++;
break;
}
len = strcspn(argv[optind_av], "=");
if (len == opts_av[i].longnm_len && strncmp(argv[optind_av],
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 (argv[optind_av][len] == '=') {
optarg_av = &(argv[optind_av][len+1]);
optind_av++;
return (opts_av[i].shortnm);
}
optarg_av = NULL;
optind_av++;
return ((int)'?');
}
}
return (EOF);
}