/* 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/. */ #include "pkcs11.h" #ifndef DEVM_H #include "devm.h" #endif /* DEVM_H */ #ifndef CKHELPER_H #include "ckhelper.h" #endif /* CKHELPER_H */ #include "pk11func.h" #include "dev3hack.h" #include "secerr.h" extern const NSSError NSS_ERROR_NOT_FOUND; extern const NSSError NSS_ERROR_INVALID_ARGUMENT; extern const NSSError NSS_ERROR_PKCS11; /* The number of object handles to grab during each call to C_FindObjects */ #define OBJECT_STACK_SIZE 16 NSS_IMPLEMENT PRStatus nssToken_Destroy( NSSToken *tok) { if (tok) { if (PR_ATOMIC_DECREMENT(&tok->base.refCount) == 0) { PK11_FreeSlot(tok->pk11slot); PZ_DestroyLock(tok->base.lock); nssTokenObjectCache_Destroy(tok->cache); (void)nssSlot_Destroy(tok->slot); return nssArena_Destroy(tok->base.arena); } } return PR_SUCCESS; } NSS_IMPLEMENT void nssToken_Remove( NSSToken *tok) { nssTokenObjectCache_Clear(tok->cache); } NSS_IMPLEMENT NSSToken * nssToken_AddRef( NSSToken *tok) { PR_ATOMIC_INCREMENT(&tok->base.refCount); return tok; } NSS_IMPLEMENT NSSSlot * nssToken_GetSlot( NSSToken *tok) { return nssSlot_AddRef(tok->slot); } NSS_IMPLEMENT void * nssToken_GetCryptokiEPV( NSSToken *token) { return nssSlot_GetCryptokiEPV(token->slot); } NSS_IMPLEMENT nssSession * nssToken_GetDefaultSession( NSSToken *token) { return token->defaultSession; } NSS_IMPLEMENT NSSUTF8 * nssToken_GetName( NSSToken *tok) { if (tok == NULL) { return ""; } if (tok->base.name[0] == 0) { (void)nssSlot_IsTokenPresent(tok->slot); } return tok->base.name; } NSS_IMPLEMENT NSSUTF8 * NSSToken_GetName( NSSToken *token) { return nssToken_GetName(token); } NSS_IMPLEMENT PRBool nssToken_IsLoginRequired( NSSToken *token) { return (token->ckFlags & CKF_LOGIN_REQUIRED); } NSS_IMPLEMENT PRBool nssToken_NeedsPINInitialization( NSSToken *token) { return (!(token->ckFlags & CKF_USER_PIN_INITIALIZED)); } NSS_IMPLEMENT PRStatus nssToken_DeleteStoredObject( nssCryptokiObject *instance) { CK_RV ckrv; PRStatus status; PRBool createdSession = PR_FALSE; NSSToken *token = instance->token; nssSession *session = NULL; void *epv = nssToken_GetCryptokiEPV(instance->token); if (token->cache) { nssTokenObjectCache_RemoveObject(token->cache, instance); } if (instance->isTokenObject) { if (token->defaultSession && nssSession_IsReadWrite(token->defaultSession)) { session = token->defaultSession; } else { session = nssSlot_CreateSession(token->slot, NULL, PR_TRUE); createdSession = PR_TRUE; } } if (session == NULL) { return PR_FAILURE; } nssSession_EnterMonitor(session); ckrv = CKAPI(epv)->C_DestroyObject(session->handle, instance->handle); nssSession_ExitMonitor(session); if (createdSession) { nssSession_Destroy(session); } status = PR_SUCCESS; if (ckrv != CKR_OK) { status = PR_FAILURE; /* use the error stack to pass the PKCS #11 error out */ nss_SetError(ckrv); nss_SetError(NSS_ERROR_PKCS11); } return status; } static nssCryptokiObject * import_object( NSSToken *tok, nssSession *sessionOpt, CK_ATTRIBUTE_PTR objectTemplate, CK_ULONG otsize) { nssSession *session = NULL; PRBool createdSession = PR_FALSE; nssCryptokiObject *object = NULL; CK_OBJECT_HANDLE handle; CK_RV ckrv; void *epv = nssToken_GetCryptokiEPV(tok); if (nssCKObject_IsTokenObjectTemplate(objectTemplate, otsize)) { if (sessionOpt) { if (!nssSession_IsReadWrite(sessionOpt)) { nss_SetError(NSS_ERROR_INVALID_ARGUMENT); return NULL; } session = sessionOpt; } else if (tok->defaultSession && nssSession_IsReadWrite(tok->defaultSession)) { session = tok->defaultSession; } else { session = nssSlot_CreateSession(tok->slot, NULL, PR_TRUE); createdSession = PR_TRUE; } } else { session = (sessionOpt) ? sessionOpt : tok->defaultSession; } if (session == NULL) { nss_SetError(NSS_ERROR_INVALID_ARGUMENT); return NULL; } nssSession_EnterMonitor(session); ckrv = CKAPI(epv)->C_CreateObject(session->handle, objectTemplate, otsize, &handle); nssSession_ExitMonitor(session); if (ckrv == CKR_OK) { object = nssCryptokiObject_Create(tok, session, handle); } else { nss_SetError(ckrv); nss_SetError(NSS_ERROR_PKCS11); } if (createdSession) { nssSession_Destroy(session); } return object; } static nssCryptokiObject ** create_objects_from_handles( NSSToken *tok, nssSession *session, CK_OBJECT_HANDLE *handles, PRUint32 numH) { nssCryptokiObject **objects; objects = nss_ZNEWARRAY(NULL, nssCryptokiObject *, numH + 1); if (objects) { PRInt32 i; for (i = 0; i < (PRInt32)numH; i++) { objects[i] = nssCryptokiObject_Create(tok, session, handles[i]); if (!objects[i]) { for (--i; i > 0; --i) { nssCryptokiObject_Destroy(objects[i]); } nss_ZFreeIf(objects); objects = NULL; break; } } } return objects; } static nssCryptokiObject ** find_objects( NSSToken *tok, nssSession *sessionOpt, CK_ATTRIBUTE_PTR obj_template, CK_ULONG otsize, PRUint32 maximumOpt, PRStatus *statusOpt) { CK_RV ckrv = CKR_OK; CK_ULONG count; CK_OBJECT_HANDLE *objectHandles = NULL; CK_OBJECT_HANDLE staticObjects[OBJECT_STACK_SIZE]; PRUint32 arraySize, numHandles; void *epv = nssToken_GetCryptokiEPV(tok); nssCryptokiObject **objects; nssSession *session = (sessionOpt) ? sessionOpt : tok->defaultSession; /* Don't ask the module to use an invalid session handle. */ if (!session || session->handle == CK_INVALID_HANDLE) { ckrv = CKR_SESSION_HANDLE_INVALID; goto loser; } /* the arena is only for the array of object handles */ if (maximumOpt > 0) { arraySize = maximumOpt; } else { arraySize = OBJECT_STACK_SIZE; } numHandles = 0; if (arraySize <= OBJECT_STACK_SIZE) { objectHandles = staticObjects; } else { objectHandles = nss_ZNEWARRAY(NULL, CK_OBJECT_HANDLE, arraySize); } if (!objectHandles) { ckrv = CKR_HOST_MEMORY; goto loser; } nssSession_EnterMonitor(session); /* ==== session lock === */ /* Initialize the find with the template */ ckrv = CKAPI(epv)->C_FindObjectsInit(session->handle, obj_template, otsize); if (ckrv != CKR_OK) { nssSession_ExitMonitor(session); goto loser; } while (PR_TRUE) { /* Issue the find for up to arraySize - numHandles objects */ ckrv = CKAPI(epv)->C_FindObjects(session->handle, objectHandles + numHandles, arraySize - numHandles, &count); if (ckrv != CKR_OK) { nssSession_ExitMonitor(session); goto loser; } /* bump the number of found objects */ numHandles += count; if (maximumOpt > 0 || numHandles < arraySize) { /* When a maximum is provided, the search is done all at once, * so the search is finished. If the number returned was less * than the number sought, the search is finished. */ break; } /* the array is filled, double it and continue */ arraySize *= 2; if (objectHandles == staticObjects) { objectHandles = nss_ZNEWARRAY(NULL, CK_OBJECT_HANDLE, arraySize); if (objectHandles) { PORT_Memcpy(objectHandles, staticObjects, OBJECT_STACK_SIZE * sizeof(objectHandles[1])); } } else { objectHandles = nss_ZREALLOCARRAY(objectHandles, CK_OBJECT_HANDLE, arraySize); } if (!objectHandles) { nssSession_ExitMonitor(session); ckrv = CKR_HOST_MEMORY; goto loser; } } ckrv = CKAPI(epv)->C_FindObjectsFinal(session->handle); nssSession_ExitMonitor(session); /* ==== end session lock === */ if (ckrv != CKR_OK) { goto loser; } if (numHandles > 0) { objects = create_objects_from_handles(tok, session, objectHandles, numHandles); } else { nss_SetError(NSS_ERROR_NOT_FOUND); objects = NULL; } if (objectHandles && objectHandles != staticObjects) { nss_ZFreeIf(objectHandles); } if (statusOpt) *statusOpt = PR_SUCCESS; return objects; loser: if (objectHandles && objectHandles != staticObjects) { nss_ZFreeIf(objectHandles); } /* * These errors should be treated the same as if the objects just weren't * found.. */ if ((ckrv == CKR_ATTRIBUTE_TYPE_INVALID) || (ckrv == CKR_ATTRIBUTE_VALUE_INVALID) || (ckrv == CKR_DATA_INVALID) || (ckrv == CKR_DATA_LEN_RANGE) || (ckrv == CKR_FUNCTION_NOT_SUPPORTED) || (ckrv == CKR_TEMPLATE_INCOMPLETE) || (ckrv == CKR_TEMPLATE_INCONSISTENT)) { nss_SetError(NSS_ERROR_NOT_FOUND); if (statusOpt) *statusOpt = PR_SUCCESS; } else { nss_SetError(ckrv); nss_SetError(NSS_ERROR_PKCS11); if (statusOpt) *statusOpt = PR_FAILURE; } return (nssCryptokiObject **)NULL; } NSS_IMPLEMENT nssCryptokiObject ** nssToken_FindObjectsByTemplate( NSSToken *token, nssSession *sessionOpt, CK_ATTRIBUTE_PTR obj_template, CK_ULONG otsize, PRUint32 maximumOpt, PRStatus *statusOpt) { CK_OBJECT_CLASS objclass = (CK_OBJECT_CLASS)-1; nssCryptokiObject **objects = NULL; PRUint32 i; if (!token) { PORT_SetError(SEC_ERROR_NO_TOKEN); if (statusOpt) *statusOpt = PR_FAILURE; return NULL; } for (i = 0; i < otsize; i++) { if (obj_template[i].type == CKA_CLASS) { objclass = *(CK_OBJECT_CLASS *)obj_template[i].pValue; break; } } PR_ASSERT(i < otsize); if (i == otsize) { PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); if (statusOpt) *statusOpt = PR_FAILURE; return NULL; } /* If these objects are being cached, try looking there first */ if (token->cache && nssTokenObjectCache_HaveObjectClass(token->cache, objclass)) { PRStatus status; objects = nssTokenObjectCache_FindObjectsByTemplate(token->cache, objclass, obj_template, otsize, maximumOpt, &status); if (status == PR_SUCCESS) { if (statusOpt) *statusOpt = status; return objects; } } /* Either they are not cached, or cache failed; look on token. */ objects = find_objects(token, sessionOpt, obj_template, otsize, maximumOpt, statusOpt); return objects; } extern const NSSError NSS_ERROR_INVALID_CERTIFICATE; NSS_IMPLEMENT nssCryptokiObject * nssToken_ImportCertificate( NSSToken *tok, nssSession *sessionOpt, NSSCertificateType certType, NSSItem *id, const NSSUTF8 *nickname, NSSDER *encoding, NSSDER *issuer, NSSDER *subject, NSSDER *serial, NSSASCII7 *email, PRBool asTokenObject) { PRStatus status; CK_CERTIFICATE_TYPE cert_type; CK_ATTRIBUTE_PTR attr; CK_ATTRIBUTE cert_tmpl[10]; CK_ULONG ctsize; nssTokenSearchType searchType; nssCryptokiObject *rvObject = NULL; if (!tok) { PORT_SetError(SEC_ERROR_NO_TOKEN); return NULL; } if (certType == NSSCertificateType_PKIX) { cert_type = CKC_X_509; } else { return (nssCryptokiObject *)NULL; } NSS_CK_TEMPLATE_START(cert_tmpl, attr, ctsize); if (asTokenObject) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); searchType = nssTokenSearchType_TokenOnly; } else { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); searchType = nssTokenSearchType_SessionOnly; } NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert); NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_CERTIFICATE_TYPE, cert_type); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ID, id); NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_LABEL, nickname); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_VALUE, encoding); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ISSUER, issuer); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SUBJECT, subject); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SERIAL_NUMBER, serial); if (email) { NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_NSS_EMAIL, email); } NSS_CK_TEMPLATE_FINISH(cert_tmpl, attr, ctsize); /* see if the cert is already there */ rvObject = nssToken_FindCertificateByIssuerAndSerialNumber(tok, sessionOpt, issuer, serial, searchType, NULL); if (rvObject) { NSSItem existingDER; NSSSlot *slot = nssToken_GetSlot(tok); nssSession *session = nssSlot_CreateSession(slot, NULL, PR_TRUE); if (!session) { nssCryptokiObject_Destroy(rvObject); nssSlot_Destroy(slot); return (nssCryptokiObject *)NULL; } /* Reject any attempt to import a new cert that has the same * issuer/serial as an existing cert, but does not have the * same encoding */ NSS_CK_TEMPLATE_START(cert_tmpl, attr, ctsize); NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_VALUE); NSS_CK_TEMPLATE_FINISH(cert_tmpl, attr, ctsize); status = nssCKObject_GetAttributes(rvObject->handle, cert_tmpl, ctsize, NULL, session, slot); NSS_CK_ATTRIBUTE_TO_ITEM(cert_tmpl, &existingDER); if (status == PR_SUCCESS) { if (!nssItem_Equal(encoding, &existingDER, NULL)) { nss_SetError(NSS_ERROR_INVALID_CERTIFICATE); status = PR_FAILURE; } nss_ZFreeIf(existingDER.data); } if (status == PR_FAILURE) { nssCryptokiObject_Destroy(rvObject); nssSession_Destroy(session); nssSlot_Destroy(slot); return (nssCryptokiObject *)NULL; } /* according to PKCS#11, label, ID, issuer, and serial number * may change after the object has been created. For PKIX, the * last two attributes can't change, so for now we'll only worry * about the first two. */ NSS_CK_TEMPLATE_START(cert_tmpl, attr, ctsize); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ID, id); if (!rvObject->label && nickname) { NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_LABEL, nickname); } NSS_CK_TEMPLATE_FINISH(cert_tmpl, attr, ctsize); /* reset the mutable attributes on the token */ nssCKObject_SetAttributes(rvObject->handle, cert_tmpl, ctsize, session, slot); if (!rvObject->label && nickname) { rvObject->label = nssUTF8_Duplicate(nickname, NULL); } nssSession_Destroy(session); nssSlot_Destroy(slot); } else { /* Import the certificate onto the token */ rvObject = import_object(tok, sessionOpt, cert_tmpl, ctsize); } if (rvObject && tok->cache) { /* The cache will overwrite the attributes if the object already * exists. */ nssTokenObjectCache_ImportObject(tok->cache, rvObject, CKO_CERTIFICATE, cert_tmpl, ctsize); } return rvObject; } /* traverse all objects of the given class - this should only happen * if the token has been marked as "traversable" */ NSS_IMPLEMENT nssCryptokiObject ** nssToken_FindObjects( NSSToken *token, nssSession *sessionOpt, CK_OBJECT_CLASS objclass, nssTokenSearchType searchType, PRUint32 maximumOpt, PRStatus *statusOpt) { CK_ATTRIBUTE_PTR attr; CK_ATTRIBUTE obj_template[2]; CK_ULONG obj_size; nssCryptokiObject **objects; NSS_CK_TEMPLATE_START(obj_template, attr, obj_size); /* Set the search to token/session only if provided */ if (searchType == nssTokenSearchType_SessionOnly) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); } else if (searchType == nssTokenSearchType_TokenOnly || searchType == nssTokenSearchType_TokenForced) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); } NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_CLASS, objclass); NSS_CK_TEMPLATE_FINISH(obj_template, attr, obj_size); if (searchType == nssTokenSearchType_TokenForced) { objects = find_objects(token, sessionOpt, obj_template, obj_size, maximumOpt, statusOpt); } else { objects = nssToken_FindObjectsByTemplate(token, sessionOpt, obj_template, obj_size, maximumOpt, statusOpt); } return objects; } NSS_IMPLEMENT nssCryptokiObject ** nssToken_FindCertificatesBySubject( NSSToken *token, nssSession *sessionOpt, NSSDER *subject, nssTokenSearchType searchType, PRUint32 maximumOpt, PRStatus *statusOpt) { CK_ATTRIBUTE_PTR attr; CK_ATTRIBUTE subj_template[3]; CK_ULONG stsize; nssCryptokiObject **objects; NSS_CK_TEMPLATE_START(subj_template, attr, stsize); /* Set the search to token/session only if provided */ if (searchType == nssTokenSearchType_SessionOnly) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); } else if (searchType == nssTokenSearchType_TokenOnly) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); } NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SUBJECT, subject); NSS_CK_TEMPLATE_FINISH(subj_template, attr, stsize); /* now locate the token certs matching this template */ objects = nssToken_FindObjectsByTemplate(token, sessionOpt, subj_template, stsize, maximumOpt, statusOpt); return objects; } NSS_IMPLEMENT nssCryptokiObject ** nssToken_FindCertificatesByNickname( NSSToken *token, nssSession *sessionOpt, const NSSUTF8 *name, nssTokenSearchType searchType, PRUint32 maximumOpt, PRStatus *statusOpt) { CK_ATTRIBUTE_PTR attr; CK_ATTRIBUTE nick_template[3]; CK_ULONG ntsize; nssCryptokiObject **objects; NSS_CK_TEMPLATE_START(nick_template, attr, ntsize); NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_LABEL, name); /* Set the search to token/session only if provided */ if (searchType == nssTokenSearchType_SessionOnly) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); } else if (searchType == nssTokenSearchType_TokenOnly) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); } NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert); NSS_CK_TEMPLATE_FINISH(nick_template, attr, ntsize); /* now locate the token certs matching this template */ objects = nssToken_FindObjectsByTemplate(token, sessionOpt, nick_template, ntsize, maximumOpt, statusOpt); if (!objects) { /* This is to workaround the fact that PKCS#11 doesn't specify * whether the '\0' should be included. XXX Is that still true? * im - this is not needed by the current softoken. However, I'm * leaving it in until I have surveyed more tokens to see if it needed. * well, its needed by the builtin token... */ nick_template[0].ulValueLen++; objects = nssToken_FindObjectsByTemplate(token, sessionOpt, nick_template, ntsize, maximumOpt, statusOpt); } return objects; } /* XXX * This function *does not* use the token object cache, because not even * the softoken will return a value for CKA_NSS_EMAIL from a call * to GetAttributes. The softoken does allow searches with that attribute, * it just won't return a value for it. */ NSS_IMPLEMENT nssCryptokiObject ** nssToken_FindCertificatesByEmail( NSSToken *token, nssSession *sessionOpt, NSSASCII7 *email, nssTokenSearchType searchType, PRUint32 maximumOpt, PRStatus *statusOpt) { CK_ATTRIBUTE_PTR attr; CK_ATTRIBUTE email_template[3]; CK_ULONG etsize; nssCryptokiObject **objects; NSS_CK_TEMPLATE_START(email_template, attr, etsize); NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_NSS_EMAIL, email); /* Set the search to token/session only if provided */ if (searchType == nssTokenSearchType_SessionOnly) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); } else if (searchType == nssTokenSearchType_TokenOnly) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); } NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert); NSS_CK_TEMPLATE_FINISH(email_template, attr, etsize); /* now locate the token certs matching this template */ objects = find_objects(token, sessionOpt, email_template, etsize, maximumOpt, statusOpt); if (!objects) { /* This is to workaround the fact that PKCS#11 doesn't specify * whether the '\0' should be included. XXX Is that still true? * im - this is not needed by the current softoken. However, I'm * leaving it in until I have surveyed more tokens to see if it needed. * well, its needed by the builtin token... */ email_template[0].ulValueLen++; objects = find_objects(token, sessionOpt, email_template, etsize, maximumOpt, statusOpt); } return objects; } NSS_IMPLEMENT nssCryptokiObject ** nssToken_FindCertificatesByID( NSSToken *token, nssSession *sessionOpt, NSSItem *id, nssTokenSearchType searchType, PRUint32 maximumOpt, PRStatus *statusOpt) { CK_ATTRIBUTE_PTR attr; CK_ATTRIBUTE id_template[3]; CK_ULONG idtsize; nssCryptokiObject **objects; NSS_CK_TEMPLATE_START(id_template, attr, idtsize); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ID, id); /* Set the search to token/session only if provided */ if (searchType == nssTokenSearchType_SessionOnly) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); } else if (searchType == nssTokenSearchType_TokenOnly) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); } NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert); NSS_CK_TEMPLATE_FINISH(id_template, attr, idtsize); /* now locate the token certs matching this template */ objects = nssToken_FindObjectsByTemplate(token, sessionOpt, id_template, idtsize, maximumOpt, statusOpt); return objects; } /* * decode the serial item and return our result. * NOTE serialDecode's data is really stored in serial. Don't free it. */ static PRStatus nssToken_decodeSerialItem(NSSItem *serial, NSSItem *serialDecode) { unsigned char *data = (unsigned char *)serial->data; int data_left, data_len, index; if ((serial->size >= 3) && (data[0] == 0x2)) { /* remove the der encoding of the serial number before generating the * key.. */ data_left = serial->size - 2; data_len = data[1]; index = 2; /* extended length ? (not very likely for a serial number) */ if (data_len & 0x80) { int len_count = data_len & 0x7f; data_len = 0; data_left -= len_count; if (data_left > 0) { while (len_count--) { data_len = (data_len << 8) | data[index++]; } } } /* XXX leaving any leading zeros on the serial number for backwards * compatibility */ /* not a valid der, must be just an unlucky serial number value */ if (data_len == data_left) { serialDecode->size = data_len; serialDecode->data = &data[index]; return PR_SUCCESS; } } return PR_FAILURE; } NSS_IMPLEMENT nssCryptokiObject * nssToken_FindCertificateByIssuerAndSerialNumber( NSSToken *token, nssSession *sessionOpt, NSSDER *issuer, NSSDER *serial, nssTokenSearchType searchType, PRStatus *statusOpt) { CK_ATTRIBUTE_PTR attr; CK_ATTRIBUTE_PTR serialAttr; CK_ATTRIBUTE cert_template[4]; CK_ULONG ctsize; nssCryptokiObject **objects; nssCryptokiObject *rvObject = NULL; NSS_CK_TEMPLATE_START(cert_template, attr, ctsize); if (!token) { PORT_SetError(SEC_ERROR_NO_TOKEN); if (statusOpt) *statusOpt = PR_FAILURE; return NULL; } /* Set the search to token/session only if provided */ if (searchType == nssTokenSearchType_SessionOnly) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); } else if ((searchType == nssTokenSearchType_TokenOnly) || (searchType == nssTokenSearchType_TokenForced)) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); } /* Set the unique id */ NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ISSUER, issuer); serialAttr = attr; NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SERIAL_NUMBER, serial); NSS_CK_TEMPLATE_FINISH(cert_template, attr, ctsize); /* get the object handle */ if (searchType == nssTokenSearchType_TokenForced) { objects = find_objects(token, sessionOpt, cert_template, ctsize, 1, statusOpt); } else { objects = nssToken_FindObjectsByTemplate(token, sessionOpt, cert_template, ctsize, 1, statusOpt); } if (objects) { rvObject = objects[0]; nss_ZFreeIf(objects); } /* * NSS used to incorrectly store serial numbers in their decoded form. * because of this old tokens have decoded serial numbers. */ if (!objects) { NSSItem serialDecode; PRStatus status; status = nssToken_decodeSerialItem(serial, &serialDecode); if (status != PR_SUCCESS) { return NULL; } NSS_CK_SET_ATTRIBUTE_ITEM(serialAttr, CKA_SERIAL_NUMBER, &serialDecode); if (searchType == nssTokenSearchType_TokenForced) { objects = find_objects(token, sessionOpt, cert_template, ctsize, 1, statusOpt); } else { objects = nssToken_FindObjectsByTemplate(token, sessionOpt, cert_template, ctsize, 1, statusOpt); } if (objects) { rvObject = objects[0]; nss_ZFreeIf(objects); } } return rvObject; } NSS_IMPLEMENT nssCryptokiObject * nssToken_FindCertificateByEncodedCertificate( NSSToken *token, nssSession *sessionOpt, NSSBER *encodedCertificate, nssTokenSearchType searchType, PRStatus *statusOpt) { CK_ATTRIBUTE_PTR attr; CK_ATTRIBUTE cert_template[3]; CK_ULONG ctsize; nssCryptokiObject **objects; nssCryptokiObject *rvObject = NULL; NSS_CK_TEMPLATE_START(cert_template, attr, ctsize); /* Set the search to token/session only if provided */ if (searchType == nssTokenSearchType_SessionOnly) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); } else if (searchType == nssTokenSearchType_TokenOnly) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); } NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_VALUE, encodedCertificate); NSS_CK_TEMPLATE_FINISH(cert_template, attr, ctsize); /* get the object handle */ objects = nssToken_FindObjectsByTemplate(token, sessionOpt, cert_template, ctsize, 1, statusOpt); if (objects) { rvObject = objects[0]; nss_ZFreeIf(objects); } return rvObject; } NSS_IMPLEMENT nssCryptokiObject ** nssToken_FindPrivateKeys( NSSToken *token, nssSession *sessionOpt, nssTokenSearchType searchType, PRUint32 maximumOpt, PRStatus *statusOpt) { CK_ATTRIBUTE_PTR attr; CK_ATTRIBUTE key_template[2]; CK_ULONG ktsize; nssCryptokiObject **objects; NSS_CK_TEMPLATE_START(key_template, attr, ktsize); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_privkey); if (searchType == nssTokenSearchType_SessionOnly) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); } else if (searchType == nssTokenSearchType_TokenOnly) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); } NSS_CK_TEMPLATE_FINISH(key_template, attr, ktsize); objects = nssToken_FindObjectsByTemplate(token, sessionOpt, key_template, ktsize, maximumOpt, statusOpt); return objects; } /* XXX ?there are no session cert objects, so only search token objects */ NSS_IMPLEMENT nssCryptokiObject * nssToken_FindPrivateKeyByID( NSSToken *token, nssSession *sessionOpt, NSSItem *keyID) { CK_ATTRIBUTE_PTR attr; CK_ATTRIBUTE key_template[3]; CK_ULONG ktsize; nssCryptokiObject **objects; nssCryptokiObject *rvKey = NULL; NSS_CK_TEMPLATE_START(key_template, attr, ktsize); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_privkey); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ID, keyID); NSS_CK_TEMPLATE_FINISH(key_template, attr, ktsize); objects = nssToken_FindObjectsByTemplate(token, sessionOpt, key_template, ktsize, 1, NULL); if (objects) { rvKey = objects[0]; nss_ZFreeIf(objects); } return rvKey; } /* XXX ?there are no session cert objects, so only search token objects */ NSS_IMPLEMENT nssCryptokiObject * nssToken_FindPublicKeyByID( NSSToken *token, nssSession *sessionOpt, NSSItem *keyID) { CK_ATTRIBUTE_PTR attr; CK_ATTRIBUTE key_template[3]; CK_ULONG ktsize; nssCryptokiObject **objects; nssCryptokiObject *rvKey = NULL; NSS_CK_TEMPLATE_START(key_template, attr, ktsize); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_pubkey); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ID, keyID); NSS_CK_TEMPLATE_FINISH(key_template, attr, ktsize); objects = nssToken_FindObjectsByTemplate(token, sessionOpt, key_template, ktsize, 1, NULL); if (objects) { rvKey = objects[0]; nss_ZFreeIf(objects); } return rvKey; } static void sha1_hash(NSSItem *input, NSSItem *output) { NSSAlgorithmAndParameters *ap; PK11SlotInfo *internal = PK11_GetInternalSlot(); NSSToken *token = PK11Slot_GetNSSToken(internal); ap = NSSAlgorithmAndParameters_CreateSHA1Digest(NULL); (void)nssToken_Digest(token, NULL, ap, input, output, NULL); nss_ZFreeIf(ap); (void)nssToken_Destroy(token); PK11_FreeSlot(internal); } static void md5_hash(NSSItem *input, NSSItem *output) { NSSAlgorithmAndParameters *ap; PK11SlotInfo *internal = PK11_GetInternalSlot(); NSSToken *token = PK11Slot_GetNSSToken(internal); ap = NSSAlgorithmAndParameters_CreateMD5Digest(NULL); (void)nssToken_Digest(token, NULL, ap, input, output, NULL); nss_ZFreeIf(ap); (void)nssToken_Destroy(token); PK11_FreeSlot(internal); } static CK_TRUST get_ck_trust( nssTrustLevel nssTrust) { CK_TRUST t; switch (nssTrust) { case nssTrustLevel_NotTrusted: t = CKT_NSS_NOT_TRUSTED; break; case nssTrustLevel_TrustedDelegator: t = CKT_NSS_TRUSTED_DELEGATOR; break; case nssTrustLevel_ValidDelegator: t = CKT_NSS_VALID_DELEGATOR; break; case nssTrustLevel_Trusted: t = CKT_NSS_TRUSTED; break; case nssTrustLevel_MustVerify: t = CKT_NSS_MUST_VERIFY_TRUST; break; case nssTrustLevel_Unknown: default: t = CKT_NSS_TRUST_UNKNOWN; break; } return t; } NSS_IMPLEMENT nssCryptokiObject * nssToken_ImportTrust( NSSToken *tok, nssSession *sessionOpt, NSSDER *certEncoding, NSSDER *certIssuer, NSSDER *certSerial, nssTrustLevel serverAuth, nssTrustLevel clientAuth, nssTrustLevel codeSigning, nssTrustLevel emailProtection, PRBool stepUpApproved, PRBool asTokenObject) { nssCryptokiObject *object; CK_OBJECT_CLASS tobjc = CKO_NSS_TRUST; CK_TRUST ckSA, ckCA, ckCS, ckEP; CK_ATTRIBUTE_PTR attr; CK_ATTRIBUTE trust_tmpl[11]; CK_ULONG tsize; PRUint8 sha1[20]; /* this is cheating... */ PRUint8 md5[16]; NSSItem sha1_result, md5_result; sha1_result.data = sha1; sha1_result.size = sizeof sha1; md5_result.data = md5; md5_result.size = sizeof md5; sha1_hash(certEncoding, &sha1_result); md5_hash(certEncoding, &md5_result); ckSA = get_ck_trust(serverAuth); ckCA = get_ck_trust(clientAuth); ckCS = get_ck_trust(codeSigning); ckEP = get_ck_trust(emailProtection); NSS_CK_TEMPLATE_START(trust_tmpl, attr, tsize); if (asTokenObject) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); } else { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); } NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_CLASS, tobjc); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ISSUER, certIssuer); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SERIAL_NUMBER, certSerial); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CERT_SHA1_HASH, &sha1_result); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CERT_MD5_HASH, &md5_result); /* now set the trust values */ NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_SERVER_AUTH, ckSA); NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_CLIENT_AUTH, ckCA); NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_CODE_SIGNING, ckCS); NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_EMAIL_PROTECTION, ckEP); if (stepUpApproved) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TRUST_STEP_UP_APPROVED, &g_ck_true); } else { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TRUST_STEP_UP_APPROVED, &g_ck_false); } NSS_CK_TEMPLATE_FINISH(trust_tmpl, attr, tsize); /* import the trust object onto the token */ object = import_object(tok, sessionOpt, trust_tmpl, tsize); if (object && tok->cache) { nssTokenObjectCache_ImportObject(tok->cache, object, tobjc, trust_tmpl, tsize); } return object; } NSS_IMPLEMENT nssCryptokiObject * nssToken_FindTrustForCertificate( NSSToken *token, nssSession *sessionOpt, NSSDER *certEncoding, NSSDER *certIssuer, NSSDER *certSerial, nssTokenSearchType searchType) { CK_OBJECT_CLASS tobjc = CKO_NSS_TRUST; CK_ATTRIBUTE_PTR attr; CK_ATTRIBUTE tobj_template[5]; CK_ULONG tobj_size; nssSession *session = sessionOpt ? sessionOpt : token->defaultSession; nssCryptokiObject *object = NULL, **objects; /* Don't ask the module to use an invalid session handle. */ if (!session || session->handle == CK_INVALID_HANDLE) { PORT_SetError(SEC_ERROR_NO_TOKEN); return object; } NSS_CK_TEMPLATE_START(tobj_template, attr, tobj_size); if (searchType == nssTokenSearchType_TokenOnly) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); } NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_CLASS, tobjc); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ISSUER, certIssuer); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SERIAL_NUMBER, certSerial); NSS_CK_TEMPLATE_FINISH(tobj_template, attr, tobj_size); objects = nssToken_FindObjectsByTemplate(token, session, tobj_template, tobj_size, 1, NULL); if (objects) { object = objects[0]; nss_ZFreeIf(objects); } return object; } NSS_IMPLEMENT nssCryptokiObject * nssToken_ImportCRL( NSSToken *token, nssSession *sessionOpt, NSSDER *subject, NSSDER *encoding, PRBool isKRL, NSSUTF8 *url, PRBool asTokenObject) { nssCryptokiObject *object; CK_OBJECT_CLASS crlobjc = CKO_NSS_CRL; CK_ATTRIBUTE_PTR attr; CK_ATTRIBUTE crl_tmpl[6]; CK_ULONG crlsize; NSS_CK_TEMPLATE_START(crl_tmpl, attr, crlsize); if (asTokenObject) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); } else { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); } NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_CLASS, crlobjc); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SUBJECT, subject); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_VALUE, encoding); NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_NSS_URL, url); if (isKRL) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_NSS_KRL, &g_ck_true); } else { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_NSS_KRL, &g_ck_false); } NSS_CK_TEMPLATE_FINISH(crl_tmpl, attr, crlsize); /* import the crl object onto the token */ object = import_object(token, sessionOpt, crl_tmpl, crlsize); if (object && token->cache) { nssTokenObjectCache_ImportObject(token->cache, object, crlobjc, crl_tmpl, crlsize); } return object; } NSS_IMPLEMENT nssCryptokiObject ** nssToken_FindCRLsBySubject( NSSToken *token, nssSession *sessionOpt, NSSDER *subject, nssTokenSearchType searchType, PRUint32 maximumOpt, PRStatus *statusOpt) { CK_OBJECT_CLASS crlobjc = CKO_NSS_CRL; CK_ATTRIBUTE_PTR attr; CK_ATTRIBUTE crlobj_template[3]; CK_ULONG crlobj_size; nssCryptokiObject **objects = NULL; nssSession *session = sessionOpt ? sessionOpt : token->defaultSession; /* Don't ask the module to use an invalid session handle. */ if (!session || session->handle == CK_INVALID_HANDLE) { PORT_SetError(SEC_ERROR_NO_TOKEN); return objects; } NSS_CK_TEMPLATE_START(crlobj_template, attr, crlobj_size); if (searchType == nssTokenSearchType_SessionOnly) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); } else if (searchType == nssTokenSearchType_TokenOnly || searchType == nssTokenSearchType_TokenForced) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); } NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_CLASS, crlobjc); NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SUBJECT, subject); NSS_CK_TEMPLATE_FINISH(crlobj_template, attr, crlobj_size); objects = nssToken_FindObjectsByTemplate(token, session, crlobj_template, crlobj_size, maximumOpt, statusOpt); return objects; } NSS_IMPLEMENT PRStatus nssToken_GetCachedObjectAttributes( NSSToken *token, NSSArena *arenaOpt, nssCryptokiObject *object, CK_OBJECT_CLASS objclass, CK_ATTRIBUTE_PTR atemplate, CK_ULONG atlen) { if (!token->cache) { return PR_FAILURE; } return nssTokenObjectCache_GetObjectAttributes(token->cache, arenaOpt, object, objclass, atemplate, atlen); } NSS_IMPLEMENT NSSItem * nssToken_Digest( NSSToken *tok, nssSession *sessionOpt, NSSAlgorithmAndParameters *ap, NSSItem *data, NSSItem *rvOpt, NSSArena *arenaOpt) { CK_RV ckrv; CK_ULONG digestLen; CK_BYTE_PTR digest; NSSItem *rvItem = NULL; void *epv = nssToken_GetCryptokiEPV(tok); nssSession *session = (sessionOpt) ? sessionOpt : tok->defaultSession; /* Don't ask the module to use an invalid session handle. */ if (!session || session->handle == CK_INVALID_HANDLE) { PORT_SetError(SEC_ERROR_NO_TOKEN); return rvItem; } nssSession_EnterMonitor(session); ckrv = CKAPI(epv)->C_DigestInit(session->handle, &ap->mechanism); if (ckrv != CKR_OK) { nssSession_ExitMonitor(session); return NULL; } #if 0 /* XXX the standard says this should work, but it doesn't */ ckrv = CKAPI(epv)->C_Digest(session->handle, NULL, 0, NULL, &digestLen); if (ckrv != CKR_OK) { nssSession_ExitMonitor(session); return NULL; } #endif digestLen = 0; /* XXX for now */ digest = NULL; if (rvOpt) { if (rvOpt->size > 0 && rvOpt->size < digestLen) { nssSession_ExitMonitor(session); /* the error should be bad args */ return NULL; } if (rvOpt->data) { digest = rvOpt->data; } digestLen = rvOpt->size; } if (!digest) { digest = (CK_BYTE_PTR)nss_ZAlloc(arenaOpt, digestLen); if (!digest) { nssSession_ExitMonitor(session); return NULL; } } ckrv = CKAPI(epv)->C_Digest(session->handle, (CK_BYTE_PTR)data->data, (CK_ULONG)data->size, (CK_BYTE_PTR)digest, &digestLen); nssSession_ExitMonitor(session); if (ckrv != CKR_OK) { nss_ZFreeIf(digest); return NULL; } if (!rvOpt) { rvItem = nssItem_Create(arenaOpt, NULL, digestLen, (void *)digest); } return rvItem; } NSS_IMPLEMENT PRStatus nssToken_BeginDigest( NSSToken *tok, nssSession *sessionOpt, NSSAlgorithmAndParameters *ap) { CK_RV ckrv; void *epv = nssToken_GetCryptokiEPV(tok); nssSession *session = (sessionOpt) ? sessionOpt : tok->defaultSession; /* Don't ask the module to use an invalid session handle. */ if (!session || session->handle == CK_INVALID_HANDLE) { PORT_SetError(SEC_ERROR_NO_TOKEN); return PR_FAILURE; } nssSession_EnterMonitor(session); ckrv = CKAPI(epv)->C_DigestInit(session->handle, &ap->mechanism); nssSession_ExitMonitor(session); return (ckrv == CKR_OK) ? PR_SUCCESS : PR_FAILURE; } NSS_IMPLEMENT PRStatus nssToken_ContinueDigest( NSSToken *tok, nssSession *sessionOpt, NSSItem *item) { CK_RV ckrv; void *epv = nssToken_GetCryptokiEPV(tok); nssSession *session = (sessionOpt) ? sessionOpt : tok->defaultSession; /* Don't ask the module to use an invalid session handle. */ if (!session || session->handle == CK_INVALID_HANDLE) { PORT_SetError(SEC_ERROR_NO_TOKEN); return PR_FAILURE; } nssSession_EnterMonitor(session); ckrv = CKAPI(epv)->C_DigestUpdate(session->handle, (CK_BYTE_PTR)item->data, (CK_ULONG)item->size); nssSession_ExitMonitor(session); return (ckrv == CKR_OK) ? PR_SUCCESS : PR_FAILURE; } NSS_IMPLEMENT NSSItem * nssToken_FinishDigest( NSSToken *tok, nssSession *sessionOpt, NSSItem *rvOpt, NSSArena *arenaOpt) { CK_RV ckrv; CK_ULONG digestLen; CK_BYTE_PTR digest; NSSItem *rvItem = NULL; void *epv = nssToken_GetCryptokiEPV(tok); nssSession *session = (sessionOpt) ? sessionOpt : tok->defaultSession; /* Don't ask the module to use an invalid session handle. */ if (!session || session->handle == CK_INVALID_HANDLE) { PORT_SetError(SEC_ERROR_NO_TOKEN); return NULL; } nssSession_EnterMonitor(session); ckrv = CKAPI(epv)->C_DigestFinal(session->handle, NULL, &digestLen); if (ckrv != CKR_OK || digestLen == 0) { nssSession_ExitMonitor(session); return NULL; } digest = NULL; if (rvOpt) { if (rvOpt->size > 0 && rvOpt->size < digestLen) { nssSession_ExitMonitor(session); /* the error should be bad args */ return NULL; } if (rvOpt->data) { digest = rvOpt->data; } digestLen = rvOpt->size; } if (!digest) { digest = (CK_BYTE_PTR)nss_ZAlloc(arenaOpt, digestLen); if (!digest) { nssSession_ExitMonitor(session); return NULL; } } ckrv = CKAPI(epv)->C_DigestFinal(session->handle, digest, &digestLen); nssSession_ExitMonitor(session); if (ckrv != CKR_OK) { nss_ZFreeIf(digest); return NULL; } if (!rvOpt) { rvItem = nssItem_Create(arenaOpt, NULL, digestLen, (void *)digest); } return rvItem; } NSS_IMPLEMENT PRBool nssToken_IsPresent( NSSToken *token) { return nssSlot_IsTokenPresent(token->slot); } /* Sigh. The methods to find objects declared above cause problems with * the low-level object cache in the softoken -- the objects are found in * toto, then one wave of GetAttributes is done, then another. Having a * large number of objects causes the cache to be thrashed, as the objects * are gone before there's any chance to ask for their attributes. * So, for now, bringing back traversal methods for certs. This way all of * the cert's attributes can be grabbed immediately after finding it, * increasing the likelihood that the cache takes care of it. */ NSS_IMPLEMENT PRStatus nssToken_TraverseCertificates( NSSToken *token, nssSession *sessionOpt, nssTokenSearchType searchType, PRStatus (*callback)(nssCryptokiObject *instance, void *arg), void *arg) { CK_RV ckrv; CK_ULONG count; CK_OBJECT_HANDLE *objectHandles; CK_ATTRIBUTE_PTR attr; CK_ATTRIBUTE cert_template[2]; CK_ULONG ctsize; NSSArena *arena; PRUint32 arraySize, numHandles; nssCryptokiObject **objects; void *epv = nssToken_GetCryptokiEPV(token); nssSession *session = (sessionOpt) ? sessionOpt : token->defaultSession; /* Don't ask the module to use an invalid session handle. */ if (!session || session->handle == CK_INVALID_HANDLE) { PORT_SetError(SEC_ERROR_NO_TOKEN); return PR_FAILURE; } /* template for all certs */ NSS_CK_TEMPLATE_START(cert_template, attr, ctsize); if (searchType == nssTokenSearchType_SessionOnly) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); } else if (searchType == nssTokenSearchType_TokenOnly || searchType == nssTokenSearchType_TokenForced) { NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); } NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert); NSS_CK_TEMPLATE_FINISH(cert_template, attr, ctsize); /* the arena is only for the array of object handles */ arena = nssArena_Create(); if (!arena) { return PR_FAILURE; } arraySize = OBJECT_STACK_SIZE; numHandles = 0; objectHandles = nss_ZNEWARRAY(arena, CK_OBJECT_HANDLE, arraySize); if (!objectHandles) { goto loser; } nssSession_EnterMonitor(session); /* ==== session lock === */ /* Initialize the find with the template */ ckrv = CKAPI(epv)->C_FindObjectsInit(session->handle, cert_template, ctsize); if (ckrv != CKR_OK) { nssSession_ExitMonitor(session); goto loser; } while (PR_TRUE) { /* Issue the find for up to arraySize - numHandles objects */ ckrv = CKAPI(epv)->C_FindObjects(session->handle, objectHandles + numHandles, arraySize - numHandles, &count); if (ckrv != CKR_OK) { nssSession_ExitMonitor(session); goto loser; } /* bump the number of found objects */ numHandles += count; if (numHandles < arraySize) { break; } /* the array is filled, double it and continue */ arraySize *= 2; objectHandles = nss_ZREALLOCARRAY(objectHandles, CK_OBJECT_HANDLE, arraySize); if (!objectHandles) { nssSession_ExitMonitor(session); goto loser; } } ckrv = CKAPI(epv)->C_FindObjectsFinal(session->handle); nssSession_ExitMonitor(session); /* ==== end session lock === */ if (ckrv != CKR_OK) { goto loser; } if (numHandles > 0) { objects = create_objects_from_handles(token, session, objectHandles, numHandles); if (objects) { nssCryptokiObject **op; for (op = objects; *op; op++) { (void)(*callback)(*op, arg); } nss_ZFreeIf(objects); } } nssArena_Destroy(arena); return PR_SUCCESS; loser: nssArena_Destroy(arena); return PR_FAILURE; } NSS_IMPLEMENT PRBool nssToken_IsPrivateKeyAvailable( NSSToken *token, NSSCertificate *c, nssCryptokiObject *instance) { CK_OBJECT_CLASS theClass; if (token == NULL) return PR_FALSE; if (c == NULL) return PR_FALSE; theClass = CKO_PRIVATE_KEY; if (!nssSlot_IsLoggedIn(token->slot)) { theClass = CKO_PUBLIC_KEY; } if (PK11_MatchItem(token->pk11slot, instance->handle, theClass) != CK_INVALID_HANDLE) { return PR_TRUE; } return PR_FALSE; }