feat: add method to obtain TLS result (#1039)

Some TLS toolkits export a result that can be checked afterwards even if
the TLS negotation returned successfully. This can be used for example
if you disabled certificate checks by default, but then want to check
the outcome.

Currently this is only supported on boring TLS.
This commit is contained in:
André Cruz
2025-02-17 08:51:45 +00:00
committed by GitHub
parent 795bedae76
commit 3e9b1876d9
4 changed files with 72 additions and 0 deletions

View File

@@ -373,6 +373,22 @@ impl AsyncSmtpConnection {
self.stream.get_ref().peer_certificate()
}
/// Currently this is only avaialable when using Boring TLS and
/// returns the result of the verification of the TLS certificate
/// presented by the peer, if any. Only the last error encountered
/// during verification is presented.
/// It can be useful when you don't want to fail outright the TLS
/// negotiation, for example when a self-signed certificate is
/// encountered, but still want to record metrics or log the fact.
/// When using DANE verification, the PKI root of trust moves from
/// the CAs to DNS, so self-signed certificates are permitted as long
/// as the TLSA records match the leaf or issuer certificates.
/// It cannot be called on non Boring TLS streams.
#[cfg(feature = "boring-tls")]
pub fn tls_verify_result(&self) -> Result<(), Error> {
self.stream.get_ref().tls_verify_result()
}
/// All the X509 certificates of the chain (DER encoded)
#[cfg(any(feature = "rustls-tls", feature = "boring-tls"))]
pub fn certificate_chain(&self) -> Result<Vec<Vec<u8>>, Error> {

View File

@@ -429,6 +429,30 @@ impl AsyncNetworkStream {
}
}
#[cfg(feature = "boring-tls")]
pub fn tls_verify_result(&self) -> Result<(), Error> {
match &self.inner {
#[cfg(feature = "tokio1")]
InnerAsyncNetworkStream::Tokio1Tcp(_) => {
Err(error::client("Connection is not encrypted"))
}
#[cfg(feature = "tokio1-native-tls")]
InnerAsyncNetworkStream::Tokio1NativeTls(_) => panic!("Unsupported"),
#[cfg(feature = "tokio1-rustls-tls")]
InnerAsyncNetworkStream::Tokio1RustlsTls(_) => panic!("Unsupported"),
#[cfg(feature = "tokio1-boring-tls")]
InnerAsyncNetworkStream::Tokio1BoringTls(stream) => {
stream.ssl().verify_result().map_err(error::tls)
}
#[cfg(feature = "async-std1")]
InnerAsyncNetworkStream::AsyncStd1Tcp(_) => {
Err(error::client("Connection is not encrypted"))
}
#[cfg(feature = "async-std1-rustls-tls")]
InnerAsyncNetworkStream::AsyncStd1RustlsTls(_) => panic!("Unsupported"),
InnerAsyncNetworkStream::None => panic!("InnerNetworkStream::None must never be built"),
}
}
pub fn certificate_chain(&self) -> Result<Vec<Vec<u8>>, Error> {
match &self.inner {
#[cfg(feature = "tokio1")]

View File

@@ -308,6 +308,22 @@ impl SmtpConnection {
self.stream.get_ref().peer_certificate()
}
/// Currently this is only avaialable when using Boring TLS and
/// returns the result of the verification of the TLS certificate
/// presented by the peer, if any. Only the last error encountered
/// during verification is presented.
/// It can be useful when you don't want to fail outright the TLS
/// negotiation, for example when a self-signed certificate is
/// encountered, but still want to record metrics or log the fact.
/// When using DANE verification, the PKI root of trust moves from
/// the CAs to DNS, so self-signed certificates are permitted as long
/// as the TLSA records match the leaf or issuer certificates.
/// It cannot be called on non Boring TLS streams.
#[cfg(feature = "boring-tls")]
pub fn tls_verify_result(&self) -> Result<(), Error> {
self.stream.get_ref().tls_verify_result()
}
/// All the X509 certificates of the chain (DER encoded)
#[cfg(any(feature = "rustls-tls", feature = "boring-tls"))]
pub fn certificate_chain(&self) -> Result<Vec<Vec<u8>>, Error> {

View File

@@ -222,6 +222,22 @@ impl NetworkStream {
}
}
#[cfg(feature = "boring-tls")]
pub fn tls_verify_result(&self) -> Result<(), Error> {
match &self.inner {
InnerNetworkStream::Tcp(_) => Err(error::client("Connection is not encrypted")),
#[cfg(feature = "native-tls")]
InnerNetworkStream::NativeTls(_) => panic!("Unsupported"),
#[cfg(feature = "rustls-tls")]
InnerNetworkStream::RustlsTls(_) => panic!("Unsupported"),
#[cfg(feature = "boring-tls")]
InnerNetworkStream::BoringTls(stream) => {
stream.ssl().verify_result().map_err(error::tls)
}
InnerNetworkStream::None => panic!("InnerNetworkStream::None must never be built"),
}
}
#[cfg(any(feature = "rustls-tls", feature = "boring-tls"))]
pub fn certificate_chain(&self) -> Result<Vec<Vec<u8>>, Error> {
match &self.inner {