| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #ifndef CKCAPI_H |
| #include "ckcapi.h" |
| #endif /* CKCAPI_H */ |
| |
| /* |
| * ckcapi/cfind.c |
| * |
| * This file implements the NSSCKMDFindObjects object for the |
| * "capi" cryptoki module. |
| */ |
| |
| struct ckcapiFOStr { |
| NSSArena *arena; |
| CK_ULONG n; |
| CK_ULONG i; |
| ckcapiInternalObject **objs; |
| }; |
| |
| static void |
| ckcapi_mdFindObjects_Final( |
| NSSCKMDFindObjects *mdFindObjects, |
| NSSCKFWFindObjects *fwFindObjects, |
| NSSCKMDSession *mdSession, |
| NSSCKFWSession *fwSession, |
| NSSCKMDToken *mdToken, |
| NSSCKFWToken *fwToken, |
| NSSCKMDInstance *mdInstance, |
| NSSCKFWInstance *fwInstance) |
| { |
| struct ckcapiFOStr *fo = (struct ckcapiFOStr *)mdFindObjects->etc; |
| NSSArena *arena = fo->arena; |
| PRUint32 i; |
| |
| /* walk down an free the unused 'objs' */ |
| for (i = fo->i; i < fo->n; i++) { |
| nss_ckcapi_DestroyInternalObject(fo->objs[i]); |
| } |
| |
| nss_ZFreeIf(fo->objs); |
| nss_ZFreeIf(fo); |
| nss_ZFreeIf(mdFindObjects); |
| if ((NSSArena *)NULL != arena) { |
| NSSArena_Destroy(arena); |
| } |
| |
| return; |
| } |
| |
| static NSSCKMDObject * |
| ckcapi_mdFindObjects_Next( |
| NSSCKMDFindObjects *mdFindObjects, |
| NSSCKFWFindObjects *fwFindObjects, |
| NSSCKMDSession *mdSession, |
| NSSCKFWSession *fwSession, |
| NSSCKMDToken *mdToken, |
| NSSCKFWToken *fwToken, |
| NSSCKMDInstance *mdInstance, |
| NSSCKFWInstance *fwInstance, |
| NSSArena *arena, |
| CK_RV *pError) |
| { |
| struct ckcapiFOStr *fo = (struct ckcapiFOStr *)mdFindObjects->etc; |
| ckcapiInternalObject *io; |
| |
| if (fo->i == fo->n) { |
| *pError = CKR_OK; |
| return (NSSCKMDObject *)NULL; |
| } |
| |
| io = fo->objs[fo->i]; |
| fo->i++; |
| |
| return nss_ckcapi_CreateMDObject(arena, io, pError); |
| } |
| |
| static CK_BBOOL |
| ckcapi_attrmatch( |
| CK_ATTRIBUTE_PTR a, |
| ckcapiInternalObject *o) |
| { |
| PRBool prb; |
| const NSSItem *b; |
| |
| b = nss_ckcapi_FetchAttribute(o, a->type); |
| if (b == NULL) { |
| return CK_FALSE; |
| } |
| |
| if (a->ulValueLen != b->size) { |
| /* match a decoded serial number */ |
| if ((a->type == CKA_SERIAL_NUMBER) && (a->ulValueLen < b->size)) { |
| unsigned int len; |
| unsigned char *data; |
| |
| data = nss_ckcapi_DERUnwrap(b->data, b->size, &len, NULL); |
| if ((len == a->ulValueLen) && |
| nsslibc_memequal(a->pValue, data, len, (PRStatus *)NULL)) { |
| return CK_TRUE; |
| } |
| } |
| return CK_FALSE; |
| } |
| |
| prb = nsslibc_memequal(a->pValue, b->data, b->size, (PRStatus *)NULL); |
| |
| if (PR_TRUE == prb) { |
| return CK_TRUE; |
| } else { |
| return CK_FALSE; |
| } |
| } |
| |
| static CK_BBOOL |
| ckcapi_match( |
| CK_ATTRIBUTE_PTR pTemplate, |
| CK_ULONG ulAttributeCount, |
| ckcapiInternalObject *o) |
| { |
| CK_ULONG i; |
| |
| for (i = 0; i < ulAttributeCount; i++) { |
| if (CK_FALSE == ckcapi_attrmatch(&pTemplate[i], o)) { |
| return CK_FALSE; |
| } |
| } |
| |
| /* Every attribute passed */ |
| return CK_TRUE; |
| } |
| |
| #define CKAPI_ITEM_CHUNK 20 |
| |
| #define PUT_Object(obj, err) \ |
| { \ |
| if (count >= size) { \ |
| *listp = *listp ? nss_ZREALLOCARRAY(*listp, ckcapiInternalObject *, \ |
| (size + \ |
| CKAPI_ITEM_CHUNK)) \ |
| : nss_ZNEWARRAY(NULL, ckcapiInternalObject *, \ |
| (size + \ |
| CKAPI_ITEM_CHUNK)); \ |
| if ((ckcapiInternalObject **)NULL == *listp) { \ |
| err = CKR_HOST_MEMORY; \ |
| goto loser; \ |
| } \ |
| size += CKAPI_ITEM_CHUNK; \ |
| } \ |
| (*listp)[count] = (obj); \ |
| count++; \ |
| } |
| |
| /* |
| * pass parameters back through the callback. |
| */ |
| typedef struct BareCollectParamsStr { |
| CK_OBJECT_CLASS objClass; |
| CK_ATTRIBUTE_PTR pTemplate; |
| CK_ULONG ulAttributeCount; |
| ckcapiInternalObject ***listp; |
| PRUint32 size; |
| PRUint32 count; |
| } BareCollectParams; |
| |
| /* collect_bare's callback. Called for each object that |
| * supposedly has a PROVINDER_INFO property */ |
| static BOOL WINAPI |
| doBareCollect( |
| const CRYPT_HASH_BLOB *msKeyID, |
| DWORD flags, |
| void *reserved, |
| void *args, |
| DWORD cProp, |
| DWORD *propID, |
| void **propData, |
| DWORD *propSize) |
| { |
| BareCollectParams *bcp = (BareCollectParams *)args; |
| PRUint32 size = bcp->size; |
| PRUint32 count = bcp->count; |
| ckcapiInternalObject ***listp = bcp->listp; |
| ckcapiInternalObject *io = NULL; |
| DWORD i; |
| CRYPT_KEY_PROV_INFO *keyProvInfo = NULL; |
| void *idData; |
| CK_RV error; |
| |
| /* make sure there is a Key Provider Info property */ |
| for (i = 0; i < cProp; i++) { |
| if (CERT_KEY_PROV_INFO_PROP_ID == propID[i]) { |
| keyProvInfo = (CRYPT_KEY_PROV_INFO *)propData[i]; |
| break; |
| } |
| } |
| if ((CRYPT_KEY_PROV_INFO *)NULL == keyProvInfo) { |
| return 1; |
| } |
| |
| /* copy the key ID */ |
| idData = nss_ZNEWARRAY(NULL, char, msKeyID->cbData); |
| if ((void *)NULL == idData) { |
| goto loser; |
| } |
| nsslibc_memcpy(idData, msKeyID->pbData, msKeyID->cbData); |
| |
| /* build a bare internal object */ |
| io = nss_ZNEW(NULL, ckcapiInternalObject); |
| if ((ckcapiInternalObject *)NULL == io) { |
| goto loser; |
| } |
| io->type = ckcapiBareKey; |
| io->objClass = bcp->objClass; |
| io->u.key.provInfo = *keyProvInfo; |
| io->u.key.provInfo.pwszContainerName = |
| nss_ckcapi_WideDup(keyProvInfo->pwszContainerName); |
| io->u.key.provInfo.pwszProvName = |
| nss_ckcapi_WideDup(keyProvInfo->pwszProvName); |
| io->u.key.provName = nss_ckcapi_WideToUTF8(keyProvInfo->pwszProvName); |
| io->u.key.containerName = |
| nss_ckcapi_WideToUTF8(keyProvInfo->pwszContainerName); |
| io->u.key.hProv = 0; |
| io->idData = idData; |
| io->id.data = idData; |
| io->id.size = msKeyID->cbData; |
| idData = NULL; |
| |
| /* see if it matches */ |
| if (CK_FALSE == ckcapi_match(bcp->pTemplate, bcp->ulAttributeCount, io)) { |
| goto loser; |
| } |
| PUT_Object(io, error); |
| bcp->size = size; |
| bcp->count = count; |
| return 1; |
| |
| loser: |
| if (io) { |
| nss_ckcapi_DestroyInternalObject(io); |
| } |
| nss_ZFreeIf(idData); |
| return 1; |
| } |
| |
| /* |
| * collect the bare keys running around |
| */ |
| static PRUint32 |
| collect_bare( |
| CK_OBJECT_CLASS objClass, |
| CK_ATTRIBUTE_PTR pTemplate, |
| CK_ULONG ulAttributeCount, |
| ckcapiInternalObject ***listp, |
| PRUint32 *sizep, |
| PRUint32 count, |
| CK_RV *pError) |
| { |
| BOOL rc; |
| BareCollectParams bareCollectParams; |
| |
| bareCollectParams.objClass = objClass; |
| bareCollectParams.pTemplate = pTemplate; |
| bareCollectParams.ulAttributeCount = ulAttributeCount; |
| bareCollectParams.listp = listp; |
| bareCollectParams.size = *sizep; |
| bareCollectParams.count = count; |
| |
| rc = CryptEnumKeyIdentifierProperties(NULL, CERT_KEY_PROV_INFO_PROP_ID, 0, |
| NULL, NULL, &bareCollectParams, doBareCollect); |
| |
| *sizep = bareCollectParams.size; |
| return bareCollectParams.count; |
| } |
| |
| /* find all the certs that represent the appropriate object (cert, priv key, or |
| * pub key) in the cert store. |
| */ |
| static PRUint32 |
| collect_class( |
| CK_OBJECT_CLASS objClass, |
| LPCSTR storeStr, |
| PRBool hasID, |
| CK_ATTRIBUTE_PTR pTemplate, |
| CK_ULONG ulAttributeCount, |
| ckcapiInternalObject ***listp, |
| PRUint32 *sizep, |
| PRUint32 count, |
| CK_RV *pError) |
| { |
| PRUint32 size = *sizep; |
| ckcapiInternalObject *next = NULL; |
| HCERTSTORE hStore; |
| PCCERT_CONTEXT certContext = NULL; |
| PRBool isKey = |
| (objClass == CKO_PUBLIC_KEY) | (objClass == CKO_PRIVATE_KEY); |
| |
| hStore = CertOpenSystemStore((HCRYPTPROV)NULL, storeStr); |
| if (NULL == hStore) { |
| return count; /* none found does not imply an error */ |
| } |
| |
| /* FUTURE: use CertFindCertificateInStore to filter better -- so we don't |
| * have to enumerate all the certificates */ |
| while ((PCERT_CONTEXT)NULL != |
| (certContext = CertEnumCertificatesInStore(hStore, certContext))) { |
| /* first filter out non user certs if we are looking for keys */ |
| if (isKey) { |
| /* make sure there is a Key Provider Info property */ |
| CRYPT_KEY_PROV_INFO *keyProvInfo; |
| DWORD size = 0; |
| BOOL rv; |
| rv = CertGetCertificateContextProperty(certContext, |
| CERT_KEY_PROV_INFO_PROP_ID, NULL, &size); |
| if (!rv) { |
| int reason = GetLastError(); |
| /* we only care if it exists, we don't really need to fetch it yet */ |
| if (reason == CRYPT_E_NOT_FOUND) { |
| continue; |
| } |
| } |
| /* filter out the non-microsoft providers */ |
| keyProvInfo = (CRYPT_KEY_PROV_INFO *)nss_ZAlloc(NULL, size); |
| if (keyProvInfo) { |
| rv = CertGetCertificateContextProperty(certContext, |
| CERT_KEY_PROV_INFO_PROP_ID, keyProvInfo, &size); |
| if (rv) { |
| char *provName = |
| nss_ckcapi_WideToUTF8(keyProvInfo->pwszProvName); |
| nss_ZFreeIf(keyProvInfo); |
| |
| if (provName && |
| (strncmp(provName, "Microsoft", sizeof("Microsoft") - 1) != 0)) { |
| continue; |
| } |
| } else { |
| int reason = |
| GetLastError(); |
| /* we only care if it exists, we don't really need to fetch it yet */ |
| nss_ZFreeIf(keyProvInfo); |
| if (reason == |
| CRYPT_E_NOT_FOUND) { |
| continue; |
| } |
| } |
| } |
| } |
| |
| if ((ckcapiInternalObject *)NULL == next) { |
| next = nss_ZNEW(NULL, ckcapiInternalObject); |
| if ((ckcapiInternalObject *)NULL == next) { |
| *pError = CKR_HOST_MEMORY; |
| goto loser; |
| } |
| } |
| next->type = ckcapiCert; |
| next->objClass = objClass; |
| next->u.cert.certContext = certContext; |
| next->u.cert.hasID = hasID; |
| next->u.cert.certStore = storeStr; |
| if (CK_TRUE == ckcapi_match(pTemplate, ulAttributeCount, next)) { |
| /* clear cached values that may be dependent on our old certContext */ |
| memset(&next->u.cert, 0, sizeof(next->u.cert)); |
| /* get a 'permanent' context */ |
| next->u.cert.certContext = CertDuplicateCertificateContext(certContext); |
| next->objClass = objClass; |
| next->u.cert.certContext = certContext; |
| next->u.cert.hasID = hasID; |
| next->u.cert.certStore = storeStr; |
| PUT_Object(next, *pError); |
| next = NULL; /* need to allocate a new one now */ |
| } else { |
| /* don't cache the values we just loaded */ |
| memset(&next->u.cert, 0, sizeof(next->u.cert)); |
| } |
| } |
| loser: |
| CertCloseStore(hStore, 0); |
| nss_ZFreeIf(next); |
| *sizep = size; |
| return count; |
| } |
| |
| NSS_IMPLEMENT PRUint32 |
| nss_ckcapi_collect_all_certs( |
| CK_ATTRIBUTE_PTR pTemplate, |
| CK_ULONG ulAttributeCount, |
| ckcapiInternalObject ***listp, |
| PRUint32 *sizep, |
| PRUint32 count, |
| CK_RV *pError) |
| { |
| count = collect_class(CKO_CERTIFICATE, "My", PR_TRUE, pTemplate, |
| ulAttributeCount, listp, sizep, count, pError); |
| /*count = collect_class(CKO_CERTIFICATE, "AddressBook", PR_FALSE, pTemplate, |
| ulAttributeCount, listp, sizep, count, pError); */ |
| count = collect_class(CKO_CERTIFICATE, "CA", PR_FALSE, pTemplate, |
| ulAttributeCount, listp, sizep, count, pError); |
| count = collect_class(CKO_CERTIFICATE, "Root", PR_FALSE, pTemplate, |
| ulAttributeCount, listp, sizep, count, pError); |
| count = collect_class(CKO_CERTIFICATE, "Trust", PR_FALSE, pTemplate, |
| ulAttributeCount, listp, sizep, count, pError); |
| count = collect_class(CKO_CERTIFICATE, "TrustedPeople", PR_FALSE, pTemplate, |
| ulAttributeCount, listp, sizep, count, pError); |
| count = collect_class(CKO_CERTIFICATE, "AuthRoot", PR_FALSE, pTemplate, |
| ulAttributeCount, listp, sizep, count, pError); |
| return count; |
| } |
| |
| CK_OBJECT_CLASS |
| ckcapi_GetObjectClass(CK_ATTRIBUTE_PTR pTemplate, |
| CK_ULONG ulAttributeCount) |
| { |
| CK_ULONG i; |
| |
| for (i = 0; i < ulAttributeCount; i++) { |
| if (pTemplate[i].type == CKA_CLASS) { |
| return *(CK_OBJECT_CLASS *)pTemplate[i].pValue; |
| } |
| } |
| /* need to return a value that says 'fetch them all' */ |
| return CK_INVALID_HANDLE; |
| } |
| |
| static PRUint32 |
| collect_objects( |
| CK_ATTRIBUTE_PTR pTemplate, |
| CK_ULONG ulAttributeCount, |
| ckcapiInternalObject ***listp, |
| CK_RV *pError) |
| { |
| PRUint32 i; |
| PRUint32 count = 0; |
| PRUint32 size = 0; |
| CK_OBJECT_CLASS objClass; |
| |
| /* |
| * first handle the static build in objects (if any) |
| */ |
| for (i = 0; i < nss_ckcapi_nObjects; i++) { |
| ckcapiInternalObject *o = (ckcapiInternalObject *)&nss_ckcapi_data[i]; |
| |
| if (CK_TRUE == ckcapi_match(pTemplate, ulAttributeCount, o)) { |
| PUT_Object(o, *pError); |
| } |
| } |
| |
| /* |
| * now handle the various object types |
| */ |
| objClass = ckcapi_GetObjectClass(pTemplate, ulAttributeCount); |
| *pError = CKR_OK; |
| switch (objClass) { |
| case CKO_CERTIFICATE: |
| count = nss_ckcapi_collect_all_certs(pTemplate, ulAttributeCount, listp, |
| &size, count, pError); |
| break; |
| case CKO_PUBLIC_KEY: |
| count = collect_class(objClass, "My", PR_TRUE, pTemplate, |
| ulAttributeCount, listp, &size, count, pError); |
| count = collect_bare(objClass, pTemplate, ulAttributeCount, listp, |
| &size, count, pError); |
| break; |
| case CKO_PRIVATE_KEY: |
| count = collect_class(objClass, "My", PR_TRUE, pTemplate, |
| ulAttributeCount, listp, &size, count, pError); |
| count = collect_bare(objClass, pTemplate, ulAttributeCount, listp, |
| &size, count, pError); |
| break; |
| /* all of them */ |
| case CK_INVALID_HANDLE: |
| count = nss_ckcapi_collect_all_certs(pTemplate, ulAttributeCount, listp, |
| &size, count, pError); |
| count = collect_class(CKO_PUBLIC_KEY, "My", PR_TRUE, pTemplate, |
| ulAttributeCount, listp, &size, count, pError); |
| count = collect_bare(CKO_PUBLIC_KEY, pTemplate, ulAttributeCount, listp, |
| &size, count, pError); |
| count = collect_class(CKO_PRIVATE_KEY, "My", PR_TRUE, pTemplate, |
| ulAttributeCount, listp, &size, count, pError); |
| count = collect_bare(CKO_PRIVATE_KEY, pTemplate, ulAttributeCount, listp, |
| &size, count, pError); |
| break; |
| default: |
| goto done; /* no other object types we understand in this module */ |
| } |
| if (CKR_OK != *pError) { |
| goto loser; |
| } |
| |
| done: |
| return count; |
| loser: |
| nss_ZFreeIf(*listp); |
| return 0; |
| } |
| |
| NSS_IMPLEMENT NSSCKMDFindObjects * |
| nss_ckcapi_FindObjectsInit( |
| NSSCKFWSession *fwSession, |
| CK_ATTRIBUTE_PTR pTemplate, |
| CK_ULONG ulAttributeCount, |
| CK_RV *pError) |
| { |
| /* This could be made more efficient. I'm rather rushed. */ |
| NSSArena *arena; |
| NSSCKMDFindObjects *rv = (NSSCKMDFindObjects *)NULL; |
| struct ckcapiFOStr *fo = (struct ckcapiFOStr *)NULL; |
| ckcapiInternalObject **temp = (ckcapiInternalObject **)NULL; |
| |
| arena = NSSArena_Create(); |
| if ((NSSArena *)NULL == arena) { |
| goto loser; |
| } |
| |
| rv = nss_ZNEW(arena, NSSCKMDFindObjects); |
| if ((NSSCKMDFindObjects *)NULL == rv) { |
| *pError = CKR_HOST_MEMORY; |
| goto loser; |
| } |
| |
| fo = nss_ZNEW(arena, struct ckcapiFOStr); |
| if ((struct ckcapiFOStr *)NULL == fo) { |
| *pError = CKR_HOST_MEMORY; |
| goto loser; |
| } |
| |
| fo->arena = arena; |
| /* fo->n and fo->i are already zero */ |
| |
| rv->etc = (void *)fo; |
| rv->Final = ckcapi_mdFindObjects_Final; |
| rv->Next = ckcapi_mdFindObjects_Next; |
| rv->null = (void *)NULL; |
| |
| fo->n = collect_objects(pTemplate, ulAttributeCount, &temp, pError); |
| if (*pError != CKR_OK) { |
| goto loser; |
| } |
| |
| fo->objs = nss_ZNEWARRAY(arena, ckcapiInternalObject *, fo->n); |
| if ((ckcapiInternalObject **)NULL == fo->objs) { |
| *pError = CKR_HOST_MEMORY; |
| goto loser; |
| } |
| |
| (void)nsslibc_memcpy(fo->objs, temp, sizeof(ckcapiInternalObject *) * fo->n); |
| nss_ZFreeIf(temp); |
| temp = (ckcapiInternalObject **)NULL; |
| |
| return rv; |
| |
| loser: |
| nss_ZFreeIf(temp); |
| nss_ZFreeIf(fo); |
| nss_ZFreeIf(rv); |
| if ((NSSArena *)NULL != arena) { |
| NSSArena_Destroy(arena); |
| } |
| return (NSSCKMDFindObjects *)NULL; |
| } |