Skip to content

Commit

Permalink
lua: Add API to expose TLS OIDs for extensions (#35953)
Browse files Browse the repository at this point in the history
This change adds two new methods (i.e. oidsPeerCertificate() and
oidsLocalCertificate()) to SSL connection object API, so as to make
ASN.1 OID cert info accessible from lua API.

Extension developer could then leverage the OID information to further
identify cert type for advanced use cases.

Fixes #14801

Signed-off-by: Qiu Yu <[email protected]>
  • Loading branch information
unicell authored Sep 12, 2024
1 parent 4d12162 commit 25bc5b2
Show file tree
Hide file tree
Showing 18 changed files with 451 additions and 5 deletions.
4 changes: 4 additions & 0 deletions changelogs/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -321,5 +321,9 @@ new_features:
change: |
Added possibility to monitor CPU utilization in Linux based systems via :ref:`cpu utilization monitor
<envoy_v3_api_msg_extensions.resource_monitors.cpu_utilization.v3.CpuUtilizationConfig>` in overload manager.
- area: lua
change: |
Added two new methods ``oidsPeerCertificate()`` and ``oidsLocalCertificate()`` to SSL
connection object API :ref:`SSL connection info object <config_http_filters_lua_ssl_socket_info>`.
deprecated:
22 changes: 22 additions & 0 deletions docs/root/configuration/http/http_filters/lua_filter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,28 @@ dnsSansLocalCertificate()
Returns the DNS entries (as a table) in the SAN field of the local certificate. Returns an empty
table if there is no local certificate, or no SAN field, or no DNS SAN entries.

oidsPeerCertificate()
^^^^^^^^^^^^^^^^^^^^^

.. code-block:: lua
downstreamSslConnection:oidsPeerCertificate()
Returns the string representation of OIDs (as a table) from the peer certificate. This is for
reading the OID strings from the certificate, not the extension values associated with OIDs.
Returns an empty table if there is no peer certificate or no OIDs.

oidsLocalCertificate()
^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: lua
downstreamSslConnection:oidsLocalCertificate()
Returns the string representation of OIDs (as a table) from the local certificate. This is for
reading the OID strings from the certificate, not the extension values associated with OIDs.
Returns an empty table if there is no local certificate or no OIDs.

validFromPeerCertificate()
^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
12 changes: 12 additions & 0 deletions envoy/ssl/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,18 @@ class ConnectionInfo {
**/
virtual absl::Span<const std::string> ipSansLocalCertificate() const PURE;

/**
* @return absl::Span<const std::string> the OID entries of the peer certificate extensions.
* Returns {} if there is no peer certificate, or no extensions.
**/
virtual absl::Span<const std::string> oidsPeerCertificate() const PURE;

/**
* @return absl::Span<const std::string> the OID entries of the local certificate extensions.
* Returns {} if there is no local certificate, or no extensions.
**/
virtual absl::Span<const std::string> oidsLocalCertificate() const PURE;

/**
* @return absl::optional<SystemTime> the time that the peer certificate was issued and should be
* considered valid from. Returns empty absl::optional if there is no peer certificate.
Expand Down
28 changes: 28 additions & 0 deletions source/common/tls/connection_info_impl_base.cc
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,34 @@ absl::Span<const std::string> ConnectionInfoImplBase::ipSansPeerCertificate() co
return cached_ip_san_peer_certificate_;
}

absl::Span<const std::string> ConnectionInfoImplBase::oidsPeerCertificate() const {
if (!cached_oid_peer_certificate_.empty()) {
return cached_oid_peer_certificate_;
}

bssl::UniquePtr<X509> cert(SSL_get_peer_certificate(ssl()));
if (!cert) {
ASSERT(cached_oid_peer_certificate_.empty());
return cached_oid_peer_certificate_;
}
cached_oid_peer_certificate_ = Utility::getCertificateExtensionOids(*cert);
return cached_oid_peer_certificate_;
}

absl::Span<const std::string> ConnectionInfoImplBase::oidsLocalCertificate() const {
if (!cached_oid_local_certificate_.empty()) {
return cached_oid_local_certificate_;
}

X509* cert = SSL_get_certificate(ssl());
if (!cert) {
ASSERT(cached_oid_local_certificate_.empty());
return cached_oid_local_certificate_;
}
cached_oid_local_certificate_ = Utility::getCertificateExtensionOids(*cert);
return cached_oid_local_certificate_;
}

uint16_t ConnectionInfoImplBase::ciphersuiteId() const {
const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl());
if (cipher == nullptr) {
Expand Down
4 changes: 4 additions & 0 deletions source/common/tls/connection_info_impl_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class ConnectionInfoImplBase : public Ssl::ConnectionInfo {
absl::Span<const std::string> dnsSansLocalCertificate() const override;
absl::Span<const std::string> ipSansPeerCertificate() const override;
absl::Span<const std::string> ipSansLocalCertificate() const override;
absl::Span<const std::string> oidsPeerCertificate() const override;
absl::Span<const std::string> oidsLocalCertificate() const override;
absl::optional<SystemTime> validFromPeerCertificate() const override;
absl::optional<SystemTime> expirationPeerCertificate() const override;
const std::string& sessionId() const override;
Expand Down Expand Up @@ -66,6 +68,8 @@ class ConnectionInfoImplBase : public Ssl::ConnectionInfo {
mutable std::vector<std::string> cached_dns_san_local_certificate_;
mutable std::vector<std::string> cached_ip_san_peer_certificate_;
mutable std::vector<std::string> cached_ip_san_local_certificate_;
mutable std::vector<std::string> cached_oid_peer_certificate_;
mutable std::vector<std::string> cached_oid_local_certificate_;
mutable std::string cached_session_id_;
mutable std::string cached_tls_version_;
mutable std::string alpn_;
Expand Down
26 changes: 23 additions & 3 deletions source/common/tls/utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ namespace Extensions {
namespace TransportSockets {
namespace Tls {

static constexpr int MAX_OID_LENGTH = 256;

static constexpr absl::string_view SSL_ERROR_UNKNOWN_ERROR_MESSAGE = "UNKNOWN_ERROR";

Envoy::Ssl::CertificateDetailsPtr Utility::certificateDetails(X509* cert, const std::string& path,
Expand Down Expand Up @@ -253,9 +255,9 @@ std::string Utility::generalNameAsString(const GENERAL_NAME* general_name) {
break;
}
case V_ASN1_OBJECT: {
char tmp_obj[256]; // OID Max length
int obj_len = OBJ_obj2txt(tmp_obj, 256, value->value.object, 1);
if (obj_len > 256 || obj_len < 0) {
char tmp_obj[MAX_OID_LENGTH];
int obj_len = OBJ_obj2txt(tmp_obj, MAX_OID_LENGTH, value->value.object, 1);
if (obj_len > MAX_OID_LENGTH || obj_len < 0) {
break;
}
san.assign(tmp_obj);
Expand Down Expand Up @@ -384,6 +386,24 @@ absl::optional<uint32_t> Utility::getDaysUntilExpiration(const X509* cert,
return absl::nullopt;
}

std::vector<std::string> Utility::getCertificateExtensionOids(X509& cert) {
std::vector<std::string> extension_oids;

int count = X509_get_ext_count(&cert);
for (int pos = 0; pos < count; pos++) {
X509_EXTENSION* extension = X509_get_ext(&cert, pos);
RELEASE_ASSERT(extension != nullptr, "");

char oid[MAX_OID_LENGTH];
int obj_len = OBJ_obj2txt(oid, MAX_OID_LENGTH, X509_EXTENSION_get_object(extension),
1 /* always_return_oid */);
if (obj_len > 0 && obj_len < MAX_OID_LENGTH) {
extension_oids.push_back(oid);
}
}
return extension_oids;
}

absl::string_view Utility::getCertificateExtensionValue(X509& cert,
absl::string_view extension_name) {
bssl::UniquePtr<ASN1_OBJECT> oid(
Expand Down
7 changes: 7 additions & 0 deletions source/common/tls/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ std::string getIssuerFromCertificate(X509& cert);
*/
std::string getSubjectFromCertificate(X509& cert);

/**
* Retrieves the extension OIDs from certificate.
* @param cert the certificate
* @return std::vector returns the string list of ASN.1 object identifiers.
*/
std::vector<std::string> getCertificateExtensionOids(X509& cert);

/**
* Retrieves the value of a specific X509 extension from the cert, if present.
* @param cert the certificate.
Expand Down
10 changes: 10 additions & 0 deletions source/extensions/filters/common/lua/wrappers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,16 @@ int SslConnectionWrapper::luaDnsSansLocalCertificate(lua_State* state) {
return 1;
}

int SslConnectionWrapper::luaOidsPeerCertificate(lua_State* state) {
createLuaTableFromStringList(state, connection_info_.oidsPeerCertificate());
return 1;
}

int SslConnectionWrapper::luaOidsLocalCertificate(lua_State* state) {
createLuaTableFromStringList(state, connection_info_.oidsLocalCertificate());
return 1;
}

int SslConnectionWrapper::luaValidFromPeerCertificate(lua_State* state) {
lua_pushinteger(state, timestampInSeconds(connection_info_.validFromPeerCertificate()));
return 1;
Expand Down
14 changes: 14 additions & 0 deletions source/extensions/filters/common/lua/wrappers.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ class SslConnectionWrapper : public BaseLuaObject<SslConnectionWrapper> {
{"subjectLocalCertificate", static_luaSubjectLocalCertificate},
{"dnsSansPeerCertificate", static_luaDnsSansPeerCertificate},
{"dnsSansLocalCertificate", static_luaDnsSansLocalCertificate},
{"oidsPeerCertificate", static_luaOidsPeerCertificate},
{"oidsLocalCertificate", static_luaOidsLocalCertificate},
{"validFromPeerCertificate", static_luaValidFromPeerCertificate},
{"expirationPeerCertificate", static_luaExpirationPeerCertificate},
{"sessionId", static_luaSessionId},
Expand Down Expand Up @@ -223,6 +225,18 @@ class SslConnectionWrapper : public BaseLuaObject<SslConnectionWrapper> {
*/
DECLARE_LUA_FUNCTION(SslConnectionWrapper, luaDnsSansLocalCertificate);

/**
* Returns the OIDs (ASN.1 Object Identifiers) of the peer certificate. Returns an empty table if
* there is no peer certificate or no OIDs.
*/
DECLARE_LUA_FUNCTION(SslConnectionWrapper, luaOidsPeerCertificate);

/**
* Returns the OIDs (ASN.1 Object Identifiers) of the peer certificate. Returns an empty table if
* there is no peer certificate or no OIDs.
*/
DECLARE_LUA_FUNCTION(SslConnectionWrapper, luaOidsLocalCertificate);

/**
* Returns the timestamp-since-epoch (in seconds) that the peer certificate was issued and should
* be considered valid from. Returns empty string if there is no peer certificate.
Expand Down
Loading

0 comments on commit 25bc5b2

Please sign in to comment.