Beispiel #1
0
/*
 * Create a DER-encoded OCSP request (for the certificate whose nickname
 * is "name") and dump it out.
 */
static SECStatus
create_request (FILE *out_file, CERTCertDBHandle *handle, CERTCertificate *cert,
		PRBool add_service_locator, PRBool add_acceptable_responses)
{
    CERTCertList *certs = NULL;
    CERTCertificate *myCert = NULL;
    CERTOCSPRequest *request = NULL;
    int64 now = PR_Now();
    SECItem *encoding = NULL;
    SECStatus rv = SECFailure;

    if (handle == NULL || cert == NULL)
	return rv;

    myCert = CERT_DupCertificate(cert);
    if (myCert == NULL)
        goto loser;

    /*
     * We need to create a list of one.
     */
    certs = CERT_NewCertList();
    if (certs == NULL)
	goto loser;

    if (CERT_AddCertToListTail (certs, myCert) != SECSuccess)
	goto loser;

    /*
     * Now that cert is included in the list, we need to be careful
     * that we do not try to destroy it twice.  This will prevent that.
     */
    myCert = NULL;

    request = CERT_CreateOCSPRequest (certs, now, add_service_locator, NULL);
    if (request == NULL)
	goto loser;

    if (add_acceptable_responses) {
	rv = CERT_AddOCSPAcceptableResponses(request,
					     SEC_OID_PKIX_OCSP_BASIC_RESPONSE);
	if (rv != SECSuccess)
	    goto loser;
    }

    encoding = CERT_EncodeOCSPRequest (NULL, request, NULL);
    if (encoding == NULL)
	goto loser;

    MAKE_FILE_BINARY(out_file);
    if (fwrite (encoding->data, encoding->len, 1, out_file) != 1)
	goto loser;

    rv = SECSuccess;

loser:
    if (encoding != NULL)
	SECITEM_FreeItem(encoding, PR_TRUE);
    if (request != NULL)
	CERT_DestroyOCSPRequest(request);
    if (certs != NULL)
	CERT_DestroyCertList (certs);
    if (myCert != NULL)
	CERT_DestroyCertificate(myCert);

    return rv;
}
Beispiel #2
0
/*
 * Create a DER-encoded OCSP request (for the certificate whose nickname is
 * "cert_name"), then get and dump a corresponding response.  The responder
 * location is either specified explicitly (as "responder_url") or found
 * via the AuthorityInfoAccess URL in the cert.
 */
static SECStatus
dump_response (FILE *out_file, CERTCertDBHandle *handle, CERTCertificate *cert,
	       const char *responder_url)
{
    CERTCertList *certs = NULL;
    CERTCertificate *myCert = NULL;
    char *loc = NULL;
    int64 now = PR_Now();
    SECItem *response = NULL;
    SECStatus rv = SECFailure;
    PRBool includeServiceLocator;

    if (handle == NULL || cert == NULL)
	return rv;

    myCert = CERT_DupCertificate(cert);
    if (myCert == NULL)
        goto loser;

    if (responder_url != NULL) {
	loc = (char *) responder_url;
	includeServiceLocator = PR_TRUE;
    } else {
	loc = CERT_GetOCSPAuthorityInfoAccessLocation (cert);
	if (loc == NULL)
	    goto loser;
	includeServiceLocator = PR_FALSE;
    }

    /*
     * We need to create a list of one.
     */
    certs = CERT_NewCertList();
    if (certs == NULL)
	goto loser;

    if (CERT_AddCertToListTail (certs, myCert) != SECSuccess)
	goto loser;

    /*
     * Now that cert is included in the list, we need to be careful
     * that we do not try to destroy it twice.  This will prevent that.
     */
    myCert = NULL;

    response = CERT_GetEncodedOCSPResponse (NULL, certs, loc, now,
					    includeServiceLocator,
					    NULL, NULL, NULL);
    if (response == NULL)
	goto loser;

    MAKE_FILE_BINARY(out_file);
    if (fwrite (response->data, response->len, 1, out_file) != 1)
	goto loser;

    rv = SECSuccess;

loser:
    if (response != NULL)
	SECITEM_FreeItem (response, PR_TRUE);
    if (certs != NULL)
	CERT_DestroyCertList (certs);
    if (myCert != NULL)
	CERT_DestroyCertificate(myCert);
    if (loc != NULL && loc != responder_url)
	PORT_Free (loc);

    return rv;
}
Beispiel #3
0
SECStatus
NSS_CMSSignedData_ImportCerts(NSSCMSSignedData *sigd, CERTCertDBHandle *certdb,
				SECCertUsage certusage, PRBool keepcerts)
{
    int certcount;
    CERTCertificate **certArray = NULL;
    CERTCertList *certList = NULL;
    CERTCertListNode *node;
    SECStatus rv;
    SECItem **rawArray;
    int i;
    PRTime now;

    if (!sigd) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }

    certcount = NSS_CMSArray_Count((void **)sigd->rawCerts);

    /* get the certs in the temp DB */
    rv = CERT_ImportCerts(certdb, certusage, certcount, sigd->rawCerts, 
			 &certArray, PR_FALSE, PR_FALSE, NULL);
    if (rv != SECSuccess) {
	goto loser;
    }

    /* save the certs so they don't get destroyed */
    for (i=0; i < certcount; i++) {
	CERTCertificate *cert = certArray[i];
	if (cert)
            NSS_CMSSignedData_AddTempCertificate(sigd, cert);
    }

    if (!keepcerts) {
	goto done;
    }

    /* build a CertList for filtering */
    certList = CERT_NewCertList();
    if (certList == NULL) {
	rv = SECFailure;
	goto loser;
    }
    for (i=0; i < certcount; i++) {
	CERTCertificate *cert = certArray[i];
	if (cert)
	    cert = CERT_DupCertificate(cert);
	if (cert)
	    CERT_AddCertToListTail(certList,cert);
    }

    /* filter out the certs we don't want */
    rv = CERT_FilterCertListByUsage(certList,certusage, PR_FALSE);
    if (rv != SECSuccess) {
	goto loser;
    }

    /* go down the remaining list of certs and verify that they have
     * valid chains, then import them.
     */
    now = PR_Now();
    for (node = CERT_LIST_HEAD(certList) ; !CERT_LIST_END(node,certList);
						node= CERT_LIST_NEXT(node)) {
	CERTCertificateList *certChain;

	if (CERT_VerifyCert(certdb, node->cert, 
		PR_TRUE, certusage, now, NULL, NULL) != SECSuccess) {
	    continue;
	}

	certChain = CERT_CertChainFromCert(node->cert, certusage, PR_FALSE);
	if (!certChain) {
	    continue;
	}

	/*
	 * CertChain returns an array of SECItems, import expects an array of
	 * SECItem pointers. Create the SECItem Pointers from the array of
	 * SECItems.
 	 */
	rawArray = (SECItem **)PORT_Alloc(certChain->len*sizeof (SECItem *));
	if (!rawArray) {
	    CERT_DestroyCertificateList(certChain);
	    continue;
	}
	for (i=0; i < certChain->len; i++) {
	    rawArray[i] = &certChain->certs[i];
	}
	(void )CERT_ImportCerts(certdb, certusage, certChain->len, 
			rawArray,  NULL, keepcerts, PR_FALSE, NULL);
	PORT_Free(rawArray);
	CERT_DestroyCertificateList(certChain);
    }

    rv = SECSuccess;

    /* XXX CRL handling */

done:
    if (sigd->signerInfos != NULL) {
	/* fill in all signerinfo's certs */
	for (i = 0; sigd->signerInfos[i] != NULL; i++)
	    (void)NSS_CMSSignerInfo_GetSigningCertificate(
						sigd->signerInfos[i], certdb);
    }

loser:
    /* now free everything */
    if (certArray) {
	CERT_DestroyCertArray(certArray,certcount);
    }
    if (certList) {
	CERT_DestroyCertList(certList);
    }

    return rv;
}
Beispiel #4
0
SECStatus
CertVerifier::VerifyCert(CERTCertificate * cert,
                         const SECCertificateUsage usage,
                         const PRTime time,
                         nsIInterfaceRequestor * pinArg,
                         const Flags flags,
                         /*optional out*/ CERTCertList **validationChain,
                         /*optional out*/ SECOidTag *evOidPolicy,
                         /*optional out*/ CERTVerifyLog *verifyLog)
{
  if (!cert) {
    PORT_SetError(SEC_ERROR_INVALID_ARGS);
    return SECFailure;
  }
  if (validationChain) {
    *validationChain = nullptr;
  }
  if (evOidPolicy) {
    *evOidPolicy = SEC_OID_UNKNOWN;
  }

  switch(usage){
    case certificateUsageSSLClient:
    case certificateUsageSSLServer:
    case certificateUsageSSLCA:
    case certificateUsageEmailSigner:
    case certificateUsageEmailRecipient:
    case certificateUsageObjectSigner:
    case certificateUsageStatusResponder:
      break;
    default:
      NS_WARNING("Calling VerifyCert with invalid usage");
      PORT_SetError(SEC_ERROR_INVALID_ARGS);
      return SECFailure;
  }

  ScopedCERTCertList trustAnchors;
  SECStatus rv;
  SECOidTag evPolicy = SEC_OID_UNKNOWN;

#ifdef NSS_NO_LIBPKIX
  return ClassicVerifyCert(cert, usage, time, pinArg, validationChain,
                           verifyLog);
#else
  // Do EV checking only for sslserver usage
  if (usage == certificateUsageSSLServer) {
    SECStatus srv = getFirstEVPolicy(cert, evPolicy);
    if (srv == SECSuccess) {
      if (evPolicy != SEC_OID_UNKNOWN) {
        trustAnchors = getRootsForOid(evPolicy);
      }
      if (!trustAnchors) {
        return SECFailure;
      }
      // pkix ignores an empty trustanchors list and
      // decides then to use the whole set of trust in the DB
      // so we set the evPolicy to unkown in this case
      if (CERT_LIST_EMPTY(trustAnchors)) {
        evPolicy = SEC_OID_UNKNOWN;
      }
    } else {
      // Do not setup EV verification params
      evPolicy = SEC_OID_UNKNOWN;
    }
  }
  
  MOZ_ASSERT_IF(evPolicy != SEC_OID_UNKNOWN, trustAnchors);

  size_t i = 0;
  size_t validationChainLocation = 0;
  size_t validationTrustAnchorLocation = 0;
  CERTValOutParam cvout[4];
  if (verifyLog) {
     cvout[i].type = cert_po_errorLog;
     cvout[i].value.pointer.log = verifyLog;
     ++i;
  }
  if (validationChain) {
    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("VerifyCert: setting up validation chain outparam.\n"));
    validationChainLocation = i;
    cvout[i].type = cert_po_certList;
    cvout[i].value.pointer.cert = nullptr;
    ++i;
    validationTrustAnchorLocation = i;
    cvout[i].type = cert_po_trustAnchor;
    cvout[i].value.pointer.chain = nullptr;
    ++i;
  }
  cvout[i].type = cert_po_end;

  CERTRevocationFlags rev;

  CERTRevocationMethodIndex revPreferredMethods[2];
  rev.leafTests.preferred_methods =
  rev.chainTests.preferred_methods = revPreferredMethods;

  uint64_t revFlagsPerMethod[2];
  rev.leafTests.cert_rev_flags_per_method =
  rev.chainTests.cert_rev_flags_per_method = revFlagsPerMethod;
  rev.leafTests.number_of_preferred_methods =
  rev.chainTests.number_of_preferred_methods = 1;

  rev.leafTests.number_of_defined_methods =
  rev.chainTests.number_of_defined_methods = cert_revocation_method_ocsp + 1;

  const bool localOnly = flags & FLAG_LOCAL_ONLY;
  CERTValInParam cvin[6];

  // Parameters for both EV and DV validation
  cvin[0].type = cert_pi_useAIACertFetch;
  cvin[0].value.scalar.b = mMissingCertDownloadEnabled && !localOnly;
  cvin[1].type = cert_pi_revocationFlags;
  cvin[1].value.pointer.revocation = &rev;
  cvin[2].type = cert_pi_date;
  cvin[2].value.scalar.time = time;
  i = 3;
  const size_t evParamLocation = i;

  if (evPolicy != SEC_OID_UNKNOWN) {
    // EV setup!
    // XXX 859872 The current flags are not quite correct. (use
    // of ocsp flags for crl preferences).
    uint64_t ocspRevMethodFlags =
      CERT_REV_M_TEST_USING_THIS_METHOD
      | ((mOCSPDownloadEnabled && !localOnly) ?
          CERT_REV_M_ALLOW_NETWORK_FETCHING : CERT_REV_M_FORBID_NETWORK_FETCHING)
      | CERT_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE
      | CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE
      | CERT_REV_M_IGNORE_MISSING_FRESH_INFO
      | CERT_REV_M_STOP_TESTING_ON_FRESH_INFO
      | (mOCSPGETEnabled ? 0 : CERT_REV_M_FORCE_POST_METHOD_FOR_OCSP);

    rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_crl] =
    rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_crl]
      = CERT_REV_M_DO_NOT_TEST_USING_THIS_METHOD;

    rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] =
    rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_ocsp]
      = ocspRevMethodFlags;

    rev.leafTests.cert_rev_method_independent_flags =
    rev.chainTests.cert_rev_method_independent_flags =
      // avoiding the network is good, let's try local first
      CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST
      // is overall revocation requirement strict or relaxed?
      |  CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE
      ;

    rev.leafTests.preferred_methods[0] =
    rev.chainTests.preferred_methods[0] = cert_revocation_method_ocsp;

    cvin[i].type = cert_pi_policyOID;
    cvin[i].value.arraySize = 1;
    cvin[i].value.array.oids = &evPolicy;
    ++i;
    MOZ_ASSERT(trustAnchors);
    cvin[i].type = cert_pi_trustAnchors;
    cvin[i].value.pointer.chain = trustAnchors;
    ++i;

    cvin[i].type = cert_pi_end;

    rv = CERT_PKIXVerifyCert(cert, usage, cvin, cvout, pinArg);
    if (rv == SECSuccess) {
      if (evOidPolicy) {
        *evOidPolicy = evPolicy;
      }
      PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
             ("VerifyCert: successful CERT_PKIXVerifyCert(ev) \n"));
      goto pkix_done;
    }
    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
           ("VerifyCert: failed CERT_PKIXVerifyCert(ev)\n"));

    if (validationChain && *validationChain) {
      // There SHOULD not be a validation chain on failure, asserion here for
      // the debug builds AND a fallback for production builds
      MOZ_ASSERT(false,
                 "certPKIXVerifyCert returned failure AND a validationChain");
      CERT_DestroyCertList(*validationChain);
      *validationChain = nullptr;
    }

    if (verifyLog) {
      // Cleanup the log so that it is ready the the next validation
      CERTVerifyLogNode *i_node;
      for (i_node = verifyLog->head; i_node; i_node = i_node->next) {
         //destroy cert if any.
         if (i_node->cert) {
           CERT_DestroyCertificate(i_node->cert);
         }
         // No need to cleanup the actual nodes in the arena.
      }
      verifyLog->count = 0;
      verifyLog->head = nullptr;
      verifyLog->tail = nullptr;
    }

  }

  if (!nsNSSComponent::globalConstFlagUsePKIXVerification){
    // XXX: we do not care about the localOnly flag (currently) as the
    // caller that wants localOnly should disable and reenable the fetching.
    return ClassicVerifyCert(cert, usage, time, pinArg, validationChain,
                             verifyLog);
  }

  // The current flags check the chain the same way as the leafs
  rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_crl] =
  rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_crl] =
    // implicit default source - makes no sense for CRLs
    CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE

    // let's not stop on fresh CRL. If OCSP is enabled, too, let's check it
    | CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO

    // no fresh CRL? well, let other flag decide whether to fail or not
    | CERT_REV_M_IGNORE_MISSING_FRESH_INFO

    // testing using local CRLs is always allowed
    | CERT_REV_M_TEST_USING_THIS_METHOD

    // no local crl and don't know where to get it from? ignore
    | CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE

    // crl download based on parameter
    | ((mCRLDownloadEnabled && !localOnly) ?
        CERT_REV_M_ALLOW_NETWORK_FETCHING : CERT_REV_M_FORBID_NETWORK_FETCHING)
    ;

  rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] =
  rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] =
    // use OCSP
      CERT_REV_M_TEST_USING_THIS_METHOD

    // if app has a default OCSP responder configured, let's use it
    | CERT_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE

    // of course OCSP doesn't work without a source. let's accept such certs
    | CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE

    // if ocsp is required stop on lack of freshness
    | (mOCSPStrict ?
       CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO : CERT_REV_M_IGNORE_MISSING_FRESH_INFO)

    // ocsp success is sufficient
    | CERT_REV_M_STOP_TESTING_ON_FRESH_INFO

    // ocsp enabled controls network fetching, too
    | ((mOCSPDownloadEnabled && !localOnly) ?
        CERT_REV_M_ALLOW_NETWORK_FETCHING : CERT_REV_M_FORBID_NETWORK_FETCHING)
    
    | (mOCSPGETEnabled ? 0 : CERT_REV_M_FORCE_POST_METHOD_FOR_OCSP);
    ;

  rev.leafTests.preferred_methods[0] =
  rev.chainTests.preferred_methods[0] = cert_revocation_method_ocsp;

  rev.leafTests.cert_rev_method_independent_flags =
  rev.chainTests.cert_rev_method_independent_flags =
    // avoiding the network is good, let's try local first
    CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST

    // is overall revocation requirement strict or relaxed?
    | (mRequireRevocationInfo ?
       CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE : CERT_REV_MI_NO_OVERALL_INFO_REQUIREMENT)
    ;

  // Skip EV parameters
  cvin[evParamLocation].type = cert_pi_end;

  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("VerifyCert: calling CERT_PKIXVerifyCert(dv) \n"));
  rv = CERT_PKIXVerifyCert(cert, usage, cvin, cvout, pinArg);

pkix_done:
  if (validationChain) {
    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("VerifyCert: validation chain requested\n"));
    ScopedCERTCertificate trustAnchor(cvout[validationTrustAnchorLocation].value.pointer.cert);

    if (rv == SECSuccess) {
      if (! cvout[validationChainLocation].value.pointer.chain) {
        PR_SetError(PR_UNKNOWN_ERROR, 0);
        return SECFailure;
      }
      PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("VerifyCert: I have a chain\n"));
      *validationChain = cvout[validationChainLocation].value.pointer.chain;
      if (trustAnchor) {
        // we should only add the issuer to the chain if it is not already
        // present. On CA cert checking, the issuer is the same cert, so in
        // that case we do not add the cert to the chain.
        if (!CERT_CompareCerts(trustAnchor, cert)) {
          PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("VerifyCert:  adding issuer to tail for display\n"));
          // note: rv is reused to catch errors on cert creation!
          ScopedCERTCertificate tempCert(CERT_DupCertificate(trustAnchor));
          rv = CERT_AddCertToListTail(*validationChain, tempCert);
          if (rv == SECSuccess) {
            tempCert.forget(); // ownership traferred to validationChain
          } else {
            CERT_DestroyCertList(*validationChain);
            *validationChain = nullptr;
          }
        }
      }
    } else {
      // Validation was a fail, clean up if needed
      if (cvout[validationChainLocation].value.pointer.chain) {
        CERT_DestroyCertList(cvout[validationChainLocation].value.pointer.chain);
      }
    }
  }

  return rv;
#endif
}
nsresult
VerifyCMSDetachedSignatureIncludingCertificate(
    const SECItem& buffer, const SECItem& detachedDigest,
    nsresult (*verifyCertificate)(CERTCertificate* cert, void* context,
                                  void* pinArg),
    void *verifyCertificateContext, void* pinArg)
{
    // XXX: missing pinArg is tolerated.
    if (NS_WARN_IF(!buffer.data && buffer.len > 0) ||
            NS_WARN_IF(!detachedDigest.data && detachedDigest.len > 0) ||
            (!verifyCertificate) ||
            NS_WARN_IF(!verifyCertificateContext)) {
        return NS_ERROR_INVALID_ARG;
    }

    ScopedNSSCMSMessage
    cmsMsg(NSS_CMSMessage_CreateFromDER(const_cast<SECItem*>(&buffer), nullptr,
                                        nullptr, nullptr, nullptr, nullptr,
                                        nullptr));
    if (!cmsMsg) {
        return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
    }

    if (!NSS_CMSMessage_IsSigned(cmsMsg.get())) {
        return NS_ERROR_CMS_VERIFY_NOT_SIGNED;
    }

    NSSCMSContentInfo* cinfo = NSS_CMSMessage_ContentLevel(cmsMsg.get(), 0);
    if (!cinfo) {
        return NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO;
    }

    // signedData is non-owning
    NSSCMSSignedData* signedData =
        reinterpret_cast<NSSCMSSignedData*>(NSS_CMSContentInfo_GetContent(cinfo));
    if (!signedData) {
        return NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO;
    }

    // Set digest value.
    if (NSS_CMSSignedData_SetDigestValue(signedData, SEC_OID_SHA1,
                                         const_cast<SECItem*>(&detachedDigest))) {
        return NS_ERROR_CMS_VERIFY_BAD_DIGEST;
    }

    // Parse the certificates into CERTCertificate objects held in memory so
    // verifyCertificate will be able to find them during path building.
    ScopedCERTCertList certs(CERT_NewCertList());
    if (!certs) {
        return NS_ERROR_OUT_OF_MEMORY;
    }
    if (signedData->rawCerts) {
        for (size_t i = 0; signedData->rawCerts[i]; ++i) {
            ScopedCERTCertificate
            cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
                                         signedData->rawCerts[i], nullptr, false,
                                         true));
            // Skip certificates that fail to parse
            if (cert) {
                if (CERT_AddCertToListTail(certs.get(), cert.get()) == SECSuccess) {
                    cert.forget(); // ownership transfered
                } else {
                    return NS_ERROR_OUT_OF_MEMORY;
                }
            }
        }
    }

    // Get the end-entity certificate.
    int numSigners = NSS_CMSSignedData_SignerInfoCount(signedData);
    if (NS_WARN_IF(numSigners != 1)) {
        return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
    }
    // signer is non-owning.
    NSSCMSSignerInfo* signer = NSS_CMSSignedData_GetSignerInfo(signedData, 0);
    if (NS_WARN_IF(!signer)) {
        return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
    }
    CERTCertificate* signerCert =
        NSS_CMSSignerInfo_GetSigningCertificate(signer, CERT_GetDefaultCertDB());
    if (!signerCert) {
        return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
    }

    nsresult rv = verifyCertificate(signerCert, verifyCertificateContext, pinArg);
    if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
    }

    // See NSS_CMSContentInfo_GetContentTypeOID, which isn't exported from NSS.
    SECOidData* contentTypeOidData =
        SECOID_FindOID(&signedData->contentInfo.contentType);
    if (!contentTypeOidData) {
        return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
    }

    return MapSECStatus(NSS_CMSSignerInfo_Verify(signer,
                        const_cast<SECItem*>(&detachedDigest),
                        &contentTypeOidData->oid));
}
/*
 *  [noscript] void importEmailCertificates(in charPtr data, in unsigned long length,
 *                                     in nsIInterfaceRequestor ctx);
 */
NS_IMETHODIMP
nsNSSCertificateDB::ImportEmailCertificate(PRUint8 * data, PRUint32 length, 
                                       nsIInterfaceRequestor *ctx)

{
  nsNSSShutDownPreventionLock locker;
  SECStatus srv = SECFailure;
  nsresult nsrv = NS_OK;
  CERTCertDBHandle *certdb;
  CERTCertificate **certArray = NULL;
  CERTCertList *certList = NULL;
  CERTCertListNode *node;
  PRTime now;
  SECCertUsage certusage;
  SECItem **rawArray;
  int numcerts;
  int i;
 
  PRArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  if (!arena)
    return NS_ERROR_OUT_OF_MEMORY;

  CERTDERCerts *certCollection = getCertsFromPackage(arena, data, length);
  if (!certCollection) {
    PORT_FreeArena(arena, PR_FALSE);
    return NS_ERROR_FAILURE;
  }

  certdb = CERT_GetDefaultCertDB();
  certusage = certUsageEmailRecipient;

  numcerts = certCollection->numcerts;

  rawArray = (SECItem **) PORT_Alloc(sizeof(SECItem *) * numcerts);
  if ( !rawArray ) {
    nsrv = NS_ERROR_FAILURE;
    goto loser;
  }

  for (i=0; i < numcerts; i++) {
    rawArray[i] = &certCollection->rawCerts[i];
  }

  srv = CERT_ImportCerts(certdb, certusage, numcerts, rawArray, 
                         &certArray, PR_FALSE, PR_FALSE, NULL);

  PORT_Free(rawArray);
  rawArray = NULL;

  if (srv != SECSuccess) {
    nsrv = NS_ERROR_FAILURE;
    goto loser;
  }

  // build a CertList for filtering
  certList = CERT_NewCertList();
  if (certList == NULL) {
    nsrv = NS_ERROR_FAILURE;
    goto loser;
  }
  for (i=0; i < numcerts; i++) {
    CERTCertificate *cert = certArray[i];
    if (cert)
      cert = CERT_DupCertificate(cert);
    if (cert)
      CERT_AddCertToListTail(certList, cert);
  }

  /* go down the remaining list of certs and verify that they have
   * valid chains, then import them.
   */
  now = PR_Now();
  for (node = CERT_LIST_HEAD(certList);
       !CERT_LIST_END(node,certList);
       node = CERT_LIST_NEXT(node)) {

    bool alert_and_skip = false;

    if (!node->cert) {
      continue;
    }

    if (CERT_VerifyCert(certdb, node->cert, 
        PR_TRUE, certusage, now, ctx, NULL) != SECSuccess) {
      alert_and_skip = true;
    }

    CERTCertificateList *certChain = nsnull;
    CERTCertificateListCleaner chainCleaner(certChain);

    if (!alert_and_skip) {
      certChain = CERT_CertChainFromCert(node->cert, certusage, PR_FALSE);
      if (!certChain) {
        alert_and_skip = true;
      }
    }

    if (alert_and_skip) {    
      nsCOMPtr<nsIX509Cert> certToShow = new nsNSSCertificate(node->cert);
      DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow);
      continue;
    }

    /*
     * CertChain returns an array of SECItems, import expects an array of
     * SECItem pointers. Create the SECItem Pointers from the array of
     * SECItems.
     */
    rawArray = (SECItem **) PORT_Alloc(certChain->len * sizeof(SECItem *));
    if (!rawArray) {
      continue;
    }
    for (i=0; i < certChain->len; i++) {
      rawArray[i] = &certChain->certs[i];
    }
    CERT_ImportCerts(certdb, certusage, certChain->len, 
                            rawArray,  NULL, PR_TRUE, PR_FALSE, NULL);

    CERT_SaveSMimeProfile(node->cert, NULL, NULL);

    PORT_Free(rawArray);
  }

loser:
  if (certArray) {
    CERT_DestroyCertArray(certArray, numcerts);
  }
  if (certList) {
    CERT_DestroyCertList(certList);
  }
  if (arena) 
    PORT_FreeArena(arena, PR_TRUE);
  return nsrv;
}
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");
    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_ERROR("Couldn't create temp cert from DER blob");
      continue;  // Let's try to import the rest of 'em
    }
    
    CERT_AddCertToListTail(certList, tmpCert2);
  }

  return ImportValidCACertsInList(certList, ctx);
}
Beispiel #8
0
SECStatus
CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
                         Time time, void* pinArg, const char* hostname,
                         const Flags flags,
            /*optional*/ const SECItem* stapledOCSPResponseSECItem,
        /*optional out*/ ScopedCERTCertList* builtChain,
        /*optional out*/ SECOidTag* evOidPolicy)
{
  PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Top of VerifyCert\n"));

  PR_ASSERT(cert);
  PR_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV));

  if (builtChain) {
    *builtChain = nullptr;
  }
  if (evOidPolicy) {
    *evOidPolicy = SEC_OID_UNKNOWN;
  }

  if (!cert ||
      (usage != certificateUsageSSLServer && (flags & FLAG_MUST_BE_EV))) {
    PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
    return SECFailure;
  }

  Result rv;

  Input certDER;
  rv = certDER.Init(cert->derCert.data, cert->derCert.len);
  if (rv != Success) {
    PR_SetError(MapResultToPRErrorCode(rv), 0);
    return SECFailure;
  }

  ChainValidationCallbackState callbackState = {
    hostname, mPinningEnforcementLevel, usage, time
  };
  CERTChainVerifyCallback callbackContainer;
  callbackContainer.isChainValid = chainValidationCallback;
  callbackContainer.isChainValidArg = &callbackState;

  NSSCertDBTrustDomain::OCSPFetching ocspFetching
    = !mOCSPDownloadEnabled ||
      (flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::NeverFetchOCSP
    : !mOCSPStrict              ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail
                                : NSSCertDBTrustDomain::FetchOCSPForDVHardFail;

  ocsp_get_config ocspGETConfig = mOCSPGETEnabled ? ocsp_get_enabled
                                                  : ocsp_get_disabled;

  Input stapledOCSPResponseInput;
  const Input* stapledOCSPResponse = nullptr;
  if (stapledOCSPResponseSECItem) {
    rv = stapledOCSPResponseInput.Init(stapledOCSPResponseSECItem->data,
                                       stapledOCSPResponseSECItem->len);
    if (rv != Success) {
      // The stapled OCSP response was too big.
      PR_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE, 0);
      return SECFailure;
    }
    stapledOCSPResponse = &stapledOCSPResponseInput;
  }

  switch (usage) {
    case certificateUsageSSLClient: {
      // XXX: We don't really have a trust bit for SSL client authentication so
      // just use trustEmail as it is the closest alternative.
      NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
                                       pinArg, ocspGETConfig, nullptr,
                                       builtChain);
      rv = BuildCertChain(trustDomain, certDER, time,
                          EndEntityOrCA::MustBeEndEntity,
                          KeyUsage::digitalSignature,
                          KeyPurposeId::id_kp_clientAuth,
                          CertPolicyId::anyPolicy, stapledOCSPResponse);
      break;
    }

    case certificateUsageSSLServer: {
      // TODO: When verifying a certificate in an SSL handshake, we should
      // restrict the acceptable key usage based on the key exchange method
      // chosen by the server.

#ifndef MOZ_NO_EV_CERTS
      // Try to validate for EV first.
      CertPolicyId evPolicy;
      SECOidTag evPolicyOidTag;
      SECStatus srv = GetFirstEVPolicy(cert, evPolicy, evPolicyOidTag);
      if (srv == SECSuccess) {
        NSSCertDBTrustDomain
          trustDomain(trustSSL,
                      ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP
                        ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
                        : NSSCertDBTrustDomain::FetchOCSPForEV,
                      mOCSPCache, pinArg, ocspGETConfig,
                      &callbackContainer, builtChain);
        rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time,
                                          KeyUsage::digitalSignature,// (EC)DHE
                                          KeyUsage::keyEncipherment, // RSA
                                          KeyUsage::keyAgreement,    // (EC)DH
                                          KeyPurposeId::id_kp_serverAuth,
                                          evPolicy, stapledOCSPResponse);
        if (rv == Success) {
          if (evOidPolicy) {
            *evOidPolicy = evPolicyOidTag;
          }
          break;
        }
      }
#endif

      if (flags & FLAG_MUST_BE_EV) {
        rv = Result::ERROR_POLICY_VALIDATION_FAILED;
        break;
      }

      // Now try non-EV.
      NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
                                       pinArg, ocspGETConfig, &callbackContainer,
                                       builtChain);
      rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time,
                                        KeyUsage::digitalSignature, // (EC)DHE
                                        KeyUsage::keyEncipherment, // RSA
                                        KeyUsage::keyAgreement, // (EC)DH
                                        KeyPurposeId::id_kp_serverAuth,
                                        CertPolicyId::anyPolicy,
                                        stapledOCSPResponse);
      break;
    }

    case certificateUsageSSLCA: {
      NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
                                       pinArg, ocspGETConfig, nullptr,
                                       builtChain);
      rv = BuildCertChain(trustDomain, certDER, time,
                          EndEntityOrCA::MustBeCA, KeyUsage::keyCertSign,
                          KeyPurposeId::id_kp_serverAuth,
                          CertPolicyId::anyPolicy, stapledOCSPResponse);
      break;
    }

    case certificateUsageEmailSigner: {
      NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
                                       pinArg, ocspGETConfig, nullptr,
                                       builtChain);
      rv = BuildCertChain(trustDomain, certDER, time,
                          EndEntityOrCA::MustBeEndEntity,
                          KeyUsage::digitalSignature,
                          KeyPurposeId::id_kp_emailProtection,
                          CertPolicyId::anyPolicy, stapledOCSPResponse);
      break;
    }

    case certificateUsageEmailRecipient: {
      // TODO: The higher level S/MIME processing should pass in which key
      // usage it is trying to verify for, and base its algorithm choices
      // based on the result of the verification(s).
      NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
                                       pinArg, ocspGETConfig, nullptr,
                                       builtChain);
      rv = BuildCertChain(trustDomain, certDER, time,
                          EndEntityOrCA::MustBeEndEntity,
                          KeyUsage::keyEncipherment, // RSA
                          KeyPurposeId::id_kp_emailProtection,
                          CertPolicyId::anyPolicy, stapledOCSPResponse);
      if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
        rv = BuildCertChain(trustDomain, certDER, time,
                            EndEntityOrCA::MustBeEndEntity,
                            KeyUsage::keyAgreement, // ECDH/DH
                            KeyPurposeId::id_kp_emailProtection,
                            CertPolicyId::anyPolicy, stapledOCSPResponse);
      }
      break;
    }

    case certificateUsageObjectSigner: {
      NSSCertDBTrustDomain trustDomain(trustObjectSigning, ocspFetching,
                                       mOCSPCache, pinArg, ocspGETConfig,
                                       nullptr, builtChain);
      rv = BuildCertChain(trustDomain, certDER, time,
                          EndEntityOrCA::MustBeEndEntity,
                          KeyUsage::digitalSignature,
                          KeyPurposeId::id_kp_codeSigning,
                          CertPolicyId::anyPolicy, stapledOCSPResponse);
      break;
    }

    case certificateUsageVerifyCA:
    case certificateUsageStatusResponder: {
      // XXX This is a pretty useless way to verify a certificate. It is used
      // by the certificate viewer UI. Because we don't know what trust bit is
      // interesting, we just try them all.
      mozilla::pkix::EndEntityOrCA endEntityOrCA;
      mozilla::pkix::KeyUsage keyUsage;
      KeyPurposeId eku;
      if (usage == certificateUsageVerifyCA) {
        endEntityOrCA = EndEntityOrCA::MustBeCA;
        keyUsage = KeyUsage::keyCertSign;
        eku = KeyPurposeId::anyExtendedKeyUsage;
      } else {
        endEntityOrCA = EndEntityOrCA::MustBeEndEntity;
        keyUsage = KeyUsage::digitalSignature;
        eku = KeyPurposeId::id_kp_OCSPSigning;
      }

      NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache, pinArg,
                                    ocspGETConfig, nullptr, builtChain);
      rv = BuildCertChain(sslTrust, certDER, time, endEntityOrCA,
                          keyUsage, eku, CertPolicyId::anyPolicy,
                          stapledOCSPResponse);
      if (rv == Result::ERROR_UNKNOWN_ISSUER) {
        NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, mOCSPCache,
                                        pinArg, ocspGETConfig, nullptr,
                                        builtChain);
        rv = BuildCertChain(emailTrust, certDER, time, endEntityOrCA,
                            keyUsage, eku, CertPolicyId::anyPolicy,
                            stapledOCSPResponse);
        if (rv == Result::ERROR_UNKNOWN_ISSUER) {
          NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
                                                  ocspFetching, mOCSPCache,
                                                  pinArg, ocspGETConfig,
                                                  nullptr, builtChain);
          rv = BuildCertChain(objectSigningTrust, certDER, time,
                              endEntityOrCA, keyUsage, eku,
                              CertPolicyId::anyPolicy, stapledOCSPResponse);
        }
      }

      break;
    }

    default:
      rv = Result::FATAL_ERROR_INVALID_ARGS;
  }

  if (rv != Success) {
    if (rv != Result::ERROR_KEY_PINNING_FAILURE) {
      ScopedCERTCertificate certCopy(CERT_DupCertificate(cert));
      if (!certCopy) {
        return SECFailure;
      }
      ScopedCERTCertList certList(CERT_NewCertList());
      if (!certList) {
        return SECFailure;
      }
      SECStatus srv = CERT_AddCertToListTail(certList.get(), certCopy.get());
      if (srv != SECSuccess) {
        return SECFailure;
      }
      certCopy.forget(); // now owned by certList
      PRBool chainOK = false;
      srv = chainValidationCallback(&callbackState, certList, &chainOK);
      if (srv != SECSuccess) {
        return SECFailure;
      }
      if (!chainOK) {
        rv = Result::ERROR_KEY_PINNING_FAILURE;
      }
    }
    PR_SetError(MapResultToPRErrorCode(rv), 0);
    return SECFailure;
  }

  return SECSuccess;
}