| /* 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 "certi.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_EMPTY(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; |
| PRBool istemp = PR_FALSE; |
| |
| 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 */ |
| rv = CERT_GetCertIsTemp(cert, &istemp); |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| if (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; |
| } |
| |
| 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; |
| if (SECITEM_CopyItem(arena, &chain->certs[i], &derCert) != SECSuccess) { |
| CERT_DestroyCertificate(cCert); |
| goto loser; |
| } |
| 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); |
| } |