Coverage Report

Created: 2022-06-23 06:44

/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.empty() && !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.empty() && 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()).empty())
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.empty() && 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(auto certstore : certstores)
350
0
         {
351
0
         crls[i] = certstore->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().empty())
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
0
   ocsp_responses.reserve(ocsp_response_futures.size());
422
423
0
   for(auto& ocsp_response_future : ocsp_response_futures)
424
0
      {
425
0
      ocsp_responses.push_back(ocsp_response_future.get());
426
0
      }
427
428
0
   return PKIX::check_ocsp(cert_path, ocsp_responses, trusted_certstores, ref_time, max_ocsp_age);
429
0
   }
430
431
CertificatePathStatusCodes
432
PKIX::check_crl_online(const std::vector<X509_Certificate>& cert_path,
433
                       const std::vector<Certificate_Store*>& certstores,
434
                       Certificate_Store_In_Memory* crl_store,
435
                       std::chrono::system_clock::time_point ref_time,
436
                       std::chrono::milliseconds timeout)
437
0
   {
438
0
   if(cert_path.empty())
439
0
      throw Invalid_Argument("PKIX::check_crl_online cert_path empty");
440
0
   if(certstores.empty())
441
0
      throw Invalid_Argument("PKIX::check_crl_online certstores empty");
442
443
0
   std::vector<std::future<std::optional<X509_CRL>>> future_crls;
444
0
   std::vector<std::optional<X509_CRL>> crls(cert_path.size());
445
446
0
   for(size_t i = 0; i != cert_path.size(); ++i)
447
0
      {
448
0
      const std::optional<X509_Certificate>& cert = cert_path.at(i);
449
0
      for(auto certstore : certstores)
450
0
         {
451
0
         crls[i] = certstore->find_crl_for(*cert);
452
0
         if(crls[i].has_value())
453
0
            break;
454
0
         }
455
456
      // TODO: check if CRL is expired and re-request?
457
458
      // Only request if we don't already have a CRL
459
0
      if(crls[i])
460
0
         {
461
         /*
462
         We already have a CRL, so just insert this empty one to hold a place in the vector
463
         so that indexes match up
464
         */
465
0
         future_crls.emplace_back(std::future<std::optional<X509_CRL>>());
466
0
         }
467
0
      else if(cert->crl_distribution_point().empty())
468
0
         {
469
         // Avoid creating a thread for this case
470
0
         future_crls.emplace_back(std::async(std::launch::deferred, [&]() -> std::optional<X509_CRL> {
471
0
               throw Not_Implemented("No CRL distribution point for this certificate");
472
0
               }));
473
0
         }
474
0
      else
475
0
         {
476
0
         future_crls.emplace_back(std::async(std::launch::async, [&]() -> std::optional<X509_CRL> {
477
0
               auto http = HTTP::GET_sync(cert->crl_distribution_point(),
478
0
                                          /*redirects*/ 1, timeout);
479
480
0
               http.throw_unless_ok();
481
               // check the mime type?
482
0
               return X509_CRL(http.body());
483
0
               }));
484
0
         }
485
0
      }
486
487
0
   for(size_t i = 0; i != future_crls.size(); ++i)
488
0
      {
489
0
      if(future_crls[i].valid())
490
0
         {
491
0
         try
492
0
            {
493
0
            crls[i] = future_crls[i].get();
494
0
            }
495
0
         catch(std::exception&)
496
0
            {
497
            // crls[i] left null
498
            // todo: log exception e.what() ?
499
0
            }
500
0
         }
501
0
      }
502
503
0
   auto crl_status = PKIX::check_crl(cert_path, crls, ref_time);
504
505
0
   if(crl_store)
506
0
      {
507
0
      for(size_t i = 0; i != crl_status.size(); ++i)
508
0
         {
509
0
         if(crl_status[i].count(Certificate_Status_Code::VALID_CRL_CHECKED))
510
0
            {
511
            // better be non-null, we supposedly validated it
512
0
            BOTAN_ASSERT_NOMSG(crls[i].has_value());
513
0
            crl_store->add_crl(*crls[i]);
514
0
            }
515
0
         }
516
0
      }
517
518
0
   return crl_status;
519
0
   }
520
521
#endif
522
523
Certificate_Status_Code
524
PKIX::build_certificate_path(std::vector<X509_Certificate>& cert_path,
525
                             const std::vector<Certificate_Store*>& trusted_certstores,
526
                             const X509_Certificate& end_entity,
527
                             const std::vector<X509_Certificate>& end_entity_extra)
528
0
   {
529
0
   if(end_entity.is_self_signed())
530
0
      {
531
0
      return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
532
0
      }
533
534
   /*
535
   * This is an inelegant but functional way of preventing path loops
536
   * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
537
   * fingerprints in the path. If there is a duplicate, we error out.
538
   * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
539
   */
540
0
   std::set<std::string> certs_seen;
541
542
0
   cert_path.push_back(end_entity);
543
0
   certs_seen.insert(end_entity.fingerprint("SHA-256"));
544
545
0
   Certificate_Store_In_Memory ee_extras;
546
0
   for(const auto& cert : end_entity_extra)
547
0
      ee_extras.add_certificate(cert);
548
549
   // iterate until we reach a root or cannot find the issuer
550
0
   for(;;)
551
0
      {
552
0
      const X509_Certificate& last = cert_path.back();
553
0
      const X509_DN issuer_dn = last.issuer_dn();
554
0
      const std::vector<uint8_t> auth_key_id = last.authority_key_id();
555
556
0
      std::optional<X509_Certificate> issuer;
557
0
      bool trusted_issuer = false;
558
559
0
      for(Certificate_Store* store : trusted_certstores)
560
0
         {
561
0
         issuer = store->find_cert(issuer_dn, auth_key_id);
562
0
         if(issuer)
563
0
            {
564
0
            trusted_issuer = true;
565
0
            break;
566
0
            }
567
0
         }
568
569
0
      if(!issuer)
570
0
         {
571
         // fall back to searching supplemental certs
572
0
         issuer = ee_extras.find_cert(issuer_dn, auth_key_id);
573
0
         }
574
575
0
      if(!issuer)
576
0
         return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND;
577
578
0
      const std::string fprint = issuer->fingerprint("SHA-256");
579
580
0
      if(certs_seen.count(fprint) > 0) // already seen?
581
0
         {
582
0
         return Certificate_Status_Code::CERT_CHAIN_LOOP;
583
0
         }
584
585
0
      certs_seen.insert(fprint);
586
0
      cert_path.push_back(*issuer);
587
588
0
      if(issuer->is_self_signed())
589
0
         {
590
0
         if(trusted_issuer)
591
0
            {
592
0
            return Certificate_Status_Code::OK;
593
0
            }
594
0
         else
595
0
            {
596
0
            return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
597
0
            }
598
0
         }
599
0
      }
600
0
   }
601
602
/**
603
 * utilities for PKIX::build_all_certificate_paths
604
 */
605
namespace
606
{
607
// <certificate, trusted?>
608
using cert_maybe_trusted = std::pair<std::optional<X509_Certificate>,bool>;
609
}
610
611
/**
612
 * Build all possible certificate paths from the end certificate to self-signed trusted roots.
613
 *
614
 * All potentially valid paths are put into the cert_paths vector. If no potentially valid paths are found,
615
 * one of the encountered errors is returned arbitrarily.
616
 *
617
 * todo add a path building function that returns detailed information on errors encountered while building
618
 * the potentially numerous path candidates.
619
 *
620
 * Basically, a DFS is performed starting from the end certificate. A stack (vector) serves to control the DFS.
621
 * At the beginning of each iteration, a pair is popped from the stack that contains (1) the next certificate
622
 * to add to the path (2) a bool that indicates if the certificate is part of a trusted certstore. Ideally, we
623
 * follow the unique issuer of the current certificate until a trusted root is reached. However, the issuer DN +
624
 * authority key id need not be unique among the certificates used for building the path. In such a case,
625
 * we consider all the matching issuers by pushing <IssuerCert, trusted?> on the stack for each of them.
626
 *
627
 */
628
Certificate_Status_Code
629
PKIX::build_all_certificate_paths(std::vector<std::vector<X509_Certificate>>& cert_paths_out,
630
                                  const std::vector<Certificate_Store*>& trusted_certstores,
631
                                  const std::optional<X509_Certificate>& end_entity,
632
                                  const std::vector<X509_Certificate>& end_entity_extra)
633
0
   {
634
0
   if(!cert_paths_out.empty())
635
0
      {
636
0
      throw Invalid_Argument("PKIX::build_all_certificate_paths: cert_paths_out must be empty");
637
0
      }
638
639
0
   if(end_entity->is_self_signed())
640
0
      {
641
0
      return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
642
0
      }
643
644
   /*
645
    * Pile up error messages
646
    */
647
0
   std::vector<Certificate_Status_Code> stats;
648
649
0
   Certificate_Store_In_Memory ee_extras;
650
0
   for(const auto& cert : end_entity_extra)
651
0
      {
652
0
      ee_extras.add_certificate(cert);
653
0
      }
654
655
   /*
656
   * This is an inelegant but functional way of preventing path loops
657
   * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
658
   * fingerprints in the path. If there is a duplicate, we error out.
659
   * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
660
   */
661
0
   std::set<std::string> certs_seen;
662
663
   // new certs are added and removed from the path during the DFS
664
   // it is copied into cert_paths_out when we encounter a trusted root
665
0
   std::vector<X509_Certificate> path_so_far;
666
667
   // todo can we assume that the end certificate is not trusted?
668
0
   std::vector<cert_maybe_trusted> stack = { {end_entity, false} };
669
670
0
   while(!stack.empty())
671
0
      {
672
0
      std::optional<X509_Certificate> last = stack.back().first;
673
      // found a deletion marker that guides the DFS, backtracing
674
0
      if(last == std::nullopt)
675
0
         {
676
0
         stack.pop_back();
677
0
         std::string fprint = path_so_far.back().fingerprint("SHA-256");
678
0
         certs_seen.erase(fprint);
679
0
         path_so_far.pop_back();
680
0
         }
681
      // process next cert on the path
682
0
      else
683
0
         {
684
0
         const bool trusted = stack.back().second;
685
0
         stack.pop_back();
686
687
         // certificate already seen?
688
0
         const std::string fprint = last->fingerprint("SHA-256");
689
0
         if(certs_seen.count(fprint) == 1)
690
0
            {
691
0
            stats.push_back(Certificate_Status_Code::CERT_CHAIN_LOOP);
692
            // the current path ended in a loop
693
0
            continue;
694
0
            }
695
696
         // the current path ends here
697
0
         if(last->is_self_signed())
698
0
            {
699
            // found a trust anchor
700
0
            if(trusted)
701
0
               {
702
0
               cert_paths_out.push_back(path_so_far);
703
0
               cert_paths_out.back().push_back(*last);
704
705
0
               continue;
706
0
               }
707
            // found an untrustworthy root
708
0
            else
709
0
               {
710
0
               stats.push_back(Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
711
0
               continue;
712
0
               }
713
0
            }
714
715
0
         const X509_DN issuer_dn = last->issuer_dn();
716
0
         const std::vector<uint8_t> auth_key_id = last->authority_key_id();
717
718
         // search for trusted issuers
719
0
         std::vector<X509_Certificate> trusted_issuers;
720
0
         for(Certificate_Store* store : trusted_certstores)
721
0
            {
722
0
            auto new_issuers = store->find_all_certs(issuer_dn, auth_key_id);
723
0
            trusted_issuers.insert(trusted_issuers.end(), new_issuers.begin(), new_issuers.end());
724
0
            }
725
726
         // search the supplemental certs
727
0
         std::vector<X509_Certificate> misc_issuers =
728
0
            ee_extras.find_all_certs(issuer_dn, auth_key_id);
729
730
         // if we could not find any issuers, the current path ends here
731
0
         if(trusted_issuers.size() + misc_issuers.size() == 0)
732
0
            {
733
0
            stats.push_back(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);
734
0
            continue;
735
0
            }
736
737
         // push the latest certificate onto the path_so_far
738
0
         path_so_far.push_back(*last);
739
0
         certs_seen.emplace(fprint);
740
741
         // push a deletion marker on the stack for backtracing later
742
0
         stack.push_back({std::optional<X509_Certificate>(), false});
743
744
0
         for(const auto& trusted_cert : trusted_issuers)
745
0
            {
746
0
            stack.push_back({trusted_cert,true});
747
0
            }
748
749
0
         for(const auto& misc : misc_issuers)
750
0
            {
751
0
            stack.push_back({misc,false});
752
0
            }
753
0
         }
754
0
      }
755
756
   // could not construct any potentially valid path
757
0
   if(cert_paths_out.empty())
758
0
      {
759
0
      if(stats.empty())
760
0
         throw Internal_Error("X509 path building failed for unknown reasons");
761
0
      else
762
         // arbitrarily return the first error
763
0
         return stats[0];
764
0
      }
765
0
   else
766
0
      {
767
0
      return Certificate_Status_Code::OK;
768
0
      }
769
0
   }
770
771
772
void PKIX::merge_revocation_status(CertificatePathStatusCodes& chain_status,
773
                                   const CertificatePathStatusCodes& crl,
774
                                   const CertificatePathStatusCodes& ocsp,
775
                                   bool require_rev_on_end_entity,
776
                                   bool require_rev_on_intermediates)
777
0
   {
778
0
   if(chain_status.empty())
779
0
      throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty");
780
781
0
   for(size_t i = 0; i != chain_status.size() - 1; ++i)
782
0
      {
783
0
      bool had_crl = false, had_ocsp = false;
784
785
0
      if(i < crl.size() && !crl[i].empty())
786
0
         {
787
0
         for(auto&& code : crl[i])
788
0
            {
789
0
            if(code == Certificate_Status_Code::VALID_CRL_CHECKED)
790
0
               {
791
0
               had_crl = true;
792
0
               }
793
0
            chain_status[i].insert(code);
794
0
            }
795
0
         }
796
797
0
      if(i < ocsp.size() && !ocsp[i].empty())
798
0
         {
799
0
         for(auto&& code : ocsp[i])
800
0
            {
801
0
            if(code == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
802
0
               code == Certificate_Status_Code::OCSP_NO_REVOCATION_URL ||  // softfail
803
0
               code == Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE) // softfail
804
0
               {
805
0
               had_ocsp = true;
806
0
               }
807
808
0
            chain_status[i].insert(code);
809
0
            }
810
0
         }
811
812
0
      if(had_crl == false && had_ocsp == false)
813
0
         {
814
0
         if((require_rev_on_end_entity && i == 0) ||
815
0
            (require_rev_on_intermediates && i > 0))
816
0
            {
817
0
            chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
818
0
            }
819
0
         }
820
0
      }
821
0
   }
822
823
Certificate_Status_Code PKIX::overall_status(const CertificatePathStatusCodes& cert_status)
824
0
   {
825
0
   if(cert_status.empty())
826
0
      throw Invalid_Argument("PKIX::overall_status empty cert status");
827
828
0
   Certificate_Status_Code overall_status = Certificate_Status_Code::OK;
829
830
   // take the "worst" error as overall
831
0
   for(const std::set<Certificate_Status_Code>& s : cert_status)
832
0
      {
833
0
      if(!s.empty())
834
0
         {
835
0
         auto worst = *s.rbegin();
836
         // Leave informative OCSP/CRL confirmations on cert-level status only
837
0
         if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS && worst > overall_status)
838
0
            {
839
0
            overall_status = worst;
840
0
            }
841
0
         }
842
0
      }
843
0
   return overall_status;
844
0
   }
845
846
Path_Validation_Result x509_path_validate(
847
   const std::vector<X509_Certificate>& end_certs,
848
   const Path_Validation_Restrictions& restrictions,
849
   const std::vector<Certificate_Store*>& trusted_roots,
850
   const std::string& hostname,
851
   Usage_Type usage,
852
   std::chrono::system_clock::time_point ref_time,
853
   std::chrono::milliseconds ocsp_timeout,
854
   const std::vector<std::optional<OCSP::Response>>& ocsp_resp)
855
0
   {
856
0
   if(end_certs.empty())
857
0
      {
858
0
      throw Invalid_Argument("x509_path_validate called with no subjects");
859
0
      }
860
861
0
   X509_Certificate end_entity = end_certs[0];
862
0
   std::vector<X509_Certificate> end_entity_extra;
863
0
   for(size_t i = 1; i < end_certs.size(); ++i)
864
0
      {
865
0
      end_entity_extra.push_back(end_certs[i]);
866
0
      }
867
868
0
   std::vector<std::vector<X509_Certificate>> cert_paths;
869
0
   Certificate_Status_Code path_building_result = PKIX::build_all_certificate_paths(cert_paths, trusted_roots, end_entity, end_entity_extra);
870
871
   // If we cannot successfully build a chain to a trusted self-signed root, stop now
872
0
   if(path_building_result != Certificate_Status_Code::OK)
873
0
      {
874
0
      return Path_Validation_Result(path_building_result);
875
0
      }
876
877
0
   std::vector<Path_Validation_Result> error_results;
878
   // Try validating all the potentially valid paths and return the first one to validate properly
879
0
   for(auto cert_path : cert_paths)
880
0
      {
881
0
      CertificatePathStatusCodes status =
882
0
         PKIX::check_chain(cert_path, ref_time,
883
0
                           hostname, usage,
884
0
                           restrictions.minimum_key_strength(),
885
0
                           restrictions.trusted_hashes());
886
887
0
      CertificatePathStatusCodes crl_status =
888
0
         PKIX::check_crl(cert_path, trusted_roots, ref_time);
889
890
0
      CertificatePathStatusCodes ocsp_status;
891
892
0
      if(!ocsp_resp.empty())
893
0
         {
894
0
         ocsp_status = PKIX::check_ocsp(cert_path, ocsp_resp, trusted_roots, ref_time, restrictions.max_ocsp_age());
895
0
         }
896
897
0
      if(ocsp_status.empty() && ocsp_timeout != std::chrono::milliseconds(0))
898
0
         {
899
0
#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL)
900
0
         ocsp_status = PKIX::check_ocsp_online(cert_path, trusted_roots, ref_time,
901
0
                                               ocsp_timeout, restrictions.ocsp_all_intermediates());
902
#else
903
         ocsp_status.resize(1);
904
         ocsp_status[0].insert(Certificate_Status_Code::OCSP_NO_HTTP);
905
#endif
906
0
         }
907
908
0
      PKIX::merge_revocation_status(status, crl_status, ocsp_status,
909
0
                                    restrictions.require_revocation_information(),
910
0
                                    restrictions.ocsp_all_intermediates());
911
912
0
      Path_Validation_Result pvd(status, std::move(cert_path));
913
0
      if(pvd.successful_validation())
914
0
         {
915
0
         return pvd;
916
0
         }
917
0
      else
918
0
         {
919
0
         error_results.push_back(std::move(pvd));
920
0
         }
921
0
      }
922
0
   return error_results[0];
923
0
   }
924
925
Path_Validation_Result x509_path_validate(
926
   const X509_Certificate& end_cert,
927
   const Path_Validation_Restrictions& restrictions,
928
   const std::vector<Certificate_Store*>& trusted_roots,
929
   const std::string& hostname,
930
   Usage_Type usage,
931
   std::chrono::system_clock::time_point when,
932
   std::chrono::milliseconds ocsp_timeout,
933
   const std::vector<std::optional<OCSP::Response>>& ocsp_resp)
934
0
   {
935
0
   std::vector<X509_Certificate> certs;
936
0
   certs.push_back(end_cert);
937
0
   return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
938
0
   }
939
940
Path_Validation_Result x509_path_validate(
941
   const std::vector<X509_Certificate>& end_certs,
942
   const Path_Validation_Restrictions& restrictions,
943
   const Certificate_Store& store,
944
   const std::string& hostname,
945
   Usage_Type usage,
946
   std::chrono::system_clock::time_point when,
947
   std::chrono::milliseconds ocsp_timeout,
948
   const std::vector<std::optional<OCSP::Response>>& ocsp_resp)
949
0
   {
950
0
   std::vector<Certificate_Store*> trusted_roots;
951
0
   trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
952
953
0
   return x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
954
0
   }
955
956
Path_Validation_Result x509_path_validate(
957
   const X509_Certificate& end_cert,
958
   const Path_Validation_Restrictions& restrictions,
959
   const Certificate_Store& store,
960
   const std::string& hostname,
961
   Usage_Type usage,
962
   std::chrono::system_clock::time_point when,
963
   std::chrono::milliseconds ocsp_timeout,
964
   const std::vector<std::optional<OCSP::Response>>& ocsp_resp)
965
0
   {
966
0
   std::vector<X509_Certificate> certs;
967
0
   certs.push_back(end_cert);
968
969
0
   std::vector<Certificate_Store*> trusted_roots;
970
0
   trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
971
972
0
   return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
973
0
   }
974
975
Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev,
976
      size_t key_strength,
977
      bool ocsp_intermediates,
978
      std::chrono::seconds max_ocsp_age) :
979
   m_require_revocation_information(require_rev),
980
   m_ocsp_all_intermediates(ocsp_intermediates),
981
   m_minimum_key_strength(key_strength),
982
   m_max_ocsp_age(max_ocsp_age)
983
0
   {
984
0
   if(key_strength <= 80)
985
0
      { m_trusted_hashes.insert("SHA-160"); }
986
987
0
   m_trusted_hashes.insert("SHA-224");
988
0
   m_trusted_hashes.insert("SHA-256");
989
0
   m_trusted_hashes.insert("SHA-384");
990
0
   m_trusted_hashes.insert("SHA-512");
991
0
   }
992
993
namespace {
994
CertificatePathStatusCodes find_warnings(const CertificatePathStatusCodes& all_statuses)
995
0
   {
996
0
   CertificatePathStatusCodes warnings;
997
0
   for(const auto& status_set_i : all_statuses)
998
0
      {
999
0
      std::set<Certificate_Status_Code> warning_set_i;
1000
0
      for(const auto& code : status_set_i)
1001
0
         {
1002
0
         if(code >= Certificate_Status_Code::FIRST_WARNING_STATUS &&
1003
0
            code < Certificate_Status_Code::FIRST_ERROR_STATUS)
1004
0
            {
1005
0
            warning_set_i.insert(code);
1006
0
            }
1007
0
         }
1008
0
      warnings.push_back(warning_set_i);
1009
0
      }
1010
0
   return warnings;
1011
0
   }
1012
}
1013
1014
Path_Validation_Result::Path_Validation_Result(CertificatePathStatusCodes status,
1015
                                               std::vector<X509_Certificate>&& cert_chain) :
1016
   m_all_status(std::move(status)),
1017
   m_warnings(find_warnings(m_all_status)),
1018
   m_cert_path(cert_chain),
1019
   m_overall(PKIX::overall_status(m_all_status))
1020
0
   {
1021
0
   }
1022
1023
const X509_Certificate& Path_Validation_Result::trust_root() const
1024
0
   {
1025
0
   if(m_cert_path.empty())
1026
0
      throw Invalid_State("Path_Validation_Result::trust_root no path set");
1027
0
   if(result() != Certificate_Status_Code::VERIFIED)
1028
0
      throw Invalid_State("Path_Validation_Result::trust_root meaningless with invalid status");
1029
1030
0
   return m_cert_path[m_cert_path.size()-1];
1031
0
   }
1032
1033
std::set<std::string> Path_Validation_Result::trusted_hashes() const
1034
0
   {
1035
0
   std::set<std::string> hashes;
1036
0
   for(const auto& cert : m_cert_path)
1037
0
      hashes.insert(cert.hash_used_for_signature());
1038
0
   return hashes;
1039
0
   }
1040
1041
bool Path_Validation_Result::successful_validation() const
1042
0
   {
1043
0
   return (result() == Certificate_Status_Code::VERIFIED ||
1044
0
           result() == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
1045
0
           result() == Certificate_Status_Code::VALID_CRL_CHECKED);
1046
0
   }
1047
1048
bool Path_Validation_Result::no_warnings() const
1049
0
   {
1050
0
   for(const auto& status_set_i : m_warnings)
1051
0
      if(!status_set_i.empty())
1052
0
         return false;
1053
0
   return true;
1054
0
   }
1055
1056
CertificatePathStatusCodes Path_Validation_Result::warnings() const
1057
0
   {
1058
0
   return m_warnings;
1059
0
   }
1060
1061
std::string Path_Validation_Result::result_string() const
1062
0
   {
1063
0
   return status_string(result());
1064
0
   }
1065
1066
const char* Path_Validation_Result::status_string(Certificate_Status_Code code)
1067
0
   {
1068
0
   if(const char* s = to_string(code))
1069
0
      return s;
1070
1071
0
   return "Unknown error";
1072
0
   }
1073
1074
std::string Path_Validation_Result::warnings_string() const
1075
0
   {
1076
0
   const std::string sep(", ");
1077
0
   std::string res;
1078
0
   for(size_t i = 0; i < m_warnings.size(); i++)
1079
0
      {
1080
0
      for(auto code : m_warnings[i])
1081
0
         res += "[" + std::to_string(i) + "] " + status_string(code) + sep;
1082
0
      }
1083
   // remove last sep
1084
0
   if(res.size() >= sep.size())
1085
0
      res = res.substr(0, res.size() - sep.size());
1086
0
   return res;
1087
0
   }
1088
}