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
|