Coverage Report

Created: 2022-11-24 06:56

/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 X509_Certificate& subject = cert_path[i];
75
0
      const X509_Certificate& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
76
77
0
      if(at_self_signed_root && (issuer.is_self_signed() == false))
78
0
         {
79
0
         status.insert(Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT);
80
0
         }
81
82
0
      if(subject.issuer_dn() != issuer.subject_dn())
83
0
         {
84
0
         status.insert(Certificate_Status_Code::CHAIN_NAME_MISMATCH);
85
0
         }
86
87
      // Check the serial number
88
0
      if(subject.is_serial_negative())
89
0
         {
90
0
         status.insert(Certificate_Status_Code::CERT_SERIAL_NEGATIVE);
91
0
         }
92
93
      // Check the subject's DN components' length
94
95
0
      for(const auto& dn_pair : subject.subject_dn().dn_info())
96
0
         {
97
0
         const size_t dn_ub = X509_DN::lookup_ub(dn_pair.first);
98
         // dn_pair = <OID,str>
99
0
         if(dn_ub > 0 && dn_pair.second.size() > dn_ub)
100
0
            {
101
0
            status.insert(Certificate_Status_Code::DN_TOO_LONG);
102
0
            }
103
0
         }
104
105
      // Check all certs for valid time range
106
0
      if(validation_time < subject.not_before())
107
0
         status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);
108
109
0
      if(validation_time > subject.not_after())
110
0
         status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
111
112
      // Check issuer constraints
113
0
      if(!issuer.is_CA_cert() && !self_signed_ee_cert)
114
0
         status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
115
116
0
      std::unique_ptr<Public_Key> issuer_key(issuer.subject_public_key());
117
118
      // Check the signature algorithm is known
119
0
      if(OIDS::oid2str_or_empty(subject.signature_algorithm().get_oid()).empty())
120
0
         {
121
0
         status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
122
0
         }
123
0
      else
124
0
         {
125
         // only perform the following checks if the signature algorithm is known
126
0
         if(!issuer_key)
127
0
            {
128
0
            status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
129
0
            }
130
0
         else
131
0
            {
132
0
            const Certificate_Status_Code sig_status = subject.verify_signature(*issuer_key);
133
134
0
            if(sig_status != Certificate_Status_Code::VERIFIED)
135
0
               status.insert(sig_status);
136
137
0
            if(issuer_key->estimated_strength() < min_signature_algo_strength)
138
0
               status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
139
0
            }
140
141
         // Ignore untrusted hashes on self-signed roots
142
0
         if(!trusted_hashes.empty() && !at_self_signed_root)
143
0
            {
144
0
            if(trusted_hashes.count(subject.hash_used_for_signature()) == 0)
145
0
               status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
146
0
            }
147
0
         }
148
149
      // Check cert extensions
150
151
0
      if(subject.x509_version() == 1)
152
0
         {
153
0
         if(subject.v2_issuer_key_id().empty() == false ||
154
0
            subject.v2_subject_key_id().empty() == false)
155
0
            {
156
0
            status.insert(Certificate_Status_Code::V2_IDENTIFIERS_IN_V1_CERT);
157
0
            }
158
0
         }
159
160
0
      Extensions extensions = subject.v3_extensions();
161
0
      const auto& extensions_vec = extensions.extensions();
162
0
      if(subject.x509_version() < 3 && !extensions_vec.empty())
163
0
         {
164
0
         status.insert(Certificate_Status_Code::EXT_IN_V1_V2_CERT);
165
0
         }
166
0
      for(auto& extension : extensions_vec)
167
0
         {
168
0
         extension.first->validate(subject, issuer, cert_path, cert_status, i);
169
0
         }
170
0
      if(extensions.extensions().size() != extensions.get_extension_oids().size())
171
0
         {
172
0
         status.insert(Certificate_Status_Code::DUPLICATE_CERT_EXTENSION);
173
0
         }
174
0
      }
175
176
   // path len check
177
0
   size_t max_path_length = cert_path.size();
178
0
   for(size_t i = cert_path.size() - 1; i > 0 ; --i)
179
0
      {
180
0
      std::set<Certificate_Status_Code>& status = cert_status.at(i);
181
0
      const X509_Certificate& subject = cert_path[i];
182
183
      /*
184
      * If the certificate was not self-issued, verify that max_path_length is
185
      * greater than zero and decrement max_path_length by 1.
186
      */
187
0
      if(subject.subject_dn() != subject.issuer_dn())
188
0
         {
189
0
         if(max_path_length > 0)
190
0
            {
191
0
            --max_path_length;
192
0
            }
193
0
         else
194
0
            {
195
0
            status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);
196
0
            }
197
0
         }
198
199
      /*
200
      * If pathLenConstraint is present in the certificate and is less than max_path_length,
201
      * set max_path_length to the value of pathLenConstraint.
202
      */
203
0
      if(subject.path_limit() != Cert_Extension::NO_CERT_PATH_LIMIT && subject.path_limit() < max_path_length)
204
0
         {
205
0
         max_path_length = subject.path_limit();
206
0
         }
207
0
      }
208
209
0
   return cert_status;
210
0
   }
211
212
CertificatePathStatusCodes
213
PKIX::check_ocsp(const std::vector<X509_Certificate>& cert_path,
214
                 const std::vector<std::optional<OCSP::Response>>& ocsp_responses,
215
                 const std::vector<Certificate_Store*>& trusted_certstores,
216
                 std::chrono::system_clock::time_point ref_time,
217
                 std::chrono::seconds max_ocsp_age)
218
0
   {
219
0
   if(cert_path.empty())
220
0
      throw Invalid_Argument("PKIX::check_ocsp cert_path empty");
221
222
0
   CertificatePathStatusCodes cert_status(cert_path.size() - 1);
223
224
0
   for(size_t i = 0; i != cert_path.size() - 1; ++i)
225
0
      {
226
0
      std::set<Certificate_Status_Code>& status = cert_status.at(i);
227
228
0
      const X509_Certificate& subject = cert_path.at(i);
229
0
      const X509_Certificate& ca = cert_path.at(i+1);
230
231
0
      if(i < ocsp_responses.size() && (ocsp_responses.at(i) != std::nullopt)
232
0
            && (ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful))
233
0
         {
234
0
         try
235
0
            {
236
0
            Certificate_Status_Code ocsp_signature_status = ocsp_responses.at(i)->check_signature(trusted_certstores, cert_path);
237
238
0
            if(ocsp_signature_status == Certificate_Status_Code::OCSP_SIGNATURE_OK)
239
0
               {
240
               // Signature ok, so check the claimed status
241
0
               Certificate_Status_Code ocsp_status = ocsp_responses.at(i)->status_for(ca, subject, ref_time, max_ocsp_age);
242
0
               status.insert(ocsp_status);
243
0
               }
244
0
            else
245
0
               {
246
               // Some signature problem
247
0
               status.insert(ocsp_signature_status);
248
0
               }
249
0
            }
250
0
         catch(Exception&)
251
0
            {
252
0
            status.insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
253
0
            }
254
0
         }
255
0
      }
256
257
0
   while(!cert_status.empty() && cert_status.back().empty())
258
0
      cert_status.pop_back();
259
260
0
   return cert_status;
261
0
   }
262
263
CertificatePathStatusCodes
264
PKIX::check_crl(const std::vector<X509_Certificate>& cert_path,
265
                const std::vector<std::optional<X509_CRL>>& crls,
266
                std::chrono::system_clock::time_point ref_time)
267
0
   {
268
0
   if(cert_path.empty())
269
0
      throw Invalid_Argument("PKIX::check_crl cert_path empty");
270
271
0
   CertificatePathStatusCodes cert_status(cert_path.size());
272
0
   const X509_Time validation_time(ref_time);
273
274
0
   for(size_t i = 0; i != cert_path.size() - 1; ++i)
275
0
      {
276
0
      std::set<Certificate_Status_Code>& status = cert_status.at(i);
277
278
0
      if(i < crls.size() && crls[i].has_value())
279
0
         {
280
0
         const X509_Certificate& subject = cert_path.at(i);
281
0
         const X509_Certificate& ca = cert_path.at(i+1);
282
283
0
         if(!ca.allowed_usage(CRL_SIGN))
284
0
            status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
285
286
0
         if(validation_time < crls[i]->this_update())
287
0
            status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);
288
289
0
         if(validation_time > crls[i]->next_update())
290
0
            status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);
291
292
0
         if(crls[i]->check_signature(ca.subject_public_key()) == false)
293
0
            status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);
294
295
0
         status.insert(Certificate_Status_Code::VALID_CRL_CHECKED);
296
297
0
         if(crls[i]->is_revoked(subject))
298
0
            status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
299
300
0
         std::string dp = subject.crl_distribution_point();
301
0
         if(!dp.empty())
302
0
            {
303
0
            if(dp != crls[i]->crl_issuing_distribution_point())
304
0
               {
305
0
               status.insert(Certificate_Status_Code::NO_MATCHING_CRLDP);
306
0
               }
307
0
            }
308
309
0
         for(const auto& extension : crls[i]->extensions().extensions())
310
0
            {
311
            // XXX this is wrong - the OID might be defined but the extention not full parsed
312
            // for example see #1652
313
314
            // is the extension critical and unknown?
315
0
            if(extension.second && OIDS::oid2str_or_empty(extension.first->oid_of()).empty())
316
0
               {
317
               /* NIST Certificate Path Valiadation Testing document: "When an implementation does not recognize a critical extension in the
318
                * crlExtensions field, it shall assume that identified certificates have been revoked and are no longer valid"
319
                */
320
0
               status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
321
0
               }
322
0
            }
323
324
0
         }
325
0
      }
326
327
0
   while(!cert_status.empty() && cert_status.back().empty())
328
0
      cert_status.pop_back();
329
330
0
   return cert_status;
331
0
   }
332
333
CertificatePathStatusCodes
334
PKIX::check_crl(const std::vector<X509_Certificate>& cert_path,
335
                const std::vector<Certificate_Store*>& certstores,
336
                std::chrono::system_clock::time_point ref_time)
337
0
   {
338
0
   if(cert_path.empty())
339
0
      throw Invalid_Argument("PKIX::check_crl cert_path empty");
340
341
0
   if(certstores.empty())
342
0
      throw Invalid_Argument("PKIX::check_crl certstores empty");
343
344
0
   std::vector<std::optional<X509_CRL>> crls(cert_path.size());
345
346
0
   for(size_t i = 0; i != cert_path.size(); ++i)
347
0
      {
348
0
      for(auto certstore : certstores)
349
0
         {
350
0
         crls[i] = certstore->find_crl_for(cert_path[i]);
351
0
         if(crls[i])
352
0
            break;
353
0
         }
354
0
      }
355
356
0
   return PKIX::check_crl(cert_path, crls, ref_time);
357
0
   }
358
359
#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
360
361
CertificatePathStatusCodes
362
PKIX::check_ocsp_online(const std::vector<X509_Certificate>& cert_path,
363
                        const std::vector<Certificate_Store*>& trusted_certstores,
364
                        std::chrono::system_clock::time_point ref_time,
365
                        std::chrono::milliseconds timeout,
366
                        bool ocsp_check_intermediate_CAs,
367
                        std::chrono::seconds max_ocsp_age)
368
0
   {
369
0
   if(cert_path.empty())
370
0
      throw Invalid_Argument("PKIX::check_ocsp_online cert_path empty");
371
372
0
   std::vector<std::future<std::optional<OCSP::Response>>> ocsp_response_futures;
373
374
0
   size_t to_ocsp = 1;
375
376
0
   if(ocsp_check_intermediate_CAs)
377
0
      to_ocsp = cert_path.size() - 1;
378
0
   if(cert_path.size() == 1)
379
0
      to_ocsp = 0;
380
381
0
   for(size_t i = 0; i < to_ocsp; ++i)
382
0
      {
383
0
      const X509_Certificate& subject = cert_path.at(i);
384
0
      const X509_Certificate& issuer = cert_path.at(i+1);
385
386
0
      if(subject.ocsp_responder().empty())
387
0
         {
388
0
         ocsp_response_futures.emplace_back(std::async(std::launch::deferred, [&]() -> std::optional<OCSP::Response> {
389
0
                  return OCSP::Response(Certificate_Status_Code::OCSP_NO_REVOCATION_URL);
390
0
                  }));
391
0
         }
392
0
      else
393
0
         {
394
0
         ocsp_response_futures.emplace_back(std::async(std::launch::async, [&]() -> std::optional<OCSP::Response> {
395
0
               OCSP::Request req(issuer, BigInt::decode(subject.serial_number()));
396
397
0
               HTTP::Response http;
398
0
               try
399
0
                  {
400
0
                  http = HTTP::POST_sync(subject.ocsp_responder(),
401
0
                                         "application/ocsp-request",
402
0
                                         req.BER_encode(),
403
0
                                         /*redirects*/1,
404
0
                                         timeout);
405
0
                  }
406
0
               catch(std::exception&)
407
0
                  {
408
                  // log e.what() ?
409
0
                  }
410
0
               if (http.status_code() != 200)
411
0
                  return OCSP::Response(Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE);
412
               // Check the MIME type?
413
414
0
               return OCSP::Response(http.body());
415
0
               }));
416
0
         }
417
0
      }
418
419
0
   std::vector<std::optional<OCSP::Response>> ocsp_responses;
420
0
   ocsp_responses.reserve(ocsp_response_futures.size());
421
422
0
   for(auto& ocsp_response_future : ocsp_response_futures)
423
0
      {
424
0
      ocsp_responses.push_back(ocsp_response_future.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(auto certstore : certstores)
449
0
         {
450
0
         crls[i] = certstore->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().empty())
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
   auto 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(const auto& cert : end_entity_extra)
546
0
      ee_extras.add_certificate(cert);
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
0
   {
633
0
   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
0
   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
0
   std::vector<Certificate_Status_Code> stats;
647
648
0
   Certificate_Store_In_Memory ee_extras;
649
0
   for(const auto& cert : end_entity_extra)
650
0
      {
651
0
      ee_extras.add_certificate(cert);
652
0
      }
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
0
   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
0
   std::vector<X509_Certificate> path_so_far;
665
666
   // todo can we assume that the end certificate is not trusted?
667
0
   std::vector<cert_maybe_trusted> stack = { {end_entity, false} };
668
669
0
   while(!stack.empty())
670
0
      {
671
0
      std::optional<X509_Certificate> last = stack.back().first;
672
      // found a deletion marker that guides the DFS, backtracing
673
0
      if(last == std::nullopt)
674
0
         {
675
0
         stack.pop_back();
676
0
         std::string fprint = path_so_far.back().fingerprint("SHA-256");
677
0
         certs_seen.erase(fprint);
678
0
         path_so_far.pop_back();
679
0
         }
680
      // process next cert on the path
681
0
      else
682
0
         {
683
0
         const bool trusted = stack.back().second;
684
0
         stack.pop_back();
685
686
         // certificate already seen?
687
0
         const std::string fprint = last->fingerprint("SHA-256");
688
0
         if(certs_seen.count(fprint) == 1)
689
0
            {
690
0
            stats.push_back(Certificate_Status_Code::CERT_CHAIN_LOOP);
691
            // the current path ended in a loop
692
0
            continue;
693
0
            }
694
695
         // the current path ends here
696
0
         if(last->is_self_signed())
697
0
            {
698
            // found a trust anchor
699
0
            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
0
            else
708
0
               {
709
0
               stats.push_back(Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
710
0
               continue;
711
0
               }
712
0
            }
713
714
0
         const X509_DN issuer_dn = last->issuer_dn();
715
0
         const std::vector<uint8_t> auth_key_id = last->authority_key_id();
716
717
         // search for trusted issuers
718
0
         std::vector<X509_Certificate> trusted_issuers;
719
0
         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
0
         std::vector<X509_Certificate> misc_issuers =
727
0
            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
0
         if(trusted_issuers.size() + misc_issuers.size() == 0)
731
0
            {
732
0
            stats.push_back(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);
733
0
            continue;
734
0
            }
735
736
         // push the latest certificate onto the path_so_far
737
0
         path_so_far.push_back(*last);
738
0
         certs_seen.emplace(fprint);
739
740
         // push a deletion marker on the stack for backtracing later
741
0
         stack.push_back({std::optional<X509_Certificate>(), false});
742
743
0
         for(const auto& trusted_cert : trusted_issuers)
744
0
            {
745
0
            stack.push_back({trusted_cert,true});
746
0
            }
747
748
0
         for(const auto& misc : misc_issuers)
749
0
            {
750
0
            stack.push_back({misc,false});
751
0
            }
752
0
         }
753
0
      }
754
755
   // could not construct any potentially valid path
756
0
   if(cert_paths_out.empty())
757
0
      {
758
0
      if(stats.empty())
759
0
         throw Internal_Error("X509 path building failed for unknown reasons");
760
0
      else
761
         // arbitrarily return the first error
762
0
         return stats[0];
763
0
      }
764
0
   else
765
0
      {
766
0
      return Certificate_Status_Code::OK;
767
0
      }
768
0
   }
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].empty())
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].empty())
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
0
   {
855
0
   if(end_certs.empty())
856
0
      {
857
0
      throw Invalid_Argument("x509_path_validate called with no subjects");
858
0
      }
859
860
0
   X509_Certificate end_entity = end_certs[0];
861
0
   std::vector<X509_Certificate> end_entity_extra;
862
0
   for(size_t i = 1; i < end_certs.size(); ++i)
863
0
      {
864
0
      end_entity_extra.push_back(end_certs[i]);
865
0
      }
866
867
0
   std::vector<std::vector<X509_Certificate>> cert_paths;
868
0
   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
0
   if(path_building_result != Certificate_Status_Code::OK)
872
0
      {
873
0
      return Path_Validation_Result(path_building_result);
874
0
      }
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.empty())
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
0
   {
983
0
   if(key_strength <= 80)
984
0
      { m_trusted_hashes.insert("SHA-160"); }
985
986
0
   m_trusted_hashes.insert("SHA-224");
987
0
   m_trusted_hashes.insert("SHA-256");
988
0
   m_trusted_hashes.insert("SHA-384");
989
0
   m_trusted_hashes.insert("SHA-512");
990
0
   }
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(std::move(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(const auto& cert : m_cert_path)
1036
0
      hashes.insert(cert.hash_used_for_signature());
1037
0
   return hashes;
1038
0
   }
1039
1040
bool Path_Validation_Result::successful_validation() const
1041
0
   {
1042
0
   return (result() == Certificate_Status_Code::VERIFIED ||
1043
0
           result() == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
1044
0
           result() == Certificate_Status_Code::VALID_CRL_CHECKED);
1045
0
   }
1046
1047
bool Path_Validation_Result::no_warnings() const
1048
0
   {
1049
0
   for(const 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
0
   {
1062
0
   return status_string(result());
1063
0
   }
1064
1065
const char* Path_Validation_Result::status_string(Certificate_Status_Code code)
1066
0
   {
1067
0
   if(const char* s = to_string(code))
1068
0
      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
}