Coverage Report

Created: 2018-09-25 14:53

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