static void free_soft_config_entry(kcf_soft_conf_entry_t *p) { kmem_free(p->ce_name, strlen(p->ce_name) + 1); crypto_free_mech_list(p->ce_mechs, p->ce_count); kmem_free(p, sizeof (kcf_soft_conf_entry_t)); }
/* * Called from CRYPTO_LOAD_DEV_DISABLED ioctl. * If new_count is 0, then completely remove the entry. */ int crypto_load_dev_disabled(char *name, uint_t instance, uint_t new_count, crypto_mech_name_t *new_array) { kcf_provider_desc_t *provider = NULL; kcf_provider_desc_t **provider_array; crypto_mech_name_t *prev_array; uint_t provider_count, prev_count; 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) { kcf_policy_remove_by_dev(name, instance, &prev_count, &prev_array); } else if ((rv = kcf_policy_load_dev_disabled(name, instance, new_count, new_array, &prev_count, &prev_array)) != CRYPTO_SUCCESS) { return (rv); } /* * Get provider table entries matching name and instance * for providers that are are in a usable or unverified state. */ rv = kcf_get_hw_prov_tab(&provider_count, &provider_array, KM_SLEEP, name, instance, B_TRUE); 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) { kcf_compare_mechs(new_count, new_array, prev_count, prev_array); kcf_change_mechs(provider, prev_count, prev_array, CRYPTO_EVENT_CHANGE_ADDED); } kcf_change_mechs(provider, new_count, new_array, CRYPTO_EVENT_CHANGE_REMOVED); } kcf_free_provider_tab(provider_count, provider_array); crypto_free_mech_list(prev_array, prev_count); return (rv); }
/* * 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 add_soft_config(char *name, uint_t count, crypto_mech_name_t *array) { static uint_t soft_config_count = 0; kcf_soft_conf_entry_t *prev = NULL, *entry = NULL, *new_entry, *p; size_t name_len; /* * Allocate storage for a new entry. * Free later if an entry already exists. */ name_len = strlen(name) + 1; new_entry = kmem_zalloc(sizeof (kcf_soft_conf_entry_t), KM_SLEEP); new_entry->ce_name = kmem_alloc(name_len, KM_SLEEP); (void) strcpy(new_entry->ce_name, name); mutex_enter(&soft_config_mutex); p = soft_config_list; if (p != NULL) { do { if (strncmp(name, p->ce_name, MAXNAMELEN) == 0) { entry = p; break; } prev = p; } while ((p = p->ce_next) != NULL); } if (entry == NULL) { if (count == 0) { mutex_exit(&soft_config_mutex); kmem_free(new_entry->ce_name, name_len); kmem_free(new_entry, sizeof (kcf_soft_conf_entry_t)); return (CRYPTO_SUCCESS); } if (soft_config_count > KCF_MAX_CONFIG_ENTRIES) { mutex_exit(&soft_config_mutex); kmem_free(new_entry->ce_name, name_len); kmem_free(new_entry, sizeof (kcf_soft_conf_entry_t)); cmn_err(CE_WARN, "out of soft_config_list entries"); return (CRYPTO_FAILED); } /* add to head of list */ new_entry->ce_next = soft_config_list; soft_config_list = new_entry; soft_config_count++; entry = new_entry; } else { kmem_free(new_entry->ce_name, name_len); kmem_free(new_entry, sizeof (kcf_soft_conf_entry_t)); } /* mechanism count == 0 means remove entry from list */ if (count == 0) { if (prev == NULL) { /* remove first in list */ soft_config_list = entry->ce_next; } else { prev->ce_next = entry->ce_next; } soft_config_count--; mutex_exit(&soft_config_mutex); /* free entry */ free_soft_config_entry(entry); return (CRYPTO_SUCCESS); } /* replace mechanisms */ if (entry->ce_mechs != NULL) crypto_free_mech_list(entry->ce_mechs, entry->ce_count); entry->ce_mechs = array; entry->ce_count = count; mutex_exit(&soft_config_mutex); return (CRYPTO_SUCCESS); }
/* * Called from CRYPTO_LOAD_SOFT_DISABLED ioctl. * If new_count is 0, then completely remove the entry. */ int crypto_load_soft_disabled(char *name, uint_t new_count, crypto_mech_name_t *new_array) { kcf_provider_desc_t *provider = NULL; crypto_mech_name_t *prev_array; uint_t prev_count = 0; int rv; provider = kcf_prov_tab_lookup_by_name(name); if (provider != NULL) { mutex_enter(&provider->pd_lock); /* * Check if any other thread is disabling or removing * this provider. We return if this is the case. */ if (provider->pd_state >= KCF_PROV_DISABLED) { mutex_exit(&provider->pd_lock); KCF_PROV_REFRELE(provider); return (CRYPTO_BUSY); } provider->pd_state = KCF_PROV_DISABLED; mutex_exit(&provider->pd_lock); undo_register_provider(provider, B_TRUE); KCF_PROV_REFRELE(provider); if (provider->pd_kstat != NULL) KCF_PROV_REFRELE(provider); mutex_enter(&provider->pd_lock); /* Wait till the existing requests complete. */ while (provider->pd_state != KCF_PROV_FREED) { cv_wait(&provider->pd_remove_cv, &provider->pd_lock); } mutex_exit(&provider->pd_lock); } if (new_count == 0) { kcf_policy_remove_by_name(name, &prev_count, &prev_array); crypto_free_mech_list(prev_array, prev_count); rv = CRYPTO_SUCCESS; goto out; } /* put disabled mechanisms into policy table */ if ((rv = kcf_policy_load_soft_disabled(name, new_count, new_array, &prev_count, &prev_array)) == CRYPTO_SUCCESS) { crypto_free_mech_list(prev_array, prev_count); } out: if (provider != NULL) { redo_register_provider(provider); if (provider->pd_kstat != NULL) KCF_PROV_REFHOLD(provider); mutex_enter(&provider->pd_lock); provider->pd_state = KCF_PROV_READY; mutex_exit(&provider->pd_lock); } 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 && (in_soft_config_list(name)) && (modload("crypto", name) != -1)) { struct modctl *mcp; boolean_t load_again = B_FALSE; if ((mcp = mod_hold_by_name(name)) != NULL) { mcp->mod_loadflags |= MOD_NOAUTOUNLOAD; /* memory pressure may have unloaded module */ if (!mcp->mod_installed) load_again = B_TRUE; mod_release_mod(mcp); if (load_again) (void) modload("crypto", name); } } } return (rv); }
/* ARGSUSED */ static int get_soft_info(dev_t dev, caddr_t arg, int mode, int *rval) { crypto_get_soft_info_t soft_info; crypto_mech_name_t *entries; size_t copyout_size; uint_t count; ulong_t offset; char *name; if (copyin(arg, &soft_info, sizeof (soft_info)) != 0) return (EFAULT); name = soft_info.si_name; /* make sure the provider name is null terminated */ if (!null_terminated(name)) { soft_info.si_return_value = CRYPTO_ARGUMENTS_BAD; if (copyout(&soft_info, arg, sizeof (soft_info)) != 0) { return (EFAULT); } return (0); } /* get mechanism names from the core module */ if (crypto_get_soft_info(name, &count, &entries) != 0) { soft_info.si_return_value = CRYPTO_FAILED; if (copyout(&soft_info, arg, sizeof (soft_info)) != 0) { return (EFAULT); } return (0); } /* check if buffer is too small */ if (count > soft_info.si_count) { soft_info.si_count = count; soft_info.si_return_value = CRYPTO_BUFFER_TOO_SMALL; crypto_free_mech_list(entries, count); if (copyout(&soft_info, arg, sizeof (soft_info)) != 0) { return (EFAULT); } return (0); } soft_info.si_count = count; soft_info.si_return_value = CRYPTO_SUCCESS; copyout_size = count * sizeof (crypto_mech_name_t); /* copyout the first stuff */ if (copyout(&soft_info, arg, sizeof (soft_info)) != 0) { crypto_free_mech_list(entries, count); return (EFAULT); } /* copyout entries */ offset = offsetof(crypto_get_soft_info_t, si_list); if (copyout(entries, arg + offset, copyout_size) != 0) { crypto_free_mech_list(entries, count); return (EFAULT); } crypto_free_mech_list(entries, count); return (0); }
/* ARGSUSED */ static int get_dev_info(dev_t dev, caddr_t arg, int mode, int *rval) { crypto_get_dev_info_t dev_info; crypto_mech_name_t *entries; size_t copyout_size; uint_t count; ulong_t offset; char *dev_name; int rv; if (copyin(arg, &dev_info, sizeof (dev_info)) != 0) return (EFAULT); dev_name = dev_info.di_dev_name; /* make sure the device name is null terminated */ if (!null_terminated(dev_name)) { dev_info.di_return_value = CRYPTO_ARGUMENTS_BAD; if (copyout(&dev_info, arg, sizeof (dev_info)) != 0) { return (EFAULT); } return (0); } /* get mechanism names from the core module */ if ((rv = crypto_get_dev_info(dev_name, dev_info.di_dev_instance, &count, &entries)) != CRYPTO_SUCCESS) { dev_info.di_return_value = rv; if (copyout(&dev_info, arg, sizeof (dev_info)) != 0) { return (EFAULT); } return (0); } /* check if buffer is too small */ if (count > dev_info.di_count) { dev_info.di_count = count; dev_info.di_return_value = CRYPTO_BUFFER_TOO_SMALL; crypto_free_mech_list(entries, count); if (copyout(&dev_info, arg, sizeof (dev_info)) != 0) { return (EFAULT); } return (0); } dev_info.di_count = count; dev_info.di_return_value = CRYPTO_SUCCESS; copyout_size = count * sizeof (crypto_mech_name_t); /* copyout the first stuff */ if (copyout(&dev_info, arg, sizeof (dev_info)) != 0) { crypto_free_mech_list(entries, count); return (EFAULT); } /* copyout entries */ offset = offsetof(crypto_get_dev_info_t, di_list); if (copyout(entries, arg + offset, copyout_size) != 0) { crypto_free_mech_list(entries, count); return (EFAULT); } crypto_free_mech_list(entries, count); return (0); }