security/nss/lib/pki/tdcache.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/. */
     5 #ifndef PKIM_H
     6 #include "pkim.h"
     7 #endif /* PKIM_H */
     9 #ifndef PKIT_H
    10 #include "pkit.h"
    11 #endif /* PKIT_H */
    13 #ifndef NSSPKI_H
    14 #include "nsspki.h"
    15 #endif /* NSSPKI_H */
    17 #ifndef PKI_H
    18 #include "pki.h"
    19 #endif /* PKI_H */
    21 #ifndef NSSBASE_H
    22 #include "nssbase.h"
    23 #endif /* NSSBASE_H */
    25 #ifndef BASE_H
    26 #include "base.h"
    27 #endif /* BASE_H */
    29 #include "cert.h"
    30 #include "dev.h"
    31 #include "pki3hack.h"
    33 #ifdef DEBUG_CACHE
    34 static PRLogModuleInfo *s_log = NULL;
    35 #endif
    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
    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
    66 /* Certificate cache routines */
    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  */
    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 };
    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 };
    97 typedef struct cache_entry_str cache_entry;
    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 }
   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 }
   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  */
   188 extern const NSSError NSS_ERROR_INTERNAL_ERROR;
   189 extern const NSSError NSS_ERROR_BUSY;
   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 }
   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 }
   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 }
   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 }
   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 }
   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;
   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 }
   364 NSS_IMPLEMENT void
   365 nssTrustDomain_LockCertCache (
   366   NSSTrustDomain *td
   367 )
   368 {
   369     PZ_Lock(td->cache->lock);
   370 }
   372 NSS_IMPLEMENT void
   373 nssTrustDomain_UnlockCertCache (
   374   NSSTrustDomain *td
   375 )
   376 {
   377     PZ_Unlock(td->cache->lock);
   378 }
   380 struct token_cert_dtor {
   381     NSSToken *token;
   382     nssTDCertificateCache *cache;
   383     NSSCertificate **certs;
   384     PRUint32 numCerts, arrSize;
   385 };
   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 }
   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 }
   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 }
   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 }
   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 }
   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 }
   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 }
   668 extern const NSSError NSS_ERROR_CERTIFICATE_IN_CACHE;
   670 static void
   671 remove_object_instances (
   672   nssPKIObject *object,
   673   nssCryptokiObject **instances,
   674   int numInstances
   675 )
   676 {
   677     int i;
   679     for (i = 0; i < numInstances; i++) {
   680 	nssPKIObject_RemoveInstanceForToken(object, instances[i]->token);
   681     }
   682 }
   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;
   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 }
   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);
   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 }
   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 }
   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 }
   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 }
   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 }
   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);
  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);
  1009 	nssList_Destroy(collectList);
  1011     return rvArray;
  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
  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
  1045     PZ_Unlock(td->cache->lock);
  1046     return rvCert;
  1049 static PRStatus
  1050 issuer_and_serial_from_encoding (
  1051   NSSBER *encoding, 
  1052   NSSDER *issuer, 
  1053   NSSDER *serial
  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;
  1064     secrv = CERT_SerialNumberFromDERCert(&derCert, &derSerial);
  1065     if (secrv != SECSuccess) {
  1066 	return PR_FAILURE;
  1068     issuer->data = derIssuer.data;
  1069     issuer->size = derIssuer.len;
  1070     serial->data = derSerial.data;
  1071     serial->size = derSerial.len;
  1072     return PR_SUCCESS;
  1075 /*
  1076  * Look for a specific cert in the cache
  1077  */
  1078 NSS_IMPLEMENT NSSCertificate *
  1079 nssTrustDomain_GetCertByDERFromCache (
  1080   NSSTrustDomain *td,
  1081   NSSDER *der
  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;
  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;
  1101 static void cert_iter(const void *k, void *v, void *a)
  1103     nssList *certList = (nssList *)a;
  1104     NSSCertificate *c = (NSSCertificate *)k;
  1105     nssList_Add(certList, nssCertificate_AddRef(c));
  1108 NSS_EXTERN NSSCertificate **
  1109 nssTrustDomain_GetCertsFromCache (
  1110   NSSTrustDomain *td,
  1111   nssList *certListOpt
  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;
  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);
  1134     return rvArray;
  1137 NSS_IMPLEMENT void
  1138 nssTrustDomain_DumpCacheInfo (
  1139   NSSTrustDomain *td,
  1140   void (* cert_dump_iter)(const void *, void *, void *),
  1141   void *arg
  1144     PZ_Lock(td->cache->lock);
  1145     nssHash_Iterate(td->cache->issuerAndSN, cert_dump_iter, arg);
  1146     PZ_Unlock(td->cache->lock);

mercurial