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