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