/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is the Netscape security libraries. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1994-2000 * the Initial Developer. All Rights Reserved. * Portions created by Red Hat, Inc, are Copyright (C) 2005 * * Contributor(s): * Bob Relyea (rrelyea@redhat.com) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifdef DEBUG static const char CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$"; #endif /* DEBUG */ #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)) { 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 key_prov; DWORD size = sizeof(CRYPT_KEY_PROV_INFO); BOOL rv; rv =CertGetCertificateContextProperty(certContext, CERT_KEY_PROV_INFO_PROP_ID, &key_prov, &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; } } } 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; }