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