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