mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-04 16:15:25 +00:00
1265 lines
32 KiB
C
1265 lines
32 KiB
C
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* 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 the Initial Developer are Copyright (C) 1994-2000
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#ifdef DEBUG
|
|
static const char CVS_ID[] = "@(#) $RCSfile: pkibase.c,v $ $Revision: 1.30 $ $Date: 2008/01/21 23:20:19 $";
|
|
#endif /* DEBUG */
|
|
|
|
#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_AtomicIncrement(&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_AtomicDecrement(&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_AtomicIncrement(&object->refCount);
|
|
return object;
|
|
}
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
nssPKIObject_AddInstance (
|
|
nssPKIObject *object,
|
|
nssCryptokiObject *instance
|
|
)
|
|
{
|
|
nssPKIObject_Lock(object);
|
|
if (object->numInstances == 0) {
|
|
object->instances = nss_ZNEWARRAY(object->arena,
|
|
nssCryptokiObject *,
|
|
object->numInstances + 1);
|
|
} else {
|
|
PRUint32 i;
|
|
for (i=0; i<object->numInstances; i++) {
|
|
if (nssCryptokiObject_Equal(object->instances[i], instance)) {
|
|
nssPKIObject_Unlock(object);
|
|
if (instance->label) {
|
|
if (!object->instances[i]->label ||
|
|
!nssUTF8_Equal(instance->label,
|
|
object->instances[i]->label, NULL))
|
|
{
|
|
/* Either the old instance did not have a label,
|
|
* or the label has changed.
|
|
*/
|
|
nss_ZFreeIf(object->instances[i]->label);
|
|
object->instances[i]->label = instance->label;
|
|
instance->label = NULL;
|
|
}
|
|
} else if (object->instances[i]->label) {
|
|
/* The old label was removed */
|
|
nss_ZFreeIf(object->instances[i]->label);
|
|
object->instances[i]->label = NULL;
|
|
}
|
|
nssCryptokiObject_Destroy(instance);
|
|
return PR_SUCCESS;
|
|
}
|
|
}
|
|
object->instances = nss_ZREALLOCARRAY(object->instances,
|
|
nssCryptokiObject *,
|
|
object->numInstances + 1);
|
|
}
|
|
if (!object->instances) {
|
|
nssPKIObject_Unlock(object);
|
|
return PR_FAILURE;
|
|
}
|
|
object->instances[object->numInstances++] = instance;
|
|
nssPKIObject_Unlock(object);
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
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))
|
|
{
|
|
/* XXX should be copy? safe as long as caller has reference */
|
|
nickname = object->instances[i]->label;
|
|
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;
|
|
NSSTime *time, sTime;
|
|
PRBool haveUsageMatch = PR_FALSE;
|
|
PRBool thisCertMatches;
|
|
|
|
if (timeOpt) {
|
|
time = timeOpt;
|
|
} else {
|
|
NSSTime_Now(&sTime);
|
|
time = &sTime;
|
|
}
|
|
if (!certs) {
|
|
return (NSSCertificate *)NULL;
|
|
}
|
|
for (; *certs; certs++) {
|
|
nssDecodedCert *dc, *bestdc;
|
|
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);
|
|
haveUsageMatch = thisCertMatches;
|
|
continue;
|
|
} else {
|
|
if (haveUsageMatch && !thisCertMatches) {
|
|
/* if already have a cert for this usage, and if this cert
|
|
* doesn't have the correct usage, continue
|
|
*/
|
|
continue;
|
|
} else if (!haveUsageMatch && thisCertMatches) {
|
|
/* this one does match usage, replace the other */
|
|
nssCertificate_Destroy(bestCert);
|
|
bestCert = nssCertificate_AddRef(c);
|
|
haveUsageMatch = PR_TRUE;
|
|
continue;
|
|
}
|
|
/* this cert match as well as any cert we've found so far,
|
|
* defer to time/policies
|
|
* */
|
|
}
|
|
bestdc = nssCertificate_GetDecoding(bestCert);
|
|
/* time */
|
|
if (bestdc->isValidAtTime(bestdc, time)) {
|
|
/* The current best cert is valid at time */
|
|
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);
|
|
}
|
|
}
|
|
/* either they are both valid at time, or neither valid;
|
|
* take the newer one
|
|
*/
|
|
if (!bestdc->isNewerThan(bestdc, dc)) {
|
|
nssCertificate_Destroy(bestCert);
|
|
bestCert = nssCertificate_AddRef(c);
|
|
}
|
|
/* 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
|
|
)
|
|
{
|
|
PRStatus status;
|
|
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:
|
|
status = (*callback->func.cert)((NSSCertificate *)node->object,
|
|
callback->arg);
|
|
break;
|
|
case pkiObjectType_CRL:
|
|
status = (*callback->func.crl)((NSSCRL *)node->object,
|
|
callback->arg);
|
|
break;
|
|
case pkiObjectType_PrivateKey:
|
|
status = (*callback->func.pvkey)((NSSPrivateKey *)node->object,
|
|
callback->arg);
|
|
break;
|
|
case pkiObjectType_PublicKey:
|
|
status = (*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
|
|
)
|
|
{
|
|
PRStatus status;
|
|
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);
|
|
status = 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
|
|
)
|
|
{
|
|
PRStatus status;
|
|
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);
|
|
status = 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);
|
|
rvTime->prTime = prTime;
|
|
return rvTime;
|
|
}
|
|
|
|
NSS_IMPLEMENT PRTime
|
|
NSSTime_GetPRTime (
|
|
NSSTime *time
|
|
)
|
|
{
|
|
return time->prTime;
|
|
}
|
|
|