Skip to content

Commit

Permalink
tls: Added support for fetching DN in RFC2253 format
Browse files Browse the repository at this point in the history
Added a dn_format flag to switch between legacy and RFC2253 format.
Legacy format matches that originally returned by the GnuTLS api
`gnutls_x509_crt_get_dn` which returns the DN in the format of
`C=US,ST=London,L=London,O=Redpanda Data,OU=Core,CN=id`.  RFC2253
format, effectively reverses the order: `CN=id,OU=Core,O=Redpanda
Data,L=London,ST=London,C=US`.

Signed-off-by: Michael Boquard <[email protected]>
  • Loading branch information
michael-redpanda committed Mar 4, 2025
1 parent 675fb3c commit dad4af6
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 16 deletions.
7 changes: 6 additions & 1 deletion include/seastar/net/tls.hh
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,11 @@ namespace tls {
server_socket listen(shared_ptr<server_credentials>, server_socket);
/// @}

enum class dn_format {
legacy, // legacy format
rfc2253
};

/**
* Get distinguished name from the leaf certificate in the certificate chain that
* the connected peer is using.
Expand All @@ -542,7 +547,7 @@ namespace tls {
* during the handshake the function returns nullopt. If the socket is not connected the
* system_error exception will be thrown.
*/
future<std::optional<session_dn>> get_dn_information(connected_socket& socket);
future<std::optional<session_dn>> get_dn_information(connected_socket& socket, dn_format format = dn_format::legacy);

/**
* Subject alt name types.
Expand Down
26 changes: 17 additions & 9 deletions src/net/ossl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1467,7 +1467,7 @@ class session : public enable_shared_from_this<session>, public session_impl {
return *_sock;
}

future<std::optional<session_dn>> get_distinguished_name() override {
future<std::optional<session_dn>> get_distinguished_name(dn_format format) override {
using result_t = std::optional<session_dn>;
if (_error) {
return make_exception_future<result_t>(_error);
Expand All @@ -1478,9 +1478,9 @@ class session : public enable_shared_from_this<session>, public session_impl {
}
if (!connected()) {
return handshake().then(
[this]() mutable { return get_distinguished_name(); });
[this, format]() mutable { return get_distinguished_name(format); });
}
result_t dn = extract_dn_information();
result_t dn = extract_dn_information(is_verification_error::no, format);
return make_ready_future<result_t>(std::move(dn));
}

Expand Down Expand Up @@ -1649,7 +1649,7 @@ class session : public enable_shared_from_this<session>, public session_impl {

using is_verification_error = bool_class<struct is_verification_error_tag>;

std::optional<session_dn> extract_dn_information(is_verification_error verification_error = is_verification_error::no) const {
std::optional<session_dn> extract_dn_information(is_verification_error verification_error = is_verification_error::no, dn_format format = dn_format::legacy) const {
const auto peer_cert = [this, verification_error]{
if (verification_error) {
// If we are attempting to get a DN from a cert that failed verification, then
Expand All @@ -1664,8 +1664,8 @@ class session : public enable_shared_from_this<session>, public session_impl {
if (!peer_cert) {
return std::nullopt;
}
auto subject = get_dn_string(X509_get_subject_name(peer_cert.get()));
auto issuer = get_dn_string(X509_get_issuer_name(peer_cert.get()));
auto subject = get_dn_string(X509_get_subject_name(peer_cert.get()), format);
auto issuer = get_dn_string(X509_get_issuer_name(peer_cert.get()), format);
if (!subject || !issuer) {
throw make_ossl_error(
"error while extracting certificate DN strings");
Expand Down Expand Up @@ -1793,10 +1793,18 @@ class session : public enable_shared_from_this<session>, public session_impl {
return ssl_ctx;
}

static std::optional<sstring> get_dn_string(X509_NAME* name) {
static std::optional<sstring> get_dn_string(X509_NAME* name, dn_format format = dn_format::legacy) {
auto out = bio_ptr(BIO_new(BIO_s_mem()));
if (-1 == X509_NAME_print_ex(out.get(), name, 0, ASN1_STRFLGS_RFC2253 | XN_FLAG_SEP_COMMA_PLUS |
XN_FLAG_FN_SN | XN_FLAG_DUMP_UNKNOWN_FIELDS)) {
unsigned long flags = [](dn_format format) {
switch(format) {
case dn_format::rfc2253:
return XN_FLAG_RFC2253;
case dn_format::legacy:
return ASN1_STRFLGS_RFC2253 | XN_FLAG_SEP_COMMA_PLUS | XN_FLAG_FN_SN | XN_FLAG_DUMP_UNKNOWN_FIELDS;
}
__builtin_unreachable();
}(format);
if (-1 == X509_NAME_print_ex(out.get(), name, 0, flags)) {
return std::nullopt;
}
char* bio_ptr = nullptr;
Expand Down
4 changes: 2 additions & 2 deletions src/net/tls-impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -714,8 +714,8 @@ static tls::tls_connected_socket_impl* get_tls_socket(connected_socket& socket)
return tls_impl;
}

future<std::optional<session_dn>> tls::get_dn_information(connected_socket& socket) {
return get_tls_socket(socket)->get_distinguished_name();
future<std::optional<session_dn>> tls::get_dn_information(connected_socket& socket, dn_format format) {
return get_tls_socket(socket)->get_distinguished_name(format);
}

future<std::vector<tls::subject_alt_name>> tls::get_alt_name_information(connected_socket& socket, std::unordered_set<subject_alt_name_type> types) {
Expand Down
6 changes: 3 additions & 3 deletions src/net/tls-impl.hh
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public:
virtual future<> flush() noexcept = 0;
virtual future<temporary_buffer<char>> get() = 0;
virtual void close() = 0;
virtual future<std::optional<session_dn>> get_distinguished_name() = 0;
virtual future<std::optional<session_dn>> get_distinguished_name(dn_format) = 0;
virtual seastar::net::connected_socket_impl & socket() const = 0;
virtual future<std::vector<subject_alt_name>> get_alt_name_information(std::unordered_set<subject_alt_name_type>) = 0;
virtual future<bool> is_resumed() = 0;
Expand Down Expand Up @@ -140,8 +140,8 @@ public:
socket_address remote_address() const noexcept override {
return _session->socket().remote_address();
}
future<std::optional<session_dn>> get_distinguished_name() {
return _session->get_distinguished_name();
future<std::optional<session_dn>> get_distinguished_name(dn_format format) {
return _session->get_distinguished_name(format);
}
future<std::vector<subject_alt_name>> get_alt_name_information(std::unordered_set<subject_alt_name_type> types) {
return _session->get_alt_name_information(std::move(types));
Expand Down
3 changes: 2 additions & 1 deletion src/net/tls.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1221,7 +1221,8 @@ class session : public enable_shared_from_this<session>, public session_impl {
return session_data(tmp.data, tmp.data + tmp.size);
});
}
future<std::optional<session_dn>> get_distinguished_name() {
future<std::optional<session_dn>> get_distinguished_name(dn_format) {
// Ignoring parameter as GnuTLS does not provide a mechanism to change the format
return state_checked_access([this] {
return extract_dn_information();
});
Expand Down
5 changes: 5 additions & 0 deletions tests/unit/tls_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1549,6 +1549,11 @@ SEASTAR_THREAD_TEST_CASE(test_dn_name_handling) {
BOOST_REQUIRE_EQUAL(dn->subject, fmt::format("C=GB,ST=London,L=London,O=Redpanda Data,OU=Core,CN={}", id));
BOOST_REQUIRE_EQUAL(dn->issuer, "C=GB,ST=London,L=London,O=Redpanda Data,OU=Core,CN=redpanda.com");

dn = tls::get_dn_information(s.connection, tls::dn_format::rfc2253).get();
BOOST_REQUIRE(dn.has_value());
BOOST_REQUIRE_EQUAL(dn->subject, fmt::format("CN={},OU=Core,O=Redpanda Data,L=London,ST=London,C=GB", id));
BOOST_REQUIRE_EQUAL(dn->issuer, "CN=redpanda.com,OU=Core,O=Redpanda Data,L=London,ST=London,C=GB");

auto client_id = fin.get();

in.close().get();
Expand Down

0 comments on commit dad4af6

Please sign in to comment.