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