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