Skip to content

Commit dbccef7

Browse files
Huilin Chenmeta-codesync[bot]
authored andcommitted
Add OpenSSLCertUtils::getSubjectAltNameEntries
Summary: - Add new `getSubjectAltNameEntries`, which includes all DNS, IP, and URI entries. - Refactor `getSubjectAltNames` and `getSubjectAltNameURIs`to use the result of `getSubjectAltNameEntries`, and filter for the correct types. Reviewed By: mingtaoy Differential Revision: D116701420 fbshipit-source-id: 1c77d2e58955609ba0ee7e3bcb555474266d056f
1 parent 8dc549c commit dbccef7

5 files changed

Lines changed: 120 additions & 33 deletions

File tree

folly/ssl/BUCK

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ fb_dirsync_cpp_library(
4343
],
4444
exported_deps = [
4545
":openssl_ptr_types",
46+
"//folly:network_address",
4647
"//folly:optional",
4748
"//folly/io:iobuf",
4849
"//folly/portability:openssl",

folly/ssl/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ folly_add_library(
2626
folly_string
2727
EXPORTED_DEPS
2828
folly_io_iobuf
29+
folly_network_address
2930
folly_optional
3031
folly_portability_openssl
3132
folly_ssl_openssl_ptr_types

folly/ssl/OpenSSLCertUtils.cpp

Lines changed: 63 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
#include <folly/ssl/OpenSSLCertUtils.h>
1818

19+
#include <optional>
20+
1921
#include <folly/FileUtil.h>
2022
#include <folly/ScopeGuard.h>
2123
#include <folly/String.h>
@@ -49,6 +51,45 @@ std::string asn1ToString(ASN1_STRING* a) {
4951
}
5052
}
5153

54+
std::optional<std::string> getStringSubjectAltName(const ASN1_STRING* value) {
55+
const auto* data =
56+
reinterpret_cast<const char*>(ASN1_STRING_get0_data(value));
57+
const int len = ASN1_STRING_length(value);
58+
if (!data || len <= 0) {
59+
return std::nullopt;
60+
}
61+
return std::string(data, len);
62+
}
63+
64+
std::optional<IPAddress> getIpSubjectAltName(const ASN1_STRING* value) {
65+
const auto* data = ASN1_STRING_get0_data(value);
66+
const int len = ASN1_STRING_length(value);
67+
if (!data || (len != 4 && len != 16)) {
68+
return std::nullopt;
69+
}
70+
return IPAddress::fromBinary(ByteRange(data, static_cast<size_t>(len)));
71+
}
72+
73+
std::optional<GeneralName> getSubjectAltName(const GENERAL_NAME& name) {
74+
if (name.type == GEN_DNS) {
75+
if (auto value = getStringSubjectAltName(name.d.dNSName)) {
76+
return GeneralName{DnsName{std::move(*value)}};
77+
}
78+
}
79+
if (name.type == GEN_URI) {
80+
if (auto value =
81+
getStringSubjectAltName(name.d.uniformResourceIdentifier)) {
82+
return GeneralName{UriName{std::move(*value)}};
83+
}
84+
}
85+
if (name.type == GEN_IPADD) {
86+
if (auto value = getIpSubjectAltName(name.d.iPAddress)) {
87+
return GeneralName{std::move(*value)};
88+
}
89+
}
90+
return std::nullopt;
91+
}
92+
5293
std::string getExtOid(X509_EXTENSION* extension) {
5394
CHECK_NOTNULL(extension);
5495
ASN1_OBJECT* object = X509_EXTENSION_get_object(extension);
@@ -113,34 +154,27 @@ Optional<std::string> OpenSSLCertUtils::getIssuerCommonName(const X509& x509) {
113154

114155
std::vector<std::string> OpenSSLCertUtils::getSubjectAltNames(
115156
const X509& x509) {
116-
auto names = reinterpret_cast<STACK_OF(GENERAL_NAME)*>(
117-
X509_get_ext_d2i(&x509, NID_subject_alt_name, nullptr, nullptr));
118-
if (!names) {
119-
return {};
157+
std::vector<std::string> result;
158+
for (auto& name : getSubjectAltNameEntries(x509)) {
159+
if (auto* dnsName = std::get_if<DnsName>(&name)) {
160+
result.emplace_back(std::move(dnsName->value));
161+
}
120162
}
121-
SCOPE_EXIT {
122-
sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
123-
};
163+
return result;
164+
}
124165

125-
std::vector<std::string> ret;
126-
auto count = sk_GENERAL_NAME_num(names);
127-
for (int i = 0; i < count; i++) {
128-
auto genName = sk_GENERAL_NAME_value(names, i);
129-
if (!genName || genName->type != GEN_DNS) {
130-
continue;
131-
}
132-
auto nameData = reinterpret_cast<const char*>(
133-
ASN1_STRING_get0_data(genName->d.dNSName));
134-
auto nameLen = ASN1_STRING_length(genName->d.dNSName);
135-
if (!nameData || nameLen <= 0) {
136-
continue;
166+
std::vector<std::string> OpenSSLCertUtils::getSubjectAltNameURIs(
167+
const X509& x509) {
168+
std::vector<std::string> result;
169+
for (auto& name : getSubjectAltNameEntries(x509)) {
170+
if (auto* uriName = std::get_if<UriName>(&name)) {
171+
result.emplace_back(std::move(uriName->value));
137172
}
138-
ret.emplace_back(nameData, nameLen);
139173
}
140-
return ret;
174+
return result;
141175
}
142176

143-
std::vector<std::string> OpenSSLCertUtils::getSubjectAltNameURIs(
177+
std::vector<GeneralName> OpenSSLCertUtils::getSubjectAltNameEntries(
144178
const X509& x509) {
145179
auto names = reinterpret_cast<STACK_OF(GENERAL_NAME)*>(
146180
X509_get_ext_d2i(&x509, NID_subject_alt_name, nullptr, nullptr));
@@ -152,22 +186,18 @@ std::vector<std::string> OpenSSLCertUtils::getSubjectAltNameURIs(
152186
};
153187

154188
auto count = sk_GENERAL_NAME_num(names);
155-
std::vector<std::string> ret;
156-
ret.reserve(count);
189+
std::vector<GeneralName> result;
190+
result.reserve(count);
157191
for (int i = 0; i < count; i++) {
158-
auto genName = sk_GENERAL_NAME_value(names, i);
159-
if (!genName || genName->type != GEN_URI) {
192+
auto name = sk_GENERAL_NAME_value(names, i);
193+
if (!name) {
160194
continue;
161195
}
162-
auto nameData = reinterpret_cast<const char*>(
163-
ASN1_STRING_get0_data(genName->d.uniformResourceIdentifier));
164-
auto nameLen = ASN1_STRING_length(genName->d.uniformResourceIdentifier);
165-
if (!nameData || nameLen <= 0) {
166-
continue;
196+
if (auto value = getSubjectAltName(*name)) {
197+
result.emplace_back(std::move(*value));
167198
}
168-
ret.emplace_back(nameData, nameLen);
169199
}
170-
return ret;
200+
return result;
171201
}
172202

173203
std::vector<std::string> OpenSSLCertUtils::getExtendedKeyUsage(

folly/ssl/OpenSSLCertUtils.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
#include <map>
2121
#include <set>
2222
#include <string>
23+
#include <variant>
2324
#include <vector>
2425

26+
#include <folly/IPAddress.h>
2527
#include <folly/Optional.h>
2628
#include <folly/io/IOBuf.h>
2729
#include <folly/portability/OpenSSL.h>
@@ -30,17 +32,33 @@
3032
namespace folly {
3133
namespace ssl {
3234

35+
struct DnsName {
36+
std::string value;
37+
};
38+
39+
struct UriName {
40+
std::string value;
41+
};
42+
43+
using GeneralName = std::variant<DnsName, UriName, folly::IPAddress>;
44+
3345
class OpenSSLCertUtils {
3446
public:
3547
// Note: non-const until OpenSSL 1.1.0
3648
static Optional<std::string> getCommonName(const X509& x509);
3749

3850
static Optional<std::string> getIssuerCommonName(const X509& x509);
3951

52+
// Returns DNS name subjectAltName entries in certificate order.
4053
static std::vector<std::string> getSubjectAltNames(const X509& x509);
4154

55+
// Returns URI subjectAltName entries in certificate order.
4256
static std::vector<std::string> getSubjectAltNameURIs(const X509& x509);
4357

58+
// Returns DNS name, URI, and IP address subjectAltName entries in certificate
59+
// order. Other GENERAL_NAME types are ignored.
60+
static std::vector<GeneralName> getSubjectAltNameEntries(const X509& x509);
61+
4462
/**
4563
* Return the Extended Key Usage (EKU) entries, if any, from the cert.
4664
* Each entry is a dotted-decimal OID string (e.g., "1.3.6.1.5.5.7.3.1").

folly/ssl/test/OpenSSLCertUtilsTest.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,22 @@ const std::string kTestCertWithMixedSan = folly::stripLeftMargin(R"(
540540
-----END CERTIFICATE-----
541541
)");
542542

543+
// SAN: IP:127.0.0.1, IP:::1, DNS:ip.example.com
544+
const std::string kTestCertWithIpSan = folly::stripLeftMargin(R"(
545+
-----BEGIN CERTIFICATE-----
546+
MIIBrzCCAVSgAwIBAgIUCnGvdtHLIk3xpNgOyIRJx8JFEoowCgYIKoZIzj0EAwIw
547+
ETEPMA0GA1UEAwwGaXB0ZXN0MCAXDTI2MDcwMjIzMjgwOFoYDzIxMjYwNjA4MjMy
548+
ODA4WjARMQ8wDQYDVQQDDAZpcHRlc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
549+
AASYSO4d5hqBVTBsRVT85pazOkjix2XnlXaRGgMsDq6Q/gs/LKX7zDBxD3Wo91iN
550+
0Xgsis2JTbp760eB/xtMwz8Ao4GHMIGEMB0GA1UdDgQWBBTzBGjCCQTQfI5cYw/t
551+
ZcetoJon6jAfBgNVHSMEGDAWgBTzBGjCCQTQfI5cYw/tZcetoJon6jAPBgNVHRMB
552+
Af8EBTADAQH/MDEGA1UdEQQqMCiHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAGCDmlw
553+
LmV4YW1wbGUuY29tMAoGCCqGSM49BAMCA0kAMEYCIQC9OqMY3kvP2rXdb+4/uBMl
554+
5hHEz75dsgQi2SxQR+VY2gIhAKD8qWAzXZPWGaW8Z6uxMZahv9VCYLEnMEvXzUoW
555+
FyD/
556+
-----END CERTIFICATE-----
557+
)");
558+
543559
TEST_P(OpenSSLCertUtilsTest, TestX509UriSans) {
544560
auto x509 = readCertFromData(kTestCertWithUriSan);
545561
EXPECT_NE(x509, nullptr);
@@ -577,6 +593,27 @@ TEST_P(OpenSSLCertUtilsTest, TestX509MixedSans) {
577593
EXPECT_EQ(uriSans, expectedUris);
578594
}
579595

596+
TEST_P(OpenSSLCertUtilsTest, TestX509SubjectAltNameEntriesWithIPs) {
597+
auto x509 = readCertFromData(kTestCertWithIpSan);
598+
EXPECT_NE(x509, nullptr);
599+
600+
std::vector<folly::IPAddress> ips;
601+
std::vector<std::string> dnsNames;
602+
for (const auto& name :
603+
folly::ssl::OpenSSLCertUtils::getSubjectAltNameEntries(*x509)) {
604+
if (const auto* ip = std::get_if<folly::IPAddress>(&name)) {
605+
ips.emplace_back(*ip);
606+
} else if (const auto* dns = std::get_if<folly::ssl::DnsName>(&name)) {
607+
dnsNames.emplace_back(dns->value);
608+
}
609+
}
610+
611+
const std::vector<folly::IPAddress> expected{
612+
folly::IPAddress("127.0.0.1"), folly::IPAddress("::1")};
613+
EXPECT_EQ(ips, expected);
614+
EXPECT_EQ(dnsNames, std::vector<std::string>{"ip.example.com"});
615+
}
616+
580617
TEST_P(OpenSSLCertUtilsTest, TestX509NoUriSans) {
581618
auto x509 = readCertFromData(kTestCertWithSan);
582619
EXPECT_NE(x509, nullptr);

0 commit comments

Comments
 (0)