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