1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/lib/pki/tdcache.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1147 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#ifndef PKIM_H 1.9 +#include "pkim.h" 1.10 +#endif /* PKIM_H */ 1.11 + 1.12 +#ifndef PKIT_H 1.13 +#include "pkit.h" 1.14 +#endif /* PKIT_H */ 1.15 + 1.16 +#ifndef NSSPKI_H 1.17 +#include "nsspki.h" 1.18 +#endif /* NSSPKI_H */ 1.19 + 1.20 +#ifndef PKI_H 1.21 +#include "pki.h" 1.22 +#endif /* PKI_H */ 1.23 + 1.24 +#ifndef NSSBASE_H 1.25 +#include "nssbase.h" 1.26 +#endif /* NSSBASE_H */ 1.27 + 1.28 +#ifndef BASE_H 1.29 +#include "base.h" 1.30 +#endif /* BASE_H */ 1.31 + 1.32 +#include "cert.h" 1.33 +#include "dev.h" 1.34 +#include "pki3hack.h" 1.35 + 1.36 +#ifdef DEBUG_CACHE 1.37 +static PRLogModuleInfo *s_log = NULL; 1.38 +#endif 1.39 + 1.40 +#ifdef DEBUG_CACHE 1.41 +static void log_item_dump(const char *msg, NSSItem *it) 1.42 +{ 1.43 + char buf[33]; 1.44 + int i, j; 1.45 + for (i=0; i<10 && i<it->size; i++) { 1.46 + sprintf(&buf[2*i], "%02X", ((PRUint8 *)it->data)[i]); 1.47 + } 1.48 + if (it->size>10) { 1.49 + sprintf(&buf[2*i], ".."); 1.50 + i += 1; 1.51 + for (j=it->size-1; i<=16 && j>10; i++, j--) { 1.52 + sprintf(&buf[2*i], "%02X", ((PRUint8 *)it->data)[j]); 1.53 + } 1.54 + } 1.55 + PR_LOG(s_log, PR_LOG_DEBUG, ("%s: %s", msg, buf)); 1.56 +} 1.57 +#endif 1.58 + 1.59 +#ifdef DEBUG_CACHE 1.60 +static void log_cert_ref(const char *msg, NSSCertificate *c) 1.61 +{ 1.62 + PR_LOG(s_log, PR_LOG_DEBUG, ("%s: %s", msg, 1.63 + (c->nickname) ? c->nickname : c->email)); 1.64 + log_item_dump("\tserial", &c->serial); 1.65 + log_item_dump("\tsubject", &c->subject); 1.66 +} 1.67 +#endif 1.68 + 1.69 +/* Certificate cache routines */ 1.70 + 1.71 +/* XXX 1.72 + * Locking is not handled well at all. A single, global lock with sub-locks 1.73 + * in the collection types. Cleanup needed. 1.74 + */ 1.75 + 1.76 +/* should it live in its own arena? */ 1.77 +struct nssTDCertificateCacheStr 1.78 +{ 1.79 + PZLock *lock; 1.80 + NSSArena *arena; 1.81 + nssHash *issuerAndSN; 1.82 + nssHash *subject; 1.83 + nssHash *nickname; 1.84 + nssHash *email; 1.85 +}; 1.86 + 1.87 +struct cache_entry_str 1.88 +{ 1.89 + union { 1.90 + NSSCertificate *cert; 1.91 + nssList *list; 1.92 + void *value; 1.93 + } entry; 1.94 + PRUint32 hits; 1.95 + PRTime lastHit; 1.96 + NSSArena *arena; 1.97 + NSSUTF8 *nickname; 1.98 +}; 1.99 + 1.100 +typedef struct cache_entry_str cache_entry; 1.101 + 1.102 +static cache_entry * 1.103 +new_cache_entry(NSSArena *arena, void *value, PRBool ownArena) 1.104 +{ 1.105 + cache_entry *ce = nss_ZNEW(arena, cache_entry); 1.106 + if (ce) { 1.107 + ce->entry.value = value; 1.108 + ce->hits = 1; 1.109 + ce->lastHit = PR_Now(); 1.110 + if (ownArena) { 1.111 + ce->arena = arena; 1.112 + } 1.113 + ce->nickname = NULL; 1.114 + } 1.115 + return ce; 1.116 +} 1.117 + 1.118 +/* this should not be exposed in a header, but is here to keep the above 1.119 + * types/functions static 1.120 + */ 1.121 +NSS_IMPLEMENT PRStatus 1.122 +nssTrustDomain_InitializeCache ( 1.123 + NSSTrustDomain *td, 1.124 + PRUint32 cacheSize 1.125 +) 1.126 +{ 1.127 + NSSArena *arena; 1.128 + nssTDCertificateCache *cache = td->cache; 1.129 +#ifdef DEBUG_CACHE 1.130 + s_log = PR_NewLogModule("nss_cache"); 1.131 + PR_ASSERT(s_log); 1.132 +#endif 1.133 + PR_ASSERT(!cache); 1.134 + arena = nssArena_Create(); 1.135 + if (!arena) { 1.136 + return PR_FAILURE; 1.137 + } 1.138 + cache = nss_ZNEW(arena, nssTDCertificateCache); 1.139 + if (!cache) { 1.140 + nssArena_Destroy(arena); 1.141 + return PR_FAILURE; 1.142 + } 1.143 + cache->lock = PZ_NewLock(nssILockCache); 1.144 + if (!cache->lock) { 1.145 + nssArena_Destroy(arena); 1.146 + return PR_FAILURE; 1.147 + } 1.148 + /* Create the issuer and serial DER --> certificate hash */ 1.149 + cache->issuerAndSN = nssHash_CreateCertificate(arena, cacheSize); 1.150 + if (!cache->issuerAndSN) { 1.151 + goto loser; 1.152 + } 1.153 + /* Create the subject DER --> subject list hash */ 1.154 + cache->subject = nssHash_CreateItem(arena, cacheSize); 1.155 + if (!cache->subject) { 1.156 + goto loser; 1.157 + } 1.158 + /* Create the nickname --> subject list hash */ 1.159 + cache->nickname = nssHash_CreateString(arena, cacheSize); 1.160 + if (!cache->nickname) { 1.161 + goto loser; 1.162 + } 1.163 + /* Create the email --> list of subject lists hash */ 1.164 + cache->email = nssHash_CreateString(arena, cacheSize); 1.165 + if (!cache->email) { 1.166 + goto loser; 1.167 + } 1.168 + cache->arena = arena; 1.169 + td->cache = cache; 1.170 +#ifdef DEBUG_CACHE 1.171 + PR_LOG(s_log, PR_LOG_DEBUG, ("Cache initialized.")); 1.172 +#endif 1.173 + return PR_SUCCESS; 1.174 +loser: 1.175 + PZ_DestroyLock(cache->lock); 1.176 + nssArena_Destroy(arena); 1.177 + td->cache = NULL; 1.178 +#ifdef DEBUG_CACHE 1.179 + PR_LOG(s_log, PR_LOG_DEBUG, ("Cache initialization failed.")); 1.180 +#endif 1.181 + return PR_FAILURE; 1.182 +} 1.183 + 1.184 +/* The entries of the hashtable are currently dependent on the certificate(s) 1.185 + * that produced them. That is, the entries will be freed when the cert is 1.186 + * released from the cache. If there are certs in the cache at any time, 1.187 + * including shutdown, the hash table entries will hold memory. In order for 1.188 + * clean shutdown, it is necessary for there to be no certs in the cache. 1.189 + */ 1.190 + 1.191 +extern const NSSError NSS_ERROR_INTERNAL_ERROR; 1.192 +extern const NSSError NSS_ERROR_BUSY; 1.193 + 1.194 +NSS_IMPLEMENT PRStatus 1.195 +nssTrustDomain_DestroyCache ( 1.196 + NSSTrustDomain *td 1.197 +) 1.198 +{ 1.199 + if (!td->cache) { 1.200 + nss_SetError(NSS_ERROR_INTERNAL_ERROR); 1.201 + return PR_FAILURE; 1.202 + } 1.203 + if (nssHash_Count(td->cache->issuerAndSN) > 0) { 1.204 + nss_SetError(NSS_ERROR_BUSY); 1.205 + return PR_FAILURE; 1.206 + } 1.207 + PZ_DestroyLock(td->cache->lock); 1.208 + nssHash_Destroy(td->cache->issuerAndSN); 1.209 + nssHash_Destroy(td->cache->subject); 1.210 + nssHash_Destroy(td->cache->nickname); 1.211 + nssHash_Destroy(td->cache->email); 1.212 + nssArena_Destroy(td->cache->arena); 1.213 + td->cache = NULL; 1.214 +#ifdef DEBUG_CACHE 1.215 + PR_LOG(s_log, PR_LOG_DEBUG, ("Cache destroyed.")); 1.216 +#endif 1.217 + return PR_SUCCESS; 1.218 +} 1.219 + 1.220 +static PRStatus 1.221 +remove_issuer_and_serial_entry ( 1.222 + nssTDCertificateCache *cache, 1.223 + NSSCertificate *cert 1.224 +) 1.225 +{ 1.226 + /* Remove the cert from the issuer/serial hash */ 1.227 + nssHash_Remove(cache->issuerAndSN, cert); 1.228 +#ifdef DEBUG_CACHE 1.229 + log_cert_ref("removed issuer/sn", cert); 1.230 +#endif 1.231 + return PR_SUCCESS; 1.232 +} 1.233 + 1.234 +static PRStatus 1.235 +remove_subject_entry ( 1.236 + nssTDCertificateCache *cache, 1.237 + NSSCertificate *cert, 1.238 + nssList **subjectList, 1.239 + NSSUTF8 **nickname, 1.240 + NSSArena **arena 1.241 +) 1.242 +{ 1.243 + PRStatus nssrv; 1.244 + cache_entry *ce; 1.245 + *subjectList = NULL; 1.246 + *arena = NULL; 1.247 + /* Get the subject list for the cert's subject */ 1.248 + ce = (cache_entry *)nssHash_Lookup(cache->subject, &cert->subject); 1.249 + if (ce) { 1.250 + /* Remove the cert from the subject hash */ 1.251 + nssList_Remove(ce->entry.list, cert); 1.252 + *subjectList = ce->entry.list; 1.253 + *nickname = ce->nickname; 1.254 + *arena = ce->arena; 1.255 + nssrv = PR_SUCCESS; 1.256 +#ifdef DEBUG_CACHE 1.257 + log_cert_ref("removed cert", cert); 1.258 + log_item_dump("from subject list", &cert->subject); 1.259 +#endif 1.260 + } else { 1.261 + nssrv = PR_FAILURE; 1.262 + } 1.263 + return nssrv; 1.264 +} 1.265 + 1.266 +static PRStatus 1.267 +remove_nickname_entry ( 1.268 + nssTDCertificateCache *cache, 1.269 + NSSUTF8 *nickname, 1.270 + nssList *subjectList 1.271 +) 1.272 +{ 1.273 + PRStatus nssrv; 1.274 + if (nickname) { 1.275 + nssHash_Remove(cache->nickname, nickname); 1.276 + nssrv = PR_SUCCESS; 1.277 +#ifdef DEBUG_CACHE 1.278 + PR_LOG(s_log, PR_LOG_DEBUG, ("removed nickname %s", nickname)); 1.279 +#endif 1.280 + } else { 1.281 + nssrv = PR_FAILURE; 1.282 + } 1.283 + return nssrv; 1.284 +} 1.285 + 1.286 +static PRStatus 1.287 +remove_email_entry ( 1.288 + nssTDCertificateCache *cache, 1.289 + NSSCertificate *cert, 1.290 + nssList *subjectList 1.291 +) 1.292 +{ 1.293 + PRStatus nssrv = PR_FAILURE; 1.294 + cache_entry *ce; 1.295 + /* Find the subject list in the email hash */ 1.296 + if (cert->email) { 1.297 + ce = (cache_entry *)nssHash_Lookup(cache->email, cert->email); 1.298 + if (ce) { 1.299 + nssList *subjects = ce->entry.list; 1.300 + /* Remove the subject list from the email hash */ 1.301 + nssList_Remove(subjects, subjectList); 1.302 +#ifdef DEBUG_CACHE 1.303 + log_item_dump("removed subject list", &cert->subject); 1.304 + PR_LOG(s_log, PR_LOG_DEBUG, ("for email %s", cert->email)); 1.305 +#endif 1.306 + if (nssList_Count(subjects) == 0) { 1.307 + /* No more subject lists for email, delete list and 1.308 + * remove hash entry 1.309 + */ 1.310 + (void)nssList_Destroy(subjects); 1.311 + nssHash_Remove(cache->email, cert->email); 1.312 + /* there are no entries left for this address, free space 1.313 + * used for email entries 1.314 + */ 1.315 + nssArena_Destroy(ce->arena); 1.316 +#ifdef DEBUG_CACHE 1.317 + PR_LOG(s_log, PR_LOG_DEBUG, ("removed email %s", cert->email)); 1.318 +#endif 1.319 + } 1.320 + nssrv = PR_SUCCESS; 1.321 + } 1.322 + } 1.323 + return nssrv; 1.324 +} 1.325 + 1.326 +NSS_IMPLEMENT void 1.327 +nssTrustDomain_RemoveCertFromCacheLOCKED ( 1.328 + NSSTrustDomain *td, 1.329 + NSSCertificate *cert 1.330 +) 1.331 +{ 1.332 + nssList *subjectList; 1.333 + cache_entry *ce; 1.334 + NSSArena *arena; 1.335 + NSSUTF8 *nickname; 1.336 + 1.337 +#ifdef DEBUG_CACHE 1.338 + log_cert_ref("attempt to remove cert", cert); 1.339 +#endif 1.340 + ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, cert); 1.341 + if (!ce || ce->entry.cert != cert) { 1.342 + /* If it's not in the cache, or a different cert is (this is really 1.343 + * for safety reasons, though it shouldn't happen), do nothing 1.344 + */ 1.345 +#ifdef DEBUG_CACHE 1.346 + PR_LOG(s_log, PR_LOG_DEBUG, ("but it wasn't in the cache")); 1.347 +#endif 1.348 + return; 1.349 + } 1.350 + (void)remove_issuer_and_serial_entry(td->cache, cert); 1.351 + (void)remove_subject_entry(td->cache, cert, &subjectList, 1.352 + &nickname, &arena); 1.353 + if (nssList_Count(subjectList) == 0) { 1.354 + (void)remove_nickname_entry(td->cache, nickname, subjectList); 1.355 + (void)remove_email_entry(td->cache, cert, subjectList); 1.356 + (void)nssList_Destroy(subjectList); 1.357 + nssHash_Remove(td->cache->subject, &cert->subject); 1.358 + /* there are no entries left for this subject, free the space used 1.359 + * for both the nickname and subject entries 1.360 + */ 1.361 + if (arena) { 1.362 + nssArena_Destroy(arena); 1.363 + } 1.364 + } 1.365 +} 1.366 + 1.367 +NSS_IMPLEMENT void 1.368 +nssTrustDomain_LockCertCache ( 1.369 + NSSTrustDomain *td 1.370 +) 1.371 +{ 1.372 + PZ_Lock(td->cache->lock); 1.373 +} 1.374 + 1.375 +NSS_IMPLEMENT void 1.376 +nssTrustDomain_UnlockCertCache ( 1.377 + NSSTrustDomain *td 1.378 +) 1.379 +{ 1.380 + PZ_Unlock(td->cache->lock); 1.381 +} 1.382 + 1.383 +struct token_cert_dtor { 1.384 + NSSToken *token; 1.385 + nssTDCertificateCache *cache; 1.386 + NSSCertificate **certs; 1.387 + PRUint32 numCerts, arrSize; 1.388 +}; 1.389 + 1.390 +static void 1.391 +remove_token_certs(const void *k, void *v, void *a) 1.392 +{ 1.393 + NSSCertificate *c = (NSSCertificate *)k; 1.394 + nssPKIObject *object = &c->object; 1.395 + struct token_cert_dtor *dtor = a; 1.396 + PRUint32 i; 1.397 + nssPKIObject_Lock(object); 1.398 + for (i=0; i<object->numInstances; i++) { 1.399 + if (object->instances[i]->token == dtor->token) { 1.400 + nssCryptokiObject_Destroy(object->instances[i]); 1.401 + object->instances[i] = object->instances[object->numInstances-1]; 1.402 + object->instances[object->numInstances-1] = NULL; 1.403 + object->numInstances--; 1.404 + dtor->certs[dtor->numCerts++] = c; 1.405 + if (dtor->numCerts == dtor->arrSize) { 1.406 + dtor->arrSize *= 2; 1.407 + dtor->certs = nss_ZREALLOCARRAY(dtor->certs, 1.408 + NSSCertificate *, 1.409 + dtor->arrSize); 1.410 + } 1.411 + break; 1.412 + } 1.413 + } 1.414 + nssPKIObject_Unlock(object); 1.415 + return; 1.416 +} 1.417 + 1.418 +/* 1.419 + * Remove all certs for the given token from the cache. This is 1.420 + * needed if the token is removed. 1.421 + */ 1.422 +NSS_IMPLEMENT PRStatus 1.423 +nssTrustDomain_RemoveTokenCertsFromCache ( 1.424 + NSSTrustDomain *td, 1.425 + NSSToken *token 1.426 +) 1.427 +{ 1.428 + NSSCertificate **certs; 1.429 + PRUint32 i, arrSize = 10; 1.430 + struct token_cert_dtor dtor; 1.431 + certs = nss_ZNEWARRAY(NULL, NSSCertificate *, arrSize); 1.432 + if (!certs) { 1.433 + return PR_FAILURE; 1.434 + } 1.435 + dtor.cache = td->cache; 1.436 + dtor.token = token; 1.437 + dtor.certs = certs; 1.438 + dtor.numCerts = 0; 1.439 + dtor.arrSize = arrSize; 1.440 + PZ_Lock(td->cache->lock); 1.441 + nssHash_Iterate(td->cache->issuerAndSN, remove_token_certs, (void *)&dtor); 1.442 + for (i=0; i<dtor.numCerts; i++) { 1.443 + if (dtor.certs[i]->object.numInstances == 0) { 1.444 + nssTrustDomain_RemoveCertFromCacheLOCKED(td, dtor.certs[i]); 1.445 + dtor.certs[i] = NULL; /* skip this cert in the second for loop */ 1.446 + } 1.447 + } 1.448 + PZ_Unlock(td->cache->lock); 1.449 + for (i=0; i<dtor.numCerts; i++) { 1.450 + if (dtor.certs[i]) { 1.451 + STAN_ForceCERTCertificateUpdate(dtor.certs[i]); 1.452 + } 1.453 + } 1.454 + nss_ZFreeIf(dtor.certs); 1.455 + return PR_SUCCESS; 1.456 +} 1.457 + 1.458 +NSS_IMPLEMENT PRStatus 1.459 +nssTrustDomain_UpdateCachedTokenCerts ( 1.460 + NSSTrustDomain *td, 1.461 + NSSToken *token 1.462 +) 1.463 +{ 1.464 + NSSCertificate **cp, **cached = NULL; 1.465 + nssList *certList; 1.466 + PRUint32 count; 1.467 + certList = nssList_Create(NULL, PR_FALSE); 1.468 + if (!certList) return PR_FAILURE; 1.469 + (void)nssTrustDomain_GetCertsFromCache(td, certList); 1.470 + count = nssList_Count(certList); 1.471 + if (count > 0) { 1.472 + cached = nss_ZNEWARRAY(NULL, NSSCertificate *, count + 1); 1.473 + if (!cached) { 1.474 + nssList_Destroy(certList); 1.475 + return PR_FAILURE; 1.476 + } 1.477 + nssList_GetArray(certList, (void **)cached, count); 1.478 + for (cp = cached; *cp; cp++) { 1.479 + nssCryptokiObject *instance; 1.480 + NSSCertificate *c = *cp; 1.481 + nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly; 1.482 + instance = nssToken_FindCertificateByIssuerAndSerialNumber( 1.483 + token, 1.484 + NULL, 1.485 + &c->issuer, 1.486 + &c->serial, 1.487 + tokenOnly, 1.488 + NULL); 1.489 + if (instance) { 1.490 + nssPKIObject_AddInstance(&c->object, instance); 1.491 + STAN_ForceCERTCertificateUpdate(c); 1.492 + } 1.493 + } 1.494 + nssCertificateArray_Destroy(cached); 1.495 + } 1.496 + nssList_Destroy(certList); 1.497 + return PR_SUCCESS; 1.498 +} 1.499 + 1.500 +static PRStatus 1.501 +add_issuer_and_serial_entry ( 1.502 + NSSArena *arena, 1.503 + nssTDCertificateCache *cache, 1.504 + NSSCertificate *cert 1.505 +) 1.506 +{ 1.507 + cache_entry *ce; 1.508 + ce = new_cache_entry(arena, (void *)cert, PR_FALSE); 1.509 +#ifdef DEBUG_CACHE 1.510 + log_cert_ref("added to issuer/sn", cert); 1.511 +#endif 1.512 + return nssHash_Add(cache->issuerAndSN, cert, (void *)ce); 1.513 +} 1.514 + 1.515 +static PRStatus 1.516 +add_subject_entry ( 1.517 + NSSArena *arena, 1.518 + nssTDCertificateCache *cache, 1.519 + NSSCertificate *cert, 1.520 + NSSUTF8 *nickname, 1.521 + nssList **subjectList 1.522 +) 1.523 +{ 1.524 + PRStatus nssrv; 1.525 + nssList *list; 1.526 + cache_entry *ce; 1.527 + *subjectList = NULL; /* this is only set if a new one is created */ 1.528 + ce = (cache_entry *)nssHash_Lookup(cache->subject, &cert->subject); 1.529 + if (ce) { 1.530 + ce->hits++; 1.531 + ce->lastHit = PR_Now(); 1.532 + /* The subject is already in, add this cert to the list */ 1.533 + nssrv = nssList_AddUnique(ce->entry.list, cert); 1.534 +#ifdef DEBUG_CACHE 1.535 + log_cert_ref("added to existing subject list", cert); 1.536 +#endif 1.537 + } else { 1.538 + NSSDER *subject; 1.539 + /* Create a new subject list for the subject */ 1.540 + list = nssList_Create(arena, PR_FALSE); 1.541 + if (!list) { 1.542 + return PR_FAILURE; 1.543 + } 1.544 + ce = new_cache_entry(arena, (void *)list, PR_TRUE); 1.545 + if (!ce) { 1.546 + return PR_FAILURE; 1.547 + } 1.548 + if (nickname) { 1.549 + ce->nickname = nssUTF8_Duplicate(nickname, arena); 1.550 + } 1.551 + nssList_SetSortFunction(list, nssCertificate_SubjectListSort); 1.552 + /* Add the cert entry to this list of subjects */ 1.553 + nssrv = nssList_AddUnique(list, cert); 1.554 + if (nssrv != PR_SUCCESS) { 1.555 + return nssrv; 1.556 + } 1.557 + /* Add the subject list to the cache */ 1.558 + subject = nssItem_Duplicate(&cert->subject, arena, NULL); 1.559 + if (!subject) { 1.560 + return PR_FAILURE; 1.561 + } 1.562 + nssrv = nssHash_Add(cache->subject, subject, ce); 1.563 + if (nssrv != PR_SUCCESS) { 1.564 + return nssrv; 1.565 + } 1.566 + *subjectList = list; 1.567 +#ifdef DEBUG_CACHE 1.568 + log_cert_ref("created subject list", cert); 1.569 +#endif 1.570 + } 1.571 + return nssrv; 1.572 +} 1.573 + 1.574 +static PRStatus 1.575 +add_nickname_entry ( 1.576 + NSSArena *arena, 1.577 + nssTDCertificateCache *cache, 1.578 + NSSUTF8 *certNickname, 1.579 + nssList *subjectList 1.580 +) 1.581 +{ 1.582 + PRStatus nssrv = PR_SUCCESS; 1.583 + cache_entry *ce; 1.584 + ce = (cache_entry *)nssHash_Lookup(cache->nickname, certNickname); 1.585 + if (ce) { 1.586 + /* This is a collision. A nickname entry already exists for this 1.587 + * subject, but a subject entry didn't. This would imply there are 1.588 + * two subjects using the same nickname, which is not allowed. 1.589 + */ 1.590 + return PR_FAILURE; 1.591 + } else { 1.592 + NSSUTF8 *nickname; 1.593 + ce = new_cache_entry(arena, subjectList, PR_FALSE); 1.594 + if (!ce) { 1.595 + return PR_FAILURE; 1.596 + } 1.597 + nickname = nssUTF8_Duplicate(certNickname, arena); 1.598 + if (!nickname) { 1.599 + return PR_FAILURE; 1.600 + } 1.601 + nssrv = nssHash_Add(cache->nickname, nickname, ce); 1.602 +#ifdef DEBUG_CACHE 1.603 + log_cert_ref("created nickname for", cert); 1.604 +#endif 1.605 + } 1.606 + return nssrv; 1.607 +} 1.608 + 1.609 +static PRStatus 1.610 +add_email_entry ( 1.611 + nssTDCertificateCache *cache, 1.612 + NSSCertificate *cert, 1.613 + nssList *subjectList 1.614 +) 1.615 +{ 1.616 + PRStatus nssrv = PR_SUCCESS; 1.617 + nssList *subjects; 1.618 + cache_entry *ce; 1.619 + ce = (cache_entry *)nssHash_Lookup(cache->email, cert->email); 1.620 + if (ce) { 1.621 + /* Already have an entry for this email address, but not subject */ 1.622 + subjects = ce->entry.list; 1.623 + nssrv = nssList_AddUnique(subjects, subjectList); 1.624 + ce->hits++; 1.625 + ce->lastHit = PR_Now(); 1.626 +#ifdef DEBUG_CACHE 1.627 + log_cert_ref("added subject to email for", cert); 1.628 +#endif 1.629 + } else { 1.630 + NSSASCII7 *email; 1.631 + NSSArena *arena; 1.632 + arena = nssArena_Create(); 1.633 + if (!arena) { 1.634 + return PR_FAILURE; 1.635 + } 1.636 + /* Create a new list of subject lists, add this subject */ 1.637 + subjects = nssList_Create(arena, PR_TRUE); 1.638 + if (!subjects) { 1.639 + nssArena_Destroy(arena); 1.640 + return PR_FAILURE; 1.641 + } 1.642 + /* Add the new subject to the list */ 1.643 + nssrv = nssList_AddUnique(subjects, subjectList); 1.644 + if (nssrv != PR_SUCCESS) { 1.645 + nssArena_Destroy(arena); 1.646 + return nssrv; 1.647 + } 1.648 + /* Add the new entry to the cache */ 1.649 + ce = new_cache_entry(arena, (void *)subjects, PR_TRUE); 1.650 + if (!ce) { 1.651 + nssArena_Destroy(arena); 1.652 + return PR_FAILURE; 1.653 + } 1.654 + email = nssUTF8_Duplicate(cert->email, arena); 1.655 + if (!email) { 1.656 + nssArena_Destroy(arena); 1.657 + return PR_FAILURE; 1.658 + } 1.659 + nssrv = nssHash_Add(cache->email, email, ce); 1.660 + if (nssrv != PR_SUCCESS) { 1.661 + nssArena_Destroy(arena); 1.662 + return nssrv; 1.663 + } 1.664 +#ifdef DEBUG_CACHE 1.665 + log_cert_ref("created email for", cert); 1.666 +#endif 1.667 + } 1.668 + return nssrv; 1.669 +} 1.670 + 1.671 +extern const NSSError NSS_ERROR_CERTIFICATE_IN_CACHE; 1.672 + 1.673 +static void 1.674 +remove_object_instances ( 1.675 + nssPKIObject *object, 1.676 + nssCryptokiObject **instances, 1.677 + int numInstances 1.678 +) 1.679 +{ 1.680 + int i; 1.681 + 1.682 + for (i = 0; i < numInstances; i++) { 1.683 + nssPKIObject_RemoveInstanceForToken(object, instances[i]->token); 1.684 + } 1.685 +} 1.686 + 1.687 +static SECStatus 1.688 +merge_object_instances ( 1.689 + nssPKIObject *to, 1.690 + nssPKIObject *from 1.691 +) 1.692 +{ 1.693 + nssCryptokiObject **instances, **ci; 1.694 + int i; 1.695 + SECStatus rv = SECSuccess; 1.696 + 1.697 + instances = nssPKIObject_GetInstances(from); 1.698 + if (instances == NULL) { 1.699 + return SECFailure; 1.700 + } 1.701 + for (ci = instances, i = 0; *ci; ci++, i++) { 1.702 + nssCryptokiObject *instance = nssCryptokiObject_Clone(*ci); 1.703 + if (instance) { 1.704 + if (nssPKIObject_AddInstance(to, instance) == PR_SUCCESS) { 1.705 + continue; 1.706 + } 1.707 + nssCryptokiObject_Destroy(instance); 1.708 + } 1.709 + remove_object_instances(to, instances, i); 1.710 + rv = SECFailure; 1.711 + break; 1.712 + } 1.713 + nssCryptokiObjectArray_Destroy(instances); 1.714 + return rv; 1.715 +} 1.716 + 1.717 +static NSSCertificate * 1.718 +add_cert_to_cache ( 1.719 + NSSTrustDomain *td, 1.720 + NSSCertificate *cert 1.721 +) 1.722 +{ 1.723 + NSSArena *arena = NULL; 1.724 + nssList *subjectList = NULL; 1.725 + PRStatus nssrv; 1.726 + PRUint32 added = 0; 1.727 + cache_entry *ce; 1.728 + NSSCertificate *rvCert = NULL; 1.729 + NSSUTF8 *certNickname = nssCertificate_GetNickname(cert, NULL); 1.730 + 1.731 + PZ_Lock(td->cache->lock); 1.732 + /* If it exists in the issuer/serial hash, it's already in all */ 1.733 + ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, cert); 1.734 + if (ce) { 1.735 + ce->hits++; 1.736 + ce->lastHit = PR_Now(); 1.737 + rvCert = nssCertificate_AddRef(ce->entry.cert); 1.738 +#ifdef DEBUG_CACHE 1.739 + log_cert_ref("attempted to add cert already in cache", cert); 1.740 +#endif 1.741 + PZ_Unlock(td->cache->lock); 1.742 + nss_ZFreeIf(certNickname); 1.743 + /* collision - somebody else already added the cert 1.744 + * to the cache before this thread got around to it. 1.745 + */ 1.746 + /* merge the instances of the cert */ 1.747 + if (merge_object_instances(&rvCert->object, &cert->object) 1.748 + != SECSuccess) { 1.749 + nssCertificate_Destroy(rvCert); 1.750 + return NULL; 1.751 + } 1.752 + STAN_ForceCERTCertificateUpdate(rvCert); 1.753 + nssCertificate_Destroy(cert); 1.754 + return rvCert; 1.755 + } 1.756 + /* create a new cache entry for this cert within the cert's arena*/ 1.757 + nssrv = add_issuer_and_serial_entry(cert->object.arena, td->cache, cert); 1.758 + if (nssrv != PR_SUCCESS) { 1.759 + goto loser; 1.760 + } 1.761 + added++; 1.762 + /* create an arena for the nickname and subject entries */ 1.763 + arena = nssArena_Create(); 1.764 + if (!arena) { 1.765 + goto loser; 1.766 + } 1.767 + /* create a new subject list for this cert, or add to existing */ 1.768 + nssrv = add_subject_entry(arena, td->cache, cert, 1.769 + certNickname, &subjectList); 1.770 + if (nssrv != PR_SUCCESS) { 1.771 + goto loser; 1.772 + } 1.773 + added++; 1.774 + /* If a new subject entry was created, also need nickname and/or email */ 1.775 + if (subjectList != NULL) { 1.776 + PRBool handle = PR_FALSE; 1.777 + if (certNickname) { 1.778 + nssrv = add_nickname_entry(arena, td->cache, 1.779 + certNickname, subjectList); 1.780 + if (nssrv != PR_SUCCESS) { 1.781 + goto loser; 1.782 + } 1.783 + handle = PR_TRUE; 1.784 + added++; 1.785 + } 1.786 + if (cert->email) { 1.787 + nssrv = add_email_entry(td->cache, cert, subjectList); 1.788 + if (nssrv != PR_SUCCESS) { 1.789 + goto loser; 1.790 + } 1.791 + handle = PR_TRUE; 1.792 + added += 2; 1.793 + } 1.794 +#ifdef nodef 1.795 + /* I think either a nickname or email address must be associated 1.796 + * with the cert. However, certs are passed to NewTemp without 1.797 + * either. This worked in the old code, so it must work now. 1.798 + */ 1.799 + if (!handle) { 1.800 + /* Require either nickname or email handle */ 1.801 + nssrv = PR_FAILURE; 1.802 + goto loser; 1.803 + } 1.804 +#endif 1.805 + } else { 1.806 + /* A new subject entry was not created. arena is unused. */ 1.807 + nssArena_Destroy(arena); 1.808 + } 1.809 + rvCert = cert; 1.810 + PZ_Unlock(td->cache->lock); 1.811 + nss_ZFreeIf(certNickname); 1.812 + return rvCert; 1.813 +loser: 1.814 + nss_ZFreeIf(certNickname); 1.815 + certNickname = NULL; 1.816 + /* Remove any handles that have been created */ 1.817 + subjectList = NULL; 1.818 + if (added >= 1) { 1.819 + (void)remove_issuer_and_serial_entry(td->cache, cert); 1.820 + } 1.821 + if (added >= 2) { 1.822 + (void)remove_subject_entry(td->cache, cert, &subjectList, 1.823 + &certNickname, &arena); 1.824 + } 1.825 + if (added == 3 || added == 5) { 1.826 + (void)remove_nickname_entry(td->cache, certNickname, subjectList); 1.827 + } 1.828 + if (added >= 4) { 1.829 + (void)remove_email_entry(td->cache, cert, subjectList); 1.830 + } 1.831 + if (subjectList) { 1.832 + nssHash_Remove(td->cache->subject, &cert->subject); 1.833 + nssList_Destroy(subjectList); 1.834 + } 1.835 + if (arena) { 1.836 + nssArena_Destroy(arena); 1.837 + } 1.838 + PZ_Unlock(td->cache->lock); 1.839 + return NULL; 1.840 +} 1.841 + 1.842 +NSS_IMPLEMENT PRStatus 1.843 +nssTrustDomain_AddCertsToCache ( 1.844 + NSSTrustDomain *td, 1.845 + NSSCertificate **certs, 1.846 + PRUint32 numCerts 1.847 +) 1.848 +{ 1.849 + PRUint32 i; 1.850 + NSSCertificate *c; 1.851 + for (i=0; i<numCerts && certs[i]; i++) { 1.852 + c = add_cert_to_cache(td, certs[i]); 1.853 + if (c == NULL) { 1.854 + return PR_FAILURE; 1.855 + } else { 1.856 + certs[i] = c; 1.857 + } 1.858 + } 1.859 + return PR_SUCCESS; 1.860 +} 1.861 + 1.862 +static NSSCertificate ** 1.863 +collect_subject_certs ( 1.864 + nssList *subjectList, 1.865 + nssList *rvCertListOpt 1.866 +) 1.867 +{ 1.868 + NSSCertificate *c; 1.869 + NSSCertificate **rvArray = NULL; 1.870 + PRUint32 count; 1.871 + nssCertificateList_AddReferences(subjectList); 1.872 + if (rvCertListOpt) { 1.873 + nssListIterator *iter = nssList_CreateIterator(subjectList); 1.874 + if (!iter) { 1.875 + return (NSSCertificate **)NULL; 1.876 + } 1.877 + for (c = (NSSCertificate *)nssListIterator_Start(iter); 1.878 + c != (NSSCertificate *)NULL; 1.879 + c = (NSSCertificate *)nssListIterator_Next(iter)) { 1.880 + nssList_Add(rvCertListOpt, c); 1.881 + } 1.882 + nssListIterator_Finish(iter); 1.883 + nssListIterator_Destroy(iter); 1.884 + } else { 1.885 + count = nssList_Count(subjectList); 1.886 + rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count + 1); 1.887 + if (!rvArray) { 1.888 + return (NSSCertificate **)NULL; 1.889 + } 1.890 + nssList_GetArray(subjectList, (void **)rvArray, count); 1.891 + } 1.892 + return rvArray; 1.893 +} 1.894 + 1.895 +/* 1.896 + * Find all cached certs with this subject. 1.897 + */ 1.898 +NSS_IMPLEMENT NSSCertificate ** 1.899 +nssTrustDomain_GetCertsForSubjectFromCache ( 1.900 + NSSTrustDomain *td, 1.901 + NSSDER *subject, 1.902 + nssList *certListOpt 1.903 +) 1.904 +{ 1.905 + NSSCertificate **rvArray = NULL; 1.906 + cache_entry *ce; 1.907 +#ifdef DEBUG_CACHE 1.908 + log_item_dump("looking for cert by subject", subject); 1.909 +#endif 1.910 + PZ_Lock(td->cache->lock); 1.911 + ce = (cache_entry *)nssHash_Lookup(td->cache->subject, subject); 1.912 + if (ce) { 1.913 + ce->hits++; 1.914 + ce->lastHit = PR_Now(); 1.915 +#ifdef DEBUG_CACHE 1.916 + PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits)); 1.917 +#endif 1.918 + rvArray = collect_subject_certs(ce->entry.list, certListOpt); 1.919 + } 1.920 + PZ_Unlock(td->cache->lock); 1.921 + return rvArray; 1.922 +} 1.923 + 1.924 +/* 1.925 + * Find all cached certs with this label. 1.926 + */ 1.927 +NSS_IMPLEMENT NSSCertificate ** 1.928 +nssTrustDomain_GetCertsForNicknameFromCache ( 1.929 + NSSTrustDomain *td, 1.930 + const NSSUTF8 *nickname, 1.931 + nssList *certListOpt 1.932 +) 1.933 +{ 1.934 + NSSCertificate **rvArray = NULL; 1.935 + cache_entry *ce; 1.936 +#ifdef DEBUG_CACHE 1.937 + PR_LOG(s_log, PR_LOG_DEBUG, ("looking for cert by nick %s", nickname)); 1.938 +#endif 1.939 + PZ_Lock(td->cache->lock); 1.940 + ce = (cache_entry *)nssHash_Lookup(td->cache->nickname, nickname); 1.941 + if (ce) { 1.942 + ce->hits++; 1.943 + ce->lastHit = PR_Now(); 1.944 +#ifdef DEBUG_CACHE 1.945 + PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits)); 1.946 +#endif 1.947 + rvArray = collect_subject_certs(ce->entry.list, certListOpt); 1.948 + } 1.949 + PZ_Unlock(td->cache->lock); 1.950 + return rvArray; 1.951 +} 1.952 + 1.953 +/* 1.954 + * Find all cached certs with this email address. 1.955 + */ 1.956 +NSS_IMPLEMENT NSSCertificate ** 1.957 +nssTrustDomain_GetCertsForEmailAddressFromCache ( 1.958 + NSSTrustDomain *td, 1.959 + NSSASCII7 *email, 1.960 + nssList *certListOpt 1.961 +) 1.962 +{ 1.963 + NSSCertificate **rvArray = NULL; 1.964 + cache_entry *ce; 1.965 + nssList *collectList = NULL; 1.966 + nssListIterator *iter = NULL; 1.967 + nssList *subjectList; 1.968 +#ifdef DEBUG_CACHE 1.969 + PR_LOG(s_log, PR_LOG_DEBUG, ("looking for cert by email %s", email)); 1.970 +#endif 1.971 + PZ_Lock(td->cache->lock); 1.972 + ce = (cache_entry *)nssHash_Lookup(td->cache->email, email); 1.973 + if (ce) { 1.974 + ce->hits++; 1.975 + ce->lastHit = PR_Now(); 1.976 +#ifdef DEBUG_CACHE 1.977 + PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits)); 1.978 +#endif 1.979 + /* loop over subject lists and get refs for certs */ 1.980 + if (certListOpt) { 1.981 + collectList = certListOpt; 1.982 + } else { 1.983 + collectList = nssList_Create(NULL, PR_FALSE); 1.984 + if (!collectList) { 1.985 + PZ_Unlock(td->cache->lock); 1.986 + return NULL; 1.987 + } 1.988 + } 1.989 + iter = nssList_CreateIterator(ce->entry.list); 1.990 + if (!iter) { 1.991 + PZ_Unlock(td->cache->lock); 1.992 + if (!certListOpt) { 1.993 + nssList_Destroy(collectList); 1.994 + } 1.995 + return NULL; 1.996 + } 1.997 + for (subjectList = (nssList *)nssListIterator_Start(iter); 1.998 + subjectList != (nssList *)NULL; 1.999 + subjectList = (nssList *)nssListIterator_Next(iter)) { 1.1000 + (void)collect_subject_certs(subjectList, collectList); 1.1001 + } 1.1002 + nssListIterator_Finish(iter); 1.1003 + nssListIterator_Destroy(iter); 1.1004 + } 1.1005 + PZ_Unlock(td->cache->lock); 1.1006 + if (!certListOpt && collectList) { 1.1007 + PRUint32 count = nssList_Count(collectList); 1.1008 + rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count); 1.1009 + if (rvArray) { 1.1010 + nssList_GetArray(collectList, (void **)rvArray, count); 1.1011 + } 1.1012 + nssList_Destroy(collectList); 1.1013 + } 1.1014 + return rvArray; 1.1015 +} 1.1016 + 1.1017 +/* 1.1018 + * Look for a specific cert in the cache 1.1019 + */ 1.1020 +NSS_IMPLEMENT NSSCertificate * 1.1021 +nssTrustDomain_GetCertForIssuerAndSNFromCache ( 1.1022 + NSSTrustDomain *td, 1.1023 + NSSDER *issuer, 1.1024 + NSSDER *serial 1.1025 +) 1.1026 +{ 1.1027 + NSSCertificate certkey; 1.1028 + NSSCertificate *rvCert = NULL; 1.1029 + cache_entry *ce; 1.1030 + certkey.issuer.data = issuer->data; 1.1031 + certkey.issuer.size = issuer->size; 1.1032 + certkey.serial.data = serial->data; 1.1033 + certkey.serial.size = serial->size; 1.1034 +#ifdef DEBUG_CACHE 1.1035 + log_item_dump("looking for cert by issuer/sn, issuer", issuer); 1.1036 + log_item_dump(" serial", serial); 1.1037 +#endif 1.1038 + PZ_Lock(td->cache->lock); 1.1039 + ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, &certkey); 1.1040 + if (ce) { 1.1041 + ce->hits++; 1.1042 + ce->lastHit = PR_Now(); 1.1043 + rvCert = nssCertificate_AddRef(ce->entry.cert); 1.1044 +#ifdef DEBUG_CACHE 1.1045 + PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits)); 1.1046 +#endif 1.1047 + } 1.1048 + PZ_Unlock(td->cache->lock); 1.1049 + return rvCert; 1.1050 +} 1.1051 + 1.1052 +static PRStatus 1.1053 +issuer_and_serial_from_encoding ( 1.1054 + NSSBER *encoding, 1.1055 + NSSDER *issuer, 1.1056 + NSSDER *serial 1.1057 +) 1.1058 +{ 1.1059 + SECItem derCert, derIssuer, derSerial; 1.1060 + SECStatus secrv; 1.1061 + derCert.data = (unsigned char *)encoding->data; 1.1062 + derCert.len = encoding->size; 1.1063 + secrv = CERT_IssuerNameFromDERCert(&derCert, &derIssuer); 1.1064 + if (secrv != SECSuccess) { 1.1065 + return PR_FAILURE; 1.1066 + } 1.1067 + secrv = CERT_SerialNumberFromDERCert(&derCert, &derSerial); 1.1068 + if (secrv != SECSuccess) { 1.1069 + return PR_FAILURE; 1.1070 + } 1.1071 + issuer->data = derIssuer.data; 1.1072 + issuer->size = derIssuer.len; 1.1073 + serial->data = derSerial.data; 1.1074 + serial->size = derSerial.len; 1.1075 + return PR_SUCCESS; 1.1076 +} 1.1077 + 1.1078 +/* 1.1079 + * Look for a specific cert in the cache 1.1080 + */ 1.1081 +NSS_IMPLEMENT NSSCertificate * 1.1082 +nssTrustDomain_GetCertByDERFromCache ( 1.1083 + NSSTrustDomain *td, 1.1084 + NSSDER *der 1.1085 +) 1.1086 +{ 1.1087 + PRStatus nssrv = PR_FAILURE; 1.1088 + NSSDER issuer, serial; 1.1089 + NSSCertificate *rvCert; 1.1090 + nssrv = issuer_and_serial_from_encoding(der, &issuer, &serial); 1.1091 + if (nssrv != PR_SUCCESS) { 1.1092 + return NULL; 1.1093 + } 1.1094 +#ifdef DEBUG_CACHE 1.1095 + log_item_dump("looking for cert by DER", der); 1.1096 +#endif 1.1097 + rvCert = nssTrustDomain_GetCertForIssuerAndSNFromCache(td, 1.1098 + &issuer, &serial); 1.1099 + PORT_Free(issuer.data); 1.1100 + PORT_Free(serial.data); 1.1101 + return rvCert; 1.1102 +} 1.1103 + 1.1104 +static void cert_iter(const void *k, void *v, void *a) 1.1105 +{ 1.1106 + nssList *certList = (nssList *)a; 1.1107 + NSSCertificate *c = (NSSCertificate *)k; 1.1108 + nssList_Add(certList, nssCertificate_AddRef(c)); 1.1109 +} 1.1110 + 1.1111 +NSS_EXTERN NSSCertificate ** 1.1112 +nssTrustDomain_GetCertsFromCache ( 1.1113 + NSSTrustDomain *td, 1.1114 + nssList *certListOpt 1.1115 +) 1.1116 +{ 1.1117 + NSSCertificate **rvArray = NULL; 1.1118 + nssList *certList; 1.1119 + if (certListOpt) { 1.1120 + certList = certListOpt; 1.1121 + } else { 1.1122 + certList = nssList_Create(NULL, PR_FALSE); 1.1123 + if (!certList) { 1.1124 + return NULL; 1.1125 + } 1.1126 + } 1.1127 + PZ_Lock(td->cache->lock); 1.1128 + nssHash_Iterate(td->cache->issuerAndSN, cert_iter, (void *)certList); 1.1129 + PZ_Unlock(td->cache->lock); 1.1130 + if (!certListOpt) { 1.1131 + PRUint32 count = nssList_Count(certList); 1.1132 + rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count); 1.1133 + nssList_GetArray(certList, (void **)rvArray, count); 1.1134 + /* array takes the references */ 1.1135 + nssList_Destroy(certList); 1.1136 + } 1.1137 + return rvArray; 1.1138 +} 1.1139 + 1.1140 +NSS_IMPLEMENT void 1.1141 +nssTrustDomain_DumpCacheInfo ( 1.1142 + NSSTrustDomain *td, 1.1143 + void (* cert_dump_iter)(const void *, void *, void *), 1.1144 + void *arg 1.1145 +) 1.1146 +{ 1.1147 + PZ_Lock(td->cache->lock); 1.1148 + nssHash_Iterate(td->cache->issuerAndSN, cert_dump_iter, arg); 1.1149 + PZ_Unlock(td->cache->lock); 1.1150 +}