metaUtil.c revision 142e971fe9419790cf7cebe77a51db0909cb335e
/*
* 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
* 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 "%Z%%M% %I% %E% SMI"
#include <cryptoutil.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <strings.h>
#include "metaGlobal.h"
extern cipher_mechs_threshold_t meta_mechs_threshold[];
{
return (CKR_HOST_MEMORY);
sizeof (CK_MECHANISM));
if ((pMechanism->ulParameterLen > 0) &&
return (CKR_HOST_MEMORY);
}
} else {
}
} else { /* reuse it */
if ((pMechanism->ulParameterLen > 0) &&
if (pMechanism->ulParameterLen !=
return (CKR_HOST_MEMORY);
}
} /* otherwise reuse it */
} else {
/*
* free the previous pParameter if not yet freed
* because we don't need it now.
*/
}
}
/* copy the rest of data */
}
return (CKR_OK);
}
/*
* meta_operation_init
*
*/
{
unsigned long i, slotCount = 0;
/*
* If an operation is already active, cleanup existing operation
* and start a new one.
*/
(optype == CKF_DIGEST)) {
mech = *pMechanism;
if ((pMechanism->ulParameterLen > 0) &&
return (CKR_HOST_MEMORY);
}
} else {
mech.ulParameterLen = 0;
}
B_FALSE);
}
return (rv);
} else {
B_FALSE);
}
}
/*
* Get a list of capable slots.
*
* If the specified mechanism is used in this session last time,
* the list of capable slots is already retrieved. We can save
* some processing, and just use that list of slots.
*/
goto finish;
}
}
/* The following 2 assignment is just to make the code more readable */
/* Attempt to initialize operation on slots until one succeeds. */
for (i = 0; i < slotCount; i++) {
init_session = NULL;
/*
* An actual session with the underlying slot is required
* for the operation. When the operation is successfully
* completed, the underlying session with the slot
* is not released back to the list of available sessions
* pool. This will help if the next operation can
* also be done on the same slot, because it avoids
* one extra trip to the session pool to get an idle session.
* If the operation can't be done on that slot,
* we release the session back to the session pool then.
*/
/*
* set it to NULL for now, assign it to
* init_session again if it is successful
*/
} else {
init_session = NULL;
}
}
if (!init_session) {
goto loop_cleanup;
}
}
/* if necessary, ensure a clone of the obj exists in slot */
if (optype != CKF_DIGEST) {
&init_key);
goto loop_cleanup;
}
}
switch (optype) {
case CKF_ENCRYPT:
break;
case CKF_DECRYPT:
break;
case CKF_DIGEST:
break;
case CKF_SIGN:
break;
case CKF_VERIFY:
break;
case CKF_SIGN_RECOVER:
break;
case CKF_VERIFY_RECOVER:
break;
default:
/*NOTREACHED*/
break;
}
break;
if (i == 0) {
}
if (init_session) {
init_session = NULL;
}
}
/*
* If currently stored session is not the one being in use now,
* release the previous one and store the current one
*/
}
/* Save the session */
} else {
}
return (rv);
}
/*
* meta_operation_init_softtoken()
* It will always do the crypto init operation on softtoken slot.
*/
{
/*
* If an operation is already active, cleanup existing operation
* and start a new one.
*/
mech = *pMechanism;
if ((pMechanism->ulParameterLen > 0) &&
return (CKR_HOST_MEMORY);
}
} else {
mech.ulParameterLen = 0;
}
key);
}
return (rv);
}
/*
* An actual session with the underlying slot is required
* for the operation. When the operation is successfully
* completed, the underlying session with the slot
* is not released back to the list of available sessions
* pool. This will help if the next operation can
* also be done on the same slot, because it avoids
* one extra trip to the session pool to get an idle session.
* If the operation can't be done on that slot,
* we release the session back to the session pool.
*/
/*
* set it to NULL for now, assign it to
* init_session again if it is successful
*/
} else {
init_session = NULL;
}
}
if (init_session == NULL) {
/* get the active session from softtoken slot */
goto finish;
}
}
/* if necessary, ensure a clone of the obj exists in softtoken slot */
if (optype != CKF_DIGEST) {
init_session, &init_key);
if (init_session != NULL) {
init_session = NULL;
}
goto finish;
}
}
/*
* Currently, we only support offloading encrypt, decrypt
* and digest operations to softtoken based on kernel
* threshold for the supported mechanisms.
*/
switch (optype) {
case CKF_ENCRYPT:
break;
case CKF_DECRYPT:
break;
case CKF_DIGEST:
break;
default:
/*NOTREACHED*/
break;
}
/*
* If currently stored session is not the one being in use now,
* release the previous one and store the current one
*/
}
/* Save the session */
/*
* The init.done flag will be checked by the meta_do_operation()
* to indicate whether the C_xxxInit has been done against
* softtoken.
*/
}
return (rv);
}
int
{
int i;
for (i = 0; i < MAX_NUM_THRESHOLD; i++) {
return (meta_mechs_threshold[i].mech_threshold);
}
/* no matching mechanism */
return (0);
}
/*
* meta_do_operation
*
* NOTES:
*
* 1) The spec says you cannot do a C_Encrypt after a C_EncUpdate,
* but we don't explicitly enforce it here (ie, disallow doing MODE_SINGLE
* after a MODE_UPDATE). Instead, we just assume the underlying provider
* will catch the problem and return an appropriate error.
*
* 2) Note that the Verify operations are a little unusual, due to the
* PKCS#11 API. For C_Verify, the last two arguments are used as inputs,
* unlike the other single pass operations (where they are outputs). For
* Final operations.
*
* 3) C_DigestKey is the only crypto operation that uses an object after
* the operation has been initialized. No other callers should provide
* this argument (use NULL).
*/
{
int threshold = 0;
/*
* We've deferred the init for encrypt, decrypt and digest
* operations. As we know the size of the input data now, we
* can decide where to perform the real init operation based
* on the kernel cipher-specific thresholds for certain
* supported mechanisms.
*/
(optype == CKF_DIGEST)) {
if (Tmp_GetThreshold != NULL) {
return (CKR_OPERATION_NOT_INITIALIZED);
}
}
/*
* Call real init operation only if the
* application has called C_xxxInit
* but the real init operation has not
* been done.
*/
goto exit;
/*
* This checking detects the case that
* directly without calling C_xxxInit.
*/
return (CKR_OPERATION_NOT_INITIALIZED);
}
} else {
/*
* The size of the input data is smaller than the
* threshold so we'll use softoken to perform the
* crypto operation for better performance reason.
*/
/*
* Call real init operation only if the
* application has called C_xxxInit
* but the real init operation has not
* been done.
*/
/*
* In case the operation fails in
* softtoken, go back to use the
* original slot again.
*/
goto exit;
}
/*
* This checking detects the case that
* directly without calling C_xxxInit.
*/
return (CKR_OPERATION_NOT_INITIALIZED);
}
}
return (CKR_OPERATION_NOT_INITIALIZED);
}
if (slot_session) {
} else {
/* should never be here */
goto exit;
}
/* Do the operation... */
outLen);
outLen);
/* noOutputForOp = TRUE; */
inLen);
/* noOutputForOp = TRUE; */
/*
* For C_DigestKey, a key is provided and
* we need the clone.
*/
outLen);
/* noOutputForOp = TRUE; */
inLen);
outLen);
/* noOutputForOp = TRUE; */
/* noOutputForOp = TRUE; */
inLen);
/* noOutputForOp = TRUE; */
inLen);
} else {
}
/*
* Mark the operation type as inactive if an abnormal error
* happens, or if the operation normally results in an inactive
* operation state.
*
* NOTE: The spec isn't very explicit about what happens when you
* call C_FooFinal (or C_Foo) with a NULL output buffer (to get the
* output size), but there is no output. Technically this should be
* no different than the normal case (ie, when there is output), and
* the operation should remain active until the second call actually
* terminates it. However, one could make the case that there is no
* need for a second call, since no data is available. This presents
* dilemma for metaslot, because we don't know if the operation is
* going to remain active or not. We will assume a strict reading of
* the spec, the operation will remain active.
*/
exit:
if (rv == CKR_BUFFER_TOO_SMALL ||
/* Leave op active for retry (with larger buffer). */
} else { /* CKR_OK */
} else { /* mode == MODE_UPDATE */
}
}
if (shutdown) {
}
}
return (rv);
}
void
{
}
}
}
/*
* meta_operation_cleanup
*
* Cleans up an operation in the specified session.
* If the operation did not finish normally, it will force
* the operation to terminate.
*/
void
{
if (!finished_normally) {
} else {
if ((optype == CKF_ENCRYPT) ||
(optype == CKF_DECRYPT) ||
(optype == CKF_DIGEST)) {
}
return;
}
/*
* There's no simple, reliable way to abort an
* operation. So, we'll force the operation to finish.
*
* We are here either because we need to abort either after
* C_xxxxxInit() or C_xxxxxUpdate().
*
* We will call C_xxxxxUpdate() with invalid argument to
* force the operation to abort. According to the PKCS#11
* spec, any call to C_xxxxxUpdate() returns in an error
* will terminate the current operation.
*/
switch (optype) {
case CKF_ENCRYPT:
break;
case CKF_DECRYPT:
break;
case CKF_DIGEST:
NULL, 8);
break;
case CKF_SIGN:
NULL, 8);
break;
case CKF_SIGN_RECOVER:
break;
case CKF_VERIFY:
NULL, 8);
break;
case CKF_VERIFY_RECOVER:
break;
default:
/*NOTREACHED*/
break;
}
}
(optype == CKF_DIGEST)) {
}
}
/*
* Gets the list of slots that supports the specified mechanism.
*
* If "token_only", check if the keystore slot supports the specified mech,
* if so, return that slot only
*
* Otherwise, get list of all slots that support the mech.
*
*/
static CK_RV
{
if (token_only) {
return (rv);
}
if (mech_supported) {
/*
* Want to leave this at 0, that way, when
* other operation needs to
* use this mechanism, but not just for the
* keystore slot, we will look at other slots
*/
*slot_count = 1;
} else {
}
} else {
/*
* Get a list of slots that support this mech .
*
* If the specified mechanism is used last time,
* the list of capable slots is already retrieved.
* We can save some processing, and just use that list of slots.
*/
(mech_support_info->num_supporting_slots == 0)) {
return (CKR_FUNCTION_FAILED);
}
}
}
return (rv);
}
/*
* meta_generate_keys
*
* Generates symmetric (k1=key, k2=null) or asymmetric (k1=pub, k2=priv) keys.
*
*/
{
unsigned long i, slotCount = 0;
/*
* Since the keygen call is in a loop, it is performance-wise useful
* to keep track of the token value
*/
&(key1->isSensitive));
&(key1->isExtractable)))
if (key2) {
}
/* Can't create token objects in a read-only session. */
return (CKR_SESSION_READ_ONLY);
}
k1AttrCount, NULL)) {
return (CKR_USER_NOT_LOGGED_IN);
B_FALSE))
return (CKR_FUNCTION_FAILED);
if (doKeyPair) {
B_FALSE))
return (CKR_FUNCTION_FAILED);
}
} else if (doKeyPair) {
/*
* If this is a keypair operation, the second key cannot be
* a FreeObject if the first is not. Both keys will have the
* same fate when it comes to provider choices
*/
}
/*
* Token objects can only be generated in the token object
* slot. If token object slot doesn't support generating
* the key, it will just not be done.
*/
token_only = B_TRUE;
}
&mech_info);
goto finish;
}
goto finish;
/* Attempt to generate key on slots until one succeeds. */
for (i = 0; i < slotCount; i++) {
gen_session = NULL;
/*
* set it to NULL for now, assign it to
* gen_session again if it is successful
*/
} else {
gen_session = NULL;
}
}
if (gen_session == NULL) {
goto loop_cleanup;
}
}
/*
* If this is a freetoken, make sure the templates are
* approriate for the slot being used.
*/
goto loop_cleanup;
}
goto loop_cleanup;
}
if (doKeyPair) {
} else {
}
break;
if (i == 0) {
}
if (gen_session) {
gen_session = NULL;
}
}
goto finish;
}
goto finish;
}
if (key2) {
key2);
goto finish;
}
}
goto finish;
}
if (doKeyPair) {
goto finish;
}
}
if (slot_key1) {
}
if (slot_key2) {
}
/* Save the session in case it can be used later */
/*
* If currently stored session is not the one being in use now,
* release the previous one and store the current one
*/
}
/* Save the session */
}
return (rv);
}
/*
* meta_wrap_key
*
*/
{
unsigned long i, slotCount = 0;
/*
* If the key to be wrapped is a token object,
* the operation can only be done in the token object slot.
*/
return (rv);
}
/* Attempt to wrap key on slots until one succeeds. */
for (i = 0; i < slotCount; i++) {
wrap_session = NULL;
/*
* set it to NULL for now, assign it to
* wrap_session again if it is successful
*/
} else {
wrap_session = NULL;
}
}
if (wrap_session == NULL) {
goto loop_cleanup;
}
}
goto loop_cleanup;
goto loop_cleanup;
break;
if (i == 0) {
}
if (wrap_session) {
wrap_session = NULL;
}
}
if (rv != CKR_BUFFER_TOO_SMALL) {
if (i == slotCount) {
}
}
}
/* Save the session in case it can be used later */
/*
* If currently stored session is not the one being in use now,
* release the previous one and store the current one
*/
}
/* Save the session */
}
return (rv);
}
/*
* meta_unwrap_key
*
*/
{
unsigned long i, slotCount = 0;
/* Can't create token objects in a read-only session. */
unwrapped_key->isToken) {
return (CKR_SESSION_READ_ONLY);
}
/*
* If the the resulting unwrapped key
* needs to be a token object, the operation can only
* be performed in the token slot, if it is supported.
*/
return (rv);
}
goto finish;
}
/* Attempt to unwrap key on slots until one succeeds. */
for (i = 0; i < slotCount; i++) {
/*
* set it to NULL for now, assign it to
* unwrap_session again if it is successful
*/
} else {
}
}
if (unwrap_session == NULL) {
goto loop_cleanup;
}
}
goto loop_cleanup;
break;
if (i == 0) {
}
if (unwrap_session) {
}
}
if (rv != CKR_BUFFER_TOO_SMALL) {
}
goto finish;
}
goto finish;
}
if (slot_unwrapped_key) {
}
/* Save the session in case it can be used later */
/*
* If currently stored session is not the one being in use now,
* release the previous one and store the current one
*/
}
/* Save the session */
}
return (rv);
}
/*
* meta_derive_key
*
* Core implementation for C_DeriveKey. This function is a bit gross because
* of PKCS#11 kludges that pass extra object handles in the mechanism
* parameters. Normally C_DeriveKey takes a single existing key as input,
* and creates a single new key as output. But a few mechanisms take 2 keys
*
* When an extra input key (basekey2) is set, we set *phBaseKey2 to the clone's
* object handle. phBaseKey2 is provided by the caller so we don't have to
* trudge down into different mechanism parameters to set it when issuing the
* operation.
*
* the new handles from pMech->pParameter in order to fill in the appropriate
* meta_object fields.
*/
{
unsigned long i, slot_count = 0;
/*
* if the derived key needs to be a token object, can only
* perform the derive operation in the token slot
*/
&(newKey1->isSensitive));
/* Can't create token objects in a read-only session. */
goto finish;
}
ulAttributeCount, NULL)) {
return (CKR_USER_NOT_LOGGED_IN);
B_FALSE))
return (CKR_FUNCTION_FAILED);
}
return (rv);
}
if (isSSL) {
}
goto finish;
}
for (i = 0; i < slot_count; i++) {
/*
* set it to NULL for now, assign it to
* derive_session again if it is successful
*/
} else {
}
}
if (derive_session == NULL) {
goto loop_cleanup;
}
}
goto loop_cleanup;
if (basekey2) {
goto loop_cleanup;
/* Pass the handle somewhere in the mech params. */
}
goto loop_cleanup;
}
break;
if (i == 0) {
}
if (derive_session) {
}
/* No need to cleanup clones, so we can reuse them later. */
}
goto finish;
}
if (isTLSPRF)
goto finish;
/*
* the new key is unused (NULL). Instead, there are 4 keys which
* are derived, and are passed back through the mechanism params.
* Both mechs use the same mechanism parameter type.
*/
if (isSSL) {
/* NULL checks already done by caller */
goto finish;
}
goto finish;
}
goto finish;
}
goto finish;
}
} else {
goto finish;
}
}
if (slotkey1) {
}
if (slotkey2) {
}
if (slotkey3) {
}
if (slotkey4) {
}
/* Save the session in case it can be used later */
/*
* If currently stored session is not the one being in use now,
* release the previous one and store the current one
*/
}
/* Save the session */
}
return (rv);
}
/*
* Check the following 4 environment variables for user/application's
* configuration for metaslot. User's configuration takes precedence
* over the system wide configuration for metaslot
*
* ${METASLOT_ENABLED}
* ${METASLOT_OBJECTSTORE_SLOT}
* ${METASLOT_OBJECTSTORE_TOKEN}
* ${METASLOT_AUTO_KEY_MIGRATE}
*
* values defined in these environment variables will be stored in the
* global variable "metaslot_config"
*/
void
{
/*
* Check to see if any environment variable is defined
* by the user for configuring metaslot.
*/
/* METASLOT_ENABLED */
if (env_val) {
} else {
/* value is neither 1 or 0, ignore this value */
}
}
/* METASLOT_AUTO_KEY_MIGRATE */
if (env_val) {
} else {
/* value is neither 1 or 0, ignore this value */
}
}
/* METASLOT_OBJECTSTORE_SLOT */
if (env_val) {
}
/* METASLOT_OBJECTSTORE_TOKEN */
if (env_val) {
}
}