diff options
author | cvs2hg <devnull@localhost> | 2002-05-20 23:21:40 +0000 |
---|---|---|
committer | cvs2hg <devnull@localhost> | 2002-05-20 23:21:40 +0000 |
commit | 43341b5c97b80aab89f6281c29352d7c26bfd107 (patch) | |
tree | 02f64b0eac2acfecf4e49ce91030839df974aa48 | |
parent | 76cb86889bc8668e8fd22fb4b728ae98cc5319f2 (diff) | |
download | nss-hg-43341b5c97b80aab89f6281c29352d7c26bfd107.tar.gz |
fixup commit for branch 'MOZILLA_1_0_BRANCH'
-rw-r--r-- | security/coreconf/AIX5.1.mk | 54 | ||||
-rw-r--r-- | security/coreconf/HP-UXB.11.20.mk | 55 | ||||
-rw-r--r-- | security/coreconf/OpenBSD.mk | 62 | ||||
-rw-r--r-- | security/nss/lib/dev/devtm.h | 58 | ||||
-rw-r--r-- | security/nss/lib/pki/pkibase.c | 1452 |
5 files changed, 1681 insertions, 0 deletions
diff --git a/security/coreconf/AIX5.1.mk b/security/coreconf/AIX5.1.mk new file mode 100644 index 000000000..daa999ba7 --- /dev/null +++ b/security/coreconf/AIX5.1.mk @@ -0,0 +1,54 @@ +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1994-2000 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. +# +# Config stuff for AIX5.1 +# + +include $(CORE_DEPTH)/coreconf/AIX.mk + + +ifeq ($(USE_64), 1) +# Next line replaced by generic name handling in arch.mk +# COMPILER_TAG = _64 + OS_CFLAGS += -DAIX_64BIT + OBJECT_MODE=64 + export OBJECT_MODE +endif +DSO_LDOPTS = -brtl -bM:SRE -bnoentry +MKSHLIB = $(LD) $(DSO_LDOPTS) -lsvld -L/usr/lpp/xlC/lib -lc -lm + +OS_LIBS += -L/usr/lpp/xlC/lib -lc -lm +ifdef MAPFILE +DSO_LDOPTS += -bexport:$(MAPFILE) +else +DSO_LDOPTS += -bexpall +endif diff --git a/security/coreconf/HP-UXB.11.20.mk b/security/coreconf/HP-UXB.11.20.mk new file mode 100644 index 000000000..a638736c5 --- /dev/null +++ b/security/coreconf/HP-UXB.11.20.mk @@ -0,0 +1,55 @@ +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 2002 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. +# +# On HP-UX 10.30 and 11.x, the default implementation strategy is +# pthreads. Classic nspr and pthreads-user are also available. +# + +ifeq ($(OS_RELEASE),B.11.20) +OS_CFLAGS += -DHPUX10 +DEFAULT_IMPL_STRATEGY = _PTH +endif + +# +# To use the true pthread (kernel thread) library on 10.30 and +# 11.x, we should define _POSIX_C_SOURCE to be 199506L. +# The _REENTRANT macro is deprecated. +# + +ifdef USE_PTHREADS + OS_CFLAGS += -D_POSIX_C_SOURCE=199506L +endif + +# +# Config stuff for HP-UXB.11.x. +# +include $(CORE_DEPTH)/coreconf/HP-UXB.11.mk diff --git a/security/coreconf/OpenBSD.mk b/security/coreconf/OpenBSD.mk new file mode 100644 index 000000000..14fa73489 --- /dev/null +++ b/security/coreconf/OpenBSD.mk @@ -0,0 +1,62 @@ +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1994-2000 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. +# +# Config stuff for OpenBSD +# + +include $(CORE_DEPTH)/coreconf/UNIX.mk + +DEFAULT_COMPILER = gcc +CC = gcc +CCC = g++ +RANLIB = ranlib + +CPU_ARCH := $(shell uname -p) +ifeq ($(CPU_ARCH),i386) +OS_REL_CFLAGS = -Di386 +CPU_ARCH = x86 +endif + +DLL_SUFFIX = so.1.0 + +OS_CFLAGS = $(DSO_CFLAGS) $(OS_REL_CFLAGS) -ansi -Wall -pipe -DOPENBSD + +OS_LIBS = + +ARCH = openbsd + +DSO_CFLAGS = -fPIC -DPIC +DSO_LDOPTS = -shared -Wl,-soname,lib$(LIBRARY_NAME)$(LIBRARY_VERSION).$(DLL_SUFFIX) +DSO_LDFLAGS = + +MKSHLIB = $(CC) $(DSO_LDOPTS) + diff --git a/security/nss/lib/dev/devtm.h b/security/nss/lib/dev/devtm.h new file mode 100644 index 000000000..5e48c5d80 --- /dev/null +++ b/security/nss/lib/dev/devtm.h @@ -0,0 +1,58 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#ifndef DEVTM_H +#define DEVTM_H + +#ifdef DEBUG +static const char DEVTM_CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$"; +#endif /* DEBUG */ + +/* + * devtm.h + * + * This file contains module-private definitions for the low-level + * cryptoki devices. + */ + +#ifndef DEVT_H +#include "devt.h" +#endif /* DEVT_H */ + +PR_BEGIN_EXTERN_C + +#define MAX_LOCAL_CACHE_OBJECTS 10 + +PR_END_EXTERN_C + +#endif /* DEVTM_H */ diff --git a/security/nss/lib/pki/pkibase.c b/security/nss/lib/pki/pkibase.c new file mode 100644 index 000000000..626dc6c90 --- /dev/null +++ b/security/nss/lib/pki/pkibase.c @@ -0,0 +1,1452 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#ifdef DEBUG +static const char CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$"; +#endif /* DEBUG */ + +#ifndef DEV_H +#include "dev.h" +#endif /* DEV_H */ + +#ifndef PKIM_H +#include "pkim.h" +#endif /* PKIM_H */ + +#ifdef NSS_3_4_CODE +#include "pki3hack.h" +#endif + +NSS_IMPLEMENT nssPKIObject * +nssPKIObject_Create +( + NSSArena *arenaOpt, + nssCryptokiObject *instanceOpt, + NSSTrustDomain *td, + NSSCryptoContext *cc +) +{ + 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; + object->lock = PZ_NewLock(nssILockOther); + if (!object->lock) { + 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); + PR_AtomicDecrement(&object->refCount); + if (object->refCount == 0) { + for (i=0; i<object->numInstances; i++) { + nssCryptokiObject_Destroy(object->instances[i]); + } + PZ_DestroyLock(object->lock); + 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 +) +{ + PZ_Lock(object->lock); + 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)) { + PZ_Unlock(object->lock); + 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) { + PZ_Unlock(object->lock); + return PR_FAILURE; + } + object->instances[object->numInstances++] = instance; + PZ_Unlock(object->lock); + return PR_SUCCESS; +} + +NSS_IMPLEMENT PRBool +nssPKIObject_HasInstance +( + nssPKIObject *object, + nssCryptokiObject *instance +) +{ + PRUint32 i; + PRBool hasIt = PR_FALSE;; + PZ_Lock(object->lock); + for (i=0; i<object->numInstances; i++) { + if (nssCryptokiObject_Equal(object->instances[i], instance)) { + hasIt = PR_TRUE; + break; + } + } + PZ_Unlock(object->lock); + return hasIt; +} + +NSS_IMPLEMENT PRStatus +nssPKIObject_RemoveInstanceForToken +( + nssPKIObject *object, + NSSToken *token +) +{ + PRUint32 i; + nssCryptokiObject *instanceToRemove = NULL; + PZ_Lock(object->lock); + if (object->numInstances == 0) { + PZ_Unlock(object->lock); + 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) { + object->instances = nss_ZREALLOCARRAY(object->instances, + nssCryptokiObject *, + object->numInstances); + if (!object->instances) { + PZ_Unlock(object->lock); + return PR_FAILURE; + } + } else { + nss_ZFreeIf(object->instances); + } + nssCryptokiObject_Destroy(instanceToRemove); + PZ_Unlock(object->lock); + 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; + NSSTrustDomain *td = object->trustDomain; + NSSCallback *pwcb = uhh ? /* is this optional? */ + uhh : + nssTrustDomain_GetDefaultCallback(td, NULL); + numNotDestroyed = 0; + PZ_Lock(object->lock); + for (i=0; i<object->numInstances; i++) { + nssCryptokiObject *instance = object->instances[i]; +#ifndef NSS_3_4_CODE + NSSSlot *slot = nssToken_GetSlot(instance->token); + /* If both the operation and the slot are friendly, login is + * not required. If either or both are not friendly, it is + * required. + */ + if (!(isFriendly && nssSlot_IsFriendly(slot))) { + status = nssSlot_Login(slot, pwcb); + nssSlot_Destroy(slot); + if (status == PR_SUCCESS) { + /* XXX this should be fixed to understand read-only tokens, + * for now, to handle the builtins, just make the attempt. + */ + status = nssToken_DeleteStoredObject(instance); + } + } +#else + status = nssToken_DeleteStoredObject(instance); +#endif + 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; + } + PZ_Unlock(object->lock); + return status; +} + +NSS_IMPLEMENT NSSToken ** +nssPKIObject_GetTokens +( + nssPKIObject *object, + PRStatus *statusOpt +) +{ + NSSToken **tokens = NULL; + PZ_Lock(object->lock); + 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); + } + } + } + PZ_Unlock(object->lock); + 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; + PZ_Lock(object->lock); + 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; + } + } + PZ_Unlock(object->lock); + return nickname; +} + +#ifdef NSS_3_4_CODE +NSS_IMPLEMENT nssCryptokiObject ** +nssPKIObject_GetInstances +( + nssPKIObject *object +) +{ + nssCryptokiObject **instances = NULL; + PRUint32 i; + if (object->numInstances == 0) { + return (nssCryptokiObject **)NULL; + } + PZ_Lock(object->lock); + instances = nss_ZNEWARRAY(NULL, nssCryptokiObject *, + object->numInstances + 1); + if (instances) { + for (i=0; i<object->numInstances; i++) { + instances[i] = nssCryptokiObject_Clone(object->instances[i]); + } + } + PZ_Unlock(object->lock); + return instances; +} +#endif + +NSS_IMPLEMENT void +nssCertificateArray_Destroy +( + NSSCertificate **certs +) +{ + if (certs) { + NSSCertificate **certp; + for (certp = certs; *certp; certp++) { +#ifdef NSS_3_4_CODE + if ((*certp)->decoding) { + CERTCertificate *cc = STAN_GetCERTCertificate(*certp); + if (cc) { + CERT_DestroyCertificate(cc); + } + continue; + } +#endif + 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, + NSSUsage *usage, + NSSPolicies *policiesOpt +) +{ + NSSCertificate *bestCert = NULL; + NSSTime *time, sTime; + 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 (!bestCert) { + /* take the first cert with matching usage */ +#ifdef NSS_3_4_CODE + if (usage->anyUsage) { +#else + if (!usage || usage->anyUsage) { +#endif + bestCert = nssCertificate_AddRef(c); + } else { + if (dc->matchUsage(dc, usage)) { + bestCert = nssCertificate_AddRef(c); + } + } + continue; + } else { + /* already have a cert for this usage, if this cert doesn't have + * the correct usage, continue + * if ths cert does match usage, defer to time/policies + */ +#ifdef NSS_3_4_CODE + if (!usage->anyUsage && !dc->matchUsage(dc, usage)) { +#else + if (PR_TRUE) { +#endif + continue; + } + } + 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); +}; + +static nssPKIObjectCollection * +nssPKIObjectCollection_Create +( + NSSTrustDomain *td, + NSSCryptoContext *ccOpt +) +{ + 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; + 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 +) +{ + 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. + */ + 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); + 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 a 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); + 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; + pkiObjectCollectionNode *node; + if (instances) { + for (; *instances; instances++, i++) { + if (numInstances > 0 && i == numInstances) { + break; + } + node = add_object_instance(collection, *instances); + if (node == NULL) { + goto loser; + } + } + } + return status; +loser: + /* free the remaining instances */ + for (; *instances; instances++, i++) { + if (numInstances > 0 && i == numInstances) { + break; + } + nssCryptokiObject_Destroy(*instances); + } + return PR_FAILURE; +} + +static PRStatus +nssPKIObjectCollection_GetObjects +( + nssPKIObjectCollection *collection, + nssPKIObject **rvObjects, + PRUint32 rvSize +) +{ + PRUint32 i = 0; + PRCList *link = PR_NEXT_LINK(&collection->head); + pkiObjectCollectionNode *node; + while (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) { + return PR_FAILURE; + } + node->haveObject = PR_TRUE; + } + rvObjects[i++] = nssPKIObject_AddRef(node->object); + link = PR_NEXT_LINK(link); + } + 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) { + return PR_FAILURE; + } + 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; + node = add_object_instance(collection, instance); + if (node == NULL) { + return PR_FAILURE; + } + if (!node->haveObject) { + node->object = (*collection->createObject)(node->object); + if (!node->object) { + return PR_FAILURE; + } + node->haveObject = PR_TRUE; + } +#ifdef NSS_3_4_CODE + else { + /* 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. + */ + STAN_ForceCERTCertificateUpdate((NSSCertificate *)node->object); + } +#endif + return PR_SUCCESS; +} + +/* + * Certificate collections + */ + +static void +cert_destroyObject(nssPKIObject *o) +{ + NSSCertificate *c = (NSSCertificate *)o; +#ifdef NSS_3_4_CODE + if (c->decoding) { + CERTCertificate *cc = STAN_GetCERTCertificate(c); + if (cc) { + CERT_DestroyCertificate(cc); + return; + } /* else destroy it as NSSCertificate below */ + } +#endif + nssCertificate_Destroy(c); +} + +static PRStatus +cert_getUIDFromObject(nssPKIObject *o, NSSItem *uid) +{ + NSSCertificate *c = (NSSCertificate *)o; +#ifdef NSS_3_4_CODE + /* 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] = *derCert; + uid[1].data = NULL; uid[1].size = 0; +#else + NSSDER *issuer, *serial; + issuer = nssCertificate_GetIssuer(c); + serial = nssCertificate_GetSerialNumber(c); + uid[0] = *issuer; + uid[1] = *serial; +#endif /* NSS_3_4_CODE */ + return PR_SUCCESS; +} + +static PRStatus +cert_getUIDFromInstance(nssCryptokiObject *instance, NSSItem *uid, + NSSArena *arena) +{ +#ifdef NSS_3_4_CODE + /* 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 */ + NULL); /* email */ +#else + return nssCryptokiCertificate_GetAttributes(instance, + NULL, /* XXX sessionOpt */ + arena, /* arena */ + NULL, /* type */ + NULL, /* id */ + NULL, /* encoding */ + &uid[0], /* issuer */ + &uid[1], /* serial */ + NULL, /* subject */ + NULL); /* email */ +#endif /* NSS_3_4_CODE */ +} + +static nssPKIObject * +cert_createObject(nssPKIObject *o) +{ + NSSCertificate *cert; + cert = nssCertificate_Create(o); +#ifdef NSS_3_4_CODE + 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); + } +#endif + return (nssPKIObject *)cert; +} + +NSS_IMPLEMENT nssPKIObjectCollection * +nssCertificateCollection_Create +( + NSSTrustDomain *td, + NSSCertificate **certsOpt +) +{ + PRStatus status; + nssPKIObjectCollection *collection; + collection = nssPKIObjectCollection_Create(td, 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); + 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); + 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, /* 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); + 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; +} + +#ifdef PURE_STAN_BUILD +/* + * PrivateKey collections + */ + +static void +privkey_destroyObject(nssPKIObject *o) +{ + NSSPrivateKey *pvk = (NSSPrivateKey *)o; + nssPrivateKey_Destroy(pvk); +} + +static PRStatus +privkey_getUIDFromObject(nssPKIObject *o, NSSItem *uid) +{ + NSSPrivateKey *pvk = (NSSPrivateKey *)o; + NSSItem *id; + id = nssPrivateKey_GetID(pvk); + uid[0] = *id; + return PR_SUCCESS; +} + +static PRStatus +privkey_getUIDFromInstance(nssCryptokiObject *instance, NSSItem *uid, + NSSArena *arena) +{ + return nssCryptokiPrivateKey_GetAttributes(instance, + NULL, /* XXX sessionOpt */ + arena, + NULL, /* type */ + &uid[0]); +} + +static nssPKIObject * +privkey_createObject(nssPKIObject *o) +{ + NSSPrivateKey *pvk; + pvk = nssPrivateKey_Create(o); + return (nssPKIObject *)pvk; +} + +NSS_IMPLEMENT nssPKIObjectCollection * +nssPrivateKeyCollection_Create +( + NSSTrustDomain *td, + NSSPrivateKey **pvkOpt +) +{ + PRStatus status; + nssPKIObjectCollection *collection; + collection = nssPKIObjectCollection_Create(td, NULL); + collection->objectType = pkiObjectType_PrivateKey; + collection->destroyObject = privkey_destroyObject; + collection->getUIDFromObject = privkey_getUIDFromObject; + collection->getUIDFromInstance = privkey_getUIDFromInstance; + collection->createObject = privkey_createObject; + if (pvkOpt) { + for (; *pvkOpt; pvkOpt++) { + nssPKIObject *o = (nssPKIObject *)(*pvkOpt); + status = nssPKIObjectCollection_AddObject(collection, o); + } + } + return collection; +} + +NSS_IMPLEMENT NSSPrivateKey ** +nssPKIObjectCollection_GetPrivateKeys +( + nssPKIObjectCollection *collection, + NSSPrivateKey **rvOpt, + PRUint32 maximumOpt, + NSSArena *arenaOpt +) +{ + PRStatus status; + PRUint32 rvSize; + PRBool allocated = PR_FALSE; + if (collection->size == 0) { + return (NSSPrivateKey **)NULL; + } + if (maximumOpt == 0) { + rvSize = collection->size; + } else { + rvSize = PR_MIN(collection->size, maximumOpt); + } + if (!rvOpt) { + rvOpt = nss_ZNEWARRAY(arenaOpt, NSSPrivateKey *, rvSize + 1); + if (!rvOpt) { + return (NSSPrivateKey **)NULL; + } + allocated = PR_TRUE; + } + status = nssPKIObjectCollection_GetObjects(collection, + (nssPKIObject **)rvOpt, + rvSize); + if (status != PR_SUCCESS) { + if (allocated) { + nss_ZFreeIf(rvOpt); + } + return (NSSPrivateKey **)NULL; + } + return rvOpt; +} + +/* + * PublicKey collections + */ + +static void +pubkey_destroyObject(nssPKIObject *o) +{ + NSSPublicKey *pubk = (NSSPublicKey *)o; + nssPublicKey_Destroy(pubk); +} + +static PRStatus +pubkey_getUIDFromObject(nssPKIObject *o, NSSItem *uid) +{ + NSSPublicKey *pubk = (NSSPublicKey *)o; + NSSItem *id; + id = nssPublicKey_GetID(pubk); + uid[0] = *id; + return PR_SUCCESS; +} + +static PRStatus +pubkey_getUIDFromInstance(nssCryptokiObject *instance, NSSItem *uid, + NSSArena *arena) +{ + return nssCryptokiPublicKey_GetAttributes(instance, + NULL, /* XXX sessionOpt */ + arena, + NULL, /* type */ + &uid[0]); +} + +static nssPKIObject * +pubkey_createObject(nssPKIObject *o) +{ + NSSPublicKey *pubk; + pubk = nssPublicKey_Create(o); + return (nssPKIObject *)pubk; +} + +NSS_IMPLEMENT nssPKIObjectCollection * +nssPublicKeyCollection_Create +( + NSSTrustDomain *td, + NSSPublicKey **pubkOpt +) +{ + PRStatus status; + nssPKIObjectCollection *collection; + collection = nssPKIObjectCollection_Create(td, NULL); + collection->objectType = pkiObjectType_PublicKey; + collection->destroyObject = pubkey_destroyObject; + collection->getUIDFromObject = pubkey_getUIDFromObject; + collection->getUIDFromInstance = pubkey_getUIDFromInstance; + collection->createObject = pubkey_createObject; + if (pubkOpt) { + for (; *pubkOpt; pubkOpt++) { + nssPKIObject *o = (nssPKIObject *)(*pubkOpt); + status = nssPKIObjectCollection_AddObject(collection, o); + } + } + return collection; +} + +NSS_IMPLEMENT NSSPublicKey ** +nssPKIObjectCollection_GetPublicKeys +( + nssPKIObjectCollection *collection, + NSSPublicKey **rvOpt, + PRUint32 maximumOpt, + NSSArena *arenaOpt +) +{ + PRStatus status; + PRUint32 rvSize; + PRBool allocated = PR_FALSE; + if (collection->size == 0) { + return (NSSPublicKey **)NULL; + } + if (maximumOpt == 0) { + rvSize = collection->size; + } else { + rvSize = PR_MIN(collection->size, maximumOpt); + } + if (!rvOpt) { + rvOpt = nss_ZNEWARRAY(arenaOpt, NSSPublicKey *, rvSize + 1); + if (!rvOpt) { + return (NSSPublicKey **)NULL; + } + allocated = PR_TRUE; + } + status = nssPKIObjectCollection_GetObjects(collection, + (nssPKIObject **)rvOpt, + rvSize); + if (status != PR_SUCCESS) { + if (allocated) { + nss_ZFreeIf(rvOpt); + } + return (NSSPublicKey **)NULL; + } + return rvOpt; +} +#endif /* PURE_STAN_BUILD */ + +/* 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; +} + |