From eefc7b72659dd4666b9d16ecebb1332a5d3680e3 Mon Sep 17 00:00:00 2001 From: James Larisch Date: Mon, 19 May 2025 11:02:56 -0400 Subject: [PATCH] Add `X509_STORE_CTX_get0_cert` interface This method reliably retrieves the certificate the `X509_STORE_CTX` is verifying, unlike `X509_STORE_CTX_get_current_cert`, which may return the "problematic" cert when verification fails. --- boring/src/ssl/test/cert_verify.rs | 22 +++++++++++++++++----- boring/src/ssl/test/server.rs | 18 ++++++++++++++++++ boring/src/x509/mod.rs | 14 ++++++++++++++ 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/boring/src/ssl/test/cert_verify.rs b/boring/src/ssl/test/cert_verify.rs index 929db48c..e8e54061 100644 --- a/boring/src/ssl/test/cert_verify.rs +++ b/boring/src/ssl/test/cert_verify.rs @@ -56,17 +56,29 @@ fn no_error_when_trusted_and_callback_returns_true() { #[test] fn callback_receives_correct_certificate() { - let server = Server::builder().build(); + // Server sends the full chain (leaf + root)... + let server = Server::builder_full_chain().build(); + // but client doesn't load the root as trusted. + // So we expect an error. let mut client = server.client(); - let expected = "59172d9313e84459bcff27f967e79e6e9217e584"; + let leaf_sha1 = "59172d9313e84459bcff27f967e79e6e9217e584"; + let root_sha1 = "c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875"; client.ctx().set_verify(SslVerifyMode::PEER); client.ctx().set_cert_verify_callback(move |x509| { assert!(!x509.verify_cert().unwrap()); + // This is set to the root, since that's the problematic cert. assert!(x509.current_cert().is_some()); + // This is set to the leaf, since that's the cert we're verifying. + assert!(x509.cert().is_some()); assert!(x509.verify_result().is_err()); - let cert = x509.current_cert().unwrap(); - let digest = cert.digest(MessageDigest::sha1()).unwrap(); - assert_eq!(hex::encode(digest), expected); + + let root = x509.current_cert().unwrap(); + let digest = root.digest(MessageDigest::sha1()).unwrap(); + assert_eq!(hex::encode(digest), root_sha1); + + let leaf = x509.cert().unwrap(); + let digest = leaf.digest(MessageDigest::sha1()).unwrap(); + assert_eq!(hex::encode(digest), leaf_sha1); true }); diff --git a/boring/src/ssl/test/server.rs b/boring/src/ssl/test/server.rs index e5c0497c..5436469d 100644 --- a/boring/src/ssl/test/server.rs +++ b/boring/src/ssl/test/server.rs @@ -36,6 +36,24 @@ impl Server { } } + /// Serves the leaf and the root together. + pub fn builder_full_chain() -> Builder { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + // Uses certs.pem instead of cert.pem. + ctx.set_certificate_chain_file("test/certs.pem").unwrap(); + ctx.set_private_key_file("test/key.pem", SslFiletype::PEM) + .unwrap(); + + Builder { + ctx, + ssl_cb: Box::new(|_| {}), + io_cb: Box::new(|_| {}), + err_cb: Box::new(|_| {}), + should_error: false, + expected_connections_count: 1, + } + } + pub fn client(&self) -> ClientBuilder { ClientBuilder { ctx: SslContext::builder(SslMethod::tls()).unwrap(), diff --git a/boring/src/x509/mod.rs b/boring/src/x509/mod.rs index f4a44ee5..62c2c8df 100644 --- a/boring/src/x509/mod.rs +++ b/boring/src/x509/mod.rs @@ -209,6 +209,20 @@ impl X509StoreContextRef { } } } + + /// Returns a reference to the certificate being verified. + /// May return None if a raw public key is being verified. + #[corresponds(X509_STORE_CTX_get0_cert)] + pub fn cert(&self) -> Option<&X509Ref> { + unsafe { + let ptr = ffi::X509_STORE_CTX_get0_cert(self.as_ptr()); + if ptr.is_null() { + None + } else { + Some(X509Ref::from_ptr(ptr)) + } + } + } } /// A builder used to construct an `X509`.