gecko-dev/security/nss/lib/certhigh/certhigh.c

1208 строки
32 KiB
C

/* 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_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;
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;
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);
}