AppTrustDomain.cpp (11879B)
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 "AppTrustDomain.h" 8 9 #include "MainThreadUtils.h" 10 #include "cert_storage/src/cert_storage.h" 11 // FIXME: these two must be included before certdb.h { 12 #include "seccomon.h" 13 #include "certt.h" 14 // } 15 #include "certdb.h" 16 #include "mozilla/Logging.h" 17 #include "mozilla/Preferences.h" 18 #include "mozpkix/pkixnss.h" 19 #include "NSSCertDBTrustDomain.h" 20 #include "nsComponentManagerUtils.h" 21 #include "nsDirectoryServiceUtils.h" 22 #include "nsIContentSignatureVerifier.h" 23 #include "nsIX509CertDB.h" 24 #include "nsNSSCertificate.h" 25 #include "nsNetUtil.h" 26 #include "prerror.h" 27 28 // Generated by gen_cert_header.py, which gets called by the build system. 29 #include "xpcshell.inc" 30 // Add-on signing Certificates 31 #include "addons-public.inc" 32 #include "addons-public-intermediate.inc" 33 #include "addons-stage.inc" 34 #include "addons-stage-intermediate.inc" 35 // Content signature root certificates 36 #include "content-signature-dev.inc" 37 #include "content-signature-local.inc" 38 #include "content-signature-prod.inc" 39 #include "content-signature-stage.inc" 40 41 using namespace mozilla::pkix; 42 43 extern mozilla::LazyLogModule gPIPNSSLog; 44 45 namespace mozilla { 46 namespace psm { 47 48 AppTrustDomain::AppTrustDomain(nsTArray<Span<const uint8_t>>&& collectedCerts) 49 : mIntermediates(std::move(collectedCerts)), 50 mCertBlocklist(do_GetService(NS_CERT_STORAGE_CID)) {} 51 52 nsresult AppTrustDomain::SetTrustedRoot(AppTrustedRoot trustedRoot) { 53 if (!mTrustedRoots.IsEmpty()) { 54 return NS_ERROR_ALREADY_INITIALIZED; 55 } 56 switch (trustedRoot) { 57 case nsIX509CertDB::AppXPCShellRoot: 58 mTrustedRoots.AppendElements(xpcshellRoots, std::size(xpcshellRoots)); 59 break; 60 61 case nsIX509CertDB::AddonsPublicRoot: 62 mTrustedRoots.AppendElements(addonsPublicRoots, 63 std::size(addonsPublicRoots)); 64 break; 65 66 case nsIX509CertDB::AddonsStageRoot: 67 mTrustedRoots.AppendElements(addonsStageRoots, 68 std::size(addonsStageRoots)); 69 break; 70 71 case nsIContentSignatureVerifier::ContentSignatureLocalRoot: 72 mTrustedRoots.AppendElements(contentSignatureLocalRoots, 73 std::size(contentSignatureLocalRoots)); 74 break; 75 76 case nsIContentSignatureVerifier::ContentSignatureProdRoot: 77 mTrustedRoots.AppendElements(contentSignatureProdRoots, 78 std::size(contentSignatureProdRoots)); 79 break; 80 81 case nsIContentSignatureVerifier::ContentSignatureStageRoot: 82 mTrustedRoots.AppendElements(contentSignatureStageRoots, 83 std::size(contentSignatureStageRoots)); 84 break; 85 86 case nsIContentSignatureVerifier::ContentSignatureDevRoot: 87 mTrustedRoots.AppendElements(contentSignatureDevRoots, 88 std::size(contentSignatureDevRoots)); 89 break; 90 91 default: 92 return NS_ERROR_INVALID_ARG; 93 } 94 95 // If we're verifying add-ons signed by our production root, we want to make 96 // sure a valid intermediate certificate is available for path building. 97 // The intermediate bundled with signed XPI files may have expired and be 98 // considered invalid, which can result in bug 1548973. 99 if (trustedRoot == nsIX509CertDB::AddonsPublicRoot) { 100 mAddonsIntermediates.AppendElements(addonsPublicIntermediates, 101 std::size(addonsPublicIntermediates)); 102 } 103 // Similarly to the above logic for production, we hardcode the intermediate 104 // stage certificate here, so that stage is equivalent to production. 105 if (trustedRoot == nsIX509CertDB::AddonsStageRoot) { 106 mAddonsIntermediates.AppendElements(addonsStageIntermediates, 107 std::size(addonsStageIntermediates)); 108 } 109 110 return NS_OK; 111 } 112 113 pkix::Result AppTrustDomain::FindIssuer(Input encodedIssuerName, 114 IssuerChecker& checker, Time) { 115 MOZ_ASSERT(!mTrustedRoots.IsEmpty()); 116 if (mTrustedRoots.IsEmpty()) { 117 return pkix::Result::FATAL_ERROR_INVALID_STATE; 118 } 119 120 nsTArray<Input> candidates; 121 for (const auto& root : mTrustedRoots) { 122 Input rootInput; 123 pkix::Result rv = rootInput.Init(root.Elements(), root.Length()); 124 // This should never fail, since the possible roots are all hard-coded and 125 // they should never be too long. 126 if (rv != Success) { 127 return rv; 128 } 129 candidates.AppendElement(std::move(rootInput)); 130 } 131 for (const auto& intermediate : mAddonsIntermediates) { 132 Input intermediateInput; 133 pkix::Result rv = 134 intermediateInput.Init(intermediate.Elements(), intermediate.Length()); 135 // Again, this should never fail for the same reason as above. 136 if (rv != Success) { 137 return rv; 138 } 139 candidates.AppendElement(std::move(intermediateInput)); 140 } 141 for (const auto& intermediate : mIntermediates) { 142 Input intermediateInput; 143 pkix::Result rv = 144 intermediateInput.Init(intermediate.Elements(), intermediate.Length()); 145 // This is untrusted input, so skip any intermediates that are too large. 146 if (rv != Success) { 147 continue; 148 } 149 candidates.AppendElement(std::move(intermediateInput)); 150 } 151 152 for (const auto& candidate : candidates) { 153 bool keepGoing; 154 pkix::Result rv = checker.Check( 155 candidate, nullptr /*additionalNameConstraints*/, keepGoing); 156 if (rv != Success) { 157 return rv; 158 } 159 if (!keepGoing) { 160 return Success; 161 } 162 } 163 164 // If the above did not succeed in building a verified certificate chain, 165 // fall back to searching for candidates in NSS. This is important in case an 166 // intermediate involved in add-on signing expires before it is replaced. See 167 // bug 1548973. 168 SECItem encodedIssuerNameSECItem = UnsafeMapInputToSECItem(encodedIssuerName); 169 UniqueCERTCertList nssCandidates(CERT_CreateSubjectCertList( 170 nullptr, CERT_GetDefaultCertDB(), &encodedIssuerNameSECItem, 0, false)); 171 if (nssCandidates) { 172 for (CERTCertListNode* n = CERT_LIST_HEAD(nssCandidates); 173 !CERT_LIST_END(n, nssCandidates); n = CERT_LIST_NEXT(n)) { 174 Input certDER; 175 pkix::Result rv = 176 certDER.Init(n->cert->derCert.data, n->cert->derCert.len); 177 if (rv != Success) { 178 continue; // probably too big 179 } 180 181 bool keepGoing; 182 rv = checker.Check(certDER, nullptr /*additionalNameConstraints*/, 183 keepGoing); 184 if (rv != Success) { 185 return rv; 186 } 187 if (!keepGoing) { 188 break; 189 } 190 } 191 } 192 193 return Success; 194 } 195 196 pkix::Result AppTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA, 197 const CertPolicyId& policy, 198 Input candidateCertDER, 199 /*out*/ TrustLevel& trustLevel) { 200 MOZ_ASSERT(policy.IsAnyPolicy()); 201 MOZ_ASSERT(!mTrustedRoots.IsEmpty()); 202 if (!policy.IsAnyPolicy()) { 203 return pkix::Result::FATAL_ERROR_INVALID_ARGS; 204 } 205 if (mTrustedRoots.IsEmpty()) { 206 return pkix::Result::FATAL_ERROR_INVALID_STATE; 207 } 208 209 nsTArray<uint8_t> issuerBytes; 210 nsTArray<uint8_t> serialBytes; 211 nsTArray<uint8_t> subjectBytes; 212 nsTArray<uint8_t> pubKeyBytes; 213 214 pkix::Result result = 215 BuildRevocationCheckArrays(candidateCertDER, endEntityOrCA, issuerBytes, 216 serialBytes, subjectBytes, pubKeyBytes); 217 if (result != Success) { 218 return result; 219 } 220 221 int16_t revocationState; 222 nsresult nsrv = mCertBlocklist->GetRevocationState( 223 issuerBytes, serialBytes, subjectBytes, pubKeyBytes, &revocationState); 224 if (NS_FAILED(nsrv)) { 225 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; 226 } 227 228 if (revocationState == nsICertStorage::STATE_ENFORCE) { 229 return pkix::Result::ERROR_REVOKED_CERTIFICATE; 230 } 231 232 // mTrustedRoots are the only trust anchors for this validation. 233 Span<const uint8_t> candidateCertDERSpan = {candidateCertDER.UnsafeGetData(), 234 candidateCertDER.GetLength()}; 235 for (const auto& trustedRoot : mTrustedRoots) { 236 if (trustedRoot == candidateCertDERSpan) { 237 trustLevel = TrustLevel::TrustAnchor; 238 return Success; 239 } 240 } 241 242 trustLevel = TrustLevel::InheritsTrust; 243 return Success; 244 } 245 246 pkix::Result AppTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg, 247 /*out*/ uint8_t* digestBuf, 248 size_t digestBufLen) { 249 return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen); 250 } 251 252 pkix::Result AppTrustDomain::CheckRevocation(EndEntityOrCA, const CertID&, Time, 253 Duration, 254 /*optional*/ const Input*, 255 /*optional*/ const Input*) { 256 // We don't currently do revocation checking. If we need to distrust an Apps 257 // certificate, we will use the active distrust mechanism. 258 return Success; 259 } 260 261 pkix::Result AppTrustDomain::IsChainValid(const DERArray& certChain, Time time, 262 const CertPolicyId& requiredPolicy) { 263 MOZ_ASSERT(requiredPolicy.IsAnyPolicy()); 264 return Success; 265 } 266 267 pkix::Result AppTrustDomain::CheckSignatureDigestAlgorithm( 268 DigestAlgorithm digestAlg, EndEntityOrCA, Time) { 269 switch (digestAlg) { 270 case DigestAlgorithm::sha256: // fall through 271 case DigestAlgorithm::sha384: // fall through 272 case DigestAlgorithm::sha512: 273 return Success; 274 case DigestAlgorithm::sha1: 275 return pkix::Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; 276 } 277 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; 278 } 279 280 pkix::Result AppTrustDomain::CheckRSAPublicKeyModulusSizeInBits( 281 EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits) { 282 if (modulusSizeInBits < 2048u) { 283 return pkix::Result::ERROR_INADEQUATE_KEY_SIZE; 284 } 285 return Success; 286 } 287 288 pkix::Result AppTrustDomain::VerifyRSAPKCS1SignedData( 289 Input data, DigestAlgorithm digestAlgorithm, Input signature, 290 Input subjectPublicKeyInfo) { 291 // TODO: We should restrict signatures to SHA-256 or better. 292 return VerifyRSAPKCS1SignedDataNSS(data, digestAlgorithm, signature, 293 subjectPublicKeyInfo, nullptr); 294 } 295 296 pkix::Result AppTrustDomain::VerifyRSAPSSSignedData( 297 Input data, DigestAlgorithm digestAlgorithm, Input signature, 298 Input subjectPublicKeyInfo) { 299 return VerifyRSAPSSSignedDataNSS(data, digestAlgorithm, signature, 300 subjectPublicKeyInfo, nullptr); 301 } 302 303 pkix::Result AppTrustDomain::CheckECDSACurveIsAcceptable( 304 EndEntityOrCA /*endEntityOrCA*/, NamedCurve curve) { 305 switch (curve) { 306 case NamedCurve::secp256r1: // fall through 307 case NamedCurve::secp384r1: // fall through 308 case NamedCurve::secp521r1: 309 return Success; 310 } 311 312 return pkix::Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE; 313 } 314 315 pkix::Result AppTrustDomain::VerifyECDSASignedData( 316 Input data, DigestAlgorithm digestAlgorithm, Input signature, 317 Input subjectPublicKeyInfo) { 318 return VerifyECDSASignedDataNSS(data, digestAlgorithm, signature, 319 subjectPublicKeyInfo, nullptr); 320 } 321 322 pkix::Result AppTrustDomain::CheckValidityIsAcceptable( 323 Time /*notBefore*/, Time /*notAfter*/, EndEntityOrCA /*endEntityOrCA*/, 324 KeyPurposeId /*keyPurpose*/) { 325 return Success; 326 } 327 328 void AppTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension /*extension*/, 329 Input /*extensionData*/) {} 330 331 } // namespace psm 332 } // namespace mozilla