diff options
-rw-r--r-- | ckcapi-cert.c | 201 | ||||
-rw-r--r-- | ckcapi.c | 49 | ||||
-rw-r--r-- | ckcapi.h | 7 |
3 files changed, 151 insertions, 106 deletions
diff --git a/ckcapi-cert.c b/ckcapi-cert.c index ebb34ff..6e51f3e 100644 --- a/ckcapi-cert.c +++ b/ckcapi-cert.c @@ -309,46 +309,25 @@ ckcapi_cert_certificate_get_bytes(PCCERT_CONTEXT cert, CK_ATTRIBUTE_PTR attr) WCHAR* utf16 = NULL; DWORD size; - /* Get the UTF16 string, a worst case of twice as long as UTF8 */ - if(data) - { - size = *len * sizeof(WCHAR); - utf16 = _alloca(size); - } - - if(!CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID, - utf16, &size)) + if(!CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID, NULL, &size)) { err = GetLastError(); if(err == CRYPT_E_NOT_FOUND) - utf16 = L""; + utf16 = L"Unnamed Certificate"; else return ckcapi_winerr_to_ckr(err); } - if(utf16) + if(!utf16) { - /* Always have a default name */ - if(!utf16[0]) - { - utf16 = L"Unnamed Certificate"; - size = wcslen(utf16) * 2; - } - - /* Convert the data into the buffer */ - *len = WideCharToMultiByte(CP_UTF8, 0, utf16, size / sizeof(WCHAR), - data, *len, NULL, NULL); - if(!*len) + utf16 = _alloca(size); + if(!CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID, utf16, &size)) return ckcapi_winerr_to_ckr(GetLastError()); } - else - { - /* Just return an appropriate allocation length */ - *len = (size / sizeof(WCHAR)) + sizeof(WCHAR); - } + return ckcapi_return_string(data, len, utf16); } - return CKR_OK; + break; /* * A byte array unique to this certificate. The CKA_ID of @@ -417,7 +396,7 @@ ckcapi_cert_certificate_get_bytes(PCCERT_CONTEXT cert, CK_ATTRIBUTE_PTR attr) */ case CKA_VALUE: return ckcapi_return_data(data, len, cert->pbCertEncoded, - cert->cbCertEncoded); + cert->cbCertEncoded); /* * If CKA_VALUE not specified, this is where the full @@ -514,22 +493,36 @@ register_cert_object(CkCapiSession* sess, PCCERT_CONTEXT cert, CkCapiObject** ob } static CK_RV -find_in_store(CkCapiSession* sess, CK_ATTRIBUTE_PTR match, - CK_ULONG count, CkCapiArray* arr) +find_in_store(CkCapiSession* sess, DWORD find_type, const void *find_criteria, + CK_ATTRIBUTE_PTR match, CK_ULONG count, CkCapiArray* arr) { PCCERT_CONTEXT cert = NULL; CkCapiObject* obj; - CertObjectData cdata; CkCapiObjectData* objdata; + CertObjectData cdata; + DWORD err; CK_RV ret = CKR_OK; /* No store, no objects */ if(!sess->store) return CKR_OK; - /* Match each certificate */ - while((cert = CertEnumCertificatesInStore(sess->store, cert)) != NULL) + for(;;) { + cert = CertFindCertificateInStore(sess->store, CKCAPI_ENCODINGS, 0, + find_type, find_criteria, cert); + 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 */ cdata.cert = cert; cdata.base.object = 0; cdata.base.data_funcs = &cert_objdata_vtable; @@ -537,72 +530,22 @@ find_in_store(CkCapiSession* sess, CK_ATTRIBUTE_PTR match, if(ckcapi_object_data_match(&cdata.base, match, count)) { ret = register_cert_object(sess, cert, &obj); - if(ret != CKR_OK) - break; - - ASSERT(obj); - - /* Store away the object data for performance reasons */ - objdata = cert_alloc_data(sess, obj, cert); - if(objdata) + if(ret == CKR_OK) { - ckcapi_session_take_object_data(sess, obj, objdata); - - /* For continuing the enumeration */ - cert = CertDuplicateCertificateContext(cert); - } - - ckcapi_array_append(arr, obj->id); - } - } - - return ret; -} - -static CK_RV -match_in_store(CkCapiSession* sess, PCERT_INFO info, CK_ATTRIBUTE_PTR match, - CK_ULONG count, CkCapiArray* arr) -{ - PCCERT_CONTEXT cert = NULL; - CkCapiObject* obj; - CkCapiObjectData* objdata; - CertObjectData cdata; - DWORD err; - CK_RV ret = CKR_OK; - - /* No store, no objects */ - if(!sess->store) - return CKR_OK; - - cert = CertGetSubjectCertificateFromStore(sess->store, CKCAPI_ENCODINGS, info); - if(cert == NULL) - { - err = GetLastError(); + ASSERT(obj); - /* 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 */ - cdata.cert = cert; - cdata.base.object = 0; - cdata.base.data_funcs = &cert_objdata_vtable; + /* Store away the object data for performance reasons */ + objdata = cert_alloc_data(sess, obj, cert); + if(objdata) + { + ckcapi_session_take_object_data(sess, obj, objdata); - if(ckcapi_object_data_match(&cdata.base, match, count)) - { - ret = register_cert_object(sess, cert, &obj); - if(ret == CKR_OK) - { - ASSERT(obj); + /* For continuing the enumeration */ + cert = CertDuplicateCertificateContext(cert); + } - /* Store away the object data for performance reasons */ - objdata = cert_alloc_data(sess, obj, cert); - if(objdata) - ckcapi_session_take_object_data(sess, obj, objdata); - ckcapi_array_append(arr, obj->id); + ckcapi_array_append(arr, obj->id); + } } } @@ -617,20 +560,46 @@ ckcapi_cert_find(CkCapiSession* sess, CK_OBJECT_CLASS cls, CK_ATTRIBUTE_PTR matc CK_ULONG count, CkCapiArray* arr) { CRYPT_INTEGER_BLOB* serial = NULL; - CERT_INFO info; CK_RV ret; CK_ULONG i; DWORD size; + CERT_INFO find_info; /* For searching by issuer and serial */ + CRYPT_HASH_BLOB find_key; /* For searching by ID */ + /* We only have certificates here */ if(cls != CKO_CERTIFICATE && cls != CKO_ANY) return CKR_OK; + /* Only work with slots that have certificates */ + if(!(ckcapi_token_get_flags (sess->slot) & CKCAPI_SLOT_CERTS)) + return CKR_OK; + /* + * There are some better searches we can do rather than + * listing everything. + * + * CKA_ISSUER + CKA_SERIAL_NUMBER * See if we have a issuer and serial number for a * specific certificate to find. + * + * CKA_ID + * Search by key identifier + * + * TODO: could search by hash (use CertFindCertificateInStore + * with CERT_FIND_HASH or CERT_FIND_SHA1_HASH or CERT_FIND_MD5_HASH) + * + * TODO: could search by issuer (use CertFindCertificateInStore + * with CERT_FIND_ISSUER_NAME) + * + * TODO: could search by subject (use CertFindCertificateInStore + * with CERT_FIND_SUBJECT_NAME) + * + * TODO: could search by CKA_VALUE (use CertFindCertificateInStore + * with CERT_FIND_EXISTING) */ - memset(&info, 0, sizeof(info)); + memset(&find_info, 0, sizeof(find_info)); + memset(&find_key, 0, sizeof(find_key)); for(i = 0; i < count; ++i) { @@ -639,10 +608,11 @@ ckcapi_cert_find(CkCapiSession* sess, CK_OBJECT_CLASS cls, CK_ATTRIBUTE_PTR matc if(match[i].type == CKA_ISSUER) { - info.Issuer.cbData = match[i].ulValueLen; - info.Issuer.pbData = match[i].pValue; + find_info.Issuer.cbData = match[i].ulValueLen; + find_info.Issuer.pbData = match[i].pValue; } - else if(!serial && match[i].type == CKA_SERIAL_NUMBER) + + else if(match[i].type == CKA_SERIAL_NUMBER && !serial) { if(!CryptDecodeObject(CKCAPI_ENCODINGS, X509_MULTI_BYTE_INTEGER, match[i].pValue, match[i].ulValueLen, 0, NULL, &size)) @@ -658,18 +628,37 @@ ckcapi_cert_find(CkCapiSession* sess, CK_OBJECT_CLASS cls, CK_ATTRIBUTE_PTR matc ASSERT(serial->cbData); ASSERT(serial->pbData); - info.SerialNumber.cbData = serial->cbData; - info.SerialNumber.pbData = serial->pbData; + find_info.SerialNumber.cbData = serial->cbData; + find_info.SerialNumber.pbData = serial->pbData; + } + + else if(match[i].type == CKA_ID) + { + find_key.cbData = match[i].ulValueLen; + find_key.pbData = match[i].pValue; } } /* Match a specific certificate */ - if(info.SerialNumber.cbData && info.Issuer.cbData) - ret = match_in_store(sess, &info, match, count, arr); + if(find_info.SerialNumber.cbData && find_info.Issuer.cbData) + { + ret = find_in_store(sess, CERT_FIND_SUBJECT_CERT, &find_info, + match, count, arr); + } + + /* Find all certificates with key identifier */ + else if(find_key.cbData) + { + ret = find_in_store(sess, CERT_FIND_KEY_IDENTIFIER, &find_key, + match, count, arr); + } /* Match any ol certificate */ else - ret = find_in_store(sess, match, count, arr); + { + ret = find_in_store(sess, CERT_FIND_ANY, NULL, + match, count, arr); + } if(serial) free(serial); @@ -160,6 +160,55 @@ ckcapi_return_data(CK_VOID_PTR dst, CK_ULONG_PTR dlen, return CKR_OK; } +CK_RV +ckcapi_return_string(CK_VOID_PTR dst, CK_ULONG_PTR dlen, + WCHAR* string) +{ + DWORD error; + int result; + + SetLastError(0); + + /* + * Sadly WideCharToMultiByte doesn't handle zero + * length strings properly. So we have to special + * case this part. + */ + if(!string[0]) + return ckcapi_return_data(dst, dlen, "\0", 1); + + result = WideCharToMultiByte(CP_UTF8, 0, string, -1, + dst, dst ? *dlen : 0, NULL, NULL); + + + /* An error result somehow */ + if(!result) + { + error = GetLastError(); + switch(error) + { + /* If buffer was too short, calculate good buffer length */ + case ERROR_INSUFFICIENT_BUFFER: + result = WideCharToMultiByte(CP_UTF8, 0, string, -1, + NULL, 0, NULL, NULL); + if(!result) + return CKR_GENERAL_ERROR; + *dlen = result; + return CKR_BUFFER_TOO_SMALL; + + /* A strange zero length success */ + case ERROR_SUCCESS: + break; + + /* All other errors are not handleable */ + default: + return CKR_GENERAL_ERROR; + } + } + + *dlen = result; + return CKR_OK; +} /* ---------------------------------------------------------------- */ @@ -91,6 +91,13 @@ 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); +/* + * This stores a string in the output buffer with appropriate + * PKCS#11 codes when the buffer is too short, or the caller + * just wants to know the length, etc. + */ +CK_RV ckcapi_return_string (CK_VOID_PTR dst, CK_ULONG_PTR dlen, + WCHAR* string); /* ------------------------------------------------------------------ */ |