Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bindings: do not enable OCSP when calling trust_location() #4016

Merged
merged 12 commits into from
Jun 22, 2023
Merged
17 changes: 17 additions & 0 deletions bindings/rust/s2n-tls/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ impl Drop for Config {
pub struct Builder {
config: Config,
load_system_certs: bool,
enable_ocsp: bool,
}

impl Builder {
Expand Down Expand Up @@ -181,6 +182,7 @@ impl Builder {
Self {
config: Config(config),
load_system_certs: true,
enable_ocsp: false,
}
}

Expand Down Expand Up @@ -288,6 +290,10 @@ impl Builder {
Ok(self)
}

/// Adds to the trust store from a CA file or directory containing trusted certificates.
///
/// NOTE: This function is equivalent to `s2n_config_set_verification_ca_location` except it does
/// not automatically enable the client to request OCSP stapling from the server.
pub fn trust_location(
&mut self,
file: Option<&Path>,
Expand Down Expand Up @@ -320,6 +326,16 @@ impl Builder {
s2n_config_set_verification_ca_location(self.as_mut_ptr(), file_ptr, dir_ptr)
.into_result()
}?;

// If OCSP has not been explicitly requested, turn off OCSP. This is to prevent this function from
// automatically enabling `OCSP` due to the legacy behavior of `s2n_config_set_verification_ca_location`
if !self.enable_ocsp {
unsafe {
s2n_config_set_status_request_type(self.as_mut_ptr(), s2n_status_request_type::NONE)
.into_result()?
};
}

Ok(self)
}

Expand Down Expand Up @@ -353,6 +369,7 @@ impl Builder {
s2n_config_set_status_request_type(self.as_mut_ptr(), s2n_status_request_type::OCSP)
.into_result()
}?;
self.enable_ocsp = true;
Ok(self)
}

Expand Down
37 changes: 37 additions & 0 deletions bindings/rust/s2n-tls/src/testing/client_hello.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{
};
use alloc::sync::Arc;
use core::{sync::atomic::Ordering, task::Poll};
use s2n_tls_sys::{s2n_client_hello_has_extension, s2n_connection_get_client_hello};
use std::{fmt, io, pin::Pin, sync::atomic::AtomicUsize};

// The Future returned by MockClientHelloHandler.
Expand Down Expand Up @@ -132,6 +133,42 @@ impl Drop for FailingCHFuture {
assert!(self.invoked.load(Ordering::SeqCst) >= 1);
}
}
/// A client hello handler that asserts that the extension with the given
/// IANA code is either present or not present in the client hello
pub struct HasExtensionClientHelloHandler {
pub extension_iana: u16,
pub extension_expected: bool,
}

impl ClientHelloCallback for HasExtensionClientHelloHandler {
fn on_client_hello(
&self,
connection: &mut crate::connection::Connection,
) -> Result<Option<Pin<Box<dyn ConnectionFuture>>>, error::Error> {
let mut exists = false;

unsafe {
let client_hello = s2n_connection_get_client_hello(connection.as_ptr());
s2n_client_hello_has_extension(client_hello, self.extension_iana, &mut exists as _);
}

if self.extension_expected {
assert!(
exists,
"Extension {} was not found in the client hello",
self.extension_iana
);
} else {
assert!(
!exists,
"Unexpected extension {} found in the client hello",
self.extension_iana
)
}

Ok(None)
}
}

#[derive(Debug)]
pub struct CustomError;
Expand Down
51 changes: 50 additions & 1 deletion bindings/rust/s2n-tls/src/testing/s2n_tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ impl Harness {
pub fn connection(&self) -> &Connection {
&self.connection
}

pub fn connection_mut(&mut self) -> &mut Connection {
&mut self.connection
}
}

impl super::Connection for Harness {
Expand Down Expand Up @@ -208,7 +212,7 @@ mod tests {
};
use alloc::sync::Arc;
use core::sync::atomic::Ordering;
use futures_test::task::new_count_waker;
use futures_test::task::{new_count_waker, noop_waker};
use std::{fs, path::Path, pin::Pin, sync::atomic::AtomicUsize};

#[test]
Expand Down Expand Up @@ -536,6 +540,51 @@ mod tests {
Ok(())
}

/// `trust_location()` calls `s2n_config_set_verification_ca_location()`, which has the legacy behavior
/// of enabling OCSP on clients. Since we do not want to replicate that behavior in the Rust bindings,
/// this test verifies that `trust_location()` does not turn on OCSP. It also verifies that turning
/// on OCSP explicitly still works when `trust_location()` is called.
#[test]
fn trust_location_does_not_change_ocsp_status() -> Result<(), Error> {
let pem_dir = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/../../../tests/pems"));
let mut cert = pem_dir.to_path_buf();
cert.push("rsa_4096_sha512_client_cert.pem");
let mut key = pem_dir.to_path_buf();
key.push("rsa_4096_sha512_client_key.pem");

const OCSP_IANA_EXTENSION_ID: u16 = 5;

for enable_ocsp in [true, false] {
let config = {
let mut config = crate::config::Builder::new();

if enable_ocsp {
config.enable_ocsp()?;
}

config.set_security_policy(&security::DEFAULT_TLS13)?;
config.set_verify_host_callback(InsecureAcceptAllCertificatesHandler {})?;
config.set_client_hello_callback(HasExtensionClientHelloHandler {
// This client hello handler will check for the OCSP extension
extension_iana: OCSP_IANA_EXTENSION_ID,
extension_expected: enable_ocsp,
})?;
goatgoose marked this conversation as resolved.
Show resolved Hide resolved
config.load_pem(&fs::read(&cert)?, &fs::read(&key)?)?;
config.trust_location(Some(&cert), None)?;
config.build()?
};

let mut pair = tls_pair(config);
pair.server
.0
.connection_mut()
.set_waker(Some(&noop_waker()))?;
goatgoose marked this conversation as resolved.
Show resolved Hide resolved

poll_tls_pair(pair);
}
Ok(())
}

#[test]
fn connection_level_verify_host_callback() -> Result<(), Error> {
let reject_config = {
Expand Down
2 changes: 2 additions & 0 deletions tls/s2n_connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,8 @@ int s2n_connection_set_config(struct s2n_connection *conn, struct s2n_config *co
* However, the s2n_config_set_verification_ca_location behavior predates client authentication
* support for OCSP stapling, so could only affect whether clients requested OCSP stapling. We
* therefore only have to maintain the legacy behavior for clients, not servers.
*
* Note: The Rust bindings do not maintain the legacy behavior.
*/
conn->request_ocsp_status = config->ocsp_status_requested_by_user;
if (config->ocsp_status_requested_by_s2n && conn->mode == S2N_CLIENT) {
Expand Down