summaryrefslogtreecommitdiff
path: root/ckcapi-trust.c
diff options
context:
space:
mode:
Diffstat (limited to 'ckcapi-trust.c')
-rw-r--r--ckcapi-trust.c437
1 files changed, 437 insertions, 0 deletions
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 <wincrypt.h>
+
+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;
+}