kcf_cryptoadm.c revision c892ebf1bef94f4f922f282c11516677c134dbe0
/*
* 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"
/*
* Core KCF (Kernel Cryptographic Framework). This file implements
* the cryptoadm entry points.
*/
/* protects the the soft_config_list. */
/*
* This linked list contains software configuration entries that
* are loaded into the kernel by the CRYPTO_LOAD_SOFT_CONFIG ioctl.
* It is protected by the soft_config_mutex.
*/
uint_t *, int);
static void free_soft_config_entry(kcf_soft_conf_entry_t *);
void
kcf_soft_config_init(void)
{
}
/*
* Utility routine to identify the providers to filter out and
* present only one provider. This happens when a hardware provider
* registers multiple units of the same device instance.
*/
static void
{
int i, j;
int n = 0;
for (i = 0; i < count; i++) {
if (skip_providers[i] == 1)
continue;
prov1 = provider_array[i];
for (j = i + 1; j < count; j++) {
prov2 = provider_array[j];
MAXNAMELEN) == 0 &&
skip_providers[j] = 1;
}
}
n++;
}
*new_count = n;
}
/* called from the CRYPTO_GET_DEV_LIST ioctl */
int
{
char *skip_providers;
/*
* Take snapshot of provider table returning only hardware providers
* that are in a usable state. Logical providers not included.
*/
if (rval != CRYPTO_SUCCESS)
return (rval);
if (provider_count == 0) {
*count = 0;
return (CRYPTO_SUCCESS);
}
skip_providers_size = provider_count * sizeof (char);
mech_counts_size = provider_count * sizeof (int);
mech_counts, &new_count);
for (i = 0, j = 0; i < provider_count; i++) {
if (skip_providers[i] == 1) {
ASSERT(mech_counts[i] == 0);
continue;
}
pd = provider_array[i];
p[j].le_mechanism_count = mech_counts[i];
j++;
}
*array = p;
return (CRYPTO_SUCCESS);
}
/*
* Called from the CRYPTO_GET_SOFT_LIST ioctl, this routine returns
* a buffer containing the null terminated names of software providers
* loaded by CRYPTO_LOAD_SOFT_CONFIG.
*/
int
{
/* first estimate */
cnt++;
}
if (cnt == 0)
goto out;
final_size = 0;
final_count = 0;
/* check for enough space */
n = n << 1;
goto again;
}
final_size += name_len;
final_count++;
}
ASSERT(final_size <= n);
/* check if buffer we allocated is too large */
if (final_size < n) {
char *final_buffer;
}
out:
*count = final_count;
*len = final_size;
return (CRYPTO_SUCCESS);
}
/* called from the CRYPTO_GET_DEV_INFO ioctl */
int
{
int rv;
int i, j, k, all_count;
/*
* Get provider table entries matching name and instance
* for hardware providers that are in a usable state.
* Logical providers not included. NULL name matches
* all hardware providers.
*/
if (rv != CRYPTO_SUCCESS)
return (rv);
if (provider_count == 0)
return (CRYPTO_ARGUMENTS_BAD);
/* Get count */
all_count = 0;
for (i = 0; i < provider_count; i++)
if (all_count == 0) {
mech_names = NULL;
goto out;
}
/* Allocate space and copy mech names */
KM_SLEEP);
k = 0;
for (i = 0; i < provider_count; i++) {
pd = provider_array[i];
for (j = 0; j < pd->pd_mech_list_count; j++, k++)
&mech_names[k][0], sizeof (crypto_mech_name_t));
}
out:
*array = mech_names;
return (CRYPTO_SUCCESS);
}
/* called from the CRYPTO_GET_SOFT_INFO ioctl */
int
{
int rv;
if (in_soft_config_list(name)) {
char *tmp;
int name_len;
/* strlen("crypto/") + NULL terminator == 8 */
return (CRYPTO_ARGUMENTS_BAD);
}
return (CRYPTO_ARGUMENTS_BAD);
}
} else {
return (CRYPTO_ARGUMENTS_BAD);
}
}
(void) ddi_modclose(modh);
return (rv);
}
static void
{
char *mech;
int i, j, n;
/*
* Nothing to add or remove from the tables since
* the provider isn't registered.
*/
return;
}
for (i = 0; i < count; i++) {
if (array[i][0] == '\0')
continue;
n = provider->pd_mech_list_count;
for (j = 0; j < n; j++) {
CRYPTO_MAX_MECH_NAME) == 0)
break;
}
if (j == n)
continue;
switch (direction) {
case CRYPTO_MECH_ADDED:
break;
case CRYPTO_MECH_REMOVED:
break;
}
/* Inform interested clients of the event */
}
}
/*
* If a mech name in the second array (prev_array) is also in the
* first array, then a NULL character is written into the first byte
* of the mech name in the second array. This effectively removes
* the mech name from the second array.
*/
static void
{
int i, j;
for (i = 0; i < prev_count; i++) {
for (j = 0; j < count; j++) {
CRYPTO_MAX_MECH_NAME) == 0) {
prev_array[i][0] = '\0';
}
}
}
}
/*
* Called from CRYPTO_LOAD_DEV_DISABLED ioctl.
* If new_count is 0, then completely remove the entry.
*/
int
{
int i, rv = CRYPTO_SUCCESS;
/*
* Remove the policy entry if new_count is 0, otherwise put disabled
* mechanisms into policy table.
*/
if (new_count == 0) {
&prev_array);
return (rv);
}
/*
* Get provider table entries matching name and instance
* for providers that are are in a usable or unverified state.
*/
if (rv != CRYPTO_SUCCESS)
return (rv);
for (i = 0; i < provider_count; i++) {
provider = provider_array[i];
/* previously disabled mechanisms may become enabled */
if (prev_array != NULL) {
}
}
return (rv);
}
/*
* Called from CRYPTO_LOAD_SOFT_DISABLED ioctl.
* If new_count is 0, then completely remove the entry.
*/
int
{
uint_t prev_count = 0;
int rv;
/*
* Check if any other thread is disabling or removing
* this provider. We return if this is the case.
*/
return (CRYPTO_BUSY);
}
/* Wait till the existing requests complete. */
}
}
if (new_count == 0) {
rv = CRYPTO_SUCCESS;
goto out;
}
/* put disabled mechanisms into policy table */
}
out:
} else if (rv == CRYPTO_SUCCESS) {
/*
* There are some cases where it is useful to kCF clients
* to have a provider whose mechanism is enabled now to be
* available. So, we attempt to load it here.
*
* The check, new_count < prev_count, ensures that we do this
* only in the case where a mechanism(s) is now enabled.
* This check assumes that enable and disable are separate
* administrative actions and are not done in a single action.
*/
/* memory pressure may have unloaded module */
if (!mcp->mod_installed)
load_again = B_TRUE;
if (load_again)
}
}
}
return (rv);
}
/* called from the CRYPTO_LOAD_SOFT_CONFIG ioctl */
int
{
}
/* called from the CRYPTO_UNLOAD_SOFT_MODULE ioctl */
int
{
int error;
/* verify that 'name' refers to a registered crypto provider */
return (CRYPTO_UNKNOWN_PROVIDER);
/*
* We save the module id and release the reference. We need to
* do this as modunload() calls unregister which waits for the
* refcnt to drop to zero.
*/
}
}
return (CRYPTO_SUCCESS);
}
/* called from CRYPTO_GET_DEV_LIST ioctl */
void
{
return;
}
/*
* Returns duplicate array of mechanisms. The array is allocated and
* must be freed by the caller.
*/
static int
{
uint_t n;
uint_t i;
if ((n = provider->pd_mech_list_count) == 0) {
*count = 0;
return (CRYPTO_SUCCESS);
}
if (mech_names == NULL)
return (CRYPTO_HOST_MEMORY);
for (i = 0; i < n; i++) {
&mech_names[i][0], sizeof (crypto_mech_name_t));
}
*count = n;
*array = mech_names;
return (CRYPTO_SUCCESS);
}
/*
* Returns B_TRUE if the specified mechanism is disabled, B_FALSE otherwise.
*/
{
uint_t i;
switch (prov_type) {
case CRYPTO_SW_PROVIDER:
/* no policy for provider - so mechanism can't be disabled */
return (B_FALSE);
break;
case CRYPTO_HW_PROVIDER:
/* no policy for provider - so mechanism can't be disabled */
return (B_FALSE);
break;
}
for (i = 0; i < policy->pd_disabled_count; i ++) {
CRYPTO_MAX_MECH_NAME) == 0) {
return (B_TRUE);
}
}
return (B_FALSE);
}
/*
* Returns B_TRUE if the specified mechanism is disabled, B_FALSE otherwise.
*
* This is a wrapper routine around is_mech_disabled_byname() above and
* takes a pointer kcf_provider_desc structure as argument.
*/
{
}
/*
* Lock the logical provider just in case one of its hardware
* provider members unregisters.
*/
pd = e->pl_provider;
/* find out if mechanism is offered by hw provider */
for (i = 0; i < count; i++) {
name, MAXNAMELEN) == 0) {
break;
}
}
if (i == count)
continue;
if (found)
break;
}
/*
* If we found the mechanism, then it means it is still enabled for
* at least one hardware provider, so the mech can't be disabled
* for the logical provider.
*/
return (!found);
}
/*
* Builds array of permitted mechanisms. The array is allocated and
* must be freed by the caller.
*/
int
{
crypto_mech_name_t *mech_names, *p;
uint_t i;
/*
* Compute number of 'permitted mechanisms', which is
* 'supported mechanisms' - 'disabled mechanisms'.
*/
for (i = 0; i < scnt; i++) {
if (is_mech_disabled(provider,
dcnt++;
}
}
/* all supported mechanisms have been disabled */
*count = 0;
return (CRYPTO_SUCCESS);
}
kmflag);
if (mech_names == NULL)
return (CRYPTO_HOST_MEMORY);
/* build array of permitted mechanisms */
for (i = 0, p = mech_names; i < scnt; i++) {
if (!is_mech_disabled(provider,
p++, sizeof (crypto_mech_name_t));
}
}
*array = mech_names;
return (CRYPTO_SUCCESS);
}
static void
{
kmem_free(p, sizeof (kcf_soft_conf_entry_t));
}
/*
* Called from the CRYPTO_LOAD_SOFT_CONFIG ioctl, this routine stores
* configuration information for software providers in a linked list.
* If the list already contains an entry for the specified provider
* and the specified mechanism list has at least one mechanism, then
* the mechanism list for the provider is updated. If the mechanism list
* is empty, the entry for the provider is removed.
*
* Important note: the array argument is consumed.
*/
static int
{
static uint_t soft_config_count = 0;
/*
* Allocate storage for a new entry.
* Free later if an entry already exists.
*/
p = soft_config_list;
if (p != NULL) {
do {
entry = p;
break;
}
prev = p;
}
if (count == 0) {
return (CRYPTO_SUCCESS);
}
if (soft_config_count > KCF_MAX_CONFIG_ENTRIES) {
return (CRYPTO_FAILED);
}
/* add to head of list */
} else {
}
/* mechanism count == 0 means remove entry from list */
if (count == 0) {
/* remove first in list */
} else {
}
/* free entry */
return (CRYPTO_SUCCESS);
}
/* replace mechanisms */
return (CRYPTO_SUCCESS);
}
/*
* This routine searches the soft_config_list for the first entry that
* has the specified mechanism in its mechanism list. If found,
* a buffer containing the name of the software module that implements
* the mechanism is allocated and stored in 'name'.
*/
int
{
kcf_soft_conf_entry_t *p, *next;
char tmp_name[MAXNAMELEN];
int i;
p = soft_config_list;
while (p != NULL) {
for (i = 0; i < p->ce_count; i++) {
break;
}
}
p = next;
}
if (name_len == 0)
return (CRYPTO_FAILED);
return (CRYPTO_SUCCESS);
}
/*
* This routine searches the soft_config_list for the specified
* software provider, returning B_TRUE if it is in the list.
*/
in_soft_config_list(char *provider_name)
{
break;
}
}
return (rv);
}