Coverage Report

Created: 2024-09-19 09:45

/proc/self/cwd/source/common/tls/utility.cc
Line
Count
Source (jump to first uncovered line)
1
#include "source/common/tls/utility.h"
2
3
#include <cstdint>
4
#include <vector>
5
6
#include "source/common/common/assert.h"
7
#include "source/common/common/empty_string.h"
8
#include "source/common/common/safe_memcpy.h"
9
#include "source/common/network/address_impl.h"
10
#include "source/common/protobuf/utility.h"
11
12
#include "absl/strings/str_join.h"
13
#include "openssl/x509v3.h"
14
15
namespace Envoy {
16
namespace Extensions {
17
namespace TransportSockets {
18
namespace Tls {
19
20
static constexpr int MAX_OID_LENGTH = 256;
21
22
static constexpr absl::string_view SSL_ERROR_UNKNOWN_ERROR_MESSAGE = "UNKNOWN_ERROR";
23
24
Envoy::Ssl::CertificateDetailsPtr Utility::certificateDetails(X509* cert, const std::string& path,
25
0
                                                              TimeSource& time_source) {
26
0
  Envoy::Ssl::CertificateDetailsPtr certificate_details =
27
0
      std::make_unique<envoy::admin::v3::CertificateDetails>();
28
0
  certificate_details->set_path(path);
29
0
  certificate_details->set_serial_number(Utility::getSerialNumberFromCertificate(*cert));
30
0
  const auto days_until_expiry = Utility::getDaysUntilExpiration(cert, time_source).value_or(0);
31
0
  certificate_details->set_days_until_expiration(days_until_expiry);
32
33
0
  ProtobufWkt::Timestamp* valid_from = certificate_details->mutable_valid_from();
34
0
  TimestampUtil::systemClockToTimestamp(Utility::getValidFrom(*cert), *valid_from);
35
0
  ProtobufWkt::Timestamp* expiration_time = certificate_details->mutable_expiration_time();
36
0
  TimestampUtil::systemClockToTimestamp(Utility::getExpirationTime(*cert), *expiration_time);
37
38
0
  for (auto& dns_san : Utility::getSubjectAltNames(*cert, GEN_DNS)) {
39
0
    envoy::admin::v3::SubjectAlternateName& subject_alt_name =
40
0
        *certificate_details->add_subject_alt_names();
41
0
    subject_alt_name.set_dns(dns_san);
42
0
  }
43
0
  for (auto& uri_san : Utility::getSubjectAltNames(*cert, GEN_URI)) {
44
0
    envoy::admin::v3::SubjectAlternateName& subject_alt_name =
45
0
        *certificate_details->add_subject_alt_names();
46
0
    subject_alt_name.set_uri(uri_san);
47
0
  }
48
0
  for (auto& ip_san : Utility::getSubjectAltNames(*cert, GEN_IPADD)) {
49
0
    envoy::admin::v3::SubjectAlternateName& subject_alt_name =
50
0
        *certificate_details->add_subject_alt_names();
51
0
    subject_alt_name.set_ip_address(ip_san);
52
0
  }
53
0
  return certificate_details;
54
0
}
55
56
0
bool Utility::labelWildcardMatch(absl::string_view dns_label, absl::string_view pattern) {
57
0
  constexpr char glob = '*';
58
  // Check the special case of a single * pattern, as it's common.
59
0
  if (pattern.size() == 1 && pattern[0] == glob) {
60
0
    return true;
61
0
  }
62
  // Only valid if wildcard character appear once.
63
0
  if (std::count(pattern.begin(), pattern.end(), glob) == 1) {
64
0
    std::vector<absl::string_view> split_pattern = absl::StrSplit(pattern, glob);
65
0
    return (pattern.size() <= dns_label.size() + 1) &&
66
0
           absl::StartsWith(dns_label, split_pattern[0]) &&
67
0
           absl::EndsWith(dns_label, split_pattern[1]);
68
0
  }
69
0
  return false;
70
0
}
71
72
0
bool Utility::dnsNameMatch(absl::string_view dns_name, absl::string_view pattern) {
73
  // A-label ACE prefix https://www.rfc-editor.org/rfc/rfc5890#section-2.3.2.5.
74
0
  constexpr absl::string_view ACE_prefix = "xn--";
75
0
  const std::string lower_case_dns_name = absl::AsciiStrToLower(dns_name);
76
0
  const std::string lower_case_pattern = absl::AsciiStrToLower(pattern);
77
0
  if (lower_case_dns_name == lower_case_pattern) {
78
0
    return true;
79
0
  }
80
81
0
  std::vector<absl::string_view> split_pattern =
82
0
      absl::StrSplit(lower_case_pattern, absl::MaxSplits('.', 1));
83
0
  std::vector<absl::string_view> split_dns_name =
84
0
      absl::StrSplit(lower_case_dns_name, absl::MaxSplits('.', 1));
85
86
  // dns name and pattern should contain more than 1 label to match.
87
0
  if (split_pattern.size() < 2 || split_dns_name.size() < 2) {
88
0
    return false;
89
0
  }
90
  // Only the left-most label in the pattern contains wildcard '*' and is not an A-label.
91
0
  if ((split_pattern[0].find('*') != absl::string_view::npos) &&
92
0
      (split_pattern[1].find('*') == absl::string_view::npos) &&
93
0
      (!absl::StartsWith(split_pattern[0], ACE_prefix))) {
94
0
    return (split_dns_name[1] == split_pattern[1]) &&
95
0
           labelWildcardMatch(split_dns_name[0], split_pattern[0]);
96
0
  }
97
98
0
  return false;
99
0
}
100
101
namespace {
102
103
enum class CertName { Issuer, Subject };
104
105
/**
106
 * Retrieves a name from a certificate and formats it as an RFC 2253 name.
107
 * @param cert the certificate.
108
 * @param desired_name the desired name (Issuer or Subject) to retrieve from the certificate.
109
 * @return std::string returns the desired name formatted as an RFC 2253 name.
110
 */
111
0
std::string getRFC2253NameFromCertificate(X509& cert, CertName desired_name) {
112
0
  bssl::UniquePtr<BIO> buf(BIO_new(BIO_s_mem()));
113
0
  RELEASE_ASSERT(buf != nullptr, "");
114
115
0
  X509_NAME* name = nullptr;
116
0
  switch (desired_name) {
117
0
  case CertName::Issuer:
118
0
    name = X509_get_issuer_name(&cert);
119
0
    break;
120
0
  case CertName::Subject:
121
0
    name = X509_get_subject_name(&cert);
122
0
    break;
123
0
  }
124
125
  // flags=XN_FLAG_RFC2253 is the documented parameter for single-line output in RFC 2253 format.
126
  // Example from the RFC:
127
  //   * Single value per Relative Distinguished Name (RDN): CN=Steve Kille,O=Isode Limited,C=GB
128
  //   * Multivalue output in first RDN: OU=Sales+CN=J. Smith,O=Widget Inc.,C=US
129
  //   * Quoted comma in Organization: CN=L. Eagle,O=Sue\, Grabbit and Runn,C=GB
130
0
  X509_NAME_print_ex(buf.get(), name, 0 /* indent */, XN_FLAG_RFC2253);
131
132
0
  const uint8_t* data;
133
0
  size_t data_len;
134
0
  int rc = BIO_mem_contents(buf.get(), &data, &data_len);
135
0
  ASSERT(rc == 1);
136
0
  return {reinterpret_cast<const char*>(data), data_len};
137
0
}
138
139
} // namespace
140
141
0
const ASN1_TIME& epochASN1Time() {
142
0
  static ASN1_TIME* e = []() -> ASN1_TIME* {
143
0
    ASN1_TIME* epoch = ASN1_TIME_new();
144
0
    const time_t epoch_time = 0;
145
0
    RELEASE_ASSERT(ASN1_TIME_set(epoch, epoch_time) != nullptr, "");
146
0
    return epoch;
147
0
  }();
148
0
  return *e;
149
0
}
150
151
0
inline bssl::UniquePtr<ASN1_TIME> currentASN1Time(TimeSource& time_source) {
152
0
  bssl::UniquePtr<ASN1_TIME> current_asn_time(ASN1_TIME_new());
153
0
  const time_t current_time = std::chrono::system_clock::to_time_t(time_source.systemTime());
154
0
  RELEASE_ASSERT(ASN1_TIME_set(current_asn_time.get(), current_time) != nullptr, "");
155
0
  return current_asn_time;
156
0
}
157
158
0
std::string Utility::getSerialNumberFromCertificate(X509& cert) {
159
0
  ASN1_INTEGER* serial_number = X509_get_serialNumber(&cert);
160
0
  BIGNUM num_bn;
161
0
  BN_init(&num_bn);
162
0
  ASN1_INTEGER_to_BN(serial_number, &num_bn);
163
0
  char* char_serial_number = BN_bn2hex(&num_bn);
164
0
  BN_free(&num_bn);
165
0
  if (char_serial_number != nullptr) {
166
0
    std::string serial_number(char_serial_number);
167
0
    OPENSSL_free(char_serial_number);
168
0
    return serial_number;
169
0
  }
170
0
  return "";
171
0
}
172
173
0
std::vector<std::string> Utility::getSubjectAltNames(X509& cert, int type, bool skip_unsupported) {
174
0
  std::vector<std::string> subject_alt_names;
175
0
  bssl::UniquePtr<GENERAL_NAMES> san_names(
176
0
      static_cast<GENERAL_NAMES*>(X509_get_ext_d2i(&cert, NID_subject_alt_name, nullptr, nullptr)));
177
0
  if (san_names == nullptr) {
178
0
    return subject_alt_names;
179
0
  }
180
0
  for (const GENERAL_NAME* san : san_names.get()) {
181
0
    if (san->type == type) {
182
0
      if (skip_unsupported) {
183
        // An IP SAN for an unsupported IP version will throw an exception.
184
        // TODO(ggreenway): remove this when IP address construction no longer throws.
185
0
        TRY_NEEDS_AUDIT_ADDRESS { subject_alt_names.push_back(generalNameAsString(san)); }
186
0
        END_TRY CATCH(const EnvoyException& e,
187
0
                      { ENVOY_LOG_MISC(debug, "Error reading SAN, value skipped: {}", e.what()); });
188
0
      } else {
189
0
        subject_alt_names.push_back(generalNameAsString(san));
190
0
      }
191
0
    }
192
0
  }
193
0
  return subject_alt_names;
194
0
}
195
196
0
std::string Utility::generalNameAsString(const GENERAL_NAME* general_name) {
197
0
  std::string san;
198
0
  ASN1_STRING* str = nullptr;
199
0
  switch (general_name->type) {
200
0
  case GEN_DNS:
201
0
    str = general_name->d.dNSName;
202
0
    san.assign(reinterpret_cast<const char*>(ASN1_STRING_data(str)), ASN1_STRING_length(str));
203
0
    break;
204
0
  case GEN_URI:
205
0
    str = general_name->d.uniformResourceIdentifier;
206
0
    san.assign(reinterpret_cast<const char*>(ASN1_STRING_data(str)), ASN1_STRING_length(str));
207
0
    break;
208
0
  case GEN_EMAIL:
209
0
    str = general_name->d.rfc822Name;
210
0
    san.assign(reinterpret_cast<const char*>(ASN1_STRING_data(str)), ASN1_STRING_length(str));
211
0
    break;
212
0
  case GEN_IPADD: {
213
0
    if (general_name->d.ip->length == 4) {
214
0
      sockaddr_in sin;
215
0
      memset(&sin, 0, sizeof(sin));
216
0
      sin.sin_port = 0;
217
0
      sin.sin_family = AF_INET;
218
0
      safeMemcpyUnsafeSrc(&sin.sin_addr, general_name->d.ip->data);
219
0
      Network::Address::Ipv4Instance addr(&sin);
220
0
      san = addr.ip()->addressAsString();
221
0
    } else if (general_name->d.ip->length == 16) {
222
0
      sockaddr_in6 sin6;
223
0
      memset(&sin6, 0, sizeof(sin6));
224
0
      sin6.sin6_port = 0;
225
0
      sin6.sin6_family = AF_INET6;
226
0
      safeMemcpyUnsafeSrc(&sin6.sin6_addr, general_name->d.ip->data);
227
0
      Network::Address::Ipv6Instance addr(sin6);
228
0
      san = addr.ip()->addressAsString();
229
0
    }
230
0
    break;
231
0
  }
232
0
  case GEN_OTHERNAME: {
233
0
    ASN1_TYPE* value = general_name->d.otherName->value;
234
0
    if (value == nullptr) {
235
0
      break;
236
0
    }
237
0
    switch (value->type) {
238
0
    case V_ASN1_NULL:
239
0
      break;
240
0
    case V_ASN1_BOOLEAN:
241
0
      san = value->value.boolean ? "true" : "false";
242
0
      break;
243
0
    case V_ASN1_ENUMERATED:
244
0
    case V_ASN1_INTEGER: {
245
0
      BIGNUM san_bn;
246
0
      BN_init(&san_bn);
247
0
      value->type == V_ASN1_ENUMERATED ? ASN1_ENUMERATED_to_BN(value->value.enumerated, &san_bn)
248
0
                                       : ASN1_INTEGER_to_BN(value->value.integer, &san_bn);
249
0
      char* san_char = BN_bn2dec(&san_bn);
250
0
      BN_free(&san_bn);
251
0
      if (san_char != nullptr) {
252
0
        san.assign(san_char);
253
0
        OPENSSL_free(san_char);
254
0
      }
255
0
      break;
256
0
    }
257
0
    case V_ASN1_OBJECT: {
258
0
      char tmp_obj[MAX_OID_LENGTH];
259
0
      int obj_len = OBJ_obj2txt(tmp_obj, MAX_OID_LENGTH, value->value.object, 1);
260
0
      if (obj_len > MAX_OID_LENGTH || obj_len < 0) {
261
0
        break;
262
0
      }
263
0
      san.assign(tmp_obj);
264
0
      break;
265
0
    }
266
0
    case V_ASN1_BIT_STRING: {
267
0
      ASN1_BIT_STRING* tmp_str = value->value.bit_string;
268
0
      san.assign(reinterpret_cast<const char*>(ASN1_STRING_data(tmp_str)),
269
0
                 ASN1_STRING_length(tmp_str));
270
0
      break;
271
0
    }
272
0
    case V_ASN1_OCTET_STRING: {
273
0
      ASN1_OCTET_STRING* tmp_str = value->value.octet_string;
274
0
      san.assign(reinterpret_cast<const char*>(ASN1_STRING_data(tmp_str)),
275
0
                 ASN1_STRING_length(tmp_str));
276
0
      break;
277
0
    }
278
0
    case V_ASN1_PRINTABLESTRING: {
279
0
      ASN1_PRINTABLESTRING* tmp_str = value->value.printablestring;
280
0
      san.assign(reinterpret_cast<const char*>(ASN1_STRING_data(tmp_str)),
281
0
                 ASN1_STRING_length(tmp_str));
282
0
      break;
283
0
    }
284
0
    case V_ASN1_T61STRING: {
285
0
      ASN1_T61STRING* tmp_str = value->value.t61string;
286
0
      san.assign(reinterpret_cast<const char*>(ASN1_STRING_data(tmp_str)),
287
0
                 ASN1_STRING_length(tmp_str));
288
0
      break;
289
0
    }
290
0
    case V_ASN1_IA5STRING: {
291
0
      ASN1_IA5STRING* tmp_str = value->value.ia5string;
292
0
      san.assign(reinterpret_cast<const char*>(ASN1_STRING_data(tmp_str)),
293
0
                 ASN1_STRING_length(tmp_str));
294
0
      break;
295
0
    }
296
0
    case V_ASN1_GENERALSTRING: {
297
0
      ASN1_GENERALSTRING* tmp_str = value->value.generalstring;
298
0
      san.assign(reinterpret_cast<const char*>(ASN1_STRING_data(tmp_str)),
299
0
                 ASN1_STRING_length(tmp_str));
300
0
      break;
301
0
    }
302
0
    case V_ASN1_BMPSTRING: {
303
      // `ASN1_BMPSTRING` is encoded using `UCS-4`, which needs conversion to UTF-8.
304
0
      unsigned char* tmp = nullptr;
305
0
      if (ASN1_STRING_to_UTF8(&tmp, value->value.bmpstring) < 0) {
306
0
        break;
307
0
      }
308
0
      san.assign(reinterpret_cast<const char*>(tmp));
309
0
      OPENSSL_free(tmp);
310
0
      break;
311
0
    }
312
0
    case V_ASN1_UNIVERSALSTRING: {
313
      // `ASN1_UNIVERSALSTRING` is encoded using `UCS-4`, which needs conversion to UTF-8.
314
0
      unsigned char* tmp = nullptr;
315
0
      if (ASN1_STRING_to_UTF8(&tmp, value->value.universalstring) < 0) {
316
0
        break;
317
0
      }
318
0
      san.assign(reinterpret_cast<const char*>(tmp));
319
0
      OPENSSL_free(tmp);
320
0
      break;
321
0
    }
322
0
    case V_ASN1_UTCTIME: {
323
0
      ASN1_UTCTIME* tmp_str = value->value.utctime;
324
0
      san.assign(reinterpret_cast<const char*>(ASN1_STRING_data(tmp_str)),
325
0
                 ASN1_STRING_length(tmp_str));
326
0
      break;
327
0
    }
328
0
    case V_ASN1_GENERALIZEDTIME: {
329
0
      ASN1_GENERALIZEDTIME* tmp_str = value->value.generalizedtime;
330
0
      san.assign(reinterpret_cast<const char*>(ASN1_STRING_data(tmp_str)),
331
0
                 ASN1_STRING_length(tmp_str));
332
0
      break;
333
0
    }
334
0
    case V_ASN1_VISIBLESTRING: {
335
0
      ASN1_VISIBLESTRING* tmp_str = value->value.visiblestring;
336
0
      san.assign(reinterpret_cast<const char*>(ASN1_STRING_data(tmp_str)),
337
0
                 ASN1_STRING_length(tmp_str));
338
0
      break;
339
0
    }
340
0
    case V_ASN1_UTF8STRING: {
341
0
      ASN1_UTF8STRING* tmp_str = value->value.utf8string;
342
0
      san.assign(reinterpret_cast<const char*>(ASN1_STRING_data(tmp_str)),
343
0
                 ASN1_STRING_length(tmp_str));
344
0
      break;
345
0
    }
346
0
    case V_ASN1_SET: {
347
0
      ASN1_STRING* tmp_str = value->value.set;
348
0
      san.assign(reinterpret_cast<const char*>(ASN1_STRING_data(tmp_str)),
349
0
                 ASN1_STRING_length(tmp_str));
350
0
      break;
351
0
    }
352
0
    case V_ASN1_SEQUENCE: {
353
0
      ASN1_STRING* tmp_str = value->value.sequence;
354
0
      san.assign(reinterpret_cast<const char*>(ASN1_STRING_data(tmp_str)),
355
0
                 ASN1_STRING_length(tmp_str));
356
0
      break;
357
0
    }
358
0
    default:
359
0
      break;
360
0
    }
361
0
  }
362
0
  }
363
0
  return san;
364
0
}
365
366
0
std::string Utility::getIssuerFromCertificate(X509& cert) {
367
0
  return getRFC2253NameFromCertificate(cert, CertName::Issuer);
368
0
}
369
370
0
std::string Utility::getSubjectFromCertificate(X509& cert) {
371
0
  return getRFC2253NameFromCertificate(cert, CertName::Subject);
372
0
}
373
374
absl::optional<uint32_t> Utility::getDaysUntilExpiration(const X509* cert,
375
0
                                                         TimeSource& time_source) {
376
0
  if (cert == nullptr) {
377
0
    return absl::make_optional(std::numeric_limits<uint32_t>::max());
378
0
  }
379
0
  int days, seconds;
380
0
  if (ASN1_TIME_diff(&days, &seconds, currentASN1Time(time_source).get(),
381
0
                     X509_get0_notAfter(cert))) {
382
0
    if (days >= 0 && seconds >= 0) {
383
0
      return absl::make_optional(days);
384
0
    }
385
0
  }
386
0
  return absl::nullopt;
387
0
}
388
389
0
std::vector<std::string> Utility::getCertificateExtensionOids(X509& cert) {
390
0
  std::vector<std::string> extension_oids;
391
392
0
  int count = X509_get_ext_count(&cert);
393
0
  for (int pos = 0; pos < count; pos++) {
394
0
    X509_EXTENSION* extension = X509_get_ext(&cert, pos);
395
0
    RELEASE_ASSERT(extension != nullptr, "");
396
397
0
    char oid[MAX_OID_LENGTH];
398
0
    int obj_len = OBJ_obj2txt(oid, MAX_OID_LENGTH, X509_EXTENSION_get_object(extension),
399
0
                              1 /* always_return_oid */);
400
0
    if (obj_len > 0 && obj_len < MAX_OID_LENGTH) {
401
0
      extension_oids.push_back(oid);
402
0
    }
403
0
  }
404
0
  return extension_oids;
405
0
}
406
407
absl::string_view Utility::getCertificateExtensionValue(X509& cert,
408
0
                                                        absl::string_view extension_name) {
409
0
  bssl::UniquePtr<ASN1_OBJECT> oid(
410
0
      OBJ_txt2obj(std::string(extension_name).c_str(), 1 /* don't search names */));
411
0
  if (oid == nullptr) {
412
0
    return {};
413
0
  }
414
415
0
  int pos = X509_get_ext_by_OBJ(&cert, oid.get(), -1);
416
0
  if (pos < 0) {
417
0
    return {};
418
0
  }
419
420
0
  X509_EXTENSION* extension = X509_get_ext(&cert, pos);
421
0
  if (extension == nullptr) {
422
0
    return {};
423
0
  }
424
425
0
  const ASN1_OCTET_STRING* octet_string = X509_EXTENSION_get_data(extension);
426
0
  RELEASE_ASSERT(octet_string != nullptr, "");
427
428
  // Return the entire DER-encoded value for this extension. Correct decoding depends on
429
  // knowledge of the expected structure of the extension's value.
430
0
  const unsigned char* octet_string_data = ASN1_STRING_get0_data(octet_string);
431
0
  const int octet_string_length = ASN1_STRING_length(octet_string);
432
433
0
  return {reinterpret_cast<const char*>(octet_string_data),
434
0
          static_cast<absl::string_view::size_type>(octet_string_length)};
435
0
}
436
437
0
SystemTime Utility::getValidFrom(const X509& cert) {
438
0
  int days, seconds;
439
0
  int rc = ASN1_TIME_diff(&days, &seconds, &epochASN1Time(), X509_get0_notBefore(&cert));
440
0
  ASSERT(rc == 1);
441
  // Casting to <time_t (64bit)> to prevent multiplication overflow when certificate valid-from date
442
  // beyond 2038-01-19T03:14:08Z.
443
0
  return std::chrono::system_clock::from_time_t(static_cast<time_t>(days) * 24 * 60 * 60 + seconds);
444
0
}
445
446
0
SystemTime Utility::getExpirationTime(const X509& cert) {
447
0
  int days, seconds;
448
0
  int rc = ASN1_TIME_diff(&days, &seconds, &epochASN1Time(), X509_get0_notAfter(&cert));
449
0
  ASSERT(rc == 1);
450
  // Casting to <time_t (64bit)> to prevent multiplication overflow when certificate not-after date
451
  // beyond 2038-01-19T03:14:08Z.
452
0
  return std::chrono::system_clock::from_time_t(static_cast<time_t>(days) * 24 * 60 * 60 + seconds);
453
0
}
454
455
0
absl::optional<std::string> Utility::getLastCryptoError() {
456
0
  auto err = ERR_get_error();
457
458
0
  if (err != 0) {
459
0
    char errbuf[256];
460
461
0
    ERR_error_string_n(err, errbuf, sizeof(errbuf));
462
0
    return std::string(errbuf);
463
0
  }
464
465
0
  return absl::nullopt;
466
0
}
467
468
0
absl::string_view Utility::getErrorDescription(int err) {
469
0
  const char* description = SSL_error_description(err);
470
0
  if (description) {
471
0
    return description;
472
0
  }
473
474
0
  IS_ENVOY_BUG("BoringSSL error had occurred: SSL_error_description() returned nullptr");
475
0
  return SSL_ERROR_UNKNOWN_ERROR_MESSAGE;
476
0
}
477
478
0
std::string Utility::getX509VerificationErrorInfo(X509_STORE_CTX* ctx) {
479
0
  const int n = X509_STORE_CTX_get_error(ctx);
480
0
  const int depth = X509_STORE_CTX_get_error_depth(ctx);
481
0
  std::string error_details =
482
0
      absl::StrCat("X509_verify_cert: certificate verification error at depth ", depth, ": ",
483
0
                   X509_verify_cert_error_string(n));
484
0
  return error_details;
485
0
}
486
487
std::vector<std::string> Utility::mapX509Stack(stack_st_X509& stack,
488
0
                                               std::function<std::string(X509&)> field_extractor) {
489
0
  std::vector<std::string> result;
490
0
  if (sk_X509_num(&stack) <= 0) {
491
0
    IS_ENVOY_BUG("x509 stack is empty or NULL");
492
0
    return result;
493
0
  }
494
0
  if (field_extractor == nullptr) {
495
0
    IS_ENVOY_BUG("field_extractor is nullptr");
496
0
    return result;
497
0
  }
498
499
0
  for (uint64_t i = 0; i < sk_X509_num(&stack); i++) {
500
0
    X509* cert = sk_X509_value(&stack, i);
501
0
    if (!cert) {
502
0
      result.push_back(""); // Add an empty string so it's clear something was omitted.
503
0
    } else {
504
0
      result.push_back(field_extractor(*cert));
505
0
    }
506
0
  }
507
508
0
  return result;
509
0
}
510
511
} // namespace Tls
512
} // namespace TransportSockets
513
} // namespace Extensions
514
} // namespace Envoy