Coverage Report

Created: 2024-11-21 07:03

/src/nss-nspr/nss/lib/pki/tdcache.c
Line
Count
Source (jump to first uncovered line)
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
0
{
101
0
    cache_entry *ce = nss_ZNEW(arena, cache_entry);
102
0
    if (ce) {
103
0
        ce->entry.value = value;
104
0
        ce->hits = 1;
105
0
        ce->lastHit = PR_Now();
106
0
        if (ownArena) {
107
0
            ce->arena = arena;
108
0
        }
109
0
        ce->nickname = NULL;
110
0
    }
111
0
    return ce;
112
0
}
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
2
{
122
2
    NSSArena *arena;
123
2
    nssTDCertificateCache *cache = td->cache;
124
#ifdef DEBUG_CACHE
125
    s_log = PR_NewLogModule("nss_cache");
126
    PR_ASSERT(s_log);
127
#endif
128
2
    PR_ASSERT(!cache);
129
2
    arena = nssArena_Create();
130
2
    if (!arena) {
131
0
        return PR_FAILURE;
132
0
    }
133
2
    cache = nss_ZNEW(arena, nssTDCertificateCache);
134
2
    if (!cache) {
135
0
        nssArena_Destroy(arena);
136
0
        return PR_FAILURE;
137
0
    }
138
2
    cache->lock = PZ_NewLock(nssILockCache);
139
2
    if (!cache->lock) {
140
0
        nssArena_Destroy(arena);
141
0
        return PR_FAILURE;
142
0
    }
143
    /* Create the issuer and serial DER --> certificate hash */
144
2
    cache->issuerAndSN = nssHash_CreateCertificate(arena, cacheSize);
145
2
    if (!cache->issuerAndSN) {
146
0
        goto loser;
147
0
    }
148
    /* Create the subject DER --> subject list hash */
149
2
    cache->subject = nssHash_CreateItem(arena, cacheSize);
150
2
    if (!cache->subject) {
151
0
        goto loser;
152
0
    }
153
    /* Create the nickname --> subject list hash */
154
2
    cache->nickname = nssHash_CreateString(arena, cacheSize);
155
2
    if (!cache->nickname) {
156
0
        goto loser;
157
0
    }
158
    /* Create the email --> list of subject lists hash */
159
2
    cache->email = nssHash_CreateString(arena, cacheSize);
160
2
    if (!cache->email) {
161
0
        goto loser;
162
0
    }
163
2
    cache->arena = arena;
164
2
    td->cache = cache;
165
#ifdef DEBUG_CACHE
166
    PR_LOG(s_log, PR_LOG_DEBUG, ("Cache initialized."));
167
#endif
168
2
    return PR_SUCCESS;
169
0
loser:
170
0
    PZ_DestroyLock(cache->lock);
171
0
    nssArena_Destroy(arena);
172
0
    td->cache = NULL;
173
#ifdef DEBUG_CACHE
174
    PR_LOG(s_log, PR_LOG_DEBUG, ("Cache initialization failed."));
175
#endif
176
0
    return PR_FAILURE;
177
2
}
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
0
{
192
0
    if (!td->cache) {
193
0
        nss_SetError(NSS_ERROR_INTERNAL_ERROR);
194
0
        return PR_FAILURE;
195
0
    }
196
0
    if (nssHash_Count(td->cache->issuerAndSN) > 0) {
197
0
        nss_SetError(NSS_ERROR_BUSY);
198
0
        return PR_FAILURE;
199
0
    }
200
0
    PZ_DestroyLock(td->cache->lock);
201
0
    nssHash_Destroy(td->cache->issuerAndSN);
202
0
    nssHash_Destroy(td->cache->subject);
203
0
    nssHash_Destroy(td->cache->nickname);
204
0
    nssHash_Destroy(td->cache->email);
205
0
    nssArena_Destroy(td->cache->arena);
206
0
    td->cache = NULL;
207
#ifdef DEBUG_CACHE
208
    PR_LOG(s_log, PR_LOG_DEBUG, ("Cache destroyed."));
209
#endif
210
0
    return PR_SUCCESS;
211
0
}
212
213
static PRStatus
214
remove_issuer_and_serial_entry(
215
    nssTDCertificateCache *cache,
216
    NSSCertificate *cert)
217
0
{
218
    /* Remove the cert from the issuer/serial hash */
219
0
    nssHash_Remove(cache->issuerAndSN, cert);
220
#ifdef DEBUG_CACHE
221
    log_cert_ref("removed issuer/sn", cert);
222
#endif
223
0
    return PR_SUCCESS;
224
0
}
225
226
static PRStatus
227
remove_subject_entry(
228
    nssTDCertificateCache *cache,
229
    NSSCertificate *cert,
230
    nssList **subjectList,
231
    NSSUTF8 **nickname,
232
    NSSArena **arena)
233
0
{
234
0
    PRStatus nssrv;
235
0
    cache_entry *ce;
236
0
    *subjectList = NULL;
237
0
    *arena = NULL;
238
    /* Get the subject list for the cert's subject */
239
0
    ce = (cache_entry *)nssHash_Lookup(cache->subject, &cert->subject);
240
0
    if (ce) {
241
        /* Remove the cert from the subject hash */
242
0
        nssList_Remove(ce->entry.list, cert);
243
0
        *subjectList = ce->entry.list;
244
0
        *nickname = ce->nickname;
245
0
        *arena = ce->arena;
246
0
        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
0
    } else {
252
0
        nssrv = PR_FAILURE;
253
0
    }
254
0
    return nssrv;
255
0
}
256
257
static PRStatus
258
remove_nickname_entry(
259
    nssTDCertificateCache *cache,
260
    NSSUTF8 *nickname,
261
    nssList *subjectList)
262
0
{
263
0
    PRStatus nssrv;
264
0
    if (nickname) {
265
0
        nssHash_Remove(cache->nickname, nickname);
266
0
        nssrv = PR_SUCCESS;
267
#ifdef DEBUG_CACHE
268
        PR_LOG(s_log, PR_LOG_DEBUG, ("removed nickname %s", nickname));
269
#endif
270
0
    } else {
271
0
        nssrv = PR_FAILURE;
272
0
    }
273
0
    return nssrv;
274
0
}
275
276
static PRStatus
277
remove_email_entry(
278
    nssTDCertificateCache *cache,
279
    NSSCertificate *cert,
280
    nssList *subjectList)
281
0
{
282
0
    PRStatus nssrv = PR_FAILURE;
283
0
    cache_entry *ce;
284
    /* Find the subject list in the email hash */
285
0
    if (cert->email) {
286
0
        ce = (cache_entry *)nssHash_Lookup(cache->email, cert->email);
287
0
        if (ce) {
288
0
            nssList *subjects = ce->entry.list;
289
            /* Remove the subject list from the email hash */
290
0
            if (subjects) {
291
0
                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
0
                if (nssList_Count(subjects) == 0) {
297
                    /* No more subject lists for email, delete list and
298
                     * remove hash entry
299
                     */
300
0
                    (void)nssList_Destroy(subjects);
301
0
                    nssHash_Remove(cache->email, cert->email);
302
                    /* there are no entries left for this address, free space
303
                     * used for email entries
304
                     */
305
0
                    nssArena_Destroy(ce->arena);
306
#ifdef DEBUG_CACHE
307
                    PR_LOG(s_log, PR_LOG_DEBUG, ("removed email %s", cert->email));
308
#endif
309
0
                }
310
0
            }
311
0
            nssrv = PR_SUCCESS;
312
0
        }
313
0
    }
314
0
    return nssrv;
315
0
}
316
317
NSS_IMPLEMENT void
318
nssTrustDomain_RemoveCertFromCacheLOCKED(
319
    NSSTrustDomain *td,
320
    NSSCertificate *cert)
321
0
{
322
0
    nssList *subjectList;
323
0
    cache_entry *ce;
324
0
    NSSArena *arena;
325
0
    NSSUTF8 *nickname = NULL;
326
327
#ifdef DEBUG_CACHE
328
    log_cert_ref("attempt to remove cert", cert);
329
#endif
330
0
    ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, cert);
331
0
    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
0
        return;
339
0
    }
340
0
    (void)remove_issuer_and_serial_entry(td->cache, cert);
341
0
    (void)remove_subject_entry(td->cache, cert, &subjectList,
342
0
                               &nickname, &arena);
343
0
    if (nssList_Count(subjectList) == 0) {
344
0
        (void)remove_nickname_entry(td->cache, nickname, subjectList);
345
0
        (void)remove_email_entry(td->cache, cert, subjectList);
346
0
        (void)nssList_Destroy(subjectList);
347
0
        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
0
        if (arena) {
352
0
            nssArena_Destroy(arena);
353
0
        }
354
0
    }
355
0
}
356
357
NSS_IMPLEMENT void
358
nssTrustDomain_LockCertCache(NSSTrustDomain *td)
359
0
{
360
0
    PZ_Lock(td->cache->lock);
361
0
}
362
363
NSS_IMPLEMENT void
364
nssTrustDomain_UnlockCertCache(NSSTrustDomain *td)
365
0
{
366
0
    PZ_Unlock(td->cache->lock);
367
0
}
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
0
{
379
0
    NSSCertificate *c = (NSSCertificate *)k;
380
0
    nssPKIObject *object = &c->object;
381
0
    struct token_cert_dtor *dtor = a;
382
0
    PRUint32 i;
383
0
    nssPKIObject_AddRef(object);
384
0
    nssPKIObject_Lock(object);
385
0
    for (i = 0; i < object->numInstances; i++) {
386
0
        if (object->instances[i]->token == dtor->token) {
387
0
            nssCryptokiObject_Destroy(object->instances[i]);
388
0
            object->instances[i] = object->instances[object->numInstances - 1];
389
0
            object->instances[object->numInstances - 1] = NULL;
390
0
            object->numInstances--;
391
0
            dtor->certs[dtor->numCerts++] = c;
392
0
            if (dtor->numCerts == dtor->arrSize) {
393
0
                dtor->arrSize *= 2;
394
0
                dtor->certs = nss_ZREALLOCARRAY(dtor->certs,
395
0
                                                NSSCertificate *,
396
0
                                                dtor->arrSize);
397
0
            }
398
0
            break;
399
0
        }
400
0
    }
401
0
    nssPKIObject_Unlock(object);
402
0
    nssPKIObject_Destroy(object);
403
0
    return;
404
0
}
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
0
{
415
0
    NSSCertificate **certs;
416
0
    PRUint32 i, arrSize = 10;
417
0
    struct token_cert_dtor dtor;
418
0
    certs = nss_ZNEWARRAY(NULL, NSSCertificate *, arrSize);
419
0
    if (!certs) {
420
0
        return PR_FAILURE;
421
0
    }
422
0
    dtor.cache = td->cache;
423
0
    dtor.token = token;
424
0
    dtor.certs = certs;
425
0
    dtor.numCerts = 0;
426
0
    dtor.arrSize = arrSize;
427
0
    PZ_Lock(td->cache->lock);
428
0
    nssHash_Iterate(td->cache->issuerAndSN, remove_token_certs, &dtor);
429
0
    for (i = 0; i < dtor.numCerts; i++) {
430
0
        if (dtor.certs[i]->object.numInstances == 0) {
431
0
            nssTrustDomain_RemoveCertFromCacheLOCKED(td, dtor.certs[i]);
432
0
            dtor.certs[i] = NULL; /* skip this cert in the second for loop */
433
0
        } else {
434
            /* make sure it doesn't disappear on us before we finish */
435
0
            nssCertificate_AddRef(dtor.certs[i]);
436
0
        }
437
0
    }
438
0
    PZ_Unlock(td->cache->lock);
439
0
    for (i = 0; i < dtor.numCerts; i++) {
440
0
        if (dtor.certs[i]) {
441
0
            STAN_ForceCERTCertificateUpdate(dtor.certs[i]);
442
0
            nssCertificate_Destroy(dtor.certs[i]);
443
0
        }
444
0
    }
445
0
    nss_ZFreeIf(dtor.certs);
446
0
    return PR_SUCCESS;
447
0
}
448
449
NSS_IMPLEMENT PRStatus
450
nssTrustDomain_UpdateCachedTokenCerts(
451
    NSSTrustDomain *td,
452
    NSSToken *token)
453
0
{
454
0
    NSSCertificate **cp, **cached = NULL;
455
0
    nssList *certList;
456
0
    PRUint32 count;
457
0
    certList = nssList_Create(NULL, PR_FALSE);
458
0
    if (!certList)
459
0
        return PR_FAILURE;
460
0
    (void)nssTrustDomain_GetCertsFromCache(td, certList);
461
0
    count = nssList_Count(certList);
462
0
    if (count > 0) {
463
0
        cached = nss_ZNEWARRAY(NULL, NSSCertificate *, count + 1);
464
0
        if (!cached) {
465
0
            nssList_Destroy(certList);
466
0
            return PR_FAILURE;
467
0
        }
468
0
        nssList_GetArray(certList, (void **)cached, count);
469
0
        for (cp = cached; *cp; cp++) {
470
0
            nssCryptokiObject *instance;
471
0
            NSSCertificate *c = *cp;
472
0
            nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly;
473
0
            instance = nssToken_FindCertificateByIssuerAndSerialNumber(
474
0
                token,
475
0
                NULL,
476
0
                &c->issuer,
477
0
                &c->serial,
478
0
                tokenOnly,
479
0
                NULL);
480
0
            if (instance) {
481
0
                nssPKIObject_AddInstance(&c->object, instance);
482
0
                STAN_ForceCERTCertificateUpdate(c);
483
0
            }
484
0
        }
485
0
        nssCertificateArray_Destroy(cached);
486
0
    }
487
0
    nssList_Destroy(certList);
488
0
    return PR_SUCCESS;
489
0
}
490
491
static PRStatus
492
add_issuer_and_serial_entry(
493
    NSSArena *arena,
494
    nssTDCertificateCache *cache,
495
    NSSCertificate *cert)
496
0
{
497
0
    cache_entry *ce;
498
0
    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
0
    return nssHash_Add(cache->issuerAndSN, cert, (void *)ce);
503
0
}
504
505
static PRStatus
506
add_subject_entry(
507
    NSSArena *arena,
508
    nssTDCertificateCache *cache,
509
    NSSCertificate *cert,
510
    NSSUTF8 *nickname,
511
    nssList **subjectList)
512
0
{
513
0
    PRStatus nssrv;
514
0
    nssList *list;
515
0
    cache_entry *ce;
516
0
    *subjectList = NULL; /* this is only set if a new one is created */
517
0
    ce = (cache_entry *)nssHash_Lookup(cache->subject, &cert->subject);
518
0
    if (ce) {
519
0
        ce->hits++;
520
0
        ce->lastHit = PR_Now();
521
        /* The subject is already in, add this cert to the list */
522
0
        nssrv = nssList_AddUnique(ce->entry.list, cert);
523
#ifdef DEBUG_CACHE
524
        log_cert_ref("added to existing subject list", cert);
525
#endif
526
0
    } else {
527
0
        NSSDER *subject;
528
        /* Create a new subject list for the subject */
529
0
        list = nssList_Create(arena, PR_FALSE);
530
0
        if (!list) {
531
0
            return PR_FAILURE;
532
0
        }
533
0
        ce = new_cache_entry(arena, (void *)list, PR_TRUE);
534
0
        if (!ce) {
535
0
            return PR_FAILURE;
536
0
        }
537
0
        if (nickname) {
538
0
            ce->nickname = nssUTF8_Duplicate(nickname, arena);
539
0
        }
540
0
        nssList_SetSortFunction(list, nssCertificate_SubjectListSort);
541
        /* Add the cert entry to this list of subjects */
542
0
        nssrv = nssList_AddUnique(list, cert);
543
0
        if (nssrv != PR_SUCCESS) {
544
0
            return nssrv;
545
0
        }
546
        /* Add the subject list to the cache */
547
0
        subject = nssItem_Duplicate(&cert->subject, arena, NULL);
548
0
        if (!subject) {
549
0
            return PR_FAILURE;
550
0
        }
551
0
        nssrv = nssHash_Add(cache->subject, subject, ce);
552
0
        if (nssrv != PR_SUCCESS) {
553
0
            return nssrv;
554
0
        }
555
0
        *subjectList = list;
556
#ifdef DEBUG_CACHE
557
        log_cert_ref("created subject list", cert);
558
#endif
559
0
    }
560
0
    return nssrv;
561
0
}
562
563
static PRStatus
564
add_nickname_entry(
565
    NSSArena *arena,
566
    nssTDCertificateCache *cache,
567
    NSSUTF8 *certNickname,
568
    nssList *subjectList)
569
0
{
570
0
    PRStatus nssrv = PR_SUCCESS;
571
0
    cache_entry *ce;
572
0
    ce = (cache_entry *)nssHash_Lookup(cache->nickname, certNickname);
573
0
    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
0
        return PR_FAILURE;
579
0
    } else {
580
0
        NSSUTF8 *nickname;
581
0
        ce = new_cache_entry(arena, subjectList, PR_FALSE);
582
0
        if (!ce) {
583
0
            return PR_FAILURE;
584
0
        }
585
0
        nickname = nssUTF8_Duplicate(certNickname, arena);
586
0
        if (!nickname) {
587
0
            return PR_FAILURE;
588
0
        }
589
0
        nssrv = nssHash_Add(cache->nickname, nickname, ce);
590
#ifdef DEBUG_CACHE
591
        log_cert_ref("created nickname for", cert);
592
#endif
593
0
    }
594
0
    return nssrv;
595
0
}
596
597
static PRStatus
598
add_email_entry(
599
    nssTDCertificateCache *cache,
600
    NSSCertificate *cert,
601
    nssList *subjectList)
602
0
{
603
0
    PRStatus nssrv = PR_SUCCESS;
604
0
    nssList *subjects;
605
0
    cache_entry *ce;
606
0
    ce = (cache_entry *)nssHash_Lookup(cache->email, cert->email);
607
0
    if (ce) {
608
        /* Already have an entry for this email address, but not subject */
609
0
        subjects = ce->entry.list;
610
0
        nssrv = nssList_AddUnique(subjects, subjectList);
611
0
        ce->hits++;
612
0
        ce->lastHit = PR_Now();
613
#ifdef DEBUG_CACHE
614
        log_cert_ref("added subject to email for", cert);
615
#endif
616
0
    } else {
617
0
        NSSASCII7 *email;
618
0
        NSSArena *arena;
619
0
        arena = nssArena_Create();
620
0
        if (!arena) {
621
0
            return PR_FAILURE;
622
0
        }
623
        /* Create a new list of subject lists, add this subject */
624
0
        subjects = nssList_Create(arena, PR_TRUE);
625
0
        if (!subjects) {
626
0
            nssArena_Destroy(arena);
627
0
            return PR_FAILURE;
628
0
        }
629
        /* Add the new subject to the list */
630
0
        nssrv = nssList_AddUnique(subjects, subjectList);
631
0
        if (nssrv != PR_SUCCESS) {
632
0
            nssArena_Destroy(arena);
633
0
            return nssrv;
634
0
        }
635
        /* Add the new entry to the cache */
636
0
        ce = new_cache_entry(arena, (void *)subjects, PR_TRUE);
637
0
        if (!ce) {
638
0
            nssArena_Destroy(arena);
639
0
            return PR_FAILURE;
640
0
        }
641
0
        email = nssUTF8_Duplicate(cert->email, arena);
642
0
        if (!email) {
643
0
            nssArena_Destroy(arena);
644
0
            return PR_FAILURE;
645
0
        }
646
0
        nssrv = nssHash_Add(cache->email, email, ce);
647
0
        if (nssrv != PR_SUCCESS) {
648
0
            nssArena_Destroy(arena);
649
0
            return nssrv;
650
0
        }
651
#ifdef DEBUG_CACHE
652
        log_cert_ref("created email for", cert);
653
#endif
654
0
    }
655
0
    return nssrv;
656
0
}
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
0
{
666
0
    int i;
667
668
0
    for (i = 0; i < numInstances; i++) {
669
0
        nssPKIObject_RemoveInstanceForToken(object, instances[i]->token);
670
0
    }
671
0
}
672
673
static SECStatus
674
merge_object_instances(
675
    nssPKIObject *to,
676
    nssPKIObject *from)
677
0
{
678
0
    nssCryptokiObject **instances, **ci;
679
0
    int i;
680
0
    SECStatus rv = SECSuccess;
681
682
0
    instances = nssPKIObject_GetInstances(from);
683
0
    if (instances == NULL) {
684
0
        return SECFailure;
685
0
    }
686
0
    for (ci = instances, i = 0; *ci; ci++, i++) {
687
0
        nssCryptokiObject *instance = nssCryptokiObject_Clone(*ci);
688
0
        if (instance) {
689
0
            if (nssPKIObject_AddInstance(to, instance) == PR_SUCCESS) {
690
0
                continue;
691
0
            }
692
0
            nssCryptokiObject_Destroy(instance);
693
0
        }
694
0
        remove_object_instances(to, instances, i);
695
0
        rv = SECFailure;
696
0
        break;
697
0
    }
698
0
    nssCryptokiObjectArray_Destroy(instances);
699
0
    return rv;
700
0
}
701
702
static NSSCertificate *
703
add_cert_to_cache(
704
    NSSTrustDomain *td,
705
    NSSCertificate *cert)
706
0
{
707
0
    NSSArena *arena = NULL;
708
0
    nssList *subjectList = NULL;
709
0
    PRStatus nssrv;
710
0
    PRUint32 added = 0;
711
0
    cache_entry *ce;
712
0
    NSSCertificate *rvCert = NULL;
713
0
    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
0
    if (cert->type == NSSCertificateType_PKIX) {
720
0
        (void)STAN_GetCERTCertificate(cert);
721
0
    }
722
723
0
    PZ_Lock(td->cache->lock);
724
    /* If it exists in the issuer/serial hash, it's already in all */
725
0
    ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, cert);
726
0
    if (ce) {
727
0
        ce->hits++;
728
0
        ce->lastHit = PR_Now();
729
0
        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
0
        PZ_Unlock(td->cache->lock);
734
0
        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
0
        if (merge_object_instances(&rvCert->object, &cert->object) != SECSuccess) {
740
0
            nssCertificate_Destroy(rvCert);
741
0
            return NULL;
742
0
        }
743
0
        STAN_ForceCERTCertificateUpdate(rvCert);
744
0
        nssCertificate_Destroy(cert);
745
0
        return rvCert;
746
0
    }
747
    /* create a new cache entry for this cert within the cert's arena*/
748
0
    nssrv = add_issuer_and_serial_entry(cert->object.arena, td->cache, cert);
749
0
    if (nssrv != PR_SUCCESS) {
750
0
        goto loser;
751
0
    }
752
0
    added++;
753
    /* create an arena for the nickname and subject entries */
754
0
    arena = nssArena_Create();
755
0
    if (!arena) {
756
0
        goto loser;
757
0
    }
758
    /* create a new subject list for this cert, or add to existing */
759
0
    nssrv = add_subject_entry(arena, td->cache, cert,
760
0
                              certNickname, &subjectList);
761
0
    if (nssrv != PR_SUCCESS) {
762
0
        goto loser;
763
0
    }
764
0
    added++;
765
    /* If a new subject entry was created, also need nickname and/or email */
766
0
    if (subjectList != NULL) {
767
#ifdef nodef
768
        PRBool handle = PR_FALSE;
769
#endif
770
0
        if (certNickname) {
771
0
            nssrv = add_nickname_entry(arena, td->cache,
772
0
                                       certNickname, subjectList);
773
0
            if (nssrv != PR_SUCCESS) {
774
0
                goto loser;
775
0
            }
776
#ifdef nodef
777
            handle = PR_TRUE;
778
#endif
779
0
            added++;
780
0
        }
781
0
        if (cert->email) {
782
0
            nssrv = add_email_entry(td->cache, cert, subjectList);
783
0
            if (nssrv != PR_SUCCESS) {
784
0
                goto loser;
785
0
            }
786
#ifdef nodef
787
            handle = PR_TRUE;
788
#endif
789
0
            added += 2;
790
0
        }
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
0
    } else {
803
        /* A new subject entry was not created.  arena is unused. */
804
0
        nssArena_Destroy(arena);
805
0
    }
806
0
    rvCert = cert;
807
0
    PZ_Unlock(td->cache->lock);
808
0
    nss_ZFreeIf(certNickname);
809
0
    return rvCert;
810
0
loser:
811
0
    nss_ZFreeIf(certNickname);
812
0
    certNickname = NULL;
813
    /* Remove any handles that have been created */
814
0
    subjectList = NULL;
815
0
    if (added >= 1) {
816
0
        (void)remove_issuer_and_serial_entry(td->cache, cert);
817
0
    }
818
0
    if (added >= 2) {
819
0
        (void)remove_subject_entry(td->cache, cert, &subjectList,
820
0
                                   &certNickname, &arena);
821
0
    }
822
0
    if (added == 3 || added == 5) {
823
0
        (void)remove_nickname_entry(td->cache, certNickname, subjectList);
824
0
    }
825
0
    if (added >= 4) {
826
0
        (void)remove_email_entry(td->cache, cert, subjectList);
827
0
    }
828
0
    if (subjectList) {
829
0
        nssHash_Remove(td->cache->subject, &cert->subject);
830
0
        nssList_Destroy(subjectList);
831
0
    }
832
0
    if (arena) {
833
0
        nssArena_Destroy(arena);
834
0
    }
835
0
    PZ_Unlock(td->cache->lock);
836
0
    return NULL;
837
0
}
838
839
NSS_IMPLEMENT PRStatus
840
nssTrustDomain_AddCertsToCache(
841
    NSSTrustDomain *td,
842
    NSSCertificate **certs,
843
    PRUint32 numCerts)
844
0
{
845
0
    PRUint32 i;
846
0
    NSSCertificate *c;
847
0
    for (i = 0; i < numCerts && certs[i]; i++) {
848
0
        c = add_cert_to_cache(td, certs[i]);
849
0
        if (c == NULL) {
850
0
            return PR_FAILURE;
851
0
        } else {
852
0
            certs[i] = c;
853
0
        }
854
0
    }
855
0
    return PR_SUCCESS;
856
0
}
857
858
static NSSCertificate **
859
collect_subject_certs(
860
    nssList *subjectList,
861
    nssList *rvCertListOpt)
862
0
{
863
0
    NSSCertificate *c;
864
0
    NSSCertificate **rvArray = NULL;
865
0
    PRUint32 count;
866
0
    nssCertificateList_AddReferences(subjectList);
867
0
    if (rvCertListOpt) {
868
0
        nssListIterator *iter = nssList_CreateIterator(subjectList);
869
0
        if (!iter) {
870
0
            return (NSSCertificate **)NULL;
871
0
        }
872
0
        for (c = (NSSCertificate *)nssListIterator_Start(iter);
873
0
             c != (NSSCertificate *)NULL;
874
0
             c = (NSSCertificate *)nssListIterator_Next(iter)) {
875
0
            nssList_Add(rvCertListOpt, c);
876
0
        }
877
0
        nssListIterator_Finish(iter);
878
0
        nssListIterator_Destroy(iter);
879
0
    } else {
880
0
        count = nssList_Count(subjectList);
881
0
        rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count + 1);
882
0
        if (!rvArray) {
883
0
            return (NSSCertificate **)NULL;
884
0
        }
885
0
        nssList_GetArray(subjectList, (void **)rvArray, count);
886
0
    }
887
0
    return rvArray;
888
0
}
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
0
{
899
0
    NSSCertificate **rvArray = NULL;
900
0
    cache_entry *ce;
901
#ifdef DEBUG_CACHE
902
    log_item_dump("looking for cert by subject", subject);
903
#endif
904
0
    PZ_Lock(td->cache->lock);
905
0
    ce = (cache_entry *)nssHash_Lookup(td->cache->subject, subject);
906
0
    if (ce) {
907
0
        ce->hits++;
908
0
        ce->lastHit = PR_Now();
909
#ifdef DEBUG_CACHE
910
        PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits));
911
#endif
912
0
        rvArray = collect_subject_certs(ce->entry.list, certListOpt);
913
0
    }
914
0
    PZ_Unlock(td->cache->lock);
915
0
    return rvArray;
916
0
}
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
0
{
927
0
    NSSCertificate **rvArray = NULL;
928
0
    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
0
    PZ_Lock(td->cache->lock);
933
0
    ce = (cache_entry *)nssHash_Lookup(td->cache->nickname, nickname);
934
0
    if (ce) {
935
0
        ce->hits++;
936
0
        ce->lastHit = PR_Now();
937
#ifdef DEBUG_CACHE
938
        PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits));
939
#endif
940
0
        rvArray = collect_subject_certs(ce->entry.list, certListOpt);
941
0
    }
942
0
    PZ_Unlock(td->cache->lock);
943
0
    return rvArray;
944
0
}
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
0
{
955
0
    NSSCertificate **rvArray = NULL;
956
0
    cache_entry *ce;
957
0
    nssList *collectList = NULL;
958
0
    nssListIterator *iter = NULL;
959
0
    nssList *subjectList;
960
#ifdef DEBUG_CACHE
961
    PR_LOG(s_log, PR_LOG_DEBUG, ("looking for cert by email %s", email));
962
#endif
963
0
    PZ_Lock(td->cache->lock);
964
0
    ce = (cache_entry *)nssHash_Lookup(td->cache->email, email);
965
0
    if (ce) {
966
0
        ce->hits++;
967
0
        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
0
        if (certListOpt) {
973
0
            collectList = certListOpt;
974
0
        } else {
975
0
            collectList = nssList_Create(NULL, PR_FALSE);
976
0
            if (!collectList) {
977
0
                PZ_Unlock(td->cache->lock);
978
0
                return NULL;
979
0
            }
980
0
        }
981
0
        iter = nssList_CreateIterator(ce->entry.list);
982
0
        if (!iter) {
983
0
            PZ_Unlock(td->cache->lock);
984
0
            if (!certListOpt) {
985
0
                nssList_Destroy(collectList);
986
0
            }
987
0
            return NULL;
988
0
        }
989
0
        for (subjectList = (nssList *)nssListIterator_Start(iter);
990
0
             subjectList != (nssList *)NULL;
991
0
             subjectList = (nssList *)nssListIterator_Next(iter)) {
992
0
            (void)collect_subject_certs(subjectList, collectList);
993
0
        }
994
0
        nssListIterator_Finish(iter);
995
0
        nssListIterator_Destroy(iter);
996
0
    }
997
0
    PZ_Unlock(td->cache->lock);
998
0
    if (!certListOpt && collectList) {
999
0
        PRUint32 count = nssList_Count(collectList);
1000
0
        rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count);
1001
0
        if (rvArray) {
1002
0
            nssList_GetArray(collectList, (void **)rvArray, count);
1003
0
        }
1004
0
        nssList_Destroy(collectList);
1005
0
    }
1006
0
    return rvArray;
1007
0
}
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
0
{
1018
0
    NSSCertificate certkey;
1019
0
    NSSCertificate *rvCert = NULL;
1020
0
    cache_entry *ce;
1021
0
    certkey.issuer.data = issuer->data;
1022
0
    certkey.issuer.size = issuer->size;
1023
0
    certkey.serial.data = serial->data;
1024
0
    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
0
    PZ_Lock(td->cache->lock);
1030
0
    ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, &certkey);
1031
0
    if (ce) {
1032
0
        ce->hits++;
1033
0
        ce->lastHit = PR_Now();
1034
0
        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
0
    }
1039
0
    PZ_Unlock(td->cache->lock);
1040
0
    return rvCert;
1041
0
}
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
0
{
1051
0
    PRStatus nssrv = PR_FAILURE;
1052
0
    NSSDER issuer, serial;
1053
0
    NSSCertificate *rvCert;
1054
0
    nssrv = nssPKIX509_GetIssuerAndSerialFromDER(der, &issuer, &serial);
1055
0
    if (nssrv != PR_SUCCESS) {
1056
0
        return NULL;
1057
0
    }
1058
#ifdef DEBUG_CACHE
1059
    log_item_dump("looking for cert by DER", der);
1060
#endif
1061
0
    rvCert = nssTrustDomain_GetCertForIssuerAndSNFromCache(td,
1062
0
                                                           &issuer, &serial);
1063
0
    PORT_Free(issuer.data);
1064
0
    PORT_Free(serial.data);
1065
0
    return rvCert;
1066
0
}
1067
1068
static void
1069
cert_iter(const void *k, void *v, void *a)
1070
0
{
1071
0
    nssList *certList = (nssList *)a;
1072
0
    NSSCertificate *c = (NSSCertificate *)k;
1073
0
    nssList_Add(certList, nssCertificate_AddRef(c));
1074
0
}
1075
1076
NSS_EXTERN NSSCertificate **
1077
nssTrustDomain_GetCertsFromCache(
1078
    NSSTrustDomain *td,
1079
    nssList *certListOpt)
1080
0
{
1081
0
    NSSCertificate **rvArray = NULL;
1082
0
    nssList *certList;
1083
0
    if (certListOpt) {
1084
0
        certList = certListOpt;
1085
0
    } else {
1086
0
        certList = nssList_Create(NULL, PR_FALSE);
1087
0
        if (!certList) {
1088
0
            return NULL;
1089
0
        }
1090
0
    }
1091
0
    PZ_Lock(td->cache->lock);
1092
0
    nssHash_Iterate(td->cache->issuerAndSN, cert_iter, (void *)certList);
1093
0
    PZ_Unlock(td->cache->lock);
1094
0
    if (!certListOpt) {
1095
0
        PRUint32 count = nssList_Count(certList);
1096
0
        rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count);
1097
0
        nssList_GetArray(certList, (void **)rvArray, count);
1098
        /* array takes the references */
1099
0
        nssList_Destroy(certList);
1100
0
    }
1101
0
    return rvArray;
1102
0
}
1103
1104
NSS_IMPLEMENT void
1105
nssTrustDomain_DumpCacheInfo(
1106
    NSSTrustDomain *td,
1107
    void (*cert_dump_iter)(const void *, void *, void *),
1108
    void *arg)
1109
0
{
1110
0
    PZ_Lock(td->cache->lock);
1111
0
    nssHash_Iterate(td->cache->issuerAndSN, cert_dump_iter, arg);
1112
0
    PZ_Unlock(td->cache->lock);
1113
0
}