OpenSolaris_b135/lib/gss_mechs/mech_krb5/crypto/pkcs11slot.c

/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <etypes.h>
#include <security/cryptoki.h>
#include <security/pkcs11.h>

/*
 * get_algo
 *
 * This routine provides a mapping from Kerberos encryption
 * and hash types to PKCS#11 encryption and hash types.
 */
CK_RV
get_algo(krb5_enctype etype, KRB5_MECH_TO_PKCS *algos)
{
	switch (etype) {
		case ENCTYPE_DES_CBC_CRC:
			algos->enc_algo = CKM_DES_CBC;
			algos->hash_algo = 0;
			algos->str2key_algo = 0;
			algos->flags = USE_ENCR;
			return (CKR_OK);
		case ENCTYPE_DES_CBC_MD5:
			algos->enc_algo = CKM_DES_CBC;
			algos->hash_algo = CKM_MD5;
			algos->str2key_algo = 0;
			algos->flags = USE_ENCR | USE_HASH;
			return (CKR_OK);
		case ENCTYPE_DES_CBC_RAW:
			algos->enc_algo = CKM_DES_CBC;
			algos->hash_algo = 0;
			algos->str2key_algo = 0;
			algos->flags = USE_ENCR;
			return (CKR_OK);
		case ENCTYPE_DES_HMAC_SHA1:
			algos->enc_algo = CKM_DES_CBC;
			algos->hash_algo = CKM_SHA_1_HMAC;
			algos->str2key_algo = 0;
			algos->flags = USE_ENCR | USE_HASH;
			return (CKR_OK);
		case ENCTYPE_DES3_CBC_SHA1:
			algos->enc_algo = CKM_DES3_CBC;
			algos->hash_algo = CKM_SHA_1_HMAC;
			algos->str2key_algo = 0;
			algos->flags = USE_ENCR | USE_HASH;
			return (CKR_OK);
		case ENCTYPE_DES3_CBC_RAW:
			algos->enc_algo = CKM_DES3_CBC;
			algos->hash_algo = 0;
			algos->str2key_algo = 0;
			algos->flags = USE_ENCR;
			return (CKR_OK);
		case ENCTYPE_ARCFOUR_HMAC:
		case ENCTYPE_ARCFOUR_HMAC_EXP:
			algos->enc_algo = CKM_RC4;
			algos->hash_algo = CKM_MD5_HMAC;
			algos->str2key_algo = 0;
			algos->flags = USE_ENCR;
			return (CKR_OK);
		case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
		case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
			algos->enc_algo = CKM_AES_CBC;
			algos->hash_algo = CKM_SHA_1_HMAC;
			algos->str2key_algo = CKM_PKCS5_PBKD2;
			algos->flags = USE_ENCR;
			return (CKR_OK);
	}
	return (CKR_MECHANISM_INVALID);
}

/*
 * get_key_type
 *
 * map Kerberos key types to PKCS#11 key type values.
 */
CK_RV
get_key_type(krb5_enctype etype, CK_KEY_TYPE *keyType)
{
	switch (etype) {
		case ENCTYPE_DES_CBC_CRC:
		case ENCTYPE_DES_CBC_MD5:
		case ENCTYPE_DES_CBC_RAW:
		case ENCTYPE_DES_HMAC_SHA1:
			*keyType = CKK_DES;
			return (CKR_OK);
		case ENCTYPE_DES3_CBC_SHA1:
		case ENCTYPE_DES3_CBC_RAW:
			*keyType = CKK_DES3;
			return (CKR_OK);
		case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
		case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
			*keyType = CKK_AES;
			return (CKR_OK);
		case ENCTYPE_ARCFOUR_HMAC:
		case ENCTYPE_ARCFOUR_HMAC_EXP:
			*keyType = CKK_RC4;
			return (CKR_OK);
	}

	/* There's no appropriate error.  Just return the general one */
	return (CKR_GENERAL_ERROR);
}

/*
 * slot_supports_krb5
 *
 * Determine whether the PKCS#11 "slot" supports the necessary
 * crypto needed for Kerberos functionality.
 *
 * Return values:
 * TRUE = The given slot is OK for Kerberos
 * FALSE = Not ok, try something else.
 */
krb5_error_code
slot_supports_krb5(CK_SLOT_ID_PTR slotid)
{
	int i;
	CK_MECHANISM_INFO info;
	CK_RV rv;
	int enctypes_found = 0;
	KRB5_MECH_TO_PKCS algos;
	krb5_enctype tempenctype;

	for (i = 0; i < krb5_enctypes_length; i++) {
		tempenctype = krb5_enctypes_list[i].etype;
		if ((rv = get_algo(tempenctype, &algos)) != CKR_OK) {
			KRB5_LOG0(KRB5_ERR, "Failed to get algorithm.");
			/*
			 * If the algorithm is not available, disable
			 * this enctype so kerberos doesn't try to use it
			 * again.
			 */
			krb5_enctypes_list[i].etype = -1;
			krb5_enctypes_list[i].in_string = "<unsupported>";
			krb5_enctypes_list[i].out_string = "<unsupported>";
			continue;
		}
		if (ENC_DEFINED(algos)) {
			size_t keysize, keylength;
			rv = C_GetMechanismInfo(*slotid, algos.enc_algo, &info);
			if (rv != CKR_OK) {
				KRB5_LOG1(KRB5_ERR, "C_GetMechanismInfo failed "
				    "for encr algorith %s: 0x%x\n",
				    krb5_enctypes_list[i].in_string,
				    rv);
				return (FALSE);
			}
			/*
			 * If the encryption algorithm is supported,
			 * make sure it supports the correct key sizes.
			 * If not, disable this enctype and continue.
			 */
			keysize = krb5_enctypes_list[i].enc->keybytes;
			keylength = krb5_enctypes_list[i].enc->keylength;

			if (keylength > info.ulMaxKeySize) {
				krb5_enctypes_list[i].etype = -1;
				krb5_enctypes_list[i].in_string =
					"<unsupported>";
				krb5_enctypes_list[i].out_string =
					"<unsupported>";
				continue;
			}
			if (!(info.flags & (CKF_ENCRYPT|CKF_RNG)))
				return (FALSE);
		}
		if (HASH_DEFINED(algos)) {
			rv = C_GetMechanismInfo(*slotid, algos.hash_algo,
			    &info);
			if (rv != CKR_OK) {
				KRB5_LOG1(KRB5_ERR, "C_GetMechanismInfo failed "
				    "for hash algorithm %s: 0x%x\n",
				    krb5_enctypes_list[i].in_string,
				    rv);
				return (FALSE);
			}
			if (!(info.flags & (CKF_DIGEST|CKF_SIGN|CKF_RNG)))
				return (FALSE);
		}
		if (algos.str2key_algo != 0) {
			rv = C_GetMechanismInfo(*slotid, algos.str2key_algo,
			    &info);
			if (rv != CKR_OK) {
				KRB5_LOG(KRB5_ERR, "C_GetMechanismInfo failed "
				    "for str2key algorithm: 0x%x\n", rv);
				return (FALSE);
			}
		}
		enctypes_found++;
	}
	/*
	 * If NO enctypes were found to be supported, return FALSE.
	 */
	if (!enctypes_found) {
		KRB5_LOG0(KRB5_ERR,
			"No crypto support available from PKCS#11.");
		return (FALSE);
	}
	return (TRUE);
}