2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A *
2N/A * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <ctype.h>
2N/A#include <security/cryptoki.h>
2N/A#include <kmfapi.h>
2N/A#include <kmfapiP.h>
2N/A#include <cryptoutil.h>
2N/A
2N/Astatic KMF_RETURN
2N/Akmf_get_token_slots(KMF_HANDLE *handle, CK_SLOT_ID_PTR *slot_list,
2N/A CK_ULONG *slot_count)
2N/A{
2N/A
2N/A KMF_RETURN kmf_rv = KMF_OK;
2N/A CK_RV ck_rv = CKR_OK;
2N/A CK_ULONG tmp_count = 0;
2N/A CK_SLOT_ID_PTR tmp_list = NULL_PTR, tmp2_list = NULL_PTR;
2N/A
2N/A ck_rv = C_GetSlotList(1, NULL_PTR, &tmp_count);
2N/A if (ck_rv == CKR_CRYPTOKI_NOT_INITIALIZED) {
2N/A ck_rv = C_Initialize(NULL);
2N/A if ((ck_rv != CKR_OK) &&
2N/A (ck_rv != CKR_CRYPTOKI_ALREADY_INITIALIZED))
2N/A return (KMF_ERR_UNINITIALIZED);
2N/A if (ck_rv == CKR_CRYPTOKI_ALREADY_INITIALIZED)
2N/A ck_rv = CKR_OK;
2N/A
2N/A ck_rv = C_GetSlotList(1, NULL_PTR, &tmp_count);
2N/A }
2N/A if (ck_rv != CKR_OK) {
2N/A if (handle != NULL) {
2N/A handle->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN;
2N/A handle->lasterr.errcode = ck_rv;
2N/A }
2N/A return (KMF_ERR_INTERNAL);
2N/A }
2N/A
2N/A if (tmp_count == 0) {
2N/A *slot_list = NULL_PTR;
2N/A *slot_count = 0;
2N/A return (KMF_OK);
2N/A }
2N/A
2N/A /* Allocate initial space for the slot list. */
2N/A if ((tmp_list = (CK_SLOT_ID_PTR) malloc(tmp_count *
2N/A sizeof (CK_SLOT_ID))) == NULL)
2N/A return (KMF_ERR_MEMORY);
2N/A
2N/A /* Then get the slot list itself. */
2N/A for (;;) {
2N/A ck_rv = C_GetSlotList(1, tmp_list, &tmp_count);
2N/A if (ck_rv == CKR_OK) {
2N/A *slot_list = tmp_list;
2N/A *slot_count = tmp_count;
2N/A kmf_rv = KMF_OK;
2N/A break;
2N/A }
2N/A
2N/A if (ck_rv != CKR_BUFFER_TOO_SMALL) {
2N/A free(tmp_list);
2N/A if (handle != NULL) {
2N/A handle->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN;
2N/A handle->lasterr.errcode = ck_rv;
2N/A }
2N/A kmf_rv = KMF_ERR_INTERNAL;
2N/A break;
2N/A }
2N/A
2N/A /*
2N/A * If the number of slots grew, try again. This
2N/A * is to be consistent with pktool in ONNV.
2N/A */
2N/A if ((tmp2_list = (CK_SLOT_ID_PTR) realloc(tmp_list,
2N/A tmp_count * sizeof (CK_SLOT_ID))) == NULL) {
2N/A free(tmp_list);
2N/A kmf_rv = KMF_ERR_MEMORY;
2N/A break;
2N/A }
2N/A tmp_list = tmp2_list;
2N/A }
2N/A
2N/A return (kmf_rv);
2N/A}
2N/A
2N/A/*
2N/A * Returns pointer to either null-terminator or next unescaped colon. The
2N/A * string to be extracted starts at the beginning and goes until one character
2N/A * before this pointer. If NULL is returned, the string itself is NULL.
2N/A */
2N/Astatic char *
2N/Afind_unescaped_colon(char *str)
2N/A{
2N/A char *end;
2N/A
2N/A if (str == NULL)
2N/A return (NULL);
2N/A
2N/A while ((end = strchr(str, ':')) != NULL) {
2N/A if (end != str && *(end-1) != '\\')
2N/A return (end);
2N/A str = end + 1; /* could point to null-terminator */
2N/A }
2N/A if (end == NULL)
2N/A end = strchr(str, '\0');
2N/A return (end);
2N/A}
2N/A
2N/A/*
2N/A * Compresses away any characters escaped with backslash from given string.
2N/A * The string is altered in-place. Example, "ab\:\\e" becomes "ab:\e".
2N/A */
2N/Astatic void
2N/Aunescape_str(char *str)
2N/A{
2N/A boolean_t escaped = B_FALSE;
2N/A char *mark;
2N/A
2N/A if (str == NULL)
2N/A return;
2N/A
2N/A for (mark = str; *str != '\0'; str++) {
2N/A if (*str != '\\' || escaped == B_TRUE) {
2N/A *mark++ = *str;
2N/A escaped = B_FALSE;
2N/A } else {
2N/A escaped = B_TRUE;
2N/A }
2N/A }
2N/A *mark = '\0';
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Given a colon-separated token specifier, this functions splits it into
2N/A * its label, manufacturer ID (if any), and serial number (if any). Literal
2N/A * colons within the label/manuf/serial can be escaped with a backslash.
2N/A * Fields can left blank and trailing colons can be omitted, however leading
2N/A * colons are required as placeholders. For example, these are equivalent:
2N/A * (a) "lbl", "lbl:", "lbl::" (b) "lbl:man", "lbl:man:"
2N/A * but these are not:
2N/A * (c) "man", ":man" (d) "ser", "::ser"
2N/A * Furthermore, the token label is required always.
2N/A *
2N/A * The buffer containing the token specifier is altered by replacing the
2N/A * colons to null-terminators, and pointers returned are pointers into this
2N/A * string. No new memory is allocated.
2N/A */
2N/Astatic int
2N/Aparse_token_spec(char *token_spec, char **token_name, char **manuf_id,
2N/A char **serial_no)
2N/A{
2N/A char *mark;
2N/A
2N/A if (token_spec == NULL || *token_spec == '\0') {
2N/A return (-1);
2N/A }
2N/A
2N/A *token_name = NULL;
2N/A *manuf_id = NULL;
2N/A *serial_no = NULL;
2N/A
2N/A /* Token label (required) */
2N/A mark = find_unescaped_colon(token_spec);
2N/A *token_name = token_spec;
2N/A if (*mark != '\0')
2N/A *mark++ = '\0'; /* mark points to next field, if any */
2N/A unescape_str(*token_name);
2N/A
2N/A if (*(*token_name) == '\0') { /* token label is required */
2N/A return (-1);
2N/A }
2N/A
2N/A if (*mark == '\0' || *(mark+1) == '\0') /* no more fields */
2N/A return (0);
2N/A token_spec = mark;
2N/A
2N/A /* Manufacturer identifier (optional) */
2N/A mark = find_unescaped_colon(token_spec);
2N/A *manuf_id = token_spec;
2N/A if ((mark != NULL) && (*mark != '\0'))
2N/A *mark++ = '\0'; /* mark points to next field, if any */
2N/A unescape_str(*manuf_id);
2N/A
2N/A if (*mark == '\0' || *(mark+1) == '\0') /* no more fields */
2N/A return (0);
2N/A token_spec = mark;
2N/A
2N/A /* Serial number (optional) */
2N/A mark = find_unescaped_colon(token_spec);
2N/A *serial_no = token_spec;
2N/A if ((mark != NULL) && (*mark != '\0'))
2N/A *mark++ = '\0'; /* null-terminate, just in case */
2N/A unescape_str(*serial_no);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Find slots that match a token identifier. Token labels take the
2N/A * form of:
2N/A * token_name:manufacturer:serial_number
2N/A * manufacturer and serial number are optional. If used, the fields
2N/A * are delimited by the colon ':' character.
2N/A */
2N/AKMF_RETURN
2N/Akmf_pk11_token_lookup(KMF_HANDLE_T handle, char *label, CK_SLOT_ID *slot_id)
2N/A{
2N/A KMF_RETURN kmf_rv = KMF_OK;
2N/A CK_RV rv;
2N/A CK_SLOT_ID_PTR slot_list = NULL;
2N/A CK_TOKEN_INFO token_info;
2N/A CK_ULONG slot_count = 0;
2N/A int i;
2N/A uint_t len, max_sz;
2N/A char *tmplabel = NULL;
2N/A char *token_name = NULL;
2N/A char *manuf_id = NULL;
2N/A char *serial_no = NULL;
2N/A boolean_t tok_match = B_FALSE;
2N/A boolean_t man_match = B_FALSE;
2N/A boolean_t ser_match = B_FALSE;
2N/A
2N/A if (slot_id == NULL || label == NULL || !strlen(label))
2N/A return (KMF_ERR_BAD_PARAMETER);
2N/A
2N/A if (handle == NULL) {
2N/A rv = C_Initialize(NULL);
2N/A if ((rv != CKR_OK) &&
2N/A (rv != CKR_CRYPTOKI_ALREADY_INITIALIZED)) {
2N/A return (KMF_ERR_UNINITIALIZED);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Parse token specifier into token_name, manuf_id, serial_no.
2N/A * Token_name is required; manuf_id and serial_no are optional.
2N/A */
2N/A tmplabel = strdup(label);
2N/A if (tmplabel == NULL)
2N/A return (KMF_ERR_MEMORY);
2N/A
2N/A if (parse_token_spec(tmplabel, &token_name, &manuf_id,
2N/A &serial_no) < 0) {
2N/A free(tmplabel);
2N/A return (KMF_ERR_BAD_PARAMETER);
2N/A }
2N/A
2N/A /* Get a list of all slots with tokens present. */
2N/A kmf_rv = kmf_get_token_slots(handle, &slot_list, &slot_count);
2N/A if (kmf_rv != KMF_OK) {
2N/A free(tmplabel);
2N/A return (kmf_rv);
2N/A }
2N/A
2N/A /* If there are no such slots, the desired token won't be found. */
2N/A if (slot_count == 0) {
2N/A free(tmplabel);
2N/A return (KMF_ERR_TOKEN_NOT_PRESENT);
2N/A }
2N/A
2N/A /* Search the slot list for the token. */
2N/A for (i = 0; i < slot_count; i++) {
2N/A if (C_GetTokenInfo(slot_list[i], &token_info) != CKR_OK) {
2N/A continue;
2N/A }
2N/A
2N/A /* See if the token label matches. */
2N/A len = strlen(token_name);
2N/A max_sz = sizeof (token_info.label);
2N/A if (memcmp_pad_max(&(token_info.label), max_sz, token_name,
2N/A len, max_sz) == 0)
2N/A tok_match = B_TRUE;
2N/A /*
2N/A * If manufacturer id was given, see if it actually matches.
2N/A * If no manufacturer id was given, assume match is true.
2N/A */
2N/A if (manuf_id) {
2N/A len = strlen(manuf_id);
2N/A max_sz = sizeof ((char *)(token_info.manufacturerID));
2N/A if (memcmp_pad_max(&(token_info.manufacturerID), max_sz,
2N/A manuf_id, len, max_sz) == 0)
2N/A man_match = B_TRUE;
2N/A } else {
2N/A man_match = B_TRUE;
2N/A }
2N/A
2N/A /*
2N/A * If serial number was given, see if it actually matches.
2N/A * If no serial number was given, assume match is true.
2N/A */
2N/A if (serial_no) {
2N/A len = strlen(serial_no);
2N/A max_sz = sizeof ((char *)(token_info.serialNumber));
2N/A if (memcmp_pad_max(&(token_info.serialNumber), max_sz,
2N/A serial_no, len, max_sz) == 0)
2N/A ser_match = B_TRUE;
2N/A } else {
2N/A ser_match = B_TRUE;
2N/A }
2N/A
2N/A if (tok_match && man_match && ser_match)
2N/A break; /* found it! */
2N/A }
2N/A
2N/A if (i < slot_count) {
2N/A /* found the desired token from the slotlist */
2N/A *slot_id = slot_list[i];
2N/A free(slot_list);
2N/A free(tmplabel);
2N/A return (KMF_OK);
2N/A }
2N/A
2N/A /*
2N/A * If we didn't find the token from the slotlist, check if this token
2N/A * is the one currently hidden by the metaslot. If that's case,
2N/A * we can just use the metaslot, the slot 0.
2N/A */
2N/A if (pkcs11_token_is_metaslot_obj_store(token_name)) {
2N/A *slot_id = slot_list[0];
2N/A kmf_rv = KMF_OK;
2N/A } else {
2N/A kmf_rv = KMF_ERR_TOKEN_NOT_PRESENT;
2N/A }
2N/A
2N/A free(slot_list);
2N/A free(tmplabel);
2N/A return (kmf_rv);
2N/A}
2N/A
2N/AKMF_RETURN
2N/Akmf_set_token_pin(KMF_HANDLE_T handle,
2N/A int num_attr,
2N/A KMF_ATTRIBUTE *attrlist)
2N/A{
2N/A KMF_RETURN ret = KMF_OK;
2N/A KMF_PLUGIN *plugin;
2N/A KMF_ATTRIBUTE_TESTER required_attrs[] = {
2N/A {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
2N/A {KMF_CREDENTIAL_ATTR, FALSE, sizeof (KMF_CREDENTIAL),
2N/A sizeof (KMF_CREDENTIAL)},
2N/A {KMF_NEWPIN_ATTR, FALSE, sizeof (KMF_CREDENTIAL),
2N/A sizeof (KMF_CREDENTIAL)},
2N/A };
2N/A
2N/A int num_req_attrs = sizeof (required_attrs) /
2N/A sizeof (KMF_ATTRIBUTE_TESTER);
2N/A uint32_t len;
2N/A KMF_KEYSTORE_TYPE kstype;
2N/A
2N/A if (handle == NULL)
2N/A return (KMF_ERR_BAD_PARAMETER);
2N/A
2N/A CLEAR_ERROR(handle, ret);
2N/A if (ret != KMF_OK)
2N/A return (ret);
2N/A
2N/A ret = test_attributes(num_req_attrs, required_attrs,
2N/A 0, NULL, num_attr, attrlist);
2N/A if (ret != KMF_OK)
2N/A return (ret);
2N/A
2N/A len = sizeof (kstype);
2N/A ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, num_attr,
2N/A &kstype, &len);
2N/A if (ret != KMF_OK)
2N/A return (ret);
2N/A
2N/A plugin = FindPlugin(handle, kstype);
2N/A if (plugin != NULL) {
2N/A if (plugin->funclist->SetTokenPin != NULL)
2N/A return (plugin->funclist->SetTokenPin(handle, num_attr,
2N/A attrlist));
2N/A else
2N/A return (KMF_ERR_FUNCTION_NOT_FOUND);
2N/A }
2N/A return (KMF_ERR_PLUGIN_NOTFOUND);
2N/A}
2N/A
2N/A/*
2N/A * Name: kmf_select_token
2N/A *
2N/A * Description:
2N/A * This function enables the user of PKCS#11 plugin to select a
2N/A * particular PKCS#11 token. Valid token label are required in order to
2N/A * successfully complete this function.
2N/A * All subsequent KMF APIs, which specify PKCS#11 keystore as
2N/A * the backend, will be performed at the selected token.
2N/A *
2N/A * Parameters:
2N/A * label(input) - pointer to the token label
2N/A *
2N/A * Returns:
2N/A * A KMF_RETURN value indicating success or specifying a particular
2N/A * error condition.
2N/A * The value KMF_OK indicates success. All other values represent
2N/A * an error condition.
2N/A */
2N/AKMF_RETURN
2N/Akmf_select_token(KMF_HANDLE_T handle, char *label, int readonly)
2N/A{
2N/A KMF_RETURN kmf_rv = KMF_OK;
2N/A CK_RV ck_rv = CKR_OK;
2N/A CK_SLOT_ID slot_id;
2N/A CK_SESSION_HANDLE hSession;
2N/A CK_FLAGS openflags;
2N/A
2N/A CLEAR_ERROR(handle, kmf_rv);
2N/A if (kmf_rv != KMF_OK)
2N/A return (kmf_rv);
2N/A
2N/A if (label == NULL) {
2N/A return (KMF_ERR_BAD_PARAMETER);
2N/A }
2N/A
2N/A kmf_rv = init_pk11();
2N/A if (kmf_rv != KMF_OK) {
2N/A return (kmf_rv);
2N/A }
2N/A
2N/A /* Only one token can be active per thread */
2N/A if (handle->pk11handle != NULL) {
2N/A return (KMF_ERR_TOKEN_SELECTED);
2N/A }
2N/A
2N/A /* Find the token with matching label */
2N/A kmf_rv = kmf_pk11_token_lookup(handle, label, &slot_id);
2N/A if (kmf_rv != KMF_OK) {
2N/A return (kmf_rv);
2N/A }
2N/A
2N/A openflags = CKF_SERIAL_SESSION;
2N/A if (!readonly)
2N/A openflags |= CKF_RW_SESSION;
2N/A
2N/A /* Open a session then log the user into the token */
2N/A ck_rv = C_OpenSession(slot_id, openflags, NULL, NULL, &hSession);
2N/A if (ck_rv != CKR_OK) {
2N/A handle->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN;
2N/A handle->lasterr.errcode = ck_rv;
2N/A return (KMF_ERR_INTERNAL);
2N/A }
2N/A
2N/A handle->pk11handle = hSession;
2N/A
2N/A return (kmf_rv);
2N/A}
2N/A
2N/ACK_SESSION_HANDLE
2N/Akmf_get_pk11_handle(KMF_HANDLE_T kmfh)
2N/A{
2N/A return (kmfh->pk11handle);
2N/A}
2N/A
2N/AKMF_RETURN
2N/Akmf_pk11_init_token(KMF_HANDLE_T handle,
2N/A char *currlabel, char *newlabel,
2N/A CK_UTF8CHAR_PTR sopin, CK_ULONG sopinlen)
2N/A{
2N/A KMF_RETURN ret = KMF_OK;
2N/A CK_RV ckrv;
2N/A CK_SLOT_ID slot_id = 0;
2N/A
2N/A CLEAR_ERROR(handle, ret);
2N/A if (ret != KMF_OK)
2N/A return (ret);
2N/A
2N/A /*
2N/A * It is best to try and lookup tokens by label.
2N/A */
2N/A if (currlabel != NULL) {
2N/A ret = kmf_pk11_token_lookup(handle, currlabel, &slot_id);
2N/A if (ret != KMF_OK)
2N/A return (ret);
2N/A } else {
2N/A /* We can't determine which slot to initialize */
2N/A return (KMF_ERR_TOKEN_NOT_PRESENT);
2N/A }
2N/A
2N/A /* Initialize and set the new label (if given) */
2N/A ckrv = C_InitToken(slot_id, sopin, sopinlen,
2N/A (CK_UTF8CHAR_PTR)(newlabel ? newlabel : currlabel));
2N/A
2N/A if (ckrv != CKR_OK) {
2N/A if (ckrv == CKR_PIN_INCORRECT)
2N/A return (KMF_ERR_AUTH_FAILED);
2N/A else
2N/A return (KMF_ERR_INTERNAL);
2N/A }
2N/A
2N/A return (ret);
2N/A}