From 16be360038c56328695f1dd8133718f82cd0c36c Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Sun, 6 May 2007 02:02:09 +0000 Subject: A bunch of trust work on the plugin. --- ckcapi-builtin.c | 14 +- ckcapi-cert.c | 222 ++++++++++++++++++++-------- ckcapi-object.c | 21 +++ ckcapi-session.c | 64 ++++---- ckcapi-trust.c | 437 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ ckcapi.c | 28 ++++ ckcapi.dsp | 4 + ckcapi.dsw | 14 +- ckcapi.h | 48 ++++-- x509-usages.h | 39 +++++ 10 files changed, 776 insertions(+), 115 deletions(-) create mode 100644 ckcapi-trust.c create mode 100644 x509-usages.h diff --git a/ckcapi-builtin.c b/ckcapi-builtin.c index 94d170e..ad70ea2 100644 --- a/ckcapi-builtin.c +++ b/ckcapi-builtin.c @@ -91,7 +91,7 @@ static const CkCapiObjectDataVtable builtin_objdata_vtable = { }; static CK_RV -builtin_load(CkCapiObject* obj, CkCapiObjectData* objdata) +builtin_load_data(CkCapiSession* sess, CkCapiObject* obj, CkCapiObjectData* objdata) { BuiltinObject* bobj = (BuiltinObject*)obj; @@ -118,12 +118,12 @@ builtin_object_release(void* data) } static const CkCapiObjectVtable builtin_object_vtable = { - builtin_load, + builtin_load_data, builtin_object_release, }; static CK_RV -register_builtin_object(CkCapiSession* sess, CK_ULONG index, CK_OBJECT_HANDLE_PTR id) +register_builtin_object(CkCapiSession* sess, CK_ULONG index, CkCapiObject** obj) { BuiltinObject* bobj; CK_RV ret; @@ -148,15 +148,15 @@ register_builtin_object(CkCapiSession* sess, CK_ULONG index, CK_OBJECT_HANDLE_PT } ASSERT(bobj->obj.id != 0); - *id = bobj->obj.id; + *obj = &(bobj->obj); return CKR_OK; } CK_RV -ckcapi_builtin_find_all(CkCapiSession* sess, CK_ATTRIBUTE_PTR match, - CK_ULONG count, CkCapiArray* arr) +ckcapi_builtin_find(CkCapiSession* sess, CK_OBJECT_CLASS cls, CK_ATTRIBUTE_PTR match, + CK_ULONG count, CkCapiArray* arr) { - CK_OBJECT_HANDLE obj; + CkCapiObject* obj; CkCapiObjectData objdata; CK_RV ret = CKR_OK; CK_ULONG i; diff --git a/ckcapi-cert.c b/ckcapi-cert.c index 0162983..887fefb 100644 --- a/ckcapi-cert.c +++ b/ckcapi-cert.c @@ -15,6 +15,18 @@ #define USE_ENCODINGS (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) +/* All the stores we look in for certificates, in this order */ +static const char* CERT_STORES[] = { + "My", + "AddressBook", + "CA", + "Root", + "Trust", + "TrustedPeople", + "AuthRoot", + NULL +}; + typedef struct _CertObject { CkCapiObject obj; @@ -33,37 +45,6 @@ typedef struct _CertObject } CertObject; -typedef struct _CertUnique -{ - int otype; - BYTE key[1]; -} -CertUnique; - -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_ATTRIBUTE_TYPE_INVALID; - - if(!val) - { - *len = cb; - return CKR_OK; - } - - if(cb > *len) - { - *len = cb; - return CKR_BUFFER_TOO_SMALL; - } - - *len = cb; - memcpy(val, data, cb); - return CKR_OK; -} - static CK_RV cert_bool_attribute(void* obj, CK_ATTRIBUTE_TYPE type, CK_VOID_PTR data, CK_ULONG_PTR len) @@ -114,7 +95,7 @@ cert_bool_attribute(void* obj, CK_ATTRIBUTE_TYPE type, return CKR_ATTRIBUTE_TYPE_INVALID; }; - return copy_static_data(data, len, &val, sizeof(CK_BBOOL)); + return ckcapi_return_data(data, len, &val, sizeof(CK_BBOOL)); } static CK_RV @@ -163,12 +144,12 @@ cert_ulong_attribute(void* obj, CK_ATTRIBUTE_TYPE type, return CKR_BUFFER_TOO_SMALL; } - return copy_static_data(data, len, &val, sizeof(CK_ULONG)); + return ckcapi_return_data(data, len, &val, sizeof(CK_ULONG)); } -static CK_RV -cert_bytes_attribute(void* obj, CK_ATTRIBUTE_TYPE type, - CK_VOID_PTR data, CK_ULONG_PTR len) +CK_RV +ckcapi_cert_get_bytes_attribute(void* obj, CK_ATTRIBUTE_TYPE type, + CK_VOID_PTR data, CK_ULONG_PTR len) { PCCERT_CONTEXT cert = (PCCERT_CONTEXT)obj; DWORD err; @@ -258,7 +239,7 @@ cert_bytes_attribute(void* obj, CK_ATTRIBUTE_TYPE type, * directly. */ case CKA_SUBJECT: - return copy_static_data(data, len, cert->pCertInfo->Subject.pbData, + return ckcapi_return_data(data, len, cert->pCertInfo->Subject.pbData, cert->pCertInfo->Subject.cbData); /* @@ -268,7 +249,7 @@ cert_bytes_attribute(void* obj, CK_ATTRIBUTE_TYPE type, * directly. */ case CKA_ISSUER: - return copy_static_data(data, len, cert->pCertInfo->Issuer.pbData, + return ckcapi_return_data(data, len, cert->pCertInfo->Issuer.pbData, cert->pCertInfo->Issuer.cbData); /* @@ -298,7 +279,7 @@ cert_bytes_attribute(void* obj, CK_ATTRIBUTE_TYPE type, * We use CAPI's CERT_CONTEXT pbCertEncoded field directly. */ case CKA_VALUE: - return copy_static_data(data, len, cert->pbCertEncoded, + return ckcapi_return_data(data, len, cert->pbCertEncoded, cert->cbCertEncoded); /* @@ -372,13 +353,13 @@ cert_release(void* data) static const CkCapiObjectDataVtable cert_objdata_vtable = { cert_bool_attribute, cert_ulong_attribute, - cert_bytes_attribute, + ckcapi_cert_get_bytes_attribute, cert_date_attribute, cert_release, }; static CK_RV -cert_load(CkCapiObject* obj, CkCapiObjectData* objdata) +cert_load_data(CkCapiSession* sess, CkCapiObject* obj, CkCapiObjectData* objdata) { CertObject* cobj = (CertObject*)obj; HCERTSTORE store; @@ -419,6 +400,10 @@ cert_load(CkCapiObject* obj, CkCapiObjectData* objdata) return ckcapi_winerr_to_ckr(GetLastError()); } + /* + * Important: The trust stuff depends on the objdata value of a + * certificate object is a CERT_CONTEXT pointer. + */ objdata->data = (void*)cert; objdata->data_funcs = cert_objdata_vtable; return CKR_OK; @@ -434,7 +419,7 @@ cert_object_release(void* data) } static const CkCapiObjectVtable cert_object_vtable = { - cert_load, + cert_load_data, cert_object_release, }; @@ -500,9 +485,9 @@ clear_object_data_for_store(CkCapiSession* sess, CkCapiObject* obj, ckcapi_session_clear_object_data(sess, obj); } -CK_RV -ckcapi_cert_find_in_store(CkCapiSession* sess, const char* store_name, - CK_ATTRIBUTE_PTR match, CK_ULONG count, CkCapiArray* arr) +static CK_RV +find_in_store(CkCapiSession* sess, const char* store_name, + CK_ATTRIBUTE_PTR match, CK_ULONG count, CkCapiArray* arr) { PCCERT_CONTEXT cert = NULL; CkCapiObject* obj; @@ -549,10 +534,73 @@ ckcapi_cert_find_in_store(CkCapiSession* sess, const char* store_name, CertFreeCertificateContext((PCCERT_CONTEXT)objdata.data); } - ckcapi_array_append(arr, obj->id); + ckcapi_array_append(arr, obj); + } + } + + ASSERT(store); + CertCloseStore(store, 0); + + return ret; +} + +static CK_RV +match_in_store(CkCapiSession* sess, const char* store_name, PCERT_INFO info, + CK_ATTRIBUTE_PTR match, CK_ULONG count, CkCapiArray* arr) +{ + PCCERT_CONTEXT cert = NULL; + CkCapiObject* 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); + } + + cert = CertGetSubjectCertificateFromStore(store, USE_ENCODINGS, info); + if(cert == NULL) + { + err = GetLastError(); + + /* Certificate not found, we don't care */ + if(err == CRYPT_E_NOT_FOUND) + return CKR_OK; + else + return ckcapi_winerr_to_ckr(err); + } + + /* Match the certificate */ + objdata.data = (void*)cert; + objdata.data_funcs = cert_objdata_vtable; + + if(ckcapi_object_data_match(&objdata, match, count)) + { + ret = register_cert_object(sess, store_name, cert, &obj); + if(ret == CKR_OK) + { + ASSERT(obj); + + /* Store away the object data for performance reasons */ + ret = ckcapi_session_set_object_data(sess, obj, &objdata); + if(ret == CKR_OK) + ckcapi_array_append(arr, obj); } } + if(ret != CKR_OK && cert) + CertFreeCertificateContext(cert); + + ASSERT(store); CertCloseStore(store, 0); @@ -560,23 +608,75 @@ ckcapi_cert_find_in_store(CkCapiSession* sess, const char* store_name, } CK_RV -ckcapi_cert_find_all(CkCapiSession* sess, CK_ATTRIBUTE_PTR match, - CK_ULONG count, CkCapiArray* arr) +ckcapi_cert_find(CkCapiSession* sess, CK_OBJECT_CLASS cls, CK_ATTRIBUTE_PTR match, + CK_ULONG count, CkCapiArray* arr) { + CRYPT_INTEGER_BLOB* serial = NULL; + CERT_INFO info; CK_RV ret; + BOOL specific; + CK_ULONG i; + DWORD size; + + /* We only have certificates here */ + if(cls != CKO_CERTIFICATE && cls != CKO_ANY) + return CKR_OK; + + /* + * See if we have a issuer and serial number for a + * specific certificate to find. + */ + memset(&info, 0, sizeof(info)); + + for(i = 0; i < count; ++i) + { + if(!match[i].pValue || !match[i].ulValueLen) + continue; + + if(match[i].type == CKA_ISSUER) + { + info.Issuer.cbData = match[i].ulValueLen; + info.Issuer.pbData = match[i].pValue; + } + else if(!serial && match[i].type == CKA_SERIAL_NUMBER) + { + if(!CryptDecodeObject(USE_ENCODINGS, X509_MULTI_BYTE_INTEGER, + match[i].pValue, match[i].ulValueLen, 0, NULL, &size)) + { + continue; + } + + serial = calloc(1, size); + if(!CryptDecodeObject(USE_ENCODINGS, X509_MULTI_BYTE_INTEGER, + match[i].pValue, match[i].ulValueLen, 0, serial, &size)) + continue; + + ASSERT(serial->cbData); + ASSERT(serial->pbData); + + info.SerialNumber.cbData = serial->cbData; + info.SerialNumber.pbData = serial->pbData; + } + } + + specific = info.SerialNumber.cbData && info.Issuer.cbData; + + for(i = 0; CERT_STORES[i]; ++i) + { + /* Match a specific certificate */ + if(specific) + ret = match_in_store(sess, CERT_STORES[i], &info, match, count, arr); + + /* Match any ol certificate */ + else + ret = find_in_store(sess, CERT_STORES[i], match, count, arr); + + if(ret != CKR_OK) + break; + } + + if(serial) + free(serial); - 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; } diff --git a/ckcapi-object.c b/ckcapi-object.c index 49da8eb..4bc0ce1 100644 --- a/ckcapi-object.c +++ b/ckcapi-object.c @@ -1,6 +1,8 @@ #include "ckcapi.h" +#include "pkcs11/pkcs11n.h" + #include #include @@ -208,6 +210,14 @@ attribute_data_type(CK_ATTRIBUTE_TYPE type) case CKA_BITS_PER_PIXEL: case CKA_MECHANISM_TYPE: case CKA_JAVA_MIDP_SECURITY_DOMAIN: + case CKA_TRUST_SERVER_AUTH: + case CKA_TRUST_CLIENT_AUTH: + case CKA_TRUST_CODE_SIGNING: + case CKA_TRUST_EMAIL_PROTECTION: + case CKA_TRUST_IPSEC_END_SYSTEM: + case CKA_TRUST_IPSEC_TUNNEL: + case CKA_TRUST_IPSEC_USER: + case CKA_TRUST_TIME_STAMPING: return DATA_ULONG; // CK_BBOOL attribute types @@ -234,6 +244,7 @@ attribute_data_type(CK_ATTRIBUTE_TYPE type) case CKA_RESET_ON_INIT: case CKA_HAS_RESET: case CKA_COLOR: + case CKA_TRUST_STEP_UP_APPROVED: return DATA_BOOL; // Raw or string data @@ -272,6 +283,8 @@ attribute_data_type(CK_ATTRIBUTE_TYPE type) case CKA_REQUIRED_CMS_ATTRIBUTES: case CKA_DEFAULT_CMS_ATTRIBUTES: case CKA_SUPPORTED_CMS_ATTRIBUTES: + case CKA_CERT_SHA1_HASH: + case CKA_CERT_MD5_HASH: return DATA_BYTES; // CK_DATE data @@ -284,6 +297,7 @@ attribute_data_type(CK_ATTRIBUTE_TYPE type) case CKA_ALLOWED_MECHANISMS: case CKA_UNWRAP_TEMPLATE: default: + DBG(("unknown attribute type: %x\n", type)); return DATA_UNKNOWN; }; } @@ -331,9 +345,15 @@ ckcapi_object_data_match_attr(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR match) break; }; + /* Unrecognized attribute */ + if(rv == CKR_ATTRIBUTE_TYPE_INVALID) + return CK_FALSE; + /* Value is longer than this one */ if(rv == CKR_BUFFER_TOO_SMALL) return CK_FALSE; + + /* All other errors */ if(rv != CKR_OK) return CK_FALSE; @@ -413,6 +433,7 @@ ckcapi_object_data_get_attrs(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attrs, /* Attribute doesn't exist */ else if(rv == CKR_ATTRIBUTE_TYPE_INVALID) { + DBG(("O%d: attribute not found: 0x%08x", objdata->object, attrs[i].type)); attrs[i].ulValueLen = (CK_ULONG)-1; } diff --git a/ckcapi-session.c b/ckcapi-session.c index 9f9a6e6..8d00316 100644 --- a/ckcapi-session.c +++ b/ckcapi-session.c @@ -348,17 +348,6 @@ ckcapi_session_close_all() * OBJECT DATA */ -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); -} - CK_RV ckcapi_session_get_object_data(CkCapiSession* sess, CkCapiObject* obj, CkCapiObjectData** objdata) @@ -384,12 +373,16 @@ ckcapi_session_get_object_data(CkCapiSession* sess, CkCapiObject* obj, return CKR_HOST_MEMORY; newdata->object = id; - ret = (obj->obj_funcs.load_data)(obj, newdata); + ret = (obj->obj_funcs.load_data)(sess, obj, newdata); if(ret != CKR_OK) { free(newdata); return ret; } + newdata->object = id; + ASSERT(newdata->data); + ASSERT(newdata->data_funcs.release); + if(!ckcapi_hash_set(sess->object_data, &newdata->object, sizeof(newdata->object), newdata)) { object_data_release(newdata); @@ -517,28 +510,25 @@ static CK_RV gather_objects(CkCapiSession* sess, CK_ATTRIBUTE_PTR match, CK_ULONG count, CkCapiArray* arr) { - CK_OBJECT_CLASS ocls = CK_INVALID_HANDLE; + CK_OBJECT_CLASS ocls = CKO_ANY; CK_RV ret = CKR_OK; - /* TODO: Reenable this once we have trust worked out - ret = ckcapi_builtin_find_all(sess, match, count, arr); + get_ulong_attribute(CKA_CLASS, match, count, &ocls); + + /* Search for builtins */ + ret = ckcapi_builtin_find(sess, ocls, match, count, arr); if(ret != CKR_OK) return ret; - */ - get_ulong_attribute(CKA_CLASS, match, count, &ocls); - switch(ocls) - { - /* all different classes */ - case CK_INVALID_HANDLE: - case CKO_CERTIFICATE: - ret = ckcapi_cert_find_all(sess, match, count, arr); - break; - case CKO_PUBLIC_KEY: - case CKO_PRIVATE_KEY: - default: - break; - }; + /* Search for certificates */ + ret = ckcapi_cert_find(sess, ocls, match, count, arr); + if(ret != CKR_OK) + return ret; + + /* Search through trust objects */ + ret = ckcapi_trust_find(sess, ocls, match, count, arr); + if(ret != CKR_OK) + return ret; return ret; } @@ -555,10 +545,10 @@ cleanup_find_operation(CkCapiSession* sess) } void -purge_duplicate_ulongs(CkCapiArray* arr) +purge_duplicate_objects(CkCapiArray* arr) { CkCapiHash* checks; - CK_ULONG* v; + CkCapiObject* v; size_t i; checks = ckcapi_hash_new(); @@ -567,15 +557,15 @@ purge_duplicate_ulongs(CkCapiArray* arr) for(i = 0; i < arr->len; ) { - v = &ckcapi_array_index(arr, CK_ULONG, i); - if(ckcapi_hash_get(checks, v, sizeof(CK_ULONG))) + v = ckcapi_array_index(arr, CkCapiObject*, i); + if(ckcapi_hash_get(checks, &v, sizeof(CkCapiObject*))) { ckcapi_array_remove_index(arr, i); /* Look at same i again */ } else { - if(!ckcapi_hash_set(checks, v, sizeof(CK_ULONG), v)) + if(!ckcapi_hash_set(checks, &v, sizeof(CkCapiObject*), &v)) break; ++i; } @@ -597,7 +587,7 @@ ckcapi_session_find_init(CkCapiSession* sess, CK_ATTRIBUTE_PTR match, if(sess->operation_type != OPERATION_NONE) return CKR_OPERATION_ACTIVE; - arr = ckcapi_array_new(0, 1, sizeof(CK_OBJECT_HANDLE)); + arr = ckcapi_array_new(0, 1, sizeof(CkCapiObject*)); if(!arr) return CKR_HOST_MEMORY; @@ -609,7 +599,7 @@ ckcapi_session_find_init(CkCapiSession* sess, CK_ATTRIBUTE_PTR match, } /* Cleanup all duplicates in the array */ - purge_duplicate_ulongs(arr); + purge_duplicate_objects(arr); sess->operation_type = OPERATION_FIND; sess->operation_data = arr; @@ -641,7 +631,7 @@ ckcapi_session_find(CkCapiSession* sess, CK_OBJECT_HANDLE_PTR objects, arr = (CkCapiArray*)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_array_index(arr, CK_OBJECT_HANDLE, i); + objects[i] = ckcapi_array_index(arr, CkCapiObject*, i)->id; ckcapi_array_remove_range(arr, 0, *object_count); return CKR_OK; diff --git a/ckcapi-trust.c b/ckcapi-trust.c new file mode 100644 index 0000000..c49a2de --- /dev/null +++ b/ckcapi-trust.c @@ -0,0 +1,437 @@ + +#include "ckcapi.h" +#include "x509-usages.h" + +#include "pkcs11/pkcs11n.h" + +#include + +typedef struct _TrustObject +{ + CkCapiObject obj; + + /* Together these form the unique key. Must be contiguous */ + unsigned int otype; + CK_OBJECT_HANDLE cert_obj; +} +TrustObject; + +typedef struct _TrustData +{ + PCCERT_CONTEXT cert; + CTL_USAGE* usage; +} +TrustData; + +static CK_ULONG +has_usage(TrustData* trust_data, const char* oid) +{ + CTL_USAGE* usage = trust_data->usage; + DWORD i; + + /* No usages, means anything goes */ + if(usage == NULL) + return CKT_NETSCAPE_TRUSTED_DELEGATOR; + + for(i = 0; i < usage->cUsageIdentifier; ++i) + { + if(usage->rgpszUsageIdentifier[i] && + strcmp(oid, usage->rgpszUsageIdentifier[i]) == 0) + return CKT_NETSCAPE_TRUSTED_DELEGATOR; + } + + /* TODO: What's the correct thing to return here? */ + return CKT_NETSCAPE_VALID_DELEGATOR; +} + +static CK_RV +trust_bool_attribute(void* obj, CK_ATTRIBUTE_TYPE type, + CK_VOID_PTR data, CK_ULONG_PTR len) +{ + CK_BBOOL val; + + ASSERT(obj); + + switch(type) + { + /* + * Resides on the token + * - Always true for CAPI objects. + */ + case CKA_TOKEN: + val = CK_TRUE; + break; + + /* + * Private vs. Public object. + * - Always false for certificates. + */ + case CKA_PRIVATE: + val = CK_FALSE; + break; + + /* + * If object can be modified. + * TODO: This needs to be set to yes once we get change support. + */ + case CKA_MODIFIABLE: + val = CK_FALSE; + break; + + /* + * TODO: Figure out what this is. + */ + case CKA_TRUST_STEP_UP_APPROVED: + val = CK_FALSE; + break; + + default: + return CKR_ATTRIBUTE_TYPE_INVALID; + }; + + return ckcapi_return_data(data, len, &val, sizeof(CK_BBOOL)); +} + +static CK_RV +trust_ulong_attribute(void* obj, CK_ATTRIBUTE_TYPE type, + CK_VOID_PTR data, CK_ULONG_PTR len) +{ + TrustData* trust_data = (TrustData*)obj; + CK_ULONG val; + + ASSERT(obj); + + switch(type) + { + + /* + * Object class. + * - Always CKO_NETSCAPE_TRUST for netscape trust + */ + case CKA_CLASS: + val = CKO_NETSCAPE_TRUST; + break; + + /* + * Various trust flags + */ + case CKA_TRUST_SERVER_AUTH: + val = has_usage(trust_data, X509_USAGE_SERVER_AUTH); + break; + case CKA_TRUST_CLIENT_AUTH: + val = has_usage(trust_data, X509_USAGE_CLIENT_AUTH); + break; + case CKA_TRUST_CODE_SIGNING: + val = has_usage(trust_data, X509_USAGE_CODE_SIGNING); + break; + case CKA_TRUST_EMAIL_PROTECTION: + val = has_usage(trust_data, X509_USAGE_EMAIL); + break; + case CKA_TRUST_IPSEC_END_SYSTEM: + val = has_usage(trust_data, X509_USAGE_IPSEC_ENDPOINT); + break; + case CKA_TRUST_IPSEC_TUNNEL: + val = has_usage(trust_data, X509_USAGE_IPSEC_TUNNEL); + break; + case CKA_TRUST_IPSEC_USER: + val = has_usage(trust_data, X509_USAGE_IPSEC_USER); + break; + case CKA_TRUST_TIME_STAMPING: + val = has_usage(trust_data, X509_USAGE_TIME_STAMPING); + break; + + default: + return CKR_ATTRIBUTE_TYPE_INVALID; + }; + + if(*len < sizeof(CK_ULONG)) + { + *len = sizeof(CK_ULONG); + return CKR_BUFFER_TOO_SMALL; + } + + return ckcapi_return_data(data, len, &val, sizeof(CK_ULONG)); +} + +static CK_RV +trust_bytes_attribute(void* obj, CK_ATTRIBUTE_TYPE type, + CK_VOID_PTR data, CK_ULONG_PTR len) +{ + TrustData* trust_data = (TrustData*)obj; + ASSERT(obj); + + switch(type) + { + /* + * Forward these through to the certificate itself. + */ + case CKA_SUBJECT: + case CKA_ISSUER: + case CKA_SERIAL_NUMBER: + case CKA_LABEL: + ASSERT(trust_data->cert); + return ckcapi_cert_get_bytes_attribute((void*)(trust_data->cert), + type, data, len); + + /* + * TODO: These should probably be implemented + */ + case CKA_CERT_MD5_HASH: + case CKA_CERT_SHA1_HASH: + return CKR_ATTRIBUTE_TYPE_INVALID; + + }; + + return CKR_ATTRIBUTE_TYPE_INVALID; +} + +static CK_RV +trust_date_attribute(void* obj, CK_ATTRIBUTE_TYPE type, + CK_VOID_PTR data, CK_ULONG_PTR len) +{ + return CKR_ATTRIBUTE_TYPE_INVALID; +} + +static void +trust_release(void* data) +{ + TrustData* trust_data = (TrustData*)data; + ASSERT(trust_data); + + ASSERT(trust_data->cert); + CertFreeCertificateContext(trust_data->cert); + + if(trust_data->usage) + free(trust_data->usage); + + free(trust_data); +} + +static const CkCapiObjectDataVtable trust_objdata_vtable = { + trust_bool_attribute, + trust_ulong_attribute, + trust_bytes_attribute, + trust_date_attribute, + trust_release, +}; + +static CK_RV +parse_usage(TrustData* trust_data) +{ + DWORD size, usize, err; + CTL_USAGE* usage; + void* buf; + + ASSERT(!trust_data->usage); + + /* Get the size of the usage property */ + if(!CertGetCertificateContextProperty(trust_data->cert, CERT_CTL_USAGE_PROP_ID, + NULL, &size)) + { + err = GetLastError(); + + /* No usage data is not an error */ + if(err == CRYPT_E_NOT_FOUND) + return CKR_OK; + return ckcapi_winerr_to_ckr(err); + } + + /* Now get the actual usage property */ + buf = _alloca(size); + if(!CertGetCertificateContextProperty(trust_data->cert, CERT_CTL_USAGE_PROP_ID, + buf, &size)) + { + err = GetLastError(); + if(err == CRYPT_E_NOT_FOUND) + return CKR_OK; + return ckcapi_winerr_to_ckr(err); + } + + /* Get the decoded size of the usage property */ + if(!CryptDecodeObject(X509_ASN_ENCODING, X509_ENHANCED_KEY_USAGE, + buf, size, 0, NULL, &usize)) + return ckcapi_winerr_to_ckr(GetLastError()); + + /* Allocate and decode it */ + usage = (CTL_USAGE*)calloc(1, usize); + if(!usage) + return CKR_HOST_MEMORY; + if(!CryptDecodeObject(X509_ASN_ENCODING, X509_ENHANCED_KEY_USAGE, + buf, size, 0, buf, &usize)) + { + free(usage); + return ckcapi_winerr_to_ckr(GetLastError()); + } + + trust_data->usage = usage; + return CKR_OK; +} + +static CK_RV +trust_load_data(CkCapiSession* sess, CkCapiObject* obj, CkCapiObjectData* objdata) +{ + TrustObject* tobj = (TrustObject*)obj; + TrustData* trust_data; + CkCapiObjectData* certdata; + CK_RV ret; + + ASSERT(tobj); + ASSERT(objdata); + + /* Get the raw data for the certificate */ + ret = ckcapi_session_get_object_data_for(sess, tobj->cert_obj, &certdata); + if(ret != CKR_OK) + return ret; + + trust_data = (TrustData*)calloc(1, sizeof(TrustData)); + if(!trust_data) + return CKR_HOST_MEMORY; + + ASSERT(certdata->data); + trust_data->cert = certdata->data; + + /* Dig up the usage data */ + ret = parse_usage(trust_data); + if(ret != CKR_OK) + { + free(trust_data); + return ret; + } + + /* And keep a reference to the certificate */ + trust_data->cert = CertDuplicateCertificateContext((PCCERT_CONTEXT)(certdata->data)); + + objdata->data = trust_data; + objdata->data_funcs = trust_objdata_vtable; + + return CKR_OK; +} + + +static void +trust_object_release(void* data) +{ + TrustObject* tobj = (TrustObject*)data; + ASSERT(tobj); + free(tobj); +} + +static const CkCapiObjectVtable trust_object_vtable = { + trust_load_data, + trust_object_release, +}; + +static CK_RV +register_trust_object(CkCapiSession* sess, CkCapiObject* cert, CkCapiObject** obj) +{ + TrustObject* tobj; + CK_RV ret; + + tobj = calloc(sizeof(TrustObject), 1); + if(!tobj) + return CKR_HOST_MEMORY; + + tobj->otype = OBJECT_TRUST; + tobj->cert_obj = cert->id; + + tobj->obj.id = 0; + tobj->obj.obj_funcs = trust_object_vtable; + tobj->obj.unique_key = UNIQUE_KEY_AT(tobj, otype); + tobj->obj.unique_len = UNIQUE_KEY_LEN(tobj, otype, cert_obj); + + ret = ckcapi_object_register(sess, &(tobj->obj)); + if(ret != CKR_OK) + { + free(tobj); + return ret; + } + + ASSERT(tobj->obj.id != 0); + *obj = &(tobj->obj); + + return CKR_OK; +} + +static CK_RV +list_matching_certificates(CkCapiSession* sess, CK_ATTRIBUTE_PTR match, + CK_ULONG count, CkCapiArray* arr) +{ + CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; + CK_ATTRIBUTE search[3]; + CK_ULONG n_search = 0; + CK_ULONG i; + + /* The class */ + search[0].type = CKA_CLASS; + search[0].pValue = &cert_class; + search[0].ulValueLen = sizeof(CK_OBJECT_CLASS); + ++n_search; + + for(i = 0; i < count && n_search < 3; ++i) + { + /* + * These are the attributes that tie a certificate + * to trust object, so try match certs with these + */ + if(match[i].type == CKA_ISSUER || + match[i].type == CKA_SERIAL_NUMBER) + { + search[n_search].type = match[i].type; + search[n_search].pValue = match[i].pValue; + search[n_search].ulValueLen = match[i].ulValueLen; + ++n_search; + } + } + + /* Do the certificate search */ + return ckcapi_cert_find(sess, CKO_CERTIFICATE, search, n_search, arr); +} + +CK_RV +ckcapi_trust_find(CkCapiSession* sess, CK_OBJECT_CLASS cls, + CK_ATTRIBUTE_PTR match, CK_ULONG count, CkCapiArray* arr) +{ + CkCapiObject* obj; + CkCapiObject* certobj; + CkCapiObjectData* objdata; + CkCapiArray* certarr; + CK_RV ret = CKR_OK; + CK_ULONG i; + + if(cls != CKO_NETSCAPE_TRUST) + return CKR_OK; + + /* Get a list of all certificates */ + certarr = ckcapi_array_new(0, 1, sizeof(CkCapiObject*)); + if(!certarr) + return CKR_HOST_MEMORY; + ret = list_matching_certificates(sess, match, count, certarr); + + /* Now match each of them against our criteria */ + if(ret == CKR_OK) + { + for(i = 0; i < certarr->len; ++i) + { + certobj = ckcapi_array_index(certarr, CkCapiObject*, i); + ASSERT(certobj); + + /* We'll register a trust object for any loaded certificate */ + ret = register_trust_object(sess, certobj, &obj); + if(ret != CKR_OK) + break; + + ASSERT(obj); + + ret = ckcapi_session_get_object_data(sess, obj, &objdata); + if(ret != CKR_OK) + break; + + /* Only return new object if it matches */ + if(ckcapi_object_data_match(objdata, match, count)) + ckcapi_array_append(arr, obj); + } + } + + ckcapi_array_free(certarr, TRUE); + return ret; +} diff --git a/ckcapi.c b/ckcapi.c index 0bed282..f0c4629 100644 --- a/ckcapi.c +++ b/ckcapi.c @@ -112,6 +112,34 @@ ckcapi_winerr_to_ckr(DWORD werr) } +CK_RV +ckcapi_return_data(CK_VOID_PTR dst, CK_ULONG_PTR dlen, + CK_VOID_PTR src, DWORD slen) +{ + // TODO: This check should probably go elsewhere + if(slen == 0) + return CKR_ATTRIBUTE_TYPE_INVALID; + + /* Just asking for the length */ + if(!dst) + { + *dlen = slen; + return CKR_OK; + } + + /* Buffer is too short */ + if(slen > *dlen) + { + *dlen = slen; + return CKR_BUFFER_TOO_SMALL; + } + + *dlen = slen; + memcpy(dst, src, slen); + return CKR_OK; +} + + /* ---------------------------------------------------------------- */ static CK_RV diff --git a/ckcapi.dsp b/ckcapi.dsp index d81ae3a..6d73e54 100644 --- a/ckcapi.dsp +++ b/ckcapi.dsp @@ -108,6 +108,10 @@ SOURCE=".\ckcapi-session.c" # End Source File # Begin Source File +SOURCE=".\ckcapi-trust.c" +# End Source File +# Begin Source File + SOURCE=".\ckcapi-util.c" # End Source File # Begin Source File diff --git a/ckcapi.dsw b/ckcapi.dsw index 7f71040..a6cb239 100644 --- a/ckcapi.dsw +++ b/ckcapi.dsw @@ -3,7 +3,19 @@ Microsoft Developer Studio Workspace File, Format Version 6.00 ############################################################################### -Project: "cryptoki_capi"=".\cryptoki_capi.dsp" - Package Owner=<4> +Project: "cryptoki_capi"=".\ckcapi.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "sandbox"=".\sandbox\sandbox.dsp" - Package Owner=<4> Package=<5> {{{ diff --git a/ckcapi.h b/ckcapi.h index d6ee27b..d3ead45 100644 --- a/ckcapi.h +++ b/ckcapi.h @@ -21,6 +21,9 @@ struct _CkCapiSession; typedef struct _CkCapiObject CkCapiObject; typedef struct _CkCapiSession CkCapiSession; +/* Represents 'any' class in searches */ +#define CKO_ANY CK_INVALID_HANDLE + /* ------------------------------------------------------------------ * cryptoki-capi.c */ @@ -33,6 +36,9 @@ void ckcapi_lock_global(void); void ckcapi_unlock_global(void); CK_RV ckcapi_winerr_to_ckr (DWORD werr); +CK_RV ckcapi_return_data(CK_VOID_PTR dst, CK_ULONG_PTR dlen, + CK_VOID_PTR src, DWORD slen); + /* object data ------------------- */ typedef CK_RV (*CkCapiGetAttribute)(void* obj, CK_ATTRIBUTE_TYPE type, @@ -126,16 +132,18 @@ void ckcapi_session_enum_object_data (CkCapiSession* sess, CkCapiEnumObjectData * ckcapi-object.c */ -/* For operation_type in CkCapiSession */ +/* Used internally to guarantee uniqueness between object types */ enum { OBJECT_CERT = 1, - OBJECT_BUILTIN = 2 + OBJECT_BUILTIN = 2, + OBJECT_TRUST = 3 }; typedef CK_RV (*CkCapiPurge)(struct _CkCapiObject* obj); -typedef CK_RV (*CkCapiLoadData)(struct _CkCapiObject* obj, CkCapiObjectData* objdata); +typedef CK_RV (*CkCapiLoadData)(CkCapiSession* sess, struct _CkCapiObject* obj, + CkCapiObjectData* objdata); typedef struct _CkCapiObjectVtable { @@ -176,6 +184,8 @@ struct _CkCapiObject #define DBGO(obj, msg) \ ckcapi_debug("O%d: %s", (obj) ? (obj)->id : 0, (msg)) +#define DBGOD(objdata, msg) \ + ckcapi_debug("O%d: %s", (objdata) ? (objdata)->obj : 0, (msg)) CK_OBJECT_HANDLE ckcapi_object_get_max_handle (void); @@ -199,18 +209,38 @@ CK_RV ckcapi_object_data_get_attrs (CkCapiObjectData* objdata, CK_ATTRIBUTE_P * ckcapi-cert.c */ -CK_RV ckcapi_cert_find_all (CkCapiSession* sess, CK_ATTRIBUTE_PTR match, - CK_ULONG count, CkCapiArray* arr); +CK_RV ckcapi_cert_find (CkCapiSession* sess, CK_OBJECT_CLASS cls, + CK_ATTRIBUTE_PTR match, CK_ULONG count, + CkCapiArray* arr); -CK_RV ckcapi_cert_find_in_store (CkCapiSession* sess, const char* store_name, - CK_ATTRIBUTE_PTR match, CK_ULONG count, CkCapiArray* arr); +CK_RV ckcapi_cert_find_specific (CkCapiSession* sess, CK_OBJECT_CLASS cls, + CK_ATTRIBUTE_PTR issuer, CK_ATTRIBUTE_PTR serial, + CK_OBJECT_HANDLE_PTR obj); + +/* Called by trust stuff */ +CK_RV ckcapi_cert_get_bytes_attribute (void* cert, CK_ATTRIBUTE_TYPE type, + CK_VOID_PTR data, CK_ULONG_PTR len); /* ------------------------------------------------------------------- * ckcapi-builtin.c */ -CK_RV ckcapi_builtin_find_all (CkCapiSession* sess, CK_ATTRIBUTE_PTR match, - CK_ULONG count, CkCapiArray* arr); +CK_RV ckcapi_builtin_find (CkCapiSession* sess, CK_OBJECT_CLASS cls, + CK_ATTRIBUTE_PTR match, CK_ULONG count, + CkCapiArray* arr); + +/* ------------------------------------------------------------------- + * ckcapi-trust.c + */ + +CK_RV ckcapi_trust_find (CkCapiSession* sess, CK_OBJECT_CLASS cls, + CK_ATTRIBUTE_PTR match, CK_ULONG count, + CkCapiArray* arr); + +CK_RV ckcapi_trust_find_specific (CkCapiSession* sess, CK_OBJECT_CLASS cls, + CK_ATTRIBUTE_PTR issuer, CK_ATTRIBUTE_PTR serial, + CK_OBJECT_HANDLE_PTR obj); + #endif /* CRYPTOKI_CAPI_H */ diff --git a/x509-usages.h b/x509-usages.h new file mode 100644 index 0000000..d63c8bb --- /dev/null +++ b/x509-usages.h @@ -0,0 +1,39 @@ +#ifndef _X509_USAGES_H_ +#define _X509_USAGES_H_ + + +#define X509_USAGE_SERVER_AUTH "1.3.6.1.5.5.7.3.1" +#define X509_USAGE_CLIENT_AUTH "1.3.6.1.5.5.7.3.2" +#define X509_USAGE_CODE_SIGNING "1.3.6.1.5.5.7.3.3" +#define X509_USAGE_EMAIL "1.3.6.1.5.5.7.3.4" +#define X509_USAGE_TIME_STAMPING "1.3.6.1.5.5.7.3.8" +#define X509_USAGE_IPSEC_ENDPOINT "1.3.6.1.5.5.7.3.5" +#define X509_USAGE_IPSEC_TUNNEL "1.3.6.1.5.5.7.3.6" +#define X509_USAGE_IPSEC_USER "1.3.6.1.5.5.7.3.7" +#define X509_USAGE_IKE_INTERMEDIATE "1.3.6.1.5.5.8.2.2" + + +#define MS_USAGE_TRUST_LIST_SIGNING "1.3.6.1.4.1.311.10.3.1" +#define MS_USAGE_TIME_STAMPING "1.3.6.1.4.1.311.10.3.2" +#define MS_USAGE_EFS "1.3.6.1.4.1.311.10.3.4" +#define MS_USAGE_DRIVER_VERIFICATION "1.3.6.1.4.1.311.10.3.5" +#define MS_USAGE_SYSTEM_VERIFICATION "1.3.6.1.4.1.311.10.3.6" +#define MS_USAGE_OEM_VERIFICATION "1.3.6.1.4.1.311.10.3.7" +#define MS_USAGE_EMBEDDED_VERIFICATION "1.3.6.1.4.1.311.10.3.8" +#define MS_USAGE_KEY_PACK "1.3.6.1.4.1.311.10.6.1" +#define MS_USAGE_LICENSE_SERVER "1.3.6.1.4.1.311.10.6.2" +#define MS_USAGE_SMART_CARD "1.3.6.1.4.1.311.20.2.2" +#define MS_USAGE_DIGITAL_RIGHTS "1.3.6.1.4.1.311.10.5.1" +#define MS_USAGE_QUALIFIED_SUBORDINATION "1.3.6.1.4.1.311.10.3.10" +#define MS_USAGE_KEY_RECOVERY "1.3.6.1.4.1.311.10.3.11" +#define MS_USAGE_DOCUMENT_SIGNING "1.3.6.1.4.1.311.10.3.12" +#define MS_USAGE_FILE_RECOVERY "1.3.6.1.4.1.311.10.3.4.1" +#define MS_USAGE_ROOT_SIGNER_LIST "1.3.6.1.4.1.311.10.3.9" +#define MS_USAGE_APPLICATION_POLICIES "1.3.6.1.4.1.311.10.12.1" +#define MS_USAGE_AD_EMAIL_REPLICATION "1.3.6.1.4.1.311.21.19" +#define MS_USAGE_CERTIFICATE_REQUEST_AGENT "1.3.6.1.4.1.311.20.2.1" +#define MS_USAGE_KEY_RECOVERY_AGENT "1.3.6.1.4.1.311.21.6" +#define MS_USAGE_CA_ENCRYPTION_CERTIFICATE "1.3.6.1.4.1.311.21.5" +#define MS_USAGE_LIFETIME_SIGNING "1.3.6.1.4.1.311.10.3.13" + +#endif /* _X509_USAGES_H_ */ -- cgit v1.2.3