security/certverifier/NSSCertDBTrustDomain.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:6302ae005ca1
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/. */
6
7 #include "NSSCertDBTrustDomain.h"
8
9 #include <stdint.h>
10
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"
24
25 using namespace mozilla::pkix;
26
27 #ifdef PR_LOGGING
28 extern PRLogModuleInfo* gCertVerifierLog;
29 #endif
30
31 namespace mozilla { namespace psm {
32
33 const char BUILTIN_ROOTS_MODULE_DEFAULT_NAME[] = "Builtin Roots Module";
34
35 void PORT_Free_string(char* str) { PORT_Free(str); }
36
37 namespace {
38
39 typedef ScopedPtr<SECMODModule, SECMOD_DestroyModule> ScopedSECMODModule;
40
41 } // unnamed namespace
42
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 }
55
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 }
68
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);
77
78 if (!candidateCert || !trustLevel) {
79 PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
80 return SECFailure;
81 }
82
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
89
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);
98
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 }
111
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 }
128
129 *trustLevel = InheritsTrust;
130 return SECSuccess;
131 }
132
133 SECStatus
134 NSSCertDBTrustDomain::VerifySignedData(const CERTSignedData* signedData,
135 const CERTCertificate* cert)
136 {
137 return ::mozilla::pkix::VerifySignedData(signedData, cert, mPinArg);
138 }
139
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 }
159
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.
170
171 // TODO: need to verify that IsRevoked isn't called for trust anchors AND
172 // that that fact is documented in mozillapkix.
173
174 PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
175 ("NSSCertDBTrustDomain: Top of CheckRevocation\n"));
176
177 PORT_Assert(cert);
178 PORT_Assert(issuerCert);
179 if (!cert || !issuerCert) {
180 PORT_SetError(SEC_ERROR_INVALID_ARGS);
181 return SECFailure;
182 }
183
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 }
192
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 }
236
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));
284
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."
289
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 }
306
307 return SECSuccess;
308 }
309
310 if (mOCSPFetching == LocalOnlyOCSPForEV) {
311 PR_SetError(cachedResponseErrorCode != 0 ? cachedResponseErrorCode
312 : SEC_ERROR_OCSP_UNKNOWN_CERT, 0);
313 return SECFailure;
314 }
315
316 ScopedPtr<char, PORT_Free_string>
317 url(CERT_GetOCSPAuthorityInfoAccessLocation(cert));
318
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 }
333
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 }
340
341 ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
342 if (!arena) {
343 return SECFailure;
344 }
345
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 }
357
358 response = DoOCSPRequest(arena.get(), url.get(), request,
359 OCSPFetchingTypeToTimeoutTime(mOCSPFetching));
360 }
361
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 }
392
393 PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
394 ("NSSCertDBTrustDomain: returning SECSuccess after "
395 "OCSP request failure"));
396 return SECSuccess; // Soft fail -> success :(
397 }
398
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 }
413
414 PRErrorCode error = PR_GetError();
415 if (error == SEC_ERROR_OCSP_UNKNOWN_CERT ||
416 error == SEC_ERROR_REVOKED_CERTIFICATE) {
417 return rv;
418 }
419
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 }
427
428 PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
429 ("NSSCertDBTrustDomain: end of CheckRevocation"));
430
431 return SECSuccess; // Soft fail -> success :(
432 }
433
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 }
471
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 }
479
480 SECStatus
481 NSSCertDBTrustDomain::IsChainValid(const CERTCertList* certChain) {
482 SECStatus rv = SECFailure;
483
484 PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
485 ("NSSCertDBTrustDomain: Top of IsChainValid mCheckCallback=%p",
486 mCheckChainCallback));
487
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 }
510
511 namespace {
512
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;
520
521 for (src = string; *src; src++) {
522 if ((*src == quote) || (*src == '\\')) {
523 escapes++;
524 }
525 size++;
526 }
527
528 newString = (char*) PORT_ZAlloc(escapes + size + 1);
529 if (!newString) {
530 return nullptr;
531 }
532
533 for (src = string, dest = newString; *src; src++, dest++) {
534 if ((*src == quote) || (*src == '\\')) {
535 *dest++ = '\\';
536 }
537 *dest = *src;
538 }
539
540 return newString;
541 }
542
543 } // unnamed namespace
544
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 }
559
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 }
570
571 SECStatus
572 LoadLoadableRoots(/*optional*/ const char* dir, const char* modNameUTF8)
573 {
574 PR_ASSERT(modNameUTF8);
575
576 if (!modNameUTF8) {
577 PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
578 return SECFailure;
579 }
580
581 ScopedPtr<char, PR_FreeLibraryName> fullLibraryPath(
582 PR_GetLibraryName(dir, "nssckbi"));
583 if (!fullLibraryPath) {
584 return SECFailure;
585 }
586
587 ScopedPtr<char, PORT_Free_string> escaped_fullLibraryPath(
588 nss_addEscape(fullLibraryPath.get(), '\"'));
589 if (!escaped_fullLibraryPath) {
590 return SECFailure;
591 }
592
593 // If a module exists with the same name, delete it.
594 int modType;
595 SECMOD_DeleteModule(modNameUTF8, &modType);
596
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 }
603
604 ScopedSECMODModule rootsModule(SECMOD_LoadUserModule(pkcs11ModuleSpec.get(),
605 nullptr, false));
606 if (!rootsModule) {
607 return SECFailure;
608 }
609
610 if (!rootsModule->loaded) {
611 PR_SetError(PR_INVALID_STATE_ERROR, 0);
612 return SECFailure;
613 }
614
615 return SECSuccess;
616 }
617
618 void
619 UnloadLoadableRoots(const char* modNameUTF8)
620 {
621 PR_ASSERT(modNameUTF8);
622 ScopedSECMODModule rootsModule(SECMOD_FindModule(modNameUTF8));
623
624 if (rootsModule) {
625 SECMOD_UnloadUserModule(rootsModule.get());
626 }
627 }
628
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 }
640
641 SEC_OcspFailureMode failureMode = strict == CertVerifier::ocsp_strict
642 ? ocspMode_FailureIsVerificationFailure
643 : ocspMode_FailureIsNotAVerificationFailure;
644 (void) CERT_SetOCSPFailureMode(failureMode);
645
646 CERT_ForcePostMethodForOCSP(get != CertVerifier::ocsp_get_enabled);
647
648 int OCSPTimeoutSeconds = 3;
649 if (strict == CertVerifier::ocsp_strict) {
650 OCSPTimeoutSeconds = 10;
651 }
652 CERT_SetOCSPTimeout(OCSPTimeoutSeconds);
653 }
654
655 char*
656 DefaultServerNicknameForCert(CERTCertificate* cert)
657 {
658 char* nickname = nullptr;
659 int count;
660 bool conflict;
661 char* servername = nullptr;
662
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 }
686
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 }
698
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 }
710
711 void
712 SaveIntermediateCerts(const ScopedCERTCertList& certList)
713 {
714 if (!certList) {
715 return;
716 }
717
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 }
727
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 }
732
733 if (node->cert->isperm) {
734 // We don't need to remember certs already stored in perm db.
735 continue;
736 }
737
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 }
750
751 } } // namespace mozilla::psm

mercurial