diff options
| author | Stef Walter <stef@memberwebs.com> | 2007-04-27 20:37:40 +0000 | 
|---|---|---|
| committer | Stef Walter <stef@memberwebs.com> | 2007-04-27 20:37:40 +0000 | 
| commit | 4928a4bff079c140d261cb3fefdc07c60c0bdebf (patch) | |
| tree | c5258568f5e417f0a2c9d19099c6ed057cfd73bd /ckcapi-cert.c | |
| parent | 3d8ed01d2653c45e52821ba00ac72099a12600e1 (diff) | |
A bunch more changes, that implement almost complete find and get support.
Diffstat (limited to 'ckcapi-cert.c')
| -rw-r--r-- | ckcapi-cert.c | 456 | 
1 files changed, 221 insertions, 235 deletions
diff --git a/ckcapi-cert.c b/ckcapi-cert.c index d1b9b32..7436307 100644 --- a/ckcapi-cert.c +++ b/ckcapi-cert.c @@ -1,18 +1,25 @@ +#include "ckcapi.h" -typedef struct _CertRef -{ -	 +#include <memory.h> +#include <wincrypt.h> + +typedef struct _CertObject +{ +	CkCapiObject obj; +	const char* store; +	BYTE* key_id; +	DWORD key_id_len;  } -CertRef; +CertObject;  static CK_RV  copy_static_data(CK_VOID_PTR val, CK_ULONG_PTR len,   				 CK_VOID_PTR data, DWORD cb)  {  	if(cb == 0) -		return CKR_CANCEL; +		return CKR_ATTRIBUTE_TYPE_INVALID;  	if(!val)  	{ @@ -39,7 +46,6 @@ cert_bool_attribute(void* obj, CK_ATTRIBUTE_TYPE type,  	CK_BBOOL val;  	ASSERT(obj); -	ASSERT(ret);  	switch(type)   	{ @@ -79,7 +85,7 @@ cert_bool_attribute(void* obj, CK_ATTRIBUTE_TYPE type,  		/* TODO: Implement */  	default: -		return CK_CANCEL; +		return CKR_ATTRIBUTE_TYPE_INVALID;  	};  	return copy_static_data(data, len, &val, sizeof(CK_BBOOL)); @@ -93,7 +99,6 @@ cert_ulong_attribute(void* obj, CK_ATTRIBUTE_TYPE type,  	CK_ULONG val;  	ASSERT(obj); -	ASSERT(ret);  	switch(type)  	{ @@ -102,7 +107,7 @@ cert_ulong_attribute(void* obj, CK_ATTRIBUTE_TYPE type,  	 * Object class.  	 * - Always CKO_CERTIFICATE for certificates.  	 */ -	case CKA_OBJECT_CLASS: +	case CKA_CLASS:  		val = CKO_CERTIFICATE;  		break; @@ -123,7 +128,7 @@ cert_ulong_attribute(void* obj, CK_ATTRIBUTE_TYPE type,  		/* TODO: Implement */  	default: -		return CK_CANCEL; +		return CKR_ATTRIBUTE_TYPE_INVALID;  	};  	if(*len < sizeof(CK_ULONG)) @@ -137,218 +142,6 @@ cert_ulong_attribute(void* obj, CK_ATTRIBUTE_TYPE type,  static CK_RV  cert_bytes_attribute(void* obj, CK_ATTRIBUTE_TYPE type,  -					 CkCapiAttrAction action, CK_ATTRIBUTE_PTR attr) -{ -	PCCERT_CONTEXT cert = (PCCERT_CONTEXT)obj; -	CK_VOID_PTR val; -	CK_ULONG len; -	size_t maxa; -	BOOL r; - - -	DWORD size = *len; - -	int allocated = 0; - -	ASSERT(obj); -	ASSERT(ret); - -	/* -	 * We don't need to retrieve the full value of any of  -	 * these fields if we don't have to. Just calculate  -	 * the max amount to retrieve, so we can compare, or  -	 * notify caller of real length.  -	 */ -	maxa = attr->ulValueLen; -	if(maxa == 0) -		maxa = 4; -	/* Keep dynamic allocations under a 64k. */ -	if(maxa > 0xFFFF) -		maxa = 0xFFFF; -		 -	switch(type) -	{ - -	/* -	 * Description of the object. -	 * - We use CAPI's CERT_FRIENDLY_NAME_PROP_ID property,  -	 *   converted into UTF8. -	 * - Yes this is slow, but this is not really a property -	 *   that's searched on or retrieved intensively. -	 */ -	case CKA_LABEL: -		{ -			DWORD size = maxa * sizeof(WCHAR); -			WCHAR* utf16; -xxxxxx, this is wrong, second call may be needed -			/* Get the UTF16 string, a worst case of twice as long as UTF8 */ -			WCHAR* utf16 = alloca(size); -			r = CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID, -												  utf16, &size); -			if(!r) -			{ -				DWORD err = GetLastError(); -				if(err == ERROR_MORE_DATA)  -				{ -					/* Truncate long names */ -					utf16[maxa - 1] = 0;  -					r = TRUE; -				} -				else if (err != CRYPT_E_NOT_FOUND) -				{ -					return ckcapi_winerr_to_ckr(err); -				} -			} - -			if(r) -			{ -				/* Convert it */ -				if(!ckcapi_util_utf16_to_utf8(utf16, &val, &len)) -					SetLastError(ERROR_NOT_ENOUGH_MEMORY); -				allocated = 1; -			} -			else -			{ -				/* We always like to have a decent label */ -				val = "Unnamed Certificate"; -				len = strlen(val); -				allocated = 0; -			} -		} -		break; - -	/* -	 * A byte array unique to this certificate. The CKA_ID of  -	 * matching certificates and private keys should match.  -	 * Should match the key identifier in an X.509v3 certificate. -	 *  -	 * We use CAPI's CERT_KEY_IDENTIFIER_PROP_ID property directly. -	 */ -	case CKA_ID: -		{ -			BOOL r; - -			val = alloca(size); -			r = CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID,  -												  val, &size); -			if(!r) -			{ -				DWORD err = GetLastError(); -				if(err == CRYPT_E_NOT_FOUND) -					return CKR_CANCEL; -				if(err != ERROR_MORE_DATA)  -					return ckcapi_winerr_to_ckr(err); -			} - -			*len = size; -		} -		break; - -	/* -	 * DER-encoding of the certificate subject name. -	 *  -	 * We use CAPI's CERT_CONTEXT pCertInfo->Subject field -	 * directly.  -	 */ -	case CKA_SUBJECT: -		if(cert->pCertInfo->Subject.pbData == NULL || -		   cert->pCertInfo->Subject.cbData == 0) -			return CKR_CANCEL; -		val = cert->pCertInfo->Subject.pbData; -		len = cert->pCertInfo->Subject.cbData; -		break; - -	/* -	 * DER-encoding of the certificate issuer name. -	 *  -	 * We use CAPI's CERT_CONTEXT pCertInfo->Issuer field -	 * directly. -	 */ -	case CKA_ISSUER: -		if(cert->pCertInfo->Issuer.pbData == NULL || -		   cert->pCertInfo->Issuer.cbData == 0) -			return CKR_CANCEL; -		val = cert->pCertInfo->Issuer.pbData; -		len = cert->pCertInfo->Issuer.cbData; -		break; - -	/* -	 * DER-encoding of the certificate serial number. -	 *  -	 * TODO: -	 *  BOOL rc = CryptEncodeObject(X509_ASN_ENCODING, -     *                    X509_MULTI_BYTE_INTEGER, -     *                   &certContext->pCertInfo->SerialNumber, -	 *		 co->derSerial, -     *                    &size); -	 */ -	case CKA_SERIAL_NUMBER: -		{ -			BOOL r; -			DWORD size = *len; - -			r = CryptEncodeObject(X509_ASN_ENCODING, X509_MULTI_BYTE_INTEGER, -								  &cert->pCertInfo->SerialNumber, val, *len); -			if(!r) -			{ -				DWORD err = GetLastError(); -				if(err == ERROR_FILE_NOT_FOUND) -					return CKR_GENERAL_ERROR; -				if(err != ERROR_MORE_DATA) -					return ckcapi_winerr_to_ckr(err); -			} - -			len = size; -		} -		break; - -	/* -	 * BER-encoding of the full certificate. -	 * -	 * We use CAPI's CERT_CONTEXT pbCertEncoded field directly. -	 */ -	case CKA_VALUE: -		if(cert->pbCertEncoded == NULL || -		   cert->cbCertEncoded == 0) -			return CKR_CANCEL; -		val = cert->pbCertEncoded; -		len = cert->cbCertEncoded; -		break; -		break; - -	/* -	 * If CKA_VALUE not specified, this is where the full  -	 * certificate can be found.  -	 *  -	 * We don't support this. All our certificates are present -	 * in full. -	 */ -	case CKA_URL: -		return CKR_CANCEL; - -	/* -	 * Checksum -	 * - TODO: Work out what to do here -	 */ -	case CKA_CHECKSUM: -		break; - -	/* -	 * TODO: Should we support these? -	 */ -	CKA_HASH_OF_SUBJECT_PUBLIC_KEY -	CKA_HASH_OF_ISSUER_PUBLIC_KEY -		break; - -	/* Not supported */ - -	default: -		break -	}; -}; - -static CK_RV -cert_bytes_attribute(void* obj, CK_ATTRIBUTE_TYPE type,   					 CK_VOID_PTR data, CK_ULONG_PTR len)  {  	PCCERT_CONTEXT cert = (PCCERT_CONTEXT)obj; @@ -372,8 +165,8 @@ cert_bytes_attribute(void* obj, CK_ATTRIBUTE_TYPE type,  			/* Get the UTF16 string, a worst case of twice as long as UTF8 */  			(*len) *= sizeof(WCHAR); -			utf16 = alloca(*len); -			r = CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID, +			utf16 = _alloca(*len); +			if(!CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID,  												  utf16, (DWORD*)len))  			{  				DWORD err = GetLastError(); @@ -384,7 +177,9 @@ cert_bytes_attribute(void* obj, CK_ATTRIBUTE_TYPE type,  			}  			/* Convert the data into the buffer */ -			return ckcapi_util_utf16_to_utf8(utf16, data, len)); +			if(!WideCharToMultiByte(CP_UTF8, 0, utf16, -1, data, *len, "?", NULL)) +				return ckcapi_winerr_to_ckr(GetLastError()); +			return CKR_OK;  		}  		break; @@ -402,7 +197,7 @@ cert_bytes_attribute(void* obj, CK_ATTRIBUTE_TYPE type,  			{  				DWORD err = GetLastError();  				if(err == CRYPT_E_NOT_FOUND) -					return CKR_CANCEL; +					return CKR_ATTRIBUTE_TYPE_INVALID;  				return ckcapi_winerr_to_ckr(err);  			}  		} @@ -474,27 +269,28 @@ cert_bytes_attribute(void* obj, CK_ATTRIBUTE_TYPE type,  	 * Checksum  	 * - TODO: Work out what to do here  	 */ -	case CKA_CHECKSUM: +	case CKA_CHECK_VALUE:  		break;  	/*  	 * TODO: Should we support these?  	 */ -	CKA_HASH_OF_SUBJECT_PUBLIC_KEY -	CKA_HASH_OF_ISSUER_PUBLIC_KEY +	case CKA_HASH_OF_SUBJECT_PUBLIC_KEY: +	case CKA_HASH_OF_ISSUER_PUBLIC_KEY:  		break;  	/* Not supported */  	default: -		break +		break;  	}; -	return CKR_CANCEL; +	return CKR_ATTRIBUTE_TYPE_INVALID;  } -static xxx -cert_date_attribute() +static CK_RV +cert_date_attribute(void* obj, CK_ATTRIBUTE_TYPE type,  +					CK_VOID_PTR data, CK_ULONG_PTR len)  {  	switch(type)  	{ @@ -505,7 +301,6 @@ cert_date_attribute()  	 *	pCertInfo->NotBefore;  	 */  	case CKA_START_DATE: -		xxxx;  		break;  	/* @@ -514,7 +309,198 @@ cert_date_attribute()  	 * pCertInfo->NotBefore;  	 */  	case CKA_END_DATE: -		xxxx;  		break;  	}; + +	return CKR_ATTRIBUTE_TYPE_INVALID; +} + +static void +cert_release(void* data) +{ +	PCCERT_CONTEXT cert = (PCCERT_CONTEXT)data; +	ASSERT(cert); +	CertFreeCertificateContext(cert); +} + +static const CkCapiObjectDataVtable cert_objdata_vtable = { +	cert_bool_attribute, +	cert_ulong_attribute, +	cert_bytes_attribute, +	cert_date_attribute, +	cert_release, +}; + +static CK_RV  +cert_load(CkCapiObject* obj, CkCapiObjectData* objdata) +{ +	CertObject* cobj = (CertObject*)obj; +	HCERTSTORE store; +	PCCERT_CONTEXT cert; +	CRYPT_HASH_BLOB blob; + +	ASSERT(cobj); +	ASSERT(objdata); + +	ASSERT(cobj->store); +	store = CertOpenSystemStore((HCRYPTPROV)NULL, cobj->store); +	if(!store) +		return ckcapi_winerr_to_ckr(GetLastError()); + +	ASSERT(cobj->key_id); +	ASSERT(cobj->key_id_len); +	blob.pbData = cobj->key_id; +	blob.cbData = cobj->key_id_len; + +	cert = CertFindCertificateInStore(store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, +									  0, CERT_FIND_KEY_IDENTIFIER, &blob, NULL); + +	CertCloseStore(store, 0); + +	if(!cert) +	{ +		DWORD err = GetLastError(); + +		/* TODO: Is this right for a deleted certificate? */ +		ASSERT(err != E_INVALIDARG); +		if(err == CRYPT_E_NOT_FOUND) +			return CKR_OBJECT_HANDLE_INVALID; +		else +			return ckcapi_winerr_to_ckr(GetLastError()); +	} +	 +	objdata->data = (void*)cert; +	objdata->data_funcs = cert_objdata_vtable; +	return CKR_OK; +} + + +static void  +cert_object_release(void* data) +{ +	CertObject* cobj = (CertObject*)data; +	ASSERT(cobj); +	free(cobj); +} + +static const CkCapiObjectVtable cert_object_vtable = { +	cert_load, +	cert_object_release, +}; + +static CK_RV +register_cert_object(CkCapiSession* sess, const char* store, PCCERT_CONTEXT cert,  +					 CK_OBJECT_HANDLE_PTR id) +{ +	CertObject* cobj; +	CK_RV ret; +	DWORD len; +	 +	if(!CertGetCertificateContextProperty(cert, CERT_KEY_IDENTIFIER_PROP_ID, NULL, &len)) +	{ +		DBG(("cannot get certificate key identifier: %d", GetLastError())); +		return CKR_ATTRIBUTE_TYPE_INVALID; +	} + +	cobj = calloc(sizeof(CertObject) + len, 1); +	if(!cobj) +		return CKR_HOST_MEMORY; + +	/* Store keyid in allocated area after CertObject */ +	cobj->key_id = (BYTE*)(cobj + 1); +	cobj->key_id_len = len; + +	if(!CertGetCertificateContextProperty(cert, CERT_KEY_IDENTIFIER_PROP_ID,  +										  cobj->key_id, &(cobj->key_id_len))) +	{ +		DBG(("cannot read certificate key identifier: %d", GetLastError())); +		free(cobj); +		return CKR_ATTRIBUTE_TYPE_INVALID; +	} + +	cobj->store = store; +	cobj->obj.id = 0; +	cobj->obj.unique_key = cobj->key_id; +	cobj->obj.unique_len = cobj->key_id_len; +	cobj->obj.obj_funcs = cert_object_vtable; +	cobj->obj.data_funcs = cert_objdata_vtable; +	 +	ret = ckcapi_object_register(sess, &(cobj->obj)); +	if(ret != CKR_OK) +	{ +		free(cobj); +		return ret; +	} + +	ASSERT(cobj->obj.id != 0); +	*id = cobj->obj.id; +	return CKR_OK; +} + +CK_RV +ckcapi_cert_find_in_store(CkCapiSession* sess, const char* store_name,  +						  CK_ATTRIBUTE_PTR match, CK_ULONG count, CkCapiArray* arr) +{ +	PCCERT_CONTEXT cert = NULL; +	CK_OBJECT_HANDLE obj; +	HCERTSTORE store; +	CkCapiObjectData objdata; +	DWORD err; +	CK_RV ret = CKR_OK; + +	store = CertOpenSystemStore((HCRYPTPROV)NULL, store_name); +	if(store == NULL) +	{ +		err = GetLastError(); + +		/* Store not found, we don't care */ +		if(err == ERROR_FILE_NOT_FOUND) +			return CKR_OK; + +		else +			return ckcapi_winerr_to_ckr(err); +	} + +	/* Match each certificate */ +	while((cert = CertEnumCertificatesInStore(store, cert)) != NULL) +	{ +		objdata.data = (void*)cert; +		objdata.data_funcs = cert_objdata_vtable; + +		if(ckcapi_object_data_match(&objdata, match, count)) +		{ +			ret = register_cert_object(sess, store, cert, &obj); +			if(ret != CKR_OK) +				break; + +			ckcapi_array_append(arr, obj); +		} +	} + +	ASSERT(store); +	CertCloseStore(store, 0); + +	return ret; +} + +CK_RV +ckcapi_cert_find_all(CkCapiSession* sess, CK_ATTRIBUTE_PTR match,  +					 CK_ULONG count, CkCapiArray* arr) +{ +	CK_RV ret; + +	ret = ckcapi_cert_find_in_store(sess, "My", match, count, arr); +	if(ret == CKR_OK) +		ret = ckcapi_cert_find_in_store(sess, "AddressBook", match, count, arr); +	if(ret == CKR_OK) +		ret = ckcapi_cert_find_in_store(sess, "CA", match, count, arr); +	if(ret == CKR_OK) +		ret = ckcapi_cert_find_in_store(sess, "Root", match, count, arr); +	if(ret == CKR_OK) +		ret = ckcapi_cert_find_in_store(sess, "Trust", match, count, arr); +	if(ret == CKR_OK) +		ret = ckcapi_cert_find_in_store(sess, "TrustedPeople", match, count, arr); +	if(ret == CKR_OK) +		ret = ckcapi_cert_find_in_store(sess, "AuthRoot", match, count, arr); +	return ret;  }  | 
