2008-06-06 05:40:11 -07:00
|
|
|
/* ***** 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
|
2008-08-14 21:12:54 -07:00
|
|
|
static const char CVS_ID[] = "@(#) $RCSfile: pkistore.c,v $ $Revision: 1.33 $ $Date: 2008/06/06 01:19:30 $";
|
2008-06-06 05:40:11 -07:00
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
|
|
#ifndef PKIM_H
|
|
|
|
#include "pkim.h"
|
|
|
|
#endif /* PKIM_H */
|
|
|
|
|
|
|
|
#ifndef PKI_H
|
|
|
|
#include "pki.h"
|
|
|
|
#endif /* PKI_H */
|
|
|
|
|
|
|
|
#ifndef NSSPKI_H
|
|
|
|
#include "nsspki.h"
|
|
|
|
#endif /* NSSPKI_H */
|
|
|
|
|
|
|
|
#ifndef BASE_H
|
|
|
|
#include "base.h"
|
|
|
|
#endif /* BASE_H */
|
|
|
|
|
|
|
|
#ifndef PKISTORE_H
|
|
|
|
#include "pkistore.h"
|
|
|
|
#endif /* PKISTORE_H */
|
|
|
|
|
|
|
|
#include "cert.h"
|
|
|
|
|
|
|
|
#include "prbit.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Certificate Store
|
|
|
|
*
|
|
|
|
* This differs from the cache in that it is a true storage facility. Items
|
|
|
|
* stay in until they are explicitly removed. It is only used by crypto
|
|
|
|
* contexts at this time, but may be more generally useful...
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct nssCertificateStoreStr
|
|
|
|
{
|
|
|
|
PRBool i_alloced_arena;
|
|
|
|
NSSArena *arena;
|
|
|
|
PZLock *lock;
|
|
|
|
nssHash *subject;
|
|
|
|
nssHash *issuer_and_serial;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct certificate_hash_entry_str certificate_hash_entry;
|
|
|
|
|
|
|
|
struct certificate_hash_entry_str
|
|
|
|
{
|
|
|
|
NSSCertificate *cert;
|
|
|
|
NSSTrust *trust;
|
|
|
|
nssSMIMEProfile *profile;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* forward static declarations */
|
|
|
|
static NSSCertificate *
|
|
|
|
nssCertStore_FindCertByIssuerAndSerialNumberLocked (
|
|
|
|
nssCertificateStore *store,
|
|
|
|
NSSDER *issuer,
|
|
|
|
NSSDER *serial
|
|
|
|
);
|
|
|
|
|
|
|
|
NSS_IMPLEMENT nssCertificateStore *
|
|
|
|
nssCertificateStore_Create (
|
|
|
|
NSSArena *arenaOpt
|
|
|
|
)
|
|
|
|
{
|
|
|
|
NSSArena *arena;
|
|
|
|
nssCertificateStore *store;
|
|
|
|
PRBool i_alloced_arena;
|
|
|
|
if (arenaOpt) {
|
|
|
|
arena = arenaOpt;
|
|
|
|
i_alloced_arena = PR_FALSE;
|
|
|
|
} else {
|
|
|
|
arena = nssArena_Create();
|
|
|
|
if (!arena) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
i_alloced_arena = PR_TRUE;
|
|
|
|
}
|
|
|
|
store = nss_ZNEW(arena, nssCertificateStore);
|
|
|
|
if (!store) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
store->lock = PZ_NewLock(nssILockOther);
|
|
|
|
if (!store->lock) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
/* Create the issuer/serial --> {cert, trust, S/MIME profile } hash */
|
|
|
|
store->issuer_and_serial = nssHash_CreateCertificate(arena, 0);
|
|
|
|
if (!store->issuer_and_serial) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
/* Create the subject DER --> subject list hash */
|
|
|
|
store->subject = nssHash_CreateItem(arena, 0);
|
|
|
|
if (!store->subject) {
|
|
|
|
goto loser;
|
|
|
|
}
|
|
|
|
store->arena = arena;
|
|
|
|
store->i_alloced_arena = i_alloced_arena;
|
|
|
|
return store;
|
|
|
|
loser:
|
|
|
|
if (store) {
|
|
|
|
if (store->lock) {
|
|
|
|
PZ_DestroyLock(store->lock);
|
|
|
|
}
|
|
|
|
if (store->issuer_and_serial) {
|
|
|
|
nssHash_Destroy(store->issuer_and_serial);
|
|
|
|
}
|
|
|
|
if (store->subject) {
|
|
|
|
nssHash_Destroy(store->subject);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i_alloced_arena) {
|
|
|
|
nssArena_Destroy(arena);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern const NSSError NSS_ERROR_BUSY;
|
|
|
|
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
|
|
nssCertificateStore_Destroy (
|
|
|
|
nssCertificateStore *store
|
|
|
|
)
|
|
|
|
{
|
|
|
|
if (nssHash_Count(store->issuer_and_serial) > 0) {
|
|
|
|
nss_SetError(NSS_ERROR_BUSY);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
PZ_DestroyLock(store->lock);
|
|
|
|
nssHash_Destroy(store->issuer_and_serial);
|
|
|
|
nssHash_Destroy(store->subject);
|
|
|
|
if (store->i_alloced_arena) {
|
|
|
|
nssArena_Destroy(store->arena);
|
|
|
|
} else {
|
|
|
|
nss_ZFreeIf(store);
|
|
|
|
}
|
|
|
|
return PR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PRStatus
|
|
|
|
add_certificate_entry (
|
|
|
|
nssCertificateStore *store,
|
|
|
|
NSSCertificate *cert
|
|
|
|
)
|
|
|
|
{
|
|
|
|
PRStatus nssrv;
|
|
|
|
certificate_hash_entry *entry;
|
|
|
|
entry = nss_ZNEW(cert->object.arena, certificate_hash_entry);
|
|
|
|
if (!entry) {
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
entry->cert = cert;
|
|
|
|
nssrv = nssHash_Add(store->issuer_and_serial, cert, entry);
|
|
|
|
if (nssrv != PR_SUCCESS) {
|
|
|
|
nss_ZFreeIf(entry);
|
|
|
|
}
|
|
|
|
return nssrv;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PRStatus
|
|
|
|
add_subject_entry (
|
|
|
|
nssCertificateStore *store,
|
|
|
|
NSSCertificate *cert
|
|
|
|
)
|
|
|
|
{
|
|
|
|
PRStatus nssrv;
|
|
|
|
nssList *subjectList;
|
|
|
|
subjectList = (nssList *)nssHash_Lookup(store->subject, &cert->subject);
|
|
|
|
if (subjectList) {
|
|
|
|
/* The subject is already in, add this cert to the list */
|
|
|
|
nssrv = nssList_AddUnique(subjectList, cert);
|
|
|
|
} else {
|
|
|
|
/* Create a new subject list for the subject */
|
|
|
|
subjectList = nssList_Create(NULL, PR_FALSE);
|
|
|
|
if (!subjectList) {
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
nssList_SetSortFunction(subjectList, nssCertificate_SubjectListSort);
|
|
|
|
/* Add the cert entry to this list of subjects */
|
|
|
|
nssrv = nssList_Add(subjectList, cert);
|
|
|
|
if (nssrv != PR_SUCCESS) {
|
|
|
|
return nssrv;
|
|
|
|
}
|
|
|
|
/* Add the subject list to the cache */
|
|
|
|
nssrv = nssHash_Add(store->subject, &cert->subject, subjectList);
|
|
|
|
}
|
|
|
|
return nssrv;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* declared below */
|
|
|
|
static void
|
|
|
|
remove_certificate_entry (
|
|
|
|
nssCertificateStore *store,
|
|
|
|
NSSCertificate *cert
|
|
|
|
);
|
|
|
|
|
|
|
|
/* Caller must hold store->lock */
|
|
|
|
static PRStatus
|
|
|
|
nssCertificateStore_AddLocked (
|
|
|
|
nssCertificateStore *store,
|
|
|
|
NSSCertificate *cert
|
|
|
|
)
|
|
|
|
{
|
|
|
|
PRStatus nssrv = add_certificate_entry(store, cert);
|
|
|
|
if (nssrv == PR_SUCCESS) {
|
|
|
|
nssrv = add_subject_entry(store, cert);
|
|
|
|
if (nssrv == PR_FAILURE) {
|
|
|
|
remove_certificate_entry(store, cert);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nssrv;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NSS_IMPLEMENT NSSCertificate *
|
|
|
|
nssCertificateStore_FindOrAdd (
|
|
|
|
nssCertificateStore *store,
|
|
|
|
NSSCertificate *c
|
|
|
|
)
|
|
|
|
{
|
|
|
|
PRStatus nssrv;
|
|
|
|
NSSCertificate *rvCert = NULL;
|
|
|
|
|
|
|
|
PZ_Lock(store->lock);
|
|
|
|
rvCert = nssCertStore_FindCertByIssuerAndSerialNumberLocked(
|
|
|
|
store, &c->issuer, &c->serial);
|
|
|
|
if (!rvCert) {
|
|
|
|
nssrv = nssCertificateStore_AddLocked(store, c);
|
|
|
|
if (PR_SUCCESS == nssrv) {
|
|
|
|
rvCert = nssCertificate_AddRef(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PZ_Unlock(store->lock);
|
|
|
|
return rvCert;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
remove_certificate_entry (
|
|
|
|
nssCertificateStore *store,
|
|
|
|
NSSCertificate *cert
|
|
|
|
)
|
|
|
|
{
|
|
|
|
certificate_hash_entry *entry;
|
|
|
|
entry = (certificate_hash_entry *)
|
|
|
|
nssHash_Lookup(store->issuer_and_serial, cert);
|
|
|
|
if (entry) {
|
|
|
|
nssHash_Remove(store->issuer_and_serial, cert);
|
|
|
|
if (entry->trust) {
|
|
|
|
nssTrust_Destroy(entry->trust);
|
|
|
|
}
|
|
|
|
if (entry->profile) {
|
|
|
|
nssSMIMEProfile_Destroy(entry->profile);
|
|
|
|
}
|
|
|
|
nss_ZFreeIf(entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
remove_subject_entry (
|
|
|
|
nssCertificateStore *store,
|
|
|
|
NSSCertificate *cert
|
|
|
|
)
|
|
|
|
{
|
|
|
|
nssList *subjectList;
|
|
|
|
/* Get the subject list for the cert's subject */
|
|
|
|
subjectList = (nssList *)nssHash_Lookup(store->subject, &cert->subject);
|
|
|
|
if (subjectList) {
|
|
|
|
/* Remove the cert from the subject hash */
|
|
|
|
nssList_Remove(subjectList, cert);
|
|
|
|
nssHash_Remove(store->subject, &cert->subject);
|
|
|
|
if (nssList_Count(subjectList) == 0) {
|
|
|
|
nssList_Destroy(subjectList);
|
|
|
|
} else {
|
|
|
|
/* The cert being released may have keyed the subject entry.
|
|
|
|
* Since there are still subject certs around, get another and
|
|
|
|
* rekey the entry just in case.
|
|
|
|
*/
|
|
|
|
NSSCertificate *subjectCert;
|
|
|
|
(void)nssList_GetArray(subjectList, (void **)&subjectCert, 1);
|
|
|
|
nssHash_Add(store->subject, &subjectCert->subject, subjectList);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NSS_IMPLEMENT void
|
|
|
|
nssCertificateStore_RemoveCertLOCKED (
|
|
|
|
nssCertificateStore *store,
|
|
|
|
NSSCertificate *cert
|
|
|
|
)
|
|
|
|
{
|
|
|
|
certificate_hash_entry *entry;
|
|
|
|
entry = (certificate_hash_entry *)
|
|
|
|
nssHash_Lookup(store->issuer_and_serial, cert);
|
|
|
|
if (entry && entry->cert == cert) {
|
|
|
|
remove_certificate_entry(store, cert);
|
|
|
|
remove_subject_entry(store, cert);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NSS_IMPLEMENT void
|
|
|
|
nssCertificateStore_Lock (
|
|
|
|
nssCertificateStore *store, nssCertificateStoreTrace* out
|
|
|
|
)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
PORT_Assert(out);
|
|
|
|
out->store = store;
|
|
|
|
out->lock = store->lock;
|
|
|
|
out->locked = PR_TRUE;
|
|
|
|
PZ_Lock(out->lock);
|
|
|
|
#else
|
|
|
|
PZ_Lock(store->lock);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
NSS_IMPLEMENT void
|
|
|
|
nssCertificateStore_Unlock (
|
2008-08-14 21:12:54 -07:00
|
|
|
nssCertificateStore *store, const nssCertificateStoreTrace* in,
|
2008-06-06 05:40:11 -07:00
|
|
|
nssCertificateStoreTrace* out
|
|
|
|
)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
PORT_Assert(in);
|
|
|
|
PORT_Assert(out);
|
|
|
|
out->store = store;
|
|
|
|
out->lock = store->lock;
|
2008-08-14 21:12:54 -07:00
|
|
|
PORT_Assert(!out->locked);
|
2008-06-06 05:40:11 -07:00
|
|
|
out->unlocked = PR_TRUE;
|
|
|
|
|
|
|
|
PORT_Assert(in->store == out->store);
|
|
|
|
PORT_Assert(in->lock == out->lock);
|
|
|
|
PORT_Assert(in->locked);
|
2008-08-14 21:12:54 -07:00
|
|
|
PORT_Assert(!in->unlocked);
|
2008-06-06 05:40:11 -07:00
|
|
|
|
|
|
|
PZ_Unlock(out->lock);
|
|
|
|
#else
|
|
|
|
PZ_Unlock(store->lock);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static NSSCertificate **
|
|
|
|
get_array_from_list (
|
|
|
|
nssList *certList,
|
|
|
|
NSSCertificate *rvOpt[],
|
|
|
|
PRUint32 maximumOpt,
|
|
|
|
NSSArena *arenaOpt
|
|
|
|
)
|
|
|
|
{
|
|
|
|
PRUint32 count;
|
|
|
|
NSSCertificate **rvArray = NULL;
|
|
|
|
count = nssList_Count(certList);
|
|
|
|
if (count == 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (maximumOpt > 0) {
|
|
|
|
count = PR_MIN(maximumOpt, count);
|
|
|
|
}
|
|
|
|
if (rvOpt) {
|
|
|
|
nssList_GetArray(certList, (void **)rvOpt, count);
|
|
|
|
} else {
|
|
|
|
rvArray = nss_ZNEWARRAY(arenaOpt, NSSCertificate *, count + 1);
|
|
|
|
if (rvArray) {
|
|
|
|
nssList_GetArray(certList, (void **)rvArray, count);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rvArray;
|
|
|
|
}
|
|
|
|
|
|
|
|
NSS_IMPLEMENT NSSCertificate **
|
|
|
|
nssCertificateStore_FindCertificatesBySubject (
|
|
|
|
nssCertificateStore *store,
|
|
|
|
NSSDER *subject,
|
|
|
|
NSSCertificate *rvOpt[],
|
|
|
|
PRUint32 maximumOpt,
|
|
|
|
NSSArena *arenaOpt
|
|
|
|
)
|
|
|
|
{
|
|
|
|
NSSCertificate **rvArray = NULL;
|
|
|
|
nssList *subjectList;
|
|
|
|
PZ_Lock(store->lock);
|
|
|
|
subjectList = (nssList *)nssHash_Lookup(store->subject, subject);
|
|
|
|
if (subjectList) {
|
|
|
|
nssCertificateList_AddReferences(subjectList);
|
|
|
|
rvArray = get_array_from_list(subjectList,
|
|
|
|
rvOpt, maximumOpt, arenaOpt);
|
|
|
|
}
|
|
|
|
PZ_Unlock(store->lock);
|
|
|
|
return rvArray;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Because only subject indexing is implemented, all other lookups require
|
|
|
|
* full traversal (unfortunately, PLHashTable doesn't allow you to exit
|
|
|
|
* early from the enumeration). The assumptions are that 1) lookups by
|
|
|
|
* fields other than subject will be rare, and 2) the hash will not have
|
|
|
|
* a large number of entries. These assumptions will be tested.
|
|
|
|
*
|
|
|
|
* XXX
|
|
|
|
* For NSS 3.4, it is worth consideration to do all forms of indexing,
|
|
|
|
* because the only crypto context is global and persistent.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct nickname_template_str
|
|
|
|
{
|
|
|
|
NSSUTF8 *nickname;
|
|
|
|
nssList *subjectList;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void match_nickname(const void *k, void *v, void *a)
|
|
|
|
{
|
|
|
|
PRStatus nssrv;
|
|
|
|
NSSCertificate *c;
|
|
|
|
NSSUTF8 *nickname;
|
|
|
|
nssList *subjectList = (nssList *)v;
|
|
|
|
struct nickname_template_str *nt = (struct nickname_template_str *)a;
|
|
|
|
nssrv = nssList_GetArray(subjectList, (void **)&c, 1);
|
|
|
|
nickname = nssCertificate_GetNickname(c, NULL);
|
|
|
|
if (nssrv == PR_SUCCESS && nickname &&
|
|
|
|
nssUTF8_Equal(nickname, nt->nickname, &nssrv))
|
|
|
|
{
|
|
|
|
nt->subjectList = subjectList;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find all cached certs with this label.
|
|
|
|
*/
|
|
|
|
NSS_IMPLEMENT NSSCertificate **
|
|
|
|
nssCertificateStore_FindCertificatesByNickname (
|
|
|
|
nssCertificateStore *store,
|
|
|
|
const NSSUTF8 *nickname,
|
|
|
|
NSSCertificate *rvOpt[],
|
|
|
|
PRUint32 maximumOpt,
|
|
|
|
NSSArena *arenaOpt
|
|
|
|
)
|
|
|
|
{
|
|
|
|
NSSCertificate **rvArray = NULL;
|
|
|
|
struct nickname_template_str nt;
|
|
|
|
nt.nickname = (char*) nickname;
|
|
|
|
nt.subjectList = NULL;
|
|
|
|
PZ_Lock(store->lock);
|
|
|
|
nssHash_Iterate(store->subject, match_nickname, &nt);
|
|
|
|
if (nt.subjectList) {
|
|
|
|
nssCertificateList_AddReferences(nt.subjectList);
|
|
|
|
rvArray = get_array_from_list(nt.subjectList,
|
|
|
|
rvOpt, maximumOpt, arenaOpt);
|
|
|
|
}
|
|
|
|
PZ_Unlock(store->lock);
|
|
|
|
return rvArray;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct email_template_str
|
|
|
|
{
|
|
|
|
NSSASCII7 *email;
|
|
|
|
nssList *emailList;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void match_email(const void *k, void *v, void *a)
|
|
|
|
{
|
|
|
|
PRStatus nssrv;
|
|
|
|
NSSCertificate *c;
|
|
|
|
nssList *subjectList = (nssList *)v;
|
|
|
|
struct email_template_str *et = (struct email_template_str *)a;
|
|
|
|
nssrv = nssList_GetArray(subjectList, (void **)&c, 1);
|
|
|
|
if (nssrv == PR_SUCCESS &&
|
|
|
|
nssUTF8_Equal(c->email, et->email, &nssrv))
|
|
|
|
{
|
|
|
|
nssListIterator *iter = nssList_CreateIterator(subjectList);
|
|
|
|
if (iter) {
|
|
|
|
for (c = (NSSCertificate *)nssListIterator_Start(iter);
|
|
|
|
c != (NSSCertificate *)NULL;
|
|
|
|
c = (NSSCertificate *)nssListIterator_Next(iter))
|
|
|
|
{
|
|
|
|
nssList_Add(et->emailList, c);
|
|
|
|
}
|
|
|
|
nssListIterator_Finish(iter);
|
|
|
|
nssListIterator_Destroy(iter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find all cached certs with this email address.
|
|
|
|
*/
|
|
|
|
NSS_IMPLEMENT NSSCertificate **
|
|
|
|
nssCertificateStore_FindCertificatesByEmail (
|
|
|
|
nssCertificateStore *store,
|
|
|
|
NSSASCII7 *email,
|
|
|
|
NSSCertificate *rvOpt[],
|
|
|
|
PRUint32 maximumOpt,
|
|
|
|
NSSArena *arenaOpt
|
|
|
|
)
|
|
|
|
{
|
|
|
|
NSSCertificate **rvArray = NULL;
|
|
|
|
struct email_template_str et;
|
|
|
|
et.email = email;
|
|
|
|
et.emailList = nssList_Create(NULL, PR_FALSE);
|
|
|
|
if (!et.emailList) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
PZ_Lock(store->lock);
|
|
|
|
nssHash_Iterate(store->subject, match_email, &et);
|
|
|
|
if (et.emailList) {
|
|
|
|
/* get references before leaving the store's lock protection */
|
|
|
|
nssCertificateList_AddReferences(et.emailList);
|
|
|
|
}
|
|
|
|
PZ_Unlock(store->lock);
|
|
|
|
if (et.emailList) {
|
|
|
|
rvArray = get_array_from_list(et.emailList,
|
|
|
|
rvOpt, maximumOpt, arenaOpt);
|
|
|
|
nssList_Destroy(et.emailList);
|
|
|
|
}
|
|
|
|
return rvArray;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Caller holds store->lock */
|
|
|
|
static NSSCertificate *
|
|
|
|
nssCertStore_FindCertByIssuerAndSerialNumberLocked (
|
|
|
|
nssCertificateStore *store,
|
|
|
|
NSSDER *issuer,
|
|
|
|
NSSDER *serial
|
|
|
|
)
|
|
|
|
{
|
|
|
|
certificate_hash_entry *entry;
|
|
|
|
NSSCertificate *rvCert = NULL;
|
|
|
|
NSSCertificate index;
|
|
|
|
|
|
|
|
index.issuer = *issuer;
|
|
|
|
index.serial = *serial;
|
|
|
|
entry = (certificate_hash_entry *)
|
|
|
|
nssHash_Lookup(store->issuer_and_serial, &index);
|
|
|
|
if (entry) {
|
|
|
|
rvCert = nssCertificate_AddRef(entry->cert);
|
|
|
|
}
|
|
|
|
return rvCert;
|
|
|
|
}
|
|
|
|
|
|
|
|
NSS_IMPLEMENT NSSCertificate *
|
|
|
|
nssCertificateStore_FindCertificateByIssuerAndSerialNumber (
|
|
|
|
nssCertificateStore *store,
|
|
|
|
NSSDER *issuer,
|
|
|
|
NSSDER *serial
|
|
|
|
)
|
|
|
|
{
|
|
|
|
NSSCertificate *rvCert = NULL;
|
|
|
|
|
|
|
|
PZ_Lock(store->lock);
|
|
|
|
rvCert = nssCertStore_FindCertByIssuerAndSerialNumberLocked (
|
|
|
|
store, issuer, serial);
|
|
|
|
PZ_Unlock(store->lock);
|
|
|
|
return rvCert;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PRStatus
|
|
|
|
issuer_and_serial_from_encoding (
|
|
|
|
NSSBER *encoding,
|
|
|
|
NSSDER *issuer,
|
|
|
|
NSSDER *serial
|
|
|
|
)
|
|
|
|
{
|
|
|
|
SECItem derCert, derIssuer, derSerial;
|
|
|
|
SECStatus secrv;
|
|
|
|
derCert.data = (unsigned char *)encoding->data;
|
|
|
|
derCert.len = encoding->size;
|
|
|
|
secrv = CERT_IssuerNameFromDERCert(&derCert, &derIssuer);
|
|
|
|
if (secrv != SECSuccess) {
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
secrv = CERT_SerialNumberFromDERCert(&derCert, &derSerial);
|
|
|
|
if (secrv != SECSuccess) {
|
|
|
|
PORT_Free(derIssuer.data);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
issuer->data = derIssuer.data;
|
|
|
|
issuer->size = derIssuer.len;
|
|
|
|
serial->data = derSerial.data;
|
|
|
|
serial->size = derSerial.len;
|
|
|
|
return PR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
NSS_IMPLEMENT NSSCertificate *
|
|
|
|
nssCertificateStore_FindCertificateByEncodedCertificate (
|
|
|
|
nssCertificateStore *store,
|
|
|
|
NSSDER *encoding
|
|
|
|
)
|
|
|
|
{
|
|
|
|
PRStatus nssrv = PR_FAILURE;
|
|
|
|
NSSDER issuer, serial;
|
|
|
|
NSSCertificate *rvCert = NULL;
|
|
|
|
nssrv = issuer_and_serial_from_encoding(encoding, &issuer, &serial);
|
|
|
|
if (nssrv != PR_SUCCESS) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
rvCert = nssCertificateStore_FindCertificateByIssuerAndSerialNumber(store,
|
|
|
|
&issuer,
|
|
|
|
&serial);
|
|
|
|
PORT_Free(issuer.data);
|
|
|
|
PORT_Free(serial.data);
|
|
|
|
return rvCert;
|
|
|
|
}
|
|
|
|
|
|
|
|
NSS_EXTERN PRStatus
|
|
|
|
nssCertificateStore_AddTrust (
|
|
|
|
nssCertificateStore *store,
|
|
|
|
NSSTrust *trust
|
|
|
|
)
|
|
|
|
{
|
|
|
|
NSSCertificate *cert;
|
|
|
|
certificate_hash_entry *entry;
|
|
|
|
cert = trust->certificate;
|
|
|
|
PZ_Lock(store->lock);
|
|
|
|
entry = (certificate_hash_entry *)
|
|
|
|
nssHash_Lookup(store->issuer_and_serial, cert);
|
|
|
|
if (entry) {
|
|
|
|
entry->trust = nssTrust_AddRef(trust);
|
|
|
|
}
|
|
|
|
PZ_Unlock(store->lock);
|
|
|
|
return (entry) ? PR_SUCCESS : PR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
NSS_IMPLEMENT NSSTrust *
|
|
|
|
nssCertificateStore_FindTrustForCertificate (
|
|
|
|
nssCertificateStore *store,
|
|
|
|
NSSCertificate *cert
|
|
|
|
)
|
|
|
|
{
|
|
|
|
certificate_hash_entry *entry;
|
|
|
|
NSSTrust *rvTrust = NULL;
|
|
|
|
PZ_Lock(store->lock);
|
|
|
|
entry = (certificate_hash_entry *)
|
|
|
|
nssHash_Lookup(store->issuer_and_serial, cert);
|
|
|
|
if (entry && entry->trust) {
|
|
|
|
rvTrust = nssTrust_AddRef(entry->trust);
|
|
|
|
}
|
|
|
|
PZ_Unlock(store->lock);
|
|
|
|
return rvTrust;
|
|
|
|
}
|
|
|
|
|
|
|
|
NSS_EXTERN PRStatus
|
|
|
|
nssCertificateStore_AddSMIMEProfile (
|
|
|
|
nssCertificateStore *store,
|
|
|
|
nssSMIMEProfile *profile
|
|
|
|
)
|
|
|
|
{
|
|
|
|
NSSCertificate *cert;
|
|
|
|
certificate_hash_entry *entry;
|
|
|
|
cert = profile->certificate;
|
|
|
|
PZ_Lock(store->lock);
|
|
|
|
entry = (certificate_hash_entry *)
|
|
|
|
nssHash_Lookup(store->issuer_and_serial, cert);
|
|
|
|
if (entry) {
|
|
|
|
entry->profile = nssSMIMEProfile_AddRef(profile);
|
|
|
|
}
|
|
|
|
PZ_Unlock(store->lock);
|
|
|
|
return (entry) ? PR_SUCCESS : PR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
NSS_IMPLEMENT nssSMIMEProfile *
|
|
|
|
nssCertificateStore_FindSMIMEProfileForCertificate (
|
|
|
|
nssCertificateStore *store,
|
|
|
|
NSSCertificate *cert
|
|
|
|
)
|
|
|
|
{
|
|
|
|
certificate_hash_entry *entry;
|
|
|
|
nssSMIMEProfile *rvProfile = NULL;
|
|
|
|
PZ_Lock(store->lock);
|
|
|
|
entry = (certificate_hash_entry *)
|
|
|
|
nssHash_Lookup(store->issuer_and_serial, cert);
|
|
|
|
if (entry && entry->profile) {
|
|
|
|
rvProfile = nssSMIMEProfile_AddRef(entry->profile);
|
|
|
|
}
|
|
|
|
PZ_Unlock(store->lock);
|
|
|
|
return rvProfile;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX this is also used by cache and should be somewhere else */
|
|
|
|
|
|
|
|
static PLHashNumber
|
|
|
|
nss_certificate_hash (
|
|
|
|
const void *key
|
|
|
|
)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
PLHashNumber h;
|
|
|
|
NSSCertificate *c = (NSSCertificate *)key;
|
|
|
|
h = 0;
|
|
|
|
for (i=0; i<c->issuer.size; i++)
|
|
|
|
h = PR_ROTATE_LEFT32(h, 4) ^ ((unsigned char *)c->issuer.data)[i];
|
|
|
|
for (i=0; i<c->serial.size; i++)
|
|
|
|
h = PR_ROTATE_LEFT32(h, 4) ^ ((unsigned char *)c->serial.data)[i];
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
nss_compare_certs(const void *v1, const void *v2)
|
|
|
|
{
|
|
|
|
PRStatus ignore;
|
|
|
|
NSSCertificate *c1 = (NSSCertificate *)v1;
|
|
|
|
NSSCertificate *c2 = (NSSCertificate *)v2;
|
|
|
|
return (int)(nssItem_Equal(&c1->issuer, &c2->issuer, &ignore) &&
|
|
|
|
nssItem_Equal(&c1->serial, &c2->serial, &ignore));
|
|
|
|
}
|
|
|
|
|
|
|
|
NSS_IMPLEMENT nssHash *
|
|
|
|
nssHash_CreateCertificate (
|
|
|
|
NSSArena *arenaOpt,
|
|
|
|
PRUint32 numBuckets
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return nssHash_Create(arenaOpt,
|
|
|
|
numBuckets,
|
|
|
|
nss_certificate_hash,
|
|
|
|
nss_compare_certs,
|
|
|
|
PL_CompareValues);
|
|
|
|
}
|
|
|
|
|
|
|
|
NSS_IMPLEMENT void
|
|
|
|
nssCertificateStore_DumpStoreInfo (
|
|
|
|
nssCertificateStore *store,
|
|
|
|
void (* cert_dump_iter)(const void *, void *, void *),
|
|
|
|
void *arg
|
|
|
|
)
|
|
|
|
{
|
|
|
|
PZ_Lock(store->lock);
|
|
|
|
nssHash_Iterate(store->issuer_and_serial, cert_dump_iter, arg);
|
|
|
|
PZ_Unlock(store->lock);
|
|
|
|
}
|
|
|
|
|