pjs/security/nss/lib/dev/devtoken.c

1712 строки
52 KiB
C

/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Netscape security libraries.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
#ifdef DEBUG
static const char CVS_ID[] = "@(#) $RCSfile: devtoken.c,v $ $Revision: 1.36 $ $Date: 2003-09-23 20:34:15 $ $Name: $";
#endif /* DEBUG */
#ifndef NSSCKEPV_H
#include "nssckepv.h"
#endif /* NSSCKEPV_H */
#ifndef DEVM_H
#include "devm.h"
#endif /* DEVM_H */
#ifndef CKHELPER_H
#include "ckhelper.h"
#endif /* CKHELPER_H */
#ifdef NSS_3_4_CODE
#include "pk11func.h"
#include "dev3hack.h"
#include "secerr.h"
#endif
extern const NSSError NSS_ERROR_NOT_FOUND;
/* The number of object handles to grab during each call to C_FindObjects */
#define OBJECT_STACK_SIZE 16
#ifdef PURE_STAN_BUILD
struct NSSTokenStr
{
struct nssDeviceBaseStr base;
NSSSlot *slot; /* Peer */
CK_FLAGS ckFlags; /* from CK_TOKEN_INFO.flags */
nssSession *defaultSession;
nssTokenObjectCache *cache;
};
NSS_IMPLEMENT NSSToken *
nssToken_Create (
CK_SLOT_ID slotID,
NSSSlot *peer
)
{
NSSArena *arena;
NSSToken *rvToken;
nssSession *session = NULL;
NSSUTF8 *tokenName = NULL;
PRUint32 length;
PRBool readWrite;
CK_TOKEN_INFO tokenInfo;
CK_RV ckrv;
void *epv = nssSlot_GetCryptokiEPV(peer);
arena = NSSArena_Create();
if(!arena) {
return (NSSToken *)NULL;
}
rvToken = nss_ZNEW(arena, NSSToken);
if (!rvToken) {
goto loser;
}
/* Get token information */
ckrv = CKAPI(epv)->C_GetTokenInfo(slotID, &tokenInfo);
if (ckrv != CKR_OK) {
/* set an error here, eh? */
goto loser;
}
/* Grab the slot description from the PKCS#11 fixed-length buffer */
length = nssPKCS11String_Length(tokenInfo.label, sizeof(tokenInfo.label));
if (length > 0) {
tokenName = nssUTF8_Create(arena, nssStringType_UTF8String,
(void *)tokenInfo.label, length);
if (!tokenName) {
goto loser;
}
}
/* Open a default session handle for the token. */
if (tokenInfo.ulMaxSessionCount == 1) {
/* if the token can only handle one session, it must be RW. */
readWrite = PR_TRUE;
} else {
readWrite = PR_FALSE;
}
session = nssSlot_CreateSession(peer, arena, readWrite);
if (session == NULL) {
goto loser;
}
/* TODO: seed the RNG here */
rvToken->base.arena = arena;
rvToken->base.refCount = 1;
rvToken->base.name = tokenName;
rvToken->base.lock = PZ_NewLock(nssNSSILockOther); /* XXX */
if (!rvToken->base.lock) {
goto loser;
}
rvToken->slot = peer; /* slot owns ref to token */
rvToken->ckFlags = tokenInfo.flags;
rvToken->defaultSession = session;
if (nssSlot_IsHardware(peer)) {
rvToken->cache = nssTokenObjectCache_Create(rvToken,
PR_TRUE, PR_TRUE, PR_TRUE);
if (!rvToken->cache) {
nssSlot_Destroy(peer);
goto loser;
}
}
return rvToken;
loser:
if (session) {
nssSession_Destroy(session);
}
nssArena_Destroy(arena);
return (NSSToken *)NULL;
}
#endif /* PURE_STAN_BUILD */
NSS_IMPLEMENT PRStatus
nssToken_Destroy (
NSSToken *tok
)
{
if (tok) {
if (PR_AtomicDecrement(&tok->base.refCount) == 0) {
PZ_DestroyLock(tok->base.lock);
nssTokenObjectCache_Destroy(tok->cache);
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_AtomicIncrement(&tok->base.refCount);
return tok;
}
NSS_IMPLEMENT NSSSlot *
nssToken_GetSlot (
NSSToken *tok
)
{
return nssSlot_AddRef(tok->slot);
}
#ifdef PURE_STAN_BUILD
NSS_IMPLEMENT NSSModule *
nssToken_GetModule (
NSSToken *token
)
{
return nssSlot_GetModule(token->slot);
}
#endif
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 (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 = (ckrv == CKR_OK) ? PR_SUCCESS : PR_FAILURE;
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)) {
return CK_INVALID_HANDLE;
} else {
session = sessionOpt;
}
} else if (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) {
return CK_INVALID_HANDLE;
}
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);
}
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]);
}
return (nssCryptokiObject **)NULL;
}
}
}
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;
CK_OBJECT_HANDLE staticObjects[OBJECT_STACK_SIZE];
PRUint32 arraySize, numHandles;
void *epv = nssToken_GetCryptokiEPV(tok);
nssCryptokiObject **objects;
nssSession *session = (sessionOpt) ? sessionOpt : tok->defaultSession;
/* 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 {
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;
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) {
#ifdef NSS_3_4_CODE
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
#endif
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,
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 (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_NETSCAPE_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 certificates - this should only happen if the token
* has been marked as "traversable"
*/
NSS_IMPLEMENT nssCryptokiObject **
nssToken_FindCertificates (
NSSToken *token,
nssSession *sessionOpt,
nssTokenSearchType searchType,
PRUint32 maximumOpt,
PRStatus *statusOpt
)
{
CK_ATTRIBUTE_PTR attr;
CK_ATTRIBUTE cert_template[2];
CK_ULONG ctsize;
nssCryptokiObject **objects;
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 ||
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);
if (searchType == nssTokenSearchType_TokenForced) {
objects = find_objects(token, sessionOpt,
cert_template, ctsize,
maximumOpt, statusOpt);
} else {
objects = find_objects_by_template(token, sessionOpt,
cert_template, ctsize,
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,
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_NETSCAPE_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_NETSCAPE_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);
/* 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;
#ifdef NSS_3_4_CODE
PK11SlotInfo *internal = PK11_GetInternalSlot();
NSSToken *token = PK11Slot_GetNSSToken(internal);
#else
NSSToken *token = nss_GetDefaultCryptoToken();
#endif
ap = NSSAlgorithmAndParameters_CreateSHA1Digest(NULL);
(void)nssToken_Digest(token, NULL, ap, input, output, NULL);
#ifdef NSS_3_4_CODE
PK11_FreeSlot(token->pk11slot);
#endif
nss_ZFreeIf(ap);
}
static void
md5_hash(NSSItem *input, NSSItem *output)
{
NSSAlgorithmAndParameters *ap;
#ifdef NSS_3_4_CODE
PK11SlotInfo *internal = PK11_GetInternalSlot();
NSSToken *token = PK11Slot_GetNSSToken(internal);
#else
NSSToken *token = nss_GetDefaultCryptoToken();
#endif
ap = NSSAlgorithmAndParameters_CreateMD5Digest(NULL);
(void)nssToken_Digest(token, NULL, ap, input, output, NULL);
#ifdef NSS_3_4_CODE
PK11_FreeSlot(token->pk11slot);
#endif
nss_ZFreeIf(ap);
}
static CK_TRUST
get_ck_trust (
nssTrustLevel nssTrust
)
{
CK_TRUST t;
switch (nssTrust) {
case nssTrustLevel_NotTrusted: t = CKT_NETSCAPE_UNTRUSTED; break;
case nssTrustLevel_TrustedDelegator: t = CKT_NETSCAPE_TRUSTED_DELEGATOR;
break;
case nssTrustLevel_ValidDelegator: t = CKT_NETSCAPE_VALID_DELEGATOR; break;
case nssTrustLevel_Trusted: t = CKT_NETSCAPE_TRUSTED; break;
case nssTrustLevel_Valid: t = CKT_NETSCAPE_VALID; break;
case nssTrustLevel_Unknown:
default: t = CKT_NETSCAPE_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 asTokenObject
)
{
nssCryptokiObject *object;
CK_OBJECT_CLASS tobjc = CKO_NETSCAPE_TRUST;
CK_TRUST ckSA, ckCA, ckCS, ckEP;
CK_ATTRIBUTE_PTR attr;
CK_ATTRIBUTE trust_tmpl[10];
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);
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_FindTrustObjects (
NSSToken *token,
nssSession *sessionOpt,
nssTokenSearchType searchType,
PRUint32 maximumOpt,
PRStatus *statusOpt
)
{
CK_OBJECT_CLASS tobjc = CKO_NETSCAPE_TRUST;
CK_ATTRIBUTE_PTR attr;
CK_ATTRIBUTE tobj_template[2];
CK_ULONG tobj_size;
nssCryptokiObject **objects;
nssSession *session = sessionOpt ? sessionOpt : token->defaultSession;
NSS_CK_TEMPLATE_START(tobj_template, attr, tobj_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, tobjc);
NSS_CK_TEMPLATE_FINISH(tobj_template, attr, tobj_size);
if (searchType == nssTokenSearchType_TokenForced) {
objects = find_objects(token, session,
tobj_template, tobj_size,
maximumOpt, statusOpt);
} else {
objects = find_objects_by_template(token, session,
tobj_template, tobj_size,
maximumOpt, statusOpt);
}
return objects;
}
NSS_IMPLEMENT nssCryptokiObject *
nssToken_FindTrustForCertificate (
NSSToken *token,
nssSession *sessionOpt,
NSSDER *certEncoding,
NSSDER *certIssuer,
NSSDER *certSerial,
nssTokenSearchType searchType
)
{
CK_OBJECT_CLASS tobjc = CKO_NETSCAPE_TRUST;
CK_ATTRIBUTE_PTR attr;
CK_ATTRIBUTE tobj_template[5];
CK_ULONG tobj_size;
nssSession *session = sessionOpt ? sessionOpt : token->defaultSession;
nssCryptokiObject *object, **objects;
NSS_CK_TEMPLATE_START(tobj_template, attr, tobj_size);
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_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);
object = NULL;
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_NETSCAPE_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_NETSCAPE_URL, url);
if (isKRL) {
NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_NETSCAPE_KRL, &g_ck_true);
} else {
NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_NETSCAPE_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_FindCRLs (
NSSToken *token,
nssSession *sessionOpt,
nssTokenSearchType searchType,
PRUint32 maximumOpt,
PRStatus *statusOpt
)
{
CK_OBJECT_CLASS crlobjc = CKO_NETSCAPE_CRL;
CK_ATTRIBUTE_PTR attr;
CK_ATTRIBUTE crlobj_template[2];
CK_ULONG crlobj_size;
nssCryptokiObject **objects;
nssSession *session = sessionOpt ? sessionOpt : token->defaultSession;
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_TEMPLATE_FINISH(crlobj_template, attr, crlobj_size);
if (searchType == nssTokenSearchType_TokenForced) {
objects = find_objects(token, session,
crlobj_template, crlobj_size,
maximumOpt, statusOpt);
} else {
objects = find_objects_by_template(token, session,
crlobj_template, crlobj_size,
maximumOpt, statusOpt);
}
return objects;
}
NSS_IMPLEMENT nssCryptokiObject **
nssToken_FindCRLsBySubject (
NSSToken *token,
nssSession *sessionOpt,
NSSDER *subject,
nssTokenSearchType searchType,
PRUint32 maximumOpt,
PRStatus *statusOpt
)
{
CK_OBJECT_CLASS crlobjc = CKO_NETSCAPE_CRL;
CK_ATTRIBUTE_PTR attr;
CK_ATTRIBUTE crlobj_template[3];
CK_ULONG crlobj_size;
nssCryptokiObject **objects;
nssSession *session = sessionOpt ? sessionOpt : token->defaultSession;
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;
session = (sessionOpt) ? sessionOpt : tok->defaultSession;
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;
nssSession *session;
void *epv = nssToken_GetCryptokiEPV(tok);
session = (sessionOpt) ? sessionOpt : tok->defaultSession;
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;
nssSession *session;
void *epv = nssToken_GetCryptokiEPV(tok);
session = (sessionOpt) ? sessionOpt : tok->defaultSession;
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;
session = (sessionOpt) ? sessionOpt : tok->defaultSession;
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;
PRStatus status;
PRUint32 arraySize, numHandles;
nssCryptokiObject **objects;
void *epv = nssToken_GetCryptokiEPV(token);
nssSession *session = (sessionOpt) ? sessionOpt : token->defaultSession;
/* 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++) {
status = (*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;
}