diff options
Diffstat (limited to 'security/nss/lib/pki/pki3hack.c')
-rw-r--r-- | security/nss/lib/pki/pki3hack.c | 1295 |
1 files changed, 1295 insertions, 0 deletions
diff --git a/security/nss/lib/pki/pki3hack.c b/security/nss/lib/pki/pki3hack.c new file mode 100644 index 000000000..8a8d3bf63 --- /dev/null +++ b/security/nss/lib/pki/pki3hack.c @@ -0,0 +1,1295 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#ifdef DEBUG +static const char CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$"; +#endif /* DEBUG */ + +/* + * Hacks to integrate NSS 3.4 and NSS 4.0 certificates. + */ + +#ifndef NSSPKI_H +#include "nsspki.h" +#endif /* NSSPKI_H */ + +#ifndef PKI_H +#include "pki.h" +#endif /* PKI_H */ + +#ifndef PKIM_H +#include "pkim.h" +#endif /* PKIM_H */ + +#ifndef DEV_H +#include "dev.h" +#endif /* DEV_H */ + +#ifndef DEVNSS3HACK_H +#include "dev3hack.h" +#endif /* DEVNSS3HACK_H */ + +#ifndef PKINSS3HACK_H +#include "pki3hack.h" +#endif /* PKINSS3HACK_H */ + +#include "secitem.h" +#include "certdb.h" +#include "certt.h" +#include "cert.h" +#include "pk11func.h" +#include "pkistore.h" +#include "secmod.h" + +/* if it's got more than 10 certs, it better handle traversal well */ +#define NSSTOKEN_MAX_LOCAL_CERTS 10 + +NSSTrustDomain *g_default_trust_domain = NULL; + +NSSCryptoContext *g_default_crypto_context = NULL; + +NSSTrustDomain * +STAN_GetDefaultTrustDomain() +{ + return g_default_trust_domain; +} + +NSSCryptoContext * +STAN_GetDefaultCryptoContext() +{ + return g_default_crypto_context; +} + +NSS_IMPLEMENT NSSToken * +STAN_GetDefaultCryptoToken +( + void +) +{ + PK11SlotInfo *pk11slot = PK11_GetInternalSlot(); + return PK11Slot_GetNSSToken(pk11slot); +} + +static CERTCertificate * +stan_GetCERTCertificate(NSSCertificate *c, PRBool forceUpdate); + +/* stuff the cert in the global trust domain cache, and then add a reference + * to remain with the token in a list. + */ +static PRStatus +cache_token_cert(NSSCertificate *c, void *arg) +{ + NSSToken *token = (NSSToken *)arg; + NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); + NSSCertificate *cp = nssCertificate_AddRef(c); + if (nssList_Count(token->certList) > NSSTOKEN_MAX_LOCAL_CERTS) { + nssToken_DestroyCertList(token, PR_TRUE); + /* terminate the traversal */ + return PR_FAILURE; + } + nssTrustDomain_AddCertsToCache(td, &c, 1); + if (cp == c) { + NSSCertificate_Destroy(cp); + } else { + /* The cert was already in the cache, from another token. Add this + * token's instance to the cert. + */ + nssCryptokiInstance *tokenInstance, *instance; + nssList_GetArray(cp->object.instanceList, (void **)&tokenInstance, 1); + instance = nssCryptokiInstance_Create(c->object.arena, token, + tokenInstance->handle, PR_TRUE); + nssList_Add(c->object.instanceList, instance); + nssListIterator_Destroy(c->object.instances); + c->object.instances = nssList_CreateIterator(c->object.instanceList); + } + /* This list reference persists with the token */ + nssList_Add(token->certList, nssCertificate_AddRef(c)); + /* The cert needs to become external (made into a CERTCertificate) + * in order for it to be properly released. + * Force an update of the nickname and slot fields. + */ + (void)stan_GetCERTCertificate(c, PR_TRUE); + return PR_SUCCESS; +} + +static void remove_token_instance(NSSCertificate *c, NSSToken *token) +{ + nssListIterator *instances; + nssCryptokiInstance *instance, *rmInstance = NULL; + instances = c->object.instances; + for (instance = (nssCryptokiInstance *)nssListIterator_Start(instances); + instance != (nssCryptokiInstance *)NULL; + instance = (nssCryptokiInstance *)nssListIterator_Next(instances)) + { + if (instance->token == token) { + rmInstance = instance; + break; + } + } + nssListIterator_Finish(instances); + if (rmInstance) { + nssList_Remove(c->object.instanceList, rmInstance); + nssListIterator_Destroy(instances); + c->object.instances = nssList_CreateIterator(c->object.instanceList); + } +} + +static PRBool instance_destructor(NSSCertificate *c, NSSToken *token) +{ + remove_token_instance(c, token); + if (nssList_Count(c->object.instanceList) == 0) { + return PR_TRUE; + } + return PR_FALSE; +} + +NSS_IMPLEMENT void +destroy_token_certs(nssList *certList, NSSToken *token, PRBool renewInstances) +{ + nssListIterator *certs; + NSSCertificate *cert; + PRBool removeIt; + certs = nssList_CreateIterator(certList); + for (cert = (NSSCertificate *)nssListIterator_Start(certs); + cert != (NSSCertificate *)NULL; + cert = (NSSCertificate *)nssListIterator_Next(certs)) + { + removeIt = instance_destructor(cert, token); + if (removeIt) { + nssList_Remove(certList, cert); + CERT_DestroyCertificate(STAN_GetCERTCertificate(cert)); + } else if (renewInstances) { + /* force an update of the nickname and slot fields of the cert */ + (void)stan_GetCERTCertificate(cert, PR_TRUE); + } + } + nssListIterator_Finish(certs); + nssListIterator_Destroy(certs); +} + +NSS_IMPLEMENT void +nssCertificateList_DestroyTokenCerts(nssList *certList, NSSToken *token) +{ + destroy_token_certs(certList, token, PR_TRUE); +} + +NSS_IMPLEMENT void +nssCertificateList_RemoveTokenCerts(nssList *certList, NSSToken *token) +{ + nssListIterator *certs; + NSSCertificate *cert; + PRBool removeIt; + certs = nssList_CreateIterator(certList); + for (cert = (NSSCertificate *)nssListIterator_Start(certs); + cert != (NSSCertificate *)NULL; + cert = (NSSCertificate *)nssListIterator_Next(certs)) + { + removeIt = instance_destructor(cert, token); + if (removeIt) { + nssList_Remove(certList, cert); + } else { + /* force an update of the nickname and slot fields of the cert */ + (void)stan_GetCERTCertificate(cert, PR_TRUE); + } + } + nssListIterator_Finish(certs); + nssListIterator_Destroy(certs); +} + +/* destroy the list of certs on a token */ +NSS_IMPLEMENT void +nssToken_DestroyCertList(NSSToken *token, PRBool renewInstances) +{ + if (!token->certList) { + return; + } + destroy_token_certs(token->certList, token, renewInstances); + nssList_Clear(token->certList, NULL); + /* leave the list non-null to prevent it from being searched */ +} + +/* create a list of local cert references for certain tokens */ +NSS_IMPLEMENT PRStatus +nssToken_LoadCerts(NSSToken *token) +{ + PRStatus nssrv = PR_SUCCESS; + nssTokenCertSearch search; + if (!PK11_IsInternal(token->pk11slot) && PK11_IsHW(token->pk11slot)) { + /* Hardware token certs will be immediately cached, and no searches + * will be performed on the token (the certs will be discovered by + * cache lookups) + */ + search.callback = cache_token_cert; + search.cbarg = token; + search.cached = NULL; + search.searchType = nssTokenSearchType_TokenOnly; + if (!token->certList) { + token->certList = nssList_Create(token->arena, PR_FALSE); + if (!token->certList) { + return PR_FAILURE; + } + } else if (nssList_Count(token->certList) > 0) { + /* already been done */ + return PR_SUCCESS; + } + /* ignore the rv, just work without the list */ + (void)nssToken_TraverseCertificates(token, NULL, &search); + (void)nssToken_SetTrustCache(token); + (void)nssToken_SetCrlCache(token); + + /* even if there are no certs, leave a valid list pointer should + * any be imported. Having the pointer will also prevent searches, + * see below. + */ + if (nssList_Count(token->certList) == 0 && + !PK11_IsLoggedIn(token->pk11slot, NULL)) { + /* If the token is not logged in, that may be the reason no + * certs were found. + */ + token->loggedIn = PR_FALSE; + } + } + return nssrv; +} + +NSS_IMPLEMENT void +nssToken_UpdateTrustForCerts(NSSToken *token) +{ + nssListIterator *certs; + NSSCertificate *cert; + certs = nssList_CreateIterator(token->certList); + for (cert = (NSSCertificate *)nssListIterator_Start(certs); + cert != (NSSCertificate *)NULL; + cert = (NSSCertificate *)nssListIterator_Next(certs)) + { + CERTCertificate *cc = STAN_GetCERTCertificate(cert); + cc->trust = NULL; + /* force an update of the trust fields of the CERTCertificate */ + (void)stan_GetCERTCertificate(cert, PR_FALSE); + } + nssListIterator_Finish(certs); + nssListIterator_Destroy(certs); +} + +NSS_IMPLEMENT PRBool +nssToken_SearchCerts +( + NSSToken *token, + PRBool *notPresentOpt +) +{ + if (notPresentOpt) { + *notPresentOpt = PR_FALSE; + } + if (!nssToken_IsPresent(token)) { + nssToken_DestroyCertList(token, PR_TRUE); /* will free cached certs */ + if (notPresentOpt) { + *notPresentOpt = PR_TRUE; + } + } else if (token->certList && + nssList_Count(token->certList) == 0 && + !token->loggedIn) { + /* If the token has no cached certs, but wasn't logged in, check + * to see if it is logged in now and retry + */ + if (PK11_IsLoggedIn(token->pk11slot, NULL)) { + token->loggedIn = PR_TRUE; + nssToken_LoadCerts(token); + } + } + return (PRBool) (token->certList == NULL); +} + +NSS_IMPLEMENT PRStatus +STAN_LoadDefaultNSS3TrustDomain +( + void +) +{ + NSSTrustDomain *td; + NSSToken *token; + PK11SlotList *list; + PK11SlotListElement *le; + SECMODModuleList *mlp; + SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock(); + int i; + + td = NSSTrustDomain_Create(NULL, NULL, NULL, NULL); + if (!td) { + return PR_FAILURE; + } + td->tokenList = nssList_Create(td->arena, PR_TRUE); + SECMOD_GetReadLock(moduleLock); + for (mlp = SECMOD_GetDefaultModuleList(); mlp != NULL; mlp=mlp->next) { + for (i=0; i < mlp->module->slotCount; i++) { + token = nssToken_CreateFromPK11SlotInfo(td, mlp->module->slots[i]); + PK11Slot_SetNSSToken(mlp->module->slots[i], token); + nssList_Add(td->tokenList, token); + } + } + SECMOD_ReleaseReadLock(moduleLock); + g_default_trust_domain = td; + g_default_crypto_context = NSSTrustDomain_CreateCryptoContext(td, NULL); + /* Cache hardware token certs with the token to make them persistent */ + td->tokens = nssList_CreateIterator(td->tokenList); + for (token = (NSSToken *)nssListIterator_Start(td->tokens); + token != (NSSToken *)NULL; + token = (NSSToken *)nssListIterator_Next(td->tokens)) + { + nssToken_LoadCerts(token); + } + nssListIterator_Finish(td->tokens); + return PR_SUCCESS; +} + +NSS_IMPLEMENT SECStatus +STAN_AddModuleToDefaultTrustDomain +( + SECMODModule *module +) +{ + NSSToken *token; + NSSTrustDomain *td; + int i; + td = STAN_GetDefaultTrustDomain(); + for (i=0; i<module->slotCount; i++) { + token = nssToken_CreateFromPK11SlotInfo(td, module->slots[i]); + PK11Slot_SetNSSToken(module->slots[i], token); + nssToken_LoadCerts(token); + nssList_Add(td->tokenList, token); + } + nssListIterator_Destroy(td->tokens); + td->tokens = nssList_CreateIterator(td->tokenList); + return SECSuccess; +} + +NSS_IMPLEMENT void +STAN_DestroyNSSToken(NSSToken *token) +{ + if (token->certList) { + nssToken_DestroyCertList(token, PR_FALSE); + } + nssToken_Destroy(token); +} + +NSS_IMPLEMENT SECStatus +STAN_RemoveModuleFromDefaultTrustDomain +( + SECMODModule *module +) +{ + NSSToken *token; + NSSTrustDomain *td; + int i; + td = STAN_GetDefaultTrustDomain(); + for (i=0; i<module->slotCount; i++) { + token = PK11Slot_GetNSSToken(module->slots[i]); + if (token) { + nssList_Remove(td->tokenList, token); + STAN_DestroyNSSToken(token); + } + } + nssListIterator_Destroy(td->tokens); + td->tokens = nssList_CreateIterator(td->tokenList); + return SECSuccess; +} + +NSS_IMPLEMENT void +STAN_Shutdown() +{ + if (g_default_trust_domain) { + NSSTrustDomain_Destroy(g_default_trust_domain); + } + if (g_default_crypto_context) { + NSSCryptoContext_Destroy(g_default_crypto_context); + } +} + +/* this function should not be a hack; it will be needed in 4.0 (rename) */ +NSS_IMPLEMENT NSSItem * +STAN_GetCertIdentifierFromDER(NSSArena *arenaOpt, NSSDER *der) +{ + NSSItem *rvKey; + SECItem secDER; + SECItem secKey = { 0 }; + SECStatus secrv; + PRArenaPool *arena; + + SECITEM_FROM_NSSITEM(&secDER, der); + + /* nss3 call uses nss3 arena's */ + arena = PORT_NewArena(256); + if (!arena) { + return NULL; + } + secrv = CERT_KeyFromDERCert(arena, &secDER, &secKey); + if (secrv != SECSuccess) { + return NULL; + } + rvKey = nssItem_Create(arenaOpt, NULL, secKey.len, (void *)secKey.data); + PORT_FreeArena(arena,PR_FALSE); + return rvKey; +} + +static NSSItem * +nss3certificate_getIdentifier(nssDecodedCert *dc) +{ + NSSItem *rvID; + CERTCertificate *c = (CERTCertificate *)dc->data; + rvID = nssItem_Create(NULL, NULL, c->certKey.len, c->certKey.data); + return rvID; +} + +static NSSItem * +nss3certificate_getIssuerIdentifier(nssDecodedCert *dc) +{ + CERTCertificate *c = (CERTCertificate *)dc->data; + CERTAuthKeyID *cAuthKeyID; + PRArenaPool *tmpArena = NULL; + SECItem issuerCertKey; + NSSItem *rvID = NULL; + SECStatus secrv; + tmpArena = PORT_NewArena(512); + cAuthKeyID = CERT_FindAuthKeyIDExten(tmpArena, c); + if (cAuthKeyID == NULL) { + goto done; + } + if (cAuthKeyID->keyID.data) { + rvID = nssItem_Create(NULL, NULL, cAuthKeyID->keyID.len, + cAuthKeyID->keyID.data); + } else if (cAuthKeyID->authCertIssuer) { + SECItem *caName = NULL; + CERTIssuerAndSN issuerSN; + CERTCertificate *issuer = NULL; + + caName = (SECItem *)CERT_GetGeneralNameByType( + cAuthKeyID->authCertIssuer, + certDirectoryName, PR_TRUE); + if (caName == NULL) { + goto done; + } + issuerSN.derIssuer.data = caName->data; + issuerSN.derIssuer.len = caName->len; + issuerSN.serialNumber.data = cAuthKeyID->authCertSerialNumber.data; + issuerSN.serialNumber.len = cAuthKeyID->authCertSerialNumber.len; + issuer = PK11_FindCertByIssuerAndSN(NULL, &issuerSN, NULL); + if (issuer) { + rvID = nssItem_Create(NULL, NULL, issuer->subjectKeyID.len, + issuer->subjectKeyID.data); + CERT_DestroyCertificate(issuer); + } + } +done: + if (tmpArena) PORT_FreeArena(tmpArena, PR_FALSE); + return rvID; +} + +static PRBool +nss3certificate_matchIdentifier(nssDecodedCert *dc, NSSItem *id) +{ + CERTCertificate *c = (CERTCertificate *)dc->data; + SECItem *subjectKeyID, authKeyID; + subjectKeyID = &c->subjectKeyID; + SECITEM_FROM_NSSITEM(&authKeyID, id); + if (SECITEM_CompareItem(subjectKeyID, &authKeyID) == SECEqual) { + return PR_TRUE; + } + return PR_FALSE; +} + +static NSSUsage * +nss3certificate_getUsage(nssDecodedCert *dc) +{ + /* CERTCertificate *c = (CERTCertificate *)dc->data; */ + return NULL; +} + +static PRBool +nss3certificate_isValidAtTime(nssDecodedCert *dc, NSSTime *time) +{ + SECCertTimeValidity validity; + CERTCertificate *c = (CERTCertificate *)dc->data; + validity = CERT_CheckCertValidTimes(c, NSSTime_GetPRTime(time), PR_TRUE); + if (validity == secCertTimeValid) { + return PR_TRUE; + } + return PR_FALSE; +} + +static PRBool +nss3certificate_isNewerThan(nssDecodedCert *dc, nssDecodedCert *cmpdc) +{ + /* I know this isn't right, but this is glue code anyway */ + if (cmpdc->type == dc->type) { + CERTCertificate *certa = (CERTCertificate *)dc->data; + CERTCertificate *certb = (CERTCertificate *)cmpdc->data; + return CERT_IsNewer(certa, certb); + } + return PR_FALSE; +} + +/* CERT_FilterCertListByUsage */ +static PRBool +nss3certificate_matchUsage(nssDecodedCert *dc, NSSUsage *usage) +{ + SECStatus secrv; + unsigned int requiredKeyUsage; + unsigned int requiredCertType; + unsigned int certType; + PRBool match; + CERTCertificate *cc = (CERTCertificate *)dc->data; + SECCertUsage secUsage = usage->nss3usage; + PRBool ca = usage->nss3lookingForCA; + secrv = CERT_KeyUsageAndTypeForCertUsage(secUsage, ca, + &requiredKeyUsage, + &requiredCertType); + if (secrv != SECSuccess) { + return PR_FALSE; + } + match = PR_TRUE; + secrv = CERT_CheckKeyUsage(cc, requiredKeyUsage); + if (secrv != SECSuccess) { + match = PR_FALSE; + } + if (ca) { + (void)CERT_IsCACert(cc, &certType); + } else { + certType = cc->nsCertType; + } + if (!(certType & requiredCertType)) { + match = PR_FALSE; + } + return match; +} + +static NSSASCII7 * +nss3certificate_getEmailAddress(nssDecodedCert *dc) +{ + CERTCertificate *cc = (CERTCertificate *)dc->data; + return cc ? (NSSASCII7 *)cc->emailAddr : NULL; +} + +static PRStatus +nss3certificate_getDERSerialNumber(nssDecodedCert *dc, + NSSDER *serial, NSSArena *arena) +{ + CERTCertificate *cc = (CERTCertificate *)dc->data; + SECItem derSerial = { 0 }; + SECStatus secrv; + secrv = CERT_SerialNumberFromDERCert(&cc->derCert, &derSerial); + if (secrv == SECSuccess) { + (void)nssItem_Create(arena, serial, derSerial.len, derSerial.data); + PORT_Free(derSerial.data); + return PR_SUCCESS; + } + return PR_FAILURE; +} + +NSS_IMPLEMENT nssDecodedCert * +nssDecodedPKIXCertificate_Create +( + NSSArena *arenaOpt, + NSSDER *encoding +) +{ + nssDecodedCert *rvDC; + SECItem secDER; + rvDC = nss_ZNEW(arenaOpt, nssDecodedCert); + rvDC->type = NSSCertificateType_PKIX; + SECITEM_FROM_NSSITEM(&secDER, encoding); + rvDC->data = (void *)CERT_DecodeDERCertificate(&secDER, PR_TRUE, NULL); + rvDC->getIdentifier = nss3certificate_getIdentifier; + rvDC->getIssuerIdentifier = nss3certificate_getIssuerIdentifier; + rvDC->matchIdentifier = nss3certificate_matchIdentifier; + rvDC->getUsage = nss3certificate_getUsage; + rvDC->isValidAtTime = nss3certificate_isValidAtTime; + rvDC->isNewerThan = nss3certificate_isNewerThan; + rvDC->matchUsage = nss3certificate_matchUsage; + rvDC->getEmailAddress = nss3certificate_getEmailAddress; + rvDC->getDERSerialNumber = nss3certificate_getDERSerialNumber; + return rvDC; +} + +static nssDecodedCert * +create_decoded_pkix_cert_from_nss3cert +( + NSSArena *arenaOpt, + CERTCertificate *cc +) +{ + nssDecodedCert *rvDC; + rvDC = nss_ZNEW(arenaOpt, nssDecodedCert); + rvDC->type = NSSCertificateType_PKIX; + rvDC->data = (void *)cc; + rvDC->getIdentifier = nss3certificate_getIdentifier; + rvDC->getIssuerIdentifier = nss3certificate_getIssuerIdentifier; + rvDC->matchIdentifier = nss3certificate_matchIdentifier; + rvDC->getUsage = nss3certificate_getUsage; + rvDC->isValidAtTime = nss3certificate_isValidAtTime; + rvDC->isNewerThan = nss3certificate_isNewerThan; + rvDC->matchUsage = nss3certificate_matchUsage; + rvDC->getEmailAddress = nss3certificate_getEmailAddress; + return rvDC; +} + +NSS_IMPLEMENT PRStatus +nssDecodedPKIXCertificate_Destroy +( + nssDecodedCert *dc +) +{ + CERTCertificate *cert = (CERTCertificate *)dc->data; + PRBool freeSlot = cert->ownSlot; + PK11SlotInfo *slot = cert->slot; + PRArenaPool *arena = cert->arena; + /* zero cert before freeing. Any stale references to this cert + * after this point will probably cause an exception. */ + PORT_Memset(cert, 0, sizeof *cert); + /* free the arena that contains the cert. */ + PORT_FreeArena(arena, PR_FALSE); + nss_ZFreeIf(dc); + if (slot && freeSlot) { + PK11_FreeSlot(slot); + } + return PR_SUCCESS; +} + +/* From pk11cert.c */ +extern PRBool +PK11_IsUserCert(PK11SlotInfo *, CERTCertificate *, CK_OBJECT_HANDLE); + +/* see pk11cert.c:pk11_HandleTrustObject */ +static unsigned int +get_nss3trust_from_cktrust(CK_TRUST t) +{ + unsigned int rt = 0; + if (t == CKT_NETSCAPE_TRUSTED) { + rt |= CERTDB_VALID_PEER | CERTDB_TRUSTED; + } + if (t == CKT_NETSCAPE_TRUSTED_DELEGATOR) { + rt |= CERTDB_VALID_CA | CERTDB_TRUSTED_CA /*| CERTDB_NS_TRUSTED_CA*/; + } + if (t == CKT_NETSCAPE_VALID) { + rt |= CERTDB_VALID_PEER; + } + if (t == CKT_NETSCAPE_VALID_DELEGATOR) { + rt |= CERTDB_VALID_CA; + } + /* user */ + return rt; +} + +static CERTCertTrust * +cert_trust_from_stan_trust(NSSTrust *t, PRArenaPool *arena) +{ + CERTCertTrust *rvTrust; + unsigned int client; + if (!t) { + return NULL; + } + rvTrust = PORT_ArenaAlloc(arena, sizeof(CERTCertTrust)); + if (!rvTrust) return NULL; + rvTrust->sslFlags = get_nss3trust_from_cktrust(t->serverAuth); + client = get_nss3trust_from_cktrust(t->clientAuth); + if (client & (CERTDB_TRUSTED_CA|CERTDB_NS_TRUSTED_CA)) { + client &= ~(CERTDB_TRUSTED_CA|CERTDB_NS_TRUSTED_CA); + rvTrust->sslFlags |= CERTDB_TRUSTED_CLIENT_CA; + } + rvTrust->sslFlags |= client; + rvTrust->emailFlags = get_nss3trust_from_cktrust(t->emailProtection); + rvTrust->objectSigningFlags = get_nss3trust_from_cktrust(t->codeSigning); + return rvTrust; +} + +static int nsstoken_get_trust_order(NSSToken *token) +{ + PK11SlotInfo *slot; + SECMODModule *module; + slot = token->pk11slot; + module = PK11_GetModule(slot); + return module->trustOrder; +} + +/* check all cert instances for private key */ +static PRBool is_user_cert(NSSCertificate *c, CERTCertificate *cc) +{ + PRBool isUser = PR_FALSE; + nssCryptokiInstance *instance; + nssListIterator *instances = c->object.instances; + for (instance = (nssCryptokiInstance *)nssListIterator_Start(instances); + instance != (nssCryptokiInstance *)NULL; + instance = (nssCryptokiInstance *)nssListIterator_Next(instances)) + { + if (PK11_IsUserCert(instance->token->pk11slot, cc, instance->handle)) { + isUser = PR_TRUE; + } + } + nssListIterator_Finish(instances); + return isUser; +} + +CERTCertTrust * +nssTrust_GetCERTCertTrustForCert(NSSCertificate *c, CERTCertificate *cc) +{ + CERTCertTrust *rvTrust; + NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); + NSSToken *tok; + NSSTrust *tokenTrust; + NSSTrust t; + nssListIterator *tokens; + int lastTrustOrder, myTrustOrder; + tokens = nssList_CreateIterator(td->tokenList); + if (!tokens) return NULL; + lastTrustOrder = 1<<16; /* just make it big */ + t.serverAuth = CKT_NETSCAPE_TRUST_UNKNOWN; + t.clientAuth = CKT_NETSCAPE_TRUST_UNKNOWN; + t.emailProtection = CKT_NETSCAPE_TRUST_UNKNOWN; + t.codeSigning = CKT_NETSCAPE_TRUST_UNKNOWN; + for (tok = (NSSToken *)nssListIterator_Start(tokens); + tok != (NSSToken *)NULL; + tok = (NSSToken *)nssListIterator_Next(tokens)) + { + tokenTrust = nssToken_FindTrustForCert(tok, NULL, c, + nssTokenSearchType_TokenOnly); + if (tokenTrust) { + myTrustOrder = nsstoken_get_trust_order(tok); + if (t.serverAuth == CKT_NETSCAPE_TRUST_UNKNOWN || + myTrustOrder < lastTrustOrder) { + t.serverAuth = tokenTrust->serverAuth; + } + if (t.clientAuth == CKT_NETSCAPE_TRUST_UNKNOWN || + myTrustOrder < lastTrustOrder) { + t.clientAuth = tokenTrust->clientAuth; + } + if (t.emailProtection == CKT_NETSCAPE_TRUST_UNKNOWN || + myTrustOrder < lastTrustOrder) { + t.emailProtection = tokenTrust->emailProtection; + } + if (t.codeSigning == CKT_NETSCAPE_TRUST_UNKNOWN || + myTrustOrder < lastTrustOrder) { + t.codeSigning = tokenTrust->codeSigning; + } + (void)nssTrust_Destroy(tokenTrust); + lastTrustOrder = myTrustOrder; + } + } + nssListIterator_Finish(tokens); + nssListIterator_Destroy(tokens); + rvTrust = cert_trust_from_stan_trust(&t, cc->arena); + if (!rvTrust) return NULL; + if (is_user_cert(c, cc)) { + rvTrust->sslFlags |= CERTDB_USER; + rvTrust->emailFlags |= CERTDB_USER; + rvTrust->objectSigningFlags |= CERTDB_USER; + } + return rvTrust; +} + +static nssCryptokiInstance * +get_cert_instance(NSSCertificate *c) +{ + nssCryptokiInstance *instance, *ci; + nssListIterator *instances = c->object.instances; + instance = NULL; + for (ci = (nssCryptokiInstance *)nssListIterator_Start(instances); + ci != (nssCryptokiInstance *)NULL; + ci = (nssCryptokiInstance *)nssListIterator_Next(instances)) + { + if (!instance) { + instance = ci; + } else { + /* This only really works for two instances... But 3.4 can't + * handle more anyway. The logic is, if there are multiple + * instances, prefer the one that is not internal (e.g., on + * a hardware device. + */ + if (PK11_IsInternal(instance->token->pk11slot)) { + instance = ci; + } + } + } + nssListIterator_Finish(instances); + return instance; +} + +static void +fill_CERTCertificateFields(NSSCertificate *c, CERTCertificate *cc, PRBool forced) +{ + NSSTrust *nssTrust; + NSSCryptoContext *context = c->object.cryptoContext; + nssCryptokiInstance *instance = get_cert_instance(c); + NSSUTF8 *stanNick = NULL; + if (instance) { + stanNick = instance->label; + } else if (context) { + stanNick = c->object.tempName; + } + /* fill other fields needed by NSS3 functions using CERTCertificate */ + if ((!cc->nickname && stanNick) || forced) { + PRStatus nssrv; + int nicklen, tokenlen, len; + NSSUTF8 *tokenName = NULL; + char *nick; + nicklen = nssUTF8_Size(stanNick, &nssrv); + if (instance && !PK11_IsInternal(instance->token->pk11slot)) { + tokenName = nssToken_GetName(instance->token); + tokenlen = nssUTF8_Size(tokenName, &nssrv); + } else { + /* don't use token name for internal slot; 3.3 didn't */ + tokenlen = 0; + } + len = tokenlen + nicklen; + cc->nickname = PORT_ArenaAlloc(cc->arena, len); + nick = cc->nickname; + if (tokenName) { + memcpy(nick, tokenName, tokenlen-1); + nick += tokenlen-1; + *nick++ = ':'; + } + memcpy(nick, stanNick, nicklen-1); + cc->nickname[len-1] = '\0'; + } + if (context) { + /* trust */ + nssTrust = nssCryptoContext_FindTrustForCertificate(context, c); + if (nssTrust) { + cc->trust = cert_trust_from_stan_trust(nssTrust, cc->arena); + nssTrust_Destroy(nssTrust); + } + } else if (instance) { + /* slot */ + cc->slot = PK11_ReferenceSlot(instance->token->pk11slot); + cc->ownSlot = PR_TRUE; + /* pkcs11ID */ + cc->pkcs11ID = instance->handle; + /* trust */ + cc->trust = nssTrust_GetCERTCertTrustForCert(c, cc); + } + /* database handle is now the trust domain */ + cc->dbhandle = c->object.trustDomain; + /* subjectList ? */ + /* istemp and isperm are supported in NSS 3.4 */ + cc->istemp = PR_FALSE; /* CERT_NewTemp will override this */ + cc->isperm = PR_TRUE; /* by default */ + /* pointer back */ + cc->nssCertificate = c; +} + +static CERTCertificate * +stan_GetCERTCertificate(NSSCertificate *c, PRBool forceUpdate) +{ + nssDecodedCert *dc; + CERTCertificate *cc; + if (!c->decoding) { + dc = nssDecodedPKIXCertificate_Create(NULL, &c->encoding); + if (!dc) return NULL; + c->decoding = dc; + } else { + dc = c->decoding; + } + cc = (CERTCertificate *)dc->data; + if (cc) { + if (!cc->nssCertificate || forceUpdate) { + fill_CERTCertificateFields(c, cc, forceUpdate); + } else if (!cc->trust && !c->object.cryptoContext) { + /* if it's a perm cert, it might have been stored before the + * trust, so look for the trust again. But a temp cert can be + * ignored. + */ + cc->trust = nssTrust_GetCERTCertTrustForCert(c, cc); + } + } + return cc; +} + +NSS_IMPLEMENT CERTCertificate * +STAN_GetCERTCertificate(NSSCertificate *c) +{ + return stan_GetCERTCertificate(c, PR_FALSE); +} + +static CK_TRUST +get_stan_trust(unsigned int t, PRBool isClientAuth) +{ + if (isClientAuth) { + if (t & CERTDB_TRUSTED_CLIENT_CA) { + return CKT_NETSCAPE_TRUSTED_DELEGATOR; + } + } else { + if (t & CERTDB_TRUSTED_CA || t & CERTDB_NS_TRUSTED_CA) { + return CKT_NETSCAPE_TRUSTED_DELEGATOR; + } + } + if (t & CERTDB_TRUSTED) { + return CKT_NETSCAPE_TRUSTED; + } + if (t & CERTDB_VALID_CA) { + return CKT_NETSCAPE_VALID_DELEGATOR; + } + if (t & CERTDB_VALID_PEER) { + return CKT_NETSCAPE_VALID; + } + return CKT_NETSCAPE_UNTRUSTED; +} + +NSS_EXTERN NSSCertificate * +STAN_GetNSSCertificate(CERTCertificate *cc) +{ + NSSCertificate *c; + nssCryptokiInstance *instance; + NSSArena *arena; + PRStatus nssrv; + c = cc->nssCertificate; + if (c) { + return c; + } + /* i don't think this should happen. but if it can, need to create + * NSSCertificate from CERTCertificate values here. */ + /* Yup, it can happen. */ + arena = NSSArena_Create(); + if (!arena) { + return NULL; + } + c = nss_ZNEW(arena, NSSCertificate); + if (!c) { + nssArena_Destroy(arena); + return NULL; + } + NSSITEM_FROM_SECITEM(&c->encoding, &cc->derCert); + c->type = NSSCertificateType_PKIX; + nssrv = nssPKIObject_Initialize(&c->object, arena, cc->dbhandle, NULL); + if (nssrv != PR_SUCCESS) { + nssPKIObject_Destroy(&c->object); + } + nssItem_Create(arena, + &c->issuer, cc->derIssuer.len, cc->derIssuer.data); + nssItem_Create(arena, + &c->subject, cc->derSubject.len, cc->derSubject.data); + if (PR_TRUE) { + /* CERTCertificate stores serial numbers decoded. I need the DER + * here. sigh. + */ + SECItem derSerial; + CERT_SerialNumberFromDERCert(&cc->derCert, &derSerial); + nssItem_Create(arena, &c->serial, derSerial.len, derSerial.data); + PORT_Free(derSerial.data); + } + if (cc->emailAddr) { + c->email = nssUTF8_Create(arena, + nssStringType_PrintableString, + (NSSUTF8 *)cc->emailAddr, + PORT_Strlen(cc->emailAddr)); + } + if (cc->slot) { + instance = nss_ZNEW(arena, nssCryptokiInstance); + instance->token = PK11Slot_GetNSSToken(cc->slot); + instance->handle = cc->pkcs11ID; + instance->isTokenObject = PR_TRUE; + if (cc->nickname) { + instance->label = nssUTF8_Create(arena, + nssStringType_UTF8String, + (NSSUTF8 *)cc->nickname, + PORT_Strlen(cc->nickname)); + } + nssList_Add(c->object.instanceList, instance); + /* XXX Fix this! */ + nssListIterator_Destroy(c->object.instances); + c->object.instances = nssList_CreateIterator(c->object.instanceList); + } + c->decoding = create_decoded_pkix_cert_from_nss3cert(NULL, cc); + cc->nssCertificate = c; + return c; +} + +NSS_EXTERN PRStatus +STAN_ChangeCertTrust(CERTCertificate *cc, CERTCertTrust *trust) +{ + PRStatus nssrv; + NSSCertificate *c = STAN_GetNSSCertificate(cc); + NSSToken *tok; + NSSTrustDomain *td; + NSSTrust *nssTrust; + NSSArena *arena; + CERTCertTrust *oldTrust; + nssListIterator *tokens; + PRBool moving_object; + oldTrust = nssTrust_GetCERTCertTrustForCert(c, cc); + if (oldTrust) { + if (memcmp(oldTrust, trust, sizeof (CERTCertTrust)) == 0) { + /* ... and the new trust is no different, done) */ + return PR_SUCCESS; + } else { + /* take over memory already allocated in cc's arena */ + cc->trust = oldTrust; + } + } else { + cc->trust = PORT_ArenaAlloc(cc->arena, sizeof(CERTCertTrust)); + } + memcpy(cc->trust, trust, sizeof(CERTCertTrust)); + /* Set the NSSCerticate's trust */ + arena = nssArena_Create(); + if (!arena) return PR_FAILURE; + nssTrust = nss_ZNEW(arena, NSSTrust); + nssrv = nssPKIObject_Initialize(&nssTrust->object, arena, NULL, NULL); + if (nssrv != PR_SUCCESS) { + nssPKIObject_Destroy(&nssTrust->object); + return PR_FAILURE; + } + nssTrust->certificate = c; + nssTrust->serverAuth = get_stan_trust(trust->sslFlags, PR_FALSE); + nssTrust->clientAuth = get_stan_trust(trust->sslFlags, PR_TRUE); + nssTrust->emailProtection = get_stan_trust(trust->emailFlags, PR_FALSE); + nssTrust->codeSigning = get_stan_trust(trust->objectSigningFlags, PR_FALSE); + if (c->object.cryptoContext != NULL) { + /* The cert is in a context, set the trust there */ + NSSCryptoContext *cc = c->object.cryptoContext; + nssrv = nssCryptoContext_ImportTrust(cc, nssTrust); + if (nssrv != PR_SUCCESS) { + nssTrust_Destroy(nssTrust); + return nssrv; + } + if (nssList_Count(c->object.instanceList) == 0) { + /* The context is the only instance, finished */ + return nssrv; + } + } + td = STAN_GetDefaultTrustDomain(); + if (PK11_IsReadOnly(cc->slot)) { + tokens = nssList_CreateIterator(td->tokenList); + if (!tokens) return PR_FAILURE; + for (tok = (NSSToken *)nssListIterator_Start(tokens); + tok != (NSSToken *)NULL; + tok = (NSSToken *)nssListIterator_Next(tokens)) + { + if (!PK11_IsReadOnly(tok->pk11slot)) break; + } + nssListIterator_Finish(tokens); + nssListIterator_Destroy(tokens); + moving_object = PR_TRUE; + } else { + /* by default, store trust on same token as cert if writeable */ + tok = PK11Slot_GetNSSToken(cc->slot); + moving_object = PR_FALSE; + } + if (tok) { + if (moving_object) { + /* this is kind of hacky. the softoken needs the cert + * object in order to store trust. forcing it to be perm + */ + NSSUTF8 *nickname = NSSCertificate_GetNickname(c, NULL); + nssrv = nssToken_ImportCertificate(tok, NULL, c, nickname, PR_TRUE); + if (nssrv != PR_SUCCESS) return nssrv; + } + nssrv = nssToken_ImportTrust(tok, NULL, nssTrust, PR_TRUE); + } else { + nssrv = PR_FAILURE; + } + (void)nssTrust_Destroy(nssTrust); + return nssrv; +} + +/* CERT_TraversePermCertsForSubject */ +NSS_IMPLEMENT PRStatus +nssTrustDomain_TraverseCertificatesBySubject +( + NSSTrustDomain *td, + NSSDER *subject, + PRStatus (*callback)(NSSCertificate *c, void *arg), + void *arg +) +{ + PRStatus nssrv = PR_SUCCESS; + NSSArena *tmpArena; + NSSCertificate **subjectCerts; + NSSCertificate *c; + PRIntn i; + tmpArena = NSSArena_Create(); + subjectCerts = NSSTrustDomain_FindCertificatesBySubject(td, subject, NULL, + 0, tmpArena); + if (subjectCerts) { + for (i=0, c = subjectCerts[i]; c; i++) { + nssrv = callback(c, arg); + if (nssrv != PR_SUCCESS) break; + } + } + nssArena_Destroy(tmpArena); + return nssrv; +} + +/* CERT_TraversePermCertsForNickname */ +NSS_IMPLEMENT PRStatus +nssTrustDomain_TraverseCertificatesByNickname +( + NSSTrustDomain *td, + NSSUTF8 *nickname, + PRStatus (*callback)(NSSCertificate *c, void *arg), + void *arg +) +{ + PRStatus nssrv = PR_SUCCESS; + NSSArena *tmpArena; + NSSCertificate **nickCerts; + NSSCertificate *c; + PRIntn i; + tmpArena = NSSArena_Create(); + nickCerts = NSSTrustDomain_FindCertificatesByNickname(td, nickname, NULL, + 0, tmpArena); + if (nickCerts) { + for (i=0, c = nickCerts[i]; c; i++) { + nssrv = callback(c, arg); + if (nssrv != PR_SUCCESS) break; + } + } + nssArena_Destroy(tmpArena); + return nssrv; +} + +/* SEC_TraversePermCerts */ +NSS_IMPLEMENT PRStatus +nssTrustDomain_TraverseCertificates +( + NSSTrustDomain *td, + PRStatus (*callback)(NSSCertificate *c, void *arg), + void *arg +) +{ + PRStatus nssrv = PR_SUCCESS; + NSSToken *token; + nssList *certList; + nssTokenCertSearch search; + /* grab all cache certs (XXX please only do this here...) + * the alternative is to provide a callback through search that allows + * the token to query the cache for the cert during traversal. + */ + certList = nssList_Create(NULL, PR_FALSE); + (void)nssTrustDomain_GetCertsFromCache(td, certList); + /* set the search criteria */ + search.callback = callback; + search.cbarg = arg; + search.cached = certList; + search.searchType = nssTokenSearchType_TokenOnly; + for (token = (NSSToken *)nssListIterator_Start(td->tokens); + token != (NSSToken *)NULL; + token = (NSSToken *)nssListIterator_Next(td->tokens)) + { + nssrv = nssToken_TraverseCertificates(token, NULL, &search); + } + nssListIterator_Finish(td->tokens); + nssList_Destroy(certList); + return nssrv; +} + +#if 0 +static CK_CERTIFICATE_TYPE +get_cert_type(NSSCertificateType nssType) +{ + switch (nssType) { + case NSSCertificateType_PKIX: + return CKC_X_509; + default: + return CK_INVALID_HANDLE; /* Not really! CK_INVALID_HANDLE is not a + * type CK_CERTIFICATE_TYPE */ + } +} +#endif + +/* CERT_AddTempCertToPerm */ +NSS_EXTERN PRStatus +nssTrustDomain_AddTempCertToPerm +( + NSSCertificate *c +) +{ +#if 0 + NSSToken *token; + CK_CERTIFICATE_TYPE cert_type; + CK_ATTRIBUTE cert_template[] = + { + { CKA_CLASS, NULL, 0 }, + { CKA_CERTIFICATE_TYPE, NULL, 0 }, + { CKA_ID, NULL, 0 }, + { CKA_VALUE, NULL, 0 }, + { CKA_LABEL, NULL, 0 }, + { CKA_ISSUER, NULL, 0 }, + { CKA_SUBJECT, NULL, 0 }, + { CKA_SERIAL_NUMBER, NULL, 0 } + }; + CK_ULONG ctsize; + ctsize = (CK_ULONG)(sizeof(cert_template) / sizeof(cert_template[0])); + /* XXX sanity checking needed */ + cert_type = get_cert_type(c->type); + /* Set up the certificate object */ + NSS_CK_SET_ATTRIBUTE_ITEM(cert_template, 0, &g_ck_class_cert); + NSS_CK_SET_ATTRIBUTE_VAR( cert_template, 1, cert_type); + NSS_CK_SET_ATTRIBUTE_ITEM(cert_template, 2, &c->id); + NSS_CK_SET_ATTRIBUTE_ITEM(cert_template, 3, &c->encoding); + NSS_CK_SET_ATTRIBUTE_UTF8(cert_template, 4, c->nickname); + NSS_CK_SET_ATTRIBUTE_ITEM(cert_template, 5, &c->issuer); + NSS_CK_SET_ATTRIBUTE_ITEM(cert_template, 6, &c->subject); + NSS_CK_SET_ATTRIBUTE_ITEM(cert_template, 7, &c->serial); + /* This is a hack, ignoring the 4.0 token ordering scheme */ + token = STAN_GetInternalToken(); + c->handle = nssToken_ImportObject(token, NULL, cert_template, ctsize); + if (c->handle == CK_INVALID_HANDLE) { + return PR_FAILURE; + } + c->token = token; + c->slot = token->slot; + /* Do the trust object */ + return PR_SUCCESS; +#endif + return PR_FAILURE; +} + +static void cert_dump_iter(const void *k, void *v, void *a) +{ + NSSCertificate *c = (NSSCertificate *)k; + CERTCertificate *cert = STAN_GetCERTCertificate(c); + printf("[%2d] \"%s\"\n", c->object.refCount, cert->subjectName); +} + +void +nss_DumpCertificateCacheInfo() +{ + NSSTrustDomain *td; + NSSCryptoContext *cc; + td = STAN_GetDefaultTrustDomain(); + cc = STAN_GetDefaultCryptoContext(); + printf("\n\nCertificates in the cache:\n"); + nssTrustDomain_DumpCacheInfo(td, cert_dump_iter, NULL); + printf("\n\nCertificates in the temporary store:\n"); + if (cc->certStore) { + nssCertificateStore_DumpStoreInfo(cc->certStore, cert_dump_iter, NULL); + } +} + |