kcf_callprov.c revision 8047c9fb10f4d3f14385d535d6b23a5eb80c0c0f
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This file contains routines which call into a provider's
* entry points and do other related work.
*/
#include <sys/taskq_impl.h>
/*
* Return B_TRUE if the specified entry point is NULL. We rely on the
* caller to provide, with offset_1 and offset_2, information to calculate
* the location of the entry point. The ops argument is a temporary local
* variable defined as caddr_t *.
*/
kcf_req_params_t *);
void
{
kcf_prov_tried_t *l;
KCF_PROV_REFRELE(l->pt_pd);
kmem_free(l, sizeof (kcf_prov_tried_t));
}
}
int kmflag)
{
kcf_prov_tried_t *l;
if (l == NULL)
return (NULL);
*list = l;
return (l);
}
static boolean_t
{
return (B_TRUE);
};
return (B_FALSE);
}
/*
* Search a mech entry's hardware provider list for the specified
* provider. Return true if found.
*/
static boolean_t
{
if (prov_chain != NULL) {
return (B_TRUE);
}
}
}
return (B_FALSE);
}
/*
* This routine, given a logical provider, returns the least loaded
* provider belonging to the logical provider. The provider must be
* able to do the specified mechanism, i.e. check that the mechanism
* hasn't been disabled. In addition, just in case providers are not
* entirely equivalent, the provider's entry point is checked for
* non-nullness. This is accomplished by having the caller pass, as
* arguments, the offset of the function group (offset_1), and the
* offset of the function within the function group (offset_2).
* Returns NULL if no provider can be found.
*/
int
{
/* get the mech entry for the specified mechanism */
return (CRYPTO_MECHANISM_INVALID);
}
return (CRYPTO_MECHANISM_INVALID);
}
/*
* We assume the provider descriptor will not go away because
* it is being held somewhere, i.e. its reference count has been
* incremented. In the case of the crypto module, the provider
* descriptor is held by the session structure.
*/
goto out;
}
/*
* Find the least loaded real provider. tq_nalloc gives
* the number of task entries in the task queue. We do
* not acquire tq_lock here as it is not critical to
* get the exact number and the lock contention may be
* too costly for this code path.
*/
p = old->pd_provider_list;
while (p != NULL) {
provider = p->pl_provider;
p = p->pl_next;
continue;
}
p = p->pl_next;
continue;
}
/* provider does second mech */
if (mech_type_2 != CRYPTO_MECH_INVALID) {
int i;
/* convert from kef to provider's number */
for (i = 0; i < provider->pd_mech_list_count;
i++) {
if (provider->pd_mechanisms[i]
.cm_mech_number == mech_type)
break;
}
if (i == provider->pd_mech_list_count) {
p = p->pl_next;
continue;
}
}
/* choose BUSY if no READY providers */
p = p->pl_next;
continue;
}
}
p = p->pl_next;
}
} else {
/* can't find provider */
}
} else {
if (!KCF_IS_PROV_USABLE(old) ||
goto out;
}
goto out;
}
}
out:
return (rv);
}
/*
* This routine, given a logical provider, returns the least loaded
* provider belonging to the logical provider. Just in case providers
* are not entirely equivalent, the provider's entry point is checked
* for non-nullness. This is accomplished by having the caller pass, as
* arguments, the offset of the function group (offset_1), and the
* offset of the function within the function group (offset_2).
* Returns NULL if no provider can be found.
*/
int
{
/*
* We assume the provider descriptor will not go away because
* it is being held somewhere, i.e. its reference count has been
* incremented. In the case of the crypto module, the provider
* descriptor is held by the session structure.
*/
goto out;
}
/*
* Find the least loaded real provider. tq_nalloc gives
* the number of task entries in the task queue. We do
* not acquire tq_lock here as it is not critical to
* get the exact number and the lock contention may be
* too costly for this code path.
*/
p = old->pd_provider_list;
while (p != NULL) {
provider = p->pl_provider;
p = p->pl_next;
continue;
}
p = p->pl_next;
continue;
}
/* choose BUSY if no READY providers */
p = p->pl_next;
continue;
}
}
p = p->pl_next;
}
} else {
/* can't find provider */
}
} else {
if (!KCF_IS_PROV_USABLE(old) ||
goto out;
}
goto out;
}
}
out:
return (rv);
}
/*
* Return the next member of a logical provider, given the previous
* member. The function returns true if the next member is found and
* bumps its refcnt before returning.
*/
{
while (p != NULL) {
/* start the search */
next = p->pl_provider;
goto found;
} else {
/* find where we were before */
if (p->pl_provider == prev) {
goto found;
}
}
}
p = p->pl_next;
}
return (B_FALSE);
return (B_TRUE);
}
/*
* Return the best provider for the specified mechanism. The provider
* is held and it is the caller's responsibility to release it when done.
* The fg input argument is used as a search criterion to pick a provider.
* A provider has to support this function group to be picked.
*
* Find the least loaded provider in the list of providers. We do a linear
* search to find one. This is fine as we assume there are only a few
* number of providers in this list. If this assumption ever changes,
* we should revisit this.
*
* call_restrict represents if the caller should not be allowed to
* use restricted providers.
*/
{
int index;
return (NULL);
}
return (NULL);
}
/*
* We check for the threshhold for using a hardware provider for
* this amount of data. If there is no software provider available
* for the mechanism, then the threshold is ignored.
*/
if ((prov_chain != NULL) &&
/* there is at least one provider */
/*
* Find the least loaded provider. tq_nalloc gives
* the number of task entries in the task queue. We do
* not acquire tq_lock here as it is not critical to
* get the exact number and the lock contention may be
* too costly for this code path.
*/
while (prov_chain != NULL) {
!KCF_IS_PROV_USABLE(pd) ||
continue;
}
< gqlen) {
}
}
}
/* No HW provider for this mech, is there a SW provider? */
!KCF_IS_PROV_USABLE(pd) ||
}
/*
* We do not want to report CRYPTO_MECH_NOT_SUPPORTED, when
* we are in the "fallback to the next provider" case. Rather
* we preserve the error, so that the client gets the right
* error code.
*/
} else
return (pd);
}
/*
* Very similar to kcf_get_mech_provider(). Finds the best provider capable of
* a dual operation with both me1 and me2.
* When no dual-ops capable providers are available, return the best provider
* for me1 only, and sets *prov_mt2 to CRYPTO_INVALID_MECHID;
* faster than the 2 fastest providers capable of the individual ops
* separately.
*/
{
/* when mech is a valid mechanism, me will be its mech_entry */
return (NULL);
}
/*
* We check the threshold for using a hardware provider for
* this amount of data. If there is no software provider available
* for the first mechanism, then the threshold is ignored.
*/
if ((prov_chain != NULL) &&
/* there is at least one provider */
/*
* Find the least loaded provider capable of the combo
* me1 + me2, and save a pointer to the least loaded
* provider capable of me1 only.
*/
while (prov_chain != NULL) {
!KCF_IS_PROV_USABLE(pd) ||
continue;
}
/* Save the best provider capable of m1 */
*prov_mt1 =
}
/* See if pd can do me2 too */
fg2) == 0)
continue;
/* Bingo! */
*prov_mt2 =
*prov_mt1 = prov_chain->
break;
}
}
}
}
/* no HW provider for this mech, is there a SW provider? */
!KCF_IS_PROV_USABLE(pd) ||
else {
/* See if pd can do me2 too */
fg2) == 0)
continue;
/* Bingo! */
*prov_mt2 =
break;
}
}
}
}
else
return (pd);
}
/*
* Do the actual work of calling the provider routines.
*
* pd - Provider structure
* ctx - Context for this operation
* params - Parameters for this operation
* rhndl - Request handle to use for notification
*
* The return values are the same as that of the respective SPI.
*/
int
{
int err = CRYPTO_ARGUMENTS_BAD;
case KCF_OG_DIGEST: {
switch (optype) {
case KCF_OP_INIT:
/*
* We should do this only here and not in KCF_WRAP_*
* macros. This is because we may want to try other
* providers, in case we recover from a failure.
*/
rhndl);
break;
case KCF_OP_SINGLE:
break;
case KCF_OP_UPDATE:
break;
case KCF_OP_FINAL:
break;
case KCF_OP_ATOMIC:
rhndl);
break;
case KCF_OP_DIGEST_KEY:
rhndl);
break;
default:
break;
}
break;
}
case KCF_OG_MAC: {
switch (optype) {
case KCF_OP_INIT:
break;
case KCF_OP_SINGLE:
break;
case KCF_OP_UPDATE:
rhndl);
break;
case KCF_OP_FINAL:
break;
case KCF_OP_ATOMIC:
break;
case KCF_OP_MAC_VERIFY_ATOMIC:
break;
default:
break;
}
break;
}
case KCF_OG_ENCRYPT: {
switch (optype) {
case KCF_OP_INIT:
break;
case KCF_OP_SINGLE:
break;
case KCF_OP_UPDATE:
break;
case KCF_OP_FINAL:
break;
case KCF_OP_ATOMIC:
break;
default:
break;
}
break;
}
case KCF_OG_DECRYPT: {
switch (optype) {
case KCF_OP_INIT:
break;
case KCF_OP_SINGLE:
break;
case KCF_OP_UPDATE:
rhndl);
break;
case KCF_OP_FINAL:
break;
case KCF_OP_ATOMIC:
break;
default:
break;
}
break;
}
case KCF_OG_SIGN: {
switch (optype) {
case KCF_OP_INIT:
break;
case KCF_OP_SIGN_RECOVER_INIT:
rhndl);
break;
case KCF_OP_SINGLE:
break;
case KCF_OP_SIGN_RECOVER:
break;
case KCF_OP_UPDATE:
rhndl);
break;
case KCF_OP_FINAL:
rhndl);
break;
case KCF_OP_ATOMIC:
break;
break;
default:
break;
}
break;
}
case KCF_OG_VERIFY: {
switch (optype) {
case KCF_OP_INIT:
break;
rhndl);
break;
case KCF_OP_SINGLE:
break;
case KCF_OP_VERIFY_RECOVER:
break;
case KCF_OP_UPDATE:
rhndl);
break;
case KCF_OP_FINAL:
rhndl);
break;
case KCF_OP_ATOMIC:
break;
break;
default:
break;
}
break;
}
case KCF_OG_ENCRYPT_MAC: {
switch (optype) {
case KCF_OP_INIT:
kcf_secondctx = ((kcf_context_t *)
if (kcf_secondctx != NULL) {
break;
}
rhndl);
break;
case KCF_OP_SINGLE:
break;
case KCF_OP_UPDATE:
kcf_secondctx = ((kcf_context_t *)
if (kcf_secondctx != NULL) {
break;
}
break;
case KCF_OP_FINAL:
kcf_secondctx = ((kcf_context_t *)
if (kcf_secondctx != NULL) {
break;
}
break;
case KCF_OP_ATOMIC:
rhndl);
break;
default:
break;
}
break;
}
case KCF_OG_MAC_DECRYPT: {
switch (optype) {
case KCF_OP_INIT:
kcf_secondctx = ((kcf_context_t *)
if (kcf_secondctx != NULL) {
break;
}
rhndl);
break;
case KCF_OP_SINGLE:
break;
case KCF_OP_UPDATE:
kcf_secondctx = ((kcf_context_t *)
if (kcf_secondctx != NULL) {
break;
}
break;
case KCF_OP_FINAL:
kcf_secondctx = ((kcf_context_t *)
if (kcf_secondctx != NULL) {
break;
}
break;
case KCF_OP_ATOMIC:
rhndl);
break;
rhndl);
break;
default:
break;
}
break;
}
case KCF_OG_KEY: {
switch (optype) {
case KCF_OP_KEY_GENERATE:
break;
case KCF_OP_KEY_GENERATE_PAIR:
break;
case KCF_OP_KEY_WRAP:
rhndl);
break;
case KCF_OP_KEY_UNWRAP:
break;
case KCF_OP_KEY_DERIVE:
break;
default:
break;
}
break;
}
case KCF_OG_RANDOM: {
switch (optype) {
case KCF_OP_RANDOM_SEED:
break;
case KCF_OP_RANDOM_GENERATE:
break;
default:
break;
}
break;
}
case KCF_OG_SESSION: {
switch (optype) {
case KCF_OP_SESSION_OPEN:
/*
* so_pd may be a logical provider, in which case
* we need to check whether it has been removed.
*/
break;
}
break;
case KCF_OP_SESSION_CLOSE:
/*
* so_pd may be a logical provider, in which case
* we need to check whether it has been removed.
*/
break;
}
break;
case KCF_OP_SESSION_LOGIN:
break;
case KCF_OP_SESSION_LOGOUT:
break;
default:
break;
}
break;
}
case KCF_OG_OBJECT: {
switch (optype) {
case KCF_OP_OBJECT_CREATE:
break;
case KCF_OP_OBJECT_COPY:
break;
case KCF_OP_OBJECT_DESTROY:
break;
case KCF_OP_OBJECT_GET_SIZE:
break;
break;
break;
case KCF_OP_OBJECT_FIND_INIT:
break;
case KCF_OP_OBJECT_FIND:
break;
case KCF_OP_OBJECT_FIND_FINAL:
rhndl);
break;
default:
break;
}
break;
}
case KCF_OG_PROVMGMT: {
switch (optype) {
case KCF_OP_MGMT_EXTINFO:
/*
* po_pd may be a logical provider, in which case
* we need to check whether it has been removed.
*/
break;
}
break;
case KCF_OP_MGMT_INITTOKEN:
break;
case KCF_OP_MGMT_INITPIN:
break;
case KCF_OP_MGMT_SETPIN:
break;
default:
break;
}
break;
}
default:
break;
} /* end of switch(params->rp_opgrp) */
return (err);
}
/*
* Emulate the call for a multipart dual ops with 2 single steps.
* This routine is always called in the context of a working thread
* running kcf_svc_do_run().
* The single steps are submitted in a pure synchronous way (blocking).
* When this routine returns, kcf_svc_do_run() will call kcf_aop_done()
* so the originating consumer's callback gets invoked. kcf_aop_done()
* takes care of freeing the operation context. So, this routine does
* not free the operation context.
*
* The provider descriptor is assumed held by the callers.
*/
static int
{
int err = CRYPTO_ARGUMENTS_BAD;
case KCF_OG_ENCRYPT_MAC: {
switch (optype) {
case KCF_OP_INIT: {
B_FALSE);
/* It can't be CRYPTO_QUEUED */
if (err != CRYPTO_SUCCESS) {
break;
}
if (err == CRYPTO_SUCCESS) {
}
break;
}
case KCF_OP_UPDATE: {
NULL);
B_FALSE);
/* It can't be CRYPTO_QUEUED */
if (err != CRYPTO_SUCCESS) {
break;
}
/*
* The previous encrypt step was an
* accumulation only and didn't produce any
* partial output
*/
break;
} else {
}
break;
}
case KCF_OP_FINAL: {
NULL);
B_FALSE);
/* It can't be CRYPTO_QUEUED */
if (err != CRYPTO_SUCCESS) {
break;
}
if (err != CRYPTO_SUCCESS) {
return (err);
}
}
/* and finally, collect the MAC */
break;
}
default:
break;
}
break;
}
case KCF_OG_MAC_DECRYPT: {
switch (optype) {
case KCF_OP_INIT: {
/* It can't be CRYPTO_QUEUED */
if (err != CRYPTO_SUCCESS) {
break;
}
B_FALSE);
/* It can't be CRYPTO_QUEUED */
if (err != CRYPTO_SUCCESS) {
break;
}
break;
}
case KCF_OP_UPDATE: {
if (err != CRYPTO_SUCCESS)
break;
/* zero ct->dd_len2 means decrypt everything */
}
break;
}
case KCF_OP_FINAL: {
if (err != CRYPTO_SUCCESS) {
break;
}
/* Get the last chunk of plaintext */
NULL);
break;
}
}
break;
}
default:
break;
} /* end of switch(params->rp_opgrp) */
return (err);
}