pkixbuild.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  5. */
  6. #include "pkix/pkix.h"
  7. #include "pkix/pkixcheck.h"
  8. #include "pkix/pkixutil.h"
  9. namespace mozilla { namespace pkix {
  10. static Result BuildForward(TrustDomain& trustDomain,
  11. const BackCert& subject,
  12. Time time,
  13. KeyUsage requiredKeyUsageIfPresent,
  14. KeyPurposeId requiredEKUIfPresent,
  15. const CertPolicyId& requiredPolicy,
  16. /*optional*/ const Input* stapledOCSPResponse,
  17. unsigned int subCACount,
  18. unsigned int& buildForwardCallBudget);
  19. TrustDomain::IssuerChecker::IssuerChecker() { }
  20. TrustDomain::IssuerChecker::~IssuerChecker() { }
  21. // The implementation of TrustDomain::IssuerTracker is in a subclass only to
  22. // hide the implementation from external users.
  23. class PathBuildingStep final : public TrustDomain::IssuerChecker
  24. {
  25. public:
  26. PathBuildingStep(TrustDomain& aTrustDomain, const BackCert& aSubject,
  27. Time aTime, KeyPurposeId aRequiredEKUIfPresent,
  28. const CertPolicyId& aRequiredPolicy,
  29. /*optional*/ const Input* aStapledOCSPResponse,
  30. unsigned int aSubCACount, Result aDeferredSubjectError,
  31. unsigned int& aBuildForwardCallBudget)
  32. : trustDomain(aTrustDomain)
  33. , subject(aSubject)
  34. , time(aTime)
  35. , requiredEKUIfPresent(aRequiredEKUIfPresent)
  36. , requiredPolicy(aRequiredPolicy)
  37. , stapledOCSPResponse(aStapledOCSPResponse)
  38. , subCACount(aSubCACount)
  39. , deferredSubjectError(aDeferredSubjectError)
  40. , subjectSignaturePublicKeyAlg(der::PublicKeyAlgorithm::Uninitialized)
  41. , result(Result::FATAL_ERROR_LIBRARY_FAILURE)
  42. , resultWasSet(false)
  43. , buildForwardCallBudget(aBuildForwardCallBudget)
  44. {
  45. }
  46. Result Check(Input potentialIssuerDER,
  47. /*optional*/ const Input* additionalNameConstraints,
  48. /*out*/ bool& keepGoing) override;
  49. Result CheckResult() const;
  50. private:
  51. TrustDomain& trustDomain;
  52. const BackCert& subject;
  53. const Time time;
  54. const KeyPurposeId requiredEKUIfPresent;
  55. const CertPolicyId& requiredPolicy;
  56. /*optional*/ Input const* const stapledOCSPResponse;
  57. const unsigned int subCACount;
  58. const Result deferredSubjectError;
  59. // Initialized lazily.
  60. uint8_t subjectSignatureDigestBuf[MAX_DIGEST_SIZE_IN_BYTES];
  61. der::PublicKeyAlgorithm subjectSignaturePublicKeyAlg;
  62. SignedDigest subjectSignature;
  63. Result RecordResult(Result currentResult, /*out*/ bool& keepGoing);
  64. Result result;
  65. bool resultWasSet;
  66. unsigned int& buildForwardCallBudget;
  67. PathBuildingStep(const PathBuildingStep&) = delete;
  68. void operator=(const PathBuildingStep&) = delete;
  69. };
  70. Result
  71. PathBuildingStep::RecordResult(Result newResult, /*out*/ bool& keepGoing)
  72. {
  73. if (newResult == Result::ERROR_UNTRUSTED_CERT) {
  74. newResult = Result::ERROR_UNTRUSTED_ISSUER;
  75. } else if (newResult == Result::ERROR_EXPIRED_CERTIFICATE) {
  76. newResult = Result::ERROR_EXPIRED_ISSUER_CERTIFICATE;
  77. } else if (newResult == Result::ERROR_NOT_YET_VALID_CERTIFICATE) {
  78. newResult = Result::ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE;
  79. }
  80. if (resultWasSet) {
  81. if (result == Success) {
  82. return NotReached("RecordResult called after finding a chain",
  83. Result::FATAL_ERROR_INVALID_STATE);
  84. }
  85. // If every potential issuer has the same problem (e.g. expired) and/or if
  86. // there is only one bad potential issuer, then return a more specific
  87. // error. Otherwise, punt on trying to decide which error should be
  88. // returned by returning the generic Result::ERROR_UNKNOWN_ISSUER error.
  89. if (newResult != Success && newResult != result) {
  90. newResult = Result::ERROR_UNKNOWN_ISSUER;
  91. }
  92. }
  93. result = newResult;
  94. resultWasSet = true;
  95. keepGoing = result != Success;
  96. return Success;
  97. }
  98. Result
  99. PathBuildingStep::CheckResult() const
  100. {
  101. if (!resultWasSet) {
  102. return Result::ERROR_UNKNOWN_ISSUER;
  103. }
  104. return result;
  105. }
  106. // The code that executes in the inner loop of BuildForward
  107. Result
  108. PathBuildingStep::Check(Input potentialIssuerDER,
  109. /*optional*/ const Input* additionalNameConstraints,
  110. /*out*/ bool& keepGoing)
  111. {
  112. BackCert potentialIssuer(potentialIssuerDER, EndEntityOrCA::MustBeCA,
  113. &subject);
  114. Result rv = potentialIssuer.Init();
  115. if (rv != Success) {
  116. return RecordResult(rv, keepGoing);
  117. }
  118. // Simple TrustDomain::FindIssuers implementations may pass in all possible
  119. // CA certificates without any filtering. Because of this, we don't consider
  120. // a mismatched name to be an error. Instead, we just pretend that any
  121. // certificate without a matching name was never passed to us. In particular,
  122. // we treat the case where the TrustDomain only asks us to check CA
  123. // certificates with mismatched names as equivalent to the case where the
  124. // TrustDomain never called Check() at all.
  125. if (!InputsAreEqual(potentialIssuer.GetSubject(), subject.GetIssuer())) {
  126. keepGoing = true;
  127. return Success;
  128. }
  129. // Loop prevention, done as recommended by RFC4158 Section 5.2
  130. // TODO: this doesn't account for subjectAltNames!
  131. // TODO(perf): This probably can and should be optimized in some way.
  132. for (const BackCert* prev = potentialIssuer.childCert; prev;
  133. prev = prev->childCert) {
  134. if (InputsAreEqual(potentialIssuer.GetSubjectPublicKeyInfo(),
  135. prev->GetSubjectPublicKeyInfo()) &&
  136. InputsAreEqual(potentialIssuer.GetSubject(), prev->GetSubject())) {
  137. // XXX: error code
  138. return RecordResult(Result::ERROR_UNKNOWN_ISSUER, keepGoing);
  139. }
  140. }
  141. if (potentialIssuer.GetNameConstraints()) {
  142. rv = CheckNameConstraints(*potentialIssuer.GetNameConstraints(),
  143. subject, requiredEKUIfPresent);
  144. if (rv != Success) {
  145. return RecordResult(rv, keepGoing);
  146. }
  147. }
  148. if (additionalNameConstraints) {
  149. rv = CheckNameConstraints(*additionalNameConstraints, subject,
  150. requiredEKUIfPresent);
  151. if (rv != Success) {
  152. return RecordResult(rv, keepGoing);
  153. }
  154. }
  155. rv = CheckTLSFeatures(subject, potentialIssuer);
  156. if (rv != Success) {
  157. return RecordResult(rv, keepGoing);
  158. }
  159. // If we've ran out of budget, stop searching.
  160. if (buildForwardCallBudget == 0) {
  161. Result savedRv = RecordResult(Result::ERROR_UNKNOWN_ISSUER, keepGoing);
  162. keepGoing = false;
  163. return savedRv;
  164. }
  165. buildForwardCallBudget--;
  166. // RFC 5280, Section 4.2.1.3: "If the keyUsage extension is present, then the
  167. // subject public key MUST NOT be used to verify signatures on certificates
  168. // or CRLs unless the corresponding keyCertSign or cRLSign bit is set."
  169. rv = BuildForward(trustDomain, potentialIssuer, time, KeyUsage::keyCertSign,
  170. requiredEKUIfPresent, requiredPolicy, nullptr, subCACount,
  171. buildForwardCallBudget);
  172. if (rv != Success) {
  173. return RecordResult(rv, keepGoing);
  174. }
  175. // Calculate the digest of the subject's signed data if we haven't already
  176. // done so. We do this lazily to avoid doing it at all if we backtrack before
  177. // getting to this point. We cache the result to avoid recalculating it if we
  178. // backtrack after getting to this point.
  179. if (subjectSignature.digest.GetLength() == 0) {
  180. rv = DigestSignedData(trustDomain, subject.GetSignedData(),
  181. subjectSignatureDigestBuf,
  182. subjectSignaturePublicKeyAlg, subjectSignature);
  183. if (rv != Success) {
  184. return rv;
  185. }
  186. }
  187. rv = VerifySignedDigest(trustDomain, subjectSignaturePublicKeyAlg,
  188. subjectSignature,
  189. potentialIssuer.GetSubjectPublicKeyInfo());
  190. if (rv != Success) {
  191. return RecordResult(rv, keepGoing);
  192. }
  193. // We avoid doing revocation checking for expired certificates because OCSP
  194. // responders are allowed to forget about expired certificates, and many OCSP
  195. // responders return an error when asked for the status of an expired
  196. // certificate.
  197. if (deferredSubjectError != Result::ERROR_EXPIRED_CERTIFICATE) {
  198. CertID certID(subject.GetIssuer(), potentialIssuer.GetSubjectPublicKeyInfo(),
  199. subject.GetSerialNumber());
  200. Time notBefore(Time::uninitialized);
  201. Time notAfter(Time::uninitialized);
  202. // This should never fail. If we're here, we've already parsed the validity
  203. // and checked that the given time is in the certificate's validity period.
  204. rv = ParseValidity(subject.GetValidity(), &notBefore, &notAfter);
  205. if (rv != Success) {
  206. return rv;
  207. }
  208. Duration validityDuration(notAfter, notBefore);
  209. rv = trustDomain.CheckRevocation(subject.endEntityOrCA, certID, time,
  210. validityDuration, stapledOCSPResponse,
  211. subject.GetAuthorityInfoAccess(),
  212. subject.GetSignedCertificateTimestamps());
  213. if (rv != Success) {
  214. // Since this is actually a problem with the current subject certificate
  215. // (rather than the issuer), it doesn't make sense to keep going; all
  216. // paths through this certificate will fail.
  217. Result savedRv = RecordResult(rv, keepGoing);
  218. keepGoing = false;
  219. return savedRv;
  220. }
  221. if (subject.endEntityOrCA == EndEntityOrCA::MustBeEndEntity) {
  222. const Input* sctExtension = subject.GetSignedCertificateTimestamps();
  223. if (sctExtension) {
  224. Input sctList;
  225. rv = ExtractSignedCertificateTimestampListFromExtension(*sctExtension,
  226. sctList);
  227. if (rv != Success) {
  228. // Again, the problem is with this certificate, and all paths through
  229. // it will fail.
  230. Result savedRv = RecordResult(rv, keepGoing);
  231. keepGoing = false;
  232. return savedRv;
  233. }
  234. trustDomain.NoteAuxiliaryExtension(AuxiliaryExtension::EmbeddedSCTList,
  235. sctList);
  236. }
  237. }
  238. }
  239. return RecordResult(Success, keepGoing);
  240. }
  241. // Recursively build the path from the given subject certificate to the root.
  242. //
  243. // Be very careful about changing the order of checks. The order is significant
  244. // because it affects which error we return when a certificate or certificate
  245. // chain has multiple problems. See the error ranking documentation in
  246. // pkix/pkix.h.
  247. static Result
  248. BuildForward(TrustDomain& trustDomain,
  249. const BackCert& subject,
  250. Time time,
  251. KeyUsage requiredKeyUsageIfPresent,
  252. KeyPurposeId requiredEKUIfPresent,
  253. const CertPolicyId& requiredPolicy,
  254. /*optional*/ const Input* stapledOCSPResponse,
  255. unsigned int subCACount,
  256. unsigned int& buildForwardCallBudget)
  257. {
  258. Result rv;
  259. TrustLevel trustLevel;
  260. // If this is an end-entity and not a trust anchor, we defer reporting
  261. // any error found here until after attempting to find a valid chain.
  262. // See the explanation of error prioritization in pkix.h.
  263. rv = CheckIssuerIndependentProperties(trustDomain, subject, time,
  264. requiredKeyUsageIfPresent,
  265. requiredEKUIfPresent, requiredPolicy,
  266. subCACount, trustLevel);
  267. Result deferredEndEntityError = Success;
  268. if (rv != Success) {
  269. if (subject.endEntityOrCA == EndEntityOrCA::MustBeEndEntity &&
  270. trustLevel != TrustLevel::TrustAnchor) {
  271. deferredEndEntityError = rv;
  272. } else {
  273. return rv;
  274. }
  275. }
  276. if (trustLevel == TrustLevel::TrustAnchor) {
  277. // End of the recursion.
  278. NonOwningDERArray chain;
  279. for (const BackCert* cert = &subject; cert; cert = cert->childCert) {
  280. rv = chain.Append(cert->GetDER());
  281. if (rv != Success) {
  282. return NotReached("NonOwningDERArray::SetItem failed.", rv);
  283. }
  284. }
  285. // This must be done here, after the chain is built but before any
  286. // revocation checks have been done.
  287. return trustDomain.IsChainValid(chain, time, requiredPolicy);
  288. }
  289. if (subject.endEntityOrCA == EndEntityOrCA::MustBeCA) {
  290. // Avoid stack overflows and poor performance by limiting cert chain
  291. // length.
  292. static const unsigned int MAX_SUBCA_COUNT = 6;
  293. static_assert(1/*end-entity*/ + MAX_SUBCA_COUNT + 1/*root*/ ==
  294. NonOwningDERArray::MAX_LENGTH,
  295. "MAX_SUBCA_COUNT and NonOwningDERArray::MAX_LENGTH mismatch.");
  296. if (subCACount >= MAX_SUBCA_COUNT) {
  297. return Result::ERROR_UNKNOWN_ISSUER;
  298. }
  299. ++subCACount;
  300. } else {
  301. assert(subCACount == 0);
  302. }
  303. // Find a trusted issuer.
  304. PathBuildingStep pathBuilder(trustDomain, subject, time,
  305. requiredEKUIfPresent, requiredPolicy,
  306. stapledOCSPResponse, subCACount,
  307. deferredEndEntityError, buildForwardCallBudget);
  308. // TODO(bug 965136): Add SKI/AKI matching optimizations
  309. rv = trustDomain.FindIssuer(subject.GetIssuer(), pathBuilder, time);
  310. if (rv != Success) {
  311. return rv;
  312. }
  313. rv = pathBuilder.CheckResult();
  314. if (rv != Success) {
  315. return rv;
  316. }
  317. // If we found a valid chain but deferred reporting an error with the
  318. // end-entity certificate, report it now.
  319. if (deferredEndEntityError != Success) {
  320. return deferredEndEntityError;
  321. }
  322. // We've built a valid chain from the subject cert up to a trusted root.
  323. return Success;
  324. }
  325. Result
  326. BuildCertChain(TrustDomain& trustDomain, Input certDER,
  327. Time time, EndEntityOrCA endEntityOrCA,
  328. KeyUsage requiredKeyUsageIfPresent,
  329. KeyPurposeId requiredEKUIfPresent,
  330. const CertPolicyId& requiredPolicy,
  331. /*optional*/ const Input* stapledOCSPResponse)
  332. {
  333. // XXX: Support the legacy use of the subject CN field for indicating the
  334. // domain name the certificate is valid for.
  335. BackCert cert(certDER, endEntityOrCA, nullptr);
  336. Result rv = cert.Init();
  337. if (rv != Success) {
  338. return rv;
  339. }
  340. // See bug 1056341 for context. If mozilla::pkix is being used in an
  341. // environment where there are many certificates that all have the same
  342. // distinguished name as their subject and issuer (but different SPKIs - see
  343. // the loop prevention as per RFC4158 Section 5.2 in PathBuildingStep::Check),
  344. // the space to search becomes exponential. Because it would be prohibitively
  345. // expensive to explore the entire space, we introduce a budget here that,
  346. // when exhausted, terminates the search with the result
  347. // Result::ERROR_UNKNOWN_ISSUER. Essentially, we limit the total number of
  348. // times `BuildForward` can be called. The current value appears to be a good
  349. // balance between finding a path when one exists (when the space isn't too
  350. // large) and timing out quickly enough when the space is too large or there
  351. // is no valid path to a trust anchor.
  352. unsigned int buildForwardCallBudget = 200000;
  353. return BuildForward(trustDomain, cert, time, requiredKeyUsageIfPresent,
  354. requiredEKUIfPresent, requiredPolicy, stapledOCSPResponse,
  355. 0/*subCACount*/, buildForwardCallBudget);
  356. }
  357. } } // namespace mozilla::pkix