Mercurial > trustbridge > nss-cmake-static
comparison nss/lib/pki/tdcache.c @ 0:1e5118fa0cb1
This is NSS with a Cmake Buildsyste
To compile a static NSS library for Windows we've used the
Chromium-NSS fork and added a Cmake buildsystem to compile
it statically for Windows. See README.chromium for chromium
changes and README.trustbridge for our modifications.
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Mon, 28 Jul 2014 10:47:06 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:1e5118fa0cb1 |
---|---|
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; | |
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_Lock(object); | |
395 for (i=0; i<object->numInstances; i++) { | |
396 if (object->instances[i]->token == dtor->token) { | |
397 nssCryptokiObject_Destroy(object->instances[i]); | |
398 object->instances[i] = object->instances[object->numInstances-1]; | |
399 object->instances[object->numInstances-1] = NULL; | |
400 object->numInstances--; | |
401 dtor->certs[dtor->numCerts++] = c; | |
402 if (dtor->numCerts == dtor->arrSize) { | |
403 dtor->arrSize *= 2; | |
404 dtor->certs = nss_ZREALLOCARRAY(dtor->certs, | |
405 NSSCertificate *, | |
406 dtor->arrSize); | |
407 } | |
408 break; | |
409 } | |
410 } | |
411 nssPKIObject_Unlock(object); | |
412 return; | |
413 } | |
414 | |
415 /* | |
416 * Remove all certs for the given token from the cache. This is | |
417 * needed if the token is removed. | |
418 */ | |
419 NSS_IMPLEMENT PRStatus | |
420 nssTrustDomain_RemoveTokenCertsFromCache ( | |
421 NSSTrustDomain *td, | |
422 NSSToken *token | |
423 ) | |
424 { | |
425 NSSCertificate **certs; | |
426 PRUint32 i, arrSize = 10; | |
427 struct token_cert_dtor dtor; | |
428 certs = nss_ZNEWARRAY(NULL, NSSCertificate *, arrSize); | |
429 if (!certs) { | |
430 return PR_FAILURE; | |
431 } | |
432 dtor.cache = td->cache; | |
433 dtor.token = token; | |
434 dtor.certs = certs; | |
435 dtor.numCerts = 0; | |
436 dtor.arrSize = arrSize; | |
437 PZ_Lock(td->cache->lock); | |
438 nssHash_Iterate(td->cache->issuerAndSN, remove_token_certs, (void *)&dtor); | |
439 for (i=0; i<dtor.numCerts; i++) { | |
440 if (dtor.certs[i]->object.numInstances == 0) { | |
441 nssTrustDomain_RemoveCertFromCacheLOCKED(td, dtor.certs[i]); | |
442 dtor.certs[i] = NULL; /* skip this cert in the second for loop */ | |
443 } | |
444 } | |
445 PZ_Unlock(td->cache->lock); | |
446 for (i=0; i<dtor.numCerts; i++) { | |
447 if (dtor.certs[i]) { | |
448 STAN_ForceCERTCertificateUpdate(dtor.certs[i]); | |
449 } | |
450 } | |
451 nss_ZFreeIf(dtor.certs); | |
452 return PR_SUCCESS; | |
453 } | |
454 | |
455 NSS_IMPLEMENT PRStatus | |
456 nssTrustDomain_UpdateCachedTokenCerts ( | |
457 NSSTrustDomain *td, | |
458 NSSToken *token | |
459 ) | |
460 { | |
461 NSSCertificate **cp, **cached = NULL; | |
462 nssList *certList; | |
463 PRUint32 count; | |
464 certList = nssList_Create(NULL, PR_FALSE); | |
465 if (!certList) return PR_FAILURE; | |
466 (void)nssTrustDomain_GetCertsFromCache(td, certList); | |
467 count = nssList_Count(certList); | |
468 if (count > 0) { | |
469 cached = nss_ZNEWARRAY(NULL, NSSCertificate *, count + 1); | |
470 if (!cached) { | |
471 nssList_Destroy(certList); | |
472 return PR_FAILURE; | |
473 } | |
474 nssList_GetArray(certList, (void **)cached, count); | |
475 for (cp = cached; *cp; cp++) { | |
476 nssCryptokiObject *instance; | |
477 NSSCertificate *c = *cp; | |
478 nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly; | |
479 instance = nssToken_FindCertificateByIssuerAndSerialNumber( | |
480 token, | |
481 NULL, | |
482 &c->issuer, | |
483 &c->serial, | |
484 tokenOnly, | |
485 NULL); | |
486 if (instance) { | |
487 nssPKIObject_AddInstance(&c->object, instance); | |
488 STAN_ForceCERTCertificateUpdate(c); | |
489 } | |
490 } | |
491 nssCertificateArray_Destroy(cached); | |
492 } | |
493 nssList_Destroy(certList); | |
494 return PR_SUCCESS; | |
495 } | |
496 | |
497 static PRStatus | |
498 add_issuer_and_serial_entry ( | |
499 NSSArena *arena, | |
500 nssTDCertificateCache *cache, | |
501 NSSCertificate *cert | |
502 ) | |
503 { | |
504 cache_entry *ce; | |
505 ce = new_cache_entry(arena, (void *)cert, PR_FALSE); | |
506 #ifdef DEBUG_CACHE | |
507 log_cert_ref("added to issuer/sn", cert); | |
508 #endif | |
509 return nssHash_Add(cache->issuerAndSN, cert, (void *)ce); | |
510 } | |
511 | |
512 static PRStatus | |
513 add_subject_entry ( | |
514 NSSArena *arena, | |
515 nssTDCertificateCache *cache, | |
516 NSSCertificate *cert, | |
517 NSSUTF8 *nickname, | |
518 nssList **subjectList | |
519 ) | |
520 { | |
521 PRStatus nssrv; | |
522 nssList *list; | |
523 cache_entry *ce; | |
524 *subjectList = NULL; /* this is only set if a new one is created */ | |
525 ce = (cache_entry *)nssHash_Lookup(cache->subject, &cert->subject); | |
526 if (ce) { | |
527 ce->hits++; | |
528 ce->lastHit = PR_Now(); | |
529 /* The subject is already in, add this cert to the list */ | |
530 nssrv = nssList_AddUnique(ce->entry.list, cert); | |
531 #ifdef DEBUG_CACHE | |
532 log_cert_ref("added to existing subject list", cert); | |
533 #endif | |
534 } else { | |
535 NSSDER *subject; | |
536 /* Create a new subject list for the subject */ | |
537 list = nssList_Create(arena, PR_FALSE); | |
538 if (!list) { | |
539 return PR_FAILURE; | |
540 } | |
541 ce = new_cache_entry(arena, (void *)list, PR_TRUE); | |
542 if (!ce) { | |
543 return PR_FAILURE; | |
544 } | |
545 if (nickname) { | |
546 ce->nickname = nssUTF8_Duplicate(nickname, arena); | |
547 } | |
548 nssList_SetSortFunction(list, nssCertificate_SubjectListSort); | |
549 /* Add the cert entry to this list of subjects */ | |
550 nssrv = nssList_AddUnique(list, cert); | |
551 if (nssrv != PR_SUCCESS) { | |
552 return nssrv; | |
553 } | |
554 /* Add the subject list to the cache */ | |
555 subject = nssItem_Duplicate(&cert->subject, arena, NULL); | |
556 if (!subject) { | |
557 return PR_FAILURE; | |
558 } | |
559 nssrv = nssHash_Add(cache->subject, subject, ce); | |
560 if (nssrv != PR_SUCCESS) { | |
561 return nssrv; | |
562 } | |
563 *subjectList = list; | |
564 #ifdef DEBUG_CACHE | |
565 log_cert_ref("created subject list", cert); | |
566 #endif | |
567 } | |
568 return nssrv; | |
569 } | |
570 | |
571 static PRStatus | |
572 add_nickname_entry ( | |
573 NSSArena *arena, | |
574 nssTDCertificateCache *cache, | |
575 NSSUTF8 *certNickname, | |
576 nssList *subjectList | |
577 ) | |
578 { | |
579 PRStatus nssrv = PR_SUCCESS; | |
580 cache_entry *ce; | |
581 ce = (cache_entry *)nssHash_Lookup(cache->nickname, certNickname); | |
582 if (ce) { | |
583 /* This is a collision. A nickname entry already exists for this | |
584 * subject, but a subject entry didn't. This would imply there are | |
585 * two subjects using the same nickname, which is not allowed. | |
586 */ | |
587 return PR_FAILURE; | |
588 } else { | |
589 NSSUTF8 *nickname; | |
590 ce = new_cache_entry(arena, subjectList, PR_FALSE); | |
591 if (!ce) { | |
592 return PR_FAILURE; | |
593 } | |
594 nickname = nssUTF8_Duplicate(certNickname, arena); | |
595 if (!nickname) { | |
596 return PR_FAILURE; | |
597 } | |
598 nssrv = nssHash_Add(cache->nickname, nickname, ce); | |
599 #ifdef DEBUG_CACHE | |
600 log_cert_ref("created nickname for", cert); | |
601 #endif | |
602 } | |
603 return nssrv; | |
604 } | |
605 | |
606 static PRStatus | |
607 add_email_entry ( | |
608 nssTDCertificateCache *cache, | |
609 NSSCertificate *cert, | |
610 nssList *subjectList | |
611 ) | |
612 { | |
613 PRStatus nssrv = PR_SUCCESS; | |
614 nssList *subjects; | |
615 cache_entry *ce; | |
616 ce = (cache_entry *)nssHash_Lookup(cache->email, cert->email); | |
617 if (ce) { | |
618 /* Already have an entry for this email address, but not subject */ | |
619 subjects = ce->entry.list; | |
620 nssrv = nssList_AddUnique(subjects, subjectList); | |
621 ce->hits++; | |
622 ce->lastHit = PR_Now(); | |
623 #ifdef DEBUG_CACHE | |
624 log_cert_ref("added subject to email for", cert); | |
625 #endif | |
626 } else { | |
627 NSSASCII7 *email; | |
628 NSSArena *arena; | |
629 arena = nssArena_Create(); | |
630 if (!arena) { | |
631 return PR_FAILURE; | |
632 } | |
633 /* Create a new list of subject lists, add this subject */ | |
634 subjects = nssList_Create(arena, PR_TRUE); | |
635 if (!subjects) { | |
636 nssArena_Destroy(arena); | |
637 return PR_FAILURE; | |
638 } | |
639 /* Add the new subject to the list */ | |
640 nssrv = nssList_AddUnique(subjects, subjectList); | |
641 if (nssrv != PR_SUCCESS) { | |
642 nssArena_Destroy(arena); | |
643 return nssrv; | |
644 } | |
645 /* Add the new entry to the cache */ | |
646 ce = new_cache_entry(arena, (void *)subjects, PR_TRUE); | |
647 if (!ce) { | |
648 nssArena_Destroy(arena); | |
649 return PR_FAILURE; | |
650 } | |
651 email = nssUTF8_Duplicate(cert->email, arena); | |
652 if (!email) { | |
653 nssArena_Destroy(arena); | |
654 return PR_FAILURE; | |
655 } | |
656 nssrv = nssHash_Add(cache->email, email, ce); | |
657 if (nssrv != PR_SUCCESS) { | |
658 nssArena_Destroy(arena); | |
659 return nssrv; | |
660 } | |
661 #ifdef DEBUG_CACHE | |
662 log_cert_ref("created email for", cert); | |
663 #endif | |
664 } | |
665 return nssrv; | |
666 } | |
667 | |
668 extern const NSSError NSS_ERROR_CERTIFICATE_IN_CACHE; | |
669 | |
670 static void | |
671 remove_object_instances ( | |
672 nssPKIObject *object, | |
673 nssCryptokiObject **instances, | |
674 int numInstances | |
675 ) | |
676 { | |
677 int i; | |
678 | |
679 for (i = 0; i < numInstances; i++) { | |
680 nssPKIObject_RemoveInstanceForToken(object, instances[i]->token); | |
681 } | |
682 } | |
683 | |
684 static SECStatus | |
685 merge_object_instances ( | |
686 nssPKIObject *to, | |
687 nssPKIObject *from | |
688 ) | |
689 { | |
690 nssCryptokiObject **instances, **ci; | |
691 int i; | |
692 SECStatus rv = SECSuccess; | |
693 | |
694 instances = nssPKIObject_GetInstances(from); | |
695 if (instances == NULL) { | |
696 return SECFailure; | |
697 } | |
698 for (ci = instances, i = 0; *ci; ci++, i++) { | |
699 nssCryptokiObject *instance = nssCryptokiObject_Clone(*ci); | |
700 if (instance) { | |
701 if (nssPKIObject_AddInstance(to, instance) == PR_SUCCESS) { | |
702 continue; | |
703 } | |
704 nssCryptokiObject_Destroy(instance); | |
705 } | |
706 remove_object_instances(to, instances, i); | |
707 rv = SECFailure; | |
708 break; | |
709 } | |
710 nssCryptokiObjectArray_Destroy(instances); | |
711 return rv; | |
712 } | |
713 | |
714 static NSSCertificate * | |
715 add_cert_to_cache ( | |
716 NSSTrustDomain *td, | |
717 NSSCertificate *cert | |
718 ) | |
719 { | |
720 NSSArena *arena = NULL; | |
721 nssList *subjectList = NULL; | |
722 PRStatus nssrv; | |
723 PRUint32 added = 0; | |
724 cache_entry *ce; | |
725 NSSCertificate *rvCert = NULL; | |
726 NSSUTF8 *certNickname = nssCertificate_GetNickname(cert, NULL); | |
727 | |
728 PZ_Lock(td->cache->lock); | |
729 /* If it exists in the issuer/serial hash, it's already in all */ | |
730 ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, cert); | |
731 if (ce) { | |
732 ce->hits++; | |
733 ce->lastHit = PR_Now(); | |
734 rvCert = nssCertificate_AddRef(ce->entry.cert); | |
735 #ifdef DEBUG_CACHE | |
736 log_cert_ref("attempted to add cert already in cache", cert); | |
737 #endif | |
738 PZ_Unlock(td->cache->lock); | |
739 nss_ZFreeIf(certNickname); | |
740 /* collision - somebody else already added the cert | |
741 * to the cache before this thread got around to it. | |
742 */ | |
743 /* merge the instances of the cert */ | |
744 if (merge_object_instances(&rvCert->object, &cert->object) | |
745 != SECSuccess) { | |
746 nssCertificate_Destroy(rvCert); | |
747 return NULL; | |
748 } | |
749 STAN_ForceCERTCertificateUpdate(rvCert); | |
750 nssCertificate_Destroy(cert); | |
751 return rvCert; | |
752 } | |
753 /* create a new cache entry for this cert within the cert's arena*/ | |
754 nssrv = add_issuer_and_serial_entry(cert->object.arena, td->cache, cert); | |
755 if (nssrv != PR_SUCCESS) { | |
756 goto loser; | |
757 } | |
758 added++; | |
759 /* create an arena for the nickname and subject entries */ | |
760 arena = nssArena_Create(); | |
761 if (!arena) { | |
762 goto loser; | |
763 } | |
764 /* create a new subject list for this cert, or add to existing */ | |
765 nssrv = add_subject_entry(arena, td->cache, cert, | |
766 certNickname, &subjectList); | |
767 if (nssrv != PR_SUCCESS) { | |
768 goto loser; | |
769 } | |
770 added++; | |
771 /* If a new subject entry was created, also need nickname and/or email */ | |
772 if (subjectList != NULL) { | |
773 PRBool handle = PR_FALSE; | |
774 if (certNickname) { | |
775 nssrv = add_nickname_entry(arena, td->cache, | |
776 certNickname, subjectList); | |
777 if (nssrv != PR_SUCCESS) { | |
778 goto loser; | |
779 } | |
780 handle = PR_TRUE; | |
781 added++; | |
782 } | |
783 if (cert->email) { | |
784 nssrv = add_email_entry(td->cache, cert, subjectList); | |
785 if (nssrv != PR_SUCCESS) { | |
786 goto loser; | |
787 } | |
788 handle = PR_TRUE; | |
789 added += 2; | |
790 } | |
791 #ifdef nodef | |
792 /* I think either a nickname or email address must be associated | |
793 * with the cert. However, certs are passed to NewTemp without | |
794 * either. This worked in the old code, so it must work now. | |
795 */ | |
796 if (!handle) { | |
797 /* Require either nickname or email handle */ | |
798 nssrv = PR_FAILURE; | |
799 goto loser; | |
800 } | |
801 #endif | |
802 } else { | |
803 /* A new subject entry was not created. arena is unused. */ | |
804 nssArena_Destroy(arena); | |
805 } | |
806 rvCert = cert; | |
807 PZ_Unlock(td->cache->lock); | |
808 nss_ZFreeIf(certNickname); | |
809 return rvCert; | |
810 loser: | |
811 nss_ZFreeIf(certNickname); | |
812 certNickname = NULL; | |
813 /* Remove any handles that have been created */ | |
814 subjectList = NULL; | |
815 if (added >= 1) { | |
816 (void)remove_issuer_and_serial_entry(td->cache, cert); | |
817 } | |
818 if (added >= 2) { | |
819 (void)remove_subject_entry(td->cache, cert, &subjectList, | |
820 &certNickname, &arena); | |
821 } | |
822 if (added == 3 || added == 5) { | |
823 (void)remove_nickname_entry(td->cache, certNickname, subjectList); | |
824 } | |
825 if (added >= 4) { | |
826 (void)remove_email_entry(td->cache, cert, subjectList); | |
827 } | |
828 if (subjectList) { | |
829 nssHash_Remove(td->cache->subject, &cert->subject); | |
830 nssList_Destroy(subjectList); | |
831 } | |
832 if (arena) { | |
833 nssArena_Destroy(arena); | |
834 } | |
835 PZ_Unlock(td->cache->lock); | |
836 return NULL; | |
837 } | |
838 | |
839 NSS_IMPLEMENT PRStatus | |
840 nssTrustDomain_AddCertsToCache ( | |
841 NSSTrustDomain *td, | |
842 NSSCertificate **certs, | |
843 PRUint32 numCerts | |
844 ) | |
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 ** | |
860 collect_subject_certs ( | |
861 nssList *subjectList, | |
862 nssList *rvCertListOpt | |
863 ) | |
864 { | |
865 NSSCertificate *c; | |
866 NSSCertificate **rvArray = NULL; | |
867 PRUint32 count; | |
868 nssCertificateList_AddReferences(subjectList); | |
869 if (rvCertListOpt) { | |
870 nssListIterator *iter = nssList_CreateIterator(subjectList); | |
871 if (!iter) { | |
872 return (NSSCertificate **)NULL; | |
873 } | |
874 for (c = (NSSCertificate *)nssListIterator_Start(iter); | |
875 c != (NSSCertificate *)NULL; | |
876 c = (NSSCertificate *)nssListIterator_Next(iter)) { | |
877 nssList_Add(rvCertListOpt, c); | |
878 } | |
879 nssListIterator_Finish(iter); | |
880 nssListIterator_Destroy(iter); | |
881 } else { | |
882 count = nssList_Count(subjectList); | |
883 rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count + 1); | |
884 if (!rvArray) { | |
885 return (NSSCertificate **)NULL; | |
886 } | |
887 nssList_GetArray(subjectList, (void **)rvArray, count); | |
888 } | |
889 return rvArray; | |
890 } | |
891 | |
892 /* | |
893 * Find all cached certs with this subject. | |
894 */ | |
895 NSS_IMPLEMENT NSSCertificate ** | |
896 nssTrustDomain_GetCertsForSubjectFromCache ( | |
897 NSSTrustDomain *td, | |
898 NSSDER *subject, | |
899 nssList *certListOpt | |
900 ) | |
901 { | |
902 NSSCertificate **rvArray = NULL; | |
903 cache_entry *ce; | |
904 #ifdef DEBUG_CACHE | |
905 log_item_dump("looking for cert by subject", subject); | |
906 #endif | |
907 PZ_Lock(td->cache->lock); | |
908 ce = (cache_entry *)nssHash_Lookup(td->cache->subject, subject); | |
909 if (ce) { | |
910 ce->hits++; | |
911 ce->lastHit = PR_Now(); | |
912 #ifdef DEBUG_CACHE | |
913 PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits)); | |
914 #endif | |
915 rvArray = collect_subject_certs(ce->entry.list, certListOpt); | |
916 } | |
917 PZ_Unlock(td->cache->lock); | |
918 return rvArray; | |
919 } | |
920 | |
921 /* | |
922 * Find all cached certs with this label. | |
923 */ | |
924 NSS_IMPLEMENT NSSCertificate ** | |
925 nssTrustDomain_GetCertsForNicknameFromCache ( | |
926 NSSTrustDomain *td, | |
927 const NSSUTF8 *nickname, | |
928 nssList *certListOpt | |
929 ) | |
930 { | |
931 NSSCertificate **rvArray = NULL; | |
932 cache_entry *ce; | |
933 #ifdef DEBUG_CACHE | |
934 PR_LOG(s_log, PR_LOG_DEBUG, ("looking for cert by nick %s", nickname)); | |
935 #endif | |
936 PZ_Lock(td->cache->lock); | |
937 ce = (cache_entry *)nssHash_Lookup(td->cache->nickname, nickname); | |
938 if (ce) { | |
939 ce->hits++; | |
940 ce->lastHit = PR_Now(); | |
941 #ifdef DEBUG_CACHE | |
942 PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits)); | |
943 #endif | |
944 rvArray = collect_subject_certs(ce->entry.list, certListOpt); | |
945 } | |
946 PZ_Unlock(td->cache->lock); | |
947 return rvArray; | |
948 } | |
949 | |
950 /* | |
951 * Find all cached certs with this email address. | |
952 */ | |
953 NSS_IMPLEMENT NSSCertificate ** | |
954 nssTrustDomain_GetCertsForEmailAddressFromCache ( | |
955 NSSTrustDomain *td, | |
956 NSSASCII7 *email, | |
957 nssList *certListOpt | |
958 ) | |
959 { | |
960 NSSCertificate **rvArray = NULL; | |
961 cache_entry *ce; | |
962 nssList *collectList = NULL; | |
963 nssListIterator *iter = NULL; | |
964 nssList *subjectList; | |
965 #ifdef DEBUG_CACHE | |
966 PR_LOG(s_log, PR_LOG_DEBUG, ("looking for cert by email %s", email)); | |
967 #endif | |
968 PZ_Lock(td->cache->lock); | |
969 ce = (cache_entry *)nssHash_Lookup(td->cache->email, email); | |
970 if (ce) { | |
971 ce->hits++; | |
972 ce->lastHit = PR_Now(); | |
973 #ifdef DEBUG_CACHE | |
974 PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits)); | |
975 #endif | |
976 /* loop over subject lists and get refs for certs */ | |
977 if (certListOpt) { | |
978 collectList = certListOpt; | |
979 } else { | |
980 collectList = nssList_Create(NULL, PR_FALSE); | |
981 if (!collectList) { | |
982 PZ_Unlock(td->cache->lock); | |
983 return NULL; | |
984 } | |
985 } | |
986 iter = nssList_CreateIterator(ce->entry.list); | |
987 if (!iter) { | |
988 PZ_Unlock(td->cache->lock); | |
989 if (!certListOpt) { | |
990 nssList_Destroy(collectList); | |
991 } | |
992 return NULL; | |
993 } | |
994 for (subjectList = (nssList *)nssListIterator_Start(iter); | |
995 subjectList != (nssList *)NULL; | |
996 subjectList = (nssList *)nssListIterator_Next(iter)) { | |
997 (void)collect_subject_certs(subjectList, collectList); | |
998 } | |
999 nssListIterator_Finish(iter); | |
1000 nssListIterator_Destroy(iter); | |
1001 } | |
1002 PZ_Unlock(td->cache->lock); | |
1003 if (!certListOpt && collectList) { | |
1004 PRUint32 count = nssList_Count(collectList); | |
1005 rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count); | |
1006 if (rvArray) { | |
1007 nssList_GetArray(collectList, (void **)rvArray, count); | |
1008 } | |
1009 nssList_Destroy(collectList); | |
1010 } | |
1011 return rvArray; | |
1012 } | |
1013 | |
1014 /* | |
1015 * Look for a specific cert in the cache | |
1016 */ | |
1017 NSS_IMPLEMENT NSSCertificate * | |
1018 nssTrustDomain_GetCertForIssuerAndSNFromCache ( | |
1019 NSSTrustDomain *td, | |
1020 NSSDER *issuer, | |
1021 NSSDER *serial | |
1022 ) | |
1023 { | |
1024 NSSCertificate certkey; | |
1025 NSSCertificate *rvCert = NULL; | |
1026 cache_entry *ce; | |
1027 certkey.issuer.data = issuer->data; | |
1028 certkey.issuer.size = issuer->size; | |
1029 certkey.serial.data = serial->data; | |
1030 certkey.serial.size = serial->size; | |
1031 #ifdef DEBUG_CACHE | |
1032 log_item_dump("looking for cert by issuer/sn, issuer", issuer); | |
1033 log_item_dump(" serial", serial); | |
1034 #endif | |
1035 PZ_Lock(td->cache->lock); | |
1036 ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, &certkey); | |
1037 if (ce) { | |
1038 ce->hits++; | |
1039 ce->lastHit = PR_Now(); | |
1040 rvCert = nssCertificate_AddRef(ce->entry.cert); | |
1041 #ifdef DEBUG_CACHE | |
1042 PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits)); | |
1043 #endif | |
1044 } | |
1045 PZ_Unlock(td->cache->lock); | |
1046 return rvCert; | |
1047 } | |
1048 | |
1049 static PRStatus | |
1050 issuer_and_serial_from_encoding ( | |
1051 NSSBER *encoding, | |
1052 NSSDER *issuer, | |
1053 NSSDER *serial | |
1054 ) | |
1055 { | |
1056 SECItem derCert, derIssuer, derSerial; | |
1057 SECStatus secrv; | |
1058 derCert.data = (unsigned char *)encoding->data; | |
1059 derCert.len = encoding->size; | |
1060 secrv = CERT_IssuerNameFromDERCert(&derCert, &derIssuer); | |
1061 if (secrv != SECSuccess) { | |
1062 return PR_FAILURE; | |
1063 } | |
1064 secrv = CERT_SerialNumberFromDERCert(&derCert, &derSerial); | |
1065 if (secrv != SECSuccess) { | |
1066 return PR_FAILURE; | |
1067 } | |
1068 issuer->data = derIssuer.data; | |
1069 issuer->size = derIssuer.len; | |
1070 serial->data = derSerial.data; | |
1071 serial->size = derSerial.len; | |
1072 return PR_SUCCESS; | |
1073 } | |
1074 | |
1075 /* | |
1076 * Look for a specific cert in the cache | |
1077 */ | |
1078 NSS_IMPLEMENT NSSCertificate * | |
1079 nssTrustDomain_GetCertByDERFromCache ( | |
1080 NSSTrustDomain *td, | |
1081 NSSDER *der | |
1082 ) | |
1083 { | |
1084 PRStatus nssrv = PR_FAILURE; | |
1085 NSSDER issuer, serial; | |
1086 NSSCertificate *rvCert; | |
1087 nssrv = issuer_and_serial_from_encoding(der, &issuer, &serial); | |
1088 if (nssrv != PR_SUCCESS) { | |
1089 return NULL; | |
1090 } | |
1091 #ifdef DEBUG_CACHE | |
1092 log_item_dump("looking for cert by DER", der); | |
1093 #endif | |
1094 rvCert = nssTrustDomain_GetCertForIssuerAndSNFromCache(td, | |
1095 &issuer, &serial); | |
1096 PORT_Free(issuer.data); | |
1097 PORT_Free(serial.data); | |
1098 return rvCert; | |
1099 } | |
1100 | |
1101 static void cert_iter(const void *k, void *v, void *a) | |
1102 { | |
1103 nssList *certList = (nssList *)a; | |
1104 NSSCertificate *c = (NSSCertificate *)k; | |
1105 nssList_Add(certList, nssCertificate_AddRef(c)); | |
1106 } | |
1107 | |
1108 NSS_EXTERN NSSCertificate ** | |
1109 nssTrustDomain_GetCertsFromCache ( | |
1110 NSSTrustDomain *td, | |
1111 nssList *certListOpt | |
1112 ) | |
1113 { | |
1114 NSSCertificate **rvArray = NULL; | |
1115 nssList *certList; | |
1116 if (certListOpt) { | |
1117 certList = certListOpt; | |
1118 } else { | |
1119 certList = nssList_Create(NULL, PR_FALSE); | |
1120 if (!certList) { | |
1121 return NULL; | |
1122 } | |
1123 } | |
1124 PZ_Lock(td->cache->lock); | |
1125 nssHash_Iterate(td->cache->issuerAndSN, cert_iter, (void *)certList); | |
1126 PZ_Unlock(td->cache->lock); | |
1127 if (!certListOpt) { | |
1128 PRUint32 count = nssList_Count(certList); | |
1129 rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count); | |
1130 nssList_GetArray(certList, (void **)rvArray, count); | |
1131 /* array takes the references */ | |
1132 nssList_Destroy(certList); | |
1133 } | |
1134 return rvArray; | |
1135 } | |
1136 | |
1137 NSS_IMPLEMENT void | |
1138 nssTrustDomain_DumpCacheInfo ( | |
1139 NSSTrustDomain *td, | |
1140 void (* cert_dump_iter)(const void *, void *, void *), | |
1141 void *arg | |
1142 ) | |
1143 { | |
1144 PZ_Lock(td->cache->lock); | |
1145 nssHash_Iterate(td->cache->issuerAndSN, cert_dump_iter, arg); | |
1146 PZ_Unlock(td->cache->lock); | |
1147 } |