security/certverifier/NSSCertDBTrustDomain.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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 "NSSCertDBTrustDomain.h"
     9 #include <stdint.h>
    11 #include "ExtendedValidation.h"
    12 #include "OCSPRequestor.h"
    13 #include "certdb.h"
    14 #include "mozilla/Telemetry.h"
    15 #include "nss.h"
    16 #include "ocsp.h"
    17 #include "pk11pub.h"
    18 #include "pkix/pkix.h"
    19 #include "prerror.h"
    20 #include "prmem.h"
    21 #include "prprf.h"
    22 #include "secerr.h"
    23 #include "secmod.h"
    25 using namespace mozilla::pkix;
    27 #ifdef PR_LOGGING
    28 extern PRLogModuleInfo* gCertVerifierLog;
    29 #endif
    31 namespace mozilla { namespace psm {
    33 const char BUILTIN_ROOTS_MODULE_DEFAULT_NAME[] = "Builtin Roots Module";
    35 void PORT_Free_string(char* str) { PORT_Free(str); }
    37 namespace {
    39 typedef ScopedPtr<SECMODModule, SECMOD_DestroyModule> ScopedSECMODModule;
    41 } // unnamed namespace
    43 NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
    44                                            OCSPFetching ocspFetching,
    45                                            OCSPCache& ocspCache,
    46                                            void* pinArg,
    47                                            CERTChainVerifyCallback* checkChainCallback)
    48   : mCertDBTrustType(certDBTrustType)
    49   , mOCSPFetching(ocspFetching)
    50   , mOCSPCache(ocspCache)
    51   , mPinArg(pinArg)
    52   , mCheckChainCallback(checkChainCallback)
    53 {
    54 }
    56 SECStatus
    57 NSSCertDBTrustDomain::FindPotentialIssuers(
    58   const SECItem* encodedIssuerName, PRTime time,
    59   /*out*/ mozilla::pkix::ScopedCERTCertList& results)
    60 {
    61   // TODO: normalize encodedIssuerName
    62   // TODO: NSS seems to be ambiguous between "no potential issuers found" and
    63   // "there was an error trying to retrieve the potential issuers."
    64   results = CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
    65                                        encodedIssuerName, time, true);
    66   return SECSuccess;
    67 }
    69 SECStatus
    70 NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
    71                                    SECOidTag policy,
    72                                    const CERTCertificate* candidateCert,
    73                                    /*out*/ TrustLevel* trustLevel)
    74 {
    75   PR_ASSERT(candidateCert);
    76   PR_ASSERT(trustLevel);
    78   if (!candidateCert || !trustLevel) {
    79     PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
    80     return SECFailure;
    81   }
    83 #ifdef MOZ_NO_EV_CERTS
    84   if (policy != SEC_OID_X509_ANY_POLICY) {
    85     PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0);
    86     return SECFailure;
    87   }
    88 #endif
    90   // XXX: CERT_GetCertTrust seems to be abusing SECStatus as a boolean, where
    91   // SECSuccess means that there is a trust record and SECFailure means there
    92   // is not a trust record. I looked at NSS's internal uses of
    93   // CERT_GetCertTrust, and all that code uses the result as a boolean meaning
    94   // "We have a trust record."
    95   CERTCertTrust trust;
    96   if (CERT_GetCertTrust(candidateCert, &trust) == SECSuccess) {
    97     PRUint32 flags = SEC_GET_TRUST_FLAGS(&trust, mCertDBTrustType);
    99     // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit,
   100     // because we can have active distrust for either type of cert. Note that
   101     // CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so if the
   102     // relevant trust bit isn't set then that means the cert must be considered
   103     // distrusted.
   104     PRUint32 relevantTrustBit = endEntityOrCA == MustBeCA ? CERTDB_TRUSTED_CA
   105                                                           : CERTDB_TRUSTED;
   106     if (((flags & (relevantTrustBit|CERTDB_TERMINAL_RECORD)))
   107             == CERTDB_TERMINAL_RECORD) {
   108       *trustLevel = ActivelyDistrusted;
   109       return SECSuccess;
   110     }
   112     // For TRUST, we only use the CERTDB_TRUSTED_CA bit, because Gecko hasn't
   113     // needed to consider end-entity certs to be their own trust anchors since
   114     // Gecko implemented nsICertOverrideService.
   115     if (flags & CERTDB_TRUSTED_CA) {
   116       if (policy == SEC_OID_X509_ANY_POLICY) {
   117         *trustLevel = TrustAnchor;
   118         return SECSuccess;
   119       }
   120 #ifndef MOZ_NO_EV_CERTS
   121       if (CertIsAuthoritativeForEVPolicy(candidateCert, policy)) {
   122         *trustLevel = TrustAnchor;
   123         return SECSuccess;
   124       }
   125 #endif
   126     }
   127   }
   129   *trustLevel = InheritsTrust;
   130   return SECSuccess;
   131 }
   133 SECStatus
   134 NSSCertDBTrustDomain::VerifySignedData(const CERTSignedData* signedData,
   135                                        const CERTCertificate* cert)
   136 {
   137   return ::mozilla::pkix::VerifySignedData(signedData, cert, mPinArg);
   138 }
   140 static PRIntervalTime
   141 OCSPFetchingTypeToTimeoutTime(NSSCertDBTrustDomain::OCSPFetching ocspFetching)
   142 {
   143   switch (ocspFetching) {
   144     case NSSCertDBTrustDomain::FetchOCSPForDVSoftFail:
   145       return PR_SecondsToInterval(2);
   146     case NSSCertDBTrustDomain::FetchOCSPForEV:
   147     case NSSCertDBTrustDomain::FetchOCSPForDVHardFail:
   148       return PR_SecondsToInterval(10);
   149     // The rest of these are error cases. Assert in debug builds, but return
   150     // the default value corresponding to 2 seconds in release builds.
   151     case NSSCertDBTrustDomain::NeverFetchOCSP:
   152     case NSSCertDBTrustDomain::LocalOnlyOCSPForEV:
   153       PR_NOT_REACHED("we should never see this OCSPFetching type here");
   154     default:
   155       PR_NOT_REACHED("we're not handling every OCSPFetching type");
   156   }
   157   return PR_SecondsToInterval(2);
   158 }
   160 SECStatus
   161 NSSCertDBTrustDomain::CheckRevocation(
   162   mozilla::pkix::EndEntityOrCA endEntityOrCA,
   163   const CERTCertificate* cert,
   164   /*const*/ CERTCertificate* issuerCert,
   165   PRTime time,
   166   /*optional*/ const SECItem* stapledOCSPResponse)
   167 {
   168   // Actively distrusted certificates will have already been blocked by
   169   // GetCertTrust.
   171   // TODO: need to verify that IsRevoked isn't called for trust anchors AND
   172   // that that fact is documented in mozillapkix.
   174   PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   175          ("NSSCertDBTrustDomain: Top of CheckRevocation\n"));
   177   PORT_Assert(cert);
   178   PORT_Assert(issuerCert);
   179   if (!cert || !issuerCert) {
   180     PORT_SetError(SEC_ERROR_INVALID_ARGS);
   181     return SECFailure;
   182   }
   184   // Bug 991815: The BR allow OCSP for intermediates to be up to one year old.
   185   // Since this affects EV there is no reason why DV should be more strict
   186   // so all intermediatates are allowed to have OCSP responses up to one year
   187   // old.
   188   uint16_t maxOCSPLifetimeInDays = 10;
   189   if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
   190     maxOCSPLifetimeInDays = 365;
   191   }
   193   // If we have a stapled OCSP response then the verification of that response
   194   // determines the result unless the OCSP response is expired. We make an
   195   // exception for expired responses because some servers, nginx in particular,
   196   // are known to serve expired responses due to bugs.
   197   // We keep track of the result of verifying the stapled response but don't
   198   // immediately return failure if the response has expired.
   199   PRErrorCode stapledOCSPResponseErrorCode = 0;
   200   if (stapledOCSPResponse) {
   201     PR_ASSERT(endEntityOrCA == MustBeEndEntity);
   202     bool expired;
   203     SECStatus rv = VerifyAndMaybeCacheEncodedOCSPResponse(cert, issuerCert,
   204                                                           time,
   205                                                           maxOCSPLifetimeInDays,
   206                                                           stapledOCSPResponse,
   207                                                           ResponseWasStapled,
   208                                                           expired);
   209     if (rv == SECSuccess) {
   210       // stapled OCSP response present and good
   211       Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 1);
   212       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   213              ("NSSCertDBTrustDomain: stapled OCSP response: good"));
   214       return rv;
   215     }
   216     stapledOCSPResponseErrorCode = PR_GetError();
   217     if (stapledOCSPResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE ||
   218         expired) {
   219       // stapled OCSP response present but expired
   220       Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 3);
   221       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   222              ("NSSCertDBTrustDomain: expired stapled OCSP response"));
   223     } else {
   224       // stapled OCSP response present but invalid for some reason
   225       Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 4);
   226       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   227              ("NSSCertDBTrustDomain: stapled OCSP response: failure"));
   228       return rv;
   229     }
   230   } else {
   231     // no stapled OCSP response
   232     Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 2);
   233     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   234            ("NSSCertDBTrustDomain: no stapled OCSP response"));
   235   }
   237   PRErrorCode cachedResponseErrorCode = 0;
   238   PRTime cachedResponseValidThrough = 0;
   239   bool cachedResponsePresent = mOCSPCache.Get(cert, issuerCert,
   240                                               cachedResponseErrorCode,
   241                                               cachedResponseValidThrough);
   242   if (cachedResponsePresent) {
   243     if (cachedResponseErrorCode == 0 && cachedResponseValidThrough >= time) {
   244       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   245              ("NSSCertDBTrustDomain: cached OCSP response: good"));
   246       return SECSuccess;
   247     }
   248     // If we have a cached revoked response, use it.
   249     if (cachedResponseErrorCode == SEC_ERROR_REVOKED_CERTIFICATE) {
   250       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   251              ("NSSCertDBTrustDomain: cached OCSP response: revoked"));
   252       PR_SetError(SEC_ERROR_REVOKED_CERTIFICATE, 0);
   253       return SECFailure;
   254     }
   255     // The cached response may indicate an unknown certificate or it may be
   256     // expired. Don't return with either of these statuses yet - we may be
   257     // able to fetch a more recent one.
   258     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   259            ("NSSCertDBTrustDomain: cached OCSP response: error %ld valid "
   260            "until %lld", cachedResponseErrorCode, cachedResponseValidThrough));
   261     // When a good cached response has expired, it is more convenient
   262     // to convert that to an error code and just deal with
   263     // cachedResponseErrorCode from here on out.
   264     if (cachedResponseErrorCode == 0 && cachedResponseValidThrough < time) {
   265       cachedResponseErrorCode = SEC_ERROR_OCSP_OLD_RESPONSE;
   266     }
   267     // We may have a cached indication of server failure. Ignore it if
   268     // it has expired.
   269     if (cachedResponseErrorCode != 0 &&
   270         cachedResponseErrorCode != SEC_ERROR_OCSP_UNKNOWN_CERT &&
   271         cachedResponseErrorCode != SEC_ERROR_OCSP_OLD_RESPONSE &&
   272         cachedResponseValidThrough < time) {
   273       cachedResponseErrorCode = 0;
   274       cachedResponsePresent = false;
   275     }
   276   } else {
   277     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   278            ("NSSCertDBTrustDomain: no cached OCSP response"));
   279   }
   280   // At this point, if and only if cachedErrorResponseCode is 0, there was no
   281   // cached response.
   282   PR_ASSERT((!cachedResponsePresent && cachedResponseErrorCode == 0) ||
   283             (cachedResponsePresent && cachedResponseErrorCode != 0));
   285   // TODO: We still need to handle the fallback for expired responses. But,
   286   // if/when we disable OCSP fetching by default, it would be ambiguous whether
   287   // security.OCSP.enable==0 means "I want the default" or "I really never want
   288   // you to ever fetch OCSP."
   290   if ((mOCSPFetching == NeverFetchOCSP) ||
   291       (endEntityOrCA == MustBeCA && (mOCSPFetching == FetchOCSPForDVHardFail ||
   292                                      mOCSPFetching == FetchOCSPForDVSoftFail))) {
   293     // We're not going to be doing any fetching, so if there was a cached
   294     // "unknown" response, say so.
   295     if (cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) {
   296       PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0);
   297       return SECFailure;
   298     }
   299     // If we're doing hard-fail, we want to know if we have a cached response
   300     // that has expired.
   301     if (mOCSPFetching == FetchOCSPForDVHardFail &&
   302         cachedResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE) {
   303       PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0);
   304       return SECFailure;
   305     }
   307     return SECSuccess;
   308   }
   310   if (mOCSPFetching == LocalOnlyOCSPForEV) {
   311     PR_SetError(cachedResponseErrorCode != 0 ? cachedResponseErrorCode
   312                                              : SEC_ERROR_OCSP_UNKNOWN_CERT, 0);
   313     return SECFailure;
   314   }
   316   ScopedPtr<char, PORT_Free_string>
   317     url(CERT_GetOCSPAuthorityInfoAccessLocation(cert));
   319   if (!url) {
   320     if (mOCSPFetching == FetchOCSPForEV ||
   321         cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) {
   322       PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0);
   323       return SECFailure;
   324     }
   325     if (cachedResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE) {
   326       PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0);
   327       return SECFailure;
   328     }
   329     if (stapledOCSPResponseErrorCode != 0) {
   330       PR_SetError(stapledOCSPResponseErrorCode, 0);
   331       return SECFailure;
   332     }
   334     // Nothing to do if we don't have an OCSP responder URI for the cert; just
   335     // assume it is good. Note that this is the confusing, but intended,
   336     // interpretation of "strict" revocation checking in the face of a
   337     // certificate that lacks an OCSP responder URI.
   338     return SECSuccess;
   339   }
   341   ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
   342   if (!arena) {
   343     return SECFailure;
   344   }
   346   // Only request a response if we didn't have a cached indication of failure
   347   // (don't keep requesting responses from a failing server).
   348   const SECItem* response = nullptr;
   349   if (cachedResponseErrorCode == 0 ||
   350       cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT ||
   351       cachedResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE) {
   352     const SECItem* request(CreateEncodedOCSPRequest(arena.get(), cert,
   353                                                     issuerCert));
   354     if (!request) {
   355       return SECFailure;
   356     }
   358     response = DoOCSPRequest(arena.get(), url.get(), request,
   359                              OCSPFetchingTypeToTimeoutTime(mOCSPFetching));
   360   }
   362   if (!response) {
   363     PRErrorCode error = PR_GetError();
   364     if (error == 0) {
   365       error = cachedResponseErrorCode;
   366     }
   367     PRTime timeout = time + ServerFailureDelay;
   368     if (mOCSPCache.Put(cert, issuerCert, error, time, timeout) != SECSuccess) {
   369       return SECFailure;
   370     }
   371     PR_SetError(error, 0);
   372     if (mOCSPFetching != FetchOCSPForDVSoftFail) {
   373       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   374              ("NSSCertDBTrustDomain: returning SECFailure after "
   375               "OCSP request failure"));
   376       return SECFailure;
   377     }
   378     if (cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) {
   379       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   380              ("NSSCertDBTrustDomain: returning SECFailure from cached "
   381               "response after OCSP request failure"));
   382       PR_SetError(cachedResponseErrorCode, 0);
   383       return SECFailure;
   384     }
   385     if (stapledOCSPResponseErrorCode != 0) {
   386       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   387              ("NSSCertDBTrustDomain: returning SECFailure from expired "
   388               "stapled response after OCSP request failure"));
   389       PR_SetError(stapledOCSPResponseErrorCode, 0);
   390       return SECFailure;
   391     }
   393     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   394            ("NSSCertDBTrustDomain: returning SECSuccess after "
   395             "OCSP request failure"));
   396     return SECSuccess; // Soft fail -> success :(
   397   }
   399   // If the response from the network has expired but indicates a revoked
   400   // or unknown certificate, PR_GetError() will return the appropriate error.
   401   // We actually ignore expired here.
   402   bool expired;
   403   SECStatus rv = VerifyAndMaybeCacheEncodedOCSPResponse(cert, issuerCert, time,
   404                                                         maxOCSPLifetimeInDays,
   405                                                         response,
   406                                                         ResponseIsFromNetwork,
   407                                                         expired);
   408   if (rv == SECSuccess || mOCSPFetching != FetchOCSPForDVSoftFail) {
   409     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   410       ("NSSCertDBTrustDomain: returning after VerifyEncodedOCSPResponse"));
   411     return rv;
   412   }
   414   PRErrorCode error = PR_GetError();
   415   if (error == SEC_ERROR_OCSP_UNKNOWN_CERT ||
   416       error == SEC_ERROR_REVOKED_CERTIFICATE) {
   417     return rv;
   418   }
   420   if (stapledOCSPResponseErrorCode != 0) {
   421     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   422            ("NSSCertDBTrustDomain: returning SECFailure from expired stapled "
   423             "response after OCSP request verification failure"));
   424     PR_SetError(stapledOCSPResponseErrorCode, 0);
   425     return SECFailure;
   426   }
   428   PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   429          ("NSSCertDBTrustDomain: end of CheckRevocation"));
   431   return SECSuccess; // Soft fail -> success :(
   432 }
   434 SECStatus
   435 NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
   436   const CERTCertificate* cert, CERTCertificate* issuerCert, PRTime time,
   437   uint16_t maxLifetimeInDays, const SECItem* encodedResponse,
   438   EncodedResponseSource responseSource, /*out*/ bool& expired)
   439 {
   440   PRTime thisUpdate = 0;
   441   PRTime validThrough = 0;
   442   SECStatus rv = VerifyEncodedOCSPResponse(*this, cert, issuerCert, time,
   443                                            maxLifetimeInDays, encodedResponse,
   444                                            expired, &thisUpdate, &validThrough);
   445   PRErrorCode error = (rv == SECSuccess ? 0 : PR_GetError());
   446   // If a response was stapled and expired, we don't want to cache it. Return
   447   // early to simplify the logic here.
   448   if (responseSource == ResponseWasStapled && expired) {
   449     PR_ASSERT(rv != SECSuccess);
   450     return rv;
   451   }
   452   // validThrough is only trustworthy if the response successfully verifies
   453   // or it indicates a revoked or unknown certificate.
   454   // If this isn't the case, store an indication of failure (to prevent
   455   // repeatedly requesting a response from a failing server).
   456   if (rv != SECSuccess && error != SEC_ERROR_REVOKED_CERTIFICATE &&
   457       error != SEC_ERROR_OCSP_UNKNOWN_CERT) {
   458     validThrough = time + ServerFailureDelay;
   459   }
   460   if (responseSource == ResponseIsFromNetwork ||
   461       rv == SECSuccess ||
   462       error == SEC_ERROR_REVOKED_CERTIFICATE ||
   463       error == SEC_ERROR_OCSP_UNKNOWN_CERT) {
   464     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   465            ("NSSCertDBTrustDomain: caching OCSP response"));
   466     if (mOCSPCache.Put(cert, issuerCert, error, thisUpdate, validThrough)
   467           != SECSuccess) {
   468       return SECFailure;
   469     }
   470   }
   472   // If the verification failed, re-set to that original error
   473   // (the call to Put may have un-set it).
   474   if (rv != SECSuccess) {
   475     PR_SetError(error, 0);
   476   }
   477   return rv;
   478 }
   480 SECStatus
   481 NSSCertDBTrustDomain::IsChainValid(const CERTCertList* certChain) {
   482   SECStatus rv = SECFailure;
   484   PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   485       ("NSSCertDBTrustDomain: Top of IsChainValid mCheckCallback=%p",
   486        mCheckChainCallback));
   488   if (!mCheckChainCallback) {
   489     return SECSuccess;
   490   }
   491   if (!mCheckChainCallback->isChainValid) {
   492     PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
   493     return SECFailure;
   494   }
   495   PRBool chainOK;
   496   rv = (mCheckChainCallback->isChainValid)(mCheckChainCallback->isChainValidArg,
   497                                            certChain, &chainOK);
   498   if (rv != SECSuccess) {
   499     return rv;
   500   }
   501   // rv = SECSuccess only implies successful call, now is time
   502   // to check the chain check status
   503   // we should only return success if the chain is valid
   504   if (chainOK) {
   505     return SECSuccess;
   506   }
   507   PR_SetError(SEC_ERROR_APPLICATION_CALLBACK_ERROR, 0);
   508   return SECFailure;
   509 }
   511 namespace {
   513 static char*
   514 nss_addEscape(const char* string, char quote)
   515 {
   516   char* newString = 0;
   517   int escapes = 0, size = 0;
   518   const char* src;
   519   char* dest;
   521   for (src = string; *src; src++) {
   522   if ((*src == quote) || (*src == '\\')) {
   523     escapes++;
   524   }
   525   size++;
   526   }
   528   newString = (char*) PORT_ZAlloc(escapes + size + 1);
   529   if (!newString) {
   530     return nullptr;
   531   }
   533   for (src = string, dest = newString; *src; src++, dest++) {
   534     if ((*src == quote) || (*src == '\\')) {
   535       *dest++ = '\\';
   536     }
   537     *dest = *src;
   538   }
   540   return newString;
   541 }
   543 } // unnamed namespace
   545 SECStatus
   546 InitializeNSS(const char* dir, bool readOnly)
   547 {
   548   // The NSS_INIT_NOROOTINIT flag turns off the loading of the root certs
   549   // module by NSS_Initialize because we will load it in InstallLoadableRoots
   550   // later.  It also allows us to work around a bug in the system NSS in
   551   // Ubuntu 8.04, which loads any nonexistent "<configdir>/libnssckbi.so" as
   552   // "/usr/lib/nss/libnssckbi.so".
   553   uint32_t flags = NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE;
   554   if (readOnly) {
   555     flags |= NSS_INIT_READONLY;
   556   }
   557   return ::NSS_Initialize(dir, "", "", SECMOD_DB, flags);
   558 }
   560 void
   561 DisableMD5()
   562 {
   563   NSS_SetAlgorithmPolicy(SEC_OID_MD5,
   564     0, NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
   565   NSS_SetAlgorithmPolicy(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION,
   566     0, NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
   567   NSS_SetAlgorithmPolicy(SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC,
   568     0, NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
   569 }
   571 SECStatus
   572 LoadLoadableRoots(/*optional*/ const char* dir, const char* modNameUTF8)
   573 {
   574   PR_ASSERT(modNameUTF8);
   576   if (!modNameUTF8) {
   577     PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
   578     return SECFailure;
   579   }
   581   ScopedPtr<char, PR_FreeLibraryName> fullLibraryPath(
   582     PR_GetLibraryName(dir, "nssckbi"));
   583   if (!fullLibraryPath) {
   584     return SECFailure;
   585   }
   587   ScopedPtr<char, PORT_Free_string> escaped_fullLibraryPath(
   588     nss_addEscape(fullLibraryPath.get(), '\"'));
   589   if (!escaped_fullLibraryPath) {
   590     return SECFailure;
   591   }
   593   // If a module exists with the same name, delete it.
   594   int modType;
   595   SECMOD_DeleteModule(modNameUTF8, &modType);
   597   ScopedPtr<char, PR_smprintf_free> pkcs11ModuleSpec(
   598     PR_smprintf("name=\"%s\" library=\"%s\"", modNameUTF8,
   599                 escaped_fullLibraryPath.get()));
   600   if (!pkcs11ModuleSpec) {
   601     return SECFailure;
   602   }
   604   ScopedSECMODModule rootsModule(SECMOD_LoadUserModule(pkcs11ModuleSpec.get(),
   605                                                        nullptr, false));
   606   if (!rootsModule) {
   607     return SECFailure;
   608   }
   610   if (!rootsModule->loaded) {
   611     PR_SetError(PR_INVALID_STATE_ERROR, 0);
   612     return SECFailure;
   613   }
   615   return SECSuccess;
   616 }
   618 void
   619 UnloadLoadableRoots(const char* modNameUTF8)
   620 {
   621   PR_ASSERT(modNameUTF8);
   622   ScopedSECMODModule rootsModule(SECMOD_FindModule(modNameUTF8));
   624   if (rootsModule) {
   625     SECMOD_UnloadUserModule(rootsModule.get());
   626   }
   627 }
   629 void
   630 SetClassicOCSPBehavior(CertVerifier::ocsp_download_config enabled,
   631                        CertVerifier::ocsp_strict_config strict,
   632                        CertVerifier::ocsp_get_config get)
   633 {
   634   CERT_DisableOCSPDefaultResponder(CERT_GetDefaultCertDB());
   635   if (enabled == CertVerifier::ocsp_off) {
   636     CERT_DisableOCSPChecking(CERT_GetDefaultCertDB());
   637   } else {
   638     CERT_EnableOCSPChecking(CERT_GetDefaultCertDB());
   639   }
   641   SEC_OcspFailureMode failureMode = strict == CertVerifier::ocsp_strict
   642                                   ? ocspMode_FailureIsVerificationFailure
   643                                   : ocspMode_FailureIsNotAVerificationFailure;
   644   (void) CERT_SetOCSPFailureMode(failureMode);
   646   CERT_ForcePostMethodForOCSP(get != CertVerifier::ocsp_get_enabled);
   648   int OCSPTimeoutSeconds = 3;
   649   if (strict == CertVerifier::ocsp_strict) {
   650     OCSPTimeoutSeconds = 10;
   651   }
   652   CERT_SetOCSPTimeout(OCSPTimeoutSeconds);
   653 }
   655 char*
   656 DefaultServerNicknameForCert(CERTCertificate* cert)
   657 {
   658   char* nickname = nullptr;
   659   int count;
   660   bool conflict;
   661   char* servername = nullptr;
   663   servername = CERT_GetCommonName(&cert->subject);
   664   if (!servername) {
   665     // Certs without common names are strange, but they do exist...
   666     // Let's try to use another string for the nickname
   667     servername = CERT_GetOrgUnitName(&cert->subject);
   668     if (!servername) {
   669       servername = CERT_GetOrgName(&cert->subject);
   670       if (!servername) {
   671         servername = CERT_GetLocalityName(&cert->subject);
   672         if (!servername) {
   673           servername = CERT_GetStateName(&cert->subject);
   674           if (!servername) {
   675             servername = CERT_GetCountryName(&cert->subject);
   676             if (!servername) {
   677               // We tried hard, there is nothing more we can do.
   678               // A cert without any names doesn't really make sense.
   679               return nullptr;
   680             }
   681           }
   682         }
   683       }
   684     }
   685   }
   687   count = 1;
   688   while (1) {
   689     if (count == 1) {
   690       nickname = PR_smprintf("%s", servername);
   691     }
   692     else {
   693       nickname = PR_smprintf("%s #%d", servername, count);
   694     }
   695     if (!nickname) {
   696       break;
   697     }
   699     conflict = SEC_CertNicknameConflict(nickname, &cert->derSubject,
   700                                         cert->dbhandle);
   701     if (!conflict) {
   702       break;
   703     }
   704     PR_Free(nickname);
   705     count++;
   706   }
   707   PR_FREEIF(servername);
   708   return nickname;
   709 }
   711 void
   712 SaveIntermediateCerts(const ScopedCERTCertList& certList)
   713 {
   714   if (!certList) {
   715     return;
   716   }
   718   bool isEndEntity = true;
   719   for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
   720         !CERT_LIST_END(node, certList);
   721         node = CERT_LIST_NEXT(node)) {
   722     if (isEndEntity) {
   723       // Skip the end-entity; we only want to store intermediates
   724       isEndEntity = false;
   725       continue;
   726     }
   728     if (node->cert->slot) {
   729       // This cert was found on a token, no need to remember it in the temp db.
   730       continue;
   731     }
   733     if (node->cert->isperm) {
   734       // We don't need to remember certs already stored in perm db.
   735       continue;
   736     }
   738     // We have found a signer cert that we want to remember.
   739     char* nickname = DefaultServerNicknameForCert(node->cert);
   740     if (nickname && *nickname) {
   741       ScopedPtr<PK11SlotInfo, PK11_FreeSlot> slot(PK11_GetInternalKeySlot());
   742       if (slot) {
   743         PK11_ImportCert(slot.get(), node->cert, CK_INVALID_HANDLE,
   744                         nickname, false);
   745       }
   746     }
   747     PR_FREEIF(nickname);
   748   }
   749 }
   751 } } // namespace mozilla::psm

mercurial