Coverage Report

Created: 2021-01-13 07:05

/src/botan/src/lib/x509/x509path.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
* X.509 Certificate Path Validation
3
* (C) 2010,2011,2012,2014,2016 Jack Lloyd
4
* (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
5
*
6
* Botan is released under the Simplified BSD License (see license.txt)
7
*/
8
9
#include <botan/x509path.h>
10
#include <botan/x509_ext.h>
11
#include <botan/pk_keys.h>
12
#include <botan/ocsp.h>
13
#include <botan/oids.h>
14
#include <algorithm>
15
#include <chrono>
16
#include <vector>
17
#include <set>
18
#include <string>
19
#include <sstream>
20
21
#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
22
  #include <future>
23
  #include <botan/internal/http_util.h>
24
#endif
25
26
namespace Botan {
27
28
/*
29
* PKIX path validation
30
*/
31
CertificatePathStatusCodes
32
PKIX::check_chain(const std::vector<X509_Certificate>& cert_path,
33
                  std::chrono::system_clock::time_point ref_time,
34
                  const std::string& hostname,
35
                  Usage_Type usage,
36
                  size_t min_signature_algo_strength,
37
                  const std::set<std::string>& trusted_hashes)
38
0
   {
39
0
   if(cert_path.empty())
40
0
      throw Invalid_Argument("PKIX::check_chain cert_path empty");
41
42
0
   const bool self_signed_ee_cert = (cert_path.size() == 1);
43
44
0
   X509_Time validation_time(ref_time);
45
46
0
   CertificatePathStatusCodes cert_status(cert_path.size());
47
48
0
   if(!hostname.empty() && !cert_path[0].matches_dns_name(hostname))
49
0
      cert_status[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH);
50
51
0
   if(!cert_path[0].allowed_usage(usage))
52
0
      cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
53
54
0
   if(cert_path[0].is_CA_cert() == false &&
55
0
      cert_path[0].has_constraints(KEY_CERT_SIGN))
56
0
      {
57
      /*
58
      "If the keyCertSign bit is asserted, then the cA bit in the
59
      basic constraints extension (Section 4.2.1.9) MUST also be
60
      asserted." - RFC 5280
61
62
      We don't bother doing this check on the rest of the path since they
63
      must have the cA bit asserted or the validation will fail anyway.
64
      */
65
0
      cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
66
0
      }
67
68
0
   for(size_t i = 0; i != cert_path.size(); ++i)
69
0
      {
70
0
      std::set<Certificate_Status_Code>& status = cert_status.at(i);
71
72
0
      const bool at_self_signed_root = (i == cert_path.size() - 1);
73
74
0
      const std::optional<X509_Certificate>& subject = cert_path[i];
75
76
0
      const std::optional<X509_Certificate>& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
77
78
0
      if(at_self_signed_root && (issuer->is_self_signed() == false))
79
0
         {
80
0
         status.insert(Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT);
81
0
         }
82
83
0
      if(subject->issuer_dn() != issuer->subject_dn())
84
0
         {
85
0
         status.insert(Certificate_Status_Code::CHAIN_NAME_MISMATCH);
86
0
         }
87
88
      // Check the serial number
89
0
      if(subject->is_serial_negative())
90
0
         {
91
0
         status.insert(Certificate_Status_Code::CERT_SERIAL_NEGATIVE);
92
0
         }
93
94
      // Check the subject's DN components' length
95
96
0
      for(const auto& dn_pair : subject->subject_dn().dn_info())
97
0
         {
98
0
         const size_t dn_ub = X509_DN::lookup_ub(dn_pair.first);
99
         // dn_pair = <OID,str>
100
0
         if(dn_ub > 0 && dn_pair.second.size() > dn_ub)
101
0
            {
102
0
            status.insert(Certificate_Status_Code::DN_TOO_LONG);
103
0
            }
104
0
         }
105
106
      // Check all certs for valid time range
107
0
      if(validation_time < subject->not_before())
108
0
         status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);
109
110
0
      if(validation_time > subject->not_after())
111
0
         status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
112
113
      // Check issuer constraints
114
0
      if(!issuer->is_CA_cert() && !self_signed_ee_cert)
115
0
         status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
116
117
0
      std::unique_ptr<Public_Key> issuer_key(issuer->subject_public_key());
118
119
      // Check the signature algorithm is known
120
0
      if(OIDS::oid2str_or_empty(subject->signature_algorithm().get_oid()).empty())
121
0
         {
122
0
         status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
123
0
         }
124
0
      else
125
0
         {
126
         // only perform the following checks if the signature algorithm is known
127
0
         if(!issuer_key)
128
0
            {
129
0
            status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
130
0
            }
131
0
         else
132
0
            {
133
0
            const Certificate_Status_Code sig_status = subject->verify_signature(*issuer_key);
134
135
0
            if(sig_status != Certificate_Status_Code::VERIFIED)
136
0
               status.insert(sig_status);
137
138
0
            if(issuer_key->estimated_strength() < min_signature_algo_strength)
139
0
               status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
140
0
            }
141
142
         // Ignore untrusted hashes on self-signed roots
143
0
         if(trusted_hashes.size() > 0 && !at_self_signed_root)
144
0
            {
145
0
            if(trusted_hashes.count(subject->hash_used_for_signature()) == 0)
146
0
               status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
147
0
            }
148
0
         }
149
150
      // Check cert extensions
151
152
0
      if(subject->x509_version() == 1)
153
0
         {
154
0
         if(subject->v2_issuer_key_id().empty() == false ||
155
0
            subject->v2_subject_key_id().empty() == false)
156
0
            {
157
0
            status.insert(Certificate_Status_Code::V2_IDENTIFIERS_IN_V1_CERT);
158
0
            }
159
0
         }
160
161
0
      Extensions extensions = subject->v3_extensions();
162
0
      const auto& extensions_vec = extensions.extensions();
163
0
      if(subject->x509_version() < 3 && !extensions_vec.empty())
164
0
         {
165
0
         status.insert(Certificate_Status_Code::EXT_IN_V1_V2_CERT);
166
0
         }
167
0
      for(auto& extension : extensions_vec)
168
0
         {
169
0
         extension.first->validate(*subject, *issuer, cert_path, cert_status, i);
170
0
         }
171
0
      if(extensions.extensions().size() != extensions.get_extension_oids().size())
172
0
         {
173
0
         status.insert(Certificate_Status_Code::DUPLICATE_CERT_EXTENSION);
174
0
         }
175
0
      }
176
177
   // path len check
178
0
   size_t max_path_length = cert_path.size();
179
0
   for(size_t i = cert_path.size() - 1; i > 0 ; --i)
180
0
      {
181
0
      std::set<Certificate_Status_Code>& status = cert_status.at(i);
182
0
      const std::optional<X509_Certificate>& subject = cert_path[i];
183
184
      /*
185
      * If the certificate was not self-issued, verify that max_path_length is
186
      * greater than zero and decrement max_path_length by 1.
187
      */
188
0
      if(subject->subject_dn() != subject->issuer_dn())
189
0
         {
190
0
         if(max_path_length > 0)
191
0
            {
192
0
            --max_path_length;
193
0
            }
194
0
         else
195
0
            {
196
0
            status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);
197
0
            }
198
0
         }
199
200
      /*
201
      * If pathLenConstraint is present in the certificate and is less than max_path_length,
202
      * set max_path_length to the value of pathLenConstraint.
203
      */
204
0
      if(subject->path_limit() != Cert_Extension::NO_CERT_PATH_LIMIT && subject->path_limit() < max_path_length)
205
0
         {
206
0
         max_path_length = subject->path_limit();
207
0
         }
208
0
      }
209
210
0
   return cert_status;
211
0
   }
212
213
CertificatePathStatusCodes
214
PKIX::check_ocsp(const std::vector<X509_Certificate>& cert_path,
215
                 const std::vector<std::optional<OCSP::Response>>& ocsp_responses,
216
                 const std::vector<Certificate_Store*>& trusted_certstores,
217
                 std::chrono::system_clock::time_point ref_time,
218
                 std::chrono::seconds max_ocsp_age)
219
0
   {
220
0
   if(cert_path.empty())
221
0
      throw Invalid_Argument("PKIX::check_ocsp cert_path empty");
222
223
0
   CertificatePathStatusCodes cert_status(cert_path.size() - 1);
224
225
0
   for(size_t i = 0; i != cert_path.size() - 1; ++i)
226
0
      {
227
0
      std::set<Certificate_Status_Code>& status = cert_status.at(i);
228
229
0
      std::optional<X509_Certificate> subject = cert_path.at(i);
230
0
      std::optional<X509_Certificate> ca = cert_path.at(i+1);
231
232
0
      if(i < ocsp_responses.size() && (ocsp_responses.at(i) != std::nullopt)
233
0
            && (ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful))
234
0
         {
235
0
         try
236
0
            {
237
0
            Certificate_Status_Code ocsp_signature_status = ocsp_responses.at(i)->check_signature(trusted_certstores, cert_path);
238
239
0
            if(ocsp_signature_status == Certificate_Status_Code::OCSP_SIGNATURE_OK)
240
0
               {
241
               // Signature ok, so check the claimed status
242
0
               Certificate_Status_Code ocsp_status = ocsp_responses.at(i)->status_for(*ca, *subject, ref_time, max_ocsp_age);
243
0
               status.insert(ocsp_status);
244
0
               }
245
0
            else
246
0
               {
247
               // Some signature problem
248
0
               status.insert(ocsp_signature_status);
249
0
               }
250
0
            }
251
0
         catch(Exception&)
252
0
            {
253
0
            status.insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
254
0
            }
255
0
         }
256
0
      }
257
258
0
   while(cert_status.size() > 0 && cert_status.back().empty())
259
0
      cert_status.pop_back();
260
261
0
   return cert_status;
262
0
   }
263
264
CertificatePathStatusCodes
265
PKIX::check_crl(const std::vector<X509_Certificate>& cert_path,
266
                const std::vector<std::optional<X509_CRL>>& crls,
267
                std::chrono::system_clock::time_point ref_time)
268
0
   {
269
0
   if(cert_path.empty())
270
0
      throw Invalid_Argument("PKIX::check_crl cert_path empty");
271
272
0
   CertificatePathStatusCodes cert_status(cert_path.size());
273
0
   const X509_Time validation_time(ref_time);
274
275
0
   for(size_t i = 0; i != cert_path.size() - 1; ++i)
276
0
      {
277
0
      std::set<Certificate_Status_Code>& status = cert_status.at(i);
278
279
0
      if(i < crls.size() && crls[i].has_value())
280
0
         {
281
0
         std::optional<X509_Certificate> subject = cert_path.at(i);
282
0
         std::optional<X509_Certificate> ca = cert_path.at(i+1);
283
284
0
         if(!ca->allowed_usage(CRL_SIGN))
285
0
            status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
286
287
0
         if(validation_time < crls[i]->this_update())
288
0
            status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);
289
290
0
         if(validation_time > crls[i]->next_update())
291
0
            status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);
292
293
0
         if(crls[i]->check_signature(ca->subject_public_key()) == false)
294
0
            status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);
295
296
0
         status.insert(Certificate_Status_Code::VALID_CRL_CHECKED);
297
298
0
         if(crls[i]->is_revoked(*subject))
299
0
            status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
300
301
0
         std::string dp = subject->crl_distribution_point();
302
0
         if(!dp.empty())
303
0
            {
304
0
            if(dp != crls[i]->crl_issuing_distribution_point())
305
0
               {
306
0
               status.insert(Certificate_Status_Code::NO_MATCHING_CRLDP);
307
0
               }
308
0
            }
309
310
0
         for(const auto& extension : crls[i]->extensions().extensions())
311
0
            {
312
            // XXX this is wrong - the OID might be defined but the extention not full parsed
313
            // for example see #1652
314
315
            // is the extension critical and unknown?
316
0
            if(extension.second && OIDS::oid2str_or_empty(extension.first->oid_of()) == "")
317
0
               {
318
               /* NIST Certificate Path Valiadation Testing document: "When an implementation does not recognize a critical extension in the
319
                * crlExtensions field, it shall assume that identified certificates have been revoked and are no longer valid"
320
                */
321
0
               status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
322
0
               }
323
0
            }
324
325
0
         }
326
0
      }
327
328
0
   while(cert_status.size() > 0 && cert_status.back().empty())
329
0
      cert_status.pop_back();
330
331
0
   return cert_status;
332
0
   }
333
334
CertificatePathStatusCodes
335
PKIX::check_crl(const std::vector<X509_Certificate>& cert_path,
336
                const std::vector<Certificate_Store*>& certstores,
337
                std::chrono::system_clock::time_point ref_time)
338
0
   {
339
0
   if(cert_path.empty())
340
0
      throw Invalid_Argument("PKIX::check_crl cert_path empty");
341
342
0
   if(certstores.empty())
343
0
      throw Invalid_Argument("PKIX::check_crl certstores empty");
344
345
0
   std::vector<std::optional<X509_CRL>> crls(cert_path.size());
346
347
0
   for(size_t i = 0; i != cert_path.size(); ++i)
348
0
      {
349
0
      for(size_t c = 0; c != certstores.size(); ++c)
350
0
         {
351
0
         crls[i] = certstores[c]->find_crl_for(cert_path[i]);
352
0
         if(crls[i])
353
0
            break;
354
0
         }
355
0
      }
356
357
0
   return PKIX::check_crl(cert_path, crls, ref_time);
358
0
   }
359
360
#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
361
362
CertificatePathStatusCodes
363
PKIX::check_ocsp_online(const std::vector<X509_Certificate>& cert_path,
364
                        const std::vector<Certificate_Store*>& trusted_certstores,
365
                        std::chrono::system_clock::time_point ref_time,
366
                        std::chrono::milliseconds timeout,
367
                        bool ocsp_check_intermediate_CAs,
368
                        std::chrono::seconds max_ocsp_age)
369
0
   {
370
0
   if(cert_path.empty())
371
0
      throw Invalid_Argument("PKIX::check_ocsp_online cert_path empty");
372
373
0
   std::vector<std::future<std::optional<OCSP::Response>>> ocsp_response_futures;
374
375
0
   size_t to_ocsp = 1;
376
377
0
   if(ocsp_check_intermediate_CAs)
378
0
      to_ocsp = cert_path.size() - 1;
379
0
   if(cert_path.size() == 1)
380
0
      to_ocsp = 0;
381
382
0
   for(size_t i = 0; i < to_ocsp; ++i)
383
0
      {
384
0
      const std::optional<X509_Certificate>& subject = cert_path.at(i);
385
0
      const std::optional<X509_Certificate>& issuer = cert_path.at(i+1);
386
387
0
      if(subject->ocsp_responder() == "")
388
0
         {
389
0
         ocsp_response_futures.emplace_back(std::async(std::launch::deferred, [&]() -> std::optional<OCSP::Response> {
390
0
                  return OCSP::Response(Certificate_Status_Code::OCSP_NO_REVOCATION_URL);
391
0
                  }));
392
0
         }
393
0
      else
394
0
         {
395
0
         ocsp_response_futures.emplace_back(std::async(std::launch::async, [&]() -> std::optional<OCSP::Response> {
396
0
               OCSP::Request req(*issuer, BigInt::decode(subject->serial_number()));
397
398
0
               HTTP::Response http;
399
0
               try
400
0
                  {
401
0
                  http = HTTP::POST_sync(subject->ocsp_responder(),
402
0
                                                "application/ocsp-request",
403
0
                                                req.BER_encode(),
404
0
                                                /*redirects*/1,
405
0
                                                timeout);
406
0
                  }
407
0
               catch(std::exception&)
408
0
                  {
409
                  // log e.what() ?
410
0
                  }
411
0
               if (http.status_code() != 200)
412
0
                  return OCSP::Response(Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE);
413
               // Check the MIME type?
414
415
0
               return OCSP::Response(http.body());
416
0
               }));
417
0
         }
418
0
      }
419
420
0
   std::vector<std::optional<OCSP::Response>> ocsp_responses;
421
422
0
   for(size_t i = 0; i < ocsp_response_futures.size(); ++i)
423
0
      {
424
0
      ocsp_responses.push_back(ocsp_response_futures[i].get());
425
0
      }
426
427
0
   return PKIX::check_ocsp(cert_path, ocsp_responses, trusted_certstores, ref_time, max_ocsp_age);
428
0
   }
429
430
CertificatePathStatusCodes
431
PKIX::check_crl_online(const std::vector<X509_Certificate>& cert_path,
432
                       const std::vector<Certificate_Store*>& certstores,
433
                       Certificate_Store_In_Memory* crl_store,
434
                       std::chrono::system_clock::time_point ref_time,
435
                       std::chrono::milliseconds timeout)
436
0
   {
437
0
   if(cert_path.empty())
438
0
      throw Invalid_Argument("PKIX::check_crl_online cert_path empty");
439
0
   if(certstores.empty())
440
0
      throw Invalid_Argument("PKIX::check_crl_online certstores empty");
441
442
0
   std::vector<std::future<std::optional<X509_CRL>>> future_crls;
443
0
   std::vector<std::optional<X509_CRL>> crls(cert_path.size());
444
445
0
   for(size_t i = 0; i != cert_path.size(); ++i)
446
0
      {
447
0
      const std::optional<X509_Certificate>& cert = cert_path.at(i);
448
0
      for(size_t c = 0; c != certstores.size(); ++c)
449
0
         {
450
0
         crls[i] = certstores[c]->find_crl_for(*cert);
451
0
         if(crls[i].has_value())
452
0
            break;
453
0
         }
454
455
      // TODO: check if CRL is expired and re-request?
456
457
      // Only request if we don't already have a CRL
458
0
      if(crls[i])
459
0
         {
460
         /*
461
         We already have a CRL, so just insert this empty one to hold a place in the vector
462
         so that indexes match up
463
         */
464
0
         future_crls.emplace_back(std::future<std::optional<X509_CRL>>());
465
0
         }
466
0
      else if(cert->crl_distribution_point() == "")
467
0
         {
468
         // Avoid creating a thread for this case
469
0
         future_crls.emplace_back(std::async(std::launch::deferred, [&]() -> std::optional<X509_CRL> {
470
0
               throw Not_Implemented("No CRL distribution point for this certificate");
471
0
               }));
472
0
         }
473
0
      else
474
0
         {
475
0
         future_crls.emplace_back(std::async(std::launch::async, [&]() -> std::optional<X509_CRL> {
476
0
               auto http = HTTP::GET_sync(cert->crl_distribution_point(),
477
0
                                          /*redirects*/ 1, timeout);
478
479
0
               http.throw_unless_ok();
480
               // check the mime type?
481
0
               return X509_CRL(http.body());
482
0
               }));
483
0
         }
484
0
      }
485
486
0
   for(size_t i = 0; i != future_crls.size(); ++i)
487
0
      {
488
0
      if(future_crls[i].valid())
489
0
         {
490
0
         try
491
0
            {
492
0
            crls[i] = future_crls[i].get();
493
0
            }
494
0
         catch(std::exception&)
495
0
            {
496
            // crls[i] left null
497
            // todo: log exception e.what() ?
498
0
            }
499
0
         }
500
0
      }
501
502
0
   const CertificatePathStatusCodes crl_status = PKIX::check_crl(cert_path, crls, ref_time);
503
504
0
   if(crl_store)
505
0
      {
506
0
      for(size_t i = 0; i != crl_status.size(); ++i)
507
0
         {
508
0
         if(crl_status[i].count(Certificate_Status_Code::VALID_CRL_CHECKED))
509
0
            {
510
            // better be non-null, we supposedly validated it
511
0
            BOTAN_ASSERT_NOMSG(crls[i].has_value());
512
0
            crl_store->add_crl(*crls[i]);
513
0
            }
514
0
         }
515
0
      }
516
517
0
   return crl_status;
518
0
   }
519
520
#endif
521
522
Certificate_Status_Code
523
PKIX::build_certificate_path(std::vector<X509_Certificate>& cert_path,
524
                             const std::vector<Certificate_Store*>& trusted_certstores,
525
                             const X509_Certificate& end_entity,
526
                             const std::vector<X509_Certificate>& end_entity_extra)
527
0
   {
528
0
   if(end_entity.is_self_signed())
529
0
      {
530
0
      return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
531
0
      }
532
533
   /*
534
   * This is an inelegant but functional way of preventing path loops
535
   * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
536
   * fingerprints in the path. If there is a duplicate, we error out.
537
   * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
538
   */
539
0
   std::set<std::string> certs_seen;
540
541
0
   cert_path.push_back(end_entity);
542
0
   certs_seen.insert(end_entity.fingerprint("SHA-256"));
543
544
0
   Certificate_Store_In_Memory ee_extras;
545
0
   for(size_t i = 0; i != end_entity_extra.size(); ++i)
546
0
      ee_extras.add_certificate(end_entity_extra[i]);
547
548
   // iterate until we reach a root or cannot find the issuer
549
0
   for(;;)
550
0
      {
551
0
      const X509_Certificate& last = cert_path.back();
552
0
      const X509_DN issuer_dn = last.issuer_dn();
553
0
      const std::vector<uint8_t> auth_key_id = last.authority_key_id();
554
555
0
      std::optional<X509_Certificate> issuer;
556
0
      bool trusted_issuer = false;
557
558
0
      for(Certificate_Store* store : trusted_certstores)
559
0
         {
560
0
         issuer = store->find_cert(issuer_dn, auth_key_id);
561
0
         if(issuer)
562
0
            {
563
0
            trusted_issuer = true;
564
0
            break;
565
0
            }
566
0
         }
567
568
0
      if(!issuer)
569
0
         {
570
         // fall back to searching supplemental certs
571
0
         issuer = ee_extras.find_cert(issuer_dn, auth_key_id);
572
0
         }
573
574
0
      if(!issuer)
575
0
         return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND;
576
577
0
      const std::string fprint = issuer->fingerprint("SHA-256");
578
579
0
      if(certs_seen.count(fprint) > 0) // already seen?
580
0
         {
581
0
         return Certificate_Status_Code::CERT_CHAIN_LOOP;
582
0
         }
583
584
0
      certs_seen.insert(fprint);
585
0
      cert_path.push_back(*issuer);
586
587
0
      if(issuer->is_self_signed())
588
0
         {
589
0
         if(trusted_issuer)
590
0
            {
591
0
            return Certificate_Status_Code::OK;
592
0
            }
593
0
         else
594
0
            {
595
0
            return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
596
0
            }
597
0
         }
598
0
      }
599
0
   }
600
601
/**
602
 * utilities for PKIX::build_all_certificate_paths
603
 */
604
namespace
605
{
606
// <certificate, trusted?>
607
using cert_maybe_trusted = std::pair<std::optional<X509_Certificate>,bool>;
608
}
609
610
/**
611
 * Build all possible certificate paths from the end certificate to self-signed trusted roots.
612
 *
613
 * All potentially valid paths are put into the cert_paths vector. If no potentially valid paths are found,
614
 * one of the encountered errors is returned arbitrarily.
615
 *
616
 * todo add a path building function that returns detailed information on errors encountered while building
617
 * the potentially numerous path candidates.
618
 *
619
 * Basically, a DFS is performed starting from the end certificate. A stack (vector) serves to control the DFS.
620
 * At the beginning of each iteration, a pair is popped from the stack that contains (1) the next certificate
621
 * to add to the path (2) a bool that indicates if the certificate is part of a trusted certstore. Ideally, we
622
 * follow the unique issuer of the current certificate until a trusted root is reached. However, the issuer DN +
623
 * authority key id need not be unique among the certificates used for building the path. In such a case,
624
 * we consider all the matching issuers by pushing <IssuerCert, trusted?> on the stack for each of them.
625
 *
626
 */
627
Certificate_Status_Code
628
PKIX::build_all_certificate_paths(std::vector<std::vector<X509_Certificate>>& cert_paths_out,
629
                                  const std::vector<Certificate_Store*>& trusted_certstores,
630
                                  const std::optional<X509_Certificate>& end_entity,
631
                                  const std::vector<X509_Certificate>& end_entity_extra)
632
626
   {
633
626
   if(!cert_paths_out.empty())
634
0
      {
635
0
      throw Invalid_Argument("PKIX::build_all_certificate_paths: cert_paths_out must be empty");
636
0
      }
637
638
626
   if(end_entity->is_self_signed())
639
0
      {
640
0
      return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
641
0
      }
642
643
   /*
644
    * Pile up error messages
645
    */
646
626
   std::vector<Certificate_Status_Code> stats;
647
648
626
   Certificate_Store_In_Memory ee_extras;
649
1.29k
   for(size_t i = 0; i != end_entity_extra.size(); ++i)
650
664
      {
651
664
      ee_extras.add_certificate(end_entity_extra[i]);
652
664
      }
653
654
   /*
655
   * This is an inelegant but functional way of preventing path loops
656
   * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
657
   * fingerprints in the path. If there is a duplicate, we error out.
658
   * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
659
   */
660
626
   std::set<std::string> certs_seen;
661
662
   // new certs are added and removed from the path during the DFS
663
   // it is copied into cert_paths_out when we encounter a trusted root
664
626
   std::vector<X509_Certificate> path_so_far;
665
666
   // todo can we assume that the end certificate is not trusted?
667
626
   std::vector<cert_maybe_trusted> stack = { {end_entity, false} };
668
669
11.4k
   while(!stack.empty())
670
10.8k
      {
671
10.8k
      std::optional<X509_Certificate> last = stack.back().first;
672
      // found a deletion marker that guides the DFS, backtracing
673
10.8k
      if(last == std::nullopt)
674
2.37k
         {
675
2.37k
         stack.pop_back();
676
2.37k
         std::string fprint = path_so_far.back().fingerprint("SHA-256");
677
2.37k
         certs_seen.erase(fprint);
678
2.37k
         path_so_far.pop_back();
679
2.37k
         }
680
      // process next cert on the path
681
8.47k
      else
682
8.47k
         {
683
8.47k
         const bool trusted = stack.back().second;
684
8.47k
         stack.pop_back();
685
686
         // certificate already seen?
687
8.47k
         const std::string fprint = last->fingerprint("SHA-256");
688
8.47k
         if(certs_seen.count(fprint) == 1)
689
4.93k
            {
690
4.93k
            stats.push_back(Certificate_Status_Code::CERT_CHAIN_LOOP);
691
            // the current path ended in a loop
692
4.93k
            continue;
693
4.93k
            }
694
695
         // the current path ends here
696
3.54k
         if(last->is_self_signed())
697
142
            {
698
            // found a trust anchor
699
142
            if(trusted)
700
0
               {
701
0
               cert_paths_out.push_back(path_so_far);
702
0
               cert_paths_out.back().push_back(*last);
703
704
0
               continue;
705
0
               }
706
            // found an untrustworthy root
707
142
            else
708
142
               {
709
142
               stats.push_back(Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
710
142
               continue;
711
142
               }
712
3.39k
            }
713
714
3.39k
         const X509_DN issuer_dn = last->issuer_dn();
715
3.39k
         const std::vector<uint8_t> auth_key_id = last->authority_key_id();
716
717
         // search for trusted issuers
718
3.39k
         std::vector<X509_Certificate> trusted_issuers;
719
3.39k
         for(Certificate_Store* store : trusted_certstores)
720
0
            {
721
0
            auto new_issuers = store->find_all_certs(issuer_dn, auth_key_id);
722
0
            trusted_issuers.insert(trusted_issuers.end(), new_issuers.begin(), new_issuers.end());
723
0
            }
724
725
         // search the supplemental certs
726
3.39k
         std::vector<X509_Certificate> misc_issuers =
727
3.39k
            ee_extras.find_all_certs(issuer_dn, auth_key_id);
728
729
         // if we could not find any issuers, the current path ends here
730
3.39k
         if(trusted_issuers.size() + misc_issuers.size() == 0)
731
1.02k
            {
732
1.02k
            stats.push_back(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);
733
1.02k
            continue;
734
1.02k
            }
735
736
         // push the latest certificate onto the path_so_far
737
2.37k
         path_so_far.push_back(*last);
738
2.37k
         certs_seen.emplace(fprint);
739
740
         // push a deletion marker on the stack for backtracing later
741
2.37k
         stack.push_back({std::optional<X509_Certificate>(), false});
742
743
2.37k
         for(const auto& trusted_cert : trusted_issuers)
744
0
            {
745
0
            stack.push_back({trusted_cert,true});
746
0
            }
747
748
2.37k
         for(const auto& misc : misc_issuers)
749
7.84k
            {
750
7.84k
            stack.push_back({misc,false});
751
7.84k
            }
752
2.37k
         }
753
10.8k
      }
754
755
   // could not construct any potentially valid path
756
626
   if(cert_paths_out.empty())
757
626
      {
758
626
      if(stats.empty())
759
0
         throw Internal_Error("X509 path building failed for unknown reasons");
760
626
      else
761
         // arbitrarily return the first error
762
626
         return stats[0];
763
0
      }
764
0
   else
765
0
      {
766
0
      return Certificate_Status_Code::OK;
767
0
      }
768
626
   }
769
770
771
void PKIX::merge_revocation_status(CertificatePathStatusCodes& chain_status,
772
                                   const CertificatePathStatusCodes& crl,
773
                                   const CertificatePathStatusCodes& ocsp,
774
                                   bool require_rev_on_end_entity,
775
                                   bool require_rev_on_intermediates)
776
0
   {
777
0
   if(chain_status.empty())
778
0
      throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty");
779
780
0
   for(size_t i = 0; i != chain_status.size() - 1; ++i)
781
0
      {
782
0
      bool had_crl = false, had_ocsp = false;
783
784
0
      if(i < crl.size() && crl[i].size() > 0)
785
0
         {
786
0
         for(auto&& code : crl[i])
787
0
            {
788
0
            if(code == Certificate_Status_Code::VALID_CRL_CHECKED)
789
0
               {
790
0
               had_crl = true;
791
0
               }
792
0
            chain_status[i].insert(code);
793
0
            }
794
0
         }
795
796
0
      if(i < ocsp.size() && ocsp[i].size() > 0)
797
0
         {
798
0
         for(auto&& code : ocsp[i])
799
0
            {
800
0
            if(code == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
801
0
               code == Certificate_Status_Code::OCSP_NO_REVOCATION_URL ||  // softfail
802
0
               code == Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE) // softfail
803
0
               {
804
0
               had_ocsp = true;
805
0
               }
806
807
0
            chain_status[i].insert(code);
808
0
            }
809
0
         }
810
811
0
      if(had_crl == false && had_ocsp == false)
812
0
         {
813
0
         if((require_rev_on_end_entity && i == 0) ||
814
0
            (require_rev_on_intermediates && i > 0))
815
0
            {
816
0
            chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
817
0
            }
818
0
         }
819
0
      }
820
0
   }
821
822
Certificate_Status_Code PKIX::overall_status(const CertificatePathStatusCodes& cert_status)
823
0
   {
824
0
   if(cert_status.empty())
825
0
      throw Invalid_Argument("PKIX::overall_status empty cert status");
826
827
0
   Certificate_Status_Code overall_status = Certificate_Status_Code::OK;
828
829
   // take the "worst" error as overall
830
0
   for(const std::set<Certificate_Status_Code>& s : cert_status)
831
0
      {
832
0
      if(!s.empty())
833
0
         {
834
0
         auto worst = *s.rbegin();
835
         // Leave informative OCSP/CRL confirmations on cert-level status only
836
0
         if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS && worst > overall_status)
837
0
            {
838
0
            overall_status = worst;
839
0
            }
840
0
         }
841
0
      }
842
0
   return overall_status;
843
0
   }
844
845
Path_Validation_Result x509_path_validate(
846
   const std::vector<X509_Certificate>& end_certs,
847
   const Path_Validation_Restrictions& restrictions,
848
   const std::vector<Certificate_Store*>& trusted_roots,
849
   const std::string& hostname,
850
   Usage_Type usage,
851
   std::chrono::system_clock::time_point ref_time,
852
   std::chrono::milliseconds ocsp_timeout,
853
   const std::vector<std::optional<OCSP::Response>>& ocsp_resp)
854
626
   {
855
626
   if(end_certs.empty())
856
0
      {
857
0
      throw Invalid_Argument("x509_path_validate called with no subjects");
858
0
      }
859
860
626
   X509_Certificate end_entity = end_certs[0];
861
626
   std::vector<X509_Certificate> end_entity_extra;
862
1.29k
   for(size_t i = 1; i < end_certs.size(); ++i)
863
664
      {
864
664
      end_entity_extra.push_back(end_certs[i]);
865
664
      }
866
867
626
   std::vector<std::vector<X509_Certificate>> cert_paths;
868
626
   Certificate_Status_Code path_building_result = PKIX::build_all_certificate_paths(cert_paths, trusted_roots, end_entity, end_entity_extra);
869
870
   // If we cannot successfully build a chain to a trusted self-signed root, stop now
871
626
   if(path_building_result != Certificate_Status_Code::OK)
872
626
      {
873
626
      return Path_Validation_Result(path_building_result);
874
626
      }
875
876
0
   std::vector<Path_Validation_Result> error_results;
877
   // Try validating all the potentially valid paths and return the first one to validate properly
878
0
   for(auto cert_path : cert_paths)
879
0
      {
880
0
      CertificatePathStatusCodes status =
881
0
         PKIX::check_chain(cert_path, ref_time,
882
0
                           hostname, usage,
883
0
                           restrictions.minimum_key_strength(),
884
0
                           restrictions.trusted_hashes());
885
886
0
      CertificatePathStatusCodes crl_status =
887
0
         PKIX::check_crl(cert_path, trusted_roots, ref_time);
888
889
0
      CertificatePathStatusCodes ocsp_status;
890
891
0
      if(ocsp_resp.size() > 0)
892
0
         {
893
0
         ocsp_status = PKIX::check_ocsp(cert_path, ocsp_resp, trusted_roots, ref_time, restrictions.max_ocsp_age());
894
0
         }
895
896
0
      if(ocsp_status.empty() && ocsp_timeout != std::chrono::milliseconds(0))
897
0
         {
898
0
#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL)
899
0
         ocsp_status = PKIX::check_ocsp_online(cert_path, trusted_roots, ref_time,
900
0
                                               ocsp_timeout, restrictions.ocsp_all_intermediates());
901
#else
902
         ocsp_status.resize(1);
903
         ocsp_status[0].insert(Certificate_Status_Code::OCSP_NO_HTTP);
904
#endif
905
0
         }
906
907
0
      PKIX::merge_revocation_status(status, crl_status, ocsp_status,
908
0
                                    restrictions.require_revocation_information(),
909
0
                                    restrictions.ocsp_all_intermediates());
910
911
0
      Path_Validation_Result pvd(status, std::move(cert_path));
912
0
      if(pvd.successful_validation())
913
0
         {
914
0
         return pvd;
915
0
         }
916
0
      else
917
0
         {
918
0
         error_results.push_back(std::move(pvd));
919
0
         }
920
0
      }
921
0
   return error_results[0];
922
0
   }
923
924
Path_Validation_Result x509_path_validate(
925
   const X509_Certificate& end_cert,
926
   const Path_Validation_Restrictions& restrictions,
927
   const std::vector<Certificate_Store*>& trusted_roots,
928
   const std::string& hostname,
929
   Usage_Type usage,
930
   std::chrono::system_clock::time_point when,
931
   std::chrono::milliseconds ocsp_timeout,
932
   const std::vector<std::optional<OCSP::Response>>& ocsp_resp)
933
0
   {
934
0
   std::vector<X509_Certificate> certs;
935
0
   certs.push_back(end_cert);
936
0
   return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
937
0
   }
938
939
Path_Validation_Result x509_path_validate(
940
   const std::vector<X509_Certificate>& end_certs,
941
   const Path_Validation_Restrictions& restrictions,
942
   const Certificate_Store& store,
943
   const std::string& hostname,
944
   Usage_Type usage,
945
   std::chrono::system_clock::time_point when,
946
   std::chrono::milliseconds ocsp_timeout,
947
   const std::vector<std::optional<OCSP::Response>>& ocsp_resp)
948
0
   {
949
0
   std::vector<Certificate_Store*> trusted_roots;
950
0
   trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
951
952
0
   return x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
953
0
   }
954
955
Path_Validation_Result x509_path_validate(
956
   const X509_Certificate& end_cert,
957
   const Path_Validation_Restrictions& restrictions,
958
   const Certificate_Store& store,
959
   const std::string& hostname,
960
   Usage_Type usage,
961
   std::chrono::system_clock::time_point when,
962
   std::chrono::milliseconds ocsp_timeout,
963
   const std::vector<std::optional<OCSP::Response>>& ocsp_resp)
964
0
   {
965
0
   std::vector<X509_Certificate> certs;
966
0
   certs.push_back(end_cert);
967
968
0
   std::vector<Certificate_Store*> trusted_roots;
969
0
   trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
970
971
0
   return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
972
0
   }
973
974
Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev,
975
      size_t key_strength,
976
      bool ocsp_intermediates,
977
      std::chrono::seconds max_ocsp_age) :
978
   m_require_revocation_information(require_rev),
979
   m_ocsp_all_intermediates(ocsp_intermediates),
980
   m_minimum_key_strength(key_strength),
981
   m_max_ocsp_age(max_ocsp_age)
982
626
   {
983
626
   if(key_strength <= 80)
984
0
      { m_trusted_hashes.insert("SHA-160"); }
985
986
626
   m_trusted_hashes.insert("SHA-224");
987
626
   m_trusted_hashes.insert("SHA-256");
988
626
   m_trusted_hashes.insert("SHA-384");
989
626
   m_trusted_hashes.insert("SHA-512");
990
626
   }
991
992
namespace {
993
CertificatePathStatusCodes find_warnings(const CertificatePathStatusCodes& all_statuses)
994
0
   {
995
0
   CertificatePathStatusCodes warnings;
996
0
   for(const auto& status_set_i : all_statuses)
997
0
      {
998
0
      std::set<Certificate_Status_Code> warning_set_i;
999
0
      for(const auto& code : status_set_i)
1000
0
         {
1001
0
         if(code >= Certificate_Status_Code::FIRST_WARNING_STATUS &&
1002
0
            code < Certificate_Status_Code::FIRST_ERROR_STATUS)
1003
0
            {
1004
0
            warning_set_i.insert(code);
1005
0
            }
1006
0
         }
1007
0
      warnings.push_back(warning_set_i);
1008
0
      }
1009
0
   return warnings;
1010
0
   }
1011
}
1012
1013
Path_Validation_Result::Path_Validation_Result(CertificatePathStatusCodes status,
1014
                                               std::vector<X509_Certificate>&& cert_chain) :
1015
   m_all_status(status),
1016
   m_warnings(find_warnings(m_all_status)),
1017
   m_cert_path(cert_chain),
1018
   m_overall(PKIX::overall_status(m_all_status))
1019
0
   {
1020
0
   }
1021
1022
const X509_Certificate& Path_Validation_Result::trust_root() const
1023
0
   {
1024
0
   if(m_cert_path.empty())
1025
0
      throw Invalid_State("Path_Validation_Result::trust_root no path set");
1026
0
   if(result() != Certificate_Status_Code::VERIFIED)
1027
0
      throw Invalid_State("Path_Validation_Result::trust_root meaningless with invalid status");
1028
1029
0
   return m_cert_path[m_cert_path.size()-1];
1030
0
   }
1031
1032
std::set<std::string> Path_Validation_Result::trusted_hashes() const
1033
0
   {
1034
0
   std::set<std::string> hashes;
1035
0
   for(size_t i = 0; i != m_cert_path.size(); ++i)
1036
0
      hashes.insert(m_cert_path[i].hash_used_for_signature());
1037
0
   return hashes;
1038
0
   }
1039
1040
bool Path_Validation_Result::successful_validation() const
1041
626
   {
1042
626
   return (result() == Certificate_Status_Code::VERIFIED ||
1043
626
           result() == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
1044
626
           result() == Certificate_Status_Code::VALID_CRL_CHECKED);
1045
626
   }
1046
1047
bool Path_Validation_Result::no_warnings() const
1048
0
   {
1049
0
   for(auto status_set_i : m_warnings)
1050
0
      if(!status_set_i.empty())
1051
0
         return false;
1052
0
   return true;
1053
0
   }
1054
1055
CertificatePathStatusCodes Path_Validation_Result::warnings() const
1056
0
   {
1057
0
   return m_warnings;
1058
0
   }
1059
1060
std::string Path_Validation_Result::result_string() const
1061
626
   {
1062
626
   return status_string(result());
1063
626
   }
1064
1065
const char* Path_Validation_Result::status_string(Certificate_Status_Code code)
1066
626
   {
1067
626
   if(const char* s = to_string(code))
1068
626
      return s;
1069
1070
0
   return "Unknown error";
1071
0
   }
1072
1073
std::string Path_Validation_Result::warnings_string() const
1074
0
   {
1075
0
   const std::string sep(", ");
1076
0
   std::string res;
1077
0
   for(size_t i = 0; i < m_warnings.size(); i++)
1078
0
      {
1079
0
      for(auto code : m_warnings[i])
1080
0
         res += "[" + std::to_string(i) + "] " + status_string(code) + sep;
1081
0
      }
1082
   // remove last sep
1083
0
   if(res.size() >= sep.size())
1084
0
      res = res.substr(0, res.size() - sep.size());
1085
0
   return res;
1086
0
   }
1087
}