From 4928a4bff079c140d261cb3fefdc07c60c0bdebf Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Fri, 27 Apr 2007 20:37:40 +0000 Subject: A bunch more changes, that implement almost complete find and get support. --- ckcapi-cert.c | 456 ++++++++++++++++++++++++++++------------------------------ 1 file changed, 221 insertions(+), 235 deletions(-) (limited to 'ckcapi-cert.c') 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 +#include + +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)) @@ -135,218 +140,6 @@ cert_ulong_attribute(void* obj, CK_ATTRIBUTE_TYPE type, return copy_static_data(data, len, &val, sizeof(CK_ULONG)); } -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) @@ -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; } -- cgit v1.2.3