LCOV - code coverage report
Current view: top level - security/pkix/lib - pkixbuild.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 127 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 11 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13