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(), ¬Before, ¬After);
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
|