summaryrefslogtreecommitdiff
path: root/ckcapi-cert.c
diff options
context:
space:
mode:
Diffstat (limited to 'ckcapi-cert.c')
-rw-r--r--ckcapi-cert.c456
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;
}