1012 lines
32 KiB
C
1012 lines
32 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: devobject.c,v $ $Revision: 1.18 $ $Date: 2002-02-15 17:37:58 $ $Name: not supported by cvs2svn $";
|
|
#endif /* DEBUG */
|
|
|
|
#ifndef DEV_H
|
|
#include "dev.h"
|
|
#endif /* DEV_H */
|
|
|
|
#ifndef DEVM_H
|
|
#include "devm.h"
|
|
#endif /* DEVM_H */
|
|
|
|
#ifndef NSSCKEPV_H
|
|
#include "nssckepv.h"
|
|
#endif /* NSSCKEPV_H */
|
|
|
|
#ifndef CKHELPER_H
|
|
#include "ckhelper.h"
|
|
#endif /* CKHELPER_H */
|
|
|
|
#ifndef BASE_H
|
|
#include "base.h"
|
|
#endif /* BASE_H */
|
|
|
|
/* XXX */
|
|
#ifndef PKI_H
|
|
#include "pki.h"
|
|
#endif /* PKI_H */
|
|
|
|
/* XXX */
|
|
#ifndef NSSPKI_H
|
|
#include "nsspki.h"
|
|
#endif /* NSSPKI_H */
|
|
|
|
#ifdef NSS_3_4_CODE
|
|
#include "pkim.h" /* for cert decoding */
|
|
#include "pk11func.h" /* for PK11_HasRootCerts */
|
|
#endif
|
|
|
|
/* The number of object handles to grab during each call to C_FindObjects */
|
|
#define OBJECT_STACK_SIZE 16
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
nssToken_DeleteStoredObject
|
|
(
|
|
nssCryptokiInstance *instance
|
|
)
|
|
{
|
|
CK_RV ckrv;
|
|
PRStatus nssrv;
|
|
PRBool createdSession = PR_FALSE;
|
|
NSSToken *token = instance->token;
|
|
nssSession *session = NULL;
|
|
if (nssCKObject_IsAttributeTrue(instance->handle, CKA_TOKEN,
|
|
token->defaultSession,
|
|
token->slot, &nssrv)) {
|
|
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(token)->C_DestroyObject(session->handle, instance->handle);
|
|
nssSession_ExitMonitor(session);
|
|
if (createdSession) {
|
|
nssSession_Destroy(session);
|
|
}
|
|
if (ckrv != CKR_OK) {
|
|
return PR_FAILURE;
|
|
}
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
static CK_OBJECT_HANDLE
|
|
import_object
|
|
(
|
|
NSSToken *tok,
|
|
nssSession *sessionOpt,
|
|
CK_ATTRIBUTE_PTR objectTemplate,
|
|
CK_ULONG otsize
|
|
)
|
|
{
|
|
nssSession *session = NULL;
|
|
PRBool createdSession = PR_FALSE;
|
|
CK_OBJECT_HANDLE object;
|
|
CK_RV ckrv;
|
|
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(tok->slot)->C_CreateObject(session->handle,
|
|
objectTemplate, otsize,
|
|
&object);
|
|
nssSession_ExitMonitor(session);
|
|
if (createdSession) {
|
|
nssSession_Destroy(session);
|
|
}
|
|
if (ckrv != CKR_OK) {
|
|
return CK_INVALID_HANDLE;
|
|
}
|
|
return object;
|
|
}
|
|
|
|
static CK_OBJECT_HANDLE
|
|
find_object_by_template
|
|
(
|
|
NSSToken *tok,
|
|
nssSession *sessionOpt,
|
|
CK_ATTRIBUTE_PTR cktemplate,
|
|
CK_ULONG ctsize
|
|
)
|
|
{
|
|
CK_SESSION_HANDLE hSession;
|
|
CK_OBJECT_HANDLE rvObject = CK_INVALID_HANDLE;
|
|
CK_ULONG count = 0;
|
|
CK_RV ckrv;
|
|
nssSession *session;
|
|
session = (sessionOpt) ? sessionOpt : tok->defaultSession;
|
|
hSession = session->handle;
|
|
nssSession_EnterMonitor(session);
|
|
ckrv = CKAPI(tok)->C_FindObjectsInit(hSession, cktemplate, ctsize);
|
|
if (ckrv != CKR_OK) {
|
|
nssSession_ExitMonitor(session);
|
|
return CK_INVALID_HANDLE;
|
|
}
|
|
ckrv = CKAPI(tok)->C_FindObjects(hSession, &rvObject, 1, &count);
|
|
if (ckrv != CKR_OK) {
|
|
nssSession_ExitMonitor(session);
|
|
return CK_INVALID_HANDLE;
|
|
}
|
|
ckrv = CKAPI(tok)->C_FindObjectsFinal(hSession);
|
|
nssSession_ExitMonitor(session);
|
|
if (ckrv != CKR_OK) {
|
|
return CK_INVALID_HANDLE;
|
|
}
|
|
return rvObject;
|
|
}
|
|
|
|
static PRStatus
|
|
traverse_objects_by_template
|
|
(
|
|
NSSToken *tok,
|
|
nssSession *sessionOpt,
|
|
CK_ATTRIBUTE_PTR obj_template,
|
|
CK_ULONG otsize,
|
|
PRStatus (*callback)(NSSToken *t, nssSession *session,
|
|
CK_OBJECT_HANDLE h, void *arg),
|
|
void *arg
|
|
)
|
|
{
|
|
NSSSlot *slot;
|
|
PRStatus cbrv;
|
|
PRUint32 i;
|
|
CK_RV ckrv;
|
|
CK_ULONG count;
|
|
CK_OBJECT_HANDLE *objectStack;
|
|
CK_OBJECT_HANDLE startOS[OBJECT_STACK_SIZE];
|
|
CK_SESSION_HANDLE hSession;
|
|
NSSArena *objectArena = NULL;
|
|
nssSession *session;
|
|
nssList *objectList = NULL;
|
|
int objectStackSize = OBJECT_STACK_SIZE;
|
|
slot = tok->slot;
|
|
objectStack = startOS;
|
|
session = (sessionOpt) ? sessionOpt : tok->defaultSession;
|
|
hSession = session->handle;
|
|
nssSession_EnterMonitor(session);
|
|
ckrv = CKAPI(slot)->C_FindObjectsInit(hSession, obj_template, otsize);
|
|
if (ckrv != CKR_OK) {
|
|
nssSession_ExitMonitor(session);
|
|
goto loser;
|
|
}
|
|
while (PR_TRUE) {
|
|
ckrv = CKAPI(slot)->C_FindObjects(hSession, objectStack,
|
|
objectStackSize, &count);
|
|
if (ckrv != CKR_OK) {
|
|
nssSession_ExitMonitor(session);
|
|
goto loser;
|
|
}
|
|
if (count == objectStackSize) {
|
|
if (!objectList) {
|
|
objectArena = NSSArena_Create();
|
|
objectList = nssList_Create(objectArena, PR_FALSE);
|
|
}
|
|
nssList_Add(objectList, objectStack);
|
|
objectStackSize = objectStackSize * 2;
|
|
objectStack = nss_ZNEWARRAY(objectArena, CK_OBJECT_HANDLE,
|
|
objectStackSize);
|
|
if (objectStack == NULL) {
|
|
count =0;
|
|
break;
|
|
/* return what we can */
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
ckrv = CKAPI(slot)->C_FindObjectsFinal(hSession);
|
|
nssSession_ExitMonitor(session);
|
|
if (ckrv != CKR_OK) {
|
|
goto loser;
|
|
}
|
|
if (objectList) {
|
|
nssListIterator *objects;
|
|
CK_OBJECT_HANDLE *localStack;
|
|
objects = nssList_CreateIterator(objectList);
|
|
objectStackSize = OBJECT_STACK_SIZE;
|
|
for (localStack = (CK_OBJECT_HANDLE *)nssListIterator_Start(objects);
|
|
localStack != NULL;
|
|
localStack = (CK_OBJECT_HANDLE *)nssListIterator_Next(objects)) {
|
|
for (i=0; i< objectStackSize; i++) {
|
|
cbrv = (*callback)(tok, session, localStack[i], arg);
|
|
}
|
|
objectStackSize = objectStackSize * 2;
|
|
}
|
|
nssListIterator_Finish(objects);
|
|
nssListIterator_Destroy(objects);
|
|
}
|
|
for (i=0; i<count; i++) {
|
|
cbrv = (*callback)(tok, session, objectStack[i], arg);
|
|
}
|
|
if (objectArena)
|
|
NSSArena_Destroy(objectArena);
|
|
return PR_SUCCESS;
|
|
loser:
|
|
if (objectArena)
|
|
NSSArena_Destroy(objectArena);
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
static nssCryptokiInstance *
|
|
create_cryptoki_instance
|
|
(
|
|
NSSArena *arena,
|
|
NSSToken *t,
|
|
CK_OBJECT_HANDLE h,
|
|
PRBool isTokenObject
|
|
)
|
|
{
|
|
nssCryptokiInstance *instance;
|
|
instance = nss_ZNEW(arena, nssCryptokiInstance);
|
|
if (!instance) {
|
|
return NULL;
|
|
}
|
|
instance->handle = h;
|
|
instance->token = t;
|
|
instance->isTokenObject = isTokenObject;
|
|
return instance;
|
|
}
|
|
|
|
static NSSCertificateType
|
|
nss_cert_type_from_ck_attrib(CK_ATTRIBUTE_PTR attrib)
|
|
{
|
|
CK_CERTIFICATE_TYPE ckCertType;
|
|
if (!attrib->pValue) {
|
|
/* default to PKIX */
|
|
return NSSCertificateType_PKIX;
|
|
}
|
|
ckCertType = *((CK_ULONG *)attrib->pValue);
|
|
switch (ckCertType) {
|
|
case CKC_X_509:
|
|
return NSSCertificateType_PKIX;
|
|
default:
|
|
break;
|
|
}
|
|
return NSSCertificateType_Unknown;
|
|
}
|
|
|
|
/* Create a certificate from an object handle. */
|
|
static NSSCertificate *
|
|
get_token_cert
|
|
(
|
|
NSSToken *token,
|
|
nssSession *sessionOpt,
|
|
CK_OBJECT_HANDLE handle
|
|
)
|
|
{
|
|
NSSCertificate *rvCert;
|
|
NSSArena *arena;
|
|
nssSession *session;
|
|
PRStatus nssrv;
|
|
CK_ULONG template_size;
|
|
CK_ATTRIBUTE cert_template[] = {
|
|
{ CKA_CERTIFICATE_TYPE, NULL, 0 },
|
|
{ CKA_ID, NULL, 0 },
|
|
{ CKA_VALUE, NULL, 0 },
|
|
{ CKA_ISSUER, NULL, 0 },
|
|
{ CKA_SERIAL_NUMBER, NULL, 0 },
|
|
{ CKA_LABEL, NULL, 0 },
|
|
{ CKA_SUBJECT, NULL, 0 },
|
|
{ CKA_NETSCAPE_EMAIL, NULL, 0 }
|
|
};
|
|
template_size = sizeof(cert_template) / sizeof(cert_template[0]);
|
|
session = (sessionOpt) ? sessionOpt : token->defaultSession;
|
|
arena = nssArena_Create();
|
|
if (!arena) {
|
|
return NULL;
|
|
}
|
|
rvCert = nss_ZNEW(arena, NSSCertificate);
|
|
if (!rvCert) {
|
|
NSSArena_Destroy(arena);
|
|
return NULL;
|
|
}
|
|
nssrv = nssPKIObject_Initialize(&rvCert->object, arena,
|
|
token->trustDomain, NULL);
|
|
if (nssrv != PR_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
nssrv = nssCKObject_GetAttributes(handle,
|
|
cert_template, template_size,
|
|
arena, session, token->slot);
|
|
if (nssrv != PR_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
rvCert->type = nss_cert_type_from_ck_attrib(&cert_template[0]);
|
|
NSS_CK_ATTRIBUTE_TO_ITEM(&cert_template[1], &rvCert->id);
|
|
NSS_CK_ATTRIBUTE_TO_ITEM(&cert_template[2], &rvCert->encoding);
|
|
NSS_CK_ATTRIBUTE_TO_ITEM(&cert_template[3], &rvCert->issuer);
|
|
NSS_CK_ATTRIBUTE_TO_ITEM(&cert_template[4], &rvCert->serial);
|
|
NSS_CK_ATTRIBUTE_TO_UTF8(&cert_template[5], rvCert->nickname);
|
|
NSS_CK_ATTRIBUTE_TO_ITEM(&cert_template[6], &rvCert->subject);
|
|
NSS_CK_ATTRIBUTE_TO_UTF8(&cert_template[7], rvCert->email);
|
|
/* XXX this would be better accomplished by dividing attributes to
|
|
* retrieve into "required" and "optional"
|
|
*/
|
|
if (rvCert->encoding.size == 0 ||
|
|
rvCert->issuer.size == 0 ||
|
|
rvCert->serial.size == 0 ||
|
|
rvCert->subject.size == 0)
|
|
{
|
|
/* received a bum object from the token */
|
|
goto loser;
|
|
}
|
|
#ifdef NSS_3_4_CODE
|
|
/* nss 3.4 database doesn't associate email address with cert */
|
|
if (!rvCert->email) {
|
|
nssDecodedCert *dc;
|
|
NSSASCII7 *email;
|
|
dc = nssCertificate_GetDecoding(rvCert);
|
|
if (dc) {
|
|
email = dc->getEmailAddress(dc);
|
|
if (email)
|
|
rvCert->email = nssUTF8_Duplicate(email, arena);
|
|
} else {
|
|
goto loser;
|
|
}
|
|
}
|
|
/* nss 3.4 must deal with tokens that do not follow the PKCS#11
|
|
* standard and return decoded serial numbers. The easiest way to
|
|
* work around this is just to grab the serial # from the full encoding
|
|
*/
|
|
if (PR_TRUE) {
|
|
nssDecodedCert *dc;
|
|
dc = nssCertificate_GetDecoding(rvCert);
|
|
if (dc) {
|
|
PRStatus sn_stat;
|
|
sn_stat = dc->getDERSerialNumber(dc, &rvCert->serial, arena);
|
|
if (sn_stat != PR_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
} else {
|
|
goto loser;
|
|
}
|
|
}
|
|
#endif
|
|
return rvCert;
|
|
loser:
|
|
nssPKIObject_Destroy(&rvCert->object);
|
|
return (NSSCertificate *)NULL;
|
|
}
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
nssToken_ImportCertificate
|
|
(
|
|
NSSToken *tok,
|
|
nssSession *sessionOpt,
|
|
NSSCertificate *cert,
|
|
PRBool asTokenObject
|
|
)
|
|
{
|
|
nssCryptokiInstance *instance;
|
|
CK_CERTIFICATE_TYPE cert_type = CKC_X_509;
|
|
CK_OBJECT_HANDLE handle;
|
|
CK_ATTRIBUTE_PTR attr;
|
|
CK_ATTRIBUTE cert_tmpl[9];
|
|
CK_ULONG ctsize;
|
|
NSS_CK_TEMPLATE_START(cert_tmpl, attr, ctsize);
|
|
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_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, &cert->id);
|
|
NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_LABEL, cert->nickname);
|
|
NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_VALUE, &cert->encoding);
|
|
NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ISSUER, &cert->issuer);
|
|
NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SUBJECT, &cert->subject);
|
|
NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SERIAL_NUMBER, &cert->serial);
|
|
NSS_CK_TEMPLATE_FINISH(cert_tmpl, attr, ctsize);
|
|
/* Import the certificate onto the token */
|
|
handle = import_object(tok, sessionOpt, cert_tmpl, ctsize);
|
|
if (handle == CK_INVALID_HANDLE) {
|
|
return PR_FAILURE;
|
|
}
|
|
instance = create_cryptoki_instance(cert->object.arena,
|
|
tok, handle, asTokenObject);
|
|
if (!instance) {
|
|
/* XXX destroy object */
|
|
return PR_FAILURE;
|
|
}
|
|
nssList_Add(cert->object.instanceList, instance);
|
|
/* XXX Fix this! */
|
|
nssListIterator_Destroy(cert->object.instances);
|
|
cert->object.instances = nssList_CreateIterator(cert->object.instanceList);
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
static PRBool
|
|
compare_cert_by_encoding(void *a, void *b)
|
|
{
|
|
NSSCertificate *c1 = (NSSCertificate *)a;
|
|
NSSCertificate *c2 = (NSSCertificate *)b;
|
|
return (nssItem_Equal(&c1->encoding, &c2->encoding, NULL));
|
|
}
|
|
|
|
static PRStatus
|
|
retrieve_cert(NSSToken *t, nssSession *session, CK_OBJECT_HANDLE h, void *arg)
|
|
{
|
|
PRStatus nssrv;
|
|
PRBool found, inCache;
|
|
nssTokenCertSearch *search = (nssTokenCertSearch *)arg;
|
|
NSSCertificate *cert = NULL;
|
|
nssListIterator *instances;
|
|
nssCryptokiInstance *ci;
|
|
CK_ATTRIBUTE derValue = { CKA_VALUE, NULL, 0 };
|
|
inCache = PR_FALSE;
|
|
if (search->cached) {
|
|
NSSCertificate csi; /* a fake cert for indexing */
|
|
nssrv = nssCKObject_GetAttributes(h, &derValue, 1,
|
|
NULL, session, t->slot);
|
|
NSS_CK_ATTRIBUTE_TO_ITEM(&derValue, &csi.encoding);
|
|
cert = (NSSCertificate *)nssList_Get(search->cached, &csi);
|
|
nss_ZFreeIf(csi.encoding.data);
|
|
}
|
|
found = PR_FALSE;
|
|
if (cert) {
|
|
inCache = PR_TRUE;
|
|
nssCertificate_AddRef(cert);
|
|
instances = cert->object.instances;
|
|
for (ci = (nssCryptokiInstance *)nssListIterator_Start(instances);
|
|
ci != (nssCryptokiInstance *)NULL;
|
|
ci = (nssCryptokiInstance *)nssListIterator_Next(instances))
|
|
{
|
|
/* The builtins token will not return the same handle for objects
|
|
* during the lifetime of the token. Thus, assuming the found
|
|
* object is the same as the cached object if there is already an
|
|
* instance for the token.
|
|
*/
|
|
if (ci->token == t) {
|
|
found = PR_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
nssListIterator_Finish(instances);
|
|
} else {
|
|
cert = get_token_cert(t, session, h);
|
|
if (!cert) return PR_FAILURE;
|
|
}
|
|
if (!found) {
|
|
PRBool isTokenObject;
|
|
/* XXX this is incorrect if the search is over both types */
|
|
isTokenObject = (search->searchType == nssTokenSearchType_TokenOnly) ?
|
|
PR_TRUE : PR_FALSE;
|
|
ci = create_cryptoki_instance(cert->object.arena, t, h, isTokenObject);
|
|
if (!ci) {
|
|
NSSCertificate_Destroy(cert);
|
|
return PR_FAILURE;
|
|
}
|
|
nssList_Add(cert->object.instanceList, ci);
|
|
/* XXX Fix this! */
|
|
nssListIterator_Destroy(cert->object.instances);
|
|
cert->object.instances = nssList_CreateIterator(cert->object.instanceList);
|
|
}
|
|
if (!inCache) {
|
|
nssrv = (*search->callback)(cert, search->cbarg);
|
|
} else {
|
|
nssrv = PR_SUCCESS; /* cached entries already handled */
|
|
}
|
|
NSSCertificate_Destroy(cert);
|
|
return nssrv;
|
|
}
|
|
|
|
/* traverse all certificates - this should only happen if the token
|
|
* has been marked as "traversable"
|
|
*/
|
|
NSS_IMPLEMENT PRStatus
|
|
nssToken_TraverseCertificates
|
|
(
|
|
NSSToken *token,
|
|
nssSession *sessionOpt,
|
|
nssTokenCertSearch *search
|
|
)
|
|
{
|
|
PRStatus nssrv;
|
|
CK_ATTRIBUTE_PTR attr;
|
|
CK_ATTRIBUTE cert_template[2];
|
|
CK_ULONG ctsize;
|
|
NSS_CK_TEMPLATE_START(cert_template, attr, ctsize);
|
|
/* Set the search to token/session only if provided */
|
|
if (search->searchType == nssTokenSearchType_SessionOnly) {
|
|
NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false);
|
|
} else if (search->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(cert_template, attr, ctsize);
|
|
if (search->cached) {
|
|
nssList_SetCompareFunction(search->cached, compare_cert_by_encoding);
|
|
}
|
|
nssrv = traverse_objects_by_template(token, sessionOpt,
|
|
cert_template, ctsize,
|
|
retrieve_cert, search);
|
|
return nssrv;
|
|
}
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
nssToken_TraverseCertificatesBySubject
|
|
(
|
|
NSSToken *token,
|
|
nssSession *sessionOpt,
|
|
NSSDER *subject,
|
|
nssTokenCertSearch *search
|
|
)
|
|
{
|
|
PRStatus nssrv;
|
|
CK_ATTRIBUTE_PTR attr;
|
|
CK_ATTRIBUTE subj_template[3];
|
|
CK_ULONG stsize;
|
|
NSS_CK_TEMPLATE_START(subj_template, attr, stsize);
|
|
/* Set the search to token/session only if provided */
|
|
if (search->searchType == nssTokenSearchType_SessionOnly) {
|
|
NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false);
|
|
} else if (search->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);
|
|
if (search->cached) {
|
|
nssList_SetCompareFunction(search->cached, compare_cert_by_encoding);
|
|
}
|
|
/* now traverse the token certs matching this template */
|
|
nssrv = traverse_objects_by_template(token, sessionOpt,
|
|
subj_template, stsize,
|
|
retrieve_cert, search);
|
|
return nssrv;
|
|
}
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
nssToken_TraverseCertificatesByNickname
|
|
(
|
|
NSSToken *token,
|
|
nssSession *sessionOpt,
|
|
NSSUTF8 *name,
|
|
nssTokenCertSearch *search
|
|
)
|
|
{
|
|
PRStatus nssrv;
|
|
CK_ATTRIBUTE_PTR attr;
|
|
CK_ATTRIBUTE nick_template[3];
|
|
CK_ULONG ntsize;
|
|
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 (search->searchType == nssTokenSearchType_SessionOnly) {
|
|
NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false);
|
|
} else if (search->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);
|
|
if (search->cached) {
|
|
nssList_SetCompareFunction(search->cached, compare_cert_by_encoding);
|
|
}
|
|
/* now traverse the token certs matching this template */
|
|
nssrv = traverse_objects_by_template(token, sessionOpt,
|
|
nick_template, ntsize,
|
|
retrieve_cert, search);
|
|
if (nssrv != PR_SUCCESS) {
|
|
return nssrv;
|
|
}
|
|
/* 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++;
|
|
nssrv = traverse_objects_by_template(token, sessionOpt,
|
|
nick_template, ntsize,
|
|
retrieve_cert, search);
|
|
return nssrv;
|
|
}
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
nssToken_TraverseCertificatesByEmail
|
|
(
|
|
NSSToken *token,
|
|
nssSession *sessionOpt,
|
|
NSSASCII7 *email,
|
|
nssTokenCertSearch *search
|
|
)
|
|
{
|
|
PRStatus nssrv;
|
|
CK_ATTRIBUTE_PTR attr;
|
|
CK_ATTRIBUTE email_template[3];
|
|
CK_ULONG etsize;
|
|
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 (search->searchType == nssTokenSearchType_SessionOnly) {
|
|
NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false);
|
|
} else if (search->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);
|
|
if (search->cached) {
|
|
nssList_SetCompareFunction(search->cached, compare_cert_by_encoding);
|
|
}
|
|
/* now traverse the token certs matching this template */
|
|
nssrv = traverse_objects_by_template(token, sessionOpt,
|
|
email_template, etsize,
|
|
retrieve_cert, search);
|
|
if (nssrv != PR_SUCCESS) {
|
|
return nssrv;
|
|
}
|
|
#if 0
|
|
/* This is to workaround the fact that PKCS#11 doesn't specify
|
|
* whether the '\0' should be included. XXX Is that still true?
|
|
*/
|
|
email_tmpl[0].ulValueLen--;
|
|
nssrv = traverse_objects_by_template(token, sessionOpt,
|
|
email_tmpl, etsize,
|
|
retrieve_cert, search);
|
|
#endif
|
|
return nssrv;
|
|
}
|
|
|
|
/* XXX these next two need to create instances as needed */
|
|
|
|
NSS_IMPLEMENT NSSCertificate *
|
|
nssToken_FindCertificateByIssuerAndSerialNumber
|
|
(
|
|
NSSToken *token,
|
|
nssSession *sessionOpt,
|
|
NSSDER *issuer,
|
|
NSSDER *serial,
|
|
nssTokenSearchType searchType
|
|
)
|
|
{
|
|
NSSCertificate *rvCert = NULL;
|
|
nssSession *session;
|
|
PRStatus nssrv;
|
|
CK_OBJECT_HANDLE object;
|
|
CK_ATTRIBUTE_PTR attr;
|
|
CK_ATTRIBUTE cert_template[4];
|
|
CK_ULONG ctsize;
|
|
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);
|
|
}
|
|
/* 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);
|
|
NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SERIAL_NUMBER, serial);
|
|
NSS_CK_TEMPLATE_FINISH(cert_template, attr, ctsize);
|
|
/* get the object handle */
|
|
object = find_object_by_template(token, sessionOpt, cert_template, ctsize);
|
|
if (object == CK_INVALID_HANDLE) {
|
|
return NULL;
|
|
}
|
|
session = (sessionOpt) ? sessionOpt : token->defaultSession;
|
|
rvCert = get_token_cert(token, sessionOpt, object);
|
|
if (rvCert) {
|
|
PRBool isTokenObject;
|
|
nssCryptokiInstance *instance;
|
|
isTokenObject = nssCKObject_IsAttributeTrue(object, CKA_TOKEN,
|
|
session, token->slot,
|
|
&nssrv);
|
|
instance = create_cryptoki_instance(rvCert->object.arena,
|
|
token, object, isTokenObject);
|
|
if (!instance) {
|
|
NSSCertificate_Destroy(rvCert);
|
|
return NULL;
|
|
}
|
|
nssList_Add(rvCert->object.instanceList, instance);
|
|
/* XXX Fix this! */
|
|
nssListIterator_Destroy(rvCert->object.instances);
|
|
rvCert->object.instances = nssList_CreateIterator(rvCert->object.instanceList);
|
|
}
|
|
return rvCert;
|
|
}
|
|
|
|
NSS_IMPLEMENT NSSCertificate *
|
|
nssToken_FindCertificateByEncodedCertificate
|
|
(
|
|
NSSToken *token,
|
|
nssSession *sessionOpt,
|
|
NSSBER *encodedCertificate,
|
|
nssTokenSearchType searchType
|
|
)
|
|
{
|
|
NSSCertificate *rvCert = NULL;
|
|
nssSession *session;
|
|
PRStatus nssrv;
|
|
CK_OBJECT_HANDLE object;
|
|
CK_ATTRIBUTE_PTR attr;
|
|
CK_ATTRIBUTE cert_template[3];
|
|
CK_ULONG ctsize;
|
|
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 */
|
|
object = find_object_by_template(token, sessionOpt, cert_template, ctsize);
|
|
if (object == CK_INVALID_HANDLE) {
|
|
return NULL;
|
|
}
|
|
session = (sessionOpt) ? sessionOpt : token->defaultSession;
|
|
rvCert = get_token_cert(token, sessionOpt, object);
|
|
if (rvCert) {
|
|
PRBool isTokenObject;
|
|
nssCryptokiInstance *instance;
|
|
isTokenObject = nssCKObject_IsAttributeTrue(object, CKA_TOKEN,
|
|
session, token->slot,
|
|
&nssrv);
|
|
instance = create_cryptoki_instance(rvCert->object.arena,
|
|
token, object, isTokenObject);
|
|
if (!instance) {
|
|
NSSCertificate_Destroy(rvCert);
|
|
return NULL;
|
|
}
|
|
nssList_Add(rvCert->object.instanceList, instance);
|
|
/* XXX Fix this! */
|
|
nssListIterator_Destroy(rvCert->object.instances);
|
|
rvCert->object.instances = nssList_CreateIterator(rvCert->object.instanceList);
|
|
}
|
|
return rvCert;
|
|
}
|
|
|
|
static void
|
|
sha1_hash(NSSItem *input, NSSItem *output)
|
|
{
|
|
NSSAlgorithmAndParameters *ap;
|
|
NSSToken *token = STAN_GetDefaultCryptoToken();
|
|
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;
|
|
NSSToken *token = STAN_GetDefaultCryptoToken();
|
|
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);
|
|
}
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
nssToken_ImportTrust
|
|
(
|
|
NSSToken *tok,
|
|
nssSession *sessionOpt,
|
|
NSSTrust *trust,
|
|
PRBool asTokenObject
|
|
)
|
|
{
|
|
CK_OBJECT_HANDLE handle;
|
|
CK_OBJECT_CLASS tobjc = CKO_NETSCAPE_TRUST;
|
|
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;
|
|
NSSCertificate *c = trust->certificate;
|
|
sha1_result.data = sha1; sha1_result.size = sizeof sha1;
|
|
md5_result.data = md5; md5_result.size = sizeof md5;
|
|
sha1_hash(&c->encoding, &sha1_result);
|
|
md5_hash(&c->encoding, &md5_result);
|
|
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, &c->issuer);
|
|
NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SERIAL_NUMBER, &c->serial);
|
|
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, trust->serverAuth);
|
|
NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_CLIENT_AUTH, trust->clientAuth);
|
|
NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_CODE_SIGNING, trust->codeSigning);
|
|
NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_EMAIL_PROTECTION,
|
|
trust->emailProtection);
|
|
NSS_CK_TEMPLATE_FINISH(trust_tmpl, attr, tsize);
|
|
/* import the trust object onto the token */
|
|
handle = import_object(tok, NULL, trust_tmpl, tsize);
|
|
if (handle != CK_INVALID_HANDLE) {
|
|
nssCryptokiInstance *instance;
|
|
instance = create_cryptoki_instance(trust->object.arena,
|
|
tok, handle, asTokenObject);
|
|
if (!instance) {
|
|
return PR_FAILURE;
|
|
}
|
|
nssList_Add(trust->object.instanceList, instance);
|
|
/* XXX Fix this! */
|
|
nssListIterator_Destroy(trust->object.instances);
|
|
trust->object.instances = nssList_CreateIterator(trust->object.instanceList);
|
|
return PR_SUCCESS;
|
|
}
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
static CK_OBJECT_HANDLE
|
|
get_cert_trust_handle
|
|
(
|
|
NSSToken *token,
|
|
nssSession *session,
|
|
NSSCertificate *c,
|
|
nssTokenSearchType searchType
|
|
)
|
|
{
|
|
CK_OBJECT_CLASS tobjc = CKO_NETSCAPE_TRUST;
|
|
CK_ATTRIBUTE_PTR attr;
|
|
CK_ATTRIBUTE tobj_template[5];
|
|
CK_ULONG tobj_size;
|
|
PRUint8 sha1[20]; /* this is cheating... */
|
|
NSSItem sha1_result;
|
|
sha1_result.data = sha1; sha1_result.size = sizeof sha1;
|
|
sha1_hash(&c->encoding, &sha1_result);
|
|
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_CERT_SHA1_HASH, &sha1_result);
|
|
#ifdef NSS_3_4_CODE
|
|
if (!PK11_HasRootCerts(token->pk11slot)) {
|
|
#endif
|
|
NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ISSUER, &c->issuer);
|
|
NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SERIAL_NUMBER , &c->serial);
|
|
#ifdef NSS_3_4_CODE
|
|
}
|
|
/*
|
|
* we need to arrange for the built-in token to lose the bottom 2
|
|
* attributes so that old built-in tokens will continue to work.
|
|
*/
|
|
#endif
|
|
NSS_CK_TEMPLATE_FINISH(tobj_template, attr, tobj_size);
|
|
return find_object_by_template(token, session,
|
|
tobj_template, tobj_size);
|
|
}
|
|
|
|
NSS_IMPLEMENT NSSTrust *
|
|
nssToken_FindTrustForCert
|
|
(
|
|
NSSToken *token,
|
|
nssSession *sessionOpt,
|
|
NSSCertificate *c,
|
|
nssTokenSearchType searchType
|
|
)
|
|
{
|
|
PRStatus nssrv;
|
|
NSSTrust *rvTrust;
|
|
nssSession *session;
|
|
NSSArena *arena;
|
|
nssCryptokiInstance *instance;
|
|
PRBool isTokenObject;
|
|
CK_BBOOL isToken;
|
|
CK_TRUST saTrust, caTrust, epTrust, csTrust;
|
|
CK_OBJECT_HANDLE tobjID;
|
|
CK_ATTRIBUTE_PTR attr;
|
|
CK_ATTRIBUTE trust_template[5];
|
|
CK_ULONG trust_size;
|
|
session = (sessionOpt) ? sessionOpt : token->defaultSession;
|
|
tobjID = get_cert_trust_handle(token, session, c, searchType);
|
|
if (tobjID == CK_INVALID_HANDLE) {
|
|
return NULL;
|
|
}
|
|
/* Then use the trust object to find the trust settings */
|
|
NSS_CK_TEMPLATE_START(trust_template, attr, trust_size);
|
|
NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TOKEN, isToken);
|
|
NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_SERVER_AUTH, saTrust);
|
|
NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_CLIENT_AUTH, caTrust);
|
|
NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_EMAIL_PROTECTION, epTrust);
|
|
NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_CODE_SIGNING, csTrust);
|
|
NSS_CK_TEMPLATE_FINISH(trust_template, attr, trust_size);
|
|
nssrv = nssCKObject_GetAttributes(tobjID,
|
|
trust_template, trust_size,
|
|
NULL, session, token->slot);
|
|
if (nssrv != PR_SUCCESS) {
|
|
return NULL;
|
|
}
|
|
arena = nssArena_Create();
|
|
if (!arena) {
|
|
return NULL;
|
|
}
|
|
rvTrust = nss_ZNEW(arena, NSSTrust);
|
|
if (!rvTrust) {
|
|
nssArena_Destroy(arena);
|
|
return NULL;
|
|
}
|
|
nssrv = nssPKIObject_Initialize(&rvTrust->object, arena,
|
|
token->trustDomain, NULL);
|
|
if (nssrv != PR_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
isTokenObject = (isToken == CK_TRUE) ? PR_TRUE : PR_FALSE;
|
|
instance = create_cryptoki_instance(arena, token, tobjID, isTokenObject);
|
|
if (!instance) {
|
|
goto loser;
|
|
}
|
|
rvTrust->serverAuth = saTrust;
|
|
rvTrust->clientAuth = caTrust;
|
|
rvTrust->emailProtection = epTrust;
|
|
rvTrust->codeSigning = csTrust;
|
|
return rvTrust;
|
|
loser:
|
|
nssPKIObject_Destroy(&rvTrust->object);
|
|
return (NSSTrust *)NULL;
|
|
}
|
|
|