security/certverifier/CertVerifier.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "CertVerifier.h"
     9 #include <stdint.h>
    11 #include "pkix/pkix.h"
    12 #include "ExtendedValidation.h"
    13 #include "NSSCertDBTrustDomain.h"
    14 #include "PublicKeyPinningService.h"
    15 #include "cert.h"
    16 #include "ocsp.h"
    17 #include "secerr.h"
    18 #include "pk11pub.h"
    19 #include "prerror.h"
    20 #include "sslerr.h"
    22 // ScopedXXX in this file are mozilla::pkix::ScopedXXX, not
    23 // mozilla::ScopedXXX.
    24 using namespace mozilla::pkix;
    25 using namespace mozilla::psm;
    27 #ifdef PR_LOGGING
    28 PRLogModuleInfo* gCertVerifierLog = nullptr;
    29 #endif
    31 namespace mozilla { namespace psm {
    33 const CertVerifier::Flags CertVerifier::FLAG_LOCAL_ONLY = 1;
    34 const CertVerifier::Flags CertVerifier::FLAG_MUST_BE_EV = 2;
    36 CertVerifier::CertVerifier(implementation_config ic,
    37 #ifndef NSS_NO_LIBPKIX
    38                            missing_cert_download_config mcdc,
    39                            crl_download_config cdc,
    40 #endif
    41                            ocsp_download_config odc,
    42                            ocsp_strict_config osc,
    43                            ocsp_get_config ogc,
    44                            pinning_enforcement_config pel)
    45   : mImplementation(ic)
    46 #ifndef NSS_NO_LIBPKIX
    47   , mMissingCertDownloadEnabled(mcdc == missing_cert_download_on)
    48   , mCRLDownloadEnabled(cdc == crl_download_allowed)
    49 #endif
    50   , mOCSPDownloadEnabled(odc == ocsp_on)
    51   , mOCSPStrict(osc == ocsp_strict)
    52   , mOCSPGETEnabled(ogc == ocsp_get_enabled)
    53   , mPinningEnforcementLevel(pel)
    54 {
    55 }
    57 CertVerifier::~CertVerifier()
    58 {
    59 }
    61 void
    62 InitCertVerifierLog()
    63 {
    64 #ifdef PR_LOGGING
    65   if (!gCertVerifierLog) {
    66     gCertVerifierLog = PR_NewLogModule("certverifier");
    67   }
    68 #endif
    69 }
    71 // Once we migrate to mozilla::pkix or change the overridable error
    72 // logic this will become unnecesary.
    73 static SECStatus
    74 insertErrorIntoVerifyLog(CERTCertificate* cert, const PRErrorCode err,
    75                          CERTVerifyLog* verifyLog){
    76   CERTVerifyLogNode* node;
    77   node = (CERTVerifyLogNode *)PORT_ArenaAlloc(verifyLog->arena,
    78                                               sizeof(CERTVerifyLogNode));
    79   if (!node) {
    80     PR_SetError(PR_UNKNOWN_ERROR, 0);
    81     return SECFailure;
    82   }
    83   node->cert = CERT_DupCertificate(cert);
    84   node->error = err;
    85   node->depth = 0;
    86   node->arg = nullptr;
    87   //and at to head!
    88   node->prev = nullptr;
    89   node->next = verifyLog->head;
    90   if (verifyLog->head) {
    91     verifyLog->head->prev = node;
    92   }
    93   verifyLog->head = node;
    94   if (!verifyLog->tail) {
    95     verifyLog->tail = node;
    96   }
    97   verifyLog->count++;
    99   return SECSuccess;
   100 }
   102 SECStatus
   103 IsCertBuiltInRoot(CERTCertificate* cert, bool& result) {
   104   result = false;
   105   ScopedPtr<PK11SlotList, PK11_FreeSlotList> slots;
   106   slots = PK11_GetAllSlotsForCert(cert, nullptr);
   107   if (!slots) {
   108     if (PORT_GetError() == SEC_ERROR_NO_TOKEN) {
   109       // no list
   110       return SECSuccess;
   111     }
   112     return SECFailure;
   113   }
   114   for (PK11SlotListElement* le = slots->head; le; le = le->next) {
   115     char* token = PK11_GetTokenName(le->slot);
   116     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   117            ("BuiltInRoot? subject=%s token=%s",cert->subjectName, token));
   118     if (strcmp("Builtin Object Token", token) == 0) {
   119       result = true;
   120       return SECSuccess;
   121     }
   122   }
   123   return SECSuccess;
   124 }
   126 struct ChainValidationCallbackState
   127 {
   128   const char* hostname;
   129   const CertVerifier::pinning_enforcement_config pinningEnforcementLevel;
   130   const SECCertificateUsage usage;
   131   const PRTime time;
   132 };
   134 SECStatus chainValidationCallback(void* state, const CERTCertList* certList,
   135                                   PRBool* chainOK)
   136 {
   137   ChainValidationCallbackState* callbackState =
   138     reinterpret_cast<ChainValidationCallbackState*>(state);
   140   *chainOK = PR_FALSE;
   142   PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   143          ("verifycert: Inside the Callback \n"));
   145   // On sanity failure we fail closed.
   146   if (!certList) {
   147     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   148            ("verifycert: Short circuit, callback, sanity check failed \n"));
   149     PR_SetError(PR_INVALID_STATE_ERROR, 0);
   150     return SECFailure;
   151   }
   152   if (!callbackState) {
   153     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   154            ("verifycert: Short circuit, callback, no state! \n"));
   155     PR_SetError(PR_INVALID_STATE_ERROR, 0);
   156     return SECFailure;
   157   }
   159   if (callbackState->usage != certificateUsageSSLServer ||
   160       callbackState->pinningEnforcementLevel == CertVerifier::pinningDisabled) {
   161     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   162            ("verifycert: Callback shortcut pel=%d \n",
   163             callbackState->pinningEnforcementLevel));
   164     *chainOK = PR_TRUE;
   165     return SECSuccess;
   166   }
   168   for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
   169        !CERT_LIST_END(node, certList);
   170        node = CERT_LIST_NEXT(node)) {
   171     CERTCertificate* currentCert = node->cert;
   172     if (CERT_LIST_END(CERT_LIST_NEXT(node), certList)) {
   173       bool isBuiltInRoot = false;
   174       SECStatus srv = IsCertBuiltInRoot(currentCert, isBuiltInRoot);
   175       if (srv != SECSuccess) {
   176         PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Is BuiltInRoot failure"));
   177         return srv;
   178       }
   179       // If desired, the user can enable "allow user CA MITM mode", in which
   180       // case key pinning is not enforced for certificates that chain to trust
   181       // anchors that are not in Mozilla's root program
   182       if (!isBuiltInRoot &&
   183           (callbackState->pinningEnforcementLevel ==
   184              CertVerifier::pinningAllowUserCAMITM)) {
   185         *chainOK = PR_TRUE;
   186         return SECSuccess;
   187       }
   188     }
   189   }
   191   const bool enforceTestMode = (callbackState->pinningEnforcementLevel ==
   192                                 CertVerifier::pinningEnforceTestMode);
   193   *chainOK = PublicKeyPinningService::
   194     ChainHasValidPins(certList, callbackState->hostname, callbackState->time,
   195                       enforceTestMode);
   197   return SECSuccess;
   198 }
   200 // This always returns secfailure but its objective is to replate
   201 // the PR_Error
   202 static void
   203 tryWorsenPRErrorInCallback(CERTCertificate* cert,
   204                            ChainValidationCallbackState* callbackState) {
   205   ScopedCERTCertificate certCopy(CERT_DupCertificate(cert));
   206   if (!certCopy) {
   207     return;
   208   }
   209   ScopedCERTCertList certList(CERT_NewCertList());
   210   if (!certList) {
   211     return;
   212   }
   213   SECStatus srv = CERT_AddCertToListTail(certList.get(), certCopy.get());
   214   if (srv != SECSuccess) {
   215     return;
   216   }
   217   certCopy.release(); // now owned by certList
   218   PRBool chainOK = false;
   219   srv = chainValidationCallback(&callbackState, certList.get(), &chainOK);
   220   if (srv != SECSuccess) {
   221     return;
   222   }
   223   if (!chainOK) {
   224     PR_SetError(SEC_ERROR_APPLICATION_CALLBACK_ERROR, 0); // same as libpkix
   225     return ;
   226   }
   227   return; // no change in PR_error
   228 }
   230 static SECStatus
   231 ClassicVerifyCert(CERTCertificate* cert,
   232                   const SECCertificateUsage usage,
   233                   const PRTime time,
   234                   void* pinArg,
   235                   ChainValidationCallbackState* callbackState,
   236                   /*optional out*/ ScopedCERTCertList* validationChain,
   237                   /*optional out*/ CERTVerifyLog* verifyLog)
   238 {
   239   SECStatus rv;
   240   SECCertUsage enumUsage;
   241   switch (usage) {
   242     case certificateUsageSSLClient:
   243       enumUsage = certUsageSSLClient;
   244       break;
   245     case certificateUsageSSLServer:
   246       enumUsage = certUsageSSLServer;
   247       break;
   248     case certificateUsageSSLCA:
   249       enumUsage = certUsageSSLCA;
   250       break;
   251     case certificateUsageEmailSigner:
   252       enumUsage = certUsageEmailSigner;
   253       break;
   254     case certificateUsageEmailRecipient:
   255       enumUsage = certUsageEmailRecipient;
   256       break;
   257     case certificateUsageObjectSigner:
   258       enumUsage = certUsageObjectSigner;
   259       break;
   260     case certificateUsageVerifyCA:
   261       enumUsage = certUsageVerifyCA;
   262       break;
   263     case certificateUsageStatusResponder:
   264       enumUsage = certUsageStatusResponder;
   265       break;
   266     default:
   267       PR_NOT_REACHED("unexpected usage");
   268       PORT_SetError(SEC_ERROR_INVALID_ARGS);
   269       return SECFailure;
   270   }
   271   if (usage == certificateUsageSSLServer) {
   272     // SSL server cert verification has always used CERT_VerifyCert, so we
   273     // continue to use it for SSL cert verification to minimize the risk of
   274     // there being any differnce in results between CERT_VerifyCert and
   275     // CERT_VerifyCertificate.
   276     rv = CERT_VerifyCert(CERT_GetDefaultCertDB(), cert, true,
   277                          certUsageSSLServer, time, pinArg, verifyLog);
   278   } else {
   279     rv = CERT_VerifyCertificate(CERT_GetDefaultCertDB(), cert, true,
   280                                 usage, time, pinArg, verifyLog, nullptr);
   281   }
   283   if (rv == SECSuccess &&
   284       (validationChain || usage == certificateUsageSSLServer)) {
   285     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   286            ("VerifyCert: getting chain in 'classic' \n"));
   287     ScopedCERTCertList certChain(CERT_GetCertChainFromCert(cert, time,
   288                                                            enumUsage));
   289     if (!certChain) {
   290       return SECFailure;
   291     }
   292     if (usage == certificateUsageSSLServer) {
   293       PRBool chainOK = PR_FALSE;
   294       SECStatus srv = chainValidationCallback(callbackState, certChain.get(),
   295                                               &chainOK);
   296       if (srv != SECSuccess) {
   297         return srv;
   298       }
   299       if (chainOK != PR_TRUE) {
   300         if (verifyLog) {
   301           insertErrorIntoVerifyLog(cert,
   302                                    SEC_ERROR_APPLICATION_CALLBACK_ERROR,
   303                                    verifyLog);
   304         }
   305         PR_SetError(SEC_ERROR_APPLICATION_CALLBACK_ERROR, 0); // same as libpkix
   306         return SECFailure;
   307       }
   308     }
   310     // If there is an error we may need to worsen to error to be a pinning failure
   311     if (rv != SECSuccess && usage == certificateUsageSSLServer) {
   312       tryWorsenPRErrorInCallback(cert, callbackState);
   313     }
   315     if (rv == SECSuccess && validationChain) {
   316       *validationChain = certChain.release();
   317     }
   318   }
   320   return rv;
   321 }
   323 #ifndef NSS_NO_LIBPKIX
   324 static void
   325 destroyCertListThatShouldNotExist(CERTCertList** certChain)
   326 {
   327   PR_ASSERT(certChain);
   328   PR_ASSERT(!*certChain);
   329   if (certChain && *certChain) {
   330     // There SHOULD not be a validation chain on failure, asserion here for
   331     // the debug builds AND a fallback for production builds
   332     CERT_DestroyCertList(*certChain);
   333     *certChain = nullptr;
   334   }
   335 }
   336 #endif
   338 static SECStatus
   339 BuildCertChainForOneKeyUsage(TrustDomain& trustDomain, CERTCertificate* cert,
   340                              PRTime time, KeyUsage ku1, KeyUsage ku2,
   341                              KeyUsage ku3, SECOidTag eku,
   342                              SECOidTag requiredPolicy,
   343                              const SECItem* stapledOCSPResponse,
   344                              ScopedCERTCertList& builtChain)
   345 {
   346   SECStatus rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
   347                                 ku1, eku, requiredPolicy, stapledOCSPResponse,
   348                                 builtChain);
   349   if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
   350     rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
   351                         ku2, eku, requiredPolicy, stapledOCSPResponse,
   352                         builtChain);
   353     if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
   354       rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
   355                           ku3, eku, requiredPolicy, stapledOCSPResponse,
   356                           builtChain);
   357       if (rv != SECSuccess) {
   358         PR_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE, 0);
   359       }
   360     }
   361   }
   362   return rv;
   363 }
   365 SECStatus
   366 CertVerifier::MozillaPKIXVerifyCert(
   367                    CERTCertificate* cert,
   368                    const SECCertificateUsage usage,
   369                    const PRTime time,
   370                    void* pinArg,
   371                    const Flags flags,
   372                    ChainValidationCallbackState* callbackState,
   373       /*optional*/ const SECItem* stapledOCSPResponse,
   374   /*optional out*/ mozilla::pkix::ScopedCERTCertList* validationChain,
   375   /*optional out*/ SECOidTag* evOidPolicy)
   376 {
   377   PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Top of MozillaPKIXVerifyCert\n"));
   379   PR_ASSERT(cert);
   380   PR_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV));
   382   if (validationChain) {
   383     *validationChain = nullptr;
   384   }
   385   if (evOidPolicy) {
   386     *evOidPolicy = SEC_OID_UNKNOWN;
   387   }
   389   if (!cert ||
   390       (usage != certificateUsageSSLServer && (flags & FLAG_MUST_BE_EV))) {
   391     PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
   392     return SECFailure;
   393   }
   395   CERTChainVerifyCallback callbackContainer;
   396   callbackContainer.isChainValid = chainValidationCallback;
   397   callbackContainer.isChainValidArg = callbackState;
   399   NSSCertDBTrustDomain::OCSPFetching ocspFetching
   400     = !mOCSPDownloadEnabled ||
   401       (flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::NeverFetchOCSP
   402     : !mOCSPStrict              ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail
   403                                 : NSSCertDBTrustDomain::FetchOCSPForDVHardFail;
   405   SECStatus rv;
   407   // TODO(bug 970750): anyExtendedKeyUsage
   408   // TODO: encipherOnly/decipherOnly
   409   // S/MIME Key Usage: http://tools.ietf.org/html/rfc3850#section-4.4.2
   410   // S/MIME EKU:       http://tools.ietf.org/html/rfc3850#section-4.4.4
   412   // TODO(bug 915931): Pass in stapled OCSP response in all calls to
   413   //                   BuildCertChain.
   415   mozilla::pkix::ScopedCERTCertList builtChain;
   416   switch (usage) {
   417     case certificateUsageSSLClient: {
   418       // XXX: We don't really have a trust bit for SSL client authentication so
   419       // just use trustEmail as it is the closest alternative.
   420       NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
   421                                        pinArg);
   422       rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
   423                           KeyUsage::digitalSignature,
   424                           SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH,
   425                           SEC_OID_X509_ANY_POLICY,
   426                           stapledOCSPResponse, builtChain);
   427       break;
   428     }
   430     case certificateUsageSSLServer: {
   431       // TODO: When verifying a certificate in an SSL handshake, we should
   432       // restrict the acceptable key usage based on the key exchange method
   433       // chosen by the server.
   435 #ifndef MOZ_NO_EV_CERTS
   436       // Try to validate for EV first.
   437       SECOidTag evPolicy = SEC_OID_UNKNOWN;
   438       rv = GetFirstEVPolicy(cert, evPolicy);
   439       if (rv == SECSuccess && evPolicy != SEC_OID_UNKNOWN) {
   440         NSSCertDBTrustDomain
   441           trustDomain(trustSSL,
   442                       ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP
   443                         ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
   444                         : NSSCertDBTrustDomain::FetchOCSPForEV,
   445                       mOCSPCache, pinArg, &callbackContainer);
   446         rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
   447                                           KeyUsage::digitalSignature, // ECDHE/DHE
   448                                           KeyUsage::keyEncipherment, // RSA
   449                                           KeyUsage::keyAgreement,    // (EC)DH
   450                                           SEC_OID_EXT_KEY_USAGE_SERVER_AUTH,
   451                                           evPolicy, stapledOCSPResponse,
   452                                           builtChain);
   453         if (rv == SECSuccess) {
   454           if (evOidPolicy) {
   455             *evOidPolicy = evPolicy;
   456           }
   457           break;
   458         }
   459         builtChain = nullptr; // clear built chain, just in case.
   460       }
   461 #endif
   463       if (flags & FLAG_MUST_BE_EV) {
   464         PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0);
   465         rv = SECFailure;
   466         break;
   467       }
   469       // Now try non-EV.
   470       NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
   471                                        pinArg, &callbackContainer);
   472       rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
   473                                         KeyUsage::digitalSignature, // (EC)DHE
   474                                         KeyUsage::keyEncipherment, // RSA
   475                                         KeyUsage::keyAgreement, // (EC)DH
   476                                         SEC_OID_EXT_KEY_USAGE_SERVER_AUTH,
   477                                         SEC_OID_X509_ANY_POLICY,
   478                                         stapledOCSPResponse, builtChain);
   479       break;
   480     }
   482     case certificateUsageSSLCA: {
   483       NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
   484                                        pinArg);
   485       rv = BuildCertChain(trustDomain, cert, time, MustBeCA,
   486                           KeyUsage::keyCertSign,
   487                           SEC_OID_EXT_KEY_USAGE_SERVER_AUTH,
   488                           SEC_OID_X509_ANY_POLICY,
   489                           stapledOCSPResponse, builtChain);
   490       break;
   491     }
   493     case certificateUsageEmailSigner: {
   494       NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
   495                                        pinArg);
   496       rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
   497                           KeyUsage::digitalSignature,
   498                           SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT,
   499                           SEC_OID_X509_ANY_POLICY,
   500                           stapledOCSPResponse, builtChain);
   501       break;
   502     }
   504     case certificateUsageEmailRecipient: {
   505       // TODO: The higher level S/MIME processing should pass in which key
   506       // usage it is trying to verify for, and base its algorithm choices
   507       // based on the result of the verification(s).
   508       NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
   509                                        pinArg);
   510       rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
   511                           KeyUsage::keyEncipherment, // RSA
   512                           SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT,
   513                           SEC_OID_X509_ANY_POLICY,
   514                           stapledOCSPResponse, builtChain);
   515       if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
   516         rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
   517                             KeyUsage::keyAgreement, // ECDH/DH
   518                             SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT,
   519                             SEC_OID_X509_ANY_POLICY,
   520                             stapledOCSPResponse, builtChain);
   521       }
   522       break;
   523     }
   525     case certificateUsageObjectSigner: {
   526       NSSCertDBTrustDomain trustDomain(trustObjectSigning, ocspFetching,
   527                                        mOCSPCache, pinArg);
   528       rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
   529                           KeyUsage::digitalSignature,
   530                           SEC_OID_EXT_KEY_USAGE_CODE_SIGN,
   531                           SEC_OID_X509_ANY_POLICY,
   532                           stapledOCSPResponse, builtChain);
   533       break;
   534     }
   536     case certificateUsageVerifyCA:
   537     case certificateUsageStatusResponder: {
   538       // XXX This is a pretty useless way to verify a certificate. It is used
   539       // by the implementation of window.crypto.importCertificates and in the
   540       // certificate viewer UI. Because we don't know what trust bit is
   541       // interesting, we just try them all.
   542       mozilla::pkix::EndEntityOrCA endEntityOrCA;
   543       mozilla::pkix::KeyUsage keyUsage;
   544       SECOidTag eku;
   545       if (usage == certificateUsageVerifyCA) {
   546         endEntityOrCA = MustBeCA;
   547         keyUsage = KeyUsage::keyCertSign;
   548         eku = SEC_OID_UNKNOWN;
   549       } else {
   550         endEntityOrCA = MustBeEndEntity;
   551         keyUsage = KeyUsage::digitalSignature;
   552         eku = SEC_OID_OCSP_RESPONDER;
   553       }
   555       NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache,
   556                                     pinArg);
   557       rv = BuildCertChain(sslTrust, cert, time, endEntityOrCA,
   558                           keyUsage, eku, SEC_OID_X509_ANY_POLICY,
   559                           stapledOCSPResponse, builtChain);
   560       if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) {
   561         NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, mOCSPCache,
   562                                         pinArg);
   563         rv = BuildCertChain(emailTrust, cert, time, endEntityOrCA, keyUsage,
   564                             eku, SEC_OID_X509_ANY_POLICY,
   565                             stapledOCSPResponse, builtChain);
   566         if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) {
   567           NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
   568                                                   ocspFetching, mOCSPCache,
   569                                                   pinArg);
   570           rv = BuildCertChain(objectSigningTrust, cert, time, endEntityOrCA,
   571                               keyUsage, eku, SEC_OID_X509_ANY_POLICY,
   572                               stapledOCSPResponse, builtChain);
   573         }
   574       }
   576       break;
   577     }
   579     default:
   580       PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
   581       return SECFailure;
   582   }
   584   // If there is an error we may need to worsen to error to be a pinning failure
   585   if (rv != SECSuccess && usage == certificateUsageSSLServer &&
   586       PR_GetError() != SEC_ERROR_APPLICATION_CALLBACK_ERROR) {
   587     tryWorsenPRErrorInCallback(cert, callbackState);
   588   }
   590   if (validationChain && rv == SECSuccess) {
   591     *validationChain = builtChain.release();
   592   }
   594   return rv;
   595 }
   597 SECStatus
   598 CertVerifier::VerifyCert(CERTCertificate* cert,
   599                          const SECCertificateUsage usage,
   600                          const PRTime time,
   601                          void* pinArg,
   602                          const char* hostname,
   603                          const Flags flags,
   604                          /*optional in*/ const SECItem* stapledOCSPResponse,
   605                          /*optional out*/ ScopedCERTCertList* validationChain,
   606                          /*optional out*/ SECOidTag* evOidPolicy,
   607                          /*optional out*/ CERTVerifyLog* verifyLog)
   608 {
   609   ChainValidationCallbackState callbackState = { hostname,
   610                                                  mPinningEnforcementLevel,
   611                                                  usage,
   612                                                  time };
   614   if (mImplementation == mozillapkix) {
   615     return MozillaPKIXVerifyCert(cert, usage, time, pinArg, flags,
   616                                  &callbackState, stapledOCSPResponse,
   617                                  validationChain, evOidPolicy);
   618   }
   620   if (!cert)
   621   {
   622     PR_NOT_REACHED("Invalid arguments to CertVerifier::VerifyCert");
   623     PORT_SetError(SEC_ERROR_INVALID_ARGS);
   624     return SECFailure;
   625   }
   626   if (validationChain) {
   627     *validationChain = nullptr;
   628   }
   629   if (evOidPolicy) {
   630     *evOidPolicy = SEC_OID_UNKNOWN;
   631   }
   633   switch(usage){
   634     case certificateUsageSSLClient:
   635     case certificateUsageSSLServer:
   636     case certificateUsageSSLCA:
   637     case certificateUsageEmailSigner:
   638     case certificateUsageEmailRecipient:
   639     case certificateUsageObjectSigner:
   640     case certificateUsageVerifyCA:
   641     case certificateUsageStatusResponder:
   642       break;
   643     default:
   644       PORT_SetError(SEC_ERROR_INVALID_ARGS);
   645       return SECFailure;
   646   }
   648   if ((flags & FLAG_MUST_BE_EV) && usage != certificateUsageSSLServer) {
   649       PORT_SetError(SEC_ERROR_INVALID_ARGS);
   650       return SECFailure;
   651   }
   653 #ifndef NSS_NO_LIBPKIX
   654   ScopedCERTCertList trustAnchors;
   655   SECStatus rv;
   656   SECOidTag evPolicy = SEC_OID_UNKNOWN;
   658   // Do EV checking only for sslserver usage
   659   if (usage == certificateUsageSSLServer) {
   660     SECStatus srv = GetFirstEVPolicy(cert, evPolicy);
   661     if (srv == SECSuccess) {
   662       if (evPolicy != SEC_OID_UNKNOWN) {
   663         trustAnchors = GetRootsForOid(evPolicy);
   664       }
   665       if (!trustAnchors) {
   666         return SECFailure;
   667       }
   668       // pkix ignores an empty trustanchors list and
   669       // decides then to use the whole set of trust in the DB
   670       // so we set the evPolicy to unkown in this case
   671       if (CERT_LIST_EMPTY(trustAnchors)) {
   672         evPolicy = SEC_OID_UNKNOWN;
   673       }
   674     } else {
   675       // No known EV policy found
   676       if (flags & FLAG_MUST_BE_EV) {
   677         PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND);
   678         return SECFailure;
   679       }
   680       // Do not setup EV verification params
   681       evPolicy = SEC_OID_UNKNOWN;
   682     }
   683     if ((evPolicy == SEC_OID_UNKNOWN) && (flags & FLAG_MUST_BE_EV)) {
   684       PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
   685       return SECFailure;
   686     }
   687   }
   689   PR_ASSERT(evPolicy == SEC_OID_UNKNOWN || trustAnchors);
   691   size_t i = 0;
   692   size_t validationChainLocation = 0;
   693   size_t validationTrustAnchorLocation = 0;
   694   CERTValOutParam cvout[4];
   695   if (verifyLog) {
   696      cvout[i].type = cert_po_errorLog;
   697      cvout[i].value.pointer.log = verifyLog;
   698      ++i;
   699   }
   700   if (validationChain) {
   701     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("VerifyCert: setting up validation chain outparam.\n"));
   702     validationChainLocation = i;
   703     cvout[i].type = cert_po_certList;
   704     cvout[i].value.pointer.chain = nullptr;
   705     ++i;
   706     validationTrustAnchorLocation = i;
   707     cvout[i].type = cert_po_trustAnchor;
   708     cvout[i].value.pointer.cert = nullptr;
   709     ++i;
   710   }
   711   cvout[i].type = cert_po_end;
   713   CERTRevocationFlags rev;
   715   CERTRevocationMethodIndex revPreferredMethods[2];
   716   rev.leafTests.preferred_methods =
   717   rev.chainTests.preferred_methods = revPreferredMethods;
   719   uint64_t revFlagsPerMethod[2];
   720   rev.leafTests.cert_rev_flags_per_method =
   721   rev.chainTests.cert_rev_flags_per_method = revFlagsPerMethod;
   722   rev.leafTests.number_of_preferred_methods =
   723   rev.chainTests.number_of_preferred_methods = 1;
   725   rev.leafTests.number_of_defined_methods =
   726   rev.chainTests.number_of_defined_methods = cert_revocation_method_ocsp + 1;
   728   const bool localOnly = flags & FLAG_LOCAL_ONLY;
   729   CERTValInParam cvin[7];
   731   // Parameters for both EV and DV validation
   732   cvin[0].type = cert_pi_useAIACertFetch;
   733   cvin[0].value.scalar.b = mMissingCertDownloadEnabled && !localOnly;
   734   cvin[1].type = cert_pi_revocationFlags;
   735   cvin[1].value.pointer.revocation = &rev;
   736   cvin[2].type = cert_pi_date;
   737   cvin[2].value.scalar.time = time;
   738   i = 3;
   740   CERTChainVerifyCallback callbackContainer;
   741   if (usage == certificateUsageSSLServer) {
   742     callbackContainer.isChainValid = chainValidationCallback;
   743     callbackContainer.isChainValidArg = &callbackState;
   744     cvin[i].type = cert_pi_chainVerifyCallback;
   745     cvin[i].value.pointer.chainVerifyCallback = &callbackContainer;
   746     ++i;
   747   }
   749   const size_t evParamLocation = i;
   751   if (evPolicy != SEC_OID_UNKNOWN) {
   752     // EV setup!
   753     // XXX 859872 The current flags are not quite correct. (use
   754     // of ocsp flags for crl preferences).
   755     uint64_t ocspRevMethodFlags =
   756       CERT_REV_M_TEST_USING_THIS_METHOD
   757       | ((mOCSPDownloadEnabled && !localOnly) ?
   758           CERT_REV_M_ALLOW_NETWORK_FETCHING : CERT_REV_M_FORBID_NETWORK_FETCHING)
   759       | CERT_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE
   760       | CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE
   761       | CERT_REV_M_IGNORE_MISSING_FRESH_INFO
   762       | CERT_REV_M_STOP_TESTING_ON_FRESH_INFO
   763       | (mOCSPGETEnabled ? 0 : CERT_REV_M_FORCE_POST_METHOD_FOR_OCSP);
   765     rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_crl] =
   766     rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_crl]
   767       = CERT_REV_M_DO_NOT_TEST_USING_THIS_METHOD;
   769     rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] =
   770     rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_ocsp]
   771       = ocspRevMethodFlags;
   773     rev.leafTests.cert_rev_method_independent_flags =
   774     rev.chainTests.cert_rev_method_independent_flags =
   775       // avoiding the network is good, let's try local first
   776       CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST
   777       // is overall revocation requirement strict or relaxed?
   778       |  CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE
   779       ;
   781     rev.leafTests.preferred_methods[0] =
   782     rev.chainTests.preferred_methods[0] = cert_revocation_method_ocsp;
   784     cvin[i].type = cert_pi_policyOID;
   785     cvin[i].value.arraySize = 1;
   786     cvin[i].value.array.oids = &evPolicy;
   787     ++i;
   788     PR_ASSERT(trustAnchors);
   789     cvin[i].type = cert_pi_trustAnchors;
   790     cvin[i].value.pointer.chain = trustAnchors.get();
   791     ++i;
   793     cvin[i].type = cert_pi_end;
   795     rv = CERT_PKIXVerifyCert(cert, usage, cvin, cvout, pinArg);
   796     if (rv == SECSuccess) {
   797       if (evOidPolicy) {
   798         *evOidPolicy = evPolicy;
   799       }
   800       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   801              ("VerifyCert: successful CERT_PKIXVerifyCert(ev) \n"));
   802       goto pkix_done;
   803     }
   804     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   805            ("VerifyCert: failed CERT_PKIXVerifyCert(ev)\n"));
   806     if (flags & FLAG_MUST_BE_EV) {
   807       return rv;
   808     }
   809     if (validationChain) {
   810       destroyCertListThatShouldNotExist(
   811         &cvout[validationChainLocation].value.pointer.chain);
   812     }
   814     if (verifyLog) {
   815       // Cleanup the log so that it is ready the the next validation
   816       CERTVerifyLogNode* i_node;
   817       for (i_node = verifyLog->head; i_node; i_node = i_node->next) {
   818          //destroy cert if any.
   819          if (i_node->cert) {
   820            CERT_DestroyCertificate(i_node->cert);
   821          }
   822          // No need to cleanup the actual nodes in the arena.
   823       }
   824       verifyLog->count = 0;
   825       verifyLog->head = nullptr;
   826       verifyLog->tail = nullptr;
   827     }
   829   }
   830 #endif
   832   // If we're here, PKIX EV verification failed.
   833   // If requested, don't do DV fallback.
   834   if (flags & FLAG_MUST_BE_EV) {
   835     PR_ASSERT(*evOidPolicy == SEC_OID_UNKNOWN);
   836 #ifdef NSS_NO_LIBPKIX
   837     PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
   838 #else
   839     PR_SetError(PR_INVALID_STATE_ERROR, 0);
   840 #endif
   841     return SECFailure;
   842   }
   844   if (mImplementation == classic) {
   845     // XXX: we do not care about the localOnly flag (currently) as the
   846     // caller that wants localOnly should disable and reenable the fetching.
   847     return ClassicVerifyCert(cert, usage, time, pinArg, &callbackState,
   848                              validationChain, verifyLog);
   849   }
   851 #ifdef NSS_NO_LIBPKIX
   852   PR_NOT_REACHED("libpkix implementation chosen but not even compiled in");
   853   PR_SetError(PR_INVALID_STATE_ERROR, 0);
   854   return SECFailure;
   855 #else
   856   PR_ASSERT(mImplementation == libpkix);
   858   // The current flags check the chain the same way as the leafs
   859   rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_crl] =
   860   rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_crl] =
   861     // implicit default source - makes no sense for CRLs
   862     CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE
   864     // let's not stop on fresh CRL. If OCSP is enabled, too, let's check it
   865     | CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO
   867     // no fresh CRL? well, let other flag decide whether to fail or not
   868     | CERT_REV_M_IGNORE_MISSING_FRESH_INFO
   870     // testing using local CRLs is always allowed
   871     | CERT_REV_M_TEST_USING_THIS_METHOD
   873     // no local crl and don't know where to get it from? ignore
   874     | CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE
   876     // crl download based on parameter
   877     | ((mCRLDownloadEnabled && !localOnly) ?
   878         CERT_REV_M_ALLOW_NETWORK_FETCHING : CERT_REV_M_FORBID_NETWORK_FETCHING)
   879     ;
   881   rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] =
   882   rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] =
   883     // use OCSP
   884       CERT_REV_M_TEST_USING_THIS_METHOD
   886     // if app has a default OCSP responder configured, let's use it
   887     | CERT_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE
   889     // of course OCSP doesn't work without a source. let's accept such certs
   890     | CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE
   892     // if ocsp is required stop on lack of freshness
   893     | (mOCSPStrict ?
   894        CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO : CERT_REV_M_IGNORE_MISSING_FRESH_INFO)
   896     // ocsp success is sufficient
   897     | CERT_REV_M_STOP_TESTING_ON_FRESH_INFO
   899     // ocsp enabled controls network fetching, too
   900     | ((mOCSPDownloadEnabled && !localOnly) ?
   901         CERT_REV_M_ALLOW_NETWORK_FETCHING : CERT_REV_M_FORBID_NETWORK_FETCHING)
   903     | (mOCSPGETEnabled ? 0 : CERT_REV_M_FORCE_POST_METHOD_FOR_OCSP);
   904     ;
   906   rev.leafTests.preferred_methods[0] =
   907   rev.chainTests.preferred_methods[0] = cert_revocation_method_ocsp;
   909   rev.leafTests.cert_rev_method_independent_flags =
   910   rev.chainTests.cert_rev_method_independent_flags =
   911     // avoiding the network is good, let's try local first
   912     CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST;
   914   // Skip EV parameters
   915   cvin[evParamLocation].type = cert_pi_end;
   917   PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("VerifyCert: calling CERT_PKIXVerifyCert(dv) \n"));
   918   rv = CERT_PKIXVerifyCert(cert, usage, cvin, cvout, pinArg);
   920 pkix_done:
   921   // If there is an error we may need to worsen to error to be a pinning failure
   922   if (rv != SECSuccess && usage == certificateUsageSSLServer &&
   923       PR_GetError() != SEC_ERROR_APPLICATION_CALLBACK_ERROR) {
   924     tryWorsenPRErrorInCallback(cert, &callbackState);
   925   }
   927   if (validationChain) {
   928     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("VerifyCert: validation chain requested\n"));
   929     ScopedCERTCertificate trustAnchor(cvout[validationTrustAnchorLocation].value.pointer.cert);
   931     if (rv == SECSuccess) {
   932       if (! cvout[validationChainLocation].value.pointer.chain) {
   933         PR_SetError(PR_UNKNOWN_ERROR, 0);
   934         return SECFailure;
   935       }
   936       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("VerifyCert: I have a chain\n"));
   937       *validationChain = cvout[validationChainLocation].value.pointer.chain;
   938       if (trustAnchor) {
   939         // we should only add the issuer to the chain if it is not already
   940         // present. On CA cert checking, the issuer is the same cert, so in
   941         // that case we do not add the cert to the chain.
   942         if (!CERT_CompareCerts(trustAnchor.get(), cert)) {
   943           PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("VerifyCert:  adding issuer to tail for display\n"));
   944           // note: rv is reused to catch errors on cert creation!
   945           ScopedCERTCertificate tempCert(CERT_DupCertificate(trustAnchor.get()));
   946           rv = CERT_AddCertToListTail(validationChain->get(), tempCert.get());
   947           if (rv == SECSuccess) {
   948             tempCert.release(); // ownership traferred to validationChain
   949           } else {
   950             *validationChain = nullptr;
   951           }
   952         }
   953       }
   954     } else {
   955       destroyCertListThatShouldNotExist(
   956         &cvout[validationChainLocation].value.pointer.chain);
   957     }
   958   }
   960   return rv;
   961 #endif
   962 }
   964 SECStatus
   965 CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert,
   966                      /*optional*/ const SECItem* stapledOCSPResponse,
   967                                   PRTime time,
   968                      /*optional*/ void* pinarg,
   969                                   const char* hostname,
   970                                   bool saveIntermediatesInPermanentDatabase,
   971                  /*optional out*/ mozilla::pkix::ScopedCERTCertList* certChainOut,
   972                  /*optional out*/ SECOidTag* evOidPolicy)
   973 {
   974   PR_ASSERT(peerCert);
   975   // XXX: PR_ASSERT(pinarg)
   976   PR_ASSERT(hostname);
   977   PR_ASSERT(hostname[0]);
   979   if (certChainOut) {
   980     *certChainOut = nullptr;
   981   }
   982   if (evOidPolicy) {
   983     *evOidPolicy = SEC_OID_UNKNOWN;
   984   }
   986   if (!hostname || !hostname[0]) {
   987     PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN, 0);
   988     return SECFailure;
   989   }
   991   // CreateCertErrorRunnable assumes that CERT_VerifyCertName is only called
   992   // if VerifyCert succeeded.
   993   ScopedCERTCertList validationChain;
   994   SECStatus rv = VerifyCert(peerCert, certificateUsageSSLServer, time, pinarg,
   995                             hostname, 0, stapledOCSPResponse, &validationChain,
   996                             evOidPolicy, nullptr);
   997   if (rv != SECSuccess) {
   998     return rv;
   999   }
  1001   rv = CERT_VerifyCertName(peerCert, hostname);
  1002   if (rv != SECSuccess) {
  1003     return rv;
  1006   if (saveIntermediatesInPermanentDatabase) {
  1007     SaveIntermediateCerts(validationChain);
  1010   if (certChainOut) {
  1011     *certChainOut = validationChain.release();
  1014   return SECSuccess;
  1017 } } // namespace mozilla::psm

mercurial