Blame src/p11_cert.c

Packit 6b81fa
/* libp11, a simple layer on to of PKCS#11 API
Packit 6b81fa
 * Copyright (C) 2005 Olaf Kirch <okir@lst.de>
Packit 6b81fa
 * Copyright (C) 2016-2018 MichaƂ Trojnara <Michal.Trojnara@stunnel.org>
Packit 6b81fa
 *
Packit 6b81fa
 *  This library is free software; you can redistribute it and/or
Packit 6b81fa
 *  modify it under the terms of the GNU Lesser General Public
Packit 6b81fa
 *  License as published by the Free Software Foundation; either
Packit 6b81fa
 *  version 2.1 of the License, or (at your option) any later version.
Packit 6b81fa
 *
Packit 6b81fa
 *  This library is distributed in the hope that it will be useful,
Packit 6b81fa
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6b81fa
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6b81fa
 *  Lesser General Public License for more details.
Packit 6b81fa
 *
Packit 6b81fa
 *  You should have received a copy of the GNU Lesser General Public
Packit 6b81fa
 *  License along with this library; if not, write to the Free Software
Packit 6b81fa
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
Packit 6b81fa
 */
Packit 6b81fa
Packit 6b81fa
/*
Packit 6b81fa
 * p11_cert.c - Handle certificates residing on a PKCS11 token
Packit 6b81fa
 *
Packit 6b81fa
 * Copyright (C) 2002, Olaf Kirch <okir@lst.de>
Packit 6b81fa
 */
Packit 6b81fa
Packit 6b81fa
#include "libp11-int.h"
Packit 6b81fa
#include <string.h>
Packit 6b81fa
Packit 6b81fa
static int pkcs11_find_certs(PKCS11_TOKEN *);
Packit 6b81fa
static int pkcs11_next_cert(PKCS11_CTX *, PKCS11_TOKEN *, CK_SESSION_HANDLE);
Packit 6b81fa
static int pkcs11_init_cert(PKCS11_CTX *ctx, PKCS11_TOKEN *token,
Packit 6b81fa
	CK_SESSION_HANDLE session, CK_OBJECT_HANDLE o, PKCS11_CERT **);
Packit 6b81fa
Packit 6b81fa
/*
Packit 6b81fa
 * Enumerate all certs on the card
Packit 6b81fa
 */
Packit 6b81fa
int pkcs11_enumerate_certs(PKCS11_TOKEN *token,
Packit 6b81fa
		PKCS11_CERT **certp, unsigned int *countp)
Packit 6b81fa
{
Packit 6b81fa
	PKCS11_SLOT *slot = TOKEN2SLOT(token);
Packit 6b81fa
	PKCS11_CTX *ctx = SLOT2CTX(slot);
Packit 6b81fa
	PKCS11_TOKEN_private *tpriv = PRIVTOKEN(token);
Packit 6b81fa
	PKCS11_SLOT_private *spriv = PRIVSLOT(slot);
Packit 6b81fa
	PKCS11_CTX_private *cpriv = PRIVCTX(ctx);
Packit 6b81fa
	int rv;
Packit 6b81fa
Packit 6b81fa
	/* Make sure we have a session */
Packit 6b81fa
	if (!spriv->haveSession && PKCS11_open_session(slot, 0))
Packit 6b81fa
		return -1;
Packit 6b81fa
Packit 6b81fa
	CRYPTO_THREAD_write_lock(cpriv->rwlock);
Packit 6b81fa
	rv = pkcs11_find_certs(token);
Packit 6b81fa
	CRYPTO_THREAD_unlock(cpriv->rwlock);
Packit 6b81fa
	if (rv < 0) {
Packit 6b81fa
		pkcs11_destroy_certs(token);
Packit 6b81fa
		return -1;
Packit 6b81fa
	}
Packit 6b81fa
Packit 6b81fa
	if (certp)
Packit 6b81fa
		*certp = tpriv->certs;
Packit 6b81fa
	if (countp)
Packit 6b81fa
		*countp = tpriv->ncerts;
Packit 6b81fa
	return 0;
Packit 6b81fa
}
Packit 6b81fa
Packit 6b81fa
/**
Packit 6b81fa
 * Remove a certificate from the associated token
Packit 6b81fa
 */
Packit 6b81fa
int pkcs11_remove_certificate(PKCS11_CERT *cert){
Packit 6b81fa
	PKCS11_SLOT *slot = CERT2SLOT(cert);
Packit 6b81fa
	PKCS11_CTX *ctx = CERT2CTX(cert);
Packit 6b81fa
	PKCS11_SLOT_private *spriv = PRIVSLOT(slot);
Packit 6b81fa
	CK_OBJECT_HANDLE obj;
Packit 6b81fa
	CK_ULONG count;
Packit 6b81fa
	CK_ATTRIBUTE search_parameters[32];
Packit 6b81fa
	unsigned int n = 0;
Packit 6b81fa
	int rv;
Packit 6b81fa
Packit 6b81fa
	/* First, make sure we have a session */
Packit 6b81fa
	if (!spriv->haveSession && PKCS11_open_session(slot, 1)){
Packit 6b81fa
		return -1;
Packit 6b81fa
	}
Packit 6b81fa
	
Packit 6b81fa
	pkcs11_addattr_int(search_parameters + n++, CKA_CLASS, CKO_CERTIFICATE);
Packit 6b81fa
	if (cert->id && cert->id_len){
Packit 6b81fa
		pkcs11_addattr(search_parameters + n++, CKA_ID, cert->id, cert->id_len);
Packit 6b81fa
	}
Packit 6b81fa
	if (cert->label){
Packit 6b81fa
	 	pkcs11_addattr_s(search_parameters + n++, CKA_LABEL, cert->label);
Packit 6b81fa
	}
Packit 6b81fa
Packit 6b81fa
	rv = CRYPTOKI_call(ctx,
Packit 6b81fa
		C_FindObjectsInit(spriv->session, search_parameters, n));
Packit 6b81fa
	CRYPTOKI_checkerr(CKR_F_PKCS11_REMOVE_CERTIFICATE, rv);
Packit 6b81fa
	
Packit 6b81fa
	rv = CRYPTOKI_call(ctx, C_FindObjects(spriv->session, &obj, 1, &count));
Packit 6b81fa
	CRYPTOKI_checkerr(CKR_F_PKCS11_REMOVE_CERTIFICATE, rv);
Packit 6b81fa
Packit 6b81fa
	CRYPTOKI_call(ctx, C_FindObjectsFinal(spriv->session));
Packit 6b81fa
	if (count!=1){
Packit 6b81fa
		pkcs11_zap_attrs(search_parameters, n);
Packit 6b81fa
		return -1;
Packit 6b81fa
	}
Packit 6b81fa
	rv = CRYPTOKI_call(ctx, C_DestroyObject(spriv->session, obj));
Packit 6b81fa
	if (rv != CKR_OK){
Packit 6b81fa
		pkcs11_zap_attrs(search_parameters, n);
Packit 6b81fa
		return -1;
Packit 6b81fa
	}
Packit 6b81fa
	pkcs11_zap_attrs(search_parameters, n);
Packit 6b81fa
	return 0;
Packit 6b81fa
}
Packit 6b81fa
Packit 6b81fa
/*
Packit 6b81fa
 * Find certificate matching a key
Packit 6b81fa
 */
Packit 6b81fa
PKCS11_CERT *pkcs11_find_certificate(PKCS11_KEY *key)
Packit 6b81fa
{
Packit 6b81fa
	PKCS11_KEY_private *kpriv;
Packit 6b81fa
	PKCS11_CERT_private *cpriv;
Packit 6b81fa
	PKCS11_CERT *cert;
Packit 6b81fa
	unsigned int n, count;
Packit 6b81fa
Packit 6b81fa
	kpriv = PRIVKEY(key);
Packit 6b81fa
	if (PKCS11_enumerate_certs(KEY2TOKEN(key), &cert, &count))
Packit 6b81fa
		return NULL;
Packit 6b81fa
	for (n = 0; n < count; n++, cert++) {
Packit 6b81fa
		cpriv = PRIVCERT(cert);
Packit 6b81fa
		if (cpriv->id_len == kpriv->id_len
Packit 6b81fa
				&& !memcmp(cpriv->id, kpriv->id, kpriv->id_len))
Packit 6b81fa
			return cert;
Packit 6b81fa
	}
Packit 6b81fa
	return NULL;
Packit 6b81fa
}
Packit 6b81fa
Packit 6b81fa
/*
Packit 6b81fa
 * Find all certs of a given type (public or private)
Packit 6b81fa
 */
Packit 6b81fa
static int pkcs11_find_certs(PKCS11_TOKEN *token)
Packit 6b81fa
{
Packit 6b81fa
	PKCS11_SLOT *slot = TOKEN2SLOT(token);
Packit 6b81fa
	PKCS11_CTX *ctx = SLOT2CTX(slot);
Packit 6b81fa
	PKCS11_SLOT_private *spriv = PRIVSLOT(slot);
Packit 6b81fa
	CK_OBJECT_CLASS cert_search_class;
Packit 6b81fa
	CK_ATTRIBUTE cert_search_attrs[] = {
Packit 6b81fa
		{CKA_CLASS, &cert_search_class, sizeof(cert_search_class)},
Packit 6b81fa
	};
Packit 6b81fa
	int rv, res = -1;
Packit 6b81fa
Packit 6b81fa
	/* Tell the PKCS11 lib to enumerate all matching objects */
Packit 6b81fa
	cert_search_class = CKO_CERTIFICATE;
Packit 6b81fa
	rv = CRYPTOKI_call(ctx, C_FindObjectsInit(spriv->session, cert_search_attrs, 1));
Packit 6b81fa
	CRYPTOKI_checkerr(CKR_F_PKCS11_FIND_CERTS, rv);
Packit 6b81fa
Packit 6b81fa
	do {
Packit 6b81fa
		res = pkcs11_next_cert(ctx, token, spriv->session);
Packit 6b81fa
	} while (res == 0);
Packit 6b81fa
Packit 6b81fa
	CRYPTOKI_call(ctx, C_FindObjectsFinal(spriv->session));
Packit 6b81fa
Packit 6b81fa
	return (res < 0) ? -1 : 0;
Packit 6b81fa
}
Packit 6b81fa
Packit 6b81fa
static int pkcs11_next_cert(PKCS11_CTX *ctx, PKCS11_TOKEN *token,
Packit 6b81fa
		CK_SESSION_HANDLE session)
Packit 6b81fa
{
Packit 6b81fa
	CK_OBJECT_HANDLE obj;
Packit 6b81fa
	CK_ULONG count;
Packit 6b81fa
	int rv;
Packit 6b81fa
Packit 6b81fa
	/* Get the next matching object */
Packit 6b81fa
	rv = CRYPTOKI_call(ctx, C_FindObjects(session, &obj, 1, &count));
Packit 6b81fa
	CRYPTOKI_checkerr(CKR_F_PKCS11_NEXT_CERT, rv);
Packit 6b81fa
Packit 6b81fa
	if (count == 0)
Packit 6b81fa
		return 1;
Packit 6b81fa
Packit 6b81fa
	if (pkcs11_init_cert(ctx, token, session, obj, NULL))
Packit 6b81fa
		return -1;
Packit 6b81fa
Packit 6b81fa
	return 0;
Packit 6b81fa
}
Packit 6b81fa
Packit 6b81fa
static int pkcs11_init_cert(PKCS11_CTX *ctx, PKCS11_TOKEN *token,
Packit 6b81fa
		CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, PKCS11_CERT ** ret)
Packit 6b81fa
{
Packit 6b81fa
	PKCS11_TOKEN_private *tpriv;
Packit 6b81fa
	PKCS11_CERT_private *cpriv;
Packit 6b81fa
	PKCS11_CERT *cert, *tmp;
Packit 6b81fa
	unsigned char *data;
Packit 6b81fa
	CK_CERTIFICATE_TYPE cert_type;
Packit 6b81fa
	size_t size;
Packit 6b81fa
	int i;
Packit 6b81fa
Packit 6b81fa
	(void)ctx;
Packit 6b81fa
	(void)session;
Packit 6b81fa
Packit 6b81fa
	/* Ignore unknown certificate types */
Packit 6b81fa
	size = sizeof(CK_CERTIFICATE_TYPE);
Packit 6b81fa
	if (pkcs11_getattr_var(token, obj, CKA_CERTIFICATE_TYPE, (CK_BYTE *)&cert_type, &size))
Packit 6b81fa
		return -1;
Packit 6b81fa
	if (cert_type != CKC_X_509)
Packit 6b81fa
		return 0;
Packit 6b81fa
Packit 6b81fa
	/* Prevent re-adding existing PKCS#11 object handles */
Packit 6b81fa
	/* TODO: Rewrite the O(n) algorithm as O(log n),
Packit 6b81fa
	 * or it may be too slow with a large number of certificates */
Packit 6b81fa
	for (i=0; i < PRIVTOKEN(token)->ncerts; ++i)
Packit 6b81fa
		if (PRIVCERT(PRIVTOKEN(token)->certs + i)->object == obj)
Packit 6b81fa
			return 0;
Packit 6b81fa
Packit 6b81fa
	/* Allocate memory */
Packit 6b81fa
	cpriv = OPENSSL_malloc(sizeof(PKCS11_CERT_private));
Packit 6b81fa
	if (cpriv == NULL)
Packit 6b81fa
		return -1;
Packit 6b81fa
	memset(cpriv, 0, sizeof(PKCS11_CERT_private));
Packit 6b81fa
	tpriv = PRIVTOKEN(token);
Packit 6b81fa
	tmp = OPENSSL_realloc(tpriv->certs,
Packit 6b81fa
		(tpriv->ncerts + 1) * sizeof(PKCS11_CERT));
Packit 6b81fa
	if (tmp == NULL)
Packit 6b81fa
		return -1;
Packit 6b81fa
	tpriv->certs = tmp;
Packit 6b81fa
	cert = tpriv->certs + tpriv->ncerts++;
Packit 6b81fa
	memset(cert, 0, sizeof(PKCS11_CERT));
Packit 6b81fa
Packit 6b81fa
	/* Fill public properties */
Packit 6b81fa
	pkcs11_getattr_alloc(token, obj, CKA_LABEL, (CK_BYTE **)&cert->label, NULL);
Packit 6b81fa
	size = 0;
Packit 6b81fa
	if (!pkcs11_getattr_alloc(token, obj, CKA_VALUE, &data, &size)) {
Packit 6b81fa
		const unsigned char *p = data;
Packit 6b81fa
Packit 6b81fa
		cert->x509 = d2i_X509(NULL, &p, (long)size);
Packit 6b81fa
		OPENSSL_free(data);
Packit 6b81fa
	}
Packit 6b81fa
	cert->id_len = 0;
Packit 6b81fa
	pkcs11_getattr_alloc(token, obj, CKA_ID, &cert->id, &cert->id_len);
Packit 6b81fa
Packit 6b81fa
	/* Fill private properties */
Packit 6b81fa
	cert->_private = cpriv;
Packit 6b81fa
	cpriv->object = obj;
Packit 6b81fa
	cpriv->parent = token;
Packit 6b81fa
	cpriv->id_len = sizeof cpriv->id;
Packit 6b81fa
	if (pkcs11_getattr_var(token, obj, CKA_ID, cpriv->id, &cpriv->id_len))
Packit 6b81fa
		cpriv->id_len = 0;
Packit 6b81fa
Packit 6b81fa
	if (ret)
Packit 6b81fa
		*ret = cert;
Packit 6b81fa
	return 0;
Packit 6b81fa
}
Packit 6b81fa
Packit 6b81fa
/*
Packit 6b81fa
 * Destroy all certs
Packit 6b81fa
 */
Packit 6b81fa
void pkcs11_destroy_certs(PKCS11_TOKEN *token)
Packit 6b81fa
{
Packit 6b81fa
	PKCS11_TOKEN_private *tpriv = PRIVTOKEN(token);
Packit 6b81fa
Packit 6b81fa
	while (tpriv->ncerts > 0) {
Packit 6b81fa
		PKCS11_CERT *cert = &tpriv->certs[--(tpriv->ncerts)];
Packit 6b81fa
Packit 6b81fa
		if (cert->x509)
Packit 6b81fa
			X509_free(cert->x509);
Packit 6b81fa
		OPENSSL_free(cert->label);
Packit 6b81fa
		if (cert->id)
Packit 6b81fa
			OPENSSL_free(cert->id);
Packit 6b81fa
		if (cert->_private != NULL)
Packit 6b81fa
			OPENSSL_free(cert->_private);
Packit 6b81fa
	}
Packit 6b81fa
	if (tpriv->certs)
Packit 6b81fa
		OPENSSL_free(tpriv->certs);
Packit 6b81fa
	tpriv->certs = NULL;
Packit 6b81fa
	tpriv->ncerts = 0;
Packit 6b81fa
}
Packit 6b81fa
Packit 6b81fa
/*
Packit 6b81fa
 * Store certificate
Packit 6b81fa
 */
Packit 6b81fa
int pkcs11_store_certificate(PKCS11_TOKEN *token, X509 *x509, char *label,
Packit 6b81fa
		unsigned char *id, size_t id_len, PKCS11_CERT ** ret_cert)
Packit 6b81fa
{
Packit 6b81fa
	PKCS11_SLOT *slot = TOKEN2SLOT(token);
Packit 6b81fa
	PKCS11_CTX *ctx = SLOT2CTX(slot);
Packit 6b81fa
	PKCS11_SLOT_private *spriv = PRIVSLOT(slot);
Packit 6b81fa
	CK_OBJECT_HANDLE object;
Packit 6b81fa
	CK_ATTRIBUTE attrs[32];
Packit 6b81fa
	unsigned int n = 0;
Packit 6b81fa
	int rv;
Packit 6b81fa
	const EVP_MD* evp_md;
Packit 6b81fa
	CK_MECHANISM_TYPE ckm_md;
Packit 6b81fa
	unsigned char md[EVP_MAX_MD_SIZE];
Packit 6b81fa
	unsigned int md_len;
Packit 6b81fa
Packit 6b81fa
	/* First, make sure we have a session */
Packit 6b81fa
	if (!PRIVSLOT(slot)->haveSession && PKCS11_open_session(slot, 1))
Packit 6b81fa
		return -1;
Packit 6b81fa
Packit 6b81fa
	/* Now build the template */
Packit 6b81fa
	pkcs11_addattr_int(attrs + n++, CKA_CLASS, CKO_CERTIFICATE);
Packit 6b81fa
	pkcs11_addattr_bool(attrs + n++, CKA_TOKEN, TRUE);
Packit 6b81fa
	pkcs11_addattr_int(attrs + n++, CKA_CERTIFICATE_TYPE, CKC_X_509);
Packit 6b81fa
	pkcs11_addattr_obj(attrs + n++, CKA_SUBJECT,
Packit 6b81fa
		(pkcs11_i2d_fn)i2d_X509_NAME, X509_get_subject_name(x509));
Packit 6b81fa
	pkcs11_addattr_obj(attrs + n++, CKA_ISSUER,
Packit 6b81fa
		(pkcs11_i2d_fn)i2d_X509_NAME, X509_get_issuer_name(x509));
Packit 6b81fa
Packit 6b81fa
	/* Get digest algorithm from x509 certificate */
Packit 6b81fa
	evp_md = EVP_get_digestbynid(X509_get_signature_nid(x509));
Packit 6b81fa
	switch (EVP_MD_type(evp_md)) {
Packit 6b81fa
	default:
Packit 6b81fa
	case NID_sha1:
Packit 6b81fa
		ckm_md = CKM_SHA_1;
Packit 6b81fa
		break;
Packit 6b81fa
	case NID_sha224:
Packit 6b81fa
		ckm_md = CKM_SHA224;
Packit 6b81fa
		break;
Packit 6b81fa
	case NID_sha256:
Packit 6b81fa
		ckm_md = CKM_SHA256;
Packit 6b81fa
		break;
Packit 6b81fa
	case NID_sha512:
Packit 6b81fa
		ckm_md = CKM_SHA512;
Packit 6b81fa
		break;
Packit 6b81fa
	case NID_sha384:
Packit 6b81fa
		ckm_md = CKM_SHA384;
Packit 6b81fa
		break;
Packit 6b81fa
	}
Packit 6b81fa
Packit 6b81fa
	/* Set hash algorithm; default is SHA-1 */
Packit 6b81fa
	pkcs11_addattr_int(attrs + n++, CKA_NAME_HASH_ALGORITHM, ckm_md);
Packit 6b81fa
	if(X509_pubkey_digest(x509,evp_md,md,&md_len))
Packit 6b81fa
		pkcs11_addattr(attrs + n++, CKA_HASH_OF_SUBJECT_PUBLIC_KEY,md,md_len);
Packit 6b81fa
Packit 6b81fa
	pkcs11_addattr_obj(attrs + n++, CKA_VALUE, (pkcs11_i2d_fn)i2d_X509, x509);
Packit 6b81fa
	if (label)
Packit 6b81fa
		pkcs11_addattr_s(attrs + n++, CKA_LABEL, label);
Packit 6b81fa
	if (id && id_len)
Packit 6b81fa
		pkcs11_addattr(attrs + n++, CKA_ID, id, id_len);
Packit 6b81fa
Packit 6b81fa
	/* Now call the pkcs11 module to create the object */
Packit 6b81fa
	rv = CRYPTOKI_call(ctx, C_CreateObject(spriv->session, attrs, n, &object));
Packit 6b81fa
Packit 6b81fa
	/* Zap all memory allocated when building the template */
Packit 6b81fa
	pkcs11_zap_attrs(attrs, n);
Packit 6b81fa
Packit 6b81fa
	CRYPTOKI_checkerr(CKR_F_PKCS11_STORE_CERTIFICATE, rv);
Packit 6b81fa
Packit 6b81fa
	/* Gobble the key object */
Packit 6b81fa
	return pkcs11_init_cert(ctx, token, spriv->session, object, ret_cert);
Packit 6b81fa
}
Packit 6b81fa
Packit 6b81fa
/* vim: set noexpandtab: */