diff options
Diffstat (limited to 'src/lib/x509/x509path.cpp')
-rw-r--r-- | src/lib/x509/x509path.cpp | 696 |
1 files changed, 472 insertions, 224 deletions
diff --git a/src/lib/x509/x509path.cpp b/src/lib/x509/x509path.cpp index f0b07e5fc..946539bab 100644 --- a/src/lib/x509/x509path.cpp +++ b/src/lib/x509/x509path.cpp @@ -1,13 +1,12 @@ /* * X.509 Certificate Path Validation -* (C) 2010,2011,2012,2014 Jack Lloyd +* (C) 2010,2011,2012,2014,2016 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include <botan/x509path.h> #include <botan/ocsp.h> -#include <botan/http_util.h> #include <botan/parsing.h> #include <botan/pubkey.h> #include <botan/oids.h> @@ -15,98 +14,58 @@ #include <chrono> #include <vector> #include <set> -#include <future> -namespace Botan { - -namespace { - -std::shared_ptr<const X509_Certificate> -find_issuing_cert(const X509_Certificate& cert, - Certificate_Store& end_certs, - const std::vector<Certificate_Store*>& certstores) - { - const X509_DN issuer_dn = cert.issuer_dn(); - const std::vector<byte> auth_key_id = cert.authority_key_id(); - - if(std::shared_ptr<const X509_Certificate> c = end_certs.find_cert(issuer_dn, auth_key_id)) - { - if(*c != cert) - return c; - } - - for(size_t i = 0; i != certstores.size(); ++i) - { - if(std::shared_ptr<const X509_Certificate> c = certstores[i]->find_cert(issuer_dn, auth_key_id)) - return c; - } - - return nullptr; - } - -std::shared_ptr<const X509_CRL> find_crls_for(const X509_Certificate& cert, - const std::vector<Certificate_Store*>& certstores) - { - for(size_t i = 0; i != certstores.size(); ++i) - { - if(std::shared_ptr<const X509_CRL> crl = certstores[i]->find_crl_for(cert)) - return crl; - } - -#if 0 - const std::string crl_url = cert.crl_distribution_point(); - if(crl_url != "") - { - std::cout << "Downloading CRL " << crl_url << "\n"; - auto http = HTTP::GET_sync(crl_url); - - std::cout << http.status_message() << "\n"; - - http.throw_unless_ok(); - // check the mime type - - std::unique_ptr<X509_CRL> crl(new X509_CRL(http.body())); - - return crl.release(); - } +#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL) + #include <future> + #include <botan/http_util.h> #endif - return nullptr; - } +namespace Botan { +/* +* PKIX path validation +*/ std::vector<std::set<Certificate_Status_Code>> -check_chain(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path, - const Path_Validation_Restrictions& restrictions, - const std::vector<Certificate_Store*>& certstores, - std::chrono::system_clock::time_point ref_time) +PKIX::check_chain(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path, + std::chrono::system_clock::time_point ref_time, + const std::string& hostname, + Usage_Type usage, + size_t min_signature_algo_strength, + const std::set<std::string>& trusted_hashes) { - const std::set<std::string>& trusted_hashes = restrictions.trusted_hashes(); + if(cert_path.empty()) + throw Invalid_Argument("PKIX::check_chain cert_path empty"); const bool self_signed_ee_cert = (cert_path.size() == 1); X509_Time validation_time(ref_time); - std::vector<std::future<OCSP::Response>> ocsp_responses; - std::vector<std::set<Certificate_Status_Code>> cert_status(cert_path.size()); + if(!hostname.empty() && !cert_path[0]->matches_dns_name(hostname)) + cert_status[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH); + + if(!cert_path[0]->allowed_usage(usage)) + cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE); + for(size_t i = 0; i != cert_path.size(); ++i) { std::set<Certificate_Status_Code>& status = cert_status.at(i); const bool at_self_signed_root = (i == cert_path.size() - 1); - std::shared_ptr<const X509_Certificate> subject = cert_path[i]; + const std::shared_ptr<const X509_Certificate>& subject = cert_path[i]; + + const std::shared_ptr<const X509_Certificate>& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)]; - std::shared_ptr<const X509_Certificate> issuer = cert_path[at_self_signed_root ? (i) : (i + 1)]; + if(at_self_signed_root && (issuer->is_self_signed() == false)) + { + status.insert(Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT); + } - if(i == 0 || restrictions.ocsp_all_intermediates()) + if(subject->issuer_dn() != issuer->subject_dn()) { - // certstore[0] is treated as trusted for OCSP (FIXME) - if(certstores.size() > 1) - ocsp_responses.push_back( - std::async(std::launch::async, - OCSP::online_check, *issuer, *subject, certstores[0])); + status.insert(Certificate_Status_Code::CHAIN_NAME_MISMATCH); } // Check all certs for valid time range @@ -128,21 +87,23 @@ check_chain(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_pat if(!issuer_key) { - status.insert(Certificate_Status_Code::SIGNATURE_ERROR); + status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID); } else { if(subject->check_signature(*issuer_key) == false) + { status.insert(Certificate_Status_Code::SIGNATURE_ERROR); + } - if(issuer_key->estimated_strength() < restrictions.minimum_key_strength()) + if(issuer_key->estimated_strength() < min_signature_algo_strength) status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK); } - // Allow untrusted hashes on self-signed roots - if(!trusted_hashes.empty() && !at_self_signed_root) + // Ignore untrusted hashes on self-signed roots + if(trusted_hashes.size() > 0 && !at_self_signed_root) { - if(!trusted_hashes.count(subject->hash_used_for_signature())) + if(trusted_hashes.count(subject->hash_used_for_signature()) == 0) status.insert(Certificate_Status_Code::UNTRUSTED_HASH); } @@ -154,6 +115,20 @@ check_chain(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_pat } } + return cert_status; + } + +std::vector<std::set<Certificate_Status_Code>> +PKIX::check_ocsp(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path, + const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses, + const std::vector<Certificate_Store*>& trusted_certstores, + std::chrono::system_clock::time_point ref_time) + { + if(cert_path.empty()) + throw Invalid_Argument("PKIX::check_ocsp cert_path empty"); + + std::vector<std::set<Certificate_Status_Code>> cert_status(cert_path.size() - 1); + for(size_t i = 0; i != cert_path.size() - 1; ++i) { std::set<Certificate_Status_Code>& status = cert_status.at(i); @@ -161,129 +136,459 @@ check_chain(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_pat std::shared_ptr<const X509_Certificate> subject = cert_path.at(i); std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1); - if(i < ocsp_responses.size()) + if(i < ocsp_responses.size() && (ocsp_responses.at(i) != nullptr)) { try { - OCSP::Response ocsp = ocsp_responses[i].get(); + Certificate_Status_Code ocsp_signature_status = ocsp_responses.at(i)->check_signature(trusted_certstores, cert_path); + + if(ocsp_signature_status == Certificate_Status_Code::OCSP_SIGNATURE_OK) + { + // Signature ok, so check the claimed status + Certificate_Status_Code ocsp_status = ocsp_responses.at(i)->status_for(*ca, *subject, ref_time); + status.insert(ocsp_status); + } + else + { + // Some signature problem + status.insert(ocsp_signature_status); + } + } + catch(Exception& e) + { + status.insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID); + } + } + } - auto ocsp_status = ocsp.status_for(*ca, *subject); + return cert_status; + } - status.insert(ocsp_status); +std::vector<std::set<Certificate_Status_Code>> +PKIX::check_crl(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path, + const std::vector<std::shared_ptr<const X509_CRL>>& crls, + std::chrono::system_clock::time_point ref_time) + { + if(cert_path.empty()) + throw Invalid_Argument("PKIX::check_crl cert_path empty"); - //std::cout << "OCSP status: " << Path_Validation_Result::status_string(ocsp_status) << "\n"; + std::vector<std::set<Certificate_Status_Code>> cert_status(cert_path.size()); + const X509_Time validation_time(ref_time); - // Either way we have a definitive answer, no need to check CRLs - if(ocsp_status == Certificate_Status_Code::CERT_IS_REVOKED) - return cert_status; - else if(ocsp_status == Certificate_Status_Code::OCSP_RESPONSE_GOOD) - continue; + for(size_t i = 0; i != cert_path.size() - 1; ++i) + { + std::set<Certificate_Status_Code>& status = cert_status.at(i); + + if(i < crls.size() && crls.at(i)) + { + std::shared_ptr<const X509_Certificate> subject = cert_path.at(i); + std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1); + + if(!ca->allowed_usage(CRL_SIGN)) + status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER); + + if(validation_time < X509_Time(crls[i]->this_update())) + status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID); + + if(validation_time > X509_Time(crls[i]->next_update())) + status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED); + + if(crls[i]->check_signature(ca->subject_public_key()) == false) + status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE); + + status.insert(Certificate_Status_Code::VALID_CRL_CHECKED); + + if(crls[i]->is_revoked(*subject)) + status.insert(Certificate_Status_Code::CERT_IS_REVOKED); + } + } + + return cert_status; + } + +std::vector<std::set<Certificate_Status_Code>> +PKIX::check_crl(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path, + const std::vector<Certificate_Store*>& certstores, + std::chrono::system_clock::time_point ref_time) + { + if(cert_path.empty()) + throw Invalid_Argument("PKIX::check_crl cert_path empty"); + + if(certstores.empty()) + throw Invalid_Argument("PKIX::check_crl certstores empty"); + + std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size()); + + for(size_t i = 0; i != cert_path.size(); ++i) + { + BOTAN_ASSERT(cert_path[i] != nullptr, "Not null"); + for(size_t c = 0; c != certstores.size(); ++c) + { + crls[i] = certstores[c]->find_crl_for(*cert_path.at(i)); + if(crls[i]) + break; + } + } + + return PKIX::check_crl(cert_path, crls, ref_time); + } + +#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL) + +std::vector<std::set<Certificate_Status_Code>> +PKIX::check_ocsp_online(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path, + const std::vector<Certificate_Store*>& trusted_certstores, + std::chrono::system_clock::time_point ref_time, + std::chrono::milliseconds timeout, + bool ocsp_check_intermediate_CAs) + { + if(cert_path.empty()) + throw Invalid_Argument("PKIX::check_ocsp_online cert_path empty"); + + std::vector<std::future<std::shared_ptr<const OCSP::Response>>> ocsp_response_futures; + + size_t to_ocsp = 1; + + if(ocsp_check_intermediate_CAs) + to_ocsp = cert_path.size() - 1; + if(cert_path.size() == 1) + to_ocsp = 0; + + for(size_t i = 0; i < to_ocsp; ++i) + { + const std::shared_ptr<const X509_Certificate>& subject = cert_path.at(i); + const std::shared_ptr<const X509_Certificate>& issuer = cert_path.at(i+1); + + if(subject->ocsp_responder() == "") + { + ocsp_response_futures.emplace_back(std::async(std::launch::deferred, [&]{ + throw Exception("No OCSP responder URL set for this certificate"); + return std::shared_ptr<const OCSP::Response>(); + })); } - catch(std::exception&) + else { - //std::cout << "OCSP error: " << e.what() << "\n"; + ocsp_response_futures.emplace_back(std::async(std::launch::async, [&]{ + OCSP::Request req(*issuer, *subject); + + auto http = HTTP::POST_sync(subject->ocsp_responder(), + "application/ocsp-request", + req.BER_encode()); + + http.throw_unless_ok(); + // Check the MIME type? + + return std::make_shared<const OCSP::Response>(http.body()); + })); } - } + } - std::shared_ptr<const X509_CRL> crl_p = find_crls_for(*subject, certstores); + std::vector<std::shared_ptr<const OCSP::Response>> ocsp_responses(ocsp_response_futures.size()); - if(!crl_p) + for(size_t pass = 1; pass < 3; ++pass) + { + for(size_t i = 0; i < ocsp_response_futures.size(); ++i) { - if(restrictions.require_revocation_information()) - status.insert(Certificate_Status_Code::NO_REVOCATION_DATA); - continue; + try + { + if(ocsp_responses[i] == nullptr && ocsp_response_futures[i].valid()) + { + std::future_status status = ocsp_response_futures[i].wait_for(timeout); + + if(status == std::future_status::ready || + status == std::future_status::deferred) + { + ocsp_responses[i] = ocsp_response_futures[i].get(); + } + } + } + catch(std::exception&) + { + // value is default initialized to null, no need to do anything + } } + } - const X509_CRL& crl = *crl_p; + return PKIX::check_ocsp(cert_path, ocsp_responses, trusted_certstores, ref_time); + } - if(!ca->allowed_usage(CRL_SIGN)) - status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER); +std::vector<std::set<Certificate_Status_Code>> +PKIX::check_crl_online(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path, + const std::vector<Certificate_Store*>& certstores, + std::chrono::system_clock::time_point ref_time, + std::chrono::milliseconds timeout) + { + if(cert_path.empty()) + throw Invalid_Argument("PKIX::check_crl_online cert_path empty"); + if(certstores.empty()) + throw Invalid_Argument("PKIX::check_crl_online certstores empty"); - if(validation_time < X509_Time(crl.this_update())) - status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID); + std::vector<std::future<std::shared_ptr<const X509_CRL>>> future_crls; + std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size()); - if(validation_time > X509_Time(crl.next_update())) - status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED); + for(size_t i = 0; i != cert_path.size(); ++i) + { + for(size_t c = 0; c != certstores.size(); ++i) + { + crls[i] = certstores[i]->find_crl_for(*cert_path[i]); + if(crls[i]) + break; + } - if(crl.check_signature(ca->subject_public_key()) == false) - status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE); + // TODO: check if CRL is expired and re-request? - if(crl.is_revoked(*subject)) - status.insert(Certificate_Status_Code::CERT_IS_REVOKED); + // Only request if we don't already have a CRL + if(crls[i]) + { + /* + We already have a CRL, so just insert this empty one to hold a place in the vector + so that indexes match up + */ + future_crls.emplace_back(std::future<std::shared_ptr<const X509_CRL>>()); + } + else if(cert_path[i]->crl_distribution_point() == "") + { + // Avoid creating a thread for this case + future_crls.emplace_back(std::async(std::launch::deferred, [&]{ + throw Exception("No CRL distribution point for this certificate"); + return std::shared_ptr<const X509_CRL>(); + })); + } + else + { + future_crls.emplace_back(std::async(std::launch::async, [&]() { + auto http = HTTP::GET_sync(cert_path[i]->crl_distribution_point()); + http.throw_unless_ok(); + // check the mime type? + return std::make_shared<const X509_CRL>(http.body()); + })); + } } - if(self_signed_ee_cert) - cert_status.back().insert(Certificate_Status_Code::CANNOT_ESTABLISH_TRUST); + for(size_t i = 0; i != future_crls.size(); ++i) + { + if(future_crls[i].valid()) + { + try + { + std::future_status status = future_crls[i].wait_for(timeout); + + if(status == std::future_status::ready) + { + crls[i] = future_crls[i].get(); + } + } + catch(std::exception& e) + { + // crls[i] left null + } + } + } - return cert_status; + return PKIX::check_crl(cert_path, crls, ref_time); } -} +#endif -Path_Validation_Result x509_path_validate( - const std::vector<X509_Certificate>& end_certs, - const Path_Validation_Restrictions& restrictions, - const std::vector<Certificate_Store*>& certstores, - const std::string& hostname, - Usage_Type usage, - std::chrono::system_clock::time_point validation_time) +Certificate_Status_Code +PKIX::build_certificate_path(std::vector<std::shared_ptr<const X509_Certificate>>& cert_path, + const std::vector<Certificate_Store*>& trusted_certstores, + const std::shared_ptr<const X509_Certificate>& end_entity, + const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra) { - if(end_certs.empty()) - throw Invalid_Argument("x509_path_validate called with no subjects"); - - std::vector<std::shared_ptr<const X509_Certificate>> cert_path; - std::vector<std::shared_ptr<const X509_Certificate>> end_certs_sharedptr; - cert_path.push_back(std::make_shared<X509_Certificate>(end_certs[0])); - - for(auto c: end_certs) - end_certs_sharedptr.push_back(std::make_shared<const X509_Certificate>(c)); + if(end_entity->is_self_signed()) + { + return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST; + } /* * This is an inelegant but functional way of preventing path loops * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate * fingerprints in the path. If there is a duplicate, we error out. + * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc. */ std::set<std::string> certs_seen; - Certificate_Store_Overlay extra(end_certs_sharedptr); + cert_path.push_back(end_entity); + certs_seen.insert(end_entity->fingerprint("SHA-256")); + + Certificate_Store_In_Memory ee_extras; + for(size_t i = 0; i != end_entity_extra.size(); ++i) + ee_extras.add_certificate(end_entity_extra[i]); // iterate until we reach a root or cannot find the issuer - while(!cert_path.back()->is_self_signed()) + for(;;) { - std::shared_ptr<const X509_Certificate> cert = find_issuing_cert(*cert_path.back(), extra, certstores); - if(!cert) - return Path_Validation_Result(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND); + const X509_Certificate& last = *cert_path.back(); + const X509_DN issuer_dn = last.issuer_dn(); + const std::vector<byte> auth_key_id = last.authority_key_id(); + + std::shared_ptr<const X509_Certificate> issuer; + bool trusted_issuer = false; + + for(Certificate_Store* store : trusted_certstores) + { + issuer = store->find_cert(issuer_dn, auth_key_id); + if(issuer) + { + trusted_issuer = true; + break; + } + } + + if(!issuer) + { + // fall back to searching supplemental certs + issuer = ee_extras.find_cert(issuer_dn, auth_key_id); + } + + if(!issuer) + return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND; + + const std::string fprint = issuer->fingerprint("SHA-256"); + + if(certs_seen.count(fprint) > 0) // already seen? + return Certificate_Status_Code::CERT_CHAIN_LOOP; - const std::string fprint = cert->fingerprint("SHA-256"); - if(certs_seen.count(fprint) > 0) - return Path_Validation_Result(Certificate_Status_Code::CERT_CHAIN_LOOP); certs_seen.insert(fprint); - cert_path.push_back(cert); + cert_path.push_back(issuer); + + if(issuer->is_self_signed()) + { + if(trusted_issuer) + { + return Certificate_Status_Code::OK; + } + else + { + return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST; + } + } } + } - std::vector<std::set<Certificate_Status_Code>> res = - check_chain(cert_path, restrictions, certstores, validation_time); +namespace { - if(!hostname.empty() && !cert_path[0]->matches_dns_name(hostname)) - res[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH); +void merge_results(std::vector<std::set<Certificate_Status_Code>>& results, + const std::vector<std::set<Certificate_Status_Code>>& crl, + const std::vector<std::set<Certificate_Status_Code>>& ocsp, + bool require_rev_on_end_entity, + bool require_rev_on_intermediates) + { + for(size_t i = 0; i != results.size() - 1; ++i) + { + bool had_crl = false, had_ocsp = false; - if(!cert_path[0]->allowed_usage(usage)) - res[0].insert(Certificate_Status_Code::INVALID_USAGE); + if(i < crl.size() && crl[i].size() > 0) + { + for(auto&& code : crl[i]) + { + if(code == Certificate_Status_Code::VALID_CRL_CHECKED) + { + had_crl = true; + } + results[i].insert(code); + } + } + + if(i < ocsp.size() && ocsp[i].size() > 0) + { + for(auto&& code : ocsp[i]) + { + if(code == Certificate_Status_Code::OCSP_RESPONSE_GOOD) + { + had_ocsp = true; + } + + results[i].insert(code); + } + } + + if(had_crl == false && had_ocsp == false) + { + if((require_rev_on_end_entity && i == 0) || + (require_rev_on_intermediates && i > 0)) + { + results[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA); + } + } + } + } + +} + +Path_Validation_Result BOTAN_DLL x509_path_validate( + const std::vector<X509_Certificate>& end_certs, + const Path_Validation_Restrictions& restrictions, + const std::vector<Certificate_Store*>& trusted_roots, + const std::string& hostname, + Usage_Type usage, + std::chrono::system_clock::time_point ref_time, + std::chrono::milliseconds ocsp_timeout) + { + if(end_certs.empty()) + throw Invalid_Argument("x509_path_validate called with no subjects"); + + std::shared_ptr<const X509_Certificate> end_entity(std::make_shared<const X509_Certificate>(end_certs[0])); + std::vector<std::shared_ptr<const X509_Certificate>> end_entity_extra; + for(size_t i = 1; i < end_certs.size(); ++i) + { + end_entity_extra.push_back(std::make_shared<const X509_Certificate>(end_certs[i])); + } + + std::vector<std::shared_ptr<const X509_Certificate>> cert_path; + Certificate_Status_Code path_building_result = + PKIX::build_certificate_path(cert_path, trusted_roots, end_entity, end_entity_extra); + + if(path_building_result != Certificate_Status_Code::OK) + { + return Path_Validation_Result(path_building_result); + } - return Path_Validation_Result(res, std::move(cert_path)); + std::vector<std::set<Certificate_Status_Code>> status = + PKIX::check_chain(cert_path, ref_time, + hostname, usage, + restrictions.minimum_key_strength(), + restrictions.trusted_hashes()); + + if(path_building_result != Certificate_Status_Code::OK) + status[0].insert(path_building_result); + + std::vector<std::set<Certificate_Status_Code>> crl_status = + PKIX::check_crl(cert_path, trusted_roots, ref_time); + + std::vector<std::set<Certificate_Status_Code>> ocsp_status; + + if(ocsp_timeout != std::chrono::milliseconds(0)) + { +#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL) + ocsp_status = PKIX::check_ocsp_online(cert_path, trusted_roots, ref_time, + ocsp_timeout, restrictions.ocsp_all_intermediates()); +#else + ocsp_status.resize(1); + ocsp_status[0].insert(Certificate_Status_Code::OCSP_NO_HTTP); +#endif + } + + merge_results(status, crl_status, ocsp_status, + restrictions.require_revocation_information(), + restrictions.ocsp_all_intermediates()); + + return Path_Validation_Result(status, std::move(cert_path)); } Path_Validation_Result x509_path_validate( const X509_Certificate& end_cert, const Path_Validation_Restrictions& restrictions, - const std::vector<Certificate_Store*>& certstores, + const std::vector<Certificate_Store*>& trusted_roots, const std::string& hostname, Usage_Type usage, - std::chrono::system_clock::time_point when) + std::chrono::system_clock::time_point when, + std::chrono::milliseconds ocsp_timeout) { std::vector<X509_Certificate> certs; certs.push_back(end_cert); - return x509_path_validate(certs, restrictions, certstores, hostname, usage, when); + return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout); } Path_Validation_Result x509_path_validate( @@ -292,12 +597,13 @@ Path_Validation_Result x509_path_validate( const Certificate_Store& store, const std::string& hostname, Usage_Type usage, - std::chrono::system_clock::time_point when) + std::chrono::system_clock::time_point when, + std::chrono::milliseconds ocsp_timeout) { - std::vector<Certificate_Store*> certstores; - certstores.push_back(const_cast<Certificate_Store*>(&store)); + std::vector<Certificate_Store*> trusted_roots; + trusted_roots.push_back(const_cast<Certificate_Store*>(&store)); - return x509_path_validate(end_certs, restrictions, certstores, hostname, usage, when); + return x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout); } Path_Validation_Result x509_path_validate( @@ -306,22 +612,23 @@ Path_Validation_Result x509_path_validate( const Certificate_Store& store, const std::string& hostname, Usage_Type usage, - std::chrono::system_clock::time_point when) + std::chrono::system_clock::time_point when, + std::chrono::milliseconds ocsp_timeout) { std::vector<X509_Certificate> certs; certs.push_back(end_cert); - std::vector<Certificate_Store*> certstores; - certstores.push_back(const_cast<Certificate_Store*>(&store)); + std::vector<Certificate_Store*> trusted_roots; + trusted_roots.push_back(const_cast<Certificate_Store*>(&store)); - return x509_path_validate(certs, restrictions, certstores, hostname, usage, when); + return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout); } Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev, size_t key_strength, - bool ocsp_all) : + bool ocsp_intermediates) : m_require_revocation_information(require_rev), - m_ocsp_all_intermediates(ocsp_all), + m_ocsp_all_intermediates(ocsp_intermediates), m_minimum_key_strength(key_strength) { if(key_strength <= 80) @@ -335,7 +642,7 @@ Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev, Path_Validation_Result::Path_Validation_Result(std::vector<std::set<Certificate_Status_Code>> status, std::vector<std::shared_ptr<const X509_Certificate>>&& cert_chain) : - m_overall(Certificate_Status_Code::VERIFIED), + m_overall(Certificate_Status_Code::OK), m_all_status(status), m_cert_path(cert_chain) { @@ -345,9 +652,11 @@ Path_Validation_Result::Path_Validation_Result(std::vector<std::set<Certificate_ if(!s.empty()) { auto worst = *s.rbegin(); - // Leave OCSP confirmations on cert-level status only - if(worst != Certificate_Status_Code::OCSP_RESPONSE_GOOD) + // Leave informative OCSP/CRL confirmations on cert-level status only + if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS) + { m_overall = worst; + } } } } @@ -372,10 +681,9 @@ std::set<std::string> Path_Validation_Result::trusted_hashes() const bool Path_Validation_Result::successful_validation() const { - if(result() == Certificate_Status_Code::VERIFIED || - result() == Certificate_Status_Code::OCSP_RESPONSE_GOOD) - return true; - return false; + return (result() == Certificate_Status_Code::VERIFIED || + result() == Certificate_Status_Code::OCSP_RESPONSE_GOOD || + result() == Certificate_Status_Code::VALID_CRL_CHECKED); } std::string Path_Validation_Result::result_string() const @@ -385,68 +693,8 @@ std::string Path_Validation_Result::result_string() const const char* Path_Validation_Result::status_string(Certificate_Status_Code code) { - switch(code) - { - case Certificate_Status_Code::VERIFIED: - return "Verified"; - case Certificate_Status_Code::OCSP_RESPONSE_GOOD: - return "OCSP response good"; - case Certificate_Status_Code::NO_REVOCATION_DATA: - return "No revocation data"; - case Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK: - return "Signature method too weak"; - case Certificate_Status_Code::UNTRUSTED_HASH: - return "Untrusted hash"; - - case Certificate_Status_Code::CERT_NOT_YET_VALID: - return "Certificate is not yet valid"; - case Certificate_Status_Code::CERT_HAS_EXPIRED: - return "Certificate has expired"; - case Certificate_Status_Code::OCSP_NOT_YET_VALID: - return "OCSP is not yet valid"; - case Certificate_Status_Code::OCSP_HAS_EXPIRED: - return "OCSP has expired"; - case Certificate_Status_Code::CRL_NOT_YET_VALID: - return "CRL is not yet valid"; - case Certificate_Status_Code::CRL_HAS_EXPIRED: - return "CRL has expired"; - - case Certificate_Status_Code::CERT_ISSUER_NOT_FOUND: - return "Certificate issuer not found"; - case Certificate_Status_Code::CANNOT_ESTABLISH_TRUST: - return "Cannot establish trust"; - case Certificate_Status_Code::CERT_CHAIN_LOOP: - return "Loop in certificate chain"; - - case Certificate_Status_Code::POLICY_ERROR: - return "Policy error"; - case Certificate_Status_Code::INVALID_USAGE: - return "Invalid usage"; - case Certificate_Status_Code::CERT_CHAIN_TOO_LONG: - return "Certificate chain too long"; - case Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER: - return "CA certificate not allowed to issue certs"; - case Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER: - return "CA certificate not allowed to issue CRLs"; - case Certificate_Status_Code::OCSP_CERT_NOT_LISTED: - return "OCSP cert not listed"; - case Certificate_Status_Code::OCSP_BAD_STATUS: - return "OCSP bad status"; - case Certificate_Status_Code::CERT_NAME_NOMATCH: - return "Certificate does not match provided name"; - case Certificate_Status_Code::NAME_CONSTRAINT_ERROR: - return "Certificate does not pass name constraint"; - case Certificate_Status_Code::UNKNOWN_CRITICAL_EXTENSION: - return "Unknown critical extension encountered"; - - case Certificate_Status_Code::CERT_IS_REVOKED: - return "Certificate is revoked"; - case Certificate_Status_Code::CRL_BAD_SIGNATURE: - return "CRL bad signature"; - case Certificate_Status_Code::SIGNATURE_ERROR: - return "Signature error"; - // intentionally no default so we are warned - } + if(const char* s = to_string(code)) + return s; return "Unknown error"; } |