tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

tdcache.c (32103B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #ifndef PKIM_H
      6 #include "pkim.h"
      7 #endif /* PKIM_H */
      8 
      9 #ifndef PKIT_H
     10 #include "pkit.h"
     11 #endif /* PKIT_H */
     12 
     13 #ifndef NSSPKI_H
     14 #include "nsspki.h"
     15 #endif /* NSSPKI_H */
     16 
     17 #ifndef PKI_H
     18 #include "pki.h"
     19 #endif /* PKI_H */
     20 
     21 #ifndef NSSBASE_H
     22 #include "nssbase.h"
     23 #endif /* NSSBASE_H */
     24 
     25 #ifndef BASE_H
     26 #include "base.h"
     27 #endif /* BASE_H */
     28 
     29 #include "cert.h"
     30 #include "dev.h"
     31 #include "pki3hack.h"
     32 
     33 #ifdef DEBUG_CACHE
     34 static PRLogModuleInfo *s_log = NULL;
     35 #endif
     36 
     37 #ifdef DEBUG_CACHE
     38 static void
     39 log_item_dump(const char *msg, NSSItem *it)
     40 {
     41    char buf[33];
     42    int i, j;
     43    for (i = 0; i < 10 && i < it->size; i++) {
     44        snprintf(&buf[2 * i], sizeof(buf) - 2 * i, "%02X", ((PRUint8 *)it->data)[i]);
     45    }
     46    if (it->size > 10) {
     47        snprintf(&buf[2 * i], sizeof(buf) - 2 * i, "..");
     48        i += 1;
     49        for (j = it->size - 1; i <= 16 && j > 10; i++, j--) {
     50            snprintf(&buf[2 * i], sizeof(buf) - 2 * i, "%02X", ((PRUint8 *)it->data)[j]);
     51        }
     52    }
     53    PR_LOG(s_log, PR_LOG_DEBUG, ("%s: %s", msg, buf));
     54 }
     55 #endif
     56 
     57 #ifdef DEBUG_CACHE
     58 static void
     59 log_cert_ref(const char *msg, NSSCertificate *c)
     60 {
     61    PR_LOG(s_log, PR_LOG_DEBUG, ("%s: %s", msg, (c->nickname) ? c->nickname : c->email));
     62    log_item_dump("\tserial", &c->serial);
     63    log_item_dump("\tsubject", &c->subject);
     64 }
     65 #endif
     66 
     67 /* Certificate cache routines */
     68 
     69 /* XXX
     70 * Locking is not handled well at all.  A single, global lock with sub-locks
     71 * in the collection types.  Cleanup needed.
     72 */
     73 
     74 /* should it live in its own arena? */
     75 struct nssTDCertificateCacheStr {
     76    PZLock *lock; /* Must not be held when calling nssSlot_IsTokenPresent. See bug 1625791. */
     77    NSSArena *arena;
     78    nssHash *issuerAndSN;
     79    nssHash *subject;
     80    nssHash *nickname;
     81    nssHash *email;
     82 };
     83 
     84 struct cache_entry_str {
     85    union {
     86        NSSCertificate *cert;
     87        nssList *list;
     88        void *value;
     89    } entry;
     90    PRUint32 hits;
     91    PRTime lastHit;
     92    NSSArena *arena;
     93    NSSUTF8 *nickname;
     94 };
     95 
     96 typedef struct cache_entry_str cache_entry;
     97 
     98 static cache_entry *
     99 new_cache_entry(NSSArena *arena, void *value, PRBool ownArena)
    100 {
    101    cache_entry *ce = nss_ZNEW(arena, cache_entry);
    102    if (ce) {
    103        ce->entry.value = value;
    104        ce->hits = 1;
    105        ce->lastHit = PR_Now();
    106        if (ownArena) {
    107            ce->arena = arena;
    108        }
    109        ce->nickname = NULL;
    110    }
    111    return ce;
    112 }
    113 
    114 /* this should not be exposed in a header, but is here to keep the above
    115 * types/functions static
    116 */
    117 NSS_IMPLEMENT PRStatus
    118 nssTrustDomain_InitializeCache(
    119    NSSTrustDomain *td,
    120    PRUint32 cacheSize)
    121 {
    122    NSSArena *arena;
    123    nssTDCertificateCache *cache = td->cache;
    124 #ifdef DEBUG_CACHE
    125    s_log = PR_NewLogModule("nss_cache");
    126    PR_ASSERT(s_log);
    127 #endif
    128    PR_ASSERT(!cache);
    129    arena = nssArena_Create();
    130    if (!arena) {
    131        return PR_FAILURE;
    132    }
    133    cache = nss_ZNEW(arena, nssTDCertificateCache);
    134    if (!cache) {
    135        nssArena_Destroy(arena);
    136        return PR_FAILURE;
    137    }
    138    cache->lock = PZ_NewLock(nssILockCache);
    139    if (!cache->lock) {
    140        nssArena_Destroy(arena);
    141        return PR_FAILURE;
    142    }
    143    /* Create the issuer and serial DER --> certificate hash */
    144    cache->issuerAndSN = nssHash_CreateCertificate(arena, cacheSize);
    145    if (!cache->issuerAndSN) {
    146        goto loser;
    147    }
    148    /* Create the subject DER --> subject list hash */
    149    cache->subject = nssHash_CreateItem(arena, cacheSize);
    150    if (!cache->subject) {
    151        goto loser;
    152    }
    153    /* Create the nickname --> subject list hash */
    154    cache->nickname = nssHash_CreateString(arena, cacheSize);
    155    if (!cache->nickname) {
    156        goto loser;
    157    }
    158    /* Create the email --> list of subject lists hash */
    159    cache->email = nssHash_CreateString(arena, cacheSize);
    160    if (!cache->email) {
    161        goto loser;
    162    }
    163    cache->arena = arena;
    164    td->cache = cache;
    165 #ifdef DEBUG_CACHE
    166    PR_LOG(s_log, PR_LOG_DEBUG, ("Cache initialized."));
    167 #endif
    168    return PR_SUCCESS;
    169 loser:
    170    PZ_DestroyLock(cache->lock);
    171    nssArena_Destroy(arena);
    172    td->cache = NULL;
    173 #ifdef DEBUG_CACHE
    174    PR_LOG(s_log, PR_LOG_DEBUG, ("Cache initialization failed."));
    175 #endif
    176    return PR_FAILURE;
    177 }
    178 
    179 /* The entries of the hashtable are currently dependent on the certificate(s)
    180 * that produced them.  That is, the entries will be freed when the cert is
    181 * released from the cache.  If there are certs in the cache at any time,
    182 * including shutdown, the hash table entries will hold memory.  In order for
    183 * clean shutdown, it is necessary for there to be no certs in the cache.
    184 */
    185 
    186 extern const NSSError NSS_ERROR_INTERNAL_ERROR;
    187 extern const NSSError NSS_ERROR_BUSY;
    188 
    189 NSS_IMPLEMENT PRStatus
    190 nssTrustDomain_DestroyCache(NSSTrustDomain *td)
    191 {
    192    if (!td->cache) {
    193        nss_SetError(NSS_ERROR_INTERNAL_ERROR);
    194        return PR_FAILURE;
    195    }
    196    if (nssHash_Count(td->cache->issuerAndSN) > 0) {
    197        nss_SetError(NSS_ERROR_BUSY);
    198        return PR_FAILURE;
    199    }
    200    PZ_DestroyLock(td->cache->lock);
    201    nssHash_Destroy(td->cache->issuerAndSN);
    202    nssHash_Destroy(td->cache->subject);
    203    nssHash_Destroy(td->cache->nickname);
    204    nssHash_Destroy(td->cache->email);
    205    nssArena_Destroy(td->cache->arena);
    206    td->cache = NULL;
    207 #ifdef DEBUG_CACHE
    208    PR_LOG(s_log, PR_LOG_DEBUG, ("Cache destroyed."));
    209 #endif
    210    return PR_SUCCESS;
    211 }
    212 
    213 static PRStatus
    214 remove_issuer_and_serial_entry(
    215    nssTDCertificateCache *cache,
    216    NSSCertificate *cert)
    217 {
    218    /* Remove the cert from the issuer/serial hash */
    219    nssHash_Remove(cache->issuerAndSN, cert);
    220 #ifdef DEBUG_CACHE
    221    log_cert_ref("removed issuer/sn", cert);
    222 #endif
    223    return PR_SUCCESS;
    224 }
    225 
    226 static PRStatus
    227 remove_subject_entry(
    228    nssTDCertificateCache *cache,
    229    NSSCertificate *cert,
    230    nssList **subjectList,
    231    NSSUTF8 **nickname,
    232    NSSArena **arena)
    233 {
    234    PRStatus nssrv;
    235    cache_entry *ce;
    236    *subjectList = NULL;
    237    *arena = NULL;
    238    /* Get the subject list for the cert's subject */
    239    ce = (cache_entry *)nssHash_Lookup(cache->subject, &cert->subject);
    240    if (ce) {
    241        /* Remove the cert from the subject hash */
    242        nssList_Remove(ce->entry.list, cert);
    243        *subjectList = ce->entry.list;
    244        *nickname = ce->nickname;
    245        *arena = ce->arena;
    246        nssrv = PR_SUCCESS;
    247 #ifdef DEBUG_CACHE
    248        log_cert_ref("removed cert", cert);
    249        log_item_dump("from subject list", &cert->subject);
    250 #endif
    251    } else {
    252        nssrv = PR_FAILURE;
    253    }
    254    return nssrv;
    255 }
    256 
    257 static PRStatus
    258 remove_nickname_entry(
    259    nssTDCertificateCache *cache,
    260    NSSUTF8 *nickname,
    261    nssList *subjectList)
    262 {
    263    PRStatus nssrv;
    264    if (nickname) {
    265        nssHash_Remove(cache->nickname, nickname);
    266        nssrv = PR_SUCCESS;
    267 #ifdef DEBUG_CACHE
    268        PR_LOG(s_log, PR_LOG_DEBUG, ("removed nickname %s", nickname));
    269 #endif
    270    } else {
    271        nssrv = PR_FAILURE;
    272    }
    273    return nssrv;
    274 }
    275 
    276 static PRStatus
    277 remove_email_entry(
    278    nssTDCertificateCache *cache,
    279    NSSCertificate *cert,
    280    nssList *subjectList)
    281 {
    282    PRStatus nssrv = PR_FAILURE;
    283    cache_entry *ce;
    284    /* Find the subject list in the email hash */
    285    if (cert->email) {
    286        ce = (cache_entry *)nssHash_Lookup(cache->email, cert->email);
    287        if (ce) {
    288            nssList *subjects = ce->entry.list;
    289            /* Remove the subject list from the email hash */
    290            if (subjects) {
    291                nssList_Remove(subjects, subjectList);
    292 #ifdef DEBUG_CACHE
    293                log_item_dump("removed subject list", &cert->subject);
    294                PR_LOG(s_log, PR_LOG_DEBUG, ("for email %s", cert->email));
    295 #endif
    296                if (nssList_Count(subjects) == 0) {
    297                    /* No more subject lists for email, delete list and
    298                     * remove hash entry
    299                     */
    300                    (void)nssList_Destroy(subjects);
    301                    nssHash_Remove(cache->email, cert->email);
    302                    /* there are no entries left for this address, free space
    303                     * used for email entries
    304                     */
    305                    nssArena_Destroy(ce->arena);
    306 #ifdef DEBUG_CACHE
    307                    PR_LOG(s_log, PR_LOG_DEBUG, ("removed email %s", cert->email));
    308 #endif
    309                }
    310            }
    311            nssrv = PR_SUCCESS;
    312        }
    313    }
    314    return nssrv;
    315 }
    316 
    317 NSS_IMPLEMENT void
    318 nssTrustDomain_RemoveCertFromCacheLOCKED(
    319    NSSTrustDomain *td,
    320    NSSCertificate *cert)
    321 {
    322    nssList *subjectList;
    323    cache_entry *ce;
    324    NSSArena *arena;
    325    NSSUTF8 *nickname = NULL;
    326 
    327 #ifdef DEBUG_CACHE
    328    log_cert_ref("attempt to remove cert", cert);
    329 #endif
    330    ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, cert);
    331    if (!ce || ce->entry.cert != cert) {
    332 /* If it's not in the cache, or a different cert is (this is really
    333 * for safety reasons, though it shouldn't happen), do nothing
    334 */
    335 #ifdef DEBUG_CACHE
    336        PR_LOG(s_log, PR_LOG_DEBUG, ("but it wasn't in the cache"));
    337 #endif
    338        return;
    339    }
    340    (void)remove_issuer_and_serial_entry(td->cache, cert);
    341    (void)remove_subject_entry(td->cache, cert, &subjectList,
    342                               &nickname, &arena);
    343    if (nssList_Count(subjectList) == 0) {
    344        (void)remove_nickname_entry(td->cache, nickname, subjectList);
    345        (void)remove_email_entry(td->cache, cert, subjectList);
    346        (void)nssList_Destroy(subjectList);
    347        nssHash_Remove(td->cache->subject, &cert->subject);
    348        /* there are no entries left for this subject, free the space used
    349         * for both the nickname and subject entries
    350         */
    351        if (arena) {
    352            nssArena_Destroy(arena);
    353        }
    354    }
    355 }
    356 
    357 NSS_IMPLEMENT void
    358 nssTrustDomain_LockCertCache(NSSTrustDomain *td)
    359 {
    360    PZ_Lock(td->cache->lock);
    361 }
    362 
    363 NSS_IMPLEMENT void
    364 nssTrustDomain_UnlockCertCache(NSSTrustDomain *td)
    365 {
    366    PZ_Unlock(td->cache->lock);
    367 }
    368 
    369 struct token_cert_dtor {
    370    NSSToken *token;
    371    nssTDCertificateCache *cache;
    372    NSSCertificate **certs;
    373    PRUint32 numCerts, arrSize;
    374 };
    375 
    376 static void
    377 remove_token_certs(const void *k, void *v, void *a)
    378 {
    379    NSSCertificate *c = (NSSCertificate *)k;
    380    nssPKIObject *object = &c->object;
    381    struct token_cert_dtor *dtor = a;
    382    PRUint32 i;
    383    nssPKIObject_AddRef(object);
    384    nssPKIObject_Lock(object);
    385    for (i = 0; i < object->numInstances; i++) {
    386        if (object->instances[i]->token == dtor->token) {
    387            nssCryptokiObject_Destroy(object->instances[i]);
    388            object->instances[i] = object->instances[object->numInstances - 1];
    389            object->instances[object->numInstances - 1] = NULL;
    390            object->numInstances--;
    391            dtor->certs[dtor->numCerts++] = c;
    392            if (dtor->numCerts == dtor->arrSize) {
    393                dtor->arrSize *= 2;
    394                dtor->certs = nss_ZREALLOCARRAY(dtor->certs,
    395                                                NSSCertificate *,
    396                                                dtor->arrSize);
    397            }
    398            break;
    399        }
    400    }
    401    nssPKIObject_Unlock(object);
    402    nssPKIObject_Destroy(object);
    403    return;
    404 }
    405 
    406 /*
    407 * Remove all certs for the given token from the cache.  This is
    408 * needed if the token is removed.
    409 */
    410 NSS_IMPLEMENT PRStatus
    411 nssTrustDomain_RemoveTokenCertsFromCache(
    412    NSSTrustDomain *td,
    413    NSSToken *token)
    414 {
    415    NSSCertificate **certs;
    416    PRUint32 i, arrSize = 10;
    417    struct token_cert_dtor dtor;
    418    certs = nss_ZNEWARRAY(NULL, NSSCertificate *, arrSize);
    419    if (!certs) {
    420        return PR_FAILURE;
    421    }
    422    dtor.cache = td->cache;
    423    dtor.token = token;
    424    dtor.certs = certs;
    425    dtor.numCerts = 0;
    426    dtor.arrSize = arrSize;
    427    PZ_Lock(td->cache->lock);
    428    nssHash_Iterate(td->cache->issuerAndSN, remove_token_certs, &dtor);
    429    for (i = 0; i < dtor.numCerts; i++) {
    430        if (dtor.certs[i]->object.numInstances == 0) {
    431            nssTrustDomain_RemoveCertFromCacheLOCKED(td, dtor.certs[i]);
    432            dtor.certs[i] = NULL; /* skip this cert in the second for loop */
    433        } else {
    434            /* make sure it doesn't disappear on us before we finish */
    435            nssCertificate_AddRef(dtor.certs[i]);
    436        }
    437    }
    438    PZ_Unlock(td->cache->lock);
    439    for (i = 0; i < dtor.numCerts; i++) {
    440        if (dtor.certs[i]) {
    441            STAN_ForceCERTCertificateUpdate(dtor.certs[i]);
    442            nssCertificate_Destroy(dtor.certs[i]);
    443        }
    444    }
    445    nss_ZFreeIf(dtor.certs);
    446    return PR_SUCCESS;
    447 }
    448 
    449 NSS_IMPLEMENT PRStatus
    450 nssTrustDomain_UpdateCachedTokenCerts(
    451    NSSTrustDomain *td,
    452    NSSToken *token)
    453 {
    454    NSSCertificate **cp, **cached = NULL;
    455    nssList *certList;
    456    PRUint32 count;
    457    certList = nssList_Create(NULL, PR_FALSE);
    458    if (!certList)
    459        return PR_FAILURE;
    460    (void)nssTrustDomain_GetCertsFromCache(td, certList);
    461    count = nssList_Count(certList);
    462    if (count > 0) {
    463        cached = nss_ZNEWARRAY(NULL, NSSCertificate *, count + 1);
    464        if (!cached) {
    465            nssList_Destroy(certList);
    466            return PR_FAILURE;
    467        }
    468        nssList_GetArray(certList, (void **)cached, count);
    469        for (cp = cached; *cp; cp++) {
    470            nssCryptokiObject *instance;
    471            NSSCertificate *c = *cp;
    472            nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly;
    473            instance = nssToken_FindCertificateByIssuerAndSerialNumber(
    474                token,
    475                NULL,
    476                &c->issuer,
    477                &c->serial,
    478                tokenOnly,
    479                NULL);
    480            if (instance) {
    481                nssPKIObject_AddInstance(&c->object, instance);
    482                STAN_ForceCERTCertificateUpdate(c);
    483            }
    484        }
    485        nssCertificateArray_Destroy(cached);
    486    }
    487    nssList_Destroy(certList);
    488    return PR_SUCCESS;
    489 }
    490 
    491 static PRStatus
    492 add_issuer_and_serial_entry(
    493    NSSArena *arena,
    494    nssTDCertificateCache *cache,
    495    NSSCertificate *cert)
    496 {
    497    cache_entry *ce;
    498    ce = new_cache_entry(arena, (void *)cert, PR_FALSE);
    499 #ifdef DEBUG_CACHE
    500    log_cert_ref("added to issuer/sn", cert);
    501 #endif
    502    return nssHash_Add(cache->issuerAndSN, cert, (void *)ce);
    503 }
    504 
    505 static PRStatus
    506 add_subject_entry(
    507    NSSArena *arena,
    508    nssTDCertificateCache *cache,
    509    NSSCertificate *cert,
    510    NSSUTF8 *nickname,
    511    nssList **subjectList)
    512 {
    513    PRStatus nssrv;
    514    nssList *list;
    515    cache_entry *ce;
    516    *subjectList = NULL; /* this is only set if a new one is created */
    517    ce = (cache_entry *)nssHash_Lookup(cache->subject, &cert->subject);
    518    if (ce) {
    519        ce->hits++;
    520        ce->lastHit = PR_Now();
    521        /* The subject is already in, add this cert to the list */
    522        nssrv = nssList_AddUnique(ce->entry.list, cert);
    523 #ifdef DEBUG_CACHE
    524        log_cert_ref("added to existing subject list", cert);
    525 #endif
    526    } else {
    527        NSSDER *subject;
    528        /* Create a new subject list for the subject */
    529        list = nssList_Create(arena, PR_FALSE);
    530        if (!list) {
    531            return PR_FAILURE;
    532        }
    533        ce = new_cache_entry(arena, (void *)list, PR_TRUE);
    534        if (!ce) {
    535            return PR_FAILURE;
    536        }
    537        if (nickname) {
    538            ce->nickname = nssUTF8_Duplicate(nickname, arena);
    539        }
    540        nssList_SetSortFunction(list, nssCertificate_SubjectListSort);
    541        /* Add the cert entry to this list of subjects */
    542        nssrv = nssList_AddUnique(list, cert);
    543        if (nssrv != PR_SUCCESS) {
    544            return nssrv;
    545        }
    546        /* Add the subject list to the cache */
    547        subject = nssItem_Duplicate(&cert->subject, arena, NULL);
    548        if (!subject) {
    549            return PR_FAILURE;
    550        }
    551        nssrv = nssHash_Add(cache->subject, subject, ce);
    552        if (nssrv != PR_SUCCESS) {
    553            return nssrv;
    554        }
    555        *subjectList = list;
    556 #ifdef DEBUG_CACHE
    557        log_cert_ref("created subject list", cert);
    558 #endif
    559    }
    560    return nssrv;
    561 }
    562 
    563 static PRStatus
    564 add_nickname_entry(
    565    NSSArena *arena,
    566    nssTDCertificateCache *cache,
    567    NSSUTF8 *certNickname,
    568    nssList *subjectList)
    569 {
    570    PRStatus nssrv = PR_SUCCESS;
    571    cache_entry *ce;
    572    ce = (cache_entry *)nssHash_Lookup(cache->nickname, certNickname);
    573    if (ce) {
    574        /* This is a collision.  A nickname entry already exists for this
    575         * subject, but a subject entry didn't.  This would imply there are
    576         * two subjects using the same nickname, which is not allowed.
    577         */
    578        return PR_FAILURE;
    579    } else {
    580        NSSUTF8 *nickname;
    581        ce = new_cache_entry(arena, subjectList, PR_FALSE);
    582        if (!ce) {
    583            return PR_FAILURE;
    584        }
    585        nickname = nssUTF8_Duplicate(certNickname, arena);
    586        if (!nickname) {
    587            return PR_FAILURE;
    588        }
    589        nssrv = nssHash_Add(cache->nickname, nickname, ce);
    590 #ifdef DEBUG_CACHE
    591        log_cert_ref("created nickname for", cert);
    592 #endif
    593    }
    594    return nssrv;
    595 }
    596 
    597 static PRStatus
    598 add_email_entry(
    599    nssTDCertificateCache *cache,
    600    NSSCertificate *cert,
    601    nssList *subjectList)
    602 {
    603    PRStatus nssrv = PR_SUCCESS;
    604    nssList *subjects;
    605    cache_entry *ce;
    606    ce = (cache_entry *)nssHash_Lookup(cache->email, cert->email);
    607    if (ce) {
    608        /* Already have an entry for this email address, but not subject */
    609        subjects = ce->entry.list;
    610        nssrv = nssList_AddUnique(subjects, subjectList);
    611        ce->hits++;
    612        ce->lastHit = PR_Now();
    613 #ifdef DEBUG_CACHE
    614        log_cert_ref("added subject to email for", cert);
    615 #endif
    616    } else {
    617        NSSASCII7 *email;
    618        NSSArena *arena;
    619        arena = nssArena_Create();
    620        if (!arena) {
    621            return PR_FAILURE;
    622        }
    623        /* Create a new list of subject lists, add this subject */
    624        subjects = nssList_Create(arena, PR_TRUE);
    625        if (!subjects) {
    626            nssArena_Destroy(arena);
    627            return PR_FAILURE;
    628        }
    629        /* Add the new subject to the list */
    630        nssrv = nssList_AddUnique(subjects, subjectList);
    631        if (nssrv != PR_SUCCESS) {
    632            nssArena_Destroy(arena);
    633            return nssrv;
    634        }
    635        /* Add the new entry to the cache */
    636        ce = new_cache_entry(arena, (void *)subjects, PR_TRUE);
    637        if (!ce) {
    638            nssArena_Destroy(arena);
    639            return PR_FAILURE;
    640        }
    641        email = nssUTF8_Duplicate(cert->email, arena);
    642        if (!email) {
    643            nssArena_Destroy(arena);
    644            return PR_FAILURE;
    645        }
    646        nssrv = nssHash_Add(cache->email, email, ce);
    647        if (nssrv != PR_SUCCESS) {
    648            nssArena_Destroy(arena);
    649            return nssrv;
    650        }
    651 #ifdef DEBUG_CACHE
    652        log_cert_ref("created email for", cert);
    653 #endif
    654    }
    655    return nssrv;
    656 }
    657 
    658 extern const NSSError NSS_ERROR_CERTIFICATE_IN_CACHE;
    659 
    660 static void
    661 remove_object_instances(
    662    nssPKIObject *object,
    663    nssCryptokiObject **instances,
    664    int numInstances)
    665 {
    666    int i;
    667 
    668    for (i = 0; i < numInstances; i++) {
    669        nssPKIObject_RemoveInstanceForToken(object, instances[i]->token);
    670    }
    671 }
    672 
    673 static SECStatus
    674 merge_object_instances(
    675    nssPKIObject *to,
    676    nssPKIObject *from)
    677 {
    678    nssCryptokiObject **instances, **ci;
    679    int i;
    680    SECStatus rv = SECSuccess;
    681 
    682    instances = nssPKIObject_GetInstances(from);
    683    if (instances == NULL) {
    684        return SECFailure;
    685    }
    686    for (ci = instances, i = 0; *ci; ci++, i++) {
    687        nssCryptokiObject *instance = nssCryptokiObject_Clone(*ci);
    688        if (instance) {
    689            if (nssPKIObject_AddInstance(to, instance) == PR_SUCCESS) {
    690                continue;
    691            }
    692            nssCryptokiObject_Destroy(instance);
    693        }
    694        remove_object_instances(to, instances, i);
    695        rv = SECFailure;
    696        break;
    697    }
    698    nssCryptokiObjectArray_Destroy(instances);
    699    return rv;
    700 }
    701 
    702 static NSSCertificate *
    703 add_cert_to_cache(
    704    NSSTrustDomain *td,
    705    NSSCertificate *cert)
    706 {
    707    NSSArena *arena = NULL;
    708    nssList *subjectList = NULL;
    709    PRStatus nssrv;
    710    PRUint32 added = 0;
    711    cache_entry *ce;
    712    NSSCertificate *rvCert = NULL;
    713    NSSUTF8 *certNickname = nssCertificate_GetNickname(cert, NULL);
    714 
    715    /* Set cc->trust and cc->nssCertificate before taking td->cache->lock.
    716     * Otherwise, the sorter in add_subject_entry may eventually call
    717     * nssSlot_IsTokenPresent, which must not occur while the cache lock
    718     * is held. See bugs 1625791 and 1651564 for details. */
    719    if (cert->type == NSSCertificateType_PKIX) {
    720        (void)STAN_GetCERTCertificate(cert);
    721    }
    722 
    723    PZ_Lock(td->cache->lock);
    724    /* If it exists in the issuer/serial hash, it's already in all */
    725    ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, cert);
    726    if (ce) {
    727        ce->hits++;
    728        ce->lastHit = PR_Now();
    729        rvCert = nssCertificate_AddRef(ce->entry.cert);
    730 #ifdef DEBUG_CACHE
    731        log_cert_ref("attempted to add cert already in cache", cert);
    732 #endif
    733        PZ_Unlock(td->cache->lock);
    734        nss_ZFreeIf(certNickname);
    735        /* collision - somebody else already added the cert
    736         * to the cache before this thread got around to it.
    737         */
    738        /* merge the instances of the cert */
    739        if (merge_object_instances(&rvCert->object, &cert->object) != SECSuccess) {
    740            nssCertificate_Destroy(rvCert);
    741            return NULL;
    742        }
    743        STAN_ForceCERTCertificateUpdate(rvCert);
    744        nssCertificate_Destroy(cert);
    745        return rvCert;
    746    }
    747    /* create a new cache entry for this cert within the cert's arena*/
    748    nssrv = add_issuer_and_serial_entry(cert->object.arena, td->cache, cert);
    749    if (nssrv != PR_SUCCESS) {
    750        goto loser;
    751    }
    752    added++;
    753    /* create an arena for the nickname and subject entries */
    754    arena = nssArena_Create();
    755    if (!arena) {
    756        goto loser;
    757    }
    758    /* create a new subject list for this cert, or add to existing */
    759    nssrv = add_subject_entry(arena, td->cache, cert,
    760                              certNickname, &subjectList);
    761    if (nssrv != PR_SUCCESS) {
    762        goto loser;
    763    }
    764    added++;
    765    /* If a new subject entry was created, also need nickname and/or email */
    766    if (subjectList != NULL) {
    767 #ifdef nodef
    768        PRBool handle = PR_FALSE;
    769 #endif
    770        if (certNickname) {
    771            nssrv = add_nickname_entry(arena, td->cache,
    772                                       certNickname, subjectList);
    773            if (nssrv != PR_SUCCESS) {
    774                goto loser;
    775            }
    776 #ifdef nodef
    777            handle = PR_TRUE;
    778 #endif
    779            added++;
    780        }
    781        if (cert->email) {
    782            nssrv = add_email_entry(td->cache, cert, subjectList);
    783            if (nssrv != PR_SUCCESS) {
    784                goto loser;
    785            }
    786 #ifdef nodef
    787            handle = PR_TRUE;
    788 #endif
    789            added += 2;
    790        }
    791 #ifdef nodef
    792        /* I think either a nickname or email address must be associated
    793         * with the cert.  However, certs are passed to NewTemp without
    794         * either.  This worked in the old code, so it must work now.
    795         */
    796        if (!handle) {
    797            /* Require either nickname or email handle */
    798            nssrv = PR_FAILURE;
    799            goto loser;
    800        }
    801 #endif
    802    } else {
    803        /* A new subject entry was not created.  arena is unused. */
    804        nssArena_Destroy(arena);
    805    }
    806    rvCert = cert;
    807    PZ_Unlock(td->cache->lock);
    808    nss_ZFreeIf(certNickname);
    809    return rvCert;
    810 loser:
    811    nss_ZFreeIf(certNickname);
    812    certNickname = NULL;
    813    /* Remove any handles that have been created */
    814    subjectList = NULL;
    815    if (added >= 1) {
    816        (void)remove_issuer_and_serial_entry(td->cache, cert);
    817    }
    818    if (added >= 2) {
    819        (void)remove_subject_entry(td->cache, cert, &subjectList,
    820                                   &certNickname, &arena);
    821    }
    822    if (added == 3 || added == 5) {
    823        (void)remove_nickname_entry(td->cache, certNickname, subjectList);
    824    }
    825    if (added >= 4) {
    826        (void)remove_email_entry(td->cache, cert, subjectList);
    827    }
    828    if (subjectList) {
    829        nssHash_Remove(td->cache->subject, &cert->subject);
    830        nssList_Destroy(subjectList);
    831    }
    832    if (arena) {
    833        nssArena_Destroy(arena);
    834    }
    835    PZ_Unlock(td->cache->lock);
    836    return NULL;
    837 }
    838 
    839 NSS_IMPLEMENT PRStatus
    840 nssTrustDomain_AddCertsToCache(
    841    NSSTrustDomain *td,
    842    NSSCertificate **certs,
    843    PRUint32 numCerts)
    844 {
    845    PRUint32 i;
    846    NSSCertificate *c;
    847    for (i = 0; i < numCerts && certs[i]; i++) {
    848        c = add_cert_to_cache(td, certs[i]);
    849        if (c == NULL) {
    850            return PR_FAILURE;
    851        } else {
    852            certs[i] = c;
    853        }
    854    }
    855    return PR_SUCCESS;
    856 }
    857 
    858 static NSSCertificate **
    859 collect_subject_certs(
    860    nssList *subjectList,
    861    nssList *rvCertListOpt)
    862 {
    863    NSSCertificate *c;
    864    NSSCertificate **rvArray = NULL;
    865    PRUint32 count;
    866    nssCertificateList_AddReferences(subjectList);
    867    if (rvCertListOpt) {
    868        nssListIterator *iter = nssList_CreateIterator(subjectList);
    869        if (!iter) {
    870            return (NSSCertificate **)NULL;
    871        }
    872        for (c = (NSSCertificate *)nssListIterator_Start(iter);
    873             c != (NSSCertificate *)NULL;
    874             c = (NSSCertificate *)nssListIterator_Next(iter)) {
    875            nssList_Add(rvCertListOpt, c);
    876        }
    877        nssListIterator_Finish(iter);
    878        nssListIterator_Destroy(iter);
    879    } else {
    880        count = nssList_Count(subjectList);
    881        rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count + 1);
    882        if (!rvArray) {
    883            return (NSSCertificate **)NULL;
    884        }
    885        nssList_GetArray(subjectList, (void **)rvArray, count);
    886    }
    887    return rvArray;
    888 }
    889 
    890 /*
    891 * Find all cached certs with this subject.
    892 */
    893 NSS_IMPLEMENT NSSCertificate **
    894 nssTrustDomain_GetCertsForSubjectFromCache(
    895    NSSTrustDomain *td,
    896    NSSDER *subject,
    897    nssList *certListOpt)
    898 {
    899    NSSCertificate **rvArray = NULL;
    900    cache_entry *ce;
    901 #ifdef DEBUG_CACHE
    902    log_item_dump("looking for cert by subject", subject);
    903 #endif
    904    PZ_Lock(td->cache->lock);
    905    ce = (cache_entry *)nssHash_Lookup(td->cache->subject, subject);
    906    if (ce) {
    907        ce->hits++;
    908        ce->lastHit = PR_Now();
    909 #ifdef DEBUG_CACHE
    910        PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits));
    911 #endif
    912        rvArray = collect_subject_certs(ce->entry.list, certListOpt);
    913    }
    914    PZ_Unlock(td->cache->lock);
    915    return rvArray;
    916 }
    917 
    918 /*
    919 * Find all cached certs with this label.
    920 */
    921 NSS_IMPLEMENT NSSCertificate **
    922 nssTrustDomain_GetCertsForNicknameFromCache(
    923    NSSTrustDomain *td,
    924    const NSSUTF8 *nickname,
    925    nssList *certListOpt)
    926 {
    927    NSSCertificate **rvArray = NULL;
    928    cache_entry *ce;
    929 #ifdef DEBUG_CACHE
    930    PR_LOG(s_log, PR_LOG_DEBUG, ("looking for cert by nick %s", nickname));
    931 #endif
    932    PZ_Lock(td->cache->lock);
    933    ce = (cache_entry *)nssHash_Lookup(td->cache->nickname, nickname);
    934    if (ce) {
    935        ce->hits++;
    936        ce->lastHit = PR_Now();
    937 #ifdef DEBUG_CACHE
    938        PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits));
    939 #endif
    940        rvArray = collect_subject_certs(ce->entry.list, certListOpt);
    941    }
    942    PZ_Unlock(td->cache->lock);
    943    return rvArray;
    944 }
    945 
    946 /*
    947 * Find all cached certs with this email address.
    948 */
    949 NSS_IMPLEMENT NSSCertificate **
    950 nssTrustDomain_GetCertsForEmailAddressFromCache(
    951    NSSTrustDomain *td,
    952    NSSASCII7 *email,
    953    nssList *certListOpt)
    954 {
    955    NSSCertificate **rvArray = NULL;
    956    cache_entry *ce;
    957    nssList *collectList = NULL;
    958    nssListIterator *iter = NULL;
    959    nssList *subjectList;
    960 #ifdef DEBUG_CACHE
    961    PR_LOG(s_log, PR_LOG_DEBUG, ("looking for cert by email %s", email));
    962 #endif
    963    PZ_Lock(td->cache->lock);
    964    ce = (cache_entry *)nssHash_Lookup(td->cache->email, email);
    965    if (ce) {
    966        ce->hits++;
    967        ce->lastHit = PR_Now();
    968 #ifdef DEBUG_CACHE
    969        PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits));
    970 #endif
    971        /* loop over subject lists and get refs for certs */
    972        if (certListOpt) {
    973            collectList = certListOpt;
    974        } else {
    975            collectList = nssList_Create(NULL, PR_FALSE);
    976            if (!collectList) {
    977                PZ_Unlock(td->cache->lock);
    978                return NULL;
    979            }
    980        }
    981        iter = nssList_CreateIterator(ce->entry.list);
    982        if (!iter) {
    983            PZ_Unlock(td->cache->lock);
    984            if (!certListOpt) {
    985                nssList_Destroy(collectList);
    986            }
    987            return NULL;
    988        }
    989        for (subjectList = (nssList *)nssListIterator_Start(iter);
    990             subjectList != (nssList *)NULL;
    991             subjectList = (nssList *)nssListIterator_Next(iter)) {
    992            (void)collect_subject_certs(subjectList, collectList);
    993        }
    994        nssListIterator_Finish(iter);
    995        nssListIterator_Destroy(iter);
    996    }
    997    PZ_Unlock(td->cache->lock);
    998    if (!certListOpt && collectList) {
    999        PRUint32 count = nssList_Count(collectList);
   1000        rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count);
   1001        if (rvArray) {
   1002            nssList_GetArray(collectList, (void **)rvArray, count);
   1003        }
   1004        nssList_Destroy(collectList);
   1005    }
   1006    return rvArray;
   1007 }
   1008 
   1009 /*
   1010 * Look for a specific cert in the cache
   1011 */
   1012 NSS_IMPLEMENT NSSCertificate *
   1013 nssTrustDomain_GetCertForIssuerAndSNFromCache(
   1014    NSSTrustDomain *td,
   1015    NSSDER *issuer,
   1016    NSSDER *serial)
   1017 {
   1018    NSSCertificate certkey;
   1019    NSSCertificate *rvCert = NULL;
   1020    cache_entry *ce;
   1021    certkey.issuer.data = issuer->data;
   1022    certkey.issuer.size = issuer->size;
   1023    certkey.serial.data = serial->data;
   1024    certkey.serial.size = serial->size;
   1025 #ifdef DEBUG_CACHE
   1026    log_item_dump("looking for cert by issuer/sn, issuer", issuer);
   1027    log_item_dump("                               serial", serial);
   1028 #endif
   1029    PZ_Lock(td->cache->lock);
   1030    ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, &certkey);
   1031    if (ce) {
   1032        ce->hits++;
   1033        ce->lastHit = PR_Now();
   1034        rvCert = nssCertificate_AddRef(ce->entry.cert);
   1035 #ifdef DEBUG_CACHE
   1036        PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits));
   1037 #endif
   1038    }
   1039    PZ_Unlock(td->cache->lock);
   1040    return rvCert;
   1041 }
   1042 
   1043 /*
   1044 * Look for a specific cert in the cache
   1045 */
   1046 NSS_IMPLEMENT NSSCertificate *
   1047 nssTrustDomain_GetCertByDERFromCache(
   1048    NSSTrustDomain *td,
   1049    NSSDER *der)
   1050 {
   1051    PRStatus nssrv = PR_FAILURE;
   1052    NSSDER issuer, serial;
   1053    NSSCertificate *rvCert;
   1054    nssrv = nssPKIX509_GetIssuerAndSerialFromDER(der, &issuer, &serial);
   1055    if (nssrv != PR_SUCCESS) {
   1056        return NULL;
   1057    }
   1058 #ifdef DEBUG_CACHE
   1059    log_item_dump("looking for cert by DER", der);
   1060 #endif
   1061    rvCert = nssTrustDomain_GetCertForIssuerAndSNFromCache(td,
   1062                                                           &issuer, &serial);
   1063    PORT_Free(issuer.data);
   1064    PORT_Free(serial.data);
   1065    return rvCert;
   1066 }
   1067 
   1068 static void
   1069 cert_iter(const void *k, void *v, void *a)
   1070 {
   1071    nssList *certList = (nssList *)a;
   1072    NSSCertificate *c = (NSSCertificate *)k;
   1073    nssList_Add(certList, nssCertificate_AddRef(c));
   1074 }
   1075 
   1076 NSS_EXTERN NSSCertificate **
   1077 nssTrustDomain_GetCertsFromCache(
   1078    NSSTrustDomain *td,
   1079    nssList *certListOpt)
   1080 {
   1081    NSSCertificate **rvArray = NULL;
   1082    nssList *certList;
   1083    if (certListOpt) {
   1084        certList = certListOpt;
   1085    } else {
   1086        certList = nssList_Create(NULL, PR_FALSE);
   1087        if (!certList) {
   1088            return NULL;
   1089        }
   1090    }
   1091    PZ_Lock(td->cache->lock);
   1092    nssHash_Iterate(td->cache->issuerAndSN, cert_iter, (void *)certList);
   1093    PZ_Unlock(td->cache->lock);
   1094    if (!certListOpt) {
   1095        PRUint32 count = nssList_Count(certList);
   1096        rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count);
   1097        nssList_GetArray(certList, (void **)rvArray, count);
   1098        /* array takes the references */
   1099        nssList_Destroy(certList);
   1100    }
   1101    return rvArray;
   1102 }
   1103 
   1104 NSS_IMPLEMENT void
   1105 nssTrustDomain_DumpCacheInfo(
   1106    NSSTrustDomain *td,
   1107    void (*cert_dump_iter)(const void *, void *, void *),
   1108    void *arg)
   1109 {
   1110    PZ_Lock(td->cache->lock);
   1111    nssHash_Iterate(td->cache->issuerAndSN, cert_dump_iter, arg);
   1112    PZ_Unlock(td->cache->lock);
   1113 }