Coverage Report

Created: 2026-05-19 06:33

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