diff options
Diffstat (limited to 'security/nss/lib/certhigh/certhigh.c')
-rw-r--r-- | security/nss/lib/certhigh/certhigh.c | 1204 |
1 files changed, 1204 insertions, 0 deletions
diff --git a/security/nss/lib/certhigh/certhigh.c b/security/nss/lib/certhigh/certhigh.c new file mode 100644 index 000000000..55259898b --- /dev/null +++ b/security/nss/lib/certhigh/certhigh.c @@ -0,0 +1,1204 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "nspr.h" +#include "secerr.h" +#include "secasn1.h" +#include "seccomon.h" +#include "pk11func.h" +#include "certdb.h" +#include "certt.h" +#include "cert.h" +#include "certxutl.h" + +#include "nsspki.h" +#include "pki.h" +#include "pkit.h" +#include "pkitm.h" +#include "pki3hack.h" + +PRBool +CERT_MatchNickname(char *name1, char *name2) +{ + char *nickname1 = NULL; + char *nickname2 = NULL; + char *token1; + char *token2; + + /* first deal with the straight comparison */ + if (PORT_Strcmp(name1, name2) == 0) { + return PR_TRUE; + } + /* we need to handle the case where one name has an explicit token and the other + * doesn't */ + token1 = PORT_Strchr(name1, ':'); + token2 = PORT_Strchr(name2, ':'); + if ((token1 && token2) || (!token1 && !token2)) { + /* either both token names are specified or neither are, not match */ + return PR_FALSE; + } + if (token1) { + nickname1 = token1; + nickname2 = name2; + } else { + nickname1 = token2; + nickname2 = name1; + } + nickname1++; + if (PORT_Strcmp(nickname1, nickname2) != 0) { + return PR_FALSE; + } + /* Bug 1192443 - compare the other token with the internal slot here */ + return PR_TRUE; +} + +/* + * Find all user certificates that match the given criteria. + * + * "handle" - database to search + * "usage" - certificate usage to match + * "oneCertPerName" - if set then only return the "best" cert per + * name + * "validOnly" - only return certs that are curently valid + * "proto_win" - window handle passed to pkcs11 + */ +CERTCertList * +CERT_FindUserCertsByUsage(CERTCertDBHandle *handle, + SECCertUsage usage, + PRBool oneCertPerName, + PRBool validOnly, + void *proto_win) +{ + CERTCertNicknames *nicknames = NULL; + char **nnptr; + int nn; + CERTCertificate *cert = NULL; + CERTCertList *certList = NULL; + SECStatus rv; + PRTime time; + CERTCertListNode *node = NULL; + CERTCertListNode *freenode = NULL; + int n; + + time = PR_Now(); + + nicknames = CERT_GetCertNicknames(handle, SEC_CERT_NICKNAMES_USER, + proto_win); + + if ((nicknames == NULL) || (nicknames->numnicknames == 0)) { + goto loser; + } + + nnptr = nicknames->nicknames; + nn = nicknames->numnicknames; + + while (nn > 0) { + cert = NULL; + /* use the pk11 call so that we pick up any certs on tokens, + * which may require login + */ + if (proto_win != NULL) { + cert = PK11_FindCertFromNickname(*nnptr, proto_win); + } + + /* Sigh, It turns out if the cert is already in the temp db, because + * it's in the perm db, then the nickname lookup doesn't work. + * since we already have the cert here, though, than we can just call + * CERT_CreateSubjectCertList directly. For those cases where we didn't + * find the cert in pkcs #11 (because we didn't have a password arg, + * or because the nickname is for a peer, server, or CA cert, then we + * go look the cert up. + */ + if (cert == NULL) { + cert = CERT_FindCertByNickname(handle, *nnptr); + } + + if (cert != NULL) { + /* collect certs for this nickname, sorting them into the list */ + certList = CERT_CreateSubjectCertList(certList, handle, + &cert->derSubject, time, validOnly); + + CERT_FilterCertListForUserCerts(certList); + + /* drop the extra reference */ + CERT_DestroyCertificate(cert); + } + + nnptr++; + nn--; + } + + /* remove certs with incorrect usage */ + rv = CERT_FilterCertListByUsage(certList, usage, PR_FALSE); + + if (rv != SECSuccess) { + goto loser; + } + + /* remove any extra certs for each name */ + if (oneCertPerName) { + PRBool *flags; + + nn = nicknames->numnicknames; + nnptr = nicknames->nicknames; + + if (!certList) { + goto loser; + } + + flags = (PRBool *)PORT_ZAlloc(sizeof(PRBool) * nn); + if (flags == NULL) { + goto loser; + } + + node = CERT_LIST_HEAD(certList); + + /* treverse all certs in the list */ + while (!CERT_LIST_END(node, certList)) { + + /* find matching nickname index */ + for (n = 0; n < nn; n++) { + if (CERT_MatchNickname(nnptr[n], node->cert->nickname)) { + /* We found a match. If this is the first one, then + * set the flag and move on to the next cert. If this + * is not the first one then delete it from the list. + */ + if (flags[n]) { + /* We have already seen a cert with this nickname, + * so delete this one. + */ + freenode = node; + node = CERT_LIST_NEXT(node); + CERT_RemoveCertListNode(freenode); + } else { + /* keep the first cert for each nickname, but set the + * flag so we know to delete any others with the same + * nickname. + */ + flags[n] = PR_TRUE; + node = CERT_LIST_NEXT(node); + } + break; + } + } + if (n == nn) { + /* if we get here it means that we didn't find a matching + * nickname, which should not happen. + */ + PORT_Assert(0); + node = CERT_LIST_NEXT(node); + } + } + PORT_Free(flags); + } + + goto done; + +loser: + if (certList != NULL) { + CERT_DestroyCertList(certList); + certList = NULL; + } + +done: + if (nicknames != NULL) { + CERT_FreeNicknames(nicknames); + } + + return (certList); +} + +/* + * Find a user certificate that matchs the given criteria. + * + * "handle" - database to search + * "nickname" - nickname to match + * "usage" - certificate usage to match + * "validOnly" - only return certs that are curently valid + * "proto_win" - window handle passed to pkcs11 + */ +CERTCertificate * +CERT_FindUserCertByUsage(CERTCertDBHandle *handle, + const char *nickname, + SECCertUsage usage, + PRBool validOnly, + void *proto_win) +{ + CERTCertificate *cert = NULL; + CERTCertList *certList = NULL; + SECStatus rv; + PRTime time; + + time = PR_Now(); + + /* use the pk11 call so that we pick up any certs on tokens, + * which may require login + */ + /* XXX - why is this restricted? */ + if (proto_win != NULL) { + cert = PK11_FindCertFromNickname(nickname, proto_win); + } + + /* sigh, There are still problems find smart cards from the temp + * db. This will get smart cards working again. The real fix + * is to make sure we can search the temp db by their token nickname. + */ + if (cert == NULL) { + cert = CERT_FindCertByNickname(handle, nickname); + } + + if (cert != NULL) { + unsigned int requiredKeyUsage; + unsigned int requiredCertType; + + rv = CERT_KeyUsageAndTypeForCertUsage(usage, PR_FALSE, + &requiredKeyUsage, &requiredCertType); + if (rv != SECSuccess) { + /* drop the extra reference */ + CERT_DestroyCertificate(cert); + cert = NULL; + goto loser; + } + /* If we already found the right cert, just return it */ + if ((!validOnly || CERT_CheckCertValidTimes(cert, time, PR_FALSE) == secCertTimeValid) && + (CERT_CheckKeyUsage(cert, requiredKeyUsage) == SECSuccess) && + (cert->nsCertType & requiredCertType) && + CERT_IsUserCert(cert)) { + return (cert); + } + + /* collect certs for this nickname, sorting them into the list */ + certList = CERT_CreateSubjectCertList(certList, handle, + &cert->derSubject, time, validOnly); + + CERT_FilterCertListForUserCerts(certList); + + /* drop the extra reference */ + CERT_DestroyCertificate(cert); + cert = NULL; + } + + if (certList == NULL) { + goto loser; + } + + /* remove certs with incorrect usage */ + rv = CERT_FilterCertListByUsage(certList, usage, PR_FALSE); + + if (rv != SECSuccess) { + goto loser; + } + + if (!CERT_LIST_END(CERT_LIST_HEAD(certList), certList)) { + cert = CERT_DupCertificate(CERT_LIST_HEAD(certList)->cert); + } + +loser: + if (certList != NULL) { + CERT_DestroyCertList(certList); + } + + return (cert); +} + +CERTCertList * +CERT_MatchUserCert(CERTCertDBHandle *handle, + SECCertUsage usage, + int nCANames, char **caNames, + void *proto_win) +{ + CERTCertList *certList = NULL; + SECStatus rv; + + certList = CERT_FindUserCertsByUsage(handle, usage, PR_TRUE, PR_TRUE, + proto_win); + if (certList == NULL) { + goto loser; + } + + rv = CERT_FilterCertListByCANames(certList, nCANames, caNames, usage); + if (rv != SECSuccess) { + goto loser; + } + + goto done; + +loser: + if (certList != NULL) { + CERT_DestroyCertList(certList); + certList = NULL; + } + +done: + + return (certList); +} + +typedef struct stringNode { + struct stringNode *next; + char *string; +} stringNode; + +static PRStatus +CollectNicknames(NSSCertificate *c, void *data) +{ + CERTCertNicknames *names; + PRBool saveit = PR_FALSE; + stringNode *node; + int len; +#ifdef notdef + NSSTrustDomain *td; + NSSTrust *trust; +#endif + char *stanNickname; + char *nickname = NULL; + + names = (CERTCertNicknames *)data; + + stanNickname = nssCertificate_GetNickname(c, NULL); + + if (stanNickname) { + nss_ZFreeIf(stanNickname); + stanNickname = NULL; + if (names->what == SEC_CERT_NICKNAMES_USER) { + saveit = NSSCertificate_IsPrivateKeyAvailable(c, NULL, NULL); + } +#ifdef notdef + else { + td = NSSCertificate_GetTrustDomain(c); + if (!td) { + return PR_SUCCESS; + } + trust = nssTrustDomain_FindTrustForCertificate(td, c); + + switch (names->what) { + case SEC_CERT_NICKNAMES_ALL: + if ((trust->sslFlags & (CERTDB_VALID_CA | CERTDB_VALID_PEER)) || + (trust->emailFlags & (CERTDB_VALID_CA | CERTDB_VALID_PEER)) || + (trust->objectSigningFlags & + (CERTDB_VALID_CA | CERTDB_VALID_PEER))) { + saveit = PR_TRUE; + } + + break; + case SEC_CERT_NICKNAMES_SERVER: + if (trust->sslFlags & CERTDB_VALID_PEER) { + saveit = PR_TRUE; + } + + break; + case SEC_CERT_NICKNAMES_CA: + if (((trust->sslFlags & CERTDB_VALID_CA) == CERTDB_VALID_CA) || + ((trust->emailFlags & CERTDB_VALID_CA) == CERTDB_VALID_CA) || + ((trust->objectSigningFlags & CERTDB_VALID_CA) == + CERTDB_VALID_CA)) { + saveit = PR_TRUE; + } + break; + } + } +#endif + } + + /* traverse the list of collected nicknames and make sure we don't make + * a duplicate + */ + if (saveit) { + nickname = STAN_GetCERTCertificateName(NULL, c); + /* nickname can only be NULL here if we are having memory + * alloc problems */ + if (nickname == NULL) { + return PR_FAILURE; + } + node = (stringNode *)names->head; + while (node != NULL) { + if (PORT_Strcmp(nickname, node->string) == 0) { + /* if the string matches, then don't save this one */ + saveit = PR_FALSE; + break; + } + node = node->next; + } + } + + if (saveit) { + + /* allocate the node */ + node = (stringNode *)PORT_ArenaAlloc(names->arena, sizeof(stringNode)); + if (node == NULL) { + PORT_Free(nickname); + return PR_FAILURE; + } + + /* copy the string */ + len = PORT_Strlen(nickname) + 1; + node->string = (char *)PORT_ArenaAlloc(names->arena, len); + if (node->string == NULL) { + PORT_Free(nickname); + return PR_FAILURE; + } + PORT_Memcpy(node->string, nickname, len); + + /* link it into the list */ + node->next = (stringNode *)names->head; + names->head = (void *)node; + + /* bump the count */ + names->numnicknames++; + } + + if (nickname) + PORT_Free(nickname); + return (PR_SUCCESS); +} + +CERTCertNicknames * +CERT_GetCertNicknames(CERTCertDBHandle *handle, int what, void *wincx) +{ + PLArenaPool *arena; + CERTCertNicknames *names; + int i; + stringNode *node; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return (NULL); + } + + names = (CERTCertNicknames *)PORT_ArenaAlloc(arena, sizeof(CERTCertNicknames)); + if (names == NULL) { + goto loser; + } + + names->arena = arena; + names->head = NULL; + names->numnicknames = 0; + names->nicknames = NULL; + names->what = what; + names->totallen = 0; + + /* make sure we are logged in */ + (void)pk11_TraverseAllSlots(NULL, NULL, PR_TRUE, wincx); + + NSSTrustDomain_TraverseCertificates(handle, + CollectNicknames, (void *)names); + if (names->numnicknames) { + names->nicknames = (char **)PORT_ArenaAlloc(arena, + names->numnicknames * + sizeof(char *)); + + if (names->nicknames == NULL) { + goto loser; + } + + node = (stringNode *)names->head; + + for (i = 0; i < names->numnicknames; i++) { + PORT_Assert(node != NULL); + + names->nicknames[i] = node->string; + names->totallen += PORT_Strlen(node->string); + node = node->next; + } + + PORT_Assert(node == NULL); + } + + return (names); + +loser: + PORT_FreeArena(arena, PR_FALSE); + return (NULL); +} + +void +CERT_FreeNicknames(CERTCertNicknames *nicknames) +{ + PORT_FreeArena(nicknames->arena, PR_FALSE); + + return; +} + +/* [ FROM pcertdb.c ] */ + +typedef struct dnameNode { + struct dnameNode *next; + SECItem name; +} dnameNode; + +void +CERT_FreeDistNames(CERTDistNames *names) +{ + PORT_FreeArena(names->arena, PR_FALSE); + + return; +} + +static SECStatus +CollectDistNames(CERTCertificate *cert, SECItem *k, void *data) +{ + CERTDistNames *names; + PRBool saveit = PR_FALSE; + CERTCertTrust trust; + dnameNode *node; + int len; + + names = (CERTDistNames *)data; + + if (CERT_GetCertTrust(cert, &trust) == SECSuccess) { + /* only collect names of CAs trusted for issuing SSL clients */ + if (trust.sslFlags & CERTDB_TRUSTED_CLIENT_CA) { + saveit = PR_TRUE; + } + } + + if (saveit) { + /* allocate the node */ + node = (dnameNode *)PORT_ArenaAlloc(names->arena, sizeof(dnameNode)); + if (node == NULL) { + return (SECFailure); + } + + /* copy the name */ + node->name.len = len = cert->derSubject.len; + node->name.type = siBuffer; + node->name.data = (unsigned char *)PORT_ArenaAlloc(names->arena, len); + if (node->name.data == NULL) { + return (SECFailure); + } + PORT_Memcpy(node->name.data, cert->derSubject.data, len); + + /* link it into the list */ + node->next = (dnameNode *)names->head; + names->head = (void *)node; + + /* bump the count */ + names->nnames++; + } + + return (SECSuccess); +} + +/* + * Return all of the CAs that are "trusted" for SSL. + */ +CERTDistNames * +CERT_DupDistNames(CERTDistNames *orig) +{ + PLArenaPool *arena; + CERTDistNames *names; + int i; + SECStatus rv; + + /* allocate an arena to use */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return (NULL); + } + + /* allocate the header structure */ + names = (CERTDistNames *)PORT_ArenaAlloc(arena, sizeof(CERTDistNames)); + if (names == NULL) { + goto loser; + } + + /* initialize the header struct */ + names->arena = arena; + names->head = NULL; + names->nnames = orig->nnames; + names->names = NULL; + + /* construct the array from the list */ + if (orig->nnames) { + names->names = (SECItem *)PORT_ArenaNewArray(arena, SECItem, + orig->nnames); + if (names->names == NULL) { + goto loser; + } + for (i = 0; i < orig->nnames; i++) { + rv = SECITEM_CopyItem(arena, &names->names[i], &orig->names[i]); + if (rv != SECSuccess) { + goto loser; + } + } + } + return (names); + +loser: + PORT_FreeArena(arena, PR_FALSE); + return (NULL); +} + +CERTDistNames * +CERT_GetSSLCACerts(CERTCertDBHandle *handle) +{ + PLArenaPool *arena; + CERTDistNames *names; + int i; + SECStatus rv; + dnameNode *node; + + /* allocate an arena to use */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return (NULL); + } + + /* allocate the header structure */ + names = (CERTDistNames *)PORT_ArenaAlloc(arena, sizeof(CERTDistNames)); + if (names == NULL) { + goto loser; + } + + /* initialize the header struct */ + names->arena = arena; + names->head = NULL; + names->nnames = 0; + names->names = NULL; + + /* collect the names from the database */ + rv = PK11_TraverseSlotCerts(CollectDistNames, (void *)names, NULL); + if (rv) { + goto loser; + } + + /* construct the array from the list */ + if (names->nnames) { + names->names = (SECItem *)PORT_ArenaAlloc(arena, names->nnames * sizeof(SECItem)); + + if (names->names == NULL) { + goto loser; + } + + node = (dnameNode *)names->head; + + for (i = 0; i < names->nnames; i++) { + PORT_Assert(node != NULL); + + names->names[i] = node->name; + node = node->next; + } + + PORT_Assert(node == NULL); + } + + return (names); + +loser: + PORT_FreeArena(arena, PR_FALSE); + return (NULL); +} + +CERTDistNames * +CERT_DistNamesFromCertList(CERTCertList *certList) +{ + CERTDistNames *dnames = NULL; + PLArenaPool *arena; + CERTCertListNode *node = NULL; + SECItem *names = NULL; + int listLen = 0, i = 0; + + if (certList == NULL) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + node = CERT_LIST_HEAD(certList); + while (!CERT_LIST_END(node, certList)) { + listLen += 1; + node = CERT_LIST_NEXT(node); + } + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) + goto loser; + dnames = PORT_ArenaZNew(arena, CERTDistNames); + if (dnames == NULL) + goto loser; + + dnames->arena = arena; + dnames->nnames = listLen; + dnames->names = names = PORT_ArenaZNewArray(arena, SECItem, listLen); + if (names == NULL) + goto loser; + + node = CERT_LIST_HEAD(certList); + while (!CERT_LIST_END(node, certList)) { + CERTCertificate *cert = node->cert; + SECStatus rv = SECITEM_CopyItem(arena, &names[i++], &cert->derSubject); + if (rv == SECFailure) { + goto loser; + } + node = CERT_LIST_NEXT(node); + } + return dnames; +loser: + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + return NULL; +} + +CERTDistNames * +CERT_DistNamesFromNicknames(CERTCertDBHandle *handle, char **nicknames, + int nnames) +{ + CERTDistNames *dnames = NULL; + PLArenaPool *arena; + int i, rv; + SECItem *names = NULL; + CERTCertificate *cert = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) + goto loser; + dnames = PORT_ArenaZNew(arena, CERTDistNames); + if (dnames == NULL) + goto loser; + + dnames->arena = arena; + dnames->nnames = nnames; + dnames->names = names = PORT_ArenaZNewArray(arena, SECItem, nnames); + if (names == NULL) + goto loser; + + for (i = 0; i < nnames; i++) { + cert = CERT_FindCertByNicknameOrEmailAddr(handle, nicknames[i]); + if (cert == NULL) + goto loser; + rv = SECITEM_CopyItem(arena, &names[i], &cert->derSubject); + if (rv == SECFailure) + goto loser; + CERT_DestroyCertificate(cert); + } + return dnames; + +loser: + if (cert != NULL) + CERT_DestroyCertificate(cert); + if (arena != NULL) + PORT_FreeArena(arena, PR_FALSE); + return NULL; +} + +/* [ from pcertdb.c - calls Ascii to Name ] */ +/* + * Lookup a certificate in the database by name + */ +CERTCertificate * +CERT_FindCertByNameString(CERTCertDBHandle *handle, char *nameStr) +{ + CERTName *name; + SECItem *nameItem; + CERTCertificate *cert = NULL; + PLArenaPool *arena = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if (arena == NULL) { + goto loser; + } + + name = CERT_AsciiToName(nameStr); + + if (name) { + nameItem = SEC_ASN1EncodeItem(arena, NULL, (void *)name, + CERT_NameTemplate); + if (nameItem != NULL) { + cert = CERT_FindCertByName(handle, nameItem); + } + CERT_DestroyName(name); + } + +loser: + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + + return (cert); +} + +/* From certv3.c */ + +CERTCrlDistributionPoints * +CERT_FindCRLDistributionPoints(CERTCertificate *cert) +{ + SECItem encodedExtenValue; + SECStatus rv; + CERTCrlDistributionPoints *dps; + + encodedExtenValue.data = NULL; + encodedExtenValue.len = 0; + + rv = cert_FindExtension(cert->extensions, SEC_OID_X509_CRL_DIST_POINTS, + &encodedExtenValue); + if (rv != SECSuccess) { + return (NULL); + } + + dps = CERT_DecodeCRLDistributionPoints(cert->arena, &encodedExtenValue); + + PORT_Free(encodedExtenValue.data); + + return dps; +} + +/* From crl.c */ +CERTSignedCrl * +CERT_ImportCRL(CERTCertDBHandle *handle, SECItem *derCRL, char *url, int type, void *wincx) +{ + CERTSignedCrl *retCrl = NULL; + PK11SlotInfo *slot = PK11_GetInternalKeySlot(); + retCrl = PK11_ImportCRL(slot, derCRL, url, type, wincx, + CRL_IMPORT_DEFAULT_OPTIONS, NULL, CRL_DECODE_DEFAULT_OPTIONS); + PK11_FreeSlot(slot); + + return retCrl; +} + +/* From certdb.c */ +static SECStatus +cert_ImportCAChain(SECItem *certs, int numcerts, SECCertUsage certUsage, PRBool trusted) +{ + SECStatus rv; + SECItem *derCert; + CERTCertificate *cert = NULL; + CERTCertificate *newcert = NULL; + CERTCertDBHandle *handle; + CERTCertTrust trust; + PRBool isca; + char *nickname; + unsigned int certtype; + + handle = CERT_GetDefaultCertDB(); + + while (numcerts--) { + derCert = certs; + certs++; + + /* decode my certificate */ + /* This use is ok -- only looks at decoded parts, calls NewTemp later */ + newcert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL); + if (newcert == NULL) { + goto loser; + } + + if (!trusted) { + /* make sure that cert is valid */ + rv = CERT_CertTimesValid(newcert); + if (rv == SECFailure) { + goto endloop; + } + } + + /* does it have the CA extension */ + + /* + * Make sure that if this is an intermediate CA in the chain that + * it was given permission by its signer to be a CA. + */ + isca = CERT_IsCACert(newcert, &certtype); + + if (!isca) { + if (!trusted) { + goto endloop; + } + trust.sslFlags = CERTDB_VALID_CA; + trust.emailFlags = CERTDB_VALID_CA; + trust.objectSigningFlags = CERTDB_VALID_CA; + } else { + /* SSL ca's must have the ssl bit set */ + if ((certUsage == certUsageSSLCA) && + ((certtype & NS_CERT_TYPE_SSL_CA) != NS_CERT_TYPE_SSL_CA)) { + goto endloop; + } + + /* it passed all of the tests, so lets add it to the database */ + /* mark it as a CA */ + PORT_Memset((void *)&trust, 0, sizeof(trust)); + switch (certUsage) { + case certUsageSSLCA: + trust.sslFlags = CERTDB_VALID_CA; + break; + case certUsageUserCertImport: + if ((certtype & NS_CERT_TYPE_SSL_CA) == NS_CERT_TYPE_SSL_CA) { + trust.sslFlags = CERTDB_VALID_CA; + } + if ((certtype & NS_CERT_TYPE_EMAIL_CA) == + NS_CERT_TYPE_EMAIL_CA) { + trust.emailFlags = CERTDB_VALID_CA; + } + if ((certtype & NS_CERT_TYPE_OBJECT_SIGNING_CA) == + NS_CERT_TYPE_OBJECT_SIGNING_CA) { + trust.objectSigningFlags = CERTDB_VALID_CA; + } + break; + default: + PORT_Assert(0); + break; + } + } + + cert = CERT_NewTempCertificate(handle, derCert, NULL, + PR_FALSE, PR_FALSE); + if (cert == NULL) { + goto loser; + } + + /* if the cert is temp, make it perm; otherwise we're done */ + if (cert->istemp) { + /* get a default nickname for it */ + nickname = CERT_MakeCANickname(cert); + + rv = CERT_AddTempCertToPerm(cert, nickname, &trust); + + /* free the nickname */ + if (nickname) { + PORT_Free(nickname); + } + } else { + rv = SECSuccess; + } + + CERT_DestroyCertificate(cert); + cert = NULL; + + if (rv != SECSuccess) { + goto loser; + } + + endloop: + if (newcert) { + CERT_DestroyCertificate(newcert); + newcert = NULL; + } + } + + rv = SECSuccess; + goto done; +loser: + rv = SECFailure; +done: + + if (newcert) { + CERT_DestroyCertificate(newcert); + newcert = NULL; + } + + if (cert) { + CERT_DestroyCertificate(cert); + cert = NULL; + } + + return (rv); +} + +SECStatus +CERT_ImportCAChain(SECItem *certs, int numcerts, SECCertUsage certUsage) +{ + return cert_ImportCAChain(certs, numcerts, certUsage, PR_FALSE); +} + +SECStatus +CERT_ImportCAChainTrusted(SECItem *certs, int numcerts, SECCertUsage certUsage) +{ + return cert_ImportCAChain(certs, numcerts, certUsage, PR_TRUE); +} + +/* Moved from certdb.c */ +/* +** CERT_CertChainFromCert +** +** Construct a CERTCertificateList consisting of the given certificate and all +** of the issuer certs until we either get to a self-signed cert or can't find +** an issuer. Since we don't know how many certs are in the chain we have to +** build a linked list first as we count them. +*/ + +typedef struct certNode { + struct certNode *next; + CERTCertificate *cert; +} certNode; + +CERTCertificateList * +CERT_CertChainFromCert(CERTCertificate *cert, SECCertUsage usage, + PRBool includeRoot) +{ + CERTCertificateList *chain = NULL; + NSSCertificate **stanChain; + NSSCertificate *stanCert; + PLArenaPool *arena; + NSSUsage nssUsage; + int i, len; + NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); + NSSCryptoContext *cc = STAN_GetDefaultCryptoContext(); + + stanCert = STAN_GetNSSCertificate(cert); + if (!stanCert) { + /* error code is set */ + return NULL; + } + nssUsage.anyUsage = PR_FALSE; + nssUsage.nss3usage = usage; + nssUsage.nss3lookingForCA = PR_FALSE; + stanChain = NSSCertificate_BuildChain(stanCert, NULL, &nssUsage, NULL, NULL, + CERT_MAX_CERT_CHAIN, NULL, NULL, td, cc); + if (!stanChain) { + PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER); + return NULL; + } + + len = 0; + stanCert = stanChain[0]; + while (stanCert) { + stanCert = stanChain[++len]; + } + + arena = PORT_NewArena(4096); + if (arena == NULL) { + goto loser; + } + + chain = (CERTCertificateList *)PORT_ArenaAlloc(arena, + sizeof(CERTCertificateList)); + if (!chain) + goto loser; + chain->certs = (SECItem *)PORT_ArenaAlloc(arena, len * sizeof(SECItem)); + if (!chain->certs) + goto loser; + i = 0; + stanCert = stanChain[i]; + while (stanCert) { + SECItem derCert; + CERTCertificate *cCert = STAN_GetCERTCertificate(stanCert); + if (!cCert) { + goto loser; + } + derCert.len = (unsigned int)stanCert->encoding.size; + derCert.data = (unsigned char *)stanCert->encoding.data; + derCert.type = siBuffer; + SECITEM_CopyItem(arena, &chain->certs[i], &derCert); + stanCert = stanChain[++i]; + if (!stanCert && !cCert->isRoot) { + /* reached the end of the chain, but the final cert is + * not a root. Don't discard it. + */ + includeRoot = PR_TRUE; + } + CERT_DestroyCertificate(cCert); + } + if (!includeRoot && len > 1) { + chain->len = len - 1; + } else { + chain->len = len; + } + + chain->arena = arena; + nss_ZFreeIf(stanChain); + return chain; +loser: + i = 0; + stanCert = stanChain[i]; + while (stanCert) { + CERTCertificate *cCert = STAN_GetCERTCertificate(stanCert); + if (cCert) { + CERT_DestroyCertificate(cCert); + } + stanCert = stanChain[++i]; + } + nss_ZFreeIf(stanChain); + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + return NULL; +} + +/* Builds a CERTCertificateList holding just one DER-encoded cert, namely +** the one for the cert passed as an argument. +*/ +CERTCertificateList * +CERT_CertListFromCert(CERTCertificate *cert) +{ + CERTCertificateList *chain = NULL; + int rv; + PLArenaPool *arena; + + /* arena for SecCertificateList */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) + goto no_memory; + + /* build the CERTCertificateList */ + chain = (CERTCertificateList *)PORT_ArenaAlloc(arena, sizeof(CERTCertificateList)); + if (chain == NULL) + goto no_memory; + chain->certs = (SECItem *)PORT_ArenaAlloc(arena, 1 * sizeof(SECItem)); + if (chain->certs == NULL) + goto no_memory; + rv = SECITEM_CopyItem(arena, chain->certs, &(cert->derCert)); + if (rv < 0) + goto loser; + chain->len = 1; + chain->arena = arena; + + return chain; + +no_memory: + PORT_SetError(SEC_ERROR_NO_MEMORY); +loser: + if (arena != NULL) { + PORT_FreeArena(arena, PR_FALSE); + } + return NULL; +} + +CERTCertificateList * +CERT_DupCertList(const CERTCertificateList *oldList) +{ + CERTCertificateList *newList = NULL; + PLArenaPool *arena = NULL; + SECItem *newItem; + SECItem *oldItem; + int len = oldList->len; + int rv; + + /* arena for SecCertificateList */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) + goto no_memory; + + /* now build the CERTCertificateList */ + newList = PORT_ArenaNew(arena, CERTCertificateList); + if (newList == NULL) + goto no_memory; + newList->arena = arena; + newItem = (SECItem *)PORT_ArenaAlloc(arena, len * sizeof(SECItem)); + if (newItem == NULL) + goto no_memory; + newList->certs = newItem; + newList->len = len; + + for (oldItem = oldList->certs; len > 0; --len, ++newItem, ++oldItem) { + rv = SECITEM_CopyItem(arena, newItem, oldItem); + if (rv < 0) + goto loser; + } + return newList; + +no_memory: + PORT_SetError(SEC_ERROR_NO_MEMORY); +loser: + if (arena != NULL) { + PORT_FreeArena(arena, PR_FALSE); + } + return NULL; +} + +void +CERT_DestroyCertificateList(CERTCertificateList *list) +{ + PORT_FreeArena(list->arena, PR_FALSE); +} |