Source code
Revision control
Copy as Markdown
Other Tools
/* 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
#ifndef DEV_H
#include "dev.h"
#endif /* DEV_H */
#ifndef PKIM_H
#include "pkim.h"
#endif /* PKIM_H */
#include "pki3hack.h"
extern const NSSError NSS_ERROR_NOT_FOUND;
NSS_IMPLEMENT void
nssPKIObject_Lock(nssPKIObject *object)
{
switch (object->lockType) {
case nssPKIMonitor:
PZ_EnterMonitor(object->sync.mlock);
break;
case nssPKILock:
PZ_Lock(object->sync.lock);
break;
default:
PORT_Assert(0);
}
}
NSS_IMPLEMENT void
nssPKIObject_Unlock(nssPKIObject *object)
{
switch (object->lockType) {
case nssPKIMonitor:
PZ_ExitMonitor(object->sync.mlock);
break;
case nssPKILock:
PZ_Unlock(object->sync.lock);
break;
default:
PORT_Assert(0);
}
}
NSS_IMPLEMENT PRStatus
nssPKIObject_NewLock(nssPKIObject *object, nssPKILockType lockType)
{
object->lockType = lockType;
switch (lockType) {
case nssPKIMonitor:
object->sync.mlock = PZ_NewMonitor(nssILockSSL);
return (object->sync.mlock ? PR_SUCCESS : PR_FAILURE);
case nssPKILock:
object->sync.lock = PZ_NewLock(nssILockSSL);
return (object->sync.lock ? PR_SUCCESS : PR_FAILURE);
default:
PORT_Assert(0);
return PR_FAILURE;
}
}
NSS_IMPLEMENT void
nssPKIObject_DestroyLock(nssPKIObject *object)
{
switch (object->lockType) {
case nssPKIMonitor:
PZ_DestroyMonitor(object->sync.mlock);
object->sync.mlock = NULL;
break;
case nssPKILock:
PZ_DestroyLock(object->sync.lock);
object->sync.lock = NULL;
break;
default:
PORT_Assert(0);
}
}
NSS_IMPLEMENT nssPKIObject *
nssPKIObject_Create(
NSSArena *arenaOpt,
nssCryptokiObject *instanceOpt,
NSSTrustDomain *td,
NSSCryptoContext *cc,
nssPKILockType lockType)
{
NSSArena *arena;
nssArenaMark *mark = NULL;
nssPKIObject *object;
if (arenaOpt) {
arena = arenaOpt;
mark = nssArena_Mark(arena);
} else {
arena = nssArena_Create();
if (!arena) {
return (nssPKIObject *)NULL;
}
}
object = nss_ZNEW(arena, nssPKIObject);
if (!object) {
goto loser;
}
object->arena = arena;
object->trustDomain = td; /* XXX */
object->cryptoContext = cc;
if (PR_SUCCESS != nssPKIObject_NewLock(object, lockType)) {
goto loser;
}
if (instanceOpt) {
if (nssPKIObject_AddInstance(object, instanceOpt) != PR_SUCCESS) {
goto loser;
}
}
PR_ATOMIC_INCREMENT(&object->refCount);
if (mark) {
nssArena_Unmark(arena, mark);
}
return object;
loser:
if (mark) {
nssArena_Release(arena, mark);
} else {
nssArena_Destroy(arena);
}
return (nssPKIObject *)NULL;
}
NSS_IMPLEMENT PRBool
nssPKIObject_Destroy(
nssPKIObject *object)
{
PRUint32 i;
PR_ASSERT(object->refCount > 0);
if (PR_ATOMIC_DECREMENT(&object->refCount) == 0) {
for (i = 0; i < object->numInstances; i++) {
nssCryptokiObject_Destroy(object->instances[i]);
}
nssPKIObject_DestroyLock(object);
nssArena_Destroy(object->arena);
return PR_TRUE;
}
return PR_FALSE;
}
NSS_IMPLEMENT nssPKIObject *
nssPKIObject_AddRef(
nssPKIObject *object)
{
PR_ATOMIC_INCREMENT(&object->refCount);
return object;
}
NSS_IMPLEMENT PRStatus
nssPKIObject_AddInstance(
nssPKIObject *object,
nssCryptokiObject *instance)
{
nssCryptokiObject **newInstances = NULL;
nssPKIObject_Lock(object);
if (object->numInstances == 0) {
newInstances = nss_ZNEWARRAY(object->arena,
nssCryptokiObject *,
object->numInstances + 1);
} else {
PRBool found = PR_FALSE;
PRUint32 i;
for (i = 0; i < object->numInstances; i++) {
if (nssCryptokiObject_Equal(object->instances[i], instance)) {
found = PR_TRUE;
break;
}
}
if (found) {
/* The new instance is identical to one in the array, except
* perhaps that the label may be different. So replace
* the label in the array instance with the label from the
* new instance, and discard the new instance.
*/
nss_ZFreeIf(object->instances[i]->label);
object->instances[i]->label = instance->label;
nssPKIObject_Unlock(object);
instance->label = NULL;
nssCryptokiObject_Destroy(instance);
return PR_SUCCESS;
}
newInstances = nss_ZREALLOCARRAY(object->instances,
nssCryptokiObject *,
object->numInstances + 1);
}
if (newInstances) {
object->instances = newInstances;
newInstances[object->numInstances++] = instance;
}
nssPKIObject_Unlock(object);
return (newInstances ? PR_SUCCESS : PR_FAILURE);
}
NSS_IMPLEMENT PRBool
nssPKIObject_HasInstance(
nssPKIObject *object,
nssCryptokiObject *instance)
{
PRUint32 i;
PRBool hasIt = PR_FALSE;
;
nssPKIObject_Lock(object);
for (i = 0; i < object->numInstances; i++) {
if (nssCryptokiObject_Equal(object->instances[i], instance)) {
hasIt = PR_TRUE;
break;
}
}
nssPKIObject_Unlock(object);
return hasIt;
}
NSS_IMPLEMENT PRStatus
nssPKIObject_RemoveInstanceForToken(
nssPKIObject *object,
NSSToken *token)
{
PRUint32 i;
nssCryptokiObject *instanceToRemove = NULL;
nssPKIObject_Lock(object);
if (object->numInstances == 0) {
nssPKIObject_Unlock(object);
return PR_SUCCESS;
}
for (i = 0; i < object->numInstances; i++) {
if (object->instances[i]->token == token) {
instanceToRemove = object->instances[i];
object->instances[i] = object->instances[object->numInstances - 1];
object->instances[object->numInstances - 1] = NULL;
break;
}
}
if (--object->numInstances > 0) {
nssCryptokiObject **instances = nss_ZREALLOCARRAY(object->instances,
nssCryptokiObject *,
object->numInstances);
if (instances) {
object->instances = instances;
}
} else {
nss_ZFreeIf(object->instances);
}
nssCryptokiObject_Destroy(instanceToRemove);
nssPKIObject_Unlock(object);
return PR_SUCCESS;
}
/* this needs more thought on what will happen when there are multiple
* instances
*/
NSS_IMPLEMENT PRStatus
nssPKIObject_DeleteStoredObject(
nssPKIObject *object,
NSSCallback *uhh,
PRBool isFriendly)
{
PRUint32 i, numNotDestroyed;
PRStatus status = PR_SUCCESS;
numNotDestroyed = 0;
nssPKIObject_Lock(object);
for (i = 0; i < object->numInstances; i++) {
nssCryptokiObject *instance = object->instances[i];
status = nssToken_DeleteStoredObject(instance);
object->instances[i] = NULL;
if (status == PR_SUCCESS) {
nssCryptokiObject_Destroy(instance);
} else {
object->instances[numNotDestroyed++] = instance;
}
}
if (numNotDestroyed == 0) {
nss_ZFreeIf(object->instances);
object->numInstances = 0;
} else {
object->numInstances = numNotDestroyed;
}
nssPKIObject_Unlock(object);
return status;
}
NSS_IMPLEMENT NSSToken **
nssPKIObject_GetTokens(
nssPKIObject *object,
PRStatus *statusOpt)
{
NSSToken **tokens = NULL;
nssPKIObject_Lock(object);
if (object->numInstances > 0) {
tokens = nss_ZNEWARRAY(NULL, NSSToken *, object->numInstances + 1);
if (tokens) {
PRUint32 i;
for (i = 0; i < object->numInstances; i++) {
tokens[i] = nssToken_AddRef(object->instances[i]->token);
}
}
}
nssPKIObject_Unlock(object);
if (statusOpt)
*statusOpt = PR_SUCCESS; /* until more logic here */
return tokens;
}
NSS_IMPLEMENT NSSUTF8 *
nssPKIObject_GetNicknameForToken(
nssPKIObject *object,
NSSToken *tokenOpt)
{
PRUint32 i;
NSSUTF8 *nickname = NULL;
nssPKIObject_Lock(object);
for (i = 0; i < object->numInstances; i++) {
if ((!tokenOpt && object->instances[i]->label) ||
(object->instances[i]->token == tokenOpt)) {
nickname = nssUTF8_Duplicate(object->instances[i]->label, NULL);
break;
}
}
nssPKIObject_Unlock(object);
return nickname;
}
NSS_IMPLEMENT nssCryptokiObject **
nssPKIObject_GetInstances(
nssPKIObject *object)
{
nssCryptokiObject **instances = NULL;
PRUint32 i;
if (object->numInstances == 0) {
return (nssCryptokiObject **)NULL;
}
nssPKIObject_Lock(object);
instances = nss_ZNEWARRAY(NULL, nssCryptokiObject *,
object->numInstances + 1);
if (instances) {
for (i = 0; i < object->numInstances; i++) {
instances[i] = nssCryptokiObject_Clone(object->instances[i]);
}
}
nssPKIObject_Unlock(object);
return instances;
}
NSS_IMPLEMENT void
nssCertificateArray_Destroy(
NSSCertificate **certs)
{
if (certs) {
NSSCertificate **certp;
for (certp = certs; *certp; certp++) {
if ((*certp)->decoding) {
CERTCertificate *cc = STAN_GetCERTCertificate(*certp);
if (cc) {
CERT_DestroyCertificate(cc);
}
continue;
}
nssCertificate_Destroy(*certp);
}
nss_ZFreeIf(certs);
}
}
NSS_IMPLEMENT void
NSSCertificateArray_Destroy(
NSSCertificate **certs)
{
nssCertificateArray_Destroy(certs);
}
NSS_IMPLEMENT NSSCertificate **
nssCertificateArray_Join(
NSSCertificate **certs1,
NSSCertificate **certs2)
{
if (certs1 && certs2) {
NSSCertificate **certs, **cp;
PRUint32 count = 0;
PRUint32 count1 = 0;
cp = certs1;
while (*cp++)
count1++;
count = count1;
cp = certs2;
while (*cp++)
count++;
certs = nss_ZREALLOCARRAY(certs1, NSSCertificate *, count + 1);
if (!certs) {
nss_ZFreeIf(certs1);
nss_ZFreeIf(certs2);
return (NSSCertificate **)NULL;
}
for (cp = certs2; *cp; cp++, count1++) {
certs[count1] = *cp;
}
nss_ZFreeIf(certs2);
return certs;
} else if (certs1) {
return certs1;
} else {
return certs2;
}
}
NSS_IMPLEMENT NSSCertificate *
nssCertificateArray_FindBestCertificate(
NSSCertificate **certs,
NSSTime *timeOpt,
const NSSUsage *usage,
NSSPolicies *policiesOpt)
{
NSSCertificate *bestCert = NULL;
nssDecodedCert *bestdc = NULL;
NSSTime *time, sTime;
PRBool bestCertMatches = PR_FALSE;
PRBool thisCertMatches;
PRBool bestCertIsValidAtTime = PR_FALSE;
PRBool bestCertIsTrusted = PR_FALSE;
if (timeOpt) {
time = timeOpt;
} else {
NSSTime_Now(&sTime);
time = &sTime;
}
if (!certs) {
return (NSSCertificate *)NULL;
}
for (; *certs; certs++) {
nssDecodedCert *dc;
NSSCertificate *c = *certs;
dc = nssCertificate_GetDecoding(c);
if (!dc)
continue;
thisCertMatches = dc->matchUsage(dc, usage);
if (!bestCert) {
/* always take the first cert, but remember whether or not
* the usage matched
*/
bestCert = nssCertificate_AddRef(c);
bestCertMatches = thisCertMatches;
bestdc = dc;
continue;
} else {
if (bestCertMatches && !thisCertMatches) {
/* if already have a cert for this usage, and if this cert
* doesn't have the correct usage, continue
*/
continue;
} else if (!bestCertMatches && thisCertMatches) {
/* this one does match usage, replace the other */
nssCertificate_Destroy(bestCert);
bestCert = nssCertificate_AddRef(c);
bestCertMatches = thisCertMatches;
bestdc = dc;
continue;
}
/* this cert match as well as any cert we've found so far,
* defer to time/policies
* */
}
/* time */
if (bestCertIsValidAtTime || bestdc->isValidAtTime(bestdc, time)) {
/* The current best cert is valid at time */
bestCertIsValidAtTime = PR_TRUE;
if (!dc->isValidAtTime(dc, time)) {
/* If the new cert isn't valid at time, it's not better */
continue;
}
} else {
/* The current best cert is not valid at time */
if (dc->isValidAtTime(dc, time)) {
/* If the new cert is valid at time, it's better */
nssCertificate_Destroy(bestCert);
bestCert = nssCertificate_AddRef(c);
bestdc = dc;
bestCertIsValidAtTime = PR_TRUE;
continue;
}
}
/* Either they are both valid at time, or neither valid.
* If only one is trusted for this usage, take it.
*/
if (bestCertIsTrusted || bestdc->isTrustedForUsage(bestdc, usage)) {
bestCertIsTrusted = PR_TRUE;
if (!dc->isTrustedForUsage(dc, usage)) {
continue;
}
} else {
/* The current best cert is not trusted */
if (dc->isTrustedForUsage(dc, usage)) {
/* If the new cert is trusted, it's better */
nssCertificate_Destroy(bestCert);
bestCert = nssCertificate_AddRef(c);
bestdc = dc;
bestCertIsTrusted = PR_TRUE;
continue;
}
}
/* Otherwise, take the newer one. */
if (!bestdc->isNewerThan(bestdc, dc)) {
nssCertificate_Destroy(bestCert);
bestCert = nssCertificate_AddRef(c);
bestdc = dc;
continue;
}
/* policies */
/* XXX later -- defer to policies */
}
return bestCert;
}
NSS_IMPLEMENT PRStatus
nssCertificateArray_Traverse(
NSSCertificate **certs,
PRStatus (*callback)(NSSCertificate *c, void *arg),
void *arg)
{
PRStatus status = PR_SUCCESS;
if (certs) {
NSSCertificate **certp;
for (certp = certs; *certp; certp++) {
status = (*callback)(*certp, arg);
if (status != PR_SUCCESS) {
break;
}
}
}
return status;
}
NSS_IMPLEMENT void
nssCRLArray_Destroy(
NSSCRL **crls)
{
if (crls) {
NSSCRL **crlp;
for (crlp = crls; *crlp; crlp++) {
nssCRL_Destroy(*crlp);
}
nss_ZFreeIf(crls);
}
}
/*
* Object collections
*/
typedef enum {
pkiObjectType_Certificate = 0,
pkiObjectType_CRL = 1,
pkiObjectType_PrivateKey = 2,
pkiObjectType_PublicKey = 3
} pkiObjectType;
/* Each object is defined by a set of items that uniquely identify it.
* Here are the uid sets:
*
* NSSCertificate ==> { issuer, serial }
* NSSPrivateKey
* (RSA) ==> { modulus, public exponent }
*
*/
#define MAX_ITEMS_FOR_UID 2
/* pkiObjectCollectionNode
*
* A node in the collection is the set of unique identifiers for a single
* object, along with either the actual object or a proto-object.
*/
typedef struct
{
PRCList link;
PRBool haveObject;
nssPKIObject *object;
NSSItem uid[MAX_ITEMS_FOR_UID];
} pkiObjectCollectionNode;
/* nssPKIObjectCollection
*
* The collection is the set of all objects, plus the interfaces needed
* to manage the objects.
*
*/
struct nssPKIObjectCollectionStr {
NSSArena *arena;
NSSTrustDomain *td;
NSSCryptoContext *cc;
PRCList head; /* list of pkiObjectCollectionNode's */
PRUint32 size;
pkiObjectType objectType;
void (*destroyObject)(nssPKIObject *o);
PRStatus (*getUIDFromObject)(nssPKIObject *o, NSSItem *uid);
PRStatus (*getUIDFromInstance)(nssCryptokiObject *co, NSSItem *uid,
NSSArena *arena);
nssPKIObject *(*createObject)(nssPKIObject *o);
nssPKILockType lockType; /* type of lock to use for new proto-objects */
};
static nssPKIObjectCollection *
nssPKIObjectCollection_Create(
NSSTrustDomain *td,
NSSCryptoContext *ccOpt,
nssPKILockType lockType)
{
NSSArena *arena;
nssPKIObjectCollection *rvCollection = NULL;
arena = nssArena_Create();
if (!arena) {
return (nssPKIObjectCollection *)NULL;
}
rvCollection = nss_ZNEW(arena, nssPKIObjectCollection);
if (!rvCollection) {
goto loser;
}
PR_INIT_CLIST(&rvCollection->head);
rvCollection->arena = arena;
rvCollection->td = td; /* XXX */
rvCollection->cc = ccOpt;
rvCollection->lockType = lockType;
return rvCollection;
loser:
nssArena_Destroy(arena);
return (nssPKIObjectCollection *)NULL;
}
NSS_IMPLEMENT void
nssPKIObjectCollection_Destroy(
nssPKIObjectCollection *collection)
{
if (collection) {
PRCList *link;
pkiObjectCollectionNode *node;
/* first destroy any objects in the collection */
link = PR_NEXT_LINK(&collection->head);
while (link != &collection->head) {
node = (pkiObjectCollectionNode *)link;
if (node->haveObject) {
(*collection->destroyObject)(node->object);
} else {
nssPKIObject_Destroy(node->object);
}
link = PR_NEXT_LINK(link);
}
/* then destroy it */
nssArena_Destroy(collection->arena);
}
}
NSS_IMPLEMENT PRUint32
nssPKIObjectCollection_Count(
nssPKIObjectCollection *collection)
{
return collection->size;
}
NSS_IMPLEMENT PRStatus
nssPKIObjectCollection_AddObject(
nssPKIObjectCollection *collection,
nssPKIObject *object)
{
pkiObjectCollectionNode *node;
node = nss_ZNEW(collection->arena, pkiObjectCollectionNode);
if (!node) {
return PR_FAILURE;
}
node->haveObject = PR_TRUE;
node->object = nssPKIObject_AddRef(object);
(*collection->getUIDFromObject)(object, node->uid);
PR_INIT_CLIST(&node->link);
PR_INSERT_BEFORE(&node->link, &collection->head);
collection->size++;
return PR_SUCCESS;
}
static pkiObjectCollectionNode *
find_instance_in_collection(
nssPKIObjectCollection *collection,
nssCryptokiObject *instance)
{
PRCList *link;
pkiObjectCollectionNode *node;
link = PR_NEXT_LINK(&collection->head);
while (link != &collection->head) {
node = (pkiObjectCollectionNode *)link;
if (nssPKIObject_HasInstance(node->object, instance)) {
return node;
}
link = PR_NEXT_LINK(link);
}
return (pkiObjectCollectionNode *)NULL;
}
static pkiObjectCollectionNode *
find_object_in_collection(
nssPKIObjectCollection *collection,
NSSItem *uid)
{
PRUint32 i;
PRStatus status;
PRCList *link;
pkiObjectCollectionNode *node;
link = PR_NEXT_LINK(&collection->head);
while (link != &collection->head) {
node = (pkiObjectCollectionNode *)link;
for (i = 0; i < MAX_ITEMS_FOR_UID; i++) {
if (!nssItem_Equal(&node->uid[i], &uid[i], &status)) {
break;
}
}
if (i == MAX_ITEMS_FOR_UID) {
return node;
}
link = PR_NEXT_LINK(link);
}
return (pkiObjectCollectionNode *)NULL;
}
static pkiObjectCollectionNode *
add_object_instance(
nssPKIObjectCollection *collection,
nssCryptokiObject *instance,
PRBool *foundIt)
{
PRUint32 i;
PRStatus status;
pkiObjectCollectionNode *node;
nssArenaMark *mark = NULL;
NSSItem uid[MAX_ITEMS_FOR_UID];
nsslibc_memset(uid, 0, sizeof uid);
/* The list is traversed twice, first (here) looking to match the
* { token, handle } tuple, and if that is not found, below a search
* for unique identifier is done. Here, a match means this exact object
* instance is already in the collection, and we have nothing to do.
*/
*foundIt = PR_FALSE;
node = find_instance_in_collection(collection, instance);
if (node) {
/* The collection is assumed to take over the instance. Since we
* are not using it, it must be destroyed.
*/
nssCryptokiObject_Destroy(instance);
*foundIt = PR_TRUE;
return node;
}
mark = nssArena_Mark(collection->arena);
if (!mark) {
goto loser;
}
status = (*collection->getUIDFromInstance)(instance, uid,
collection->arena);
if (status != PR_SUCCESS) {
goto loser;
}
/* Search for unique identifier. A match here means the object exists
* in the collection, but does not have this instance, so the instance
* needs to be added.
*/
node = find_object_in_collection(collection, uid);
if (node) {
/* This is an object with multiple instances */
status = nssPKIObject_AddInstance(node->object, instance);
} else {
/* This is a completely new object. Create a node for it. */
node = nss_ZNEW(collection->arena, pkiObjectCollectionNode);
if (!node) {
goto loser;
}
node->object = nssPKIObject_Create(NULL, instance,
collection->td, collection->cc,
collection->lockType);
if (!node->object) {
goto loser;
}
for (i = 0; i < MAX_ITEMS_FOR_UID; i++) {
node->uid[i] = uid[i];
}
node->haveObject = PR_FALSE;
PR_INIT_CLIST(&node->link);
PR_INSERT_BEFORE(&node->link, &collection->head);
collection->size++;
status = PR_SUCCESS;
}
nssArena_Unmark(collection->arena, mark);
return node;
loser:
if (mark) {
nssArena_Release(collection->arena, mark);
}
nssCryptokiObject_Destroy(instance);
return (pkiObjectCollectionNode *)NULL;
}
NSS_IMPLEMENT PRStatus
nssPKIObjectCollection_AddInstances(
nssPKIObjectCollection *collection,
nssCryptokiObject **instances,
PRUint32 numInstances)
{
PRStatus status = PR_SUCCESS;
PRUint32 i = 0;
PRBool foundIt;
pkiObjectCollectionNode *node;
if (instances) {
while ((!numInstances || i < numInstances) && *instances) {
if (status == PR_SUCCESS) {
node = add_object_instance(collection, *instances, &foundIt);
if (node == NULL) {
/* add_object_instance freed the current instance */
/* free the remaining instances */
status = PR_FAILURE;
}
} else {
nssCryptokiObject_Destroy(*instances);
}
instances++;
i++;
}
}
return status;
}
static void
nssPKIObjectCollection_RemoveNode(
nssPKIObjectCollection *collection,
pkiObjectCollectionNode *node)
{
PR_REMOVE_LINK(&node->link);
collection->size--;
}
static PRStatus
nssPKIObjectCollection_GetObjects(
nssPKIObjectCollection *collection,
nssPKIObject **rvObjects,
PRUint32 rvSize)
{
PRUint32 i = 0;
PRCList *link = PR_NEXT_LINK(&collection->head);
pkiObjectCollectionNode *node;
int error = 0;
while ((i < rvSize) && (link != &collection->head)) {
node = (pkiObjectCollectionNode *)link;
if (!node->haveObject) {
/* Convert the proto-object to an object */
node->object = (*collection->createObject)(node->object);
if (!node->object) {
link = PR_NEXT_LINK(link);
/*remove bogus object from list*/
nssPKIObjectCollection_RemoveNode(collection, node);
error++;
continue;
}
node->haveObject = PR_TRUE;
}
rvObjects[i++] = nssPKIObject_AddRef(node->object);
link = PR_NEXT_LINK(link);
}
if (!error && *rvObjects == NULL) {
nss_SetError(NSS_ERROR_NOT_FOUND);
}
return PR_SUCCESS;
}
NSS_IMPLEMENT PRStatus
nssPKIObjectCollection_Traverse(
nssPKIObjectCollection *collection,
nssPKIObjectCallback *callback)
{
PRCList *link = PR_NEXT_LINK(&collection->head);
pkiObjectCollectionNode *node;
while (link != &collection->head) {
node = (pkiObjectCollectionNode *)link;
if (!node->haveObject) {
node->object = (*collection->createObject)(node->object);
if (!node->object) {
link = PR_NEXT_LINK(link);
/*remove bogus object from list*/
nssPKIObjectCollection_RemoveNode(collection, node);
continue;
}
node->haveObject = PR_TRUE;
}
switch (collection->objectType) {
case pkiObjectType_Certificate:
(void)(*callback->func.cert)((NSSCertificate *)node->object,
callback->arg);
break;
case pkiObjectType_CRL:
(void)(*callback->func.crl)((NSSCRL *)node->object,
callback->arg);
break;
case pkiObjectType_PrivateKey:
(void)(*callback->func.pvkey)((NSSPrivateKey *)node->object,
callback->arg);
break;
case pkiObjectType_PublicKey:
(void)(*callback->func.pbkey)((NSSPublicKey *)node->object,
callback->arg);
break;
}
link = PR_NEXT_LINK(link);
}
return PR_SUCCESS;
}
NSS_IMPLEMENT PRStatus
nssPKIObjectCollection_AddInstanceAsObject(
nssPKIObjectCollection *collection,
nssCryptokiObject *instance)
{
pkiObjectCollectionNode *node;
PRBool foundIt;
node = add_object_instance(collection, instance, &foundIt);
if (node == NULL) {
return PR_FAILURE;
}
if (!node->haveObject) {
nssPKIObject *original = node->object;
node->object = (*collection->createObject)(node->object);
if (!node->object) {
/*remove bogus object from list*/
nssPKIObject_Destroy(original);
nssPKIObjectCollection_RemoveNode(collection, node);
return PR_FAILURE;
}
node->haveObject = PR_TRUE;
} else if (!foundIt) {
/* The instance was added to a pre-existing node. This
* function is *only* being used for certificates, and having
* multiple instances of certs in 3.X requires updating the
* CERTCertificate.
* But only do it if it was a new instance!!! If the same instance
* is encountered, we set *foundIt to true. Detect that here and
* ignore it.
*/
STAN_ForceCERTCertificateUpdate((NSSCertificate *)node->object);
}
return PR_SUCCESS;
}
/*
* Certificate collections
*/
static void
cert_destroyObject(nssPKIObject *o)
{
NSSCertificate *c = (NSSCertificate *)o;
if (c->decoding) {
CERTCertificate *cc = STAN_GetCERTCertificate(c);
if (cc) {
CERT_DestroyCertificate(cc);
return;
} /* else destroy it as NSSCertificate below */
}
nssCertificate_Destroy(c);
}
static PRStatus
cert_getUIDFromObject(nssPKIObject *o, NSSItem *uid)
{
NSSCertificate *c = (NSSCertificate *)o;
/* The builtins are still returning decoded serial numbers. Until
* this compatibility issue is resolved, use the full DER of the
* cert to uniquely identify it.
*/
NSSDER *derCert;
derCert = nssCertificate_GetEncoding(c);
uid[0].data = NULL;
uid[0].size = 0;
uid[1].data = NULL;
uid[1].size = 0;
if (derCert != NULL) {
uid[0] = *derCert;
}
return PR_SUCCESS;
}
static PRStatus
cert_getUIDFromInstance(nssCryptokiObject *instance, NSSItem *uid,
NSSArena *arena)
{
/* The builtins are still returning decoded serial numbers. Until
* this compatibility issue is resolved, use the full DER of the
* cert to uniquely identify it.
*/
uid[1].data = NULL;
uid[1].size = 0;
return nssCryptokiCertificate_GetAttributes(instance,
NULL, /* XXX sessionOpt */
arena, /* arena */
NULL, /* type */
NULL, /* id */
&uid[0], /* encoding */
NULL, /* issuer */
NULL, /* serial */
NULL); /* subject */
}
static nssPKIObject *
cert_createObject(nssPKIObject *o)
{
NSSCertificate *cert;
cert = nssCertificate_Create(o);
/* if (STAN_GetCERTCertificate(cert) == NULL) {
nssCertificate_Destroy(cert);
return (nssPKIObject *)NULL;
} */
/* In 3.4, have to maintain uniqueness of cert pointers by caching all
* certs. Cache the cert here, before returning. If it is already
* cached, take the cached entry.
*/
{
NSSTrustDomain *td = o->trustDomain;
nssTrustDomain_AddCertsToCache(td, &cert, 1);
}
return (nssPKIObject *)cert;
}
NSS_IMPLEMENT nssPKIObjectCollection *
nssCertificateCollection_Create(
NSSTrustDomain *td,
NSSCertificate **certsOpt)
{
nssPKIObjectCollection *collection;
collection = nssPKIObjectCollection_Create(td, NULL, nssPKIMonitor);
if (!collection) {
return NULL;
}
collection->objectType = pkiObjectType_Certificate;
collection->destroyObject = cert_destroyObject;
collection->getUIDFromObject = cert_getUIDFromObject;
collection->getUIDFromInstance = cert_getUIDFromInstance;
collection->createObject = cert_createObject;
if (certsOpt) {
for (; *certsOpt; certsOpt++) {
nssPKIObject *object = (nssPKIObject *)(*certsOpt);
(void)nssPKIObjectCollection_AddObject(collection, object);
}
}
return collection;
}
NSS_IMPLEMENT NSSCertificate **
nssPKIObjectCollection_GetCertificates(
nssPKIObjectCollection *collection,
NSSCertificate **rvOpt,
PRUint32 maximumOpt,
NSSArena *arenaOpt)
{
PRStatus status;
PRUint32 rvSize;
PRBool allocated = PR_FALSE;
if (collection->size == 0) {
return (NSSCertificate **)NULL;
}
if (maximumOpt == 0) {
rvSize = collection->size;
} else {
rvSize = PR_MIN(collection->size, maximumOpt);
}
if (!rvOpt) {
rvOpt = nss_ZNEWARRAY(arenaOpt, NSSCertificate *, rvSize + 1);
if (!rvOpt) {
return (NSSCertificate **)NULL;
}
allocated = PR_TRUE;
}
status = nssPKIObjectCollection_GetObjects(collection,
(nssPKIObject **)rvOpt,
rvSize);
if (status != PR_SUCCESS) {
if (allocated) {
nss_ZFreeIf(rvOpt);
}
return (NSSCertificate **)NULL;
}
return rvOpt;
}
/*
* CRL/KRL collections
*/
static void
crl_destroyObject(nssPKIObject *o)
{
NSSCRL *crl = (NSSCRL *)o;
nssCRL_Destroy(crl);
}
static PRStatus
crl_getUIDFromObject(nssPKIObject *o, NSSItem *uid)
{
NSSCRL *crl = (NSSCRL *)o;
NSSDER *encoding;
encoding = nssCRL_GetEncoding(crl);
if (!encoding) {
nss_SetError(NSS_ERROR_INVALID_ARGUMENT);
return PR_FALSE;
}
uid[0] = *encoding;
uid[1].data = NULL;
uid[1].size = 0;
return PR_SUCCESS;
}
static PRStatus
crl_getUIDFromInstance(nssCryptokiObject *instance, NSSItem *uid,
NSSArena *arena)
{
return nssCryptokiCRL_GetAttributes(instance,
NULL, /* XXX sessionOpt */
arena, /* arena */
&uid[0], /* encoding */
NULL, /* subject */
NULL, /* class */
NULL, /* url */
NULL); /* isKRL */
}
static nssPKIObject *
crl_createObject(nssPKIObject *o)
{
return (nssPKIObject *)nssCRL_Create(o);
}
NSS_IMPLEMENT nssPKIObjectCollection *
nssCRLCollection_Create(
NSSTrustDomain *td,
NSSCRL **crlsOpt)
{
nssPKIObjectCollection *collection;
collection = nssPKIObjectCollection_Create(td, NULL, nssPKILock);
if (!collection) {
return NULL;
}
collection->objectType = pkiObjectType_CRL;
collection->destroyObject = crl_destroyObject;
collection->getUIDFromObject = crl_getUIDFromObject;
collection->getUIDFromInstance = crl_getUIDFromInstance;
collection->createObject = crl_createObject;
if (crlsOpt) {
for (; *crlsOpt; crlsOpt++) {
nssPKIObject *object = (nssPKIObject *)(*crlsOpt);
(void)nssPKIObjectCollection_AddObject(collection, object);
}
}
return collection;
}
NSS_IMPLEMENT NSSCRL **
nssPKIObjectCollection_GetCRLs(
nssPKIObjectCollection *collection,
NSSCRL **rvOpt,
PRUint32 maximumOpt,
NSSArena *arenaOpt)
{
PRStatus status;
PRUint32 rvSize;
PRBool allocated = PR_FALSE;
if (collection->size == 0) {
return (NSSCRL **)NULL;
}
if (maximumOpt == 0) {
rvSize = collection->size;
} else {
rvSize = PR_MIN(collection->size, maximumOpt);
}
if (!rvOpt) {
rvOpt = nss_ZNEWARRAY(arenaOpt, NSSCRL *, rvSize + 1);
if (!rvOpt) {
return (NSSCRL **)NULL;
}
allocated = PR_TRUE;
}
status = nssPKIObjectCollection_GetObjects(collection,
(nssPKIObject **)rvOpt,
rvSize);
if (status != PR_SUCCESS) {
if (allocated) {
nss_ZFreeIf(rvOpt);
}
return (NSSCRL **)NULL;
}
return rvOpt;
}
/* how bad would it be to have a static now sitting around, updated whenever
* this was called? would avoid repeated allocs...
*/
NSS_IMPLEMENT NSSTime *
NSSTime_Now(NSSTime *timeOpt)
{
return NSSTime_SetPRTime(timeOpt, PR_Now());
}
NSS_IMPLEMENT NSSTime *
NSSTime_SetPRTime(
NSSTime *timeOpt,
PRTime prTime)
{
NSSTime *rvTime;
rvTime = (timeOpt) ? timeOpt : nss_ZNEW(NULL, NSSTime);
if (rvTime) {
rvTime->prTime = prTime;
}
return rvTime;
}
NSS_IMPLEMENT PRTime
NSSTime_GetPRTime(
NSSTime *time)
{
return time->prTime;
}