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-object.c | 744 +++++++++++++++++++------------------------------------- 1 file changed, 248 insertions(+), 496 deletions(-) (limited to 'ckcapi-object.c') diff --git a/ckcapi-object.c b/ckcapi-object.c index 531ad2f..c02c365 100644 --- a/ckcapi-object.c +++ b/ckcapi-object.c @@ -1,9 +1,165 @@ -#include "cryptoki-capi.h" +#include "ckcapi.h" + +#include #include #include +static CkCapiArray* object_array = NULL; +static CkCapiHash* object_hash = NULL; + +static void +object_free(CkCapiObject* obj) +{ + ASSERT(obj); + ASSERT(obj->obj_funcs.release); + (obj->obj_funcs.release)(obj); +} + +CkCapiObject* +ckcapi_object_lookup(CkCapiSession* sess, CK_OBJECT_HANDLE obj) +{ + /* This must be called without any locks held */ + + CkCapiObject* ret = NULL; + + ASSERT(sess); + ASSERT(obj > 0); + + ckcapi_lock_global(); + + if(object_array && obj < object_array->len) + ret = ckcapi_array_index(object_array, CkCapiObject*, obj); + + ckcapi_unlock_global(); + + return ret; +} + +CK_RV +ckcapi_object_register(CkCapiSession* sess, CkCapiObject* obj) +{ + CkCapiObject* prev; + CK_RV ret = CKR_OK; + + ASSERT(sess); + ASSERT(obj->id == 0); + ASSERT(obj->unique_key); + ASSERT(obj->unique_len); + + DBGS(sess, "registering new session"); + + ckcapi_lock_global(); + + if(!object_array) + { + object_array = ckcapi_array_sized_new(0, 1, sizeof(CkCapiObject*), 16); + if(object_array) + { + /* A blank entry for '0' */ + CkCapiObject* blank = NULL; + ckcapi_array_append(object_array, blank); + } + + object_hash = ckcapi_hash_new(); + + + if(!object_array || !object_hash) + { + /* Allocation failed above */ + ret = CKR_HOST_MEMORY; + } + } + + if(ret == CKR_OK) + { + ASSERT(object_array); + ASSERT(object_hash); + + /* Look in the hash and find a previous object */ + prev = ckcapi_hash_get(object_hash, obj->unique_key, obj->unique_len); + if(prev) + { + /* Register it in the previous object's place */ + obj->id = prev->id; + ASSERT(prev->id < object_array->len); + if(ckcapi_hash_set(object_hash, obj->unique_key, obj->unique_len, obj)) + { + ckcapi_array_index(object_array, CkCapiObject*, obj->id) = obj; + object_free(prev); + DBGO(obj, "found old object id"); + } + else + { + ret = CKR_HOST_MEMORY; + } + + } + else + { + /* Register it at the end of the array */ + obj->id = object_array->len; + ASSERT(obj->id > 0); + if(ckcapi_hash_set(object_hash, obj->unique_key, obj->unique_len, obj)) + { + if(ckcapi_array_append(object_array, obj)) + { + DBGO(obj, "registered new object id"); + } + else + { + ret = CKR_HOST_MEMORY; + + /* Roll back our addition */ + ckcapi_hash_rem(object_hash, obj->unique_key, obj->unique_len); + } + } + else + { + ret = CKR_HOST_MEMORY; + } + } + } + + ckcapi_unlock_global(); + + return ret; + +} + +CK_RV +ckcapi_object_load_data(CkCapiObject* obj, CkCapiObjectData* objdata) +{ + ASSERT(obj); + ASSERT(obj->id); + ASSERT(obj->obj_funcs.load_data); + + memset(objdata, 0, sizeof(*objdata)); + return (obj->obj_funcs.load_data)(obj, objdata); +} + +void +ckcapi_object_data_release(CkCapiObjectData* objdata) +{ + ASSERT(objdata->data_funcs.release); + (objdata->data_funcs.release)(objdata->data); + memset(objdata, 0, sizeof(*objdata)); +} + +CK_RV +ckcapi_object_load_data_for(CkCapiSession* sess, CK_OBJECT_HANDLE hand, + CkCapiObjectData* objdata) +{ + CkCapiObject* obj; + + obj = ckcapi_object_lookup(sess, hand); + if(!obj) + return CKR_OBJECT_HANDLE_INVALID; + + return ckcapi_object_load_data(obj, objdata); +} + enum { DATA_UNKNOWN = 0, @@ -13,7 +169,6 @@ enum DATA_BYTES }; - int attribute_data_type(CK_ATTRIBUTE_TYPE type) { @@ -27,7 +182,7 @@ attribute_data_type(CK_ATTRIBUTE_TYPE type) case CKA_MODULUS_BITS: case CKA_PRIME_BITS: case CKA_SUBPRIME_BITS: - case CKA_SUB_PRIME_BITS: + /* case CKA_SUB_PRIME_BITS: */ case CKA_VALUE_BITS: case CKA_VALUE_LEN: case CKA_KEY_GEN_MECHANISM: @@ -40,7 +195,7 @@ attribute_data_type(CK_ATTRIBUTE_TYPE type) case CKA_BITS_PER_PIXEL: case CKA_MECHANISM_TYPE: case CKA_JAVA_MIDP_SECURITY_DOMAIN: - return DATA_ULONG: + return DATA_ULONG; // CK_BBOOL attribute types case CKA_TOKEN: @@ -56,7 +211,7 @@ attribute_data_type(CK_ATTRIBUTE_TYPE type) case CKA_NEVER_EXTRACTABLE: case CKA_ALWAYS_SENSITIVE: case CKA_WRAP_WITH_TRUSTED: - case CKA_ALWAYS_AUTHENTICATE + case CKA_ALWAYS_AUTHENTICATE: case CKA_ENCRYPT: case CKA_WRAP: case CKA_VERIFY: @@ -96,7 +251,7 @@ attribute_data_type(CK_ATTRIBUTE_TYPE type) case CKA_SUBPRIME: case CKA_BASE: case CKA_ECDSA_PARAMS: - case CKA_EC_PARAMS: + /* case CKA_EC_PARAMS: */ case CKA_EC_POINT: case CKA_CHAR_SETS: case CKA_ENCODING_METHODS: @@ -120,536 +275,133 @@ attribute_data_type(CK_ATTRIBUTE_TYPE type) }; } -CK_RV -attribute_fill(CK_ATTRIBUTE_PTR attr, CK_ATTRIBUTE_TYPE type, - CK_VOID_PTR* val, CK_ULONG len) +CK_BBOOL +ckcapi_object_data_match_attr(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR match) { - ASSERT(attr); - ASSERT(val); - ASSERT(len); - - attr->type = type; + CK_VOID_PTR value; + CK_ULONG len; + CK_RV rv; + int dtype; - if(attr->pValue && attr->ulValueLen < len) - { - attr->ulValueLen = len; - return CKR_BUFFER_TOO_SMALL; - } - - else if(!attr->pValue) - { - attr->ulValueLen = len; - return CKR_OK; - } + ASSERT(match); + ASSERT(objdata && objdata->data); - else - { - memcpy(attr->pValue, &val, len); - attr->ulValueLen = len; - return CKR_OK; - } -} + /* Get the data type of the attribute */ + dtype = attribute_data_type(match->type); + if(dtype == DATA_UNKNOWN) + return CK_FALSE; -CK_RV -attribute_match(CK_ATTRIBUTE_PTR attr, CK_ATTRIBUTE_TYPE type, - CK_VOID_PTR* val, CK_ULONG len) -{ - ASSERT(attr); - ASSERT(val); - ASSERT(len); - - if(attr->pValue && attr->ulValueLen == len && - memcmp(attr->pValue, &val, len); - return CKR_OK; - return CKR_CANCEL; -} + /* We only do byte matching */ + if(match->pValue == NULL) + return CK_FALSE; -/* -------------------------------------------------------------------------- - * CERTIFICATES - */ + /* Only load as much data as is needed */ + value = _alloca(match->ulValueLen > 4 ? match->ulValueLen : 4); + len = match->ulValueLen; -static CK_RV -cert_bool_attribute(CK_ATTRIBUTE_TYPE type, PCCERT_CONTEXT cert, - CK_ATTRIBUTE_PTR attr, CK_BBOOL fill) -{ - CK_BBOOL val; - switch(type) + switch(dtype) { - /* - * Resides on the token - * - Always true for CAPI objects. - */ - case CKA_TOKEN: - val = CK_TRUE; + case DATA_BOOL: + rv = (objdata->data_funcs.get_bool)(objdata->data, match->type, value, &len); break; - - /* - * Private vs. Public object. - * - Always false for certificates. - */ - case CKA_PRIVATE: - val = CK_FALSE; + case DATA_ULONG: + rv = (objdata->data_funcs.get_ulong)(objdata->data, match->type, value, &len); break; - - /* - * If object can be modified. - * - Currently always false. In the future with additional - * functionality this may change. - */ - case CKA_MODIFIABLE: - val = CK_FALSE; + case DATA_BYTES: + rv = (objdata->data_funcs.get_bytes)(objdata->data, match->type, value, &len); break; - - /* - * Whether the certificate can be trusted for the application - * in which it was created. - * - Use CertGetCertificateChain to build up a chain - * for this certificate, and then look in the CERT_CHAIN_CONTEXT - * TrustStatus field. - */ - case CKA_TRUSTED: - /* TODO: Implement */ - - default: - return CK_CANCEL; - }; - - if(fill) - return attribute_fill(attr, type, &val, sizeof(CK_BBOOL)); - else - return attribute_match(attr, type, &val, sizeof(CK_BBOOL)); -} - -static CK_RV -cert_ulong_attribute(CK_ATTRIBUTE_TYPE type, PCCERT_CONTEXT cert, - CK_ATTRIBUTE_PTR attr, CK_BBOOL fill) -{ - CK_ULONG val; - switch(type) - { - - /* - * Object class. - * - Always CKO_CERTIFICATE for certificates. - */ - case CKA_OBJECT_CLASS: - val = CKO_CERTIFICATE; + case DATA_DATE: + rv = (objdata->data_funcs.get_date)(objdata->data, match->type, value, &len); break; - - /* - * Type of certificate. - * - Always X509. - */ - case CKA_CERTIFICATE_TYPE: - val = CKC_X_509; - break; - - /* - * Whether a CA, user certificate, other. - * - Get certificate szOID_ENHANCED_KEY_USAGE - * extension or CERT_CTL_PROP_ID and look into CTL_USAGE structure. - */ - case CKA_CERTIFICATE_CATEGORY: - /* TODO: Implement */ - default: - return CK_CANCEL; - }; - - if(fill) - return attribute_fill(attr, type, &val, sizeof(CK_ULONG)); - else - return attribute_match(attr, type, &val, sizeof(CK_ULONG)); -} - -static CK_RV -cert_bytes_attribute(CK_ATTRIBUTE_TYPE type, PCCERT_CONTEXT cert, - CK_ATTRIBUTE_PTR attr, CK_BBOOL fill) -{ - switch(type) - { - - /* - * Description of the object. - * - We use CAPI's CERT_FRIENDLY_NAME_PROP_ID property, - * converted into UTF8. - */ - case CKA_LABEL: - xxxxx - 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: - xxxxx + ASSERT(0 && "unrecognized type"); break; - - /* - * DER-encoding of the certificate subject name. - * - * We use CAPI's CERT_CONTEXT pCertInfo->Subject field - * directly. - */ - case CKA_SUBJECT: - xxxxx - break; - - /* - * DER-encoding of the certificate issuer name. - * - * We use CAPI's CERT_CONTEXT pCertInfo->Issuer field - * directly. - */ - case CKA_ISSUER: - xxxxx - 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: - xxxxx - break; - - /* - * BER-encoding of the full certificate. - * - * We use CAPI's CERT_CONTEXT pbCertEncoded field directly. - */ - case CKA_VALUE: - xxxxx - 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: - break; - - /* - * 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 xxx -cert_date_attribute() -{ - switch(type) - { - - /* - * Start date for the certificate. - * - TODO: Work out where to get this. - */ - pCertInfo->NotBefore; - case CKA_START_DATE: - xxxx; - break; - - /* - * End date for the certificate. - * - TODO: Work out where to get this. - */ - pCertInfo->NotAfter; - case CKA_END_DATE: - xxxx; - break; - }; -} -xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - -static CK_RV -cert_ulong_attribute(CK_ATTRIBUTE_TYPE type, PCCERT_CONTEXT cert, - CK_ATTRIBUTE_PTR attr) -{ - CK_ATTRIBUTE_PTR ret; - CK_BBOOL val; - - switch(type) - { - case CKA_TOKEN: - val = CK_TRUE; - break; - case CKA_PRIVATE: - case CKA_MODIFIABLE: - val = CK_FALSE; - break; - }; - - /* If attr is initialized, just do a match */ - if(attr.type == type) - { - if(attr->pValue && attr->ulValueLen == sizeof(CK_BBOOL) && - memcmp(attr->pValue, &val, sizeof(CK_BBOOL)); - return CKR_OK; - return CKR_CANCEL; - } - - /* Otherwise fill in the value */ - else - { - return prep_attribute(attr, type, &val, sizeof(CK_BBOOL); - } -} - -static CK_ATTRIBUTE_PTR -cert_load_attribute(CK_ATTRIBUTE_TYPE type, PCCERT_CONTEXT cert) -{ - CK_BBOOL vval = CK_FALSE; - CK_ULONG uval = 0; - CK_VOID_PTR val; - CK_ULONG n_val; - - switch(type) - { - case CKA_CLASS: - uval = CKO_CERTIFICATE; - break; - case CKA_TOKEN; - - } -} - -static CK_BBOOL -cert_match_attr(CK_ATTRIBUTE_PTR match, PCCERT_CONTEXT cert) -{ - CK_ATTRIBUTE_PTR attr; - CK_BBOOL ret; - - attr = cert_load_attribute(match->type, cert); - if(!attr) + /* Value is longer than this one */ + if(rv == CKR_BUFFER_TOO_SMALL) + return CK_FALSE; + if(rv != CKR_OK) return CK_FALSE; - ret = (attr->ulValueLen == match->ulValueLen && - memcmp(attr->pValue, match->pValue, attr->ulValueLen) == 0); - - free_attribute(attr); - return ret; + return (match->ulValueLen == len && + memcmp(match->pValue, value, len) == 0); } -static CK_BBOOL -cert_match(CK_ATTRIBUTE_PTR matches, CK_ULONG count, - PCCERT_CONTEXT cert) +CK_BBOOL +ckcapi_object_data_match(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR matches, + CK_ULONG count) { CK_ULONG i; for(i = 0; i < count; ++i) { - if(!cert_match_attr(&match[i], cert)) + if(!ckcapi_object_data_match_attr(objdata, &matches[i])) return CK_FALSE; } return CK_TRUE; } -static CK_RV -gather_store_certs(const char* store_name, Array* arr, - CK_ATTRIBUTE_PTR match, CK_ULONG count) +CK_RV +ckcapi_object_data_get_attrs(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attrs, + CK_ULONG count) { - PCCERT_CONTEXT cert = NULL; - CK_OBJECT_HANDLE obj; - HCERTSTORE store; - 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; + CK_ULONG i; + CK_RV rv, ret = CKR_OK; - else - return ckcapi_winerr_to_ckr(err); - } + ASSERT(objdata && objdata->data); + ASSERT(!count || attrs); - /* Match each certificate */ - while((cert = CertEnumCertificatesInStore(store, cert)) != NULL) + for(i = 0; i < count; ++i) { - if(match_cert(match, count, cert)) + /* Get the data type of the attribute */ + switch(attribute_data_type(attrs[i].type)) { - obj = register_cert(store, cert); - if(!obj) - { - ret = CKR_HOST_MEMORY; - break; - } - - ckcapi_util_array_append(arr, obj); + case DATA_BOOL: + rv = (objdata->data_funcs.get_bool)(objdata->data, attrs[i].type, + attrs[i].pValue, &attrs[i].ulValueLen); + break; + case DATA_ULONG: + rv = (objdata->data_funcs.get_ulong)(objdata->data, attrs[i].type, + attrs[i].pValue, &attrs[i].ulValueLen); + break; + case DATA_BYTES: + rv = (objdata->data_funcs.get_bytes)(objdata->data, attrs[i].type, + attrs[i].pValue, &attrs[i].ulValueLen); + break; + case DATA_DATE: + rv = (objdata->data_funcs.get_date)(objdata->data, attrs[i].type, + attrs[i].pValue, &attrs[i].ulValueLen); + break; + case DATA_UNKNOWN: + rv = CKR_ATTRIBUTE_TYPE_INVALID; + break; + default: + ASSERT(0 && "unrecognized type"); + break; + }; + + /* Not an error if they were just requesting the size */ + if(rv == CKR_BUFFER_TOO_SMALL) + { + if(!attrs[i].pValue) + rv = CKR_OK; } - } - ASSERT(store); - CertCloseStore(store, 0); - - return ret; -} - -/* ---------------------------------------------------------------------------- - * FIND - */ - -BOOL -get_ulong_attribute(CK_ATTRIBUTE_TYPE type, CK_ATTRIBUTE_PTR templ, - CK_ULONG count, CK_ULONG* val) -{ - CK_ULONG i; - - ASSERT(val); - ASSERT(!count || templ); + /* Is it a fatal error? */ + else if(rv != CKR_ATTRIBUTE_TYPE_INVALID) + { + ret = rv; + break; + } - for(i = 0; i < count; ++i) - { - if(templ[i].type == type) + /* Transfer any non-fatal errors outward */ + if(rv != CKR_OK && ret == CKR_OK) { - *val = *((CK_ULONG*)templ[i].pValue); - return TRUE; + ret = rv; } } - return FALSE; -} - - - - -CK_RV -gather_objects(Array* arr, CK_ATTRIBUTE_PTR match, CK_ULONG count) -{ - CK_OBJECT_CLASS ocls = CK_INVALID_HANDLE; - CK_RV ret = CKR_OK; - - get_ulong_attribute(CKA_CLASS, match, count, &ocls); - switch(ocls) - { - /* all different classes */ - case CK_INVALID_HANDLE: - case CKO_CERTIFICATE: - ret = gather_certificates(arr, match, count); - break; - case CKO_PUBLIC_KEY: - case CKO_PRIVATE_KEY: - default: - break; - }; - return ret; -} - -void -cleanup_find_operation(Session* sess) -{ - ASSERT(sess->operation_type == OPERATION_FIND); - if(sess->operation_data) - ckcapi_util_array_free((Array*)sess->operation_data, TRUE); - sess->operation_type = OPERATION_NONE; - sess->operation_data = NULL; - sess->operation_cancel = NULL; -} - -CK_RV -ckcapi_object_find_init(Session* sess, CK_ATTRIBUTE_PTR match, - CK_ULONG count) -{ - Array* arr; - CK_RV ret; - - ASSERT(sess); - ASSERT(!count || match); - - if(sess->operation_type != OPERATION_NONE) - return CKR_OPERATION_ACTIVE; - - arr = ckcapi_util_array_new(0, 1, sizeof(CK_OBJECT_HANDLE)); - if(!arr) - return CKR_HOST_MEMORY; - - ret = gather_objects(arr, match, count); - if(ret != CKR_OK) - { - ckcapi_util_array_free(arr, TRUE); - return ret; - } - - sess->operation_type = OPERATION_FIND; - sess->operation_data = arr; - sess->operation_cancel = cleanup_find_operation; - - return CKR_OK; -} - -CK_RV -ckcapi_object_find(Session* sess, CK_OBJECT_HANDLE_PTR objects, - CK_ULONG max_object_count, CK_ULONG_PTR object_count) -{ - Array* arr; - size_t i; - - ASSERT(sess); - ASSERT(object_count); - ASSERT(!max_object_count || objects); - - if(sess->operation_type != OPERATION_FIND) - return CKR_OPERATION_NOT_INITIALIZED; - - if(!max_object_count) - { - *object_count = 0; - return CKR_OK; - } - - arr = (Array*)sess->operation_data; - *object_count = (max_object_count > arr->len ? arr->len : max_object_count); - for(i = 0; i < *object_count; ++i) - objects[i] = ckcapi_util_array_index(arr, CK_OBJECT_HANDLE, i); - ckcapi_util_array_remove_range(arr, 0, *object_count); - - return CKR_OK; -} - -CK_RV -ckcapi_objects_find_final(Session* sess) -{ - ASSERT(sess); - - if(sess->operation_type != OPERATION_FIND) - return CKR_OPERATION_NOT_INITIALIZED; - - cleanup_find_operation(sess); - return CKR_OK; -} - +} \ No newline at end of file -- cgit v1.2.3