Bug Summary

File:s/lib/pki/tdcache.c
Warning:line 789, column 13
Value stored to 'added' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

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