/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* 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.
* The initial list is just software providers loaded by kcf_soft_config_init().
* Additional entries may appear for both hardware and software providers
* and updates this table using the CRYPTO_LOAD_SOFT_CONFIG ioctl.
* Further cryptoadm commands modify this file and update this table with ioctl.
* This list is protected by the soft_config_mutex.
*/
uint_t *, int);
static void free_soft_config_entry(kcf_soft_conf_entry_t *);
#if DEBUG
extern int kcf_frmwrk_debug;
static void kcf_soft_config_dump(char *message);
#endif /* DEBUG */
/*
* Count and return the number of mechanisms in an array of crypto_mech_name_t
* (excluding final NUL-character string element).
*/
static int
int count;
return (count);
}
/*
* Initialize a mutex and populate soft_config_list with default entries
* of kernel software providers.
* Called from kcf module _init().
*/
void
kcf_soft_config_init(void)
{
typedef struct {
char *name;
/*
* This provides initial default values to soft_config_list.
* (without line breaks and indenting):
*
* des:supportedlist=CKM_DES_CBC,CKM_DES_ECB,CKM_DES3_CBC,CKM_DES3_ECB
* aes:supportedlist=CKM_AES_ECB,CKM_AES_CBC,CKM_AES_CTR,CKM_AES_CCM,\
* CKM_AES_GCM,CKM_AES_GMAC
* arcfour:supportedlist=CKM_RC4
* blowfish:supportedlist=CKM_BLOWFISH_ECB,CKM_BLOWFISH_CBC
* ecc:supportedlist=CKM_EC_KEY_PAIR_GEN,CKM_ECDH1_DERIVE,CKM_ECDSA,\
* CKM_ECDSA_SHA1
* sha1:supportedlist=CKM_SHA_1,CKM_SHA_1_HMAC_GENERAL,CKM_SHA_1_HMAC
* sha2:supportedlist=CKM_SHA256,CKM_SHA256_HMAC,\
* CKM_SHA256_HMAC_GENERAL,CKM_SHA384,CKM_SHA384_HMAC,\
* CKM_SHA384_HMAC_GENERAL,CKM_SHA512,CKM_SHA512_HMAC,\
* CKM_SHA512_HMAC_GENERAL
* md4:supportedlist=CKM_MD4
* md5:supportedlist=CKM_MD5,CKM_MD5_HMAC_GENERAL,CKM_MD5_HMAC
* rsa:supportedlist=CKM_RSA_PKCS,CKM_RSA_X_509,CKM_MD5_RSA_PKCS,\
* CKM_SHA1_RSA_PKCS,CKM_SHA256_RSA_PKCS,CKM_SHA384_RSA_PKCS,\
* CKM_SHA512_RSA_PKCS
* swrand:supportedlist=random
*
* WARNING: If you add a new kernel crypto provider or mechanism,
* you must update these structures.
*
* 1. To add a new mechanism to a provider add the string to the
* appropriate array below and comment above.
*
* 2. To add a new provider, create a new *_mechs array listing the
* provider's mechanism(s) and a new comment line above.
* Add the new *_mechs array to initial_soft_config_entry[].
*
* 3. If appropriate (that is the new mechanism is needed before
* cryptosvc runs), add to kcf_init_mech_tabs() in kcf_mech_tabs.c.
*/
"CKM_DES_CBC", "CKM_DES_ECB", "CKM_DES3_CBC", "CKM_DES3_ECB", ""};
"CKM_AES_ECB", "CKM_AES_CBC", "CKM_AES_CTR", "CKM_AES_CCM",
"CKM_AES_GCM", "CKM_AES_GMAC", ""};
"CKM_RC4", ""};
"CKM_BLOWFISH_ECB", "CKM_BLOWFISH_CBC", ""};
"CKM_EC_KEY_PAIR_GEN", "CKM_ECDH1_DERIVE", "CKM_ECDSA",
"CKM_ECDSA_SHA1", ""};
"CKM_SHA_1", "CKM_SHA_1_HMAC_GENERAL", "CKM_SHA_1_HMAC", ""};
"CKM_SHA256", "CKM_SHA256_HMAC", "CKM_SHA256_HMAC_GENERAL",
"CKM_SHA384", "CKM_SHA384_HMAC", "CKM_SHA384_HMAC_GENERAL",
"CKM_SHA512", "CKM_SHA512_HMAC", "CKM_SHA512_HMAC_GENERAL", ""};
"CKM_MD4", ""};
"CKM_MD5", "CKM_MD5_HMAC_GENERAL", "CKM_MD5_HMAC", ""};
"CKM_RSA_PKCS", "CKM_RSA_X_509", "CKM_MD5_RSA_PKCS",
"CKM_SHA1_RSA_PKCS", "CKM_SHA256_RSA_PKCS", "CKM_SHA384_RSA_PKCS",
"CKM_SHA512_RSA_PKCS", ""};
"random", NULL};
static initial_soft_config_entry_t
"des", des_mechs,
"aes", aes_mechs,
"arcfour", arcfour_mechs,
"blowfish", blowfish_mechs,
"ecc", ecc_mechs,
"sha1", sha1_mechs,
"sha2", sha2_mechs,
"md4", md4_mechs,
"md5", md5_mechs,
"rsa", rsa_mechs,
"swrand", swrand_mechs
};
const int initial_soft_config_entries =
sizeof (initial_soft_config_entry)
/ sizeof (initial_soft_config_entry_t);
int i;
/*
* Initialize soft_config_list with default providers.
* Populate the linked list backwards so the first entry appears first.
*/
for (i = initial_soft_config_entries - 1; i >= 0; --i) {
int mech_count, r;
/* allocate/initialize memory for mechanism list */
if (r != 0)
"add_soft_config(%s) failed; returned %d\n",
p->name, r);
}
#if DEBUG
if (kcf_frmwrk_debug >= 1)
kcf_soft_config_dump("kcf_soft_config_init");
#endif /* DEBUG */
}
#if DEBUG
/*
* Dump soft_config_list, containing a list of kernel software providers
* and (optionally) hardware providers, with updates from kcf.conf.
* Dump mechanism lists too if kcf_frmwrk_debug is >= 2.
*/
static void
{
uint_t i;
printf("Soft provider config list soft_config_list: %s\n",
if (kcf_frmwrk_debug >= 2) {
printf("\tce_mechs: ");
for (i = 0; i < p->ce_count; i++) {
}
printf("\n");
}
}
printf("(end of soft_config_list)\n");
}
#endif /* DEBUG */
/*
* 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.
*
* Called from crypto_get_dev_list().
*/
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;
}
/*
* Return a list of kernel hardware providers and a count of each
* provider's supported mechanisms.
* 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);
}
/*
* Return a buffer containing the null terminated names of software providers
* loaded by CRYPTO_LOAD_SOFT_CONFIG.
* Called from the CRYPTO_GET_SOFT_LIST ioctl.
*/
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);
}
/*
* Check if a mechanism name is already in a mechanism name array
* Called by crypto_get_dev_info().
*/
static boolean_t
{
int i;
for (i = 0; i < count; i++) {
sizeof (crypto_mech_name_t)) == 0)
return (B_TRUE);
}
return (B_FALSE);
}
/*
* Return a list of kernel hardware providers for a given name and instance.
* For each entry, also return a list of their supported mechanisms.
* Called from the CRYPTO_GET_DEV_INFO ioctl.
*/
int
{
int rv;
int i, j, k = 0, max_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);
/* Count all mechanisms supported by all providers */
max_count = 0;
for (i = 0; i < provider_count; i++)
if (max_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++) {
/* check for duplicate */
mech_names, k))
continue;
&mech_names[k][0], sizeof (crypto_mech_name_t));
k++;
}
}
/* resize */
if (k != max_count) {
k * sizeof (crypto_mech_name_t));
max_count * sizeof (crypto_mech_name_t));
}
out:
*count = k;
*array = mech_names;
return (CRYPTO_SUCCESS);
}
/*
* Given a kernel software provider name, return a list of mechanisms
* it supports.
* Called from the CRYPTO_GET_SOFT_INFO ioctl.
*/
int
{
int rv;
char *tmp;
int name_len;
/* strlen("crypto/") + NULL terminator == 8 */
return (CRYPTO_ARGUMENTS_BAD);
}
return (CRYPTO_ARGUMENTS_BAD);
}
}
(void) ddi_modclose(modh);
return (rv);
}
/*
* Change the mechanism list for a provider.
* If "direction" is CRYPTO_MECH_ADDED, add new mechanisms.
* If "direction" is CRYPTO_MECH_REMOVED, remove the mechanism list.
* Called from crypto_load_dev_disabled().
*/
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
{
/*
* 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
{
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. */
/* wait 1 second and try again. */
}
}
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.
*/
if ((new_count < prev_count) &&
/* 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
{
}
/*
* Unload a kernel software crypto module.
* 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);
}
/*
* Free the list of kernel hardware crypto providers.
* Called by get_dev_list() for the 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
{
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);
}
/*
* Free memory for elements in a kcf_soft_config_entry_t. This entry must
* have been previously removed from the soft_config_list linked list.
*/
static void
{
kmem_free(p, sizeof (kcf_soft_conf_entry_t));
}
/*
* Store 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.
*
* Called from kcf_soft_config_init() (to initially populate the list
* with default kernel providers) and from crypto_load_soft_config() for
* the CRYPTO_LOAD_SOFT_CONFIG ioctl (for third-party kernel modules).
*
* Important note: the array argument must be allocated memory
* since it is consumed in soft_config_list.
*
* Parameters:
* name Provider name to add or remove.
* count Number of mechanisms to add.
* If 0, then remove provider from the list (instead of add).
* array An array of "count" mechanism names (use only if count > 0).
*/
static int
{
/*
* Allocate storage for a new entry.
* Free later if an entry already exists.
*/
/* Search to see if provider already in soft_config_list */
entry = p;
break;
}
prev = p;
}
if (count == 0) { /* free memory--no entry exists to remove */
return (CRYPTO_SUCCESS);
}
return (CRYPTO_FAILED);
}
/* add new provider to head of list */
} else { /* mechanism already in soft_config_list */
}
if (count == 0) { /* remove provider entry from soft_config_list */
/* entry to remove is at the head of the list */
} else {
}
/* free entry */
} else { /* add provider entry to soft_config_list */
/*
* Don't replace a mechanism list if it's already present.
* This is because the default entries for Software providers
* are more up-to-date than possibly stale entries in kcf.conf.
* If an entry is to be deleted, the proper way to do it is
* to add it to the disablelist (with cryptoadm(1M)),
* instead of removing it from the supportedlist.
*/
} else { /* ignore replacement mechanism list */
}
}
return (CRYPTO_SUCCESS);
}
/*
* This function removes a module entry from the soft_config_list.
*
* This comes in handy if FIPS 140 is enabled, but fails to validate. At
* which point when the kernel reports its' supported modules, it shows only
* those that are not within the boundary
*/
void
{
/* Search for provider in soft_config_list */
entry = p;
break;
}
prev = p;
}
/* entry to remove is at the head of the list */
} else {
}
/* free entry */
}
/*
* 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
{
int i;
p = soft_config_list;
while (p != NULL) {
for (i = 0; i < p->ce_count; i++) {
CRYPTO_MAX_MECH_NAME) == 0) {
break;
}
}
p = next;
}
if (name_len == 0)
return (CRYPTO_FAILED);
return (CRYPTO_SUCCESS);
}