1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/certverifier/NSSCertDBTrustDomain.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,751 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "NSSCertDBTrustDomain.h" 1.11 + 1.12 +#include <stdint.h> 1.13 + 1.14 +#include "ExtendedValidation.h" 1.15 +#include "OCSPRequestor.h" 1.16 +#include "certdb.h" 1.17 +#include "mozilla/Telemetry.h" 1.18 +#include "nss.h" 1.19 +#include "ocsp.h" 1.20 +#include "pk11pub.h" 1.21 +#include "pkix/pkix.h" 1.22 +#include "prerror.h" 1.23 +#include "prmem.h" 1.24 +#include "prprf.h" 1.25 +#include "secerr.h" 1.26 +#include "secmod.h" 1.27 + 1.28 +using namespace mozilla::pkix; 1.29 + 1.30 +#ifdef PR_LOGGING 1.31 +extern PRLogModuleInfo* gCertVerifierLog; 1.32 +#endif 1.33 + 1.34 +namespace mozilla { namespace psm { 1.35 + 1.36 +const char BUILTIN_ROOTS_MODULE_DEFAULT_NAME[] = "Builtin Roots Module"; 1.37 + 1.38 +void PORT_Free_string(char* str) { PORT_Free(str); } 1.39 + 1.40 +namespace { 1.41 + 1.42 +typedef ScopedPtr<SECMODModule, SECMOD_DestroyModule> ScopedSECMODModule; 1.43 + 1.44 +} // unnamed namespace 1.45 + 1.46 +NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType, 1.47 + OCSPFetching ocspFetching, 1.48 + OCSPCache& ocspCache, 1.49 + void* pinArg, 1.50 + CERTChainVerifyCallback* checkChainCallback) 1.51 + : mCertDBTrustType(certDBTrustType) 1.52 + , mOCSPFetching(ocspFetching) 1.53 + , mOCSPCache(ocspCache) 1.54 + , mPinArg(pinArg) 1.55 + , mCheckChainCallback(checkChainCallback) 1.56 +{ 1.57 +} 1.58 + 1.59 +SECStatus 1.60 +NSSCertDBTrustDomain::FindPotentialIssuers( 1.61 + const SECItem* encodedIssuerName, PRTime time, 1.62 + /*out*/ mozilla::pkix::ScopedCERTCertList& results) 1.63 +{ 1.64 + // TODO: normalize encodedIssuerName 1.65 + // TODO: NSS seems to be ambiguous between "no potential issuers found" and 1.66 + // "there was an error trying to retrieve the potential issuers." 1.67 + results = CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(), 1.68 + encodedIssuerName, time, true); 1.69 + return SECSuccess; 1.70 +} 1.71 + 1.72 +SECStatus 1.73 +NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA, 1.74 + SECOidTag policy, 1.75 + const CERTCertificate* candidateCert, 1.76 + /*out*/ TrustLevel* trustLevel) 1.77 +{ 1.78 + PR_ASSERT(candidateCert); 1.79 + PR_ASSERT(trustLevel); 1.80 + 1.81 + if (!candidateCert || !trustLevel) { 1.82 + PR_SetError(SEC_ERROR_INVALID_ARGS, 0); 1.83 + return SECFailure; 1.84 + } 1.85 + 1.86 +#ifdef MOZ_NO_EV_CERTS 1.87 + if (policy != SEC_OID_X509_ANY_POLICY) { 1.88 + PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0); 1.89 + return SECFailure; 1.90 + } 1.91 +#endif 1.92 + 1.93 + // XXX: CERT_GetCertTrust seems to be abusing SECStatus as a boolean, where 1.94 + // SECSuccess means that there is a trust record and SECFailure means there 1.95 + // is not a trust record. I looked at NSS's internal uses of 1.96 + // CERT_GetCertTrust, and all that code uses the result as a boolean meaning 1.97 + // "We have a trust record." 1.98 + CERTCertTrust trust; 1.99 + if (CERT_GetCertTrust(candidateCert, &trust) == SECSuccess) { 1.100 + PRUint32 flags = SEC_GET_TRUST_FLAGS(&trust, mCertDBTrustType); 1.101 + 1.102 + // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit, 1.103 + // because we can have active distrust for either type of cert. Note that 1.104 + // CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so if the 1.105 + // relevant trust bit isn't set then that means the cert must be considered 1.106 + // distrusted. 1.107 + PRUint32 relevantTrustBit = endEntityOrCA == MustBeCA ? CERTDB_TRUSTED_CA 1.108 + : CERTDB_TRUSTED; 1.109 + if (((flags & (relevantTrustBit|CERTDB_TERMINAL_RECORD))) 1.110 + == CERTDB_TERMINAL_RECORD) { 1.111 + *trustLevel = ActivelyDistrusted; 1.112 + return SECSuccess; 1.113 + } 1.114 + 1.115 + // For TRUST, we only use the CERTDB_TRUSTED_CA bit, because Gecko hasn't 1.116 + // needed to consider end-entity certs to be their own trust anchors since 1.117 + // Gecko implemented nsICertOverrideService. 1.118 + if (flags & CERTDB_TRUSTED_CA) { 1.119 + if (policy == SEC_OID_X509_ANY_POLICY) { 1.120 + *trustLevel = TrustAnchor; 1.121 + return SECSuccess; 1.122 + } 1.123 +#ifndef MOZ_NO_EV_CERTS 1.124 + if (CertIsAuthoritativeForEVPolicy(candidateCert, policy)) { 1.125 + *trustLevel = TrustAnchor; 1.126 + return SECSuccess; 1.127 + } 1.128 +#endif 1.129 + } 1.130 + } 1.131 + 1.132 + *trustLevel = InheritsTrust; 1.133 + return SECSuccess; 1.134 +} 1.135 + 1.136 +SECStatus 1.137 +NSSCertDBTrustDomain::VerifySignedData(const CERTSignedData* signedData, 1.138 + const CERTCertificate* cert) 1.139 +{ 1.140 + return ::mozilla::pkix::VerifySignedData(signedData, cert, mPinArg); 1.141 +} 1.142 + 1.143 +static PRIntervalTime 1.144 +OCSPFetchingTypeToTimeoutTime(NSSCertDBTrustDomain::OCSPFetching ocspFetching) 1.145 +{ 1.146 + switch (ocspFetching) { 1.147 + case NSSCertDBTrustDomain::FetchOCSPForDVSoftFail: 1.148 + return PR_SecondsToInterval(2); 1.149 + case NSSCertDBTrustDomain::FetchOCSPForEV: 1.150 + case NSSCertDBTrustDomain::FetchOCSPForDVHardFail: 1.151 + return PR_SecondsToInterval(10); 1.152 + // The rest of these are error cases. Assert in debug builds, but return 1.153 + // the default value corresponding to 2 seconds in release builds. 1.154 + case NSSCertDBTrustDomain::NeverFetchOCSP: 1.155 + case NSSCertDBTrustDomain::LocalOnlyOCSPForEV: 1.156 + PR_NOT_REACHED("we should never see this OCSPFetching type here"); 1.157 + default: 1.158 + PR_NOT_REACHED("we're not handling every OCSPFetching type"); 1.159 + } 1.160 + return PR_SecondsToInterval(2); 1.161 +} 1.162 + 1.163 +SECStatus 1.164 +NSSCertDBTrustDomain::CheckRevocation( 1.165 + mozilla::pkix::EndEntityOrCA endEntityOrCA, 1.166 + const CERTCertificate* cert, 1.167 + /*const*/ CERTCertificate* issuerCert, 1.168 + PRTime time, 1.169 + /*optional*/ const SECItem* stapledOCSPResponse) 1.170 +{ 1.171 + // Actively distrusted certificates will have already been blocked by 1.172 + // GetCertTrust. 1.173 + 1.174 + // TODO: need to verify that IsRevoked isn't called for trust anchors AND 1.175 + // that that fact is documented in mozillapkix. 1.176 + 1.177 + PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, 1.178 + ("NSSCertDBTrustDomain: Top of CheckRevocation\n")); 1.179 + 1.180 + PORT_Assert(cert); 1.181 + PORT_Assert(issuerCert); 1.182 + if (!cert || !issuerCert) { 1.183 + PORT_SetError(SEC_ERROR_INVALID_ARGS); 1.184 + return SECFailure; 1.185 + } 1.186 + 1.187 + // Bug 991815: The BR allow OCSP for intermediates to be up to one year old. 1.188 + // Since this affects EV there is no reason why DV should be more strict 1.189 + // so all intermediatates are allowed to have OCSP responses up to one year 1.190 + // old. 1.191 + uint16_t maxOCSPLifetimeInDays = 10; 1.192 + if (endEntityOrCA == EndEntityOrCA::MustBeCA) { 1.193 + maxOCSPLifetimeInDays = 365; 1.194 + } 1.195 + 1.196 + // If we have a stapled OCSP response then the verification of that response 1.197 + // determines the result unless the OCSP response is expired. We make an 1.198 + // exception for expired responses because some servers, nginx in particular, 1.199 + // are known to serve expired responses due to bugs. 1.200 + // We keep track of the result of verifying the stapled response but don't 1.201 + // immediately return failure if the response has expired. 1.202 + PRErrorCode stapledOCSPResponseErrorCode = 0; 1.203 + if (stapledOCSPResponse) { 1.204 + PR_ASSERT(endEntityOrCA == MustBeEndEntity); 1.205 + bool expired; 1.206 + SECStatus rv = VerifyAndMaybeCacheEncodedOCSPResponse(cert, issuerCert, 1.207 + time, 1.208 + maxOCSPLifetimeInDays, 1.209 + stapledOCSPResponse, 1.210 + ResponseWasStapled, 1.211 + expired); 1.212 + if (rv == SECSuccess) { 1.213 + // stapled OCSP response present and good 1.214 + Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 1); 1.215 + PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, 1.216 + ("NSSCertDBTrustDomain: stapled OCSP response: good")); 1.217 + return rv; 1.218 + } 1.219 + stapledOCSPResponseErrorCode = PR_GetError(); 1.220 + if (stapledOCSPResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE || 1.221 + expired) { 1.222 + // stapled OCSP response present but expired 1.223 + Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 3); 1.224 + PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, 1.225 + ("NSSCertDBTrustDomain: expired stapled OCSP response")); 1.226 + } else { 1.227 + // stapled OCSP response present but invalid for some reason 1.228 + Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 4); 1.229 + PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, 1.230 + ("NSSCertDBTrustDomain: stapled OCSP response: failure")); 1.231 + return rv; 1.232 + } 1.233 + } else { 1.234 + // no stapled OCSP response 1.235 + Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 2); 1.236 + PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, 1.237 + ("NSSCertDBTrustDomain: no stapled OCSP response")); 1.238 + } 1.239 + 1.240 + PRErrorCode cachedResponseErrorCode = 0; 1.241 + PRTime cachedResponseValidThrough = 0; 1.242 + bool cachedResponsePresent = mOCSPCache.Get(cert, issuerCert, 1.243 + cachedResponseErrorCode, 1.244 + cachedResponseValidThrough); 1.245 + if (cachedResponsePresent) { 1.246 + if (cachedResponseErrorCode == 0 && cachedResponseValidThrough >= time) { 1.247 + PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, 1.248 + ("NSSCertDBTrustDomain: cached OCSP response: good")); 1.249 + return SECSuccess; 1.250 + } 1.251 + // If we have a cached revoked response, use it. 1.252 + if (cachedResponseErrorCode == SEC_ERROR_REVOKED_CERTIFICATE) { 1.253 + PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, 1.254 + ("NSSCertDBTrustDomain: cached OCSP response: revoked")); 1.255 + PR_SetError(SEC_ERROR_REVOKED_CERTIFICATE, 0); 1.256 + return SECFailure; 1.257 + } 1.258 + // The cached response may indicate an unknown certificate or it may be 1.259 + // expired. Don't return with either of these statuses yet - we may be 1.260 + // able to fetch a more recent one. 1.261 + PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, 1.262 + ("NSSCertDBTrustDomain: cached OCSP response: error %ld valid " 1.263 + "until %lld", cachedResponseErrorCode, cachedResponseValidThrough)); 1.264 + // When a good cached response has expired, it is more convenient 1.265 + // to convert that to an error code and just deal with 1.266 + // cachedResponseErrorCode from here on out. 1.267 + if (cachedResponseErrorCode == 0 && cachedResponseValidThrough < time) { 1.268 + cachedResponseErrorCode = SEC_ERROR_OCSP_OLD_RESPONSE; 1.269 + } 1.270 + // We may have a cached indication of server failure. Ignore it if 1.271 + // it has expired. 1.272 + if (cachedResponseErrorCode != 0 && 1.273 + cachedResponseErrorCode != SEC_ERROR_OCSP_UNKNOWN_CERT && 1.274 + cachedResponseErrorCode != SEC_ERROR_OCSP_OLD_RESPONSE && 1.275 + cachedResponseValidThrough < time) { 1.276 + cachedResponseErrorCode = 0; 1.277 + cachedResponsePresent = false; 1.278 + } 1.279 + } else { 1.280 + PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, 1.281 + ("NSSCertDBTrustDomain: no cached OCSP response")); 1.282 + } 1.283 + // At this point, if and only if cachedErrorResponseCode is 0, there was no 1.284 + // cached response. 1.285 + PR_ASSERT((!cachedResponsePresent && cachedResponseErrorCode == 0) || 1.286 + (cachedResponsePresent && cachedResponseErrorCode != 0)); 1.287 + 1.288 + // TODO: We still need to handle the fallback for expired responses. But, 1.289 + // if/when we disable OCSP fetching by default, it would be ambiguous whether 1.290 + // security.OCSP.enable==0 means "I want the default" or "I really never want 1.291 + // you to ever fetch OCSP." 1.292 + 1.293 + if ((mOCSPFetching == NeverFetchOCSP) || 1.294 + (endEntityOrCA == MustBeCA && (mOCSPFetching == FetchOCSPForDVHardFail || 1.295 + mOCSPFetching == FetchOCSPForDVSoftFail))) { 1.296 + // We're not going to be doing any fetching, so if there was a cached 1.297 + // "unknown" response, say so. 1.298 + if (cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) { 1.299 + PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0); 1.300 + return SECFailure; 1.301 + } 1.302 + // If we're doing hard-fail, we want to know if we have a cached response 1.303 + // that has expired. 1.304 + if (mOCSPFetching == FetchOCSPForDVHardFail && 1.305 + cachedResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE) { 1.306 + PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0); 1.307 + return SECFailure; 1.308 + } 1.309 + 1.310 + return SECSuccess; 1.311 + } 1.312 + 1.313 + if (mOCSPFetching == LocalOnlyOCSPForEV) { 1.314 + PR_SetError(cachedResponseErrorCode != 0 ? cachedResponseErrorCode 1.315 + : SEC_ERROR_OCSP_UNKNOWN_CERT, 0); 1.316 + return SECFailure; 1.317 + } 1.318 + 1.319 + ScopedPtr<char, PORT_Free_string> 1.320 + url(CERT_GetOCSPAuthorityInfoAccessLocation(cert)); 1.321 + 1.322 + if (!url) { 1.323 + if (mOCSPFetching == FetchOCSPForEV || 1.324 + cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) { 1.325 + PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0); 1.326 + return SECFailure; 1.327 + } 1.328 + if (cachedResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE) { 1.329 + PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0); 1.330 + return SECFailure; 1.331 + } 1.332 + if (stapledOCSPResponseErrorCode != 0) { 1.333 + PR_SetError(stapledOCSPResponseErrorCode, 0); 1.334 + return SECFailure; 1.335 + } 1.336 + 1.337 + // Nothing to do if we don't have an OCSP responder URI for the cert; just 1.338 + // assume it is good. Note that this is the confusing, but intended, 1.339 + // interpretation of "strict" revocation checking in the face of a 1.340 + // certificate that lacks an OCSP responder URI. 1.341 + return SECSuccess; 1.342 + } 1.343 + 1.344 + ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); 1.345 + if (!arena) { 1.346 + return SECFailure; 1.347 + } 1.348 + 1.349 + // Only request a response if we didn't have a cached indication of failure 1.350 + // (don't keep requesting responses from a failing server). 1.351 + const SECItem* response = nullptr; 1.352 + if (cachedResponseErrorCode == 0 || 1.353 + cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT || 1.354 + cachedResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE) { 1.355 + const SECItem* request(CreateEncodedOCSPRequest(arena.get(), cert, 1.356 + issuerCert)); 1.357 + if (!request) { 1.358 + return SECFailure; 1.359 + } 1.360 + 1.361 + response = DoOCSPRequest(arena.get(), url.get(), request, 1.362 + OCSPFetchingTypeToTimeoutTime(mOCSPFetching)); 1.363 + } 1.364 + 1.365 + if (!response) { 1.366 + PRErrorCode error = PR_GetError(); 1.367 + if (error == 0) { 1.368 + error = cachedResponseErrorCode; 1.369 + } 1.370 + PRTime timeout = time + ServerFailureDelay; 1.371 + if (mOCSPCache.Put(cert, issuerCert, error, time, timeout) != SECSuccess) { 1.372 + return SECFailure; 1.373 + } 1.374 + PR_SetError(error, 0); 1.375 + if (mOCSPFetching != FetchOCSPForDVSoftFail) { 1.376 + PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, 1.377 + ("NSSCertDBTrustDomain: returning SECFailure after " 1.378 + "OCSP request failure")); 1.379 + return SECFailure; 1.380 + } 1.381 + if (cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) { 1.382 + PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, 1.383 + ("NSSCertDBTrustDomain: returning SECFailure from cached " 1.384 + "response after OCSP request failure")); 1.385 + PR_SetError(cachedResponseErrorCode, 0); 1.386 + return SECFailure; 1.387 + } 1.388 + if (stapledOCSPResponseErrorCode != 0) { 1.389 + PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, 1.390 + ("NSSCertDBTrustDomain: returning SECFailure from expired " 1.391 + "stapled response after OCSP request failure")); 1.392 + PR_SetError(stapledOCSPResponseErrorCode, 0); 1.393 + return SECFailure; 1.394 + } 1.395 + 1.396 + PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, 1.397 + ("NSSCertDBTrustDomain: returning SECSuccess after " 1.398 + "OCSP request failure")); 1.399 + return SECSuccess; // Soft fail -> success :( 1.400 + } 1.401 + 1.402 + // If the response from the network has expired but indicates a revoked 1.403 + // or unknown certificate, PR_GetError() will return the appropriate error. 1.404 + // We actually ignore expired here. 1.405 + bool expired; 1.406 + SECStatus rv = VerifyAndMaybeCacheEncodedOCSPResponse(cert, issuerCert, time, 1.407 + maxOCSPLifetimeInDays, 1.408 + response, 1.409 + ResponseIsFromNetwork, 1.410 + expired); 1.411 + if (rv == SECSuccess || mOCSPFetching != FetchOCSPForDVSoftFail) { 1.412 + PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, 1.413 + ("NSSCertDBTrustDomain: returning after VerifyEncodedOCSPResponse")); 1.414 + return rv; 1.415 + } 1.416 + 1.417 + PRErrorCode error = PR_GetError(); 1.418 + if (error == SEC_ERROR_OCSP_UNKNOWN_CERT || 1.419 + error == SEC_ERROR_REVOKED_CERTIFICATE) { 1.420 + return rv; 1.421 + } 1.422 + 1.423 + if (stapledOCSPResponseErrorCode != 0) { 1.424 + PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, 1.425 + ("NSSCertDBTrustDomain: returning SECFailure from expired stapled " 1.426 + "response after OCSP request verification failure")); 1.427 + PR_SetError(stapledOCSPResponseErrorCode, 0); 1.428 + return SECFailure; 1.429 + } 1.430 + 1.431 + PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, 1.432 + ("NSSCertDBTrustDomain: end of CheckRevocation")); 1.433 + 1.434 + return SECSuccess; // Soft fail -> success :( 1.435 +} 1.436 + 1.437 +SECStatus 1.438 +NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse( 1.439 + const CERTCertificate* cert, CERTCertificate* issuerCert, PRTime time, 1.440 + uint16_t maxLifetimeInDays, const SECItem* encodedResponse, 1.441 + EncodedResponseSource responseSource, /*out*/ bool& expired) 1.442 +{ 1.443 + PRTime thisUpdate = 0; 1.444 + PRTime validThrough = 0; 1.445 + SECStatus rv = VerifyEncodedOCSPResponse(*this, cert, issuerCert, time, 1.446 + maxLifetimeInDays, encodedResponse, 1.447 + expired, &thisUpdate, &validThrough); 1.448 + PRErrorCode error = (rv == SECSuccess ? 0 : PR_GetError()); 1.449 + // If a response was stapled and expired, we don't want to cache it. Return 1.450 + // early to simplify the logic here. 1.451 + if (responseSource == ResponseWasStapled && expired) { 1.452 + PR_ASSERT(rv != SECSuccess); 1.453 + return rv; 1.454 + } 1.455 + // validThrough is only trustworthy if the response successfully verifies 1.456 + // or it indicates a revoked or unknown certificate. 1.457 + // If this isn't the case, store an indication of failure (to prevent 1.458 + // repeatedly requesting a response from a failing server). 1.459 + if (rv != SECSuccess && error != SEC_ERROR_REVOKED_CERTIFICATE && 1.460 + error != SEC_ERROR_OCSP_UNKNOWN_CERT) { 1.461 + validThrough = time + ServerFailureDelay; 1.462 + } 1.463 + if (responseSource == ResponseIsFromNetwork || 1.464 + rv == SECSuccess || 1.465 + error == SEC_ERROR_REVOKED_CERTIFICATE || 1.466 + error == SEC_ERROR_OCSP_UNKNOWN_CERT) { 1.467 + PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, 1.468 + ("NSSCertDBTrustDomain: caching OCSP response")); 1.469 + if (mOCSPCache.Put(cert, issuerCert, error, thisUpdate, validThrough) 1.470 + != SECSuccess) { 1.471 + return SECFailure; 1.472 + } 1.473 + } 1.474 + 1.475 + // If the verification failed, re-set to that original error 1.476 + // (the call to Put may have un-set it). 1.477 + if (rv != SECSuccess) { 1.478 + PR_SetError(error, 0); 1.479 + } 1.480 + return rv; 1.481 +} 1.482 + 1.483 +SECStatus 1.484 +NSSCertDBTrustDomain::IsChainValid(const CERTCertList* certChain) { 1.485 + SECStatus rv = SECFailure; 1.486 + 1.487 + PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, 1.488 + ("NSSCertDBTrustDomain: Top of IsChainValid mCheckCallback=%p", 1.489 + mCheckChainCallback)); 1.490 + 1.491 + if (!mCheckChainCallback) { 1.492 + return SECSuccess; 1.493 + } 1.494 + if (!mCheckChainCallback->isChainValid) { 1.495 + PR_SetError(SEC_ERROR_INVALID_ARGS, 0); 1.496 + return SECFailure; 1.497 + } 1.498 + PRBool chainOK; 1.499 + rv = (mCheckChainCallback->isChainValid)(mCheckChainCallback->isChainValidArg, 1.500 + certChain, &chainOK); 1.501 + if (rv != SECSuccess) { 1.502 + return rv; 1.503 + } 1.504 + // rv = SECSuccess only implies successful call, now is time 1.505 + // to check the chain check status 1.506 + // we should only return success if the chain is valid 1.507 + if (chainOK) { 1.508 + return SECSuccess; 1.509 + } 1.510 + PR_SetError(SEC_ERROR_APPLICATION_CALLBACK_ERROR, 0); 1.511 + return SECFailure; 1.512 +} 1.513 + 1.514 +namespace { 1.515 + 1.516 +static char* 1.517 +nss_addEscape(const char* string, char quote) 1.518 +{ 1.519 + char* newString = 0; 1.520 + int escapes = 0, size = 0; 1.521 + const char* src; 1.522 + char* dest; 1.523 + 1.524 + for (src = string; *src; src++) { 1.525 + if ((*src == quote) || (*src == '\\')) { 1.526 + escapes++; 1.527 + } 1.528 + size++; 1.529 + } 1.530 + 1.531 + newString = (char*) PORT_ZAlloc(escapes + size + 1); 1.532 + if (!newString) { 1.533 + return nullptr; 1.534 + } 1.535 + 1.536 + for (src = string, dest = newString; *src; src++, dest++) { 1.537 + if ((*src == quote) || (*src == '\\')) { 1.538 + *dest++ = '\\'; 1.539 + } 1.540 + *dest = *src; 1.541 + } 1.542 + 1.543 + return newString; 1.544 +} 1.545 + 1.546 +} // unnamed namespace 1.547 + 1.548 +SECStatus 1.549 +InitializeNSS(const char* dir, bool readOnly) 1.550 +{ 1.551 + // The NSS_INIT_NOROOTINIT flag turns off the loading of the root certs 1.552 + // module by NSS_Initialize because we will load it in InstallLoadableRoots 1.553 + // later. It also allows us to work around a bug in the system NSS in 1.554 + // Ubuntu 8.04, which loads any nonexistent "<configdir>/libnssckbi.so" as 1.555 + // "/usr/lib/nss/libnssckbi.so". 1.556 + uint32_t flags = NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE; 1.557 + if (readOnly) { 1.558 + flags |= NSS_INIT_READONLY; 1.559 + } 1.560 + return ::NSS_Initialize(dir, "", "", SECMOD_DB, flags); 1.561 +} 1.562 + 1.563 +void 1.564 +DisableMD5() 1.565 +{ 1.566 + NSS_SetAlgorithmPolicy(SEC_OID_MD5, 1.567 + 0, NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE); 1.568 + NSS_SetAlgorithmPolicy(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, 1.569 + 0, NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE); 1.570 + NSS_SetAlgorithmPolicy(SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC, 1.571 + 0, NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE); 1.572 +} 1.573 + 1.574 +SECStatus 1.575 +LoadLoadableRoots(/*optional*/ const char* dir, const char* modNameUTF8) 1.576 +{ 1.577 + PR_ASSERT(modNameUTF8); 1.578 + 1.579 + if (!modNameUTF8) { 1.580 + PR_SetError(SEC_ERROR_INVALID_ARGS, 0); 1.581 + return SECFailure; 1.582 + } 1.583 + 1.584 + ScopedPtr<char, PR_FreeLibraryName> fullLibraryPath( 1.585 + PR_GetLibraryName(dir, "nssckbi")); 1.586 + if (!fullLibraryPath) { 1.587 + return SECFailure; 1.588 + } 1.589 + 1.590 + ScopedPtr<char, PORT_Free_string> escaped_fullLibraryPath( 1.591 + nss_addEscape(fullLibraryPath.get(), '\"')); 1.592 + if (!escaped_fullLibraryPath) { 1.593 + return SECFailure; 1.594 + } 1.595 + 1.596 + // If a module exists with the same name, delete it. 1.597 + int modType; 1.598 + SECMOD_DeleteModule(modNameUTF8, &modType); 1.599 + 1.600 + ScopedPtr<char, PR_smprintf_free> pkcs11ModuleSpec( 1.601 + PR_smprintf("name=\"%s\" library=\"%s\"", modNameUTF8, 1.602 + escaped_fullLibraryPath.get())); 1.603 + if (!pkcs11ModuleSpec) { 1.604 + return SECFailure; 1.605 + } 1.606 + 1.607 + ScopedSECMODModule rootsModule(SECMOD_LoadUserModule(pkcs11ModuleSpec.get(), 1.608 + nullptr, false)); 1.609 + if (!rootsModule) { 1.610 + return SECFailure; 1.611 + } 1.612 + 1.613 + if (!rootsModule->loaded) { 1.614 + PR_SetError(PR_INVALID_STATE_ERROR, 0); 1.615 + return SECFailure; 1.616 + } 1.617 + 1.618 + return SECSuccess; 1.619 +} 1.620 + 1.621 +void 1.622 +UnloadLoadableRoots(const char* modNameUTF8) 1.623 +{ 1.624 + PR_ASSERT(modNameUTF8); 1.625 + ScopedSECMODModule rootsModule(SECMOD_FindModule(modNameUTF8)); 1.626 + 1.627 + if (rootsModule) { 1.628 + SECMOD_UnloadUserModule(rootsModule.get()); 1.629 + } 1.630 +} 1.631 + 1.632 +void 1.633 +SetClassicOCSPBehavior(CertVerifier::ocsp_download_config enabled, 1.634 + CertVerifier::ocsp_strict_config strict, 1.635 + CertVerifier::ocsp_get_config get) 1.636 +{ 1.637 + CERT_DisableOCSPDefaultResponder(CERT_GetDefaultCertDB()); 1.638 + if (enabled == CertVerifier::ocsp_off) { 1.639 + CERT_DisableOCSPChecking(CERT_GetDefaultCertDB()); 1.640 + } else { 1.641 + CERT_EnableOCSPChecking(CERT_GetDefaultCertDB()); 1.642 + } 1.643 + 1.644 + SEC_OcspFailureMode failureMode = strict == CertVerifier::ocsp_strict 1.645 + ? ocspMode_FailureIsVerificationFailure 1.646 + : ocspMode_FailureIsNotAVerificationFailure; 1.647 + (void) CERT_SetOCSPFailureMode(failureMode); 1.648 + 1.649 + CERT_ForcePostMethodForOCSP(get != CertVerifier::ocsp_get_enabled); 1.650 + 1.651 + int OCSPTimeoutSeconds = 3; 1.652 + if (strict == CertVerifier::ocsp_strict) { 1.653 + OCSPTimeoutSeconds = 10; 1.654 + } 1.655 + CERT_SetOCSPTimeout(OCSPTimeoutSeconds); 1.656 +} 1.657 + 1.658 +char* 1.659 +DefaultServerNicknameForCert(CERTCertificate* cert) 1.660 +{ 1.661 + char* nickname = nullptr; 1.662 + int count; 1.663 + bool conflict; 1.664 + char* servername = nullptr; 1.665 + 1.666 + servername = CERT_GetCommonName(&cert->subject); 1.667 + if (!servername) { 1.668 + // Certs without common names are strange, but they do exist... 1.669 + // Let's try to use another string for the nickname 1.670 + servername = CERT_GetOrgUnitName(&cert->subject); 1.671 + if (!servername) { 1.672 + servername = CERT_GetOrgName(&cert->subject); 1.673 + if (!servername) { 1.674 + servername = CERT_GetLocalityName(&cert->subject); 1.675 + if (!servername) { 1.676 + servername = CERT_GetStateName(&cert->subject); 1.677 + if (!servername) { 1.678 + servername = CERT_GetCountryName(&cert->subject); 1.679 + if (!servername) { 1.680 + // We tried hard, there is nothing more we can do. 1.681 + // A cert without any names doesn't really make sense. 1.682 + return nullptr; 1.683 + } 1.684 + } 1.685 + } 1.686 + } 1.687 + } 1.688 + } 1.689 + 1.690 + count = 1; 1.691 + while (1) { 1.692 + if (count == 1) { 1.693 + nickname = PR_smprintf("%s", servername); 1.694 + } 1.695 + else { 1.696 + nickname = PR_smprintf("%s #%d", servername, count); 1.697 + } 1.698 + if (!nickname) { 1.699 + break; 1.700 + } 1.701 + 1.702 + conflict = SEC_CertNicknameConflict(nickname, &cert->derSubject, 1.703 + cert->dbhandle); 1.704 + if (!conflict) { 1.705 + break; 1.706 + } 1.707 + PR_Free(nickname); 1.708 + count++; 1.709 + } 1.710 + PR_FREEIF(servername); 1.711 + return nickname; 1.712 +} 1.713 + 1.714 +void 1.715 +SaveIntermediateCerts(const ScopedCERTCertList& certList) 1.716 +{ 1.717 + if (!certList) { 1.718 + return; 1.719 + } 1.720 + 1.721 + bool isEndEntity = true; 1.722 + for (CERTCertListNode* node = CERT_LIST_HEAD(certList); 1.723 + !CERT_LIST_END(node, certList); 1.724 + node = CERT_LIST_NEXT(node)) { 1.725 + if (isEndEntity) { 1.726 + // Skip the end-entity; we only want to store intermediates 1.727 + isEndEntity = false; 1.728 + continue; 1.729 + } 1.730 + 1.731 + if (node->cert->slot) { 1.732 + // This cert was found on a token, no need to remember it in the temp db. 1.733 + continue; 1.734 + } 1.735 + 1.736 + if (node->cert->isperm) { 1.737 + // We don't need to remember certs already stored in perm db. 1.738 + continue; 1.739 + } 1.740 + 1.741 + // We have found a signer cert that we want to remember. 1.742 + char* nickname = DefaultServerNicknameForCert(node->cert); 1.743 + if (nickname && *nickname) { 1.744 + ScopedPtr<PK11SlotInfo, PK11_FreeSlot> slot(PK11_GetInternalKeySlot()); 1.745 + if (slot) { 1.746 + PK11_ImportCert(slot.get(), node->cert, CK_INVALID_HANDLE, 1.747 + nickname, false); 1.748 + } 1.749 + } 1.750 + PR_FREEIF(nickname); 1.751 + } 1.752 +} 1.753 + 1.754 +} } // namespace mozilla::psm