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