static void kcf_change_mechs(kcf_provider_desc_t *provider, uint_t count, crypto_mech_name_t *array, crypto_event_change_t direction) { crypto_notify_event_change_t ec; crypto_mech_info_t *mi; kcf_prov_mech_desc_t *pmd; char *mech; int i, j, n; ASSERT(direction == CRYPTO_EVENT_CHANGE_ADDED || direction == CRYPTO_EVENT_CHANGE_REMOVED); if (provider == NULL) { /* * 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; mech = &array[i][0]; n = provider->pd_mech_list_count; for (j = 0; j < n; j++) { mi = &provider->pd_mechanisms[j]; if (strncmp(mi->cm_mech_name, mech, CRYPTO_MAX_MECH_NAME) == 0) break; } if (j == n) continue; switch (direction) { case CRYPTO_EVENT_CHANGE_ADDED: (void) kcf_add_mech_provider(mi, provider, &pmd); break; case CRYPTO_EVENT_CHANGE_REMOVED: kcf_remove_mech_provider(mech, provider); break; } /* Inform interested clients of the event */ ec.ec_provider_type = provider->pd_prov_type; ec.ec_change = direction; (void) strncpy(ec.ec_mech_name, mech, CRYPTO_MAX_MECH_NAME); kcf_walk_ntfylist(CRYPTO_EVENT_PROVIDERS_CHANGE, &ec); } }
/* * Utility routine called from failure paths in crypto_register_provider() * and from crypto_load_soft_disabled(). */ void undo_register_provider(kcf_provider_desc_t *desc, boolean_t remove_prov) { uint_t mech_idx; /* remove the provider from the mechanisms tables */ for (mech_idx = 0; mech_idx < desc->pd_mech_list_count; mech_idx++) { kcf_remove_mech_provider( desc->pd_mechanisms[mech_idx].cm_mech_name, desc); } /* remove provider from providers table */ if (remove_prov) (void) kcf_prov_tab_rem_provider(desc->pd_prov_id); }
/* * 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 init_prov_mechs(crypto_provider_info_t *info, kcf_provider_desc_t *desc) { uint_t mech_idx; uint_t cleanup_idx; int err = CRYPTO_SUCCESS; kcf_prov_mech_desc_t *pmd; int desc_use_count = 0; int mcount = desc->pd_mech_list_count; if (desc->pd_prov_type == CRYPTO_LOGICAL_PROVIDER) { if (info != NULL) { ASSERT(info->pi_mechanisms != NULL); bcopy(info->pi_mechanisms, desc->pd_mechanisms, 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. */ if (info != NULL) { if (info->pi_ops_vector->co_random_ops != NULL) { crypto_mech_info_t *rand_mi; /* * Need the following check as it is possible to have * a provider that implements just random_ops and has * pi_mechanisms == NULL. */ if (info->pi_mechanisms != NULL) { bcopy(info->pi_mechanisms, desc->pd_mechanisms, sizeof (crypto_mech_info_t) * (mcount - 1)); } rand_mi = &desc->pd_mechanisms[mcount - 1]; bzero(rand_mi, sizeof (crypto_mech_info_t)); (void) strncpy(rand_mi->cm_mech_name, SUN_RANDOM, CRYPTO_MAX_MECH_NAME); rand_mi->cm_func_group_mask = CRYPTO_FG_RANDOM; } else { ASSERT(info->pi_mechanisms != NULL); bcopy(info->pi_mechanisms, desc->pd_mechanisms, sizeof (crypto_mech_info_t) * mcount); } } /* * For each mechanism support by the provider, add the provider * to the corresponding KCF mechanism mech_entry chain. */ for (mech_idx = 0; mech_idx < desc->pd_mech_list_count; mech_idx++) { crypto_mech_info_t *mi = &desc->pd_mechanisms[mech_idx]; if ((mi->cm_mech_flags & CRYPTO_KEYSIZE_UNIT_IN_BITS) && (mi->cm_mech_flags & CRYPTO_KEYSIZE_UNIT_IN_BYTES)) { err = CRYPTO_ARGUMENTS_BAD; break; } if (desc->pd_flags & CRYPTO_HASH_NO_UPDATE && mi->cm_func_group_mask & CRYPTO_FG_DIGEST) { /* * We ask the provider to specify the limit * per hash mechanism. But, in practice, a * hardware limitation means all hash mechanisms * will have the same maximum size allowed for * input data. So, we make it a per provider * limit to keep it simple. */ if (mi->cm_max_input_length == 0) { err = CRYPTO_ARGUMENTS_BAD; break; } else { desc->pd_hash_limit = mi->cm_max_input_length; } } if ((err = kcf_add_mech_provider(mech_idx, desc, &pmd)) != KCF_SUCCESS) break; if (pmd == NULL) continue; /* The provider will be used for this mechanism */ desc_use_count++; } /* * 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. */ if (desc_use_count == 0 && desc->pd_prov_type == CRYPTO_SW_PROVIDER) return (CRYPTO_ARGUMENTS_BAD); if (err == KCF_SUCCESS) return (CRYPTO_SUCCESS); /* * An error occurred while adding the mechanism, cleanup * and bail. */ for (cleanup_idx = 0; cleanup_idx < mech_idx; cleanup_idx++) { kcf_remove_mech_provider( desc->pd_mechanisms[cleanup_idx].cm_mech_name, desc); } if (err == KCF_MECH_TAB_FULL) return (CRYPTO_HOST_MEMORY); return (CRYPTO_ARGUMENTS_BAD); }
/* * 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 crypto_unregister_provider(crypto_kcf_provider_handle_t handle) { uint_t mech_idx; kcf_provider_desc_t *desc; kcf_prov_state_t saved_state; /* lookup provider descriptor */ if ((desc = kcf_prov_tab_lookup((crypto_provider_id_t)handle)) == NULL) return (CRYPTO_UNKNOWN_PROVIDER); mutex_enter(&desc->pd_lock); /* * Check if any other thread is disabling or removing * this provider. We return if this is the case. */ if (desc->pd_state >= KCF_PROV_DISABLED) { mutex_exit(&desc->pd_lock); /* Release reference held by kcf_prov_tab_lookup(). */ KCF_PROV_REFRELE(desc); return (CRYPTO_BUSY); } saved_state = desc->pd_state; desc->pd_state = KCF_PROV_REMOVED; if (saved_state == KCF_PROV_BUSY) { /* * The per-provider taskq threads may be waiting. We * signal them so that they can start failing requests. */ cv_broadcast(&desc->pd_resume_cv); } if (desc->pd_prov_type == CRYPTO_SW_PROVIDER) { /* * Check if this provider is currently being used. * pd_irefcnt is the number of holds from the internal * structures. We add one to account for the above lookup. */ if (desc->pd_refcnt > desc->pd_irefcnt + 1) { desc->pd_state = saved_state; mutex_exit(&desc->pd_lock); /* Release reference held by kcf_prov_tab_lookup(). */ KCF_PROV_REFRELE(desc); /* * The administrator presumably will stop the clients * thus removing the holds, when they get the busy * return value. Any retry will succeed then. */ return (CRYPTO_BUSY); } } mutex_exit(&desc->pd_lock); if (desc->pd_prov_type != CRYPTO_SW_PROVIDER) { remove_provider(desc); } if (desc->pd_prov_type != CRYPTO_LOGICAL_PROVIDER) { /* remove the provider from the mechanisms tables */ for (mech_idx = 0; mech_idx < desc->pd_mech_list_count; mech_idx++) { kcf_remove_mech_provider( desc->pd_mechanisms[mech_idx].cm_mech_name, desc); } } /* remove provider from providers table */ if (kcf_prov_tab_rem_provider((crypto_provider_id_t)handle) != CRYPTO_SUCCESS) { /* Release reference held by kcf_prov_tab_lookup(). */ KCF_PROV_REFRELE(desc); return (CRYPTO_UNKNOWN_PROVIDER); } delete_kstat(desc); if (desc->pd_prov_type == CRYPTO_SW_PROVIDER) { /* Release reference held by kcf_prov_tab_lookup(). */ KCF_PROV_REFRELE(desc); /* * Wait till the existing requests complete. */ mutex_enter(&desc->pd_lock); while (desc->pd_state != KCF_PROV_FREED) cv_wait(&desc->pd_remove_cv, &desc->pd_lock); mutex_exit(&desc->pd_lock); } else { /* * Wait until requests that have been sent to the provider * complete. */ mutex_enter(&desc->pd_lock); while (desc->pd_irefcnt > 0) cv_wait(&desc->pd_remove_cv, &desc->pd_lock); mutex_exit(&desc->pd_lock); } kcf_do_notify(desc, B_FALSE); if (desc->pd_prov_type == CRYPTO_SW_PROVIDER) { /* * This is the only place where kcf_free_provider_desc() * is called directly. KCF_PROV_REFRELE() should free the * structure in all other places. */ ASSERT(desc->pd_state == KCF_PROV_FREED && desc->pd_refcnt == 0); kcf_free_provider_desc(desc); } else { KCF_PROV_REFRELE(desc); } return (CRYPTO_SUCCESS); }