LCOV - code coverage report
Current view: top level - source/extensions/transport_sockets/tls - utility.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 0 243 0.0 %
Date: 2024-01-05 06:35:25 Functions: 0 19 0.0 %

          Line data    Source code
       1             : #include "source/extensions/transport_sockets/tls/utility.h"
       2             : 
       3             : #include <cstdint>
       4             : 
       5             : #include "source/common/common/assert.h"
       6             : #include "source/common/common/empty_string.h"
       7             : #include "source/common/common/safe_memcpy.h"
       8             : #include "source/common/network/address_impl.h"
       9             : #include "source/common/protobuf/utility.h"
      10             : 
      11             : #include "absl/strings/str_join.h"
      12             : #include "openssl/x509v3.h"
      13             : 
      14             : namespace Envoy {
      15             : namespace Extensions {
      16             : namespace TransportSockets {
      17             : namespace Tls {
      18             : 
      19             : static constexpr absl::string_view SSL_ERROR_UNKNOWN_ERROR_MESSAGE = "UNKNOWN_ERROR";
      20             : 
      21             : Envoy::Ssl::CertificateDetailsPtr Utility::certificateDetails(X509* cert, const std::string& path,
      22           0 :                                                               TimeSource& time_source) {
      23           0 :   Envoy::Ssl::CertificateDetailsPtr certificate_details =
      24           0 :       std::make_unique<envoy::admin::v3::CertificateDetails>();
      25           0 :   certificate_details->set_path(path);
      26           0 :   certificate_details->set_serial_number(Utility::getSerialNumberFromCertificate(*cert));
      27           0 :   const auto days_until_expiry = Utility::getDaysUntilExpiration(cert, time_source).value_or(0);
      28           0 :   certificate_details->set_days_until_expiration(days_until_expiry);
      29             : 
      30           0 :   ProtobufWkt::Timestamp* valid_from = certificate_details->mutable_valid_from();
      31           0 :   TimestampUtil::systemClockToTimestamp(Utility::getValidFrom(*cert), *valid_from);
      32           0 :   ProtobufWkt::Timestamp* expiration_time = certificate_details->mutable_expiration_time();
      33           0 :   TimestampUtil::systemClockToTimestamp(Utility::getExpirationTime(*cert), *expiration_time);
      34             : 
      35           0 :   for (auto& dns_san : Utility::getSubjectAltNames(*cert, GEN_DNS)) {
      36           0 :     envoy::admin::v3::SubjectAlternateName& subject_alt_name =
      37           0 :         *certificate_details->add_subject_alt_names();
      38           0 :     subject_alt_name.set_dns(dns_san);
      39           0 :   }
      40           0 :   for (auto& uri_san : Utility::getSubjectAltNames(*cert, GEN_URI)) {
      41           0 :     envoy::admin::v3::SubjectAlternateName& subject_alt_name =
      42           0 :         *certificate_details->add_subject_alt_names();
      43           0 :     subject_alt_name.set_uri(uri_san);
      44           0 :   }
      45           0 :   for (auto& ip_san : Utility::getSubjectAltNames(*cert, GEN_IPADD)) {
      46           0 :     envoy::admin::v3::SubjectAlternateName& subject_alt_name =
      47           0 :         *certificate_details->add_subject_alt_names();
      48           0 :     subject_alt_name.set_ip_address(ip_san);
      49           0 :   }
      50           0 :   return certificate_details;
      51           0 : }
      52             : 
      53           0 : bool Utility::labelWildcardMatch(absl::string_view dns_label, absl::string_view pattern) {
      54           0 :   constexpr char glob = '*';
      55             :   // Check the special case of a single * pattern, as it's common.
      56           0 :   if (pattern.size() == 1 && pattern[0] == glob) {
      57           0 :     return true;
      58           0 :   }
      59             :   // Only valid if wildcard character appear once.
      60           0 :   if (std::count(pattern.begin(), pattern.end(), glob) == 1) {
      61           0 :     std::vector<absl::string_view> split_pattern = absl::StrSplit(pattern, glob);
      62           0 :     return (pattern.size() <= dns_label.size() + 1) &&
      63           0 :            absl::StartsWith(dns_label, split_pattern[0]) &&
      64           0 :            absl::EndsWith(dns_label, split_pattern[1]);
      65           0 :   }
      66           0 :   return false;
      67           0 : }
      68             : 
      69           0 : bool Utility::dnsNameMatch(absl::string_view dns_name, absl::string_view pattern) {
      70             :   // A-label ACE prefix https://www.rfc-editor.org/rfc/rfc5890#section-2.3.2.5.
      71           0 :   constexpr absl::string_view ACE_prefix = "xn--";
      72           0 :   const std::string lower_case_dns_name = absl::AsciiStrToLower(dns_name);
      73           0 :   const std::string lower_case_pattern = absl::AsciiStrToLower(pattern);
      74           0 :   if (lower_case_dns_name == lower_case_pattern) {
      75           0 :     return true;
      76           0 :   }
      77             : 
      78           0 :   std::vector<absl::string_view> split_pattern =
      79           0 :       absl::StrSplit(lower_case_pattern, absl::MaxSplits('.', 1));
      80           0 :   std::vector<absl::string_view> split_dns_name =
      81           0 :       absl::StrSplit(lower_case_dns_name, absl::MaxSplits('.', 1));
      82             : 
      83             :   // dns name and pattern should contain more than 1 label to match.
      84           0 :   if (split_pattern.size() < 2 || split_dns_name.size() < 2) {
      85           0 :     return false;
      86           0 :   }
      87             :   // Only the left-most label in the pattern contains wildcard '*' and is not an A-label.
      88           0 :   if ((split_pattern[0].find('*') != absl::string_view::npos) &&
      89           0 :       (split_pattern[1].find('*') == absl::string_view::npos) &&
      90           0 :       (!absl::StartsWith(split_pattern[0], ACE_prefix))) {
      91           0 :     return (split_dns_name[1] == split_pattern[1]) &&
      92           0 :            labelWildcardMatch(split_dns_name[0], split_pattern[0]);
      93           0 :   }
      94             : 
      95           0 :   return false;
      96           0 : }
      97             : 
      98             : namespace {
      99             : 
     100             : enum class CertName { Issuer, Subject };
     101             : 
     102             : /**
     103             :  * Retrieves a name from a certificate and formats it as an RFC 2253 name.
     104             :  * @param cert the certificate.
     105             :  * @param desired_name the desired name (Issuer or Subject) to retrieve from the certificate.
     106             :  * @return std::string returns the desired name formatted as an RFC 2253 name.
     107             :  */
     108           0 : std::string getRFC2253NameFromCertificate(X509& cert, CertName desired_name) {
     109           0 :   bssl::UniquePtr<BIO> buf(BIO_new(BIO_s_mem()));
     110           0 :   RELEASE_ASSERT(buf != nullptr, "");
     111             : 
     112           0 :   X509_NAME* name = nullptr;
     113           0 :   switch (desired_name) {
     114           0 :   case CertName::Issuer:
     115           0 :     name = X509_get_issuer_name(&cert);
     116           0 :     break;
     117           0 :   case CertName::Subject:
     118           0 :     name = X509_get_subject_name(&cert);
     119           0 :     break;
     120           0 :   }
     121             : 
     122             :   // flags=XN_FLAG_RFC2253 is the documented parameter for single-line output in RFC 2253 format.
     123             :   // Example from the RFC:
     124             :   //   * Single value per Relative Distinguished Name (RDN): CN=Steve Kille,O=Isode Limited,C=GB
     125             :   //   * Multivalue output in first RDN: OU=Sales+CN=J. Smith,O=Widget Inc.,C=US
     126             :   //   * Quoted comma in Organization: CN=L. Eagle,O=Sue\, Grabbit and Runn,C=GB
     127           0 :   X509_NAME_print_ex(buf.get(), name, 0 /* indent */, XN_FLAG_RFC2253);
     128             : 
     129           0 :   const uint8_t* data;
     130           0 :   size_t data_len;
     131           0 :   int rc = BIO_mem_contents(buf.get(), &data, &data_len);
     132           0 :   ASSERT(rc == 1);
     133           0 :   return {reinterpret_cast<const char*>(data), data_len};
     134           0 : }
     135             : 
     136             : } // namespace
     137             : 
     138           0 : const ASN1_TIME& epochASN1Time() {
     139           0 :   static ASN1_TIME* e = []() -> ASN1_TIME* {
     140           0 :     ASN1_TIME* epoch = ASN1_TIME_new();
     141           0 :     const time_t epoch_time = 0;
     142           0 :     RELEASE_ASSERT(ASN1_TIME_set(epoch, epoch_time) != nullptr, "");
     143           0 :     return epoch;
     144           0 :   }();
     145           0 :   return *e;
     146           0 : }
     147             : 
     148           0 : inline bssl::UniquePtr<ASN1_TIME> currentASN1Time(TimeSource& time_source) {
     149           0 :   bssl::UniquePtr<ASN1_TIME> current_asn_time(ASN1_TIME_new());
     150           0 :   const time_t current_time = std::chrono::system_clock::to_time_t(time_source.systemTime());
     151           0 :   RELEASE_ASSERT(ASN1_TIME_set(current_asn_time.get(), current_time) != nullptr, "");
     152           0 :   return current_asn_time;
     153           0 : }
     154             : 
     155           0 : std::string Utility::getSerialNumberFromCertificate(X509& cert) {
     156           0 :   ASN1_INTEGER* serial_number = X509_get_serialNumber(&cert);
     157           0 :   BIGNUM num_bn;
     158           0 :   BN_init(&num_bn);
     159           0 :   ASN1_INTEGER_to_BN(serial_number, &num_bn);
     160           0 :   char* char_serial_number = BN_bn2hex(&num_bn);
     161           0 :   BN_free(&num_bn);
     162           0 :   if (char_serial_number != nullptr) {
     163           0 :     std::string serial_number(char_serial_number);
     164           0 :     OPENSSL_free(char_serial_number);
     165           0 :     return serial_number;
     166           0 :   }
     167           0 :   return "";
     168           0 : }
     169             : 
     170           0 : std::vector<std::string> Utility::getSubjectAltNames(X509& cert, int type) {
     171           0 :   std::vector<std::string> subject_alt_names;
     172           0 :   bssl::UniquePtr<GENERAL_NAMES> san_names(
     173           0 :       static_cast<GENERAL_NAMES*>(X509_get_ext_d2i(&cert, NID_subject_alt_name, nullptr, nullptr)));
     174           0 :   if (san_names == nullptr) {
     175           0 :     return subject_alt_names;
     176           0 :   }
     177           0 :   for (const GENERAL_NAME* san : san_names.get()) {
     178           0 :     if (san->type == type) {
     179           0 :       subject_alt_names.push_back(generalNameAsString(san));
     180           0 :     }
     181           0 :   }
     182           0 :   return subject_alt_names;
     183           0 : }
     184             : 
     185           0 : std::string Utility::generalNameAsString(const GENERAL_NAME* general_name) {
     186           0 :   std::string san;
     187           0 :   switch (general_name->type) {
     188           0 :   case GEN_DNS: {
     189           0 :     ASN1_STRING* str = general_name->d.dNSName;
     190           0 :     san.assign(reinterpret_cast<const char*>(ASN1_STRING_data(str)), ASN1_STRING_length(str));
     191           0 :     break;
     192           0 :   }
     193           0 :   case GEN_URI: {
     194           0 :     ASN1_STRING* str = general_name->d.uniformResourceIdentifier;
     195           0 :     san.assign(reinterpret_cast<const char*>(ASN1_STRING_data(str)), ASN1_STRING_length(str));
     196           0 :     break;
     197           0 :   }
     198           0 :   case GEN_EMAIL: {
     199           0 :     ASN1_STRING* str = general_name->d.rfc822Name;
     200           0 :     san.assign(reinterpret_cast<const char*>(ASN1_STRING_data(str)), ASN1_STRING_length(str));
     201           0 :     break;
     202           0 :   }
     203           0 :   case GEN_IPADD: {
     204           0 :     if (general_name->d.ip->length == 4) {
     205           0 :       sockaddr_in sin;
     206           0 :       memset(&sin, 0, sizeof(sin));
     207           0 :       sin.sin_port = 0;
     208           0 :       sin.sin_family = AF_INET;
     209           0 :       safeMemcpyUnsafeSrc(&sin.sin_addr, general_name->d.ip->data);
     210           0 :       Network::Address::Ipv4Instance addr(&sin);
     211           0 :       san = addr.ip()->addressAsString();
     212           0 :     } else if (general_name->d.ip->length == 16) {
     213           0 :       sockaddr_in6 sin6;
     214           0 :       memset(&sin6, 0, sizeof(sin6));
     215           0 :       sin6.sin6_port = 0;
     216           0 :       sin6.sin6_family = AF_INET6;
     217           0 :       safeMemcpyUnsafeSrc(&sin6.sin6_addr, general_name->d.ip->data);
     218           0 :       Network::Address::Ipv6Instance addr(sin6);
     219           0 :       san = addr.ip()->addressAsString();
     220           0 :     }
     221           0 :     break;
     222           0 :   }
     223           0 :   }
     224           0 :   return san;
     225           0 : }
     226             : 
     227           0 : std::string Utility::getIssuerFromCertificate(X509& cert) {
     228           0 :   return getRFC2253NameFromCertificate(cert, CertName::Issuer);
     229           0 : }
     230             : 
     231           0 : std::string Utility::getSubjectFromCertificate(X509& cert) {
     232           0 :   return getRFC2253NameFromCertificate(cert, CertName::Subject);
     233           0 : }
     234             : 
     235             : absl::optional<uint32_t> Utility::getDaysUntilExpiration(const X509* cert,
     236           0 :                                                          TimeSource& time_source) {
     237           0 :   if (cert == nullptr) {
     238           0 :     return absl::make_optional(std::numeric_limits<uint32_t>::max());
     239           0 :   }
     240           0 :   int days, seconds;
     241           0 :   if (ASN1_TIME_diff(&days, &seconds, currentASN1Time(time_source).get(),
     242           0 :                      X509_get0_notAfter(cert))) {
     243           0 :     if (days >= 0 && seconds >= 0) {
     244           0 :       return absl::make_optional(days);
     245           0 :     }
     246           0 :   }
     247           0 :   return absl::nullopt;
     248           0 : }
     249             : 
     250             : absl::string_view Utility::getCertificateExtensionValue(X509& cert,
     251           0 :                                                         absl::string_view extension_name) {
     252           0 :   bssl::UniquePtr<ASN1_OBJECT> oid(
     253           0 :       OBJ_txt2obj(std::string(extension_name).c_str(), 1 /* don't search names */));
     254           0 :   if (oid == nullptr) {
     255           0 :     return {};
     256           0 :   }
     257             : 
     258           0 :   int pos = X509_get_ext_by_OBJ(&cert, oid.get(), -1);
     259           0 :   if (pos < 0) {
     260           0 :     return {};
     261           0 :   }
     262             : 
     263           0 :   X509_EXTENSION* extension = X509_get_ext(&cert, pos);
     264           0 :   if (extension == nullptr) {
     265           0 :     return {};
     266           0 :   }
     267             : 
     268           0 :   const ASN1_OCTET_STRING* octet_string = X509_EXTENSION_get_data(extension);
     269           0 :   RELEASE_ASSERT(octet_string != nullptr, "");
     270             : 
     271             :   // Return the entire DER-encoded value for this extension. Correct decoding depends on
     272             :   // knowledge of the expected structure of the extension's value.
     273           0 :   const unsigned char* octet_string_data = ASN1_STRING_get0_data(octet_string);
     274           0 :   const int octet_string_length = ASN1_STRING_length(octet_string);
     275             : 
     276           0 :   return {reinterpret_cast<const char*>(octet_string_data),
     277           0 :           static_cast<absl::string_view::size_type>(octet_string_length)};
     278           0 : }
     279             : 
     280           0 : SystemTime Utility::getValidFrom(const X509& cert) {
     281           0 :   int days, seconds;
     282           0 :   int rc = ASN1_TIME_diff(&days, &seconds, &epochASN1Time(), X509_get0_notBefore(&cert));
     283           0 :   ASSERT(rc == 1);
     284             :   // Casting to <time_t (64bit)> to prevent multiplication overflow when certificate valid-from date
     285             :   // beyond 2038-01-19T03:14:08Z.
     286           0 :   return std::chrono::system_clock::from_time_t(static_cast<time_t>(days) * 24 * 60 * 60 + seconds);
     287           0 : }
     288             : 
     289           0 : SystemTime Utility::getExpirationTime(const X509& cert) {
     290           0 :   int days, seconds;
     291           0 :   int rc = ASN1_TIME_diff(&days, &seconds, &epochASN1Time(), X509_get0_notAfter(&cert));
     292           0 :   ASSERT(rc == 1);
     293             :   // Casting to <time_t (64bit)> to prevent multiplication overflow when certificate not-after date
     294             :   // beyond 2038-01-19T03:14:08Z.
     295           0 :   return std::chrono::system_clock::from_time_t(static_cast<time_t>(days) * 24 * 60 * 60 + seconds);
     296           0 : }
     297             : 
     298           0 : absl::optional<std::string> Utility::getLastCryptoError() {
     299           0 :   auto err = ERR_get_error();
     300             : 
     301           0 :   if (err != 0) {
     302           0 :     char errbuf[256];
     303             : 
     304           0 :     ERR_error_string_n(err, errbuf, sizeof(errbuf));
     305           0 :     return std::string(errbuf);
     306           0 :   }
     307             : 
     308           0 :   return absl::nullopt;
     309           0 : }
     310             : 
     311           0 : absl::string_view Utility::getErrorDescription(int err) {
     312           0 :   const char* description = SSL_error_description(err);
     313           0 :   if (description) {
     314           0 :     return description;
     315           0 :   }
     316             : 
     317           0 :   IS_ENVOY_BUG("BoringSSL error had occurred: SSL_error_description() returned nullptr");
     318           0 :   return SSL_ERROR_UNKNOWN_ERROR_MESSAGE;
     319           0 : }
     320             : 
     321           0 : std::string Utility::getX509VerificationErrorInfo(X509_STORE_CTX* ctx) {
     322           0 :   const int n = X509_STORE_CTX_get_error(ctx);
     323           0 :   const int depth = X509_STORE_CTX_get_error_depth(ctx);
     324           0 :   std::string error_details =
     325           0 :       absl::StrCat("X509_verify_cert: certificate verification error at depth ", depth, ": ",
     326           0 :                    X509_verify_cert_error_string(n));
     327           0 :   return error_details;
     328           0 : }
     329             : 
     330             : } // namespace Tls
     331             : } // namespace TransportSockets
     332             : } // namespace Extensions
     333             : } // namespace Envoy

Generated by: LCOV version 1.15