/*
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright 2010 Nexenta Systems, Inc. All rights reserved.
*/
/*
* This file is part of the core Kernel Cryptographic Framework.
* It implements the SPI functions exported to cryptographic
* providers.
*/
/*
* minalloc and maxalloc values to be used for taskq_create().
*/
static void remove_provider(kcf_provider_desc_t *);
static void process_logical_providers(crypto_provider_info_t *,
static int kcf_prov_kstat_update(kstat_t *, int);
static void delete_kstat(kcf_provider_desc_t *);
{ "kcf_ops_total", KSTAT_DATA_UINT64 },
{ "kcf_ops_passed", KSTAT_DATA_UINT64 },
{ "kcf_ops_failed", KSTAT_DATA_UINT64 },
{ "kcf_ops_returned_busy", KSTAT_DATA_UINT64 }
};
extern int sys_shutdown;
/*
* Copy an ops vector from src to dst. Used during provider registration
* to copy the ops vector from the provider info structure to the
* provider descriptor maintained by KCF.
* Copying the ops vector specified by the provider is needed since the
* framework does not require the provider info structure to be
* persistent.
*/
static void
{
}
static void
{
}
static void
{
}
static void
{
}
/*
* This routine is used to add cryptographic providers to the KEF framework.
* Providers pass a crypto_provider_info structure to crypto_register_provider()
* and get back a handle. The crypto_provider_info structure contains a
* list of mechanisms supported by the provider and an ops vector containing
* provider entry points. Hardware providers call this routine in their attach
* routines. Software providers call this routine in their _init() routine.
*/
int
{
char *name;
goto errormsg;
}
/*
* Check provider type, must be software, hardware, or logical.
*/
goto errormsg;
/*
* Allocate and initialize a new provider descriptor. We also
* hold it and release it when done.
*/
/* provider-private handle, opaque to KCF */
/* copy provider description string */
/*
* pi_provider_descriptor is a string that can contain
* up to CRYPTO_PROVIDER_DESCR_MAX_LEN + 1 characters
* INCLUDING the terminating null character. A bcopy()
* is necessary here as pd_description should not have
* a null character. See comments in kcf_alloc_provider_desc()
* for details on pd_description field.
*/
}
goto bail;
}
}
}
}
}
/* object_ops and nostore_key_ops are mutually exclusive */
goto bail;
}
/*
* For software providers, copy the module name and module ID.
* For hardware providers, copy the driver name and instance.
*/
switch (info->pi_provider_type) {
case CRYPTO_SW_PROVIDER:
goto bail;
goto bail;
break;
case CRYPTO_HW_PROVIDER:
case CRYPTO_LOGICAL_PROVIDER:
goto bail;
break;
}
goto bail;
goto bail;
/* process the mechanisms supported by the provider */
goto bail;
/*
* Add provider to providers tables, also sets the descriptor
* pd_prov_id field.
*/
goto bail;
}
/*
* We create a taskq only for a hardware provider. The global
* software queue is used for software providers. We handle ordering
* of multi-part requests in the taskq routine. So, it is safe to
* have multiple threads for the taskq. We pass TASKQ_PREPOPULATE flag
* to keep some entries cached to improve performance.
*/
else
/* no kernel session to logical providers and no pd_flags */
/*
* Open a session for session-oriented providers. This session
* is used for all kernel consumers. This is fine as a provider
* is required to support multiple thread access to a session.
* We can do this only after the taskq has been created as we
* do a kcf_submit_request() to open the session.
*/
B_FALSE);
if (ret != CRYPTO_SUCCESS)
goto undo_then_bail;
}
/*
* Get the value for the maximum input length allowed if
* CRYPTO_HASH_NO_UPDATE or CRYPTO_HASH_NO_UPDATE is specified.
*/
goto undo_then_bail;
if (ret != CRYPTO_SUCCESS)
goto undo_then_bail;
}
}
}
}
/*
* Create the kstat for this provider. There is a kstat
* installed for each successfully registered provider.
* This kstat is deleted, when the provider unregisters.
*/
} else {
}
KSTAT_TYPE_NAMED, sizeof (kcf_prov_stats_t) /
sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL);
sizeof (kcf_stats_ks_data_template));
}
}
exit:
return (CRYPTO_SUCCESS);
ret = CRYPTO_FAILED;
bail:
switch (ret) {
case CRYPTO_FAILED:
"Cryptographic Framework.",
break;
"registering with the Cryptographic Framework.",
break;
case CRYPTO_ARGUMENTS_BAD:
"not registered with the Cryptographic Framework.",
break;
case CRYPTO_VERSION_MISMATCH:
"Cryptographic Framework as there is a SPI version "
"mismatch (%d) error.",
break;
case CRYPTO_FIPS140_ERROR:
"Cryptographic Framework as there was a FIPS 140 "
break;
default:
"Cryptographic Framework. (0x%x)",
};
}
return (ret);
}
/* Return the number of holds on a provider. */
int
{
int i;
int refcnt = 0;
if (do_lock)
if (do_lock)
return (refcnt);
}
/*
* This routine is used to notify the framework when a provider is being
* removed. Hardware providers call this routine in their detach routines.
* Software providers call this routine in their _fini() routine.
*/
int
{
/* lookup provider descriptor */
NULL) {
goto errormsg;
}
/*
* Check if any other thread is disabling or removing
* this provider. We return if this is the case.
*/
/* Release reference held by kcf_prov_tab_lookup(). */
ret = CRYPTO_BUSY;
goto errormsg;
}
if (saved_state == KCF_PROV_BUSY) {
/*
* The per-provider taskq threads may be waiting. We
* signal them so that they can start failing requests.
*/
}
}
/* remove the provider from the mechanisms tables */
mech_idx++) {
}
}
/* remove provider from providers table */
/* Release reference held by kcf_prov_tab_lookup(). */
goto errormsg;
}
/*
* Wait till the existing requests with the provider complete
* and all the holds are released. All the holds on a software
* provider are from kernel clients and the hold time
* is expected to be short. So, we won't be stuck here forever.
*/
/* wait 1 second and try again. */
}
} else {
int i;
/*
* Wait until requests that have been sent to the provider
* complete.
*/
}
}
}
/* Release reference held by kcf_prov_tab_lookup(). */
/* kcf_free_provider_desc drops prov_tab_mutex */
} else {
/*
* remove any holds on us from a dormant PKCS #11 app.
* For now, we check the provider table for
* KCF_PROV_UNREGISTERED entries when a provider is
* added to the table or when a provider is removed from it
* and free them when refcnt reaches zero.
*/
}
switch (ret) {
case CRYPTO_UNKNOWN_PROVIDER:
"requested to unregister from the cryptographic "
break;
case CRYPTO_BUSY:
"the Cryptographic Framework as it is busy.",
break;
default:
"Cryptographic Framework. (0x%x)",
};
}
return (ret);
}
/*
* This routine is used to notify the framework that the state of
* a cryptographic provider has changed. Valid state codes are:
*
* CRYPTO_PROVIDER_READY
* The provider indicates that it can process more requests. A provider
* will notify with this event if it previously has notified us with a
* CRYPTO_PROVIDER_BUSY.
*
* CRYPTO_PROVIDER_BUSY
* The provider can not take more requests.
*
* CRYPTO_PROVIDER_FAILED
* The provider encountered an internal error. The framework will not
* be sending any more requests to the provider. The provider may notify
* with a CRYPTO_PROVIDER_READY, if it is able to recover from the error.
*
* This routine can be called from user or interrupt context.
*/
void
{
/* lookup the provider from the given handle */
return;
goto out;
"logical provider (%x) ignored\n", handle);
goto out;
}
switch (state) {
case CRYPTO_PROVIDER_READY:
case KCF_PROV_BUSY:
/*
* Signal the per-provider taskq threads that they
* can start submitting requests.
*/
break;
case KCF_PROV_FAILED:
/*
* The provider recovered from the error. Let us
* use it now.
*/
break;
}
break;
case CRYPTO_PROVIDER_BUSY:
case KCF_PROV_READY:
break;
}
break;
case CRYPTO_PROVIDER_FAILED:
/*
* We note the failure and return. The per-provider taskq
* threads check this flag and start failing the
* requests, if it is set. See process_req_hwp() for details.
*/
case KCF_PROV_READY:
break;
case KCF_PROV_BUSY:
/*
* The per-provider taskq threads may be waiting. We
* signal them so that they can start failing requests.
*/
break;
}
break;
}
out:
}
/*
* This routine is used to notify the framework the result of
* an asynchronous request handled by a provider. Valid error
* codes are the same as the CRYPTO_* errors defined in common.h.
*
* This routine can be called from user or interrupt context.
*/
void
{
return;
} else {
}
}
/*
* This routine is used by software providers to determine
* whether to use KM_SLEEP or KM_NOSLEEP during memory allocation.
* Note that hardware providers can always use KM_SLEEP. So,
* they do not need to call this routine.
*
* This routine can be called from user or interrupt context.
*/
int
{
return (REQHNDL2_KMFLAG(handle));
}
/*
* Process the mechanism info structures specified by the provider
* during registration. A NULL crypto_provider_info_t indicates
* an already initialized provider descriptor.
*
* Mechanisms are not added to the kernel's mechanism table if the
* provider is a logical provider.
*
* Returns CRYPTO_SUCCESS on success, CRYPTO_ARGUMENTS if one
* of the specified mechanisms was malformed, or CRYPTO_HOST_MEMORY
* if the table of mechanisms is full.
*/
static int
{
int desc_use_count = 0;
sizeof (crypto_mech_info_t) * mcount);
}
return (CRYPTO_SUCCESS);
}
/*
* Copy the mechanism list from the provider info to the provider
* descriptor. desc->pd_mechanisms has an extra crypto_mech_info_t
* element if the provider has random_ops since we keep an internal
* mechanism, SUN_RANDOM, in this case.
*/
/*
* Need the following check as it is possible to have
* a provider that implements just random_ops and has
* pi_mechanisms == NULL.
*/
}
} else {
sizeof (crypto_mech_info_t) * mcount);
}
}
/*
* For each mechanism support by the provider, add the provider
* to the corresponding KCF mechanism mech_entry chain.
*/
break;
}
break;
continue;
/* The provider will be used for this mechanism */
}
/*
* Don't allow multiple software providers with disabled mechanisms
* to register. Subsequent enabling of mechanisms will result in
* an unsupported configuration, i.e. multiple software providers
* per mechanism.
*/
return (CRYPTO_ARGUMENTS_BAD);
if (err == KCF_SUCCESS)
return (CRYPTO_SUCCESS);
/*
* An error occurred while adding the mechanism, cleanup
* and bail.
*/
}
if (err == KCF_MECH_TAB_FULL)
return (CRYPTO_HOST_MEMORY);
return (CRYPTO_ARGUMENTS_BAD);
}
/*
* Update routine for kstat. Only privileged users are allowed to
* access this information, since this information is sensitive.
* There are some cryptographic attacks (e.g. traffic analysis)
* which can use this information.
*/
static int
{
int i;
if (rw == KSTAT_WRITE)
return (EACCES);
} else {
/* No locking done since an exact count is not required. */
}
}
return (0);
}
/*
* Utility routine called from failure paths in crypto_register_provider()
* and from crypto_load_soft_disabled().
*/
void
{
/* remove the provider from the mechanisms tables */
mech_idx++) {
}
/* remove provider from providers table */
if (remove_prov)
}
/*
* Utility routine called from crypto_load_soft_disabled(). Callers
* should have done a prior undo_register_provider().
*/
void
{
/* process the mechanisms supported by the provider */
/*
* Hold provider in providers table. We should not call
* kcf_prov_tab_add_provider() here as the provider descriptor
* is still valid which means it has an entry in the provider
* table.
*/
}
/*
* Add provider (p1) to another provider's array of providers (p2).
* Hardware and logical providers use this array to cross-reference
* each other.
*/
static void
{
}
/*
* Remove provider (p1) from another provider's array of providers (p2).
* Hardware and logical providers use this array to cross-reference
* each other.
*/
static void
{
break;
}
}
return;
}
/* detach and free kcf_provider_list structure */
}
/*
* Convert an array of logical provider handles (crypto_provider_id)
* stored in a crypto_provider_info structure into an array of provider
* descriptors (kcf_provider_desc_t) attached to a logical provider.
*/
static void
{
int i;
/* add hardware provider to each logical provider */
for (i = 0; i < count; i++) {
continue;
}
/*
* A hardware provider has to have the provider descriptor of
* every logical provider it belongs to, so it can be removed
* from the logical provider if the hardware provider
* unregisters from the framework.
*/
}
}
/*
* This routine removes a provider from all of the logical or
* hardware providers it belongs to, and frees the provider's
* array of pointers to providers.
*/
static void
{
p = e->pl_provider;
if (p->pd_prov_type == CRYPTO_HW_PROVIDER &&
p->pd_provider_list == NULL)
p->pd_flags &= ~KCF_LPROV_MEMBER;
kmem_free(e, sizeof (*e));
}
}
/*
* Dispatch events as needed for a provider. is_added flag tells
* whether the provider is registering or unregistering.
*/
void
{
int i;
/*
* Inform interested clients of the mechanisms becoming
* available/unavailable. We skip this for logical providers
* as they do not affect mechanisms.
*/
for (i = 0; i < prov_desc->pd_mech_list_count; i++) {
/* Skip any mechanisms not allowed by the policy */
continue;
}
}
/*
* Inform interested clients about the new or departing provider.
* In case of a logical provider, we need to notify the event only
* for the logical provider and not for the underlying
* providers which are known by the KCF_LPROV_MEMBER bit.
*/
}
}
static void
{
/* destroy the kstat created for this provider */
/* release reference held by desc->pd_kstat->ks_private */
}
}