Bug Summary

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