예제 #1
0
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);
	}
}
예제 #2
0
파일: kcf_spi.c 프로젝트: MarkGavalda/zfs
/*
 * 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);
}
예제 #3
0
파일: kcf_spi.c 프로젝트: MarkGavalda/zfs
/*
 * 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);
}
예제 #4
0
파일: kcf_spi.c 프로젝트: MarkGavalda/zfs
/*
 * 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);
}