Coverage Report

Created: 2020-06-30 13:58

/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
0
42
0
   const bool self_signed_ee_cert = (cert_path.size() == 1);
43
0
44
0
   X509_Time validation_time(ref_time);
45
0
46
0
   CertificatePathStatusCodes cert_status(cert_path.size());
47
0
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
0
51
0
   if(!cert_path[0]->allowed_usage(usage))
52
0
      cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
53
0
54
0
   if(cert_path[0]->is_CA_cert() == false &&
55
0
      cert_path[0]->has_constraints(KEY_CERT_SIGN))
56
0
      {
57
0
      /*
58
0
      "If the keyCertSign bit is asserted, then the cA bit in the
59
0
      basic constraints extension (Section 4.2.1.9) MUST also be
60
0
      asserted." - RFC 5280
61
0
62
0
      We don't bother doing this check on the rest of the path since they
63
0
      must have the cA bit asserted or the validation will fail anyway.
64
0
      */
65
0
      cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
66
0
      }
67
0
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
0
72
0
      const bool at_self_signed_root = (i == cert_path.size() - 1);
73
0
74
0
      const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
75
0
76
0
      const std::shared_ptr<const X509_Certificate>& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
77
0
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
0
83
0
      if(subject->issuer_dn() != issuer->subject_dn())
84
0
         {
85
0
         status.insert(Certificate_Status_Code::CHAIN_NAME_MISMATCH);
86
0
         }
87
0
88
0
      // 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
0
94
0
      // Check the subject's DN components' length
95
0
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
0
         // 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
0
106
0
      // 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
0
110
0
      if(validation_time > subject->not_after())
111
0
         status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
112
0
113
0
      // 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
0
117
0
      std::unique_ptr<Public_Key> issuer_key(issuer->subject_public_key());
118
0
119
0
      // 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
0
         // 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
0
135
0
            if(sig_status != Certificate_Status_Code::VERIFIED)
136
0
               status.insert(sig_status);
137
0
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
0
142
0
         // 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
0
150
0
      // Check cert extensions
151
0
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
0
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
0
177
0
   // 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
0
184
0
      /*
185
0
      * If the certificate was not self-issued, verify that max_path_length is
186
0
      * greater than zero and decrement max_path_length by 1.
187
0
      */
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
0
200
0
      /*
201
0
      * If pathLenConstraint is present in the certificate and is less than max_path_length,
202
0
      * set max_path_length to the value of pathLenConstraint.
203
0
      */
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
0
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
0
223
0
   CertificatePathStatusCodes cert_status(cert_path.size() - 1);
224
0
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
0
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
0
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
0
239
0
            if(ocsp_signature_status == Certificate_Status_Code::OCSP_SIGNATURE_OK)
240
0
               {
241
0
               // 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
0
               // 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
0
258
0
   while(cert_status.size() > 0 && cert_status.back().empty())
259
0
      cert_status.pop_back();
260
0
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
0
272
0
   CertificatePathStatusCodes cert_status(cert_path.size());
273
0
   const X509_Time validation_time(ref_time);
274
0
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
0
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
0
284
0
         if(!ca->allowed_usage(CRL_SIGN))
285
0
            status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
286
0
287
0
         if(validation_time < crls[i]->this_update())
288
0
            status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);
289
0
290
0
         if(validation_time > crls[i]->next_update())
291
0
            status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);
292
0
293
0
         if(crls[i]->check_signature(ca->subject_public_key()) == false)
294
0
            status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);
295
0
296
0
         status.insert(Certificate_Status_Code::VALID_CRL_CHECKED);
297
0
298
0
         if(crls[i]->is_revoked(*subject))
299
0
            status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
300
0
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
0
310
0
         for(const auto& extension : crls[i]->extensions().extensions())
311
0
            {
312
0
            // XXX this is wrong - the OID might be defined but the extention not full parsed
313
0
            // for example see #1652
314
0
315
0
            // is the extension critical and unknown?
316
0
            if(extension.second && OIDS::oid2str_or_empty(extension.first->oid_of()) == "")
317
0
               {
318
0
               /* NIST Certificate Path Valiadation Testing document: "When an implementation does not recognize a critical extension in the
319
0
                * crlExtensions field, it shall assume that identified certificates have been revoked and are no longer valid"
320
0
                */
321
0
               status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
322
0
               }
323
0
            }
324
0
325
0
         }
326
0
      }
327
0
328
0
   while(cert_status.size() > 0 && cert_status.back().empty())
329
0
      cert_status.pop_back();
330
0
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
0
342
0
   if(certstores.empty())
343
0
      throw Invalid_Argument("PKIX::check_crl certstores empty");
344
0
345
0
   std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size());
346
0
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
0
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
0
374
0
   std::vector<std::future<std::shared_ptr<const OCSP::Response>>> ocsp_response_futures;
375
0
376
0
   size_t to_ocsp = 1;
377
0
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
0
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
0
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
0
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
0
                  // 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
0
               // Check the MIME type?
415
0
416
0
               return std::make_shared<const OCSP::Response>(http.body());
417
0
               }));
418
0
         }
419
0
      }
420
0
421
0
   std::vector<std::shared_ptr<const OCSP::Response>> ocsp_responses;
422
0
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
0
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
0
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
0
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
0
456
0
      // TODO: check if CRL is expired and re-request?
457
0
458
0
      // Only request if we don't already have a CRL
459
0
      if(crls[i])
460
0
         {
461
0
         /*
462
0
         We already have a CRL, so just insert this empty one to hold a place in the vector
463
0
         so that indexes match up
464
0
         */
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
0
         // 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
0
480
0
               http.throw_unless_ok();
481
0
               // check the mime type?
482
0
               return std::make_shared<const X509_CRL>(http.body());
483
0
               }));
484
0
         }
485
0
      }
486
0
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
0
            // crls[i] left null
498
0
            // todo: log exception e.what() ?
499
0
            }
500
0
         }
501
0
      }
502
0
503
0
   const CertificatePathStatusCodes crl_status = PKIX::check_crl(cert_path, crls, ref_time);
504
0
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
0
            // 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
0
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
0
534
0
   /*
535
0
   * This is an inelegant but functional way of preventing path loops
536
0
   * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
537
0
   * fingerprints in the path. If there is a duplicate, we error out.
538
0
   * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
539
0
   */
540
0
   std::set<std::string> certs_seen;
541
0
542
0
   cert_path.push_back(end_entity);
543
0
   certs_seen.insert(end_entity->fingerprint("SHA-256"));
544
0
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
0
549
0
   // 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
0
556
0
      std::shared_ptr<const X509_Certificate> issuer;
557
0
      bool trusted_issuer = false;
558
0
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
0
569
0
      if(!issuer)
570
0
         {
571
0
         // fall back to searching supplemental certs
572
0
         issuer = ee_extras.find_cert(issuer_dn, auth_key_id);
573
0
         }
574
0
575
0
      if(!issuer)
576
0
         return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND;
577
0
578
0
      const std::string fprint = issuer->fingerprint("SHA-256");
579
0
580
0
      if(certs_seen.count(fprint) > 0) // already seen?
581
0
         {
582
0
         return Certificate_Status_Code::CERT_CHAIN_LOOP;
583
0
         }
584
0
585
0
      certs_seen.insert(fprint);
586
0
      cert_path.push_back(issuer);
587
0
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
687
   {
634
687
   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
687
639
687
   if(end_entity->is_self_signed())
640
0
      {
641
0
      return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
642
0
      }
643
687
644
687
   /*
645
687
    * Pile up error messages
646
687
    */
647
687
   std::vector<Certificate_Status_Code> stats;
648
687
649
687
   Certificate_Store_In_Memory ee_extras;
650
1.39k
   for(size_t i = 0; i != end_entity_extra.size(); ++i)
651
706
      {
652
706
      ee_extras.add_certificate(end_entity_extra[i]);
653
706
      }
654
687
655
687
   /*
656
687
   * This is an inelegant but functional way of preventing path loops
657
687
   * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
658
687
   * fingerprints in the path. If there is a duplicate, we error out.
659
687
   * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
660
687
   */
661
687
   std::set<std::string> certs_seen;
662
687
663
687
   // new certs are added and removed from the path during the DFS
664
687
   // it is copied into cert_paths_out when we encounter a trusted root
665
687
   std::vector<std::shared_ptr<const X509_Certificate>> path_so_far;
666
687
667
687
   // todo can we assume that the end certificate is not trusted?
668
687
   std::vector<cert_maybe_trusted> stack = { {end_entity, false} };
669
687
670
10.5k
   while(!stack.empty())
671
9.86k
      {
672
9.86k
      // found a deletion marker that guides the DFS, backtracing
673
9.86k
      if(stack.back().first == nullptr)
674
2.16k
         {
675
2.16k
         stack.pop_back();
676
2.16k
         std::string fprint = path_so_far.back()->fingerprint("SHA-256");
677
2.16k
         certs_seen.erase(fprint);
678
2.16k
         path_so_far.pop_back();
679
2.16k
         }
680
7.70k
      // process next cert on the path
681
7.70k
      else
682
7.70k
         {
683
7.70k
         std::shared_ptr<const X509_Certificate> last = stack.back().first;
684
7.70k
         bool trusted = stack.back().second;
685
7.70k
         stack.pop_back();
686
7.70k
687
7.70k
         // certificate already seen?
688
7.70k
         const std::string fprint = last->fingerprint("SHA-256");
689
7.70k
         if(certs_seen.count(fprint) == 1)
690
4.31k
            {
691
4.31k
            stats.push_back(Certificate_Status_Code::CERT_CHAIN_LOOP);
692
4.31k
            // the current path ended in a loop
693
4.31k
            continue;
694
4.31k
            }
695
3.38k
696
3.38k
         // the current path ends here
697
3.38k
         if(last->is_self_signed())
698
118
            {
699
118
            // found a trust anchor
700
118
            if(trusted)
701
0
               {
702
0
               cert_paths_out.push_back(path_so_far);
703
0
               cert_paths_out.back().push_back(last);
704
0
705
0
               continue;
706
0
               }
707
118
            // found an untrustworthy root
708
118
            else
709
118
               {
710
118
               stats.push_back(Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
711
118
               continue;
712
118
               }
713
3.26k
            }
714
3.26k
715
3.26k
         const X509_DN issuer_dn = last->issuer_dn();
716
3.26k
         const std::vector<uint8_t> auth_key_id = last->authority_key_id();
717
3.26k
718
3.26k
         // search for trusted issuers
719
3.26k
         std::vector<std::shared_ptr<const X509_Certificate>> trusted_issuers;
720
3.26k
         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
3.26k
726
3.26k
         // search the supplemental certs
727
3.26k
         std::vector<std::shared_ptr<const X509_Certificate>> misc_issuers =
728
3.26k
            ee_extras.find_all_certs(issuer_dn, auth_key_id);
729
3.26k
730
3.26k
         // if we could not find any issuers, the current path ends here
731
3.26k
         if(trusted_issuers.size() + misc_issuers.size() == 0)
732
1.10k
            {
733
1.10k
            stats.push_back(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);
734
1.10k
            continue;
735
1.10k
            }
736
2.16k
737
2.16k
         // push the latest certificate onto the path_so_far
738
2.16k
         path_so_far.push_back(last);
739
2.16k
         certs_seen.emplace(fprint);
740
2.16k
741
2.16k
         // push a deletion marker on the stack for backtracing later
742
2.16k
         stack.push_back({std::shared_ptr<const X509_Certificate>(nullptr),false});
743
2.16k
744
2.16k
         for(const auto& trusted_cert : trusted_issuers)
745
0
            {
746
0
            stack.push_back({trusted_cert,true});
747
0
            }
748
2.16k
749
2.16k
         for(const auto& misc : misc_issuers)
750
7.01k
            {
751
7.01k
            stack.push_back({misc,false});
752
7.01k
            }
753
2.16k
         }
754
9.86k
      }
755
687
756
687
   // could not construct any potentially valid path
757
687
   if(cert_paths_out.empty())
758
687
      {
759
687
      if(stats.empty())
760
0
         throw Internal_Error("X509 path building failed for unknown reasons");
761
687
      else
762
687
         // arbitrarily return the first error
763
687
         return stats[0];
764
0
      }
765
0
   else
766
0
      {
767
0
      return Certificate_Status_Code::OK;
768
0
      }
769
687
   }
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
0
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
0
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
0
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
0
808
0
            chain_status[i].insert(code);
809
0
            }
810
0
         }
811
0
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
0
828
0
   Certificate_Status_Code overall_status = Certificate_Status_Code::OK;
829
0
830
0
   // 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
0
         // 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
687
   {
856
687
   if(end_certs.empty())
857
0
      {
858
0
      throw Invalid_Argument("x509_path_validate called with no subjects");
859
0
      }
860
687
861
687
   std::shared_ptr<const X509_Certificate> end_entity(std::make_shared<const X509_Certificate>(end_certs[0]));
862
687
   std::vector<std::shared_ptr<const X509_Certificate>> end_entity_extra;
863
1.39k
   for(size_t i = 1; i < end_certs.size(); ++i)
864
706
      {
865
706
      end_entity_extra.push_back(std::make_shared<const X509_Certificate>(end_certs[i]));
866
706
      }
867
687
868
687
   std::vector<std::vector<std::shared_ptr<const X509_Certificate>>> cert_paths;
869
687
   Certificate_Status_Code path_building_result = PKIX::build_all_certificate_paths(cert_paths, trusted_roots, end_entity, end_entity_extra);
870
687
871
687
   // If we cannot successfully build a chain to a trusted self-signed root, stop now
872
687
   if(path_building_result != Certificate_Status_Code::OK)
873
687
      {
874
687
      return Path_Validation_Result(path_building_result);
875
687
      }
876
0
877
0
   std::vector<Path_Validation_Result> error_results;
878
0
   // 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
0
887
0
      CertificatePathStatusCodes crl_status =
888
0
         PKIX::check_crl(cert_path, trusted_roots, ref_time);
889
0
890
0
      CertificatePathStatusCodes ocsp_status;
891
0
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
0
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
         }
907
0
908
0
      PKIX::merge_revocation_status(status, crl_status, ocsp_status,
909
0
                                    restrictions.require_revocation_information(),
910
0
                                    restrictions.ocsp_all_intermediates());
911
0
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
0
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
0
969
0
   std::vector<Certificate_Store*> trusted_roots;
970
0
   trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
971
0
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
687
   {
984
687
   if(key_strength <= 80)
985
0
      { m_trusted_hashes.insert("SHA-160"); }
986
687
987
687
   m_trusted_hashes.insert("SHA-224");
988
687
   m_trusted_hashes.insert("SHA-256");
989
687
   m_trusted_hashes.insert("SHA-384");
990
687
   m_trusted_hashes.insert("SHA-512");
991
687
   }
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
0
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
687
   {
1043
687
   return (result() == Certificate_Status_Code::VERIFIED ||
1044
687
           result() == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
1045
687
           result() == Certificate_Status_Code::VALID_CRL_CHECKED);
1046
687
   }
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
687
   {
1063
687
   return status_string(result());
1064
687
   }
1065
1066
const char* Path_Validation_Result::status_string(Certificate_Status_Code code)
1067
687
   {
1068
687
   if(const char* s = to_string(code))
1069
687
      return s;
1070
0
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
0
   // 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
}