Coverage Report

Created: 2020-11-21 08:34

/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/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<std::shared_ptr<const 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::shared_ptr<const X509_Certificate>& subject = cert_path[i];
75
76
0
      const std::shared_ptr<const 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::shared_ptr<const 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<std::shared_ptr<const X509_Certificate>>& cert_path,
215
                 const std::vector<std::shared_ptr<const 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::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
230
0
      std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
231
232
0
      if(i < ocsp_responses.size() && (ocsp_responses.at(i) != nullptr)
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<std::shared_ptr<const X509_Certificate>>& cert_path,
266
                const std::vector<std::shared_ptr<const 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.at(i))
280
0
         {
281
0
         std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
282
0
         std::shared_ptr<const 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<std::shared_ptr<const 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::shared_ptr<const X509_CRL>> crls(cert_path.size());
346
347
0
   for(size_t i = 0; i != cert_path.size(); ++i)
348
0
      {
349
0
      BOTAN_ASSERT_NONNULL(cert_path[i]);
350
0
      for(size_t c = 0; c != certstores.size(); ++c)
351
0
         {
352
0
         crls[i] = certstores[c]->find_crl_for(*cert_path[i]);
353
0
         if(crls[i])
354
0
            break;
355
0
         }
356
0
      }
357
358
0
   return PKIX::check_crl(cert_path, crls, ref_time);
359
0
   }
360
361
#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
362
363
CertificatePathStatusCodes
364
PKIX::check_ocsp_online(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
365
                        const std::vector<Certificate_Store*>& trusted_certstores,
366
                        std::chrono::system_clock::time_point ref_time,
367
                        std::chrono::milliseconds timeout,
368
                        bool ocsp_check_intermediate_CAs,
369
                        std::chrono::seconds max_ocsp_age)
370
0
   {
371
0
   if(cert_path.empty())
372
0
      throw Invalid_Argument("PKIX::check_ocsp_online cert_path empty");
373
374
0
   std::vector<std::future<std::shared_ptr<const OCSP::Response>>> ocsp_response_futures;
375
376
0
   size_t to_ocsp = 1;
377
378
0
   if(ocsp_check_intermediate_CAs)
379
0
      to_ocsp = cert_path.size() - 1;
380
0
   if(cert_path.size() == 1)
381
0
      to_ocsp = 0;
382
383
0
   for(size_t i = 0; i < to_ocsp; ++i)
384
0
      {
385
0
      const std::shared_ptr<const X509_Certificate>& subject = cert_path.at(i);
386
0
      const std::shared_ptr<const X509_Certificate>& issuer = cert_path.at(i+1);
387
388
0
      if(subject->ocsp_responder() == "")
389
0
         {
390
0
         ocsp_response_futures.emplace_back(std::async(std::launch::deferred, [&]() -> std::shared_ptr<const OCSP::Response> {
391
0
                  return std::make_shared<const OCSP::Response>(Certificate_Status_Code::OCSP_NO_REVOCATION_URL);
392
0
                  }));
393
0
         }
394
0
      else
395
0
         {
396
0
         ocsp_response_futures.emplace_back(std::async(std::launch::async, [&]() -> std::shared_ptr<const OCSP::Response> {
397
0
               OCSP::Request req(*issuer, BigInt::decode(subject->serial_number()));
398
399
0
               HTTP::Response http;
400
0
               try
401
0
                  {
402
0
                  http = HTTP::POST_sync(subject->ocsp_responder(),
403
0
                                                "application/ocsp-request",
404
0
                                                req.BER_encode(),
405
0
                                                /*redirects*/1,
406
0
                                                timeout);
407
0
                  }
408
0
               catch(std::exception&)
409
0
                  {
410
                  // log e.what() ?
411
0
                  }
412
0
               if (http.status_code() != 200)
413
0
                  return std::make_shared<const OCSP::Response>(Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE);
414
               // Check the MIME type?
415
416
0
               return std::make_shared<const OCSP::Response>(http.body());
417
0
               }));
418
0
         }
419
0
      }
420
421
0
   std::vector<std::shared_ptr<const OCSP::Response>> ocsp_responses;
422
423
0
   for(size_t i = 0; i < ocsp_response_futures.size(); ++i)
424
0
      {
425
0
      ocsp_responses.push_back(ocsp_response_futures[i].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<std::shared_ptr<const 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::shared_ptr<const X509_CRL>>> future_crls;
444
0
   std::vector<std::shared_ptr<const 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::shared_ptr<const X509_Certificate>& cert = cert_path.at(i);
449
0
      for(size_t c = 0; c != certstores.size(); ++c)
450
0
         {
451
0
         crls[i] = certstores[c]->find_crl_for(*cert);
452
0
         if(crls[i])
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::shared_ptr<const X509_CRL>>());
466
0
         }
467
0
      else if(cert->crl_distribution_point() == "")
468
0
         {
469
         // Avoid creating a thread for this case
470
0
         future_crls.emplace_back(std::async(std::launch::deferred, [&]() -> std::shared_ptr<const 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::shared_ptr<const 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 std::make_shared<const 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
   const CertificatePathStatusCodes 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_NONNULL(crls[i]);
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<std::shared_ptr<const X509_Certificate>>& cert_path,
525
                             const std::vector<Certificate_Store*>& trusted_certstores,
526
                             const std::shared_ptr<const X509_Certificate>& end_entity,
527
                             const std::vector<std::shared_ptr<const 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(size_t i = 0; i != end_entity_extra.size(); ++i)
547
0
      ee_extras.add_certificate(end_entity_extra[i]);
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::shared_ptr<const 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::shared_ptr<const 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<std::shared_ptr<const X509_Certificate>>>& cert_paths_out,
630
                                  const std::vector<Certificate_Store*>& trusted_certstores,
631
                                  const std::shared_ptr<const X509_Certificate>& end_entity,
632
                                  const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra)
633
526
   {
634
526
   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
526
   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
526
   std::vector<Certificate_Status_Code> stats;
648
649
526
   Certificate_Store_In_Memory ee_extras;
650
1.26k
   for(size_t i = 0; i != end_entity_extra.size(); ++i)
651
740
      {
652
740
      ee_extras.add_certificate(end_entity_extra[i]);
653
740
      }
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
526
   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
526
   std::vector<std::shared_ptr<const X509_Certificate>> path_so_far;
666
667
   // todo can we assume that the end certificate is not trusted?
668
526
   std::vector<cert_maybe_trusted> stack = { {end_entity, false} };
669
670
11.0k
   while(!stack.empty())
671
10.5k
      {
672
      // found a deletion marker that guides the DFS, backtracing
673
10.5k
      if(stack.back().first == nullptr)
674
2.28k
         {
675
2.28k
         stack.pop_back();
676
2.28k
         std::string fprint = path_so_far.back()->fingerprint("SHA-256");
677
2.28k
         certs_seen.erase(fprint);
678
2.28k
         path_so_far.pop_back();
679
2.28k
         }
680
      // process next cert on the path
681
8.21k
      else
682
8.21k
         {
683
8.21k
         std::shared_ptr<const X509_Certificate> last = stack.back().first;
684
8.21k
         bool trusted = stack.back().second;
685
8.21k
         stack.pop_back();
686
687
         // certificate already seen?
688
8.21k
         const std::string fprint = last->fingerprint("SHA-256");
689
8.21k
         if(certs_seen.count(fprint) == 1)
690
4.77k
            {
691
4.77k
            stats.push_back(Certificate_Status_Code::CERT_CHAIN_LOOP);
692
            // the current path ended in a loop
693
4.77k
            continue;
694
4.77k
            }
695
696
         // the current path ends here
697
3.44k
         if(last->is_self_signed())
698
160
            {
699
            // found a trust anchor
700
160
            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
160
            else
709
160
               {
710
160
               stats.push_back(Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
711
160
               continue;
712
160
               }
713
3.28k
            }
714
715
3.28k
         const X509_DN issuer_dn = last->issuer_dn();
716
3.28k
         const std::vector<uint8_t> auth_key_id = last->authority_key_id();
717
718
         // search for trusted issuers
719
3.28k
         std::vector<std::shared_ptr<const X509_Certificate>> trusted_issuers;
720
3.28k
         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
3.28k
         std::vector<std::shared_ptr<const X509_Certificate>> misc_issuers =
728
3.28k
            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
3.28k
         if(trusted_issuers.size() + misc_issuers.size() == 0)
732
992
            {
733
992
            stats.push_back(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);
734
992
            continue;
735
992
            }
736
737
         // push the latest certificate onto the path_so_far
738
2.28k
         path_so_far.push_back(last);
739
2.28k
         certs_seen.emplace(fprint);
740
741
         // push a deletion marker on the stack for backtracing later
742
2.28k
         stack.push_back({std::shared_ptr<const X509_Certificate>(nullptr),false});
743
744
2.28k
         for(const auto& trusted_cert : trusted_issuers)
745
0
            {
746
0
            stack.push_back({trusted_cert,true});
747
0
            }
748
749
2.28k
         for(const auto& misc : misc_issuers)
750
7.68k
            {
751
7.68k
            stack.push_back({misc,false});
752
7.68k
            }
753
2.28k
         }
754
10.5k
      }
755
756
   // could not construct any potentially valid path
757
526
   if(cert_paths_out.empty())
758
526
      {
759
526
      if(stats.empty())
760
0
         throw Internal_Error("X509 path building failed for unknown reasons");
761
526
      else
762
         // arbitrarily return the first error
763
526
         return stats[0];
764
0
      }
765
0
   else
766
0
      {
767
0
      return Certificate_Status_Code::OK;
768
0
      }
769
526
   }
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].size() > 0)
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].size() > 0)
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::shared_ptr<const OCSP::Response>>& ocsp_resp)
855
526
   {
856
526
   if(end_certs.empty())
857
0
      {
858
0
      throw Invalid_Argument("x509_path_validate called with no subjects");
859
0
      }
860
861
526
   std::shared_ptr<const X509_Certificate> end_entity(std::make_shared<const X509_Certificate>(end_certs[0]));
862
526
   std::vector<std::shared_ptr<const X509_Certificate>> end_entity_extra;
863
1.26k
   for(size_t i = 1; i < end_certs.size(); ++i)
864
740
      {
865
740
      end_entity_extra.push_back(std::make_shared<const X509_Certificate>(end_certs[i]));
866
740
      }
867
868
526
   std::vector<std::vector<std::shared_ptr<const X509_Certificate>>> cert_paths;
869
526
   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
526
   if(path_building_result != Certificate_Status_Code::OK)
873
526
      {
874
526
      return Path_Validation_Result(path_building_result);
875
526
      }
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.size() > 0)
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::shared_ptr<const 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::shared_ptr<const 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::shared_ptr<const 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
526
   {
984
526
   if(key_strength <= 80)
985
0
      { m_trusted_hashes.insert("SHA-160"); }
986
987
526
   m_trusted_hashes.insert("SHA-224");
988
526
   m_trusted_hashes.insert("SHA-256");
989
526
   m_trusted_hashes.insert("SHA-384");
990
526
   m_trusted_hashes.insert("SHA-512");
991
526
   }
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<std::shared_ptr<const X509_Certificate>>&& cert_chain) :
1016
   m_all_status(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(size_t i = 0; i != m_cert_path.size(); ++i)
1037
0
      hashes.insert(m_cert_path[i]->hash_used_for_signature());
1038
0
   return hashes;
1039
0
   }
1040
1041
bool Path_Validation_Result::successful_validation() const
1042
526
   {
1043
526
   return (result() == Certificate_Status_Code::VERIFIED ||
1044
526
           result() == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
1045
526
           result() == Certificate_Status_Code::VALID_CRL_CHECKED);
1046
526
   }
1047
1048
bool Path_Validation_Result::no_warnings() const
1049
0
   {
1050
0
   for(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
526
   {
1063
526
   return status_string(result());
1064
526
   }
1065
1066
const char* Path_Validation_Result::status_string(Certificate_Status_Code code)
1067
526
   {
1068
526
   if(const char* s = to_string(code))
1069
526
      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
}