michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsUsageArrayHelper.h" michael@0: michael@0: #include "mozilla/Assertions.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIDateTimeFormat.h" michael@0: #include "nsDateTimeFormatCID.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsNSSCertificate.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: michael@0: #include "nspr.h" michael@0: #include "secerr.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::psm; michael@0: michael@0: #ifdef PR_LOGGING michael@0: extern PRLogModuleInfo* gPIPNSSLog; michael@0: #endif michael@0: michael@0: static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); // XXX? needed?:: michael@0: michael@0: nsUsageArrayHelper::nsUsageArrayHelper(CERTCertificate *aCert) michael@0: :mCert(aCert) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: defaultcertdb = CERT_GetDefaultCertDB(); michael@0: nssComponent = do_GetService(kNSSComponentCID, &m_rv); michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: // Some validation errors are non-fatal in that, we should keep checking the michael@0: // cert for other usages after receiving them; i.e. they are errors that NSS michael@0: // returns when a certificate isn't valid for a particular usage, but which michael@0: // don't indicate that the certificate is invalid for ANY usage. Others errors michael@0: // (e.g. revocation) are fatal, and we should immediately stop validation of michael@0: // the cert when we encounter them. michael@0: bool michael@0: isFatalError(uint32_t checkResult) michael@0: { michael@0: return checkResult != nsIX509Cert::VERIFIED_OK && michael@0: checkResult != nsIX509Cert::USAGE_NOT_ALLOWED && michael@0: checkResult != nsIX509Cert::ISSUER_NOT_TRUSTED && michael@0: checkResult != nsIX509Cert::ISSUER_UNKNOWN; michael@0: } michael@0: michael@0: } // unnamed namespace michael@0: michael@0: // Validates the certificate for the given usage. If the certificate is valid michael@0: // for the given usage, aCounter is incremented, a string description of the michael@0: // usage is appended to outUsages, and nsNSSCertificate::VERIFIED_OK is michael@0: // returned. Otherwise, if validation failed, one of the other "Constants for michael@0: // certificate verification results" in nsIX509Cert is returned. michael@0: uint32_t michael@0: nsUsageArrayHelper::check(uint32_t previousCheckResult, michael@0: const char *suffix, michael@0: CertVerifier * certVerifier, michael@0: SECCertificateUsage aCertUsage, michael@0: PRTime time, michael@0: CertVerifier::Flags flags, michael@0: uint32_t &aCounter, michael@0: char16_t **outUsages) michael@0: { michael@0: if (!aCertUsage) { michael@0: MOZ_CRASH("caller should have supplied non-zero aCertUsage"); michael@0: } michael@0: michael@0: if (isFatalError(previousCheckResult)) { michael@0: return previousCheckResult; michael@0: } michael@0: michael@0: nsAutoCString typestr; michael@0: switch (aCertUsage) { michael@0: case certificateUsageSSLClient: michael@0: typestr = "VerifySSLClient"; michael@0: break; michael@0: case certificateUsageSSLServer: michael@0: typestr = "VerifySSLServer"; michael@0: break; michael@0: case certificateUsageEmailSigner: michael@0: typestr = "VerifyEmailSigner"; michael@0: break; michael@0: case certificateUsageEmailRecipient: michael@0: typestr = "VerifyEmailRecip"; michael@0: break; michael@0: case certificateUsageObjectSigner: michael@0: typestr = "VerifyObjSign"; michael@0: break; michael@0: case certificateUsageSSLCA: michael@0: typestr = "VerifySSLCA"; michael@0: break; michael@0: case certificateUsageVerifyCA: michael@0: typestr = "VerifyCAVerifier"; michael@0: break; michael@0: case certificateUsageStatusResponder: michael@0: typestr = "VerifyStatusResponder"; michael@0: break; michael@0: default: michael@0: MOZ_CRASH("unknown cert usage passed to check()"); michael@0: } michael@0: michael@0: SECStatus rv = certVerifier->VerifyCert(mCert, aCertUsage, time, michael@0: nullptr /*XXX:wincx*/, michael@0: nullptr /*hostname*/, flags); michael@0: michael@0: if (rv == SECSuccess) { michael@0: typestr.Append(suffix); michael@0: nsAutoString verifyDesc; michael@0: m_rv = nssComponent->GetPIPNSSBundleString(typestr.get(), verifyDesc); michael@0: if (NS_SUCCEEDED(m_rv)) { michael@0: outUsages[aCounter++] = ToNewUnicode(verifyDesc); michael@0: } michael@0: return nsIX509Cert::VERIFIED_OK; michael@0: } michael@0: michael@0: PRErrorCode error = PR_GetError(); michael@0: michael@0: uint32_t result = nsIX509Cert::NOT_VERIFIED_UNKNOWN; michael@0: verifyFailed(&result, error); michael@0: michael@0: // USAGE_NOT_ALLOWED is the weakest non-fatal error; let all other errors michael@0: // override it. michael@0: if (result == nsIX509Cert::USAGE_NOT_ALLOWED && michael@0: previousCheckResult != nsIX509Cert::VERIFIED_OK) { michael@0: result = previousCheckResult; michael@0: } michael@0: michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, michael@0: ("error validating certificate for usage %s: %s (%d) -> %ud \n", michael@0: typestr.get(), PR_ErrorToName(error), (int) error, (int) result)); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: michael@0: // Maps the error code to one of the Constants for certificate verification michael@0: // results" in nsIX509Cert. michael@0: void michael@0: nsUsageArrayHelper::verifyFailed(uint32_t *_verified, int err) michael@0: { michael@0: switch (err) { michael@0: /* For these cases, verify only failed for the particular usage */ michael@0: case SEC_ERROR_INADEQUATE_KEY_USAGE: michael@0: case SEC_ERROR_INADEQUATE_CERT_TYPE: michael@0: case SEC_ERROR_CA_CERT_INVALID: michael@0: *_verified = nsNSSCertificate::USAGE_NOT_ALLOWED; break; michael@0: /* These are the cases that have individual error messages */ michael@0: case SEC_ERROR_REVOKED_CERTIFICATE: michael@0: *_verified = nsNSSCertificate::CERT_REVOKED; break; michael@0: case SEC_ERROR_EXPIRED_CERTIFICATE: michael@0: *_verified = nsNSSCertificate::CERT_EXPIRED; break; michael@0: case SEC_ERROR_UNTRUSTED_CERT: michael@0: *_verified = nsNSSCertificate::CERT_NOT_TRUSTED; break; michael@0: case SEC_ERROR_UNTRUSTED_ISSUER: michael@0: *_verified = nsNSSCertificate::ISSUER_NOT_TRUSTED; break; michael@0: case SEC_ERROR_UNKNOWN_ISSUER: michael@0: *_verified = nsNSSCertificate::ISSUER_UNKNOWN; break; michael@0: case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: michael@0: // XXX are there other error for this? michael@0: *_verified = nsNSSCertificate::INVALID_CA; break; michael@0: case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED: michael@0: *_verified = nsNSSCertificate::SIGNATURE_ALGORITHM_DISABLED; break; michael@0: default: michael@0: *_verified = nsNSSCertificate::NOT_VERIFIED_UNKNOWN; break; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsUsageArrayHelper::GetUsagesArray(const char *suffix, michael@0: bool localOnly, michael@0: uint32_t outArraySize, michael@0: uint32_t *_verified, michael@0: uint32_t *_count, michael@0: char16_t **outUsages) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (NS_FAILED(m_rv)) michael@0: return m_rv; michael@0: michael@0: NS_ENSURE_TRUE(nssComponent, NS_ERROR_NOT_AVAILABLE); michael@0: michael@0: if (outArraySize < max_returned_out_array_size) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: RefPtr certVerifier(GetDefaultCertVerifier()); michael@0: NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED); michael@0: michael@0: uint32_t &count = *_count; michael@0: count = 0; michael@0: michael@0: PRTime now = PR_Now(); michael@0: CertVerifier::Flags flags = localOnly ? CertVerifier::FLAG_LOCAL_ONLY : 0; michael@0: michael@0: // The following list of checks must be < max_returned_out_array_size michael@0: michael@0: uint32_t result; michael@0: result = check(nsIX509Cert::VERIFIED_OK, suffix, certVerifier, michael@0: certificateUsageSSLClient, now, flags, count, outUsages); michael@0: result = check(result, suffix, certVerifier, michael@0: certificateUsageSSLServer, now, flags, count, outUsages); michael@0: result = check(result, suffix, certVerifier, michael@0: certificateUsageEmailSigner, now, flags, count, outUsages); michael@0: result = check(result, suffix, certVerifier, michael@0: certificateUsageEmailRecipient, now, flags, count, outUsages); michael@0: result = check(result, suffix, certVerifier, michael@0: certificateUsageObjectSigner, now, flags, count, outUsages); michael@0: result = check(result, suffix, certVerifier, michael@0: certificateUsageSSLCA, now, flags, count, outUsages); michael@0: result = check(result, suffix, certVerifier, michael@0: certificateUsageStatusResponder, now, flags, count, outUsages); michael@0: michael@0: if (isFatalError(result) || count == 0) { michael@0: MOZ_ASSERT(result != nsIX509Cert::VERIFIED_OK); michael@0: michael@0: // Clear the output usage strings in the case where we encountered a fatal michael@0: // error after we already successfully validated the cert for some usages. michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: delete outUsages[i]; michael@0: outUsages[i] = nullptr; michael@0: } michael@0: count = 0; michael@0: *_verified = result; michael@0: } else { michael@0: *_verified = nsNSSCertificate::VERIFIED_OK; michael@0: } michael@0: return NS_OK; michael@0: }