Coverage Report

Created: 2020-02-14 15:38

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