Back to index

lightning-sunbird  0.9+nobinonly
tdcache.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 #ifdef DEBUG
00038 static const char CVS_ID[] = "@(#) $RCSfile: tdcache.c,v $ $Revision: 1.42.2.3 $ $Date: 2007/11/21 18:22:11 $";
00039 #endif /* DEBUG */
00040 
00041 #ifndef PKIM_H
00042 #include "pkim.h"
00043 #endif /* PKIM_H */
00044 
00045 #ifndef PKIT_H
00046 #include "pkit.h"
00047 #endif /* PKIT_H */
00048 
00049 #ifndef NSSPKI_H
00050 #include "nsspki.h"
00051 #endif /* NSSPKI_H */
00052 
00053 #ifndef PKI_H
00054 #include "pki.h"
00055 #endif /* PKI_H */
00056 
00057 #ifndef NSSBASE_H
00058 #include "nssbase.h"
00059 #endif /* NSSBASE_H */
00060 
00061 #ifndef BASE_H
00062 #include "base.h"
00063 #endif /* BASE_H */
00064 
00065 #include "cert.h"
00066 #include "dev.h"
00067 #include "pki3hack.h"
00068 
00069 #ifdef DEBUG_CACHE
00070 static PRLogModuleInfo *s_log = NULL;
00071 #endif
00072 
00073 #ifdef DEBUG_CACHE
00074 static void log_item_dump(const char *msg, NSSItem *it)
00075 {
00076     char buf[33];
00077     int i, j;
00078     for (i=0; i<10 && i<it->size; i++) {
00079        sprintf(&buf[2*i], "%02X", ((PRUint8 *)it->data)[i]);
00080     }
00081     if (it->size>10) {
00082        sprintf(&buf[2*i], "..");
00083        i += 1;
00084        for (j=it->size-1; i<=16 && j>10; i++, j--) {
00085            sprintf(&buf[2*i], "%02X", ((PRUint8 *)it->data)[j]);
00086        }
00087     }
00088     PR_LOG(s_log, PR_LOG_DEBUG, ("%s: %s", msg, buf));
00089 }
00090 #endif
00091 
00092 #ifdef DEBUG_CACHE
00093 static void log_cert_ref(const char *msg, NSSCertificate *c)
00094 {
00095     PR_LOG(s_log, PR_LOG_DEBUG, ("%s: %s", msg,
00096                            (c->nickname) ? c->nickname : c->email));
00097     log_item_dump("\tserial", &c->serial);
00098     log_item_dump("\tsubject", &c->subject);
00099 }
00100 #endif
00101 
00102 /* Certificate cache routines */
00103 
00104 /* XXX
00105  * Locking is not handled well at all.  A single, global lock with sub-locks
00106  * in the collection types.  Cleanup needed.
00107  */
00108 
00109 /* should it live in its own arena? */
00110 struct nssTDCertificateCacheStr 
00111 {
00112     PZLock *lock;
00113     NSSArena *arena;
00114     nssHash *issuerAndSN;
00115     nssHash *subject;
00116     nssHash *nickname;
00117     nssHash *email;
00118 };
00119 
00120 struct cache_entry_str 
00121 {
00122     union {
00123        NSSCertificate *cert;
00124        nssList *list;
00125        void *value;
00126     } entry;
00127     PRUint32 hits;
00128     PRTime lastHit;
00129     NSSArena *arena;
00130     NSSUTF8 *nickname;
00131 };
00132 
00133 typedef struct cache_entry_str cache_entry;
00134 
00135 static cache_entry *
00136 new_cache_entry(NSSArena *arena, void *value, PRBool ownArena)
00137 {
00138     cache_entry *ce = nss_ZNEW(arena, cache_entry);
00139     if (ce) {
00140        ce->entry.value = value;
00141        ce->hits = 1;
00142        ce->lastHit = PR_Now();
00143        if (ownArena) {
00144            ce->arena = arena;
00145        }
00146        ce->nickname = NULL;
00147     }
00148     return ce;
00149 }
00150 
00151 /* this should not be exposed in a header, but is here to keep the above
00152  * types/functions static
00153  */
00154 NSS_IMPLEMENT PRStatus
00155 nssTrustDomain_InitializeCache (
00156   NSSTrustDomain *td,
00157   PRUint32 cacheSize
00158 )
00159 {
00160     NSSArena *arena;
00161     nssTDCertificateCache *cache = td->cache;
00162 #ifdef DEBUG_CACHE
00163     s_log = PR_NewLogModule("nss_cache");
00164     PR_ASSERT(s_log);
00165 #endif
00166     PR_ASSERT(!cache);
00167     arena = nssArena_Create();
00168     if (!arena) {
00169        return PR_FAILURE;
00170     }
00171     cache = nss_ZNEW(arena, nssTDCertificateCache);
00172     if (!cache) {
00173        nssArena_Destroy(arena);
00174        return PR_FAILURE;
00175     }
00176     cache->lock = PZ_NewLock(nssILockCache);
00177     if (!cache->lock) {
00178        nssArena_Destroy(arena);
00179        return PR_FAILURE;
00180     }
00181     /* Create the issuer and serial DER --> certificate hash */
00182     cache->issuerAndSN = nssHash_CreateCertificate(arena, cacheSize);
00183     if (!cache->issuerAndSN) {
00184        goto loser;
00185     }
00186     /* Create the subject DER --> subject list hash */
00187     cache->subject = nssHash_CreateItem(arena, cacheSize);
00188     if (!cache->subject) {
00189        goto loser;
00190     }
00191     /* Create the nickname --> subject list hash */
00192     cache->nickname = nssHash_CreateString(arena, cacheSize);
00193     if (!cache->nickname) {
00194        goto loser;
00195     }
00196     /* Create the email --> list of subject lists hash */
00197     cache->email = nssHash_CreateString(arena, cacheSize);
00198     if (!cache->email) {
00199        goto loser;
00200     }
00201     cache->arena = arena;
00202     td->cache = cache;
00203 #ifdef DEBUG_CACHE
00204     PR_LOG(s_log, PR_LOG_DEBUG, ("Cache initialized."));
00205 #endif
00206     return PR_SUCCESS;
00207 loser:
00208     PZ_DestroyLock(cache->lock);
00209     nssArena_Destroy(arena);
00210     td->cache = NULL;
00211 #ifdef DEBUG_CACHE
00212     PR_LOG(s_log, PR_LOG_DEBUG, ("Cache initialization failed."));
00213 #endif
00214     return PR_FAILURE;
00215 }
00216 
00217 /* The entries of the hashtable are currently dependent on the certificate(s)
00218  * that produced them.  That is, the entries will be freed when the cert is
00219  * released from the cache.  If there are certs in the cache at any time,
00220  * including shutdown, the hash table entries will hold memory.  In order for
00221  * clean shutdown, it is necessary for there to be no certs in the cache.
00222  */
00223 
00224 extern const NSSError NSS_ERROR_INTERNAL_ERROR;
00225 extern const NSSError NSS_ERROR_BUSY;
00226 
00227 NSS_IMPLEMENT PRStatus
00228 nssTrustDomain_DestroyCache (
00229   NSSTrustDomain *td
00230 )
00231 {
00232     if (!td->cache) {
00233        nss_SetError(NSS_ERROR_INTERNAL_ERROR);
00234        return PR_FAILURE;
00235     }
00236     if (nssHash_Count(td->cache->issuerAndSN) > 0) {
00237        nss_SetError(NSS_ERROR_BUSY);
00238        return PR_FAILURE;
00239     }
00240     PZ_DestroyLock(td->cache->lock);
00241     nssHash_Destroy(td->cache->issuerAndSN);
00242     nssHash_Destroy(td->cache->subject);
00243     nssHash_Destroy(td->cache->nickname);
00244     nssHash_Destroy(td->cache->email);
00245     nssArena_Destroy(td->cache->arena);
00246     td->cache = NULL;
00247 #ifdef DEBUG_CACHE
00248     PR_LOG(s_log, PR_LOG_DEBUG, ("Cache destroyed."));
00249 #endif
00250     return PR_SUCCESS;
00251 }
00252 
00253 static PRStatus
00254 remove_issuer_and_serial_entry (
00255   nssTDCertificateCache *cache,
00256   NSSCertificate *cert
00257 )
00258 {
00259     /* Remove the cert from the issuer/serial hash */
00260     nssHash_Remove(cache->issuerAndSN, cert);
00261 #ifdef DEBUG_CACHE
00262     log_cert_ref("removed issuer/sn", cert);
00263 #endif
00264     return PR_SUCCESS;
00265 }
00266 
00267 static PRStatus
00268 remove_subject_entry (
00269   nssTDCertificateCache *cache,
00270   NSSCertificate *cert,
00271   nssList **subjectList,
00272   NSSUTF8 **nickname,
00273   NSSArena **arena
00274 )
00275 {
00276     PRStatus nssrv;
00277     cache_entry *ce;
00278     *subjectList = NULL;
00279     *arena = NULL;
00280     /* Get the subject list for the cert's subject */
00281     ce = (cache_entry *)nssHash_Lookup(cache->subject, &cert->subject);
00282     if (ce) {
00283        /* Remove the cert from the subject hash */
00284        nssList_Remove(ce->entry.list, cert);
00285        *subjectList = ce->entry.list;
00286        *nickname = ce->nickname;
00287        *arena = ce->arena;
00288        nssrv = PR_SUCCESS;
00289 #ifdef DEBUG_CACHE
00290        log_cert_ref("removed cert", cert);
00291        log_item_dump("from subject list", &cert->subject);
00292 #endif
00293     } else {
00294        nssrv = PR_FAILURE;
00295     }
00296     return nssrv;
00297 }
00298 
00299 static PRStatus
00300 remove_nickname_entry (
00301   nssTDCertificateCache *cache,
00302   NSSUTF8 *nickname,
00303   nssList *subjectList
00304 )
00305 {
00306     PRStatus nssrv;
00307     if (nickname) {
00308        nssHash_Remove(cache->nickname, nickname);
00309        nssrv = PR_SUCCESS;
00310 #ifdef DEBUG_CACHE
00311        PR_LOG(s_log, PR_LOG_DEBUG, ("removed nickname %s", nickname));
00312 #endif
00313     } else {
00314        nssrv = PR_FAILURE;
00315     }
00316     return nssrv;
00317 }
00318 
00319 static PRStatus
00320 remove_email_entry (
00321   nssTDCertificateCache *cache,
00322   NSSCertificate *cert,
00323   nssList *subjectList
00324 )
00325 {
00326     PRStatus nssrv = PR_FAILURE;
00327     cache_entry *ce;
00328     /* Find the subject list in the email hash */
00329     if (cert->email) {
00330        ce = (cache_entry *)nssHash_Lookup(cache->email, cert->email);
00331        if (ce) {
00332            nssList *subjects = ce->entry.list;
00333            /* Remove the subject list from the email hash */
00334            nssList_Remove(subjects, subjectList);
00335 #ifdef DEBUG_CACHE
00336            log_item_dump("removed subject list", &cert->subject);
00337            PR_LOG(s_log, PR_LOG_DEBUG, ("for email %s", cert->email));
00338 #endif
00339            if (nssList_Count(subjects) == 0) {
00340               /* No more subject lists for email, delete list and
00341               * remove hash entry
00342               */
00343               (void)nssList_Destroy(subjects);
00344               nssHash_Remove(cache->email, cert->email);
00345               /* there are no entries left for this address, free space
00346                * used for email entries
00347                */
00348               nssArena_Destroy(ce->arena);
00349 #ifdef DEBUG_CACHE
00350               PR_LOG(s_log, PR_LOG_DEBUG, ("removed email %s", cert->email));
00351 #endif
00352            }
00353            nssrv = PR_SUCCESS;
00354        }
00355     }
00356     return nssrv;
00357 }
00358 
00359 NSS_IMPLEMENT void
00360 nssTrustDomain_RemoveCertFromCacheLOCKED (
00361   NSSTrustDomain *td,
00362   NSSCertificate *cert
00363 )
00364 {
00365     nssList *subjectList;
00366     cache_entry *ce;
00367     NSSArena *arena;
00368     NSSUTF8 *nickname;
00369 
00370 #ifdef DEBUG_CACHE
00371     log_cert_ref("attempt to remove cert", cert);
00372 #endif
00373     ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, cert);
00374     if (!ce || ce->entry.cert != cert) {
00375        /* If it's not in the cache, or a different cert is (this is really
00376         * for safety reasons, though it shouldn't happen), do nothing 
00377         */
00378 #ifdef DEBUG_CACHE
00379        PR_LOG(s_log, PR_LOG_DEBUG, ("but it wasn't in the cache"));
00380 #endif
00381        return;
00382     }
00383     (void)remove_issuer_and_serial_entry(td->cache, cert);
00384     (void)remove_subject_entry(td->cache, cert, &subjectList, 
00385                                &nickname, &arena);
00386     if (nssList_Count(subjectList) == 0) {
00387        (void)remove_nickname_entry(td->cache, nickname, subjectList);
00388        (void)remove_email_entry(td->cache, cert, subjectList);
00389        (void)nssList_Destroy(subjectList);
00390        nssHash_Remove(td->cache->subject, &cert->subject);
00391        /* there are no entries left for this subject, free the space used
00392         * for both the nickname and subject entries
00393         */
00394        if (arena) {
00395            nssArena_Destroy(arena);
00396        }
00397     }
00398 }
00399 
00400 NSS_IMPLEMENT void
00401 nssTrustDomain_LockCertCache (
00402   NSSTrustDomain *td
00403 )
00404 {
00405     PZ_Lock(td->cache->lock);
00406 }
00407 
00408 NSS_IMPLEMENT void
00409 nssTrustDomain_UnlockCertCache (
00410   NSSTrustDomain *td
00411 )
00412 {
00413     PZ_Unlock(td->cache->lock);
00414 }
00415 
00416 struct token_cert_dtor {
00417     NSSToken *token;
00418     nssTDCertificateCache *cache;
00419     NSSCertificate **certs;
00420     PRUint32 numCerts, arrSize;
00421 };
00422 
00423 static void 
00424 remove_token_certs(const void *k, void *v, void *a)
00425 {
00426     NSSCertificate *c = (NSSCertificate *)k;
00427     nssPKIObject *object = &c->object;
00428     struct token_cert_dtor *dtor = a;
00429     PRUint32 i;
00430     nssPKIObject_Lock(object);
00431     for (i=0; i<object->numInstances; i++) {
00432        if (object->instances[i]->token == dtor->token) {
00433            nssCryptokiObject_Destroy(object->instances[i]);
00434            object->instances[i] = object->instances[object->numInstances-1];
00435            object->instances[object->numInstances-1] = NULL;
00436            object->numInstances--;
00437            dtor->certs[dtor->numCerts++] = c;
00438            if (dtor->numCerts == dtor->arrSize) {
00439               dtor->arrSize *= 2;
00440               dtor->certs = nss_ZREALLOCARRAY(dtor->certs, 
00441                                               NSSCertificate *,
00442                                               dtor->arrSize);
00443            }
00444            break;
00445        }
00446     }
00447     nssPKIObject_Unlock(object);
00448     return;
00449 }
00450 
00451 /* 
00452  * Remove all certs for the given token from the cache.  This is
00453  * needed if the token is removed. 
00454  */
00455 NSS_IMPLEMENT PRStatus
00456 nssTrustDomain_RemoveTokenCertsFromCache (
00457   NSSTrustDomain *td,
00458   NSSToken *token
00459 )
00460 {
00461     NSSCertificate **certs;
00462     PRUint32 i, arrSize = 10;
00463     struct token_cert_dtor dtor;
00464     certs = nss_ZNEWARRAY(NULL, NSSCertificate *, arrSize);
00465     if (!certs) {
00466        return PR_FAILURE;
00467     }
00468     dtor.cache = td->cache;
00469     dtor.token = token;
00470     dtor.certs = certs;
00471     dtor.numCerts = 0;
00472     dtor.arrSize = arrSize;
00473     PZ_Lock(td->cache->lock);
00474     nssHash_Iterate(td->cache->issuerAndSN, remove_token_certs, (void *)&dtor);
00475     for (i=0; i<dtor.numCerts; i++) {
00476        if (dtor.certs[i]->object.numInstances == 0) {
00477            nssTrustDomain_RemoveCertFromCacheLOCKED(td, dtor.certs[i]);
00478            dtor.certs[i] = NULL;  /* skip this cert in the second for loop */
00479        }
00480     }
00481     PZ_Unlock(td->cache->lock);
00482     for (i=0; i<dtor.numCerts; i++) {
00483        if (dtor.certs[i]) {
00484            STAN_ForceCERTCertificateUpdate(dtor.certs[i]);
00485        }
00486     }
00487     nss_ZFreeIf(dtor.certs);
00488     return PR_SUCCESS;
00489 }
00490 
00491 NSS_IMPLEMENT PRStatus
00492 nssTrustDomain_UpdateCachedTokenCerts (
00493   NSSTrustDomain *td,
00494   NSSToken *token
00495 )
00496 {
00497     NSSCertificate **cp, **cached = NULL;
00498     nssList *certList;
00499     PRUint32 count;
00500     certList = nssList_Create(NULL, PR_FALSE);
00501     if (!certList) return PR_FAILURE;
00502     (void *)nssTrustDomain_GetCertsFromCache(td, certList);
00503     count = nssList_Count(certList);
00504     if (count > 0) {
00505        cached = nss_ZNEWARRAY(NULL, NSSCertificate *, count + 1);
00506        if (!cached) {
00507            return PR_FAILURE;
00508        }
00509        nssList_GetArray(certList, (void **)cached, count);
00510        nssList_Destroy(certList);
00511        for (cp = cached; *cp; cp++) {
00512            nssCryptokiObject *instance;
00513            NSSCertificate *c = *cp;
00514            nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly;
00515            instance = nssToken_FindCertificateByIssuerAndSerialNumber(
00516                                                               token,
00517                                                                NULL,
00518                                                                &c->issuer,
00519                                                                &c->serial,
00520                                                                tokenOnly,
00521                                                                NULL);
00522            if (instance) {
00523               nssPKIObject_AddInstance(&c->object, instance);
00524               STAN_ForceCERTCertificateUpdate(c);
00525            }
00526        }
00527        nssCertificateArray_Destroy(cached);
00528     }
00529     return PR_SUCCESS;
00530 }
00531 
00532 static PRStatus
00533 add_issuer_and_serial_entry (
00534   NSSArena *arena,
00535   nssTDCertificateCache *cache, 
00536   NSSCertificate *cert
00537 )
00538 {
00539     cache_entry *ce;
00540     ce = new_cache_entry(arena, (void *)cert, PR_FALSE);
00541 #ifdef DEBUG_CACHE
00542     log_cert_ref("added to issuer/sn", cert);
00543 #endif
00544     return nssHash_Add(cache->issuerAndSN, cert, (void *)ce);
00545 }
00546 
00547 static PRStatus
00548 add_subject_entry (
00549   NSSArena *arena,
00550   nssTDCertificateCache *cache, 
00551   NSSCertificate *cert,
00552   NSSUTF8 *nickname,
00553   nssList **subjectList
00554 )
00555 {
00556     PRStatus nssrv;
00557     nssList *list;
00558     cache_entry *ce;
00559     *subjectList = NULL;  /* this is only set if a new one is created */
00560     ce = (cache_entry *)nssHash_Lookup(cache->subject, &cert->subject);
00561     if (ce) {
00562        ce->hits++;
00563        ce->lastHit = PR_Now();
00564        /* The subject is already in, add this cert to the list */
00565        nssrv = nssList_AddUnique(ce->entry.list, cert);
00566 #ifdef DEBUG_CACHE
00567        log_cert_ref("added to existing subject list", cert);
00568 #endif
00569     } else {
00570        NSSDER *subject;
00571        /* Create a new subject list for the subject */
00572        list = nssList_Create(arena, PR_FALSE);
00573        if (!list) {
00574            return PR_FAILURE;
00575        }
00576        ce = new_cache_entry(arena, (void *)list, PR_TRUE);
00577        if (!ce) {
00578            return PR_FAILURE;
00579        }
00580        if (nickname) {
00581            ce->nickname = nssUTF8_Duplicate(nickname, arena);
00582        }
00583        nssList_SetSortFunction(list, nssCertificate_SubjectListSort);
00584        /* Add the cert entry to this list of subjects */
00585        nssrv = nssList_AddUnique(list, cert);
00586        if (nssrv != PR_SUCCESS) {
00587            return nssrv;
00588        }
00589        /* Add the subject list to the cache */
00590        subject = nssItem_Duplicate(&cert->subject, arena, NULL);
00591        if (!subject) {
00592            return PR_FAILURE;
00593        }
00594        nssrv = nssHash_Add(cache->subject, subject, ce);
00595        if (nssrv != PR_SUCCESS) {
00596            return nssrv;
00597        }
00598        *subjectList = list;
00599 #ifdef DEBUG_CACHE
00600        log_cert_ref("created subject list", cert);
00601 #endif
00602     }
00603     return nssrv;
00604 }
00605 
00606 static PRStatus
00607 add_nickname_entry (
00608   NSSArena *arena,
00609   nssTDCertificateCache *cache, 
00610   NSSUTF8 *certNickname,
00611   nssList *subjectList
00612 )
00613 {
00614     PRStatus nssrv = PR_SUCCESS;
00615     cache_entry *ce;
00616     ce = (cache_entry *)nssHash_Lookup(cache->nickname, certNickname);
00617     if (ce) {
00618        /* This is a collision.  A nickname entry already exists for this
00619         * subject, but a subject entry didn't.  This would imply there are
00620         * two subjects using the same nickname, which is not allowed.
00621         */
00622        return PR_FAILURE;
00623     } else {
00624        NSSUTF8 *nickname;
00625        ce = new_cache_entry(arena, subjectList, PR_FALSE);
00626        if (!ce) {
00627            return PR_FAILURE;
00628        }
00629        nickname = nssUTF8_Duplicate(certNickname, arena);
00630        if (!nickname) {
00631            return PR_FAILURE;
00632        }
00633        nssrv = nssHash_Add(cache->nickname, nickname, ce);
00634 #ifdef DEBUG_CACHE
00635        log_cert_ref("created nickname for", cert);
00636 #endif
00637     }
00638     return nssrv;
00639 }
00640 
00641 static PRStatus
00642 add_email_entry (
00643   nssTDCertificateCache *cache, 
00644   NSSCertificate *cert,
00645   nssList *subjectList
00646 )
00647 {
00648     PRStatus nssrv = PR_SUCCESS;
00649     nssList *subjects;
00650     cache_entry *ce;
00651     ce = (cache_entry *)nssHash_Lookup(cache->email, cert->email);
00652     if (ce) {
00653        /* Already have an entry for this email address, but not subject */
00654        subjects = ce->entry.list;
00655        nssrv = nssList_AddUnique(subjects, subjectList);
00656        ce->hits++;
00657        ce->lastHit = PR_Now();
00658 #ifdef DEBUG_CACHE
00659        log_cert_ref("added subject to email for", cert);
00660 #endif
00661     } else {
00662        NSSASCII7 *email;
00663        NSSArena *arena;
00664        arena = nssArena_Create();
00665        if (!arena) {
00666            return PR_FAILURE;
00667        }
00668        /* Create a new list of subject lists, add this subject */
00669        subjects = nssList_Create(arena, PR_TRUE);
00670        if (!subjects) {
00671            nssArena_Destroy(arena);
00672            return PR_FAILURE;
00673        }
00674        /* Add the new subject to the list */
00675        nssrv = nssList_AddUnique(subjects, subjectList);
00676        if (nssrv != PR_SUCCESS) {
00677            nssArena_Destroy(arena);
00678            return nssrv;
00679        }
00680        /* Add the new entry to the cache */
00681        ce = new_cache_entry(arena, (void *)subjects, PR_TRUE);
00682        if (!ce) {
00683            nssArena_Destroy(arena);
00684            return PR_FAILURE;
00685        }
00686        email = nssUTF8_Duplicate(cert->email, arena);
00687        if (!email) {
00688            nssArena_Destroy(arena);
00689            return PR_FAILURE;
00690        }
00691        nssrv = nssHash_Add(cache->email, email, ce);
00692        if (nssrv != PR_SUCCESS) {
00693            nssArena_Destroy(arena);
00694            return nssrv;
00695        }
00696 #ifdef DEBUG_CACHE
00697        log_cert_ref("created email for", cert);
00698 #endif
00699     }
00700     return nssrv;
00701 }
00702 
00703 extern const NSSError NSS_ERROR_CERTIFICATE_IN_CACHE;
00704 
00705 static void
00706 remove_object_instances (
00707   nssPKIObject *object,
00708   nssCryptokiObject **instances,
00709   int numInstances
00710 )
00711 {
00712     int i;
00713 
00714     for (i = 0; i < numInstances; i++) {
00715        nssPKIObject_RemoveInstanceForToken(object, instances[i]->token);
00716     }
00717 }
00718 
00719 static SECStatus
00720 merge_object_instances (
00721   nssPKIObject *to,
00722   nssPKIObject *from
00723 )
00724 {
00725     nssCryptokiObject **instances, **ci;
00726     int i;
00727     SECStatus rv = SECSuccess;
00728 
00729     instances = nssPKIObject_GetInstances(from);
00730     if (instances == NULL) {
00731        return SECFailure;
00732     }
00733     for (ci = instances, i = 0; *ci; ci++, i++) {
00734        nssCryptokiObject *instance = nssCryptokiObject_Clone(*ci);
00735        if (instance) {
00736            if (nssPKIObject_AddInstance(to, instance) == SECSuccess) {
00737               continue;
00738            }
00739            nssCryptokiObject_Destroy(instance);
00740        }
00741        remove_object_instances(to, instances, i);
00742        rv = SECFailure;
00743        break;
00744     }
00745     nssCryptokiObjectArray_Destroy(instances);
00746     return rv;
00747 }
00748 
00749 static NSSCertificate *
00750 add_cert_to_cache (
00751   NSSTrustDomain *td, 
00752   NSSCertificate *cert
00753 )
00754 {
00755     NSSArena *arena = NULL;
00756     nssList *subjectList = NULL;
00757     PRStatus nssrv;
00758     PRUint32 added = 0;
00759     cache_entry *ce;
00760     NSSCertificate *rvCert = NULL;
00761     NSSUTF8 *certNickname = nssCertificate_GetNickname(cert, NULL);
00762 
00763     PZ_Lock(td->cache->lock);
00764     /* If it exists in the issuer/serial hash, it's already in all */
00765     ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, cert);
00766     if (ce) {
00767        ce->hits++;
00768        ce->lastHit = PR_Now();
00769        rvCert = nssCertificate_AddRef(ce->entry.cert);
00770 #ifdef DEBUG_CACHE
00771        log_cert_ref("attempted to add cert already in cache", cert);
00772 #endif
00773        PZ_Unlock(td->cache->lock);
00774        /* collision - somebody else already added the cert
00775         * to the cache before this thread got around to it.
00776         */
00777        /* merge the instances of the cert */
00778        if (merge_object_instances(&rvCert->object, &cert->object)
00779                                                  != SECSuccess) {
00780            nssCertificate_Destroy(rvCert);
00781            return NULL;
00782        }
00783        STAN_ForceCERTCertificateUpdate(rvCert);
00784        nssCertificate_Destroy(cert);
00785        return rvCert;
00786     }
00787     /* create a new cache entry for this cert within the cert's arena*/
00788     nssrv = add_issuer_and_serial_entry(cert->object.arena, td->cache, cert);
00789     if (nssrv != PR_SUCCESS) {
00790        goto loser;
00791     }
00792     added++;
00793     /* create an arena for the nickname and subject entries */
00794     arena = nssArena_Create();
00795     if (!arena) {
00796        goto loser;
00797     }
00798     /* create a new subject list for this cert, or add to existing */
00799     nssrv = add_subject_entry(arena, td->cache, cert, 
00800                                           certNickname, &subjectList);
00801     if (nssrv != PR_SUCCESS) {
00802        goto loser;
00803     }
00804     added++;
00805     /* If a new subject entry was created, also need nickname and/or email */
00806     if (subjectList != NULL) {
00807        PRBool handle = PR_FALSE;
00808        if (certNickname) {
00809            nssrv = add_nickname_entry(arena, td->cache, 
00810                                           certNickname, subjectList);
00811            if (nssrv != PR_SUCCESS) {
00812               goto loser;
00813            }
00814            handle = PR_TRUE;
00815            added++;
00816        }
00817        if (cert->email) {
00818            nssrv = add_email_entry(td->cache, cert, subjectList);
00819            if (nssrv != PR_SUCCESS) {
00820               goto loser;
00821            }
00822            handle = PR_TRUE;
00823            added += 2;
00824        }
00825 #ifdef nodef
00826        /* I think either a nickname or email address must be associated
00827         * with the cert.  However, certs are passed to NewTemp without
00828         * either.  This worked in the old code, so it must work now.
00829         */
00830        if (!handle) {
00831            /* Require either nickname or email handle */
00832            nssrv = PR_FAILURE;
00833            goto loser;
00834        }
00835 #endif
00836     }
00837     rvCert = cert;
00838     PZ_Unlock(td->cache->lock);
00839     return rvCert;
00840 loser:
00841     /* Remove any handles that have been created */
00842     subjectList = NULL;
00843     if (added >= 1) {
00844        (void)remove_issuer_and_serial_entry(td->cache, cert);
00845     }
00846     if (added >= 2) {
00847        (void)remove_subject_entry(td->cache, cert, &subjectList, 
00848                                           &certNickname, &arena);
00849     }
00850     if (added == 3 || added == 5) {
00851        (void)remove_nickname_entry(td->cache, certNickname, subjectList);
00852     }
00853     if (added >= 4) {
00854        (void)remove_email_entry(td->cache, cert, subjectList);
00855     }
00856     if (subjectList) {
00857        nssHash_Remove(td->cache->subject, &cert->subject);
00858        nssList_Destroy(subjectList);
00859     }
00860     if (arena) {
00861        nssArena_Destroy(arena);
00862     }
00863     PZ_Unlock(td->cache->lock);
00864     return NULL;
00865 }
00866 
00867 NSS_IMPLEMENT PRStatus
00868 nssTrustDomain_AddCertsToCache (
00869   NSSTrustDomain *td,
00870   NSSCertificate **certs,
00871   PRUint32 numCerts
00872 )
00873 {
00874     PRUint32 i;
00875     NSSCertificate *c;
00876     for (i=0; i<numCerts && certs[i]; i++) {
00877        c = add_cert_to_cache(td, certs[i]);
00878        if (c == NULL) {
00879            return PR_FAILURE;
00880        } else {
00881            certs[i] = c;
00882        }
00883     }
00884     return PR_SUCCESS;
00885 }
00886 
00887 static NSSCertificate **
00888 collect_subject_certs (
00889   nssList *subjectList,
00890   nssList *rvCertListOpt
00891 )
00892 {
00893     NSSCertificate *c;
00894     NSSCertificate **rvArray = NULL;
00895     PRUint32 count;
00896     nssCertificateList_AddReferences(subjectList);
00897     if (rvCertListOpt) {
00898        nssListIterator *iter = nssList_CreateIterator(subjectList);
00899        for (c  = (NSSCertificate *)nssListIterator_Start(iter);
00900             c != (NSSCertificate *)NULL;
00901             c  = (NSSCertificate *)nssListIterator_Next(iter)) {
00902            nssList_Add(rvCertListOpt, c);
00903        }
00904        nssListIterator_Finish(iter);
00905        nssListIterator_Destroy(iter);
00906     } else {
00907        count = nssList_Count(subjectList);
00908        rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count + 1);
00909        if (!rvArray) {
00910            return (NSSCertificate **)NULL;
00911        }
00912        nssList_GetArray(subjectList, (void **)rvArray, count);
00913     }
00914     return rvArray;
00915 }
00916 
00917 /*
00918  * Find all cached certs with this subject.
00919  */
00920 NSS_IMPLEMENT NSSCertificate **
00921 nssTrustDomain_GetCertsForSubjectFromCache (
00922   NSSTrustDomain *td,
00923   NSSDER *subject,
00924   nssList *certListOpt
00925 )
00926 {
00927     NSSCertificate **rvArray = NULL;
00928     cache_entry *ce;
00929 #ifdef DEBUG_CACHE
00930     log_item_dump("looking for cert by subject", subject);
00931 #endif
00932     PZ_Lock(td->cache->lock);
00933     ce = (cache_entry *)nssHash_Lookup(td->cache->subject, subject);
00934     if (ce) {
00935        ce->hits++;
00936        ce->lastHit = PR_Now();
00937 #ifdef DEBUG_CACHE
00938        PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits));
00939 #endif
00940        rvArray = collect_subject_certs(ce->entry.list, certListOpt);
00941     }
00942     PZ_Unlock(td->cache->lock);
00943     return rvArray;
00944 }
00945 
00946 /*
00947  * Find all cached certs with this label.
00948  */
00949 NSS_IMPLEMENT NSSCertificate **
00950 nssTrustDomain_GetCertsForNicknameFromCache (
00951   NSSTrustDomain *td,
00952   NSSUTF8 *nickname,
00953   nssList *certListOpt
00954 )
00955 {
00956     NSSCertificate **rvArray = NULL;
00957     cache_entry *ce;
00958 #ifdef DEBUG_CACHE
00959     PR_LOG(s_log, PR_LOG_DEBUG, ("looking for cert by nick %s", nickname));
00960 #endif
00961     PZ_Lock(td->cache->lock);
00962     ce = (cache_entry *)nssHash_Lookup(td->cache->nickname, nickname);
00963     if (ce) {
00964        ce->hits++;
00965        ce->lastHit = PR_Now();
00966 #ifdef DEBUG_CACHE
00967        PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits));
00968 #endif
00969        rvArray = collect_subject_certs(ce->entry.list, certListOpt);
00970     }
00971     PZ_Unlock(td->cache->lock);
00972     return rvArray;
00973 }
00974 
00975 /*
00976  * Find all cached certs with this email address.
00977  */
00978 NSS_IMPLEMENT NSSCertificate **
00979 nssTrustDomain_GetCertsForEmailAddressFromCache (
00980   NSSTrustDomain *td,
00981   NSSASCII7 *email,
00982   nssList *certListOpt
00983 )
00984 {
00985     NSSCertificate **rvArray = NULL;
00986     cache_entry *ce;
00987     nssList *collectList = NULL;
00988     nssListIterator *iter = NULL;
00989     nssList *subjectList;
00990 #ifdef DEBUG_CACHE
00991     PR_LOG(s_log, PR_LOG_DEBUG, ("looking for cert by email %s", email));
00992 #endif
00993     PZ_Lock(td->cache->lock);
00994     ce = (cache_entry *)nssHash_Lookup(td->cache->email, email);
00995     if (ce) {
00996        ce->hits++;
00997        ce->lastHit = PR_Now();
00998 #ifdef DEBUG_CACHE
00999        PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits));
01000 #endif
01001        /* loop over subject lists and get refs for certs */
01002        if (certListOpt) {
01003            collectList = certListOpt;
01004        } else {
01005            collectList = nssList_Create(NULL, PR_FALSE);
01006            if (!collectList) {
01007               PZ_Unlock(td->cache->lock);
01008               return NULL;
01009            }
01010        }
01011        iter = nssList_CreateIterator(ce->entry.list);
01012        if (!iter) {
01013            PZ_Unlock(td->cache->lock);
01014            if (!certListOpt) {
01015               nssList_Destroy(collectList);
01016            }
01017            return NULL;
01018        }
01019        for (subjectList  = (nssList *)nssListIterator_Start(iter);
01020             subjectList != (nssList *)NULL;
01021             subjectList  = (nssList *)nssListIterator_Next(iter)) {
01022            (void)collect_subject_certs(subjectList, collectList);
01023        }
01024        nssListIterator_Finish(iter);
01025        nssListIterator_Destroy(iter);
01026     }
01027     PZ_Unlock(td->cache->lock);
01028     if (!certListOpt && collectList) {
01029        PRUint32 count = nssList_Count(collectList);
01030        rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count);
01031        if (rvArray) {
01032            nssList_GetArray(collectList, (void **)rvArray, count);
01033        }
01034        nssList_Destroy(collectList);
01035     }
01036     return rvArray;
01037 }
01038 
01039 /*
01040  * Look for a specific cert in the cache
01041  */
01042 NSS_IMPLEMENT NSSCertificate *
01043 nssTrustDomain_GetCertForIssuerAndSNFromCache (
01044   NSSTrustDomain *td,
01045   NSSDER *issuer,
01046   NSSDER *serial
01047 )
01048 {
01049     NSSCertificate certkey;
01050     NSSCertificate *rvCert = NULL;
01051     cache_entry *ce;
01052     certkey.issuer.data = issuer->data;
01053     certkey.issuer.size = issuer->size;
01054     certkey.serial.data = serial->data;
01055     certkey.serial.size = serial->size;
01056 #ifdef DEBUG_CACHE
01057     log_item_dump("looking for cert by issuer/sn, issuer", issuer);
01058     log_item_dump("                               serial", serial);
01059 #endif
01060     PZ_Lock(td->cache->lock);
01061     ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, &certkey);
01062     if (ce) {
01063        ce->hits++;
01064        ce->lastHit = PR_Now();
01065        rvCert = nssCertificate_AddRef(ce->entry.cert);
01066 #ifdef DEBUG_CACHE
01067        PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits));
01068 #endif
01069     }
01070     PZ_Unlock(td->cache->lock);
01071     return rvCert;
01072 }
01073 
01074 static PRStatus
01075 issuer_and_serial_from_encoding (
01076   NSSBER *encoding, 
01077   NSSDER *issuer, 
01078   NSSDER *serial
01079 )
01080 {
01081     SECItem derCert, derIssuer, derSerial;
01082     SECStatus secrv;
01083     derCert.data = (unsigned char *)encoding->data;
01084     derCert.len = encoding->size;
01085     secrv = CERT_IssuerNameFromDERCert(&derCert, &derIssuer);
01086     if (secrv != SECSuccess) {
01087        return PR_FAILURE;
01088     }
01089     secrv = CERT_SerialNumberFromDERCert(&derCert, &derSerial);
01090     if (secrv != SECSuccess) {
01091        return PR_FAILURE;
01092     }
01093     issuer->data = derIssuer.data;
01094     issuer->size = derIssuer.len;
01095     serial->data = derSerial.data;
01096     serial->size = derSerial.len;
01097     return PR_SUCCESS;
01098 }
01099 
01100 /*
01101  * Look for a specific cert in the cache
01102  */
01103 NSS_IMPLEMENT NSSCertificate *
01104 nssTrustDomain_GetCertByDERFromCache (
01105   NSSTrustDomain *td,
01106   NSSDER *der
01107 )
01108 {
01109     PRStatus nssrv = PR_FAILURE;
01110     NSSDER issuer, serial;
01111     NSSCertificate *rvCert;
01112     nssrv = issuer_and_serial_from_encoding(der, &issuer, &serial);
01113     if (nssrv != PR_SUCCESS) {
01114        return NULL;
01115     }
01116 #ifdef DEBUG_CACHE
01117     log_item_dump("looking for cert by DER", der);
01118 #endif
01119     rvCert = nssTrustDomain_GetCertForIssuerAndSNFromCache(td, 
01120                                                            &issuer, &serial);
01121     PORT_Free(issuer.data);
01122     PORT_Free(serial.data);
01123     return rvCert;
01124 }
01125 
01126 static void cert_iter(const void *k, void *v, void *a)
01127 {
01128     nssList *certList = (nssList *)a;
01129     NSSCertificate *c = (NSSCertificate *)k;
01130     nssList_Add(certList, nssCertificate_AddRef(c));
01131 }
01132 
01133 NSS_EXTERN NSSCertificate **
01134 nssTrustDomain_GetCertsFromCache (
01135   NSSTrustDomain *td,
01136   nssList *certListOpt
01137 )
01138 {
01139     NSSCertificate **rvArray = NULL;
01140     nssList *certList;
01141     if (certListOpt) {
01142        certList = certListOpt;
01143     } else {
01144        certList = nssList_Create(NULL, PR_FALSE);
01145        if (!certList) {
01146            return NULL;
01147        }
01148     }
01149     PZ_Lock(td->cache->lock);
01150     nssHash_Iterate(td->cache->issuerAndSN, cert_iter, (void *)certList);
01151     PZ_Unlock(td->cache->lock);
01152     if (!certListOpt) {
01153        PRUint32 count = nssList_Count(certList);
01154        rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count);
01155        nssList_GetArray(certList, (void **)rvArray, count);
01156        /* array takes the references */
01157        nssList_Destroy(certList);
01158     }
01159     return rvArray;
01160 }
01161 
01162 NSS_IMPLEMENT void
01163 nssTrustDomain_DumpCacheInfo (
01164   NSSTrustDomain *td,
01165   void (* cert_dump_iter)(const void *, void *, void *),
01166   void *arg
01167 )
01168 {
01169     PZ_Lock(td->cache->lock);
01170     nssHash_Iterate(td->cache->issuerAndSN, cert_dump_iter, arg);
01171     PZ_Unlock(td->cache->lock);
01172 }