mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-13 11:10:13 +01:00
44b7f056d9
bug1001332, 56b691c003ad, bug1086145, bug1054069, bug1155922, bug991783, bug1125025, bug1162521, bug1162644, bug1132941, bug1164364, bug1166205, bug1166163, bug1166515, bug1138554, bug1167046, bug1167043, bug1169451, bug1172128, bug1170322, bug102794, bug1128184, bug557830, bug1174648, bug1180244, bug1177784, bug1173413, bug1169174, bug1084669, bug951455, bug1183395, bug1177430, bug1183827, bug1160139, bug1154106, bug1142209, bug1185033, bug1193467, bug1182667(with sha512 changes backed out, which breaks VC6 compilation), bug1158489, bug337796
1252 lines
31 KiB
C
1252 lines
31 KiB
C
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#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))
|
|
{
|
|
/* Must copy, see bug 745548 */
|
|
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) {
|
|
node->object = (*collection->createObject)(node->object);
|
|
if (!node->object) {
|
|
/*remove bogus object from list*/
|
|
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);
|
|
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);
|
|
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;
|
|
}
|
|
|