/* * Copyright (C) 2007 Nate Nielsen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "ckcapi.h" #include "x509-usages.h" #include "pkcs11/pkcs11n.h" /* * These are the attributes expected by NSS on a trust object: * * CKA_CLASS * CKA_TOKEN * CKA_LABEL * CKA_CERT_SHA1_HASH * CKA_CERT_MD5_HASH * CKA_ISSUER * CKA_SUBJECT * CKA_TRUST_SERVER_AUTH * CKA_TRUST_CLIENT_AUTH * CKA_TRUST_EMAIL_PROTECTION * CKA_TRUST_CODE_SIGNING * CKA_SERIAL_NUMBER */ 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 _TrustObjectData { CkCapiObjectData base; PCCERT_CONTEXT cert; CTL_USAGE* usage; } TrustObjectData; static CK_ULONG has_usage(TrustObjectData* tdata, const char* oid) { CTL_USAGE* usage = tdata->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(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attr) { CK_BBOOL val; ASSERT(objdata); ASSERT(attr); switch(attr->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(attr->pValue, &attr->ulValueLen, &val, sizeof(CK_BBOOL)); } static CK_RV trust_ulong_attribute(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attr) { TrustObjectData* tdata = (TrustObjectData*)objdata; CK_ULONG val; ASSERT(tdata); ASSERT(attr); switch(attr->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(tdata, X509_USAGE_SERVER_AUTH); break; case CKA_TRUST_CLIENT_AUTH: val = has_usage(tdata, X509_USAGE_CLIENT_AUTH); break; case CKA_TRUST_CODE_SIGNING: val = has_usage(tdata, X509_USAGE_CODE_SIGNING); break; case CKA_TRUST_EMAIL_PROTECTION: val = has_usage(tdata, X509_USAGE_EMAIL); break; case CKA_TRUST_IPSEC_END_SYSTEM: val = has_usage(tdata, X509_USAGE_IPSEC_ENDPOINT); break; case CKA_TRUST_IPSEC_TUNNEL: val = has_usage(tdata, X509_USAGE_IPSEC_TUNNEL); break; case CKA_TRUST_IPSEC_USER: val = has_usage(tdata, X509_USAGE_IPSEC_USER); break; case CKA_TRUST_TIME_STAMPING: val = has_usage(tdata, X509_USAGE_TIME_STAMPING); break; default: return CKR_ATTRIBUTE_TYPE_INVALID; }; return ckcapi_return_data(attr->pValue, &attr->ulValueLen, &val, sizeof(CK_ULONG)); } static CK_RV trust_bytes_attribute(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attr) { TrustObjectData* tdata = (TrustObjectData*)objdata; ASSERT(tdata); ASSERT(attr); switch(attr->type) { /* * Forward these through to the certificate itself. */ case CKA_SUBJECT: case CKA_ISSUER: case CKA_SERIAL_NUMBER: case CKA_LABEL: ASSERT(tdata->cert); return ckcapi_cert_certificate_get_bytes(tdata->cert, attr); /* * The hash of the DER encoded certificate. */ case CKA_CERT_MD5_HASH: case CKA_CERT_SHA1_HASH: if(!CryptHashCertificate(0, attr->type == CKA_CERT_MD5_HASH ? CALG_MD5 : CALG_SHA1, 0, tdata->cert->pbCertEncoded, tdata->cert->cbCertEncoded, attr->pValue, (DWORD*)(&attr->ulValueLen))) return ckcapi_winerr_to_ckr(GetLastError()); return CKR_OK; }; return CKR_ATTRIBUTE_TYPE_INVALID; } static CK_RV trust_date_attribute(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attr) { return CKR_ATTRIBUTE_TYPE_INVALID; } static void trust_release(void* data) { TrustObjectData* tdata = (TrustObjectData*)data; ASSERT(tdata); ASSERT(tdata->cert); CertFreeCertificateContext(tdata->cert); if(tdata->usage) free(tdata->usage); free(tdata); } 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(TrustObjectData* tdata) { DWORD size, usize, err; CTL_USAGE* usage; void* buf; ASSERT(!tdata->usage); /* Get the size of the usage property */ if(!CertGetCertificateContextProperty(tdata->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(tdata->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()); } tdata->usage = usage; return CKR_OK; } static CK_RV trust_load_data(CkCapiSession* sess, CkCapiObject* obj, CkCapiObjectData** objdata) { TrustObject* tobj = (TrustObject*)obj; TrustObjectData* tdata; 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; tdata = (TrustObjectData*)calloc(1, sizeof(TrustObjectData)); if(!tdata) return CKR_HOST_MEMORY; tdata->cert = ckcapi_cert_object_data_get_certificate (certdata); ASSERT(tdata->cert); /* Dig up the usage data */ ret = parse_usage(tdata); if(ret != CKR_OK) { free(tdata); return ret; } /* And keep a reference to the certificate */ tdata->cert = CertDuplicateCertificateContext(tdata->cert); tdata->base.object = obj->id; tdata->base.data_funcs = &trust_objdata_vtable; *objdata = &(tdata->base); 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) { CK_OBJECT_HANDLE id; 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(CK_OBJECT_HANDLE)); 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) { id = ckcapi_array_index(certarr, CK_OBJECT_HANDLE, i); ASSERT(id); certobj = ckcapi_object_lookup (sess, id); 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->id); } } ckcapi_array_free(certarr, TRUE); return ret; }