Ejemplo n.º 1
0
static PRBool
nss3certificate_isValidIssuer(nssDecodedCert *dc)
{
    CERTCertificate *c = (CERTCertificate *)dc->data;
    unsigned int ignore;
    return CERT_IsCACert(c, &ignore);
}
Ejemplo n.º 2
0
static CERTCertList *get_all_root_certs(void)
{
	PK11SlotInfo *slot = PK11_GetInternalKeySlot();

	if (slot == NULL)
		return NULL;

	if (PK11_NeedLogin(slot)) {
		SECStatus rv = PK11_Authenticate(slot, PR_TRUE,
				lsw_return_nss_password_file_info());
		if (rv != SECSuccess)
			return NULL;
	}

	CERTCertList *allcerts = PK11_ListCertsInSlot(slot);

	if (allcerts == NULL)
		return NULL;

	CERTCertList *roots = CERT_NewCertList();

	CERTCertListNode *node;

	for (node = CERT_LIST_HEAD(allcerts); !CERT_LIST_END(node, allcerts);
						node = CERT_LIST_NEXT(node)) {
		if (CERT_IsCACert(node->cert, NULL) && node->cert->isRoot) {
			CERT_DupCertificate(node->cert);
			CERT_AddCertToListTail(roots, node->cert);
		}
	}

	CERT_DestroyCertList(allcerts);

	if (roots == NULL || CERT_LIST_EMPTY(roots))
		return NULL;

	return roots;
}
Ejemplo n.º 3
0
/* CERT_FilterCertListByUsage */
static PRBool
nss3certificate_matchUsage(nssDecodedCert *dc, const NSSUsage *usage)
{
    CERTCertificate *cc;
    unsigned int requiredKeyUsage = 0;
    unsigned int requiredCertType = 0;
    SECStatus secrv;
    PRBool match;
    PRBool ca;

    /* This is for NSS 3.3 functions that do not specify a usage */
    if (usage->anyUsage) {
	return PR_TRUE;
    }
    ca = usage->nss3lookingForCA;
    secrv = CERT_KeyUsageAndTypeForCertUsage(usage->nss3usage, ca,
                                             &requiredKeyUsage,
                                             &requiredCertType);
    if (secrv != SECSuccess) {
	return PR_FALSE;
    }
    cc = (CERTCertificate *)dc->data;
    secrv = CERT_CheckKeyUsage(cc, requiredKeyUsage);
    match = (PRBool)(secrv == SECSuccess);
    if (match) {
	unsigned int certType = 0;
	if (ca) {
	    (void)CERT_IsCACert(cc, &certType);
	} else {
	    certType = cc->nsCertType;
	}
	if (!(certType & requiredCertType)) {
	    match = PR_FALSE;
	}
    }
    return match;
}
Ejemplo n.º 4
0
static int vfy_chain_pkix(CERTCertificate **chain, int chain_len,
						   CERTCertificate **end_out,
						   bool *rev_opts)
{
	CERTCertificate *end_cert = NULL;
	CERTCertList *trustcl = get_all_root_certs();

	if (trustcl == NULL) {
		DBG(DBG_X509, DBG_log("X509: no trust anchor available for verification"));
		return VERIFY_RET_SKIP;
	}

	int i;

	for (i = 0; i < chain_len; i++) {
		if (!CERT_IsCACert(chain[i], NULL)) {
			end_cert = chain[i];
			break;
		}
	}

	if (end_cert == NULL) {
		libreswan_log("X509: no EE-cert in chain!");
		return VERIFY_RET_FAIL;
	}


	CERTVerifyLog *cur_log = NULL;
	CERTVerifyLog vfy_log;
	CERTVerifyLog vfy_log2;

	new_vfy_log(&vfy_log);
	new_vfy_log(&vfy_log2);

	CERTRevocationFlags rev;
	zero(&rev);	/* ??? are there pointer fields? */

	PRUint64 revFlagsLeaf[2] = { 0, 0 };
	PRUint64 revFlagsChain[2] = { 0, 0 };

	set_rev_per_meth(&rev, revFlagsLeaf, revFlagsChain);
	set_rev_params(&rev, rev_opts[RO_CRL_S], rev_opts[RO_OCSP],
						 rev_opts[RO_OCSP_S]);
	int in_idx = 0;
	CERTValInParam cvin[7];
	CERTValOutParam cvout[3];
	zero(&cvin);	/* ??? are there pointer fields? */
	zero(&cvout);	/* ??? are there pointer fields? */

	cvin[in_idx].type = cert_pi_revocationFlags;
	cvin[in_idx++].value.pointer.revocation = &rev;

	cvin[in_idx].type = cert_pi_useAIACertFetch;
	cvin[in_idx++].value.scalar.b = rev_opts[RO_OCSP];

	cvin[in_idx].type = cert_pi_trustAnchors;
	cvin[in_idx++].value.pointer.chain = trustcl;

	cvin[in_idx].type = cert_pi_useOnlyTrustAnchors;
	cvin[in_idx++].value.scalar.b = PR_TRUE;

	cvin[in_idx].type = cert_pi_end;

	cvout[0].type = cert_po_errorLog;
	cvout[0].value.pointer.log = cur_log = &vfy_log;
	cvout[1].type = cert_po_certList;
	cvout[1].value.pointer.chain = NULL;
	cvout[2].type = cert_po_end;

	/* kludge alert!!
	 * verification may be performed twice: once with the
	 * 'client' usage and once with 'server', which is an NSS
	 * detail and not related to IKE. In the absence of a real
	 * IKE profile being available for NSS, this covers more
	 * KU/EKU combinations
	 */

	int fin;
	SECCertificateUsage usage;

	for (usage = certificateUsageSSLClient; ; usage = certificateUsageSSLServer) {
		SECStatus rv = CERT_PKIXVerifyCert(end_cert, usage, cvin, cvout, NULL);

		if (rv != SECSuccess || cur_log->count > 0) {
			if (cur_log->count > 0 && cur_log->head != NULL) {
				if (usage == certificateUsageSSLClient &&
				    RETRYABLE_TYPE(cur_log->head->error)) {
					/* try again, after some adjustments */
					DBG(DBG_X509,
					    DBG_log("retrying verification with the NSS serverAuth profile"));
					/* ??? since we are about to overwrite cvout[1],
					 * should we be doing:
					 * if (cvout[1].value.pointer.chain != NULL)
					 *	CERT_DestroyCertList(cvout[1].value.pointer.chain);
					 */
					cvout[0].value.pointer.log = cur_log = &vfy_log2;
					cvout[1].value.pointer.chain = NULL;
					continue;
				} else {
					fin = nss_err_to_revfail(cur_log->head);
				}
			} else {
				/*
				 * An rv != SECSuccess without CERTVerifyLog results should not
				 * happen, but catch it anyway
				 */
				libreswan_log("X509: unspecified NSS verification failure");
				fin = VERIFY_RET_FAIL;
			}
		} else {
			DBG(DBG_X509, DBG_log("certificate is valid"));
			*end_out = end_cert;
			fin = VERIFY_RET_OK;
		}
		break;
	}

	CERT_DestroyCertList(trustcl);
	PORT_FreeArena(vfy_log.arena, PR_FALSE);
	PORT_FreeArena(vfy_log2.arena, PR_FALSE);

	if (cvout[1].value.pointer.chain != NULL) {
		CERT_DestroyCertList(cvout[1].value.pointer.chain);
	}

	return fin;
}
/* 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);
}
Ejemplo n.º 6
0
// Based on nsNSSCertificateDB::handleCACertDownload, minus the UI bits.
bool ImportCACerts(const net::CertificateList& certificates,
                   net::X509Certificate* root,
                   net::NSSCertDatabase::TrustBits trustBits,
                   net::NSSCertDatabase::ImportCertFailureList* not_imported) {
  if (certificates.empty() || !root)
    return false;

  crypto::ScopedPK11Slot slot(crypto::GetPublicNSSKeySlot());
  if (!slot.get()) {
    LOG(ERROR) << "Couldn't get internal key slot!";
    return false;
  }

  // Mozilla had some code here to check if a perm version of the cert exists
  // already and use that, but CERT_NewTempCertificate actually does that
  // itself, so we skip it here.

  if (!CERT_IsCACert(root->os_cert_handle(), NULL)) {
    not_imported->push_back(net::NSSCertDatabase::ImportCertFailure(
        root, net::ERR_IMPORT_CA_CERT_NOT_CA));
  } else if (root->os_cert_handle()->isperm) {
    // Mozilla just returns here, but we continue in case there are other certs
    // in the list which aren't already imported.
    // TODO(mattm): should we set/add trust if it differs from the present
    // settings?
    not_imported->push_back(net::NSSCertDatabase::ImportCertFailure(
        root, net::ERR_IMPORT_CERT_ALREADY_EXISTS));
  } else {
    // Mozilla uses CERT_AddTempCertToPerm, however it is privately exported,
    // and it doesn't take the slot as an argument either.  Instead, we use
    // PK11_ImportCert and CERT_ChangeCertTrust.
    SECStatus srv = PK11_ImportCert(
        slot.get(),
        root->os_cert_handle(),
        CK_INVALID_HANDLE,
        root->GetDefaultNickname(net::CA_CERT).c_str(),
        PR_FALSE /* includeTrust (unused) */);
    if (srv != SECSuccess) {
      LOG(ERROR) << "PK11_ImportCert failed with error " << PORT_GetError();
      return false;
    }
    if (!SetCertTrust(root, net::CA_CERT, trustBits))
      return false;
  }

  PRTime now = PR_Now();
  // Import additional delivered certificates that can be verified.
  // This is sort of merged in from Mozilla's ImportValidCACertsInList.  Mozilla
  // uses CERT_FilterCertListByUsage to filter out non-ca certs, but we want to
  // keep using X509Certificates, so that we can use them to build the
  // |not_imported| result.  So, we keep using our net::CertificateList and
  // filter it ourself.
  for (size_t i = 0; i < certificates.size(); i++) {
    const scoped_refptr<net::X509Certificate>& cert = certificates[i];
    if (cert == root) {
      // we already processed that one
      continue;
    }

    // Mozilla uses CERT_FilterCertListByUsage(certList, certUsageAnyCA,
    // PR_TRUE).  Afaict, checking !CERT_IsCACert on each cert is equivalent.
    if (!CERT_IsCACert(cert->os_cert_handle(), NULL)) {
      not_imported->push_back(net::NSSCertDatabase::ImportCertFailure(
          cert, net::ERR_IMPORT_CA_CERT_NOT_CA));
      VLOG(1) << "skipping cert (non-ca)";
      continue;
    }

    if (cert->os_cert_handle()->isperm) {
      not_imported->push_back(net::NSSCertDatabase::ImportCertFailure(
          cert, net::ERR_IMPORT_CERT_ALREADY_EXISTS));
      VLOG(1) << "skipping cert (perm)";
      continue;
    }

    if (CERT_VerifyCert(CERT_GetDefaultCertDB(), cert->os_cert_handle(),
        PR_TRUE, certUsageVerifyCA, now, NULL, NULL) != SECSuccess) {
      // TODO(mattm): use better error code (map PORT_GetError to an appropriate
      // error value).  (maybe make MapSecurityError or MapCertErrorToCertStatus
      // public.)
      not_imported->push_back(net::NSSCertDatabase::ImportCertFailure(
          cert, net::ERR_FAILED));
      VLOG(1) << "skipping cert (verify) " << PORT_GetError();
      continue;
    }

    // Mozilla uses CERT_ImportCerts, which doesn't take a slot arg.  We use
    // PK11_ImportCert instead.
    SECStatus srv = PK11_ImportCert(
        slot.get(),
        cert->os_cert_handle(),
        CK_INVALID_HANDLE,
        cert->GetDefaultNickname(net::CA_CERT).c_str(),
        PR_FALSE /* includeTrust (unused) */);
    if (srv != SECSuccess) {
      LOG(ERROR) << "PK11_ImportCert failed with error " << PORT_GetError();
      // TODO(mattm): Should we bail or continue on error here?  Mozilla doesn't
      // check error code at all.
      not_imported->push_back(net::NSSCertDatabase::ImportCertFailure(
          cert, net::ERR_IMPORT_CA_CERT_FAILED));
    }
  }

  // Any errors importing individual certs will be in listed in |not_imported|.
  return true;
}
Ejemplo n.º 7
0
/*
 * FUNCTION: pkix_pl_OcspResponse_VerifySignature
 * DESCRIPTION:
 *
 *  This function verifies the ocspResponse signature field in the OcspResponse
 *  pointed to by "response", storing PKIX_TRUE at "pPassed" if verification
 *  is successful and PKIX_FALSE otherwise. If verification is unsuccessful an
 *  error code (an enumeration of type SECErrorCodes) is stored at *pReturnCode.
 *
 * PARAMETERS
 *  "response"
 *      The address of the OcspResponse whose signature field is to be
 *      retrieved. Must be non-NULL.
 *  "cert"
 *      The address of the Cert for which the OCSP query was made. Must be
 *      non-NULL.
 *  "procParams"
 *      Address of ProcessingParams used to initialize the ExpirationChecker
 *      and TargetCertChecker. Must be non-NULL.
 *  "pPassed"
 *      Address at which the Boolean result is stored. Must be non-NULL.
 *  "pNBIOContext"
 *      Address at which the NBIOContext is stored indicating whether the
 *      checking is complete. Must be non-NULL.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns NULL if the function succeeds.
 *  Returns an OcspResponse Error if the function fails in a non-fatal way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
PKIX_Error *
pkix_pl_OcspResponse_VerifySignature(
        PKIX_PL_OcspResponse *response,
        PKIX_PL_Cert *cert,
        PKIX_ProcessingParams *procParams,
        PKIX_Boolean *pPassed,
        void **pNBIOContext,
        void *plContext)
{
        SECStatus rv = SECFailure;
        CERTOCSPResponse *nssOCSPResponse = NULL;
        CERTCertificate *issuerCert = NULL;
        PKIX_BuildResult *buildResult = NULL;
        void *nbio = NULL;
        void *state = NULL;

        ocspSignature *signature = NULL;
        ocspResponseData *tbsData = NULL;
        SECItem *tbsResponseDataDER = NULL;


        PKIX_ENTER(OCSPRESPONSE, "pkix_pl_OcspResponse_VerifySignature");
        PKIX_NULLCHECK_FOUR(response, cert, pPassed,  pNBIOContext);

        nbio = *pNBIOContext;
        *pNBIOContext = NULL;

        nssOCSPResponse = response->nssOCSPResponse;
        if (nssOCSPResponse == NULL) {
            PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
            goto cleanup;
        }

        tbsData =
            ocsp_GetResponseData(nssOCSPResponse, &tbsResponseDataDER);
        
        signature = ocsp_GetResponseSignature(nssOCSPResponse);


        /* Are we resuming after a WOULDBLOCK response? */
        if (nbio == NULL) {
            /* No, this is a new query */

            issuerCert = CERT_FindCertIssuer(cert->nssCert, PR_Now(),
                                             certUsageAnyCA);
            
            /*
             * If this signature has already gone through verification,
             * just return the cached result.
             */
            if (signature->wasChecked) {
                if (signature->status == SECSuccess) {
                    response->signerCert =
                        CERT_DupCertificate(signature->cert);
                } else {
                    PORT_SetError(signature->failureReason);
                    goto cleanup;
                }
            }
            
            response->signerCert = 
                ocsp_GetSignerCertificate(response->handle, tbsData,
                                          signature, issuerCert);
            
            if (response->signerCert == NULL) {
                PORT_SetError(SEC_ERROR_UNKNOWN_SIGNER);
                goto cleanup;
            }
            
            PKIX_CHECK( 
                PKIX_PL_Cert_CreateFromCERTCertificate(response->signerCert,
                                                       &(response->pkixSignerCert),
                                                       plContext),
                PKIX_CERTCREATEWITHNSSCERTFAILED);
            
            /*
             * We could mark this true at the top of this function, or
             * always below at "finish", but if the problem was just that
             * we could not find the signer's cert, leave that as if the
             * signature hasn't been checked. Maybe a subsequent call will
             * have better luck.
             */
            signature->wasChecked = PR_TRUE;
            
            /*
             * We are about to verify the signer certificate; we need to
             * specify *when* that certificate must be valid -- for our
             * purposes we expect it to be valid when the response was
             * signed. The value of "producedAt" is the signing time.
             */
            rv = DER_GeneralizedTimeToTime(&response->producedAt,
                                           &tbsData->producedAt);
            if (rv != SECSuccess) {
                PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
                goto cleanup;
            }
            
            /*
             * We need producedAtDate and pkixSignerCert if we are calling a
             * user-supplied verification function. Let's put their
             * creation before the code that gets repeated when
             * non-blocking I/O is used.
             */
            
            PKIX_CHECK(
                pkix_pl_Date_CreateFromPRTime((PRTime)response->producedAt,
                                              &(response->producedAtDate),
                                              plContext),
                PKIX_DATECREATEFROMPRTIMEFAILED);
            
	}
        
        /*
         * Just because we have a cert does not mean it is any good; check
         * it for validity, trust and usage. Use the caller-supplied
         * verification function, if one was supplied.
         */
        if (ocsp_CertIsOCSPDefaultResponder(response->handle,
                                            response->signerCert)) {
            rv = SECSuccess;
        } else {
            SECCertUsage certUsage;
            if (CERT_IsCACert(response->signerCert, NULL)) {
                certUsage = certUsageAnyCA;
            } else {
                certUsage = certUsageStatusResponder;
            }
            PKIX_CHECK_ONLY_FATAL(
                pkix_pl_OcspResponse_VerifyResponse(response, procParams,
                                                    certUsage, &state,
                                                    &buildResult, &nbio,
                                                    plContext),
                PKIX_CERTVERIFYKEYUSAGEFAILED);
            if (pkixTempErrorReceived) {
                rv = SECFailure;
                goto cleanup;
            }
            if (nbio != NULL) {
                *pNBIOContext = nbio;
                goto cleanup;
            }            
        }

        rv = ocsp_VerifyResponseSignature(response->signerCert, signature,
                                          tbsResponseDataDER, NULL);
        
cleanup:
        if (rv == SECSuccess) {
            *pPassed = PKIX_TRUE;
        } else {
            *pPassed = PKIX_FALSE;
        }
        
        if (signature) {
            if (signature->wasChecked) {
                signature->status = rv;
            }
            
            if (rv != SECSuccess) {
                signature->failureReason = PORT_GetError();
                if (response->signerCert != NULL) {
                    CERT_DestroyCertificate(response->signerCert);
                    response->signerCert = NULL;
                }
            } else {
                /* Save signer's certificate in signature. */
                signature->cert = CERT_DupCertificate(response->signerCert);
            }
        }

	if (issuerCert)
	    CERT_DestroyCertificate(issuerCert);
        
        PKIX_RETURN(OCSPRESPONSE);
}
Ejemplo n.º 8
0
nsresult
nsNSSCertificateDB::handleCACertDownload(nsIArray *x509Certs,
                                         nsIInterfaceRequestor *ctx)
{
  // First thing we have to do is figure out which certificate we're 
  // gonna present to the user.  The CA may have sent down a list of 
  // certs which may or may not be a chained list of certs.  Until
  // the day we can design some solid UI for the general case, we'll
  // code to the > 90% case.  That case is where a CA sends down a
  // list that is a hierarchy whose root is either the first or 
  // the last cert.  What we're gonna do is compare the first 
  // 2 entries, if the second was signed by the first, we assume
  // the root cert is the first cert and display it.  Otherwise,
  // we compare the last 2 entries, if the second to last cert was
  // signed by the last cert, then we assume the last cert is the
  // root and display it.

  nsNSSShutDownPreventionLock locker;

  PRUint32 numCerts;

  x509Certs->GetLength(&numCerts);
  NS_ASSERTION(numCerts > 0, "Didn't get any certs to import.");
  if (numCerts == 0)
    return NS_OK; // Nothing to import, so nothing to do.

  nsCOMPtr<nsIX509Cert> certToShow;
  nsCOMPtr<nsISupports> isupports;
  PRUint32 selCertIndex;
  if (numCerts == 1) {
    // There's only one cert, so let's show it.
    selCertIndex = 0;
    certToShow = do_QueryElementAt(x509Certs, selCertIndex);
  } else {
    nsCOMPtr<nsIX509Cert> cert0;    // first cert
    nsCOMPtr<nsIX509Cert> cert1;    // second cert
    nsCOMPtr<nsIX509Cert> certn_2;  // second to last cert
    nsCOMPtr<nsIX509Cert> certn_1;  // last cert

    cert0 = do_QueryElementAt(x509Certs, 0);
    cert1 = do_QueryElementAt(x509Certs, 1);
    certn_2 = do_QueryElementAt(x509Certs, numCerts-2);
    certn_1 = do_QueryElementAt(x509Certs, numCerts-1);

    nsXPIDLString cert0SubjectName;
    nsXPIDLString cert1IssuerName;
    nsXPIDLString certn_2IssuerName;
    nsXPIDLString certn_1SubjectName;

    cert0->GetSubjectName(cert0SubjectName);
    cert1->GetIssuerName(cert1IssuerName);
    certn_2->GetIssuerName(certn_2IssuerName);
    certn_1->GetSubjectName(certn_1SubjectName);

    if (cert1IssuerName.Equals(cert0SubjectName)) {
      // In this case, the first cert in the list signed the second,
      // so the first cert is the root.  Let's display it. 
      selCertIndex = 0;
      certToShow = cert0;
    } else 
    if (certn_2IssuerName.Equals(certn_1SubjectName)) { 
      // In this case the last cert has signed the second to last cert.
      // The last cert is the root, so let's display it.
      selCertIndex = numCerts-1;
      certToShow = certn_1;
    } else {
      // It's not a chain, so let's just show the first one in the 
      // downloaded list.
      selCertIndex = 0;
      certToShow = cert0;
    }
  }

  if (!certToShow)
    return NS_ERROR_FAILURE;

  nsCOMPtr<nsICertificateDialogs> dialogs;
  nsresult rv = ::getNSSDialogs(getter_AddRefs(dialogs), 
                                NS_GET_IID(nsICertificateDialogs),
                                NS_CERTIFICATEDIALOGS_CONTRACTID);
                       
  if (NS_FAILED(rv))
    return rv;
 
  SECItem der;
  rv=certToShow->GetRawDER(&der.len, (PRUint8 **)&der.data);

  if (NS_FAILED(rv))
    return rv;

  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Creating temp cert\n"));
  CERTCertificate *tmpCert;
  CERTCertDBHandle *certdb = CERT_GetDefaultCertDB();
  tmpCert = CERT_FindCertByDERCert(certdb, &der);
  if (!tmpCert) {
    tmpCert = CERT_NewTempCertificate(certdb, &der,
                                      nsnull, PR_FALSE, PR_TRUE);
  }
  nsMemory::Free(der.data);
  der.data = nsnull;
  der.len = 0;
  
  if (!tmpCert) {
    NS_ERROR("Couldn't create cert from DER blob\n");
    return NS_ERROR_FAILURE;
  }

  CERTCertificateCleaner tmpCertCleaner(tmpCert);

  if (!CERT_IsCACert(tmpCert, NULL)) {
    DisplayCertificateAlert(ctx, "NotACACert", certToShow);
    return NS_ERROR_FAILURE;
  }

  if (tmpCert->isperm) {
    DisplayCertificateAlert(ctx, "CaCertExists", certToShow);
    return NS_ERROR_FAILURE;
  }

  PRUint32 trustBits;
  PRBool allows;
  rv = dialogs->ConfirmDownloadCACert(ctx, certToShow, &trustBits, &allows);
  if (NS_FAILED(rv))
    return rv;

  if (!allows)
    return NS_ERROR_NOT_AVAILABLE;

  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("trust is %d\n", trustBits));
  nsXPIDLCString nickname;
  nickname.Adopt(CERT_MakeCANickname(tmpCert));

  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Created nick \"%s\"\n", nickname.get()));

  nsNSSCertTrust trust;
  trust.SetValidCA();
  trust.AddCATrust(!!(trustBits & nsIX509CertDB::TRUSTED_SSL),
                   !!(trustBits & nsIX509CertDB::TRUSTED_EMAIL),
                   !!(trustBits & nsIX509CertDB::TRUSTED_OBJSIGN));

  SECStatus srv = CERT_AddTempCertToPerm(tmpCert, 
                                         const_cast<char*>(nickname.get()), 
                                         trust.GetTrust()); 

  if (srv != SECSuccess)
    return NS_ERROR_FAILURE;

  // Import additional delivered certificates that can be verified.

  // build a CertList for filtering
  CERTCertList *certList = CERT_NewCertList();
  if (certList == NULL) {
    return NS_ERROR_FAILURE;
  }

  CERTCertListCleaner listCleaner(certList);

  // get all remaining certs into temp store

  for (PRUint32 i=0; i<numCerts; i++) {
    if (i == selCertIndex) {
      // we already processed that one
      continue;
    }

    certToShow = do_QueryElementAt(x509Certs, i);
    certToShow->GetRawDER(&der.len, (PRUint8 **)&der.data);

    CERTCertificate *tmpCert2 = 
      CERT_NewTempCertificate(certdb, &der, nsnull, PR_FALSE, PR_TRUE);

    nsMemory::Free(der.data);
    der.data = nsnull;
    der.len = 0;

    if (!tmpCert2) {
      NS_ASSERTION(0, "Couldn't create temp cert from DER blob\n");
      continue;  // Let's try to import the rest of 'em
    }
    
    CERT_AddCertToListTail(certList, tmpCert2);
  }

  return ImportValidCACertsInList(certList, ctx);
}