From e41c3550098d0942fe621d39ff0193d645a3189a Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Tue, 2 Apr 2024 17:22:16 +0100 Subject: [PATCH 01/29] Implement `SSL_select_next_proto` Rework ALPN parsing to use an iterator. --- rustls-libssl/MATRIX.md | 2 +- rustls-libssl/build.rs | 1 + rustls-libssl/src/entry.rs | 190 ++++++++++++++++++++++++++++++++++++- rustls-libssl/src/lib.rs | 39 ++++++-- 4 files changed, 221 insertions(+), 11 deletions(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index 6d9dbf8..60903d4 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -408,7 +408,7 @@ | `SSL_renegotiate_pending` | | | | | `SSL_rstate_string` | | | | | `SSL_rstate_string_long` | | | | -| `SSL_select_next_proto` | | :white_check_mark: | | +| `SSL_select_next_proto` | | :white_check_mark: | :white_check_mark: | | `SSL_sendfile` | | | | | `SSL_session_reused` | | :white_check_mark: | :exclamation: [^stub] | | `SSL_set0_CA_list` | | | | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index 905d4e5..5467fc7 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -130,6 +130,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_new", "SSL_pending", "SSL_read", + "SSL_select_next_proto", "SSL_SESSION_free", "SSL_SESSION_get_id", "SSL_session_reused", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index 2f89493..70fc1cc 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -8,7 +8,10 @@ use std::io::{self, Read}; use std::os::raw::{c_char, c_int, c_long, c_uchar, c_uint, c_void}; use std::{fs, path::PathBuf}; -use openssl_sys::{stack_st_X509, OPENSSL_malloc, EVP_PKEY, X509, X509_STORE, X509_STORE_CTX}; +use openssl_sys::{ + stack_st_X509, OPENSSL_malloc, EVP_PKEY, OPENSSL_NPN_NEGOTIATED, OPENSSL_NPN_NO_OVERLAP, X509, + X509_STORE, X509_STORE_CTX, +}; use rustls::pki_types::{CertificateDer, PrivatePkcs8KeyDer}; use crate::bio::{Bio, BIO, BIO_METHOD}; @@ -1024,6 +1027,71 @@ impl Castable for SSL_CIPHER { type RustType = SSL_CIPHER; } +entry! { + pub fn _SSL_select_next_proto( + out: *mut *mut c_uchar, + out_len: *mut c_uchar, + server: *const c_uchar, + server_len: c_uint, + client: *const c_uchar, + client_len: c_uint, + ) -> c_int { + let server = try_slice!(server, server_len); + let client = try_slice!(client, client_len); + + if out.is_null() || out_len.is_null() { + return 0; + } + + // ensure `out` and `out_len` are written even on error. + unsafe { + ptr::write(out, ptr::null_mut()); + ptr::write(out_len, 0); + } + + // ensure `client` is fully validated irrespective of `server` value + for offer in crate::iter_alpn(client) { + if offer.is_none() { + return OPENSSL_NPN_NO_OVERLAP; + } + } + + for supported in crate::iter_alpn(server) { + match supported { + None => { + return OPENSSL_NPN_NO_OVERLAP; + } + + Some(supported) + if crate::iter_alpn(client).any(|offer| offer == Some(supported)) => + { + unsafe { + // safety: + // 1) the openssl API is const-incorrect, we must slice the const from `supported` + // 2) supported.len() must fit inside c_uchar; it was decoded from that + ptr::write(out, supported.as_ptr() as *mut c_uchar); + ptr::write(out_len, supported.len() as c_uchar); + return OPENSSL_NPN_NEGOTIATED; + } + } + + Some(_) => { + continue; + } + } + } + + // fallback: "If no match is found, the first item in client, client_len is returned" + if let Some(Some(fallback)) = crate::iter_alpn(client).next() { + unsafe { + ptr::write(out, fallback.as_ptr() as *mut c_uchar); + ptr::write(out_len, fallback.len() as c_uchar); + } + } + OPENSSL_NPN_NO_OVERLAP + } +} + /// Normal OpenSSL return value convention success indicator. /// /// Compare [`crate::ffi::MysteriouslyOppositeReturnValue`]. @@ -1435,4 +1503,124 @@ mod tests { _SSL_free(ssl); _SSL_CTX_free(ctx); } + + #[test] + fn test_SSL_select_next_proto_match() { + let mut output = ptr::null_mut(); + let mut output_len = 0u8; + let client = b"\x05hello\x05world"; + let server = b"\x05uhoh!\x05world"; + assert_eq!( + _SSL_select_next_proto( + &mut output as *mut *mut u8, + &mut output_len as *mut u8, + server.as_ptr(), + server.len() as c_uint, + client.as_ptr(), + client.len() as c_uint + ), + 1i32 + ); + assert_eq!(b"world", &server[7..]); + assert_eq!(output as *const u8, server[7..].as_ptr()); + assert_eq!(output_len, 5); + } + + #[test] + fn test_SSL_select_next_proto_no_overlap() { + let mut output = ptr::null_mut(); + let mut output_len = 0u8; + let client = b"\x05hello\x05world"; + let server = b"\x05uhoh!\x05what!"; + assert_eq!( + _SSL_select_next_proto( + &mut output as *mut *mut u8, + &mut output_len as *mut u8, + server.as_ptr(), + server.len() as c_uint, + client.as_ptr(), + client.len() as c_uint + ), + 2i32 + ); + assert_eq!(b"hello", &client[1..6]); + assert_eq!(output as *const u8, client[1..].as_ptr()); + assert_eq!(output_len, 5); + } + + #[test] + fn test_SSL_select_next_proto_illegal_client() { + let mut output = ptr::null_mut(); + let mut output_len = 0u8; + let client = b"\x09hello"; + let server = b"\x05uhoh!\x05world"; + assert_eq!( + _SSL_select_next_proto( + &mut output as *mut *mut u8, + &mut output_len as *mut u8, + server.as_ptr(), + server.len() as c_uint, + client.as_ptr(), + client.len() as c_uint + ), + 2i32 + ); + assert_eq!(output as *const u8, ptr::null_mut()); + } + + #[test] + fn test_SSL_select_next_proto_null() { + let mut output = ptr::null_mut(); + let mut output_len = 0u8; + let client = b"\x05hello\x05world"; + let server = b"\x05uhoh!\x05world"; + + assert_eq!( + _SSL_select_next_proto( + ptr::null_mut(), + &mut output_len as *mut u8, + server.as_ptr(), + server.len() as c_uint, + client.as_ptr(), + client.len() as c_uint + ), + 0 + ); + + assert_eq!( + _SSL_select_next_proto( + &mut output as *mut *mut u8, + ptr::null_mut(), + server.as_ptr(), + server.len() as c_uint, + client.as_ptr(), + client.len() as c_uint + ), + 0 + ); + + assert_eq!( + _SSL_select_next_proto( + &mut output as *mut *mut u8, + &mut output_len as *mut u8, + ptr::null(), + server.len() as c_uint, + client.as_ptr(), + client.len() as c_uint + ), + 0 + ); + + assert_eq!( + _SSL_select_next_proto( + &mut output as *mut *mut u8, + &mut output_len as *mut u8, + server.as_ptr(), + server.len() as c_uint, + ptr::null(), + client.len() as c_uint + ), + 0 + ); + } } diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs index 03c35e2..4568771 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -320,22 +320,43 @@ impl SslContext { /// /// For an empty `slice`, returns `Some(vec![])`. /// For a slice with invalid contents, returns `None`. -pub fn parse_alpn(mut slice: &[u8]) -> Option>> { +pub fn parse_alpn(slice: &[u8]) -> Option>> { let mut out = vec![]; - while !slice.is_empty() { - let len = *slice.first()? as usize; - if len == 0 { - return None; - } - let body = slice.get(1..1 + len)?; - out.push(body.to_vec()); - slice = &slice[1 + len..]; + for item in iter_alpn(slice) { + out.push(item?.to_vec()); } Some(out) } +pub fn iter_alpn(mut slice: &[u8]) -> impl Iterator> { + std::iter::from_fn(move || { + // None => end iteration + // Some(None) => error + // Some(_) => an item + + let len = match slice.first() { + None => { + return None; + } + Some(len) => *len as usize, + }; + + if len == 0 { + return Some(None); + } + + match slice.get(1..1 + len) { + None => Some(None), + Some(body) => { + slice = &slice[1 + len..]; + Some(Some(body)) + } + } + }) +} + struct Ssl { ctx: Arc>, raw_options: u64, From 7798fe773aaed16a79e0239c7b6c828a4beba6d4 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Wed, 3 Apr 2024 09:26:28 +0100 Subject: [PATCH 02/29] Implement `SSL_CTX_set_alpn_select_cb` and callbacks Future callbacks will be necessary from rustls trait implementations (like certificate verifier & session store callbacks). For this reason, this commit introduces an indirect storage style for obtaining the original *SSL object pointer. This uses thread-local storage. --- rustls-libssl/MATRIX.md | 2 +- rustls-libssl/build.rs | 1 + rustls-libssl/src/callbacks.rs | 99 ++++++++++++++++++++++++++++++++++ rustls-libssl/src/entry.rs | 26 ++++++++- rustls-libssl/src/lib.rs | 53 ++++++++++++++++-- rustls-libssl/tests/server.c | 25 +++++++-- 6 files changed, 197 insertions(+), 9 deletions(-) create mode 100644 rustls-libssl/src/callbacks.rs diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index 60903d4..afe09c0 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -131,7 +131,7 @@ | `SSL_CTX_set1_param` | | | | | `SSL_CTX_set_allow_early_data_cb` | | | | | `SSL_CTX_set_alpn_protos` | :white_check_mark: | :white_check_mark: | :white_check_mark: | -| `SSL_CTX_set_alpn_select_cb` | | :white_check_mark: | | +| `SSL_CTX_set_alpn_select_cb` | | :white_check_mark: | :white_check_mark: | | `SSL_CTX_set_async_callback` | | | | | `SSL_CTX_set_async_callback_arg` | | | | | `SSL_CTX_set_block_padding` | | | | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index 5467fc7..4659566 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -78,6 +78,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_CTX_sess_set_new_cb", "SSL_CTX_sess_set_remove_cb", "SSL_CTX_set_alpn_protos", + "SSL_CTX_set_alpn_select_cb", "SSL_CTX_set_cipher_list", "SSL_CTX_set_ciphersuites", "SSL_CTX_set_default_passwd_cb", diff --git a/rustls-libssl/src/callbacks.rs b/rustls-libssl/src/callbacks.rs new file mode 100644 index 0000000..9c37330 --- /dev/null +++ b/rustls-libssl/src/callbacks.rs @@ -0,0 +1,99 @@ +use core::cell::RefCell; +use core::ffi::{c_uchar, c_void}; +use core::{ptr, slice}; + +use openssl_sys::{SSL_TLSEXT_ERR_NOACK, SSL_TLSEXT_ERR_OK}; + +use crate::entry::{SSL_CTX_alpn_select_cb_func, SSL}; +use crate::error::Error; + +/// Smuggling SSL* pointers from the outer entrypoint into the +/// callback call site. +pub struct SslCallbackContext; + +impl SslCallbackContext { + /// Register the original SSL* pointer for use in later callbacks. + /// + /// The returned object de-registers itself when dropped. + pub fn new(ssl: *mut SSL) -> Self { + SSL_CALLBACK_CONTEXT.set(Some(ssl)); + Self + } + + /// Get the original SSL* pointer, or else `NULL` + /// + /// This has thread-local semantics: it uses the most recent + /// object of this type created on this thread. + pub fn ssl_ptr() -> *mut SSL { + SSL_CALLBACK_CONTEXT.with_borrow(|holder| { + holder + .as_ref() + .map(|inner| *inner) + .unwrap_or_else(ptr::null_mut) + }) + } +} + +impl Drop for SslCallbackContext { + fn drop(&mut self) { + SSL_CALLBACK_CONTEXT.set(None); + } +} + +thread_local! { + static SSL_CALLBACK_CONTEXT: RefCell> = const { RefCell::new(None) }; +} + +/// Configuration needed to call [`invoke_alpn_callback`] later +#[derive(Debug, Clone)] +pub struct AlpnCallbackConfig { + pub cb: SSL_CTX_alpn_select_cb_func, + pub context: *mut c_void, +} + +impl AlpnCallbackConfig { + /// Call a `SSL_CTX_alpn_select_cb_func` callback + /// + /// Returns the selected ALPN, or None, or an error. + pub fn invoke(&self, offer: &[u8]) -> Result>, Error> { + let callback = match self.cb { + Some(callback) => callback, + None => { + return Ok(None); + } + }; + + let ssl = SslCallbackContext::ssl_ptr(); + + let mut output_ptr: *const c_uchar = ptr::null(); + let mut output_len = 0u8; + let result = unsafe { + callback( + ssl, + &mut output_ptr as *mut *const c_uchar, + &mut output_len as *mut u8, + offer.as_ptr(), + offer.len() as u32, + self.context, + ) + }; + + if result == SSL_TLSEXT_ERR_OK && !output_ptr.is_null() { + let chosen = unsafe { slice::from_raw_parts(output_ptr, output_len as usize) }; + Ok(Some(chosen.to_vec())) + } else if result == SSL_TLSEXT_ERR_NOACK { + Ok(None) + } else { + Err(Error::bad_data("alpn not chosen")) + } + } +} + +impl Default for AlpnCallbackConfig { + fn default() -> Self { + Self { + cb: None, + context: ptr::null_mut(), + } + } +} diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index 70fc1cc..212bffd 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -15,6 +15,7 @@ use openssl_sys::{ use rustls::pki_types::{CertificateDer, PrivatePkcs8KeyDer}; use crate::bio::{Bio, BIO, BIO_METHOD}; +use crate::callbacks::SslCallbackContext; use crate::error::{ffi_panic_boundary, Error, MysteriouslyOppositeReturnValue}; use crate::evp_pkey::EvpPkey; use crate::ffi::{ @@ -447,12 +448,34 @@ entry! { } } +pub type SSL_CTX_alpn_select_cb_func = Option< + unsafe extern "C" fn( + ssl: *mut SSL, + out: *mut *const c_uchar, + outlen: *mut c_uchar, + in_: *const c_uchar, + inlen: c_uint, + arg: *mut c_void, + ) -> c_int, +>; + +entry! { + pub fn _SSL_CTX_set_alpn_select_cb( + ctx: *mut SSL_CTX, + cb: SSL_CTX_alpn_select_cb_func, + arg: *mut c_void, + ) { + let ctx = try_clone_arc!(ctx); + ctx.get_mut().set_alpn_select_cb(cb, arg); + } +} + impl Castable for SSL_CTX { type Ownership = OwnershipArc; type RustType = NotThreadSafe; } -type SSL = crate::Ssl; +pub type SSL = crate::Ssl; entry! { pub fn _SSL_new(ctx: *mut SSL_CTX) -> *mut SSL { @@ -653,6 +676,7 @@ entry! { entry! { pub fn _SSL_accept(ssl: *mut SSL) -> c_int { + let _callbacks = SslCallbackContext::new(ssl); match try_clone_arc!(ssl).get_mut().accept() { Err(e) => e.raise().into(), Ok(()) => C_INT_SUCCESS, diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs index 4568771..e783459 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -1,4 +1,4 @@ -use core::ffi::{c_int, c_uint, CStr}; +use core::ffi::{c_int, c_uint, c_void, CStr}; use core::{mem, ptr}; use std::fs; use std::io::{ErrorKind, Read, Write}; @@ -20,6 +20,7 @@ use rustls::{ use not_thread_safe::NotThreadSafe; mod bio; +mod callbacks; #[macro_use] mod constants; #[allow( @@ -219,6 +220,7 @@ pub struct SslContext { alpn: Vec>, default_cert_file: Option, default_cert_dir: Option, + alpn_callback: callbacks::AlpnCallbackConfig, auth_keys: sign::CertifiedKeySet, } @@ -233,6 +235,7 @@ impl SslContext { alpn: vec![], default_cert_file: None, default_cert_dir: None, + alpn_callback: callbacks::AlpnCallbackConfig::default(), auth_keys: sign::CertifiedKeySet::default(), } } @@ -294,6 +297,10 @@ impl SslContext { self.alpn = alpn; } + fn set_alpn_select_cb(&mut self, cb: entry::SSL_CTX_alpn_select_cb_func, context: *mut c_void) { + self.alpn_callback = callbacks::AlpnCallbackConfig { cb, context }; + } + fn stage_certificate_end_entity(&mut self, end: CertificateDer<'static>) { self.auth_keys.stage_certificate_end_entity(end) } @@ -365,6 +372,7 @@ struct Ssl { verify_roots: RootCertStore, verify_server_name: Option>, alpn: Vec>, + alpn_callback: callbacks::AlpnCallbackConfig, sni_server_name: Option>, bio: Option, conn: ConnState, @@ -393,6 +401,7 @@ impl Ssl { verify_roots: Self::load_verify_certs(inner)?, verify_server_name: None, alpn: inner.alpn.clone(), + alpn_callback: inner.alpn_callback.clone(), sni_server_name: None, bio: None, conn: ConnState::Nothing, @@ -569,8 +578,30 @@ impl Ssl { self.conn = ConnState::Accepting(Acceptor::default()); } - self.try_io()?; + self.try_io() + } + + fn invoke_accepted_callbacks(&mut self) -> Result<(), error::Error> { + // called on transition from `Accepting` -> `Accepted` + let accepted = match &self.conn { + ConnState::Accepted(accepted) => accepted, + _ => unreachable!(), + }; + if let Some(alpn_iter) = accepted.client_hello().alpn() { + let offer = encode_alpn(alpn_iter); + + let choice = self.alpn_callback.invoke(&offer)?; + + if let Some(choice) = choice { + self.alpn = vec![choice]; + } + } + + self.complete_accept() + } + + fn complete_accept(&mut self) -> Result<(), error::Error> { if let ConnState::Accepted(_) = self.conn { self.init_server_conn()?; } @@ -596,12 +627,14 @@ impl Ssl { .server_resolver() .ok_or_else(|| error::Error::bad_data("missing server keys"))?; - let config = ServerConfig::builder_with_provider(provider) + let mut config = ServerConfig::builder_with_provider(provider) .with_protocol_versions(method.server_versions) .map_err(error::Error::from_rustls)? .with_client_cert_verifier(verifier.clone()) .with_cert_resolver(resolver); + config.alpn_protocols = mem::take(&mut self.alpn); + let accepted = match mem::replace(&mut self.conn, ConnState::Nothing) { ConnState::Accepted(accepted) => accepted, _ => unreachable!(), @@ -718,7 +751,7 @@ impl Ssl { Ok(None) => Ok(()), Ok(Some(accepted)) => { self.conn = ConnState::Accepted(accepted); - Ok(()) + self.invoke_accepted_callbacks() } Err((error, mut alert)) => { alert.write_all(bio).map_err(error::Error::from_io)?; @@ -915,6 +948,18 @@ impl Ssl { } } +/// Encode rustls's internal representation in the wire format. +fn encode_alpn<'a>(iter: impl Iterator) -> Vec { + let mut out = vec![]; + + for item in iter { + out.push(item.len() as u8); + out.extend_from_slice(item); + } + + out +} + /// This is a reduced-fidelity version of `OSSL_HANDSHAKE_STATE`. /// /// We don't track all the individual message states (rustls doesn't expose that detail). diff --git a/rustls-libssl/tests/server.c b/rustls-libssl/tests/server.c index 3fbf152..db1c7a9 100644 --- a/rustls-libssl/tests/server.c +++ b/rustls-libssl/tests/server.c @@ -19,6 +19,25 @@ #include "helpers.h" +static int alpn_cookie = 12345; + +static int alpn_callback(SSL *ssl, const uint8_t **out, uint8_t *outlen, + const uint8_t *in, unsigned int inlen, void *arg) { + printf("in alpn_callback:\n"); + assert(ssl != NULL); + assert(arg == &alpn_cookie); + hexdump(" in", in, (int)inlen); + if (SSL_select_next_proto((uint8_t **)out, outlen, + (const uint8_t *)"\x08http/1.1", 9, in, + inlen) == OPENSSL_NPN_NEGOTIATED) { + hexdump(" select", *out, (int)*outlen); + return SSL_TLSEXT_ERR_OK; + } else { + printf(" alpn failed\n"); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } +} + int main(int argc, char **argv) { if (argc != 5) { printf("%s |unauth\n\n", @@ -55,6 +74,9 @@ int main(int argc, char **argv) { printf("client auth disabled\n"); } + SSL_CTX_set_alpn_select_cb(ctx, alpn_callback, &alpn_cookie); + dump_openssl_error_stack(); + X509 *server_cert = NULL; EVP_PKEY *server_key = NULL; TRACE(SSL_CTX_use_certificate_chain_file(ctx, certfile)); @@ -62,9 +84,6 @@ int main(int argc, char **argv) { server_key = SSL_CTX_get0_privatekey(ctx); server_cert = SSL_CTX_get0_certificate(ctx); - TRACE(SSL_CTX_set_alpn_protos(ctx, (const uint8_t *)"\x08http/1.1", 9)); - dump_openssl_error_stack(); - SSL *ssl = SSL_new(ctx); dump_openssl_error_stack(); printf("SSL_new: SSL_get_privatekey %s SSL_CTX_get0_privatekey\n", From 6a0241749feafdcde5c85fffee54f85ad31ef8a6 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Wed, 3 Apr 2024 14:40:44 +0100 Subject: [PATCH 03/29] Implement `SSL_do_handshake` --- rustls-libssl/MATRIX.md | 2 +- rustls-libssl/build.rs | 1 + rustls-libssl/src/entry.rs | 12 ++++++++++++ rustls-libssl/src/lib.rs | 8 ++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index afe09c0..f93971d 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -293,7 +293,7 @@ | `SSL_dane_enable` | | | | | `SSL_dane_set_flags` | | | | | `SSL_dane_tlsa_add` | | | | -| `SSL_do_handshake` | | :white_check_mark: | | +| `SSL_do_handshake` | | :white_check_mark: | :white_check_mark: | | `SSL_dup` | | | | | `SSL_dup_CA_list` | | | | | `SSL_enable_ct` [^ct] | | | | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index 4659566..83d1088 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -103,6 +103,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_CTX_use_certificate_file", "SSL_CTX_use_PrivateKey", "SSL_CTX_use_PrivateKey_file", + "SSL_do_handshake", "SSL_free", "SSL_get0_alpn_selected", "SSL_get0_peer_certificate", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index 212bffd..f7ea56a 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -684,6 +684,18 @@ entry! { } } +entry! { + pub fn _SSL_do_handshake(ssl: *mut SSL) -> c_int { + let _callbacks = SslCallbackContext::new(ssl); + let ssl = try_clone_arc!(ssl); + + match ssl.get_mut().handshake() { + Err(e) => e.raise().into(), + Ok(()) => C_INT_SUCCESS, + } + } +} + entry! { pub fn _SSL_write(ssl: *mut SSL, buf: *const c_void, num: c_int) -> c_int { const ERROR: c_int = -1; diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs index e783459..a1379c3 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -519,6 +519,14 @@ impl Ssl { .unwrap_or_else(ptr::null_mut) } + fn handshake(&mut self) -> Result<(), error::Error> { + match self.mode { + ConnMode::Client => self.connect(), + ConnMode::Server => self.accept(), + ConnMode::Unknown => Err(error::Error::bad_data("connection mode required")), + } + } + fn connect(&mut self) -> Result<(), error::Error> { if let ConnMode::Unknown = self.mode { self.set_client_mode(); From 442d12b091565e935468d14367f68f68f53d3134 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Wed, 3 Apr 2024 15:29:00 +0100 Subject: [PATCH 04/29] Stub out NPN-related functions (NPN was abandoned in 2011 and replaced with ALPN.) --- rustls-libssl/MATRIX.md | 4 ++-- rustls-libssl/build.rs | 2 ++ rustls-libssl/src/entry.rs | 25 +++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index f93971d..3e0106e 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -163,7 +163,7 @@ | `SSL_CTX_set_max_early_data` | | :white_check_mark: | | | `SSL_CTX_set_msg_callback` | :white_check_mark: | | :exclamation: [^stub] | | `SSL_CTX_set_next_proto_select_cb` [^nextprotoneg] | :white_check_mark: | | :exclamation: [^stub] | -| `SSL_CTX_set_next_protos_advertised_cb` [^nextprotoneg] | | :white_check_mark: | | +| `SSL_CTX_set_next_protos_advertised_cb` [^nextprotoneg] | | :white_check_mark: | :exclamation: [^stub] | | `SSL_CTX_set_not_resumable_session_callback` | | | | | `SSL_CTX_set_num_tickets` | | | | | `SSL_CTX_set_options` | :white_check_mark: | :white_check_mark: | :white_check_mark: | @@ -307,7 +307,7 @@ | `SSL_get0_dane` | | | | | `SSL_get0_dane_authority` | | | | | `SSL_get0_dane_tlsa` | | | | -| `SSL_get0_next_proto_negotiated` [^nextprotoneg] | | :white_check_mark: | | +| `SSL_get0_next_proto_negotiated` [^nextprotoneg] | | :white_check_mark: | :exclamation: [^stub] | | `SSL_get0_param` | | | | | `SSL_get0_peer_CA_list` | | | | | `SSL_get0_peer_certificate` | | | :white_check_mark: | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index 83d1088..cefb6e1 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -91,6 +91,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_CTX_set_keylog_callback", "SSL_CTX_set_msg_callback", "SSL_CTX_set_next_proto_select_cb", + "SSL_CTX_set_next_protos_advertised_cb", "SSL_CTX_set_options", "SSL_CTX_set_post_handshake_auth", "SSL_CTX_set_session_id_context", @@ -106,6 +107,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_do_handshake", "SSL_free", "SSL_get0_alpn_selected", + "SSL_get0_next_proto_negotiated", "SSL_get0_peer_certificate", "SSL_get0_verified_chain", "SSL_get1_peer_certificate", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index f7ea56a..3ffe894 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -1382,6 +1382,31 @@ pub type SSL_CTX_npn_select_cb_func = Option< ) -> c_int, >; +entry_stub! { + pub fn _SSL_get0_next_proto_negotiated( + _ssl: *const SSL, + _data: *mut *const c_uchar, + _len: *mut c_uint, + ); +} + +entry_stub! { + pub fn _SSL_CTX_set_next_protos_advertised_cb( + _ctx: *mut SSL_CTX, + _cb: SSL_CTX_npn_advertised_cb_func, + _arg: *mut c_void, + ); +} + +pub type SSL_CTX_npn_advertised_cb_func = Option< + unsafe extern "C" fn( + ssl: *mut SSL, + out: *mut *const c_uchar, + outlen: *mut c_uint, + arg: *mut c_void, + ) -> c_int, +>; + // no password-protected key loading entry_stub! { From 2531945fe4a5f777962469e2bacd667d573bfb26 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Fri, 5 Apr 2024 16:31:28 +0100 Subject: [PATCH 05/29] Implement `SSL_CTX_set_cert_cb` --- rustls-libssl/MATRIX.md | 2 +- rustls-libssl/build.rs | 1 + rustls-libssl/src/callbacks.rs | 37 +++++++++++++++++++++++++++++++++- rustls-libssl/src/entry.rs | 10 +++++++++ rustls-libssl/src/lib.rs | 10 +++++++++ rustls-libssl/tests/server.c | 12 +++++++++++ 6 files changed, 70 insertions(+), 2 deletions(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index 3e0106e..7aaf997 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -135,7 +135,7 @@ | `SSL_CTX_set_async_callback` | | | | | `SSL_CTX_set_async_callback_arg` | | | | | `SSL_CTX_set_block_padding` | | | | -| `SSL_CTX_set_cert_cb` | | :white_check_mark: | | +| `SSL_CTX_set_cert_cb` | | :white_check_mark: | :white_check_mark: | | `SSL_CTX_set_cert_store` | | | | | `SSL_CTX_set_cert_verify_callback` | | | | | `SSL_CTX_set_cipher_list` | :white_check_mark: | :white_check_mark: | :exclamation: [^stub] | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index cefb6e1..de43f3e 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -79,6 +79,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_CTX_sess_set_remove_cb", "SSL_CTX_set_alpn_protos", "SSL_CTX_set_alpn_select_cb", + "SSL_CTX_set_cert_cb", "SSL_CTX_set_cipher_list", "SSL_CTX_set_ciphersuites", "SSL_CTX_set_default_passwd_cb", diff --git a/rustls-libssl/src/callbacks.rs b/rustls-libssl/src/callbacks.rs index 9c37330..084cec8 100644 --- a/rustls-libssl/src/callbacks.rs +++ b/rustls-libssl/src/callbacks.rs @@ -4,7 +4,7 @@ use core::{ptr, slice}; use openssl_sys::{SSL_TLSEXT_ERR_NOACK, SSL_TLSEXT_ERR_OK}; -use crate::entry::{SSL_CTX_alpn_select_cb_func, SSL}; +use crate::entry::{SSL_CTX_alpn_select_cb_func, SSL_CTX_cert_cb_func, SSL}; use crate::error::Error; /// Smuggling SSL* pointers from the outer entrypoint into the @@ -97,3 +97,38 @@ impl Default for AlpnCallbackConfig { } } } + +/// Configuration needed to call [`invoke_cert_callback`] later +#[derive(Debug, Clone)] +pub struct CertCallbackConfig { + pub cb: SSL_CTX_cert_cb_func, + pub context: *mut c_void, +} + +impl CertCallbackConfig { + pub fn invoke(&self) -> Result<(), Error> { + let callback = match self.cb { + Some(callback) => callback, + None => { + return Ok(()); + } + }; + let ssl = SslCallbackContext::ssl_ptr(); + + let result = unsafe { callback(ssl, self.context) }; + + match result { + 1 => Ok(()), + _ => Err(Error::not_supported("SSL_CTX_cert_cb_func returned != 1")), + } + } +} + +impl Default for CertCallbackConfig { + fn default() -> Self { + Self { + cb: None, + context: ptr::null_mut(), + } + } +} diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index 3ffe894..1fb57b0 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -470,6 +470,16 @@ entry! { } } +pub type SSL_CTX_cert_cb_func = + Option c_int>; + +entry! { + pub fn _SSL_CTX_set_cert_cb(ctx: *mut SSL_CTX, cb: SSL_CTX_cert_cb_func, arg: *mut c_void) { + let ctx = try_clone_arc!(ctx); + ctx.get_mut().set_cert_cb(cb, arg); + } +} + impl Castable for SSL_CTX { type Ownership = OwnershipArc; type RustType = NotThreadSafe; diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs index a1379c3..c665629 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -221,6 +221,7 @@ pub struct SslContext { default_cert_file: Option, default_cert_dir: Option, alpn_callback: callbacks::AlpnCallbackConfig, + cert_callback: callbacks::CertCallbackConfig, auth_keys: sign::CertifiedKeySet, } @@ -236,6 +237,7 @@ impl SslContext { default_cert_file: None, default_cert_dir: None, alpn_callback: callbacks::AlpnCallbackConfig::default(), + cert_callback: callbacks::CertCallbackConfig::default(), auth_keys: sign::CertifiedKeySet::default(), } } @@ -301,6 +303,10 @@ impl SslContext { self.alpn_callback = callbacks::AlpnCallbackConfig { cb, context }; } + fn set_cert_cb(&mut self, cb: entry::SSL_CTX_cert_cb_func, context: *mut c_void) { + self.cert_callback = callbacks::CertCallbackConfig { cb, context }; + } + fn stage_certificate_end_entity(&mut self, end: CertificateDer<'static>) { self.auth_keys.stage_certificate_end_entity(end) } @@ -373,6 +379,7 @@ struct Ssl { verify_server_name: Option>, alpn: Vec>, alpn_callback: callbacks::AlpnCallbackConfig, + cert_callback: callbacks::CertCallbackConfig, sni_server_name: Option>, bio: Option, conn: ConnState, @@ -402,6 +409,7 @@ impl Ssl { verify_server_name: None, alpn: inner.alpn.clone(), alpn_callback: inner.alpn_callback.clone(), + cert_callback: inner.cert_callback.clone(), sni_server_name: None, bio: None, conn: ConnState::Nothing, @@ -606,6 +614,8 @@ impl Ssl { } } + self.cert_callback.invoke()?; + self.complete_accept() } diff --git a/rustls-libssl/tests/server.c b/rustls-libssl/tests/server.c index db1c7a9..e7bd802 100644 --- a/rustls-libssl/tests/server.c +++ b/rustls-libssl/tests/server.c @@ -38,6 +38,15 @@ static int alpn_callback(SSL *ssl, const uint8_t **out, uint8_t *outlen, } } +static int cert_cookie = 12345; + +static int cert_callback(SSL *ssl, void *arg) { + printf("in cert_callback\n"); + assert(ssl != NULL); + assert(arg == &cert_cookie); + return 1; +} + int main(int argc, char **argv) { if (argc != 5) { printf("%s |unauth\n\n", @@ -77,6 +86,9 @@ int main(int argc, char **argv) { SSL_CTX_set_alpn_select_cb(ctx, alpn_callback, &alpn_cookie); dump_openssl_error_stack(); + SSL_CTX_set_cert_cb(ctx, cert_callback, &cert_cookie); + dump_openssl_error_stack(); + X509 *server_cert = NULL; EVP_PKEY *server_key = NULL; TRACE(SSL_CTX_use_certificate_chain_file(ctx, certfile)); From d5a06431ff8fdc5158ff35b59b2895922fc002e4 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Fri, 5 Apr 2024 16:32:25 +0100 Subject: [PATCH 06/29] Implement `SSL_get_servername` and `SSL_get_servername_type` --- rustls-libssl/MATRIX.md | 4 ++-- rustls-libssl/build.rs | 2 ++ rustls-libssl/src/entry.rs | 24 +++++++++++++++++++++-- rustls-libssl/src/lib.rs | 37 +++++++++++++++++++++++++++++++++++- rustls-libssl/tests/client.c | 8 ++++++++ rustls-libssl/tests/server.c | 3 +++ 6 files changed, 73 insertions(+), 5 deletions(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index 7aaf997..5834c58 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -362,8 +362,8 @@ | `SSL_get_security_level` | | | | | `SSL_get_selected_srtp_profile` [^srtp] | | | | | `SSL_get_server_random` | | | | -| `SSL_get_servername` | | :white_check_mark: | | -| `SSL_get_servername_type` | | | | +| `SSL_get_servername` | | :white_check_mark: | :white_check_mark: | +| `SSL_get_servername_type` | | | :white_check_mark: | | `SSL_get_session` | | :white_check_mark: | :exclamation: [^stub] | | `SSL_get_shared_ciphers` | | | | | `SSL_get_shared_sigalgs` | | | | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index de43f3e..e650376 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -121,6 +121,8 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_get_peer_cert_chain", "SSL_get_privatekey", "SSL_get_rbio", + "SSL_get_servername", + "SSL_get_servername_type", "SSL_get_session", "SSL_get_shutdown", "SSL_get_state", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index 1fb57b0..1f223e5 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -9,8 +9,8 @@ use std::os::raw::{c_char, c_int, c_long, c_uchar, c_uint, c_void}; use std::{fs, path::PathBuf}; use openssl_sys::{ - stack_st_X509, OPENSSL_malloc, EVP_PKEY, OPENSSL_NPN_NEGOTIATED, OPENSSL_NPN_NO_OVERLAP, X509, - X509_STORE, X509_STORE_CTX, + stack_st_X509, OPENSSL_malloc, TLSEXT_NAMETYPE_host_name, EVP_PKEY, OPENSSL_NPN_NEGOTIATED, + OPENSSL_NPN_NO_OVERLAP, X509, X509_STORE, X509_STORE_CTX, }; use rustls::pki_types::{CertificateDer, PrivatePkcs8KeyDer}; @@ -968,6 +968,26 @@ entry! { } } +entry! { + pub fn _SSL_get_servername(ssl: *const SSL, ty: c_int) -> *const c_char { + if ty != TLSEXT_NAMETYPE_host_name { + return ptr::null(); + } + + try_clone_arc!(ssl).get_mut().server_name_pointer() + } +} + +entry! { + pub fn _SSL_get_servername_type(ssl: *const SSL) -> c_int { + if _SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name).is_null() { + -1 + } else { + TLSEXT_NAMETYPE_host_name + } + } +} + impl Castable for SSL { type Ownership = OwnershipArc; type RustType = NotThreadSafe; diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs index c665629..e680ff2 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -1,5 +1,6 @@ -use core::ffi::{c_int, c_uint, c_void, CStr}; +use core::ffi::{c_char, c_int, c_uint, c_void, CStr}; use core::{mem, ptr}; +use std::ffi::CString; use std::fs; use std::io::{ErrorKind, Read, Write}; use std::path::PathBuf; @@ -381,6 +382,7 @@ struct Ssl { alpn_callback: callbacks::AlpnCallbackConfig, cert_callback: callbacks::CertCallbackConfig, sni_server_name: Option>, + server_name: Option, bio: Option, conn: ConnState, peer_cert: Option, @@ -411,6 +413,7 @@ impl Ssl { alpn_callback: inner.alpn_callback.clone(), cert_callback: inner.cert_callback.clone(), sni_server_name: None, + server_name: None, bio: None, conn: ConnState::Nothing, peer_cert: None, @@ -501,6 +504,33 @@ impl Ssl { } } + fn server_name_pointer(&mut self) -> *const c_char { + // This does double duty (see `SSL_get_servername`): + // + // for clients, it is just `sni_server_name` + // (filled in here, lazily) + // + // for servers, it is the client's offered SNI name + // (filled in below in `invoke_accepted_callbacks`) + // + // the remaining annoyance is that the returned pointer has to NUL-terminated. + + match self.mode { + ConnMode::Server => self.server_name.as_ref().map(|cstr| cstr.as_ptr()), + ConnMode::Client | ConnMode::Unknown => match &self.server_name { + Some(existing) => Some(existing.as_ptr()), + None => { + self.server_name = self + .sni_server_name + .as_ref() + .and_then(|name| CString::new(name.to_str().as_bytes()).ok()); + self.server_name.as_ref().map(|cstr| cstr.as_ptr()) + } + }, + } + .unwrap_or_else(ptr::null) + } + fn set_bio(&mut self, bio: bio::Bio) { self.bio = Some(bio); } @@ -604,6 +634,11 @@ impl Ssl { _ => unreachable!(), }; + self.server_name = accepted + .client_hello() + .server_name() + .and_then(|sni| CString::new(sni.as_bytes()).ok()); + if let Some(alpn_iter) = accepted.client_hello().alpn() { let offer = encode_alpn(alpn_iter); diff --git a/rustls-libssl/tests/client.c b/rustls-libssl/tests/client.c index 4d57886..6f37848 100644 --- a/rustls-libssl/tests/client.c +++ b/rustls-libssl/tests/client.c @@ -77,6 +77,14 @@ int main(int argc, char **argv) { printf("SSL_new: SSL_get_certificate %s SSL_CTX_get0_certificate\n", SSL_get_certificate(ssl) == client_cert ? "same as" : "differs to"); state(ssl); + printf("SSL_get_servername: %s (%d)\n", + SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name), + SSL_get_servername_type(ssl)); + TRACE(SSL_set_tlsext_host_name(ssl, "localhost")); + dump_openssl_error_stack(); + printf("SSL_get_servername: %s (%d)\n", + SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name), + SSL_get_servername_type(ssl)); TRACE(SSL_set1_host(ssl, host)); dump_openssl_error_stack(); TRACE(SSL_set_fd(ssl, sock)); diff --git a/rustls-libssl/tests/server.c b/rustls-libssl/tests/server.c index e7bd802..a91481a 100644 --- a/rustls-libssl/tests/server.c +++ b/rustls-libssl/tests/server.c @@ -123,6 +123,9 @@ int main(int argc, char **argv) { printf("version: %s\n", SSL_get_version(ssl)); printf("verify-result: %ld\n", SSL_get_verify_result(ssl)); printf("cipher: %s\n", SSL_CIPHER_standard_name(SSL_get_current_cipher(ssl))); + printf("SSL_get_servername: %s (%d)\n", + SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name), + SSL_get_servername_type(ssl)); show_peer_certificate("client", ssl); From 5feda71c5df024e42dd6d56b19b2e8692305d984 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Thu, 4 Apr 2024 13:12:24 +0100 Subject: [PATCH 07/29] Implement `SSL_CTX_set_tlsext_servername_callback` and co `SSL_CTX_set_tlsext_servername_callback` is a define to `SSL_CTX_callback_ctrl`. `SSL_CTX_set_tlsext_servername_arg` is a define to `SSL_CTX_ctrl`. --- rustls-libssl/MATRIX.md | 2 +- rustls-libssl/build.rs | 1 + rustls-libssl/src/callbacks.rs | 51 ++++++++++++++++++++++++++++++++-- rustls-libssl/src/entry.rs | 36 ++++++++++++++++++++++-- rustls-libssl/src/lib.rs | 14 ++++++++++ rustls-libssl/tests/server.c | 18 ++++++++++++ 6 files changed, 117 insertions(+), 5 deletions(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index 5834c58..e063dbb 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -67,7 +67,7 @@ | `SSL_CTX_add_custom_ext` | | | | | `SSL_CTX_add_server_custom_ext` | | | | | `SSL_CTX_add_session` | | | | -| `SSL_CTX_callback_ctrl` | | :white_check_mark: | | +| `SSL_CTX_callback_ctrl` | | :white_check_mark: | :white_check_mark: | | `SSL_CTX_check_private_key` | :white_check_mark: | | :white_check_mark: | | `SSL_CTX_clear_options` | | :white_check_mark: | :white_check_mark: | | `SSL_CTX_config` | | | | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index e650376..4d82da1 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -61,6 +61,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_connect", "SSL_ctrl", "SSL_CTX_add_client_CA", + "SSL_CTX_callback_ctrl", "SSL_CTX_check_private_key", "SSL_CTX_clear_options", "SSL_CTX_ctrl", diff --git a/rustls-libssl/src/callbacks.rs b/rustls-libssl/src/callbacks.rs index 084cec8..05f0b62 100644 --- a/rustls-libssl/src/callbacks.rs +++ b/rustls-libssl/src/callbacks.rs @@ -1,10 +1,13 @@ use core::cell::RefCell; -use core::ffi::{c_uchar, c_void}; +use core::ffi::{c_int, c_uchar, c_void}; use core::{ptr, slice}; use openssl_sys::{SSL_TLSEXT_ERR_NOACK, SSL_TLSEXT_ERR_OK}; +use rustls::AlertDescription; -use crate::entry::{SSL_CTX_alpn_select_cb_func, SSL_CTX_cert_cb_func, SSL}; +use crate::entry::{ + SSL_CTX_alpn_select_cb_func, SSL_CTX_cert_cb_func, SSL_CTX_servername_callback_func, SSL, +}; use crate::error::Error; /// Smuggling SSL* pointers from the outer entrypoint into the @@ -132,3 +135,47 @@ impl Default for CertCallbackConfig { } } } + +/// Configuration needed to call [`invoke_servername_callback`] later +#[derive(Debug, Clone)] +pub struct ServerNameCallbackConfig { + pub cb: SSL_CTX_servername_callback_func, + pub context: *mut c_void, +} + +impl ServerNameCallbackConfig { + pub fn invoke(&self) -> Result<(), Error> { + let callback = match self.cb { + Some(callback) => callback, + None => { + return Ok(()); + } + }; + + let ssl = SslCallbackContext::ssl_ptr(); + + let unrecognised_name = u8::from(AlertDescription::UnrecognisedName) as c_int; + let mut alert = unrecognised_name; + let result = unsafe { callback(ssl, &mut alert as *mut c_int, self.context) }; + + if alert != unrecognised_name { + log::trace!("NYI: customised alert during servername callback"); + } + + match result { + SSL_TLSEXT_ERR_OK => Ok(()), + _ => Err(Error::not_supported( + "SSL_CTX_servername_callback_func return error", + )), + } + } +} + +impl Default for ServerNameCallbackConfig { + fn default() -> Self { + Self { + cb: None, + context: ptr::null_mut(), + } + } +} diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index 1f223e5..132a076 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -164,7 +164,7 @@ entry! { log::warn!("unimplemented SSL_CTX_set_max_proto_version()"); 1 } - Ok(SslCtrl::SetTlsExtHostname) => { + Ok(SslCtrl::SetTlsExtHostname) | Ok(SslCtrl::SetTlsExtServerNameCallback) => { // not a defined operation in the OpenSSL API 0 } @@ -184,7 +184,10 @@ entry! { ctx.get_mut().stage_certificate_chain(chain); C_INT_SUCCESS as i64 } - + Ok(SslCtrl::SetTlsExtServerNameArg) => { + ctx.get_mut().set_servername_callback_context(parg); + C_INT_SUCCESS as c_long + } Err(()) => { log::warn!("unimplemented _SSL_CTX_ctrl(..., {cmd}, {larg}, ...)"); 0 @@ -480,6 +483,31 @@ entry! { } } +// nb. calls into SSL_CTX_callback_ctrl cast away the real function pointer type, +// and then cast back to the real type based on `cmd`. +pub type SSL_CTX_any_func = Option; + +pub type SSL_CTX_servername_callback_func = + Option c_int>; + +entry! { + pub fn _SSL_CTX_callback_ctrl(ctx: *mut SSL_CTX, cmd: c_int, fp: SSL_CTX_any_func) -> c_long { + let ctx = try_clone_arc!(ctx); + + match SslCtrl::try_from(cmd) { + Ok(SslCtrl::SetTlsExtServerNameCallback) => { + // safety: same layout + let fp = unsafe { + mem::transmute::(fp) + }; + ctx.get_mut().set_servername_callback(fp); + C_INT_SUCCESS as c_long + } + _ => 0, + } + } +} + impl Castable for SSL_CTX { type Ownership = OwnershipArc; type RustType = NotThreadSafe; @@ -551,6 +579,8 @@ entry! { ssl.get_mut().stage_certificate_chain(chain); C_INT_SUCCESS as i64 } + // not a defined operation in the OpenSSL API + Ok(SslCtrl::SetTlsExtServerNameCallback) | Ok(SslCtrl::SetTlsExtServerNameArg) => 0, Err(()) => { log::warn!("unimplemented _SSL_ctrl(..., {cmd}, {larg}, ...)"); 0 @@ -1202,6 +1232,8 @@ num_enum! { enum SslCtrl { Mode = 33, SetMsgCallbackArg = 16, + SetTlsExtServerNameCallback = 53, + SetTlsExtServerNameArg = 54, SetTlsExtHostname = 55, SetChain = 88, SetMaxProtoVersion = 124, diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs index e680ff2..9321858 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -223,6 +223,7 @@ pub struct SslContext { default_cert_dir: Option, alpn_callback: callbacks::AlpnCallbackConfig, cert_callback: callbacks::CertCallbackConfig, + servername_callback: callbacks::ServerNameCallbackConfig, auth_keys: sign::CertifiedKeySet, } @@ -239,6 +240,7 @@ impl SslContext { default_cert_dir: None, alpn_callback: callbacks::AlpnCallbackConfig::default(), cert_callback: callbacks::CertCallbackConfig::default(), + servername_callback: callbacks::ServerNameCallbackConfig::default(), auth_keys: sign::CertifiedKeySet::default(), } } @@ -327,6 +329,14 @@ impl SslContext { fn get_privatekey(&self) -> *mut EVP_PKEY { self.auth_keys.borrow_current_key() } + + fn set_servername_callback(&mut self, cb: entry::SSL_CTX_servername_callback_func) { + self.servername_callback.cb = cb; + } + + fn set_servername_callback_context(&mut self, context: *mut c_void) { + self.servername_callback.context = context; + } } /// Parse the ALPN wire format (which is used in the openssl API) @@ -381,6 +391,7 @@ struct Ssl { alpn: Vec>, alpn_callback: callbacks::AlpnCallbackConfig, cert_callback: callbacks::CertCallbackConfig, + servername_callback: callbacks::ServerNameCallbackConfig, sni_server_name: Option>, server_name: Option, bio: Option, @@ -412,6 +423,7 @@ impl Ssl { alpn: inner.alpn.clone(), alpn_callback: inner.alpn_callback.clone(), cert_callback: inner.cert_callback.clone(), + servername_callback: inner.servername_callback.clone(), sni_server_name: None, server_name: None, bio: None, @@ -639,6 +651,8 @@ impl Ssl { .server_name() .and_then(|sni| CString::new(sni.as_bytes()).ok()); + self.servername_callback.invoke()?; + if let Some(alpn_iter) = accepted.client_hello().alpn() { let offer = encode_alpn(alpn_iter); diff --git a/rustls-libssl/tests/server.c b/rustls-libssl/tests/server.c index a91481a..8a65d74 100644 --- a/rustls-libssl/tests/server.c +++ b/rustls-libssl/tests/server.c @@ -47,6 +47,19 @@ static int cert_callback(SSL *ssl, void *arg) { return 1; } +static int sni_cookie = 12345; + +static int sni_callback(SSL *ssl, int *al, void *arg) { + printf("in sni_callback\n"); + assert(ssl != NULL); + assert(arg == &sni_cookie); + assert(*al == SSL_AD_UNRECOGNIZED_NAME); + printf(" SSL_get_servername: %s (%d)\n", + SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name), + SSL_get_servername_type(ssl)); + return SSL_TLSEXT_ERR_OK; +} + int main(int argc, char **argv) { if (argc != 5) { printf("%s |unauth\n\n", @@ -89,6 +102,11 @@ int main(int argc, char **argv) { SSL_CTX_set_cert_cb(ctx, cert_callback, &cert_cookie); dump_openssl_error_stack(); + SSL_CTX_set_tlsext_servername_callback(ctx, sni_callback); + dump_openssl_error_stack(); + SSL_CTX_set_tlsext_servername_arg(ctx, &sni_cookie); + dump_openssl_error_stack(); + X509 *server_cert = NULL; EVP_PKEY *server_key = NULL; TRACE(SSL_CTX_use_certificate_chain_file(ctx, certfile)); From d98c8c72eb01c8ece4a75930f87df0a053c0280a Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Thu, 4 Apr 2024 13:38:13 +0100 Subject: [PATCH 08/29] Implement `SSL_set_verify` --- rustls-libssl/MATRIX.md | 2 +- rustls-libssl/build.rs | 1 + rustls-libssl/src/entry.rs | 15 +++++++++++++++ rustls-libssl/src/lib.rs | 4 ++++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index e063dbb..128c642 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -473,7 +473,7 @@ | `SSL_set_tlsext_use_srtp` [^srtp] | | | | | `SSL_set_tmp_dh_callback` [^deprecatedin_3_0] [^dh] | | | | | `SSL_set_trust` | | | | -| `SSL_set_verify` | | :white_check_mark: | | +| `SSL_set_verify` | | :white_check_mark: | :white_check_mark: | | `SSL_set_verify_depth` | | :white_check_mark: | | | `SSL_set_verify_result` | | | | | `SSL_set_wfd` [^sock] | | | | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index 4d82da1..b2f6244 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -158,6 +158,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_set_session_id_context", "SSL_set_shutdown", "SSL_set_SSL_CTX", + "SSL_set_verify", "SSL_shutdown", "SSL_up_ref", "SSL_use_certificate", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index 132a076..4e3fbfa 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -1018,6 +1018,21 @@ entry! { } } +entry! { + pub fn _SSL_set_verify(ssl: *mut SSL, mode: c_int, callback: SSL_verify_cb) { + let ssl = try_clone_arc!(ssl); + + if callback.is_some() { + // supporting verify callbacks would mean we need to fully use + // the openssl certificate verifier, because X509_STORE and + // X509_STORE_CTX are both in libcrypto. + return Error::not_supported("verify callback").raise().into(); + } + + ssl.get_mut().set_verify(crate::VerifyMode::from(mode)); + } +} + impl Castable for SSL { type Ownership = OwnershipArc; type RustType = NotThreadSafe; diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs index 9321858..cbe8141 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -506,6 +506,10 @@ impl Ssl { } } + fn set_verify(&mut self, mode: VerifyMode) { + self.verify_mode = mode; + } + fn set_sni_hostname(&mut self, hostname: &str) -> bool { match ServerName::try_from(hostname).ok() { Some(server_name) => { From 2b0b32468e6d71d68acc217af0ec8d4b742fe819 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Thu, 4 Apr 2024 13:39:26 +0100 Subject: [PATCH 09/29] Round trip `SSL_{CTX_,}[sg]et_verify_depth` But don't actually implement the limit. Justification: verifiers limiting the chain length is best accomplished by _issuer_ certs instead specifying a path length constraint, and/or not issuing valid but long chains. Theoretically we could implement this inside `webpki`, or even do it ourselves by supplying a `verify_path` callback to it. --- rustls-libssl/MATRIX.md | 8 ++++---- rustls-libssl/build.rs | 4 ++++ rustls-libssl/src/entry.rs | 24 ++++++++++++++++++++++++ rustls-libssl/src/lib.rs | 20 ++++++++++++++++++++ rustls-libssl/tests/client.c | 3 +++ 5 files changed, 55 insertions(+), 4 deletions(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index 128c642..68267f6 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -106,7 +106,7 @@ | `SSL_CTX_get_ssl_method` | | | | | `SSL_CTX_get_timeout` | | :white_check_mark: | | | `SSL_CTX_get_verify_callback` | | :white_check_mark: | | -| `SSL_CTX_get_verify_depth` | | :white_check_mark: | | +| `SSL_CTX_get_verify_depth` | | :white_check_mark: | :white_check_mark: | | `SSL_CTX_get_verify_mode` | | :white_check_mark: | | | `SSL_CTX_has_client_custom_ext` | | | | | `SSL_CTX_load_verify_dir` | :white_check_mark: | | :white_check_mark: | @@ -198,7 +198,7 @@ | `SSL_CTX_set_tmp_dh_callback` [^deprecatedin_3_0] [^dh] | | | | | `SSL_CTX_set_trust` | | | | | `SSL_CTX_set_verify` | :white_check_mark: | :white_check_mark: | :white_check_mark: | -| `SSL_CTX_set_verify_depth` | | :white_check_mark: | | +| `SSL_CTX_set_verify_depth` | | :white_check_mark: | :white_check_mark: | | `SSL_CTX_up_ref` | | | :white_check_mark: | | `SSL_CTX_use_PrivateKey` | :white_check_mark: | :white_check_mark: | :white_check_mark: | | `SSL_CTX_use_PrivateKey_ASN1` | | | | @@ -378,7 +378,7 @@ | `SSL_get_ssl_method` | | | | | `SSL_get_state` | | | :white_check_mark: | | `SSL_get_verify_callback` | | | | -| `SSL_get_verify_depth` | | | | +| `SSL_get_verify_depth` | | | :white_check_mark: | | `SSL_get_verify_mode` | | | | | `SSL_get_verify_result` | :white_check_mark: | :white_check_mark: | :white_check_mark: | | `SSL_get_version` | :white_check_mark: | :white_check_mark: | :white_check_mark: | @@ -474,7 +474,7 @@ | `SSL_set_tmp_dh_callback` [^deprecatedin_3_0] [^dh] | | | | | `SSL_set_trust` | | | | | `SSL_set_verify` | | :white_check_mark: | :white_check_mark: | -| `SSL_set_verify_depth` | | :white_check_mark: | | +| `SSL_set_verify_depth` | | :white_check_mark: | :white_check_mark: | | `SSL_set_verify_result` | | | | | `SSL_set_wfd` [^sock] | | | | | `SSL_shutdown` | :white_check_mark: | :white_check_mark: | :white_check_mark: | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index b2f6244..fb0f528 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -71,6 +71,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_CTX_get_cert_store", "SSL_CTX_get_ex_data", "SSL_CTX_get_options", + "SSL_CTX_get_verify_depth", "SSL_CTX_load_verify_dir", "SSL_CTX_load_verify_file", "SSL_CTX_new", @@ -100,6 +101,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_CTX_set_srp_password", "SSL_CTX_set_srp_username", "SSL_CTX_set_verify", + "SSL_CTX_set_verify_depth", "SSL_CTX_up_ref", "SSL_CTX_use_certificate", "SSL_CTX_use_certificate_chain_file", @@ -127,6 +129,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_get_session", "SSL_get_shutdown", "SSL_get_state", + "SSL_get_verify_depth", "SSL_get_verify_result", "SSL_get_version", "SSL_get_wbio", @@ -159,6 +162,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_set_shutdown", "SSL_set_SSL_CTX", "SSL_set_verify", + "SSL_set_verify_depth", "SSL_shutdown", "SSL_up_ref", "SSL_use_certificate", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index 4e3fbfa..f62f342 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -211,6 +211,18 @@ entry! { } } +entry! { + pub fn _SSL_CTX_set_verify_depth(ctx: *mut SSL_CTX, depth: c_int) { + try_clone_arc!(ctx).get_mut().set_verify_depth(depth) + } +} + +entry! { + pub fn _SSL_CTX_get_verify_depth(ctx: *mut SSL_CTX) -> c_int { + try_clone_arc!(ctx).get().get_verify_depth() + } +} + pub type SSL_verify_cb = Option c_int>; @@ -1033,6 +1045,18 @@ entry! { } } +entry! { + pub fn _SSL_set_verify_depth(ssl: *mut SSL, depth: c_int) { + try_clone_arc!(ssl).get_mut().set_verify_depth(depth) + } +} + +entry! { + pub fn _SSL_get_verify_depth(ssl: *mut SSL) -> c_int { + try_clone_arc!(ssl).get().get_verify_depth() + } +} + impl Castable for SSL { type Ownership = OwnershipArc; type RustType = NotThreadSafe; diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs index cbe8141..4a36b1f 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -216,6 +216,7 @@ pub struct SslContext { method: &'static SslMethod, raw_options: u64, verify_mode: VerifyMode, + verify_depth: c_int, verify_roots: RootCertStore, verify_x509_store: x509::OwnedX509Store, alpn: Vec>, @@ -233,6 +234,7 @@ impl SslContext { method, raw_options: 0, verify_mode: VerifyMode::default(), + verify_depth: -1, verify_roots: RootCertStore::empty(), verify_x509_store: x509::OwnedX509Store::new(), alpn: vec![], @@ -282,6 +284,14 @@ impl SslContext { self.default_cert_file = cert_file; } + fn set_verify_depth(&mut self, depth: c_int) { + self.verify_depth = depth; + } + + fn get_verify_depth(&self) -> c_int { + self.verify_depth + } + fn add_trusted_certs( &mut self, certs: Vec>, @@ -386,6 +396,7 @@ struct Ssl { raw_options: u64, mode: ConnMode, verify_mode: VerifyMode, + verify_depth: c_int, verify_roots: RootCertStore, verify_server_name: Option>, alpn: Vec>, @@ -418,6 +429,7 @@ impl Ssl { raw_options: inner.raw_options, mode: inner.method.mode(), verify_mode: inner.verify_mode, + verify_depth: inner.verify_depth, verify_roots: Self::load_verify_certs(inner)?, verify_server_name: None, alpn: inner.alpn.clone(), @@ -510,6 +522,14 @@ impl Ssl { self.verify_mode = mode; } + fn set_verify_depth(&mut self, depth: c_int) { + self.verify_depth = depth; + } + + fn get_verify_depth(&self) -> c_int { + self.verify_depth + } + fn set_sni_hostname(&mut self, hostname: &str) -> bool { match ServerName::try_from(hostname).ok() { Some(server_name) => { diff --git a/rustls-libssl/tests/client.c b/rustls-libssl/tests/client.c index 6f37848..42a7863 100644 --- a/rustls-libssl/tests/client.c +++ b/rustls-libssl/tests/client.c @@ -57,6 +57,8 @@ int main(int argc, char **argv) { TRACE(SSL_CTX_load_verify_file(ctx, cacert)); dump_openssl_error_stack(); } + printf("SSL_CTX_get_verify_depth default %d\n", + SSL_CTX_get_verify_depth(ctx)); X509 *client_cert = NULL; EVP_PKEY *client_key = NULL; @@ -77,6 +79,7 @@ int main(int argc, char **argv) { printf("SSL_new: SSL_get_certificate %s SSL_CTX_get0_certificate\n", SSL_get_certificate(ssl) == client_cert ? "same as" : "differs to"); state(ssl); + printf("SSL_get_verify_depth default %d\n", SSL_get_verify_depth(ssl)); printf("SSL_get_servername: %s (%d)\n", SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name), SSL_get_servername_type(ssl)); From 9024b3020fe3c7d53c2b40e1c306031e5a781ff1 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Fri, 5 Apr 2024 13:52:24 +0100 Subject: [PATCH 10/29] Implement `SSL_CTX_get_verify_{callback,mode}` --- rustls-libssl/MATRIX.md | 4 ++-- rustls-libssl/build.rs | 2 ++ rustls-libssl/src/entry.rs | 12 ++++++++++++ rustls-libssl/src/error.rs | 6 ++++++ rustls-libssl/src/lib.rs | 15 +++++++++++++++ rustls-libssl/tests/client.c | 2 ++ 6 files changed, 39 insertions(+), 2 deletions(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index 68267f6..07b8ec0 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -105,9 +105,9 @@ | `SSL_CTX_get_security_level` | | | | | `SSL_CTX_get_ssl_method` | | | | | `SSL_CTX_get_timeout` | | :white_check_mark: | | -| `SSL_CTX_get_verify_callback` | | :white_check_mark: | | +| `SSL_CTX_get_verify_callback` | | :white_check_mark: | :white_check_mark: | | `SSL_CTX_get_verify_depth` | | :white_check_mark: | :white_check_mark: | -| `SSL_CTX_get_verify_mode` | | :white_check_mark: | | +| `SSL_CTX_get_verify_mode` | | :white_check_mark: | :white_check_mark: | | `SSL_CTX_has_client_custom_ext` | | | | | `SSL_CTX_load_verify_dir` | :white_check_mark: | | :white_check_mark: | | `SSL_CTX_load_verify_file` | :white_check_mark: | | :white_check_mark: | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index fb0f528..bd41377 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -71,7 +71,9 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_CTX_get_cert_store", "SSL_CTX_get_ex_data", "SSL_CTX_get_options", + "SSL_CTX_get_verify_callback", "SSL_CTX_get_verify_depth", + "SSL_CTX_get_verify_mode", "SSL_CTX_load_verify_dir", "SSL_CTX_load_verify_file", "SSL_CTX_new", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index f62f342..cc893ff 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -211,6 +211,18 @@ entry! { } } +entry! { + pub fn _SSL_CTX_get_verify_callback(ctx: *const SSL_CTX) -> SSL_verify_cb { + try_clone_arc!(ctx).get().get_verify_callback() + } +} + +entry! { + pub fn _SSL_CTX_get_verify_mode(ctx: *const SSL_CTX) -> c_int { + try_clone_arc!(ctx).get().get_verify_mode().into() + } +} + entry! { pub fn _SSL_CTX_set_verify_depth(ctx: *mut SSL_CTX, depth: c_int) { try_clone_arc!(ctx).get_mut().set_verify_depth(depth) diff --git a/rustls-libssl/src/error.rs b/rustls-libssl/src/error.rs index d6590d1..7e5e0b8 100644 --- a/rustls-libssl/src/error.rs +++ b/rustls-libssl/src/error.rs @@ -215,6 +215,12 @@ impl From for () { } } +impl From for crate::entry::SSL_verify_cb { + fn from(_: Error) -> crate::entry::SSL_verify_cb { + None + } +} + #[macro_export] macro_rules! ffi_panic_boundary { ( $($tt:tt)* ) => { diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs index 4a36b1f..4a31471 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -284,6 +284,15 @@ impl SslContext { self.default_cert_file = cert_file; } + fn get_verify_mode(&self) -> VerifyMode { + self.verify_mode + } + + fn get_verify_callback(&self) -> entry::SSL_verify_cb { + // TODO: `SSL_CTX_set_verify` currently rejects non-NULL callback + None + } + fn set_verify_depth(&mut self, depth: c_int) { self.verify_depth = depth; } @@ -1173,6 +1182,12 @@ impl From for VerifyMode { } } +impl From for i32 { + fn from(v: VerifyMode) -> Self { + v.0 + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/rustls-libssl/tests/client.c b/rustls-libssl/tests/client.c index 42a7863..e353358 100644 --- a/rustls-libssl/tests/client.c +++ b/rustls-libssl/tests/client.c @@ -54,6 +54,8 @@ int main(int argc, char **argv) { } else { SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); dump_openssl_error_stack(); + assert(SSL_CTX_get_verify_mode(ctx) == SSL_VERIFY_PEER); + assert(SSL_CTX_get_verify_callback(ctx) == NULL); TRACE(SSL_CTX_load_verify_file(ctx, cacert)); dump_openssl_error_stack(); } From 715eeae813afdae02f16e7511235692ed4ef891e Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Fri, 5 Apr 2024 14:21:26 +0100 Subject: [PATCH 11/29] Implement `SSL_CTX_{get,set}_max_early_data` --- rustls-libssl/MATRIX.md | 4 ++-- rustls-libssl/build.rs | 2 ++ rustls-libssl/src/entry.rs | 15 +++++++++++++++ rustls-libssl/src/lib.rs | 13 +++++++++++++ rustls-libssl/tests/server.c | 3 +++ 5 files changed, 35 insertions(+), 2 deletions(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index 07b8ec0..77df3fb 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -95,7 +95,7 @@ | `SSL_CTX_get_ex_data` | | :white_check_mark: | :exclamation: [^stub] | | `SSL_CTX_get_info_callback` | | | | | `SSL_CTX_get_keylog_callback` | | | | -| `SSL_CTX_get_max_early_data` | | :white_check_mark: | | +| `SSL_CTX_get_max_early_data` | | :white_check_mark: | :white_check_mark: | | `SSL_CTX_get_num_tickets` | | | | | `SSL_CTX_get_options` | | :white_check_mark: | :white_check_mark: | | `SSL_CTX_get_quiet_shutdown` | | | | @@ -160,7 +160,7 @@ | `SSL_CTX_set_generate_session_id` | | | | | `SSL_CTX_set_info_callback` | | :white_check_mark: | | | `SSL_CTX_set_keylog_callback` | :white_check_mark: | | :exclamation: [^stub] | -| `SSL_CTX_set_max_early_data` | | :white_check_mark: | | +| `SSL_CTX_set_max_early_data` | | :white_check_mark: | :white_check_mark: | | `SSL_CTX_set_msg_callback` | :white_check_mark: | | :exclamation: [^stub] | | `SSL_CTX_set_next_proto_select_cb` [^nextprotoneg] | :white_check_mark: | | :exclamation: [^stub] | | `SSL_CTX_set_next_protos_advertised_cb` [^nextprotoneg] | | :white_check_mark: | :exclamation: [^stub] | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index bd41377..4281137 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -70,6 +70,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_CTX_get0_privatekey", "SSL_CTX_get_cert_store", "SSL_CTX_get_ex_data", + "SSL_CTX_get_max_early_data", "SSL_CTX_get_options", "SSL_CTX_get_verify_callback", "SSL_CTX_get_verify_depth", @@ -94,6 +95,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_CTX_set_default_verify_store", "SSL_CTX_set_ex_data", "SSL_CTX_set_keylog_callback", + "SSL_CTX_set_max_early_data", "SSL_CTX_set_msg_callback", "SSL_CTX_set_next_proto_select_cb", "SSL_CTX_set_next_protos_advertised_cb", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index cc893ff..252112b 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -532,6 +532,21 @@ entry! { } } +entry! { + pub fn _SSL_CTX_get_max_early_data(ctx: *const SSL_CTX) -> u32 { + try_clone_arc!(ctx).get().get_max_early_data() + } +} + +entry! { + pub fn _SSL_CTX_set_max_early_data(ctx: *mut SSL_CTX, max_early_data: u32) -> c_int { + try_clone_arc!(ctx) + .get_mut() + .set_max_early_data(max_early_data); + C_INT_SUCCESS + } +} + impl Castable for SSL_CTX { type Ownership = OwnershipArc; type RustType = NotThreadSafe; diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs index 4a31471..8493b77 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -226,6 +226,7 @@ pub struct SslContext { cert_callback: callbacks::CertCallbackConfig, servername_callback: callbacks::ServerNameCallbackConfig, auth_keys: sign::CertifiedKeySet, + max_early_data: u32, } impl SslContext { @@ -244,6 +245,7 @@ impl SslContext { cert_callback: callbacks::CertCallbackConfig::default(), servername_callback: callbacks::ServerNameCallbackConfig::default(), auth_keys: sign::CertifiedKeySet::default(), + max_early_data: 0, } } @@ -261,6 +263,14 @@ impl SslContext { self.raw_options } + fn set_max_early_data(&mut self, max: u32) { + self.max_early_data = max; + } + + fn get_max_early_data(&self) -> u32 { + self.max_early_data + } + fn set_verify(&mut self, mode: VerifyMode) { self.verify_mode = mode; } @@ -420,6 +430,7 @@ struct Ssl { peer_cert_chain: Option, shutdown_flags: ShutdownFlags, auth_keys: sign::CertifiedKeySet, + max_early_data: u32, } #[allow(clippy::large_enum_variant)] @@ -453,6 +464,7 @@ impl Ssl { peer_cert_chain: None, shutdown_flags: ShutdownFlags::default(), auth_keys: inner.auth_keys.clone(), + max_early_data: inner.max_early_data, }) } @@ -734,6 +746,7 @@ impl Ssl { .with_cert_resolver(resolver); config.alpn_protocols = mem::take(&mut self.alpn); + config.max_early_data_size = self.max_early_data; let accepted = match mem::replace(&mut self.conn, ConnState::Nothing) { ConnState::Accepted(accepted) => accepted, diff --git a/rustls-libssl/tests/server.c b/rustls-libssl/tests/server.c index 8a65d74..897f6f4 100644 --- a/rustls-libssl/tests/server.c +++ b/rustls-libssl/tests/server.c @@ -114,6 +114,9 @@ int main(int argc, char **argv) { server_key = SSL_CTX_get0_privatekey(ctx); server_cert = SSL_CTX_get0_certificate(ctx); + printf("SSL_CTX_get_max_early_data default %lu\n", + (unsigned long)SSL_CTX_get_max_early_data(ctx)); + SSL *ssl = SSL_new(ctx); dump_openssl_error_stack(); printf("SSL_new: SSL_get_privatekey %s SSL_CTX_get0_privatekey\n", From d00431b9fac432333885ce47ff50e24477f3efe8 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Fri, 5 Apr 2024 14:31:17 +0100 Subject: [PATCH 12/29] Stub out `SSL_{read,write}_early_data` We can definitely do these, but not for an MVP. --- rustls-libssl/MATRIX.md | 4 ++-- rustls-libssl/build.rs | 2 ++ rustls-libssl/src/entry.rs | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index 77df3fb..bfb0681 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -401,7 +401,7 @@ | `SSL_peek_ex` | | | | | `SSL_pending` | :white_check_mark: | | :white_check_mark: | | `SSL_read` | :white_check_mark: | :white_check_mark: | :white_check_mark: | -| `SSL_read_early_data` | | :white_check_mark: | | +| `SSL_read_early_data` | | :white_check_mark: | :exclamation: [^stub] | | `SSL_read_ex` | | | | | `SSL_renegotiate` | | | | | `SSL_renegotiate_abbreviated` | | | | @@ -502,7 +502,7 @@ | `SSL_waiting_for_async` | | | | | `SSL_want` | | | :white_check_mark: | | `SSL_write` | :white_check_mark: | :white_check_mark: | :white_check_mark: | -| `SSL_write_early_data` | | :white_check_mark: | | +| `SSL_write_early_data` | | :white_check_mark: | :exclamation: [^stub] | | `SSL_write_ex` | | | | | `SSLv3_client_method` [^deprecatedin_1_1_0] [^ssl3_method] | | | | | `SSLv3_method` [^deprecatedin_1_1_0] [^ssl3_method] | | | | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index 4281137..d8e8ced 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -145,6 +145,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_new", "SSL_pending", "SSL_read", + "SSL_read_early_data", "SSL_select_next_proto", "SSL_SESSION_free", "SSL_SESSION_get_id", @@ -173,6 +174,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_use_PrivateKey", "SSL_want", "SSL_write", + "SSL_write_early_data", "TLS_client_method", "TLS_method", "TLS_server_method", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index 252112b..68154bf 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -1471,6 +1471,24 @@ entry_stub! { pub fn _SSL_SESSION_free(_sess: *mut SSL_SESSION); } +entry_stub! { + pub fn _SSL_write_early_data( + _ssl: *mut SSL, + _buf: *const c_void, + _num: usize, + _written: *mut usize, + ) -> c_int; +} + +entry_stub! { + pub fn _SSL_read_early_data( + _ssl: *mut SSL, + _buf: *mut c_void, + _num: usize, + _readbytes: *mut usize, + ) -> c_int; +} + // no individual message logging entry_stub! { From 1ae04f2650749b67c3bfa1e744886e8af5887818 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Fri, 5 Apr 2024 14:44:27 +0100 Subject: [PATCH 13/29] Stub out `SSL_CTX_{set,get}_timeout` rustls currently could support these with stateless resumption. --- rustls-libssl/MATRIX.md | 4 ++-- rustls-libssl/build.rs | 2 ++ rustls-libssl/src/entry.rs | 8 ++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index bfb0681..bcf2975 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -104,7 +104,7 @@ | `SSL_CTX_get_security_callback` | | | | | `SSL_CTX_get_security_level` | | | | | `SSL_CTX_get_ssl_method` | | | | -| `SSL_CTX_get_timeout` | | :white_check_mark: | | +| `SSL_CTX_get_timeout` | | :white_check_mark: | :exclamation: [^stub] | | `SSL_CTX_get_verify_callback` | | :white_check_mark: | :white_check_mark: | | `SSL_CTX_get_verify_depth` | | :white_check_mark: | :white_check_mark: | | `SSL_CTX_get_verify_mode` | | :white_check_mark: | :white_check_mark: | @@ -191,7 +191,7 @@ | `SSL_CTX_set_ssl_version` [^deprecatedin_3_0] | | | | | `SSL_CTX_set_stateless_cookie_generate_cb` | | | | | `SSL_CTX_set_stateless_cookie_verify_cb` | | | | -| `SSL_CTX_set_timeout` | | :white_check_mark: | | +| `SSL_CTX_set_timeout` | | :white_check_mark: | :exclamation: [^stub] | | `SSL_CTX_set_tlsext_max_fragment_length` | | | | | `SSL_CTX_set_tlsext_ticket_key_evp_cb` | | | | | `SSL_CTX_set_tlsext_use_srtp` [^srtp] | | | | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index d8e8ced..574a7c9 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -72,6 +72,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_CTX_get_ex_data", "SSL_CTX_get_max_early_data", "SSL_CTX_get_options", + "SSL_CTX_get_timeout", "SSL_CTX_get_verify_callback", "SSL_CTX_get_verify_depth", "SSL_CTX_get_verify_mode", @@ -104,6 +105,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_CTX_set_session_id_context", "SSL_CTX_set_srp_password", "SSL_CTX_set_srp_username", + "SSL_CTX_set_timeout", "SSL_CTX_set_verify", "SSL_CTX_set_verify_depth", "SSL_CTX_up_ref", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index 68154bf..dd0d551 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -1489,6 +1489,14 @@ entry_stub! { ) -> c_int; } +entry_stub! { + pub fn _SSL_CTX_get_timeout(_ctx: *const SSL_CTX) -> c_long; +} + +entry_stub! { + pub fn _SSL_CTX_set_timeout(_ctx: *mut SSL_CTX, _t: c_long) -> c_long; +} + // no individual message logging entry_stub! { From 1071fc92805fa7d629989b05a669fba7686b7ecb Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Fri, 5 Apr 2024 14:54:25 +0100 Subject: [PATCH 14/29] Stub out `SSL_CTX_{get,set}_client_CA_list` Definitely could implement this with some conversions: it is the same concept as `ClientCertVerifier::root_hint_subjects()` --- rustls-libssl/MATRIX.md | 4 ++-- rustls-libssl/build.rs | 2 ++ rustls-libssl/src/entry.rs | 12 ++++++++++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index bcf2975..dff1105 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -88,7 +88,7 @@ | `SSL_CTX_get0_security_ex_data` | | | | | `SSL_CTX_get_cert_store` | :white_check_mark: | :white_check_mark: | :white_check_mark: | | `SSL_CTX_get_ciphers` | | | | -| `SSL_CTX_get_client_CA_list` | | :white_check_mark: | | +| `SSL_CTX_get_client_CA_list` | | :white_check_mark: | :exclamation: [^stub] | | `SSL_CTX_get_client_cert_cb` | | | | | `SSL_CTX_get_default_passwd_cb` | | | | | `SSL_CTX_get_default_passwd_cb_userdata` | | | | @@ -140,7 +140,7 @@ | `SSL_CTX_set_cert_verify_callback` | | | | | `SSL_CTX_set_cipher_list` | :white_check_mark: | :white_check_mark: | :exclamation: [^stub] | | `SSL_CTX_set_ciphersuites` | :white_check_mark: | | :exclamation: [^stub] | -| `SSL_CTX_set_client_CA_list` | | :white_check_mark: | | +| `SSL_CTX_set_client_CA_list` | | :white_check_mark: | :exclamation: [^stub] | | `SSL_CTX_set_client_cert_cb` | | | | | `SSL_CTX_set_client_cert_engine` [^engine] | | | | | `SSL_CTX_set_client_hello_cb` | | | | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index 574a7c9..307d5f0 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -69,6 +69,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_CTX_get0_certificate", "SSL_CTX_get0_privatekey", "SSL_CTX_get_cert_store", + "SSL_CTX_get_client_CA_list", "SSL_CTX_get_ex_data", "SSL_CTX_get_max_early_data", "SSL_CTX_get_options", @@ -88,6 +89,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_CTX_set_cert_cb", "SSL_CTX_set_cipher_list", "SSL_CTX_set_ciphersuites", + "SSL_CTX_set_client_CA_list", "SSL_CTX_set_default_passwd_cb", "SSL_CTX_set_default_passwd_cb_userdata", "SSL_CTX_set_default_verify_dir", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index dd0d551..c9d562b 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -9,8 +9,8 @@ use std::os::raw::{c_char, c_int, c_long, c_uchar, c_uint, c_void}; use std::{fs, path::PathBuf}; use openssl_sys::{ - stack_st_X509, OPENSSL_malloc, TLSEXT_NAMETYPE_host_name, EVP_PKEY, OPENSSL_NPN_NEGOTIATED, - OPENSSL_NPN_NO_OVERLAP, X509, X509_STORE, X509_STORE_CTX, + stack_st_X509, stack_st_X509_NAME, OPENSSL_malloc, TLSEXT_NAMETYPE_host_name, EVP_PKEY, + OPENSSL_NPN_NEGOTIATED, OPENSSL_NPN_NO_OVERLAP, X509, X509_STORE, X509_STORE_CTX, }; use rustls::pki_types::{CertificateDer, PrivatePkcs8KeyDer}; @@ -1497,6 +1497,14 @@ entry_stub! { pub fn _SSL_CTX_set_timeout(_ctx: *mut SSL_CTX, _t: c_long) -> c_long; } +entry_stub! { + pub fn _SSL_CTX_get_client_CA_list(_ctx: *const SSL_CTX) -> *mut stack_st_X509_NAME; +} + +entry_stub! { + pub fn _SSL_CTX_set_client_CA_list(_ctx: *mut SSL_CTX, _name_list: *mut stack_st_X509_NAME); +} + // no individual message logging entry_stub! { From 76579a512e6ee68157c47c531d90f25b8708689e Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Fri, 5 Apr 2024 15:02:23 +0100 Subject: [PATCH 15/29] Stub out `SSL_CTX_set_info_callback` This one seems unlikely. --- rustls-libssl/MATRIX.md | 2 +- rustls-libssl/build.rs | 1 + rustls-libssl/src/entry.rs | 9 +++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index dff1105..8591ac2 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -158,7 +158,7 @@ | `SSL_CTX_set_default_verify_store` | | | :exclamation: [^stub] | | `SSL_CTX_set_ex_data` | | :white_check_mark: | :exclamation: [^stub] | | `SSL_CTX_set_generate_session_id` | | | | -| `SSL_CTX_set_info_callback` | | :white_check_mark: | | +| `SSL_CTX_set_info_callback` | | :white_check_mark: | :exclamation: [^stub] | | `SSL_CTX_set_keylog_callback` | :white_check_mark: | | :exclamation: [^stub] | | `SSL_CTX_set_max_early_data` | | :white_check_mark: | :white_check_mark: | | `SSL_CTX_set_msg_callback` | :white_check_mark: | | :exclamation: [^stub] | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index 307d5f0..f3b4e48 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -97,6 +97,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_CTX_set_default_verify_paths", "SSL_CTX_set_default_verify_store", "SSL_CTX_set_ex_data", + "SSL_CTX_set_info_callback", "SSL_CTX_set_keylog_callback", "SSL_CTX_set_max_early_data", "SSL_CTX_set_msg_callback", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index c9d562b..eafacca 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -1523,6 +1523,15 @@ pub type SSL_CTX_msg_cb_func = Option< ), >; +// no state machine observation + +entry_stub! { + pub fn _SSL_CTX_set_info_callback( + _ctx: *mut SSL_CTX, + _cb: Option, + ); +} + // no NPN (obsolete precursor to ALPN) entry_stub! { From 9f50f3aae405e8d4ace0baff2398ce6568c0b3b0 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Fri, 5 Apr 2024 15:23:00 +0100 Subject: [PATCH 16/29] Implement `SSL_CTX_load_verify_locations` --- rustls-libssl/MATRIX.md | 2 +- rustls-libssl/build.rs | 1 + rustls-libssl/src/entry.rs | 22 ++++++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index 8591ac2..a1ed288 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -111,7 +111,7 @@ | `SSL_CTX_has_client_custom_ext` | | | | | `SSL_CTX_load_verify_dir` | :white_check_mark: | | :white_check_mark: | | `SSL_CTX_load_verify_file` | :white_check_mark: | | :white_check_mark: | -| `SSL_CTX_load_verify_locations` | | :white_check_mark: | | +| `SSL_CTX_load_verify_locations` | | :white_check_mark: | :white_check_mark: | | `SSL_CTX_load_verify_store` | | | | | `SSL_CTX_new` | :white_check_mark: | :white_check_mark: | :white_check_mark: | | `SSL_CTX_new_ex` | | | | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index f3b4e48..8962d62 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -79,6 +79,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_CTX_get_verify_mode", "SSL_CTX_load_verify_dir", "SSL_CTX_load_verify_file", + "SSL_CTX_load_verify_locations", "SSL_CTX_new", "SSL_CTX_remove_session", "SSL_CTX_sess_set_get_cb", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index eafacca..a667030 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -305,6 +305,28 @@ entry! { } } +entry! { + pub fn _SSL_CTX_load_verify_locations( + ctx: *mut SSL_CTX, + ca_file: *const c_char, + ca_path: *const c_char, + ) -> c_int { + if ca_path.is_null() && ca_path.is_null() { + return 0; + } + + if !ca_file.is_null() && _SSL_CTX_load_verify_file(ctx, ca_file) == 0 { + return 0; + } + + if !ca_path.is_null() && _SSL_CTX_load_verify_dir(ctx, ca_path) == 0 { + return 0; + } + + C_INT_SUCCESS + } +} + entry! { pub fn _SSL_CTX_set_alpn_protos( ctx: *mut SSL_CTX, From 571a9f57d1eb7bbe957dca423f9eaa18676a30d0 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Fri, 5 Apr 2024 15:50:13 +0100 Subject: [PATCH 17/29] Stub out `SSL_load_client_CA_file` We could implement this. --- rustls-libssl/MATRIX.md | 2 +- rustls-libssl/build.rs | 1 + rustls-libssl/src/entry.rs | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index a1ed288..8db82d4 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -393,7 +393,7 @@ | `SSL_is_init_finished` | | :white_check_mark: | :white_check_mark: | | `SSL_is_server` | | | :white_check_mark: | | `SSL_key_update` | | | | -| `SSL_load_client_CA_file` | | :white_check_mark: | | +| `SSL_load_client_CA_file` | | :white_check_mark: | :exclamation: [^stub] | | `SSL_load_client_CA_file_ex` | | | | | `SSL_new` | :white_check_mark: | :white_check_mark: | :white_check_mark: | | `SSL_new_session_ticket` | | | | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index 8962d62..7023065 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -148,6 +148,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_in_init", "SSL_is_init_finished", "SSL_is_server", + "SSL_load_client_CA_file", "SSL_new", "SSL_pending", "SSL_read", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index a667030..63338c6 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -1527,6 +1527,10 @@ entry_stub! { pub fn _SSL_CTX_set_client_CA_list(_ctx: *mut SSL_CTX, _name_list: *mut stack_st_X509_NAME); } +entry_stub! { + pub fn _SSL_load_client_CA_file(_file: *const c_char) -> *mut stack_st_X509_NAME; +} + // no individual message logging entry_stub! { From 22fa9ea21a6d43d79e466083407aa59ecd3226f4 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Wed, 17 Apr 2024 13:19:57 +0100 Subject: [PATCH 18/29] Implement `SSL_set_quiet_shutdown` --- rustls-libssl/MATRIX.md | 2 +- rustls-libssl/build.rs | 1 + rustls-libssl/src/entry.rs | 6 ++++++ rustls-libssl/src/lib.rs | 33 +++++++++++++++++++++++++++++++-- 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index 8db82d4..492be52 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -452,7 +452,7 @@ | `SSL_set_psk_server_callback` [^psk] | | | | | `SSL_set_psk_use_session_callback` | | | | | `SSL_set_purpose` | | | | -| `SSL_set_quiet_shutdown` | | :white_check_mark: | | +| `SSL_set_quiet_shutdown` | | :white_check_mark: | :white_check_mark: | | `SSL_set_read_ahead` | | | | | `SSL_set_record_padding_callback` | | | | | `SSL_set_record_padding_callback_arg` | | | | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index 7023065..eb19cc3 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -169,6 +169,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_set_fd", "SSL_set_options", "SSL_set_post_handshake_auth", + "SSL_set_quiet_shutdown", "SSL_set_session", "SSL_set_session_id_context", "SSL_set_shutdown", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index 63338c6..b875088 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -879,6 +879,12 @@ entry! { } } +entry! { + pub fn _SSL_set_quiet_shutdown(ssl: *mut SSL, mode: c_int) { + try_clone_arc!(ssl).get_mut().set_quiet_shutdown(mode != 0) + } +} + entry! { pub fn _SSL_pending(ssl: *const SSL) -> c_int { try_clone_arc!(ssl).get_mut().get_pending_plaintext() as c_int diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs index 8493b77..f208412 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -877,6 +877,12 @@ impl Ssl { } fn try_shutdown(&mut self) -> Result { + if self.shutdown_flags.quiet() { + self.shutdown_flags.set_sent(); + self.shutdown_flags.set_received(); + return Ok(ShutdownResult::Received); + } + if !self.shutdown_flags.is_sent() { if let Some(conn) = self.conn_mut() { conn.send_close_notify(); @@ -894,13 +900,17 @@ impl Ssl { } fn get_shutdown(&self) -> i32 { - self.shutdown_flags.0 + self.shutdown_flags.get() } fn set_shutdown(&mut self, flags: i32) { self.shutdown_flags.set(flags); } + fn set_quiet_shutdown(&mut self, enabled: bool) { + self.shutdown_flags.set_quiet(enabled); + } + fn get_pending_plaintext(&mut self) -> usize { self.conn_mut() .as_mut() @@ -1140,6 +1150,9 @@ struct ShutdownFlags(i32); impl ShutdownFlags { const SENT: i32 = 1; const RECEIVED: i32 = 2; + const PUBLIC: i32 = Self::SENT | Self::RECEIVED; + + const PRIV_QUIET: i32 = 4; fn is_sent(&self) -> bool { self.0 & ShutdownFlags::SENT == ShutdownFlags::SENT @@ -1158,7 +1171,23 @@ impl ShutdownFlags { } fn set(&mut self, flags: i32) { - self.0 |= flags & (ShutdownFlags::SENT | ShutdownFlags::RECEIVED); + self.0 |= flags & ShutdownFlags::PUBLIC; + } + + fn get(&self) -> i32 { + self.0 & ShutdownFlags::PUBLIC + } + + fn set_quiet(&mut self, enabled: bool) { + if enabled { + self.0 |= ShutdownFlags::PRIV_QUIET; + } else { + self.0 &= !ShutdownFlags::PRIV_QUIET; + } + } + + fn quiet(&self) -> bool { + self.0 & ShutdownFlags::PRIV_QUIET == ShutdownFlags::PRIV_QUIET } } From 0bd42fb4ff57a2bf25153d6f53fc1cbe3fd2e5b1 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Mon, 8 Apr 2024 11:49:18 +0100 Subject: [PATCH 19/29] Implement ex_data functions for SSL and SSL_CTX --- rustls-libssl/MATRIX.md | 8 +-- rustls-libssl/src/entry.rs | 80 ++++++++++++++++++------- rustls-libssl/src/ex_data.rs | 110 +++++++++++++++++++++++++++++++++++ rustls-libssl/src/lib.rs | 29 +++++++++ rustls-libssl/src/miri.rs | 24 +++++++- rustls-libssl/tests/server.c | 26 +++++++++ 6 files changed, 251 insertions(+), 26 deletions(-) create mode 100644 rustls-libssl/src/ex_data.rs diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index 492be52..b8a50db 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -92,7 +92,7 @@ | `SSL_CTX_get_client_cert_cb` | | | | | `SSL_CTX_get_default_passwd_cb` | | | | | `SSL_CTX_get_default_passwd_cb_userdata` | | | | -| `SSL_CTX_get_ex_data` | | :white_check_mark: | :exclamation: [^stub] | +| `SSL_CTX_get_ex_data` | | :white_check_mark: | :white_check_mark: | | `SSL_CTX_get_info_callback` | | | | | `SSL_CTX_get_keylog_callback` | | | | | `SSL_CTX_get_max_early_data` | | :white_check_mark: | :white_check_mark: | @@ -156,7 +156,7 @@ | `SSL_CTX_set_default_verify_file` | | | :white_check_mark: | | `SSL_CTX_set_default_verify_paths` | | | :white_check_mark: | | `SSL_CTX_set_default_verify_store` | | | :exclamation: [^stub] | -| `SSL_CTX_set_ex_data` | | :white_check_mark: | :exclamation: [^stub] | +| `SSL_CTX_set_ex_data` | | :white_check_mark: | :white_check_mark: | | `SSL_CTX_set_generate_session_id` | | | | | `SSL_CTX_set_info_callback` | | :white_check_mark: | :exclamation: [^stub] | | `SSL_CTX_set_keylog_callback` | :white_check_mark: | | :exclamation: [^stub] | @@ -336,7 +336,7 @@ | `SSL_get_default_timeout` | | | | | `SSL_get_early_data_status` | | | | | `SSL_get_error` | :white_check_mark: | :white_check_mark: | :white_check_mark: | -| `SSL_get_ex_data` | :white_check_mark: | :white_check_mark: | :exclamation: [^stub] | +| `SSL_get_ex_data` | :white_check_mark: | :white_check_mark: | :white_check_mark: | | `SSL_get_ex_data_X509_STORE_CTX_idx` | | :white_check_mark: | | | `SSL_get_fd` | | | | | `SSL_get_finished` | | | | @@ -436,7 +436,7 @@ | `SSL_set_default_passwd_cb` | | | | | `SSL_set_default_passwd_cb_userdata` | | | | | `SSL_set_default_read_buffer_len` | | | | -| `SSL_set_ex_data` | :white_check_mark: | :white_check_mark: | :exclamation: [^stub] | +| `SSL_set_ex_data` | :white_check_mark: | :white_check_mark: | :white_check_mark: | | `SSL_set_fd` [^sock] | :white_check_mark: | :white_check_mark: | :white_check_mark: | | `SSL_set_generate_session_id` | | | | | `SSL_set_hostflags` | | | | diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index b875088..8148ac9 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -18,9 +18,11 @@ use crate::bio::{Bio, BIO, BIO_METHOD}; use crate::callbacks::SslCallbackContext; use crate::error::{ffi_panic_boundary, Error, MysteriouslyOppositeReturnValue}; use crate::evp_pkey::EvpPkey; +use crate::ex_data::ExData; use crate::ffi::{ - free_arc, str_from_cstring, to_arc_mut_ptr, try_clone_arc, try_from, try_mut_slice_int, - try_ref_from_ptr, try_slice, try_slice_int, try_str, Castable, OwnershipArc, OwnershipRef, + clone_arc, free_arc, str_from_cstring, to_arc_mut_ptr, try_clone_arc, try_from, + try_mut_slice_int, try_ref_from_ptr, try_slice, try_slice_int, try_str, Castable, OwnershipArc, + OwnershipRef, }; use crate::not_thread_safe::NotThreadSafe; use crate::x509::{load_certs, OwnedX509, OwnedX509Stack}; @@ -106,12 +108,23 @@ impl Castable for SSL_METHOD { type RustType = SSL_METHOD; } -type SSL_CTX = crate::SslContext; +pub type SSL_CTX = crate::SslContext; entry! { pub fn _SSL_CTX_new(meth: *const SSL_METHOD) -> *mut SSL_CTX { let method = try_ref_from_ptr!(meth); - to_arc_mut_ptr(NotThreadSafe::new(crate::SslContext::new(method))) + let out = to_arc_mut_ptr(NotThreadSafe::new(crate::SslContext::new(method))); + let ex_data = match ExData::new_ssl_ctx(out) { + None => { + _SSL_CTX_free(out); + return ptr::null_mut(); + } + Some(ex_data) => ex_data, + }; + + // safety: we just made this object, the pointer must be valid + clone_arc(out).unwrap().get_mut().install_ex_data(ex_data); + out } } @@ -129,6 +142,21 @@ entry! { } } +entry! { + pub fn _SSL_CTX_set_ex_data(ctx: *mut SSL_CTX, idx: c_int, data: *mut c_void) -> c_int { + match try_clone_arc!(ctx).get_mut().set_ex_data(idx, data) { + Err(e) => e.raise().into(), + Ok(()) => C_INT_SUCCESS, + } + } +} + +entry! { + pub fn _SSL_CTX_get_ex_data(ctx: *const SSL_CTX, idx: c_int) -> *mut c_void { + try_clone_arc!(ctx).get().get_ex_data(idx) + } +} + entry! { pub fn _SSL_CTX_get_options(ctx: *const SSL_CTX) -> u64 { try_clone_arc!(ctx).get().get_options() @@ -585,7 +613,18 @@ entry! { None => return ptr::null_mut(), }; - to_arc_mut_ptr(NotThreadSafe::new(ssl)) + let out = to_arc_mut_ptr(NotThreadSafe::new(ssl)); + let ex_data = match ExData::new_ssl(out) { + None => { + _SSL_free(out); + return ptr::null_mut(); + } + Some(ex_data) => ex_data, + }; + + // safety: we just made this object, the pointer must be valid. + clone_arc(out).unwrap().get_mut().install_ex_data(ex_data); + out } } @@ -603,6 +642,21 @@ entry! { } } +entry! { + pub fn _SSL_set_ex_data(ssl: *mut SSL, idx: c_int, data: *mut c_void) -> c_int { + match try_clone_arc!(ssl).get_mut().set_ex_data(idx, data) { + Err(e) => e.raise().into(), + Ok(()) => C_INT_SUCCESS, + } + } +} + +entry! { + pub fn _SSL_get_ex_data(ssl: *const SSL, idx: c_int) -> *mut c_void { + try_clone_arc!(ssl).get().get_ex_data(idx) + } +} + entry! { pub fn _SSL_ctrl(ssl: *mut SSL, cmd: c_int, larg: c_long, parg: *mut c_void) -> c_long { let ssl = try_clone_arc!(ssl); @@ -1358,22 +1412,6 @@ macro_rules! entry_stub { // things we support and should be able to implement to // some extent: -entry_stub! { - pub fn _SSL_CTX_set_ex_data(_ssl: *mut SSL_CTX, _idx: c_int, _data: *mut c_void) -> c_int; -} - -entry_stub! { - pub fn _SSL_CTX_get_ex_data(_ssl: *const SSL_CTX, _idx: c_int) -> *mut c_void; -} - -entry_stub! { - pub fn _SSL_set_ex_data(_ssl: *mut SSL, _idx: c_int, _data: *mut c_void) -> c_int; -} - -entry_stub! { - pub fn _SSL_get_ex_data(_ssl: *const SSL, _idx: c_int) -> *mut c_void; -} - entry_stub! { pub fn _SSL_set_session(_ssl: *mut SSL, _session: *mut SSL_SESSION) -> c_int; } diff --git a/rustls-libssl/src/ex_data.rs b/rustls-libssl/src/ex_data.rs new file mode 100644 index 0000000..a7cfea9 --- /dev/null +++ b/rustls-libssl/src/ex_data.rs @@ -0,0 +1,110 @@ +use core::ffi::{c_int, c_void}; +use core::ptr; + +use crate::entry::{SSL, SSL_CTX}; +use crate::error::Error; + +/// Safe(ish), owning wrapper around an OpenSSL `CRYPTO_EX_DATA`. +/// +/// `ty` and `owner` allow us to drop this object with no extra context. +/// +/// Because this refers to the object that contains it, a two-step +/// construction is needed. +pub struct ExData { + ex_data: CRYPTO_EX_DATA, + ty: c_int, + owner: *mut c_void, +} + +impl ExData { + /// Makes a new CRYPTO_EX_DATA for an SSL object. + pub fn new_ssl(ssl: *mut SSL) -> Option { + let mut ex_data = CRYPTO_EX_DATA::default(); + let owner = ssl as *mut c_void; + let ty = CRYPTO_EX_INDEX_SSL; + let rc = unsafe { CRYPTO_new_ex_data(ty, owner, &mut ex_data) }; + if rc == 1 { + Some(Self { ex_data, ty, owner }) + } else { + None + } + } + + /// Makes a new CRYPTO_EX_DATA for an SSL_CTX object. + pub fn new_ssl_ctx(ctx: *mut SSL_CTX) -> Option { + let mut ex_data = CRYPTO_EX_DATA::default(); + let owner = ctx as *mut c_void; + let ty = CRYPTO_EX_INDEX_SSL_CTX; + let rc = unsafe { CRYPTO_new_ex_data(ty, owner, &mut ex_data) }; + if rc == 1 { + Some(Self { ex_data, ty, owner }) + } else { + None + } + } + + pub fn set(&mut self, idx: c_int, data: *mut c_void) -> Result<(), Error> { + let rc = unsafe { CRYPTO_set_ex_data(&mut self.ex_data, idx, data) }; + if rc == 1 { + Ok(()) + } else { + Err(Error::bad_data("CRYPTO_set_ex_data")) + } + } + + pub fn get(&self, idx: c_int) -> *mut c_void { + unsafe { CRYPTO_get_ex_data(&self.ex_data, idx) } + } +} + +impl Drop for ExData { + fn drop(&mut self) { + if !self.owner.is_null() { + unsafe { + CRYPTO_free_ex_data(self.ty, self.owner, &mut self.ex_data); + }; + self.owner = ptr::null_mut(); + } + } +} + +impl Default for ExData { + fn default() -> Self { + Self { + ex_data: CRYPTO_EX_DATA::default(), + ty: -1, + owner: ptr::null_mut(), + } + } +} + +/// This has the same layout prefix as `struct crypto_ex_data_st` aka +/// `CRYPTO_EX_DATA` -- just two pointers. We don't need to know +/// the types of these; the API lets us treat them opaquely. +/// +/// This is _not_ owning. +#[repr(C)] +struct CRYPTO_EX_DATA { + ctx: *mut c_void, + sk: *mut c_void, +} + +impl Default for CRYPTO_EX_DATA { + fn default() -> Self { + Self { + ctx: ptr::null_mut(), + sk: ptr::null_mut(), + } + } +} + +// See `crypto.h` +const CRYPTO_EX_INDEX_SSL: c_int = 0; +const CRYPTO_EX_INDEX_SSL_CTX: c_int = 1; + +extern "C" { + fn CRYPTO_new_ex_data(class_index: c_int, obj: *mut c_void, ed: *mut CRYPTO_EX_DATA) -> c_int; + fn CRYPTO_set_ex_data(ed: *mut CRYPTO_EX_DATA, index: c_int, data: *mut c_void) -> c_int; + fn CRYPTO_get_ex_data(ed: *const CRYPTO_EX_DATA, index: c_int) -> *mut c_void; + fn CRYPTO_free_ex_data(class_index: c_int, obj: *mut c_void, ed: *mut CRYPTO_EX_DATA); +} diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs index f208412..a5a70a7 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -35,6 +35,7 @@ mod constants; mod entry; mod error; mod evp_pkey; +mod ex_data; #[macro_use] #[allow(unused_macros, dead_code, unused_imports)] mod ffi; @@ -214,6 +215,7 @@ static TLS13_CHACHA20_POLY1305_SHA256: SslCipher = SslCipher { pub struct SslContext { method: &'static SslMethod, + ex_data: ex_data::ExData, raw_options: u64, verify_mode: VerifyMode, verify_depth: c_int, @@ -233,6 +235,7 @@ impl SslContext { fn new(method: &'static SslMethod) -> Self { Self { method, + ex_data: ex_data::ExData::default(), raw_options: 0, verify_mode: VerifyMode::default(), verify_depth: -1, @@ -249,6 +252,18 @@ impl SslContext { } } + fn install_ex_data(&mut self, ex_data: ex_data::ExData) { + self.ex_data = ex_data; + } + + fn set_ex_data(&mut self, idx: c_int, data: *mut c_void) -> Result<(), error::Error> { + self.ex_data.set(idx, data) + } + + fn get_ex_data(&self, idx: c_int) -> *mut c_void { + self.ex_data.get(idx) + } + fn get_options(&self) -> u64 { self.raw_options } @@ -412,6 +427,7 @@ pub fn iter_alpn(mut slice: &[u8]) -> impl Iterator> { struct Ssl { ctx: Arc>, + ex_data: ex_data::ExData, raw_options: u64, mode: ConnMode, verify_mode: VerifyMode, @@ -446,6 +462,7 @@ impl Ssl { fn new(ctx: Arc>, inner: &SslContext) -> Result { Ok(Self { ctx, + ex_data: ex_data::ExData::default(), raw_options: inner.raw_options, mode: inner.method.mode(), verify_mode: inner.verify_mode, @@ -468,6 +485,18 @@ impl Ssl { }) } + fn install_ex_data(&mut self, ex_data: ex_data::ExData) { + self.ex_data = ex_data; + } + + fn set_ex_data(&mut self, idx: c_int, data: *mut c_void) -> Result<(), error::Error> { + self.ex_data.set(idx, data) + } + + fn get_ex_data(&self, idx: c_int) -> *mut c_void { + self.ex_data.get(idx) + } + fn set_ctx(&mut self, ctx: Arc>) { // there are no docs for `SSL_set_SSL_CTX`. it seems the only // meaningful reason to use this is key/certificate switching diff --git a/rustls-libssl/src/miri.rs b/rustls-libssl/src/miri.rs index 9429525..b6dcece 100644 --- a/rustls-libssl/src/miri.rs +++ b/rustls-libssl/src/miri.rs @@ -1,5 +1,6 @@ +use core::ptr; /// Shims for functions we call, written in rust so they are visible to miri. -use std::ffi::{c_char, c_int, CStr}; +use std::ffi::{c_char, c_int, c_void, CStr}; pub struct X509_STORE(()); @@ -27,3 +28,24 @@ pub extern "C" fn ERR_set_error(lib: c_int, reason: c_int, message: *const c_cha CStr::from_ptr(message) }); } + +#[no_mangle] +pub extern "C" fn CRYPTO_new_ex_data( + ty: c_int, + owner: *mut c_void, + out: *mut [*mut c_void; 2], +) -> c_int { + eprintln!("CRYPTO_new_ex_data({ty}, {owner:?});"); + let marker = [owner, owner]; + unsafe { + ptr::write(out, marker); + }; + 1 +} + +#[no_mangle] +pub extern "C" fn CRYPTO_free_ex_data(ty: c_int, owner: *mut c_void, ed: *mut [*mut c_void; 2]) { + let marker: [*mut c_void; 2] = unsafe { ptr::read(ed) }; + assert!(marker[0] == owner); + assert!(marker[1] == owner); +} diff --git a/rustls-libssl/tests/server.c b/rustls-libssl/tests/server.c index 897f6f4..d329960 100644 --- a/rustls-libssl/tests/server.c +++ b/rustls-libssl/tests/server.c @@ -19,6 +19,9 @@ #include "helpers.h" +static int ssl_ctx_ex_data_idx_message; +static int ssl_ex_data_idx_message; + static int alpn_cookie = 12345; static int alpn_callback(SSL *ssl, const uint8_t **out, uint8_t *outlen, @@ -26,6 +29,8 @@ static int alpn_callback(SSL *ssl, const uint8_t **out, uint8_t *outlen, printf("in alpn_callback:\n"); assert(ssl != NULL); assert(arg == &alpn_cookie); + printf(" ssl_ex_data_idx_message: %s\n", + (const char *)SSL_get_ex_data(ssl, ssl_ex_data_idx_message)); hexdump(" in", in, (int)inlen); if (SSL_select_next_proto((uint8_t **)out, outlen, (const uint8_t *)"\x08http/1.1", 9, in, @@ -44,6 +49,8 @@ static int cert_callback(SSL *ssl, void *arg) { printf("in cert_callback\n"); assert(ssl != NULL); assert(arg == &cert_cookie); + printf(" ssl_ex_data_idx_message: %s\n", + (const char *)SSL_get_ex_data(ssl, ssl_ex_data_idx_message)); return 1; } @@ -57,6 +64,8 @@ static int sni_callback(SSL *ssl, int *al, void *arg) { printf(" SSL_get_servername: %s (%d)\n", SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name), SSL_get_servername_type(ssl)); + printf(" ssl_ex_data_idx_message: %s\n", + (const char *)SSL_get_ex_data(ssl, ssl_ex_data_idx_message)); return SSL_TLSEXT_ERR_OK; } @@ -96,6 +105,13 @@ int main(int argc, char **argv) { printf("client auth disabled\n"); } + ssl_ctx_ex_data_idx_message = + SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); + TRACE(SSL_CTX_set_ex_data(ctx, ssl_ctx_ex_data_idx_message, + "hello from SSL_CTX!")); + printf("ssl_ctx_ex_data_idx_message: %s\n", + (const char *)SSL_CTX_get_ex_data(ctx, ssl_ctx_ex_data_idx_message)); + SSL_CTX_set_alpn_select_cb(ctx, alpn_callback, &alpn_cookie); dump_openssl_error_stack(); @@ -123,6 +139,12 @@ int main(int argc, char **argv) { SSL_get_privatekey(ssl) == server_key ? "same as" : "differs to"); printf("SSL_new: SSL_get_certificate %s SSL_CTX_get0_certificate\n", SSL_get_certificate(ssl) == server_cert ? "same as" : "differs to"); + + ssl_ex_data_idx_message = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); + TRACE(SSL_set_ex_data(ssl, ssl_ex_data_idx_message, "hello from SSL!")); + printf("ssl_ex_data_idx_message: %s\n", + (const char *)SSL_get_ex_data(ssl, ssl_ex_data_idx_message)); + state(ssl); TRACE(SSL_set_fd(ssl, sock)); dump_openssl_error_stack(); @@ -180,6 +202,10 @@ int main(int argc, char **argv) { TRACE(SSL_shutdown(ssl)); dump_openssl_error_stack(); + printf("ssl_ex_data_idx_message: %s\n", + (const char *)SSL_get_ex_data(ssl, ssl_ex_data_idx_message)); + printf("ssl_ctx_ex_data_idx_message: %s\n", + (const char *)SSL_CTX_get_ex_data(ctx, ssl_ctx_ex_data_idx_message)); close(sock); close(listener); SSL_free(ssl); From f42e077ef4f11f93ec05205b401c52257f4d13a9 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Mon, 8 Apr 2024 14:33:35 +0100 Subject: [PATCH 20/29] Allowlist `SSL_CTX_set_cipher_list` for `NGX_DEFAULT_CIPHERS` --- rustls-libssl/MATRIX.md | 2 +- rustls-libssl/src/entry.rs | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index b8a50db..510eb0b 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -138,7 +138,7 @@ | `SSL_CTX_set_cert_cb` | | :white_check_mark: | :white_check_mark: | | `SSL_CTX_set_cert_store` | | | | | `SSL_CTX_set_cert_verify_callback` | | | | -| `SSL_CTX_set_cipher_list` | :white_check_mark: | :white_check_mark: | :exclamation: [^stub] | +| `SSL_CTX_set_cipher_list` | :white_check_mark: | :white_check_mark: | :white_check_mark: | | `SSL_CTX_set_ciphersuites` | :white_check_mark: | | :exclamation: [^stub] | | `SSL_CTX_set_client_CA_list` | | :white_check_mark: | :exclamation: [^stub] | | `SSL_CTX_set_client_cert_cb` | | | | diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index 8148ac9..98de39f 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -597,6 +597,17 @@ entry! { } } +entry! { + pub fn _SSL_CTX_set_cipher_list(_ctx: *mut SSL_CTX, s: *const c_char) -> c_int { + match try_str!(s) { + "HIGH:!aNULL:!MD5" => C_INT_SUCCESS, + _ => Error::not_supported("SSL_CTX_set_cipher_list") + .raise() + .into(), + } + } +} + impl Castable for SSL_CTX { type Ownership = OwnershipArc; type RustType = NotThreadSafe; @@ -1510,10 +1521,6 @@ entry_stub! { pub fn _i2d_SSL_SESSION(_in: *const SSL_SESSION, _pp: *mut *mut c_uchar) -> c_int; } -entry_stub! { - pub fn _SSL_CTX_set_cipher_list(_ctx: *mut SSL_CTX, _s: *const c_char) -> c_int; -} - entry_stub! { pub fn _SSL_CTX_set_ciphersuites(_ctx: *mut SSL_CTX, _s: *const c_char) -> c_int; } From 34e09142c96ab4610a5ca78a9294425593e2217d Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Tue, 9 Apr 2024 17:54:50 +0100 Subject: [PATCH 21/29] Stub out `SSL_check_private_key` --- rustls-libssl/MATRIX.md | 2 +- rustls-libssl/build.rs | 1 + rustls-libssl/src/entry.rs | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index 510eb0b..f283a18 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -272,7 +272,7 @@ | `SSL_callback_ctrl` | | | | | `SSL_certs_clear` | | | | | `SSL_check_chain` | | | | -| `SSL_check_private_key` | | | | +| `SSL_check_private_key` | | | :white_check_mark: | | `SSL_clear` | | | | | `SSL_clear_options` | | :white_check_mark: | :white_check_mark: | | `SSL_client_hello_get0_ciphers` | | | | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index eb19cc3..e6eb2ed 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -49,6 +49,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_accept", "SSL_alert_desc_string", "SSL_alert_desc_string_long", + "SSL_check_private_key", "SSL_CIPHER_description", "SSL_CIPHER_find", "SSL_CIPHER_get_bits", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index 98de39f..9026455 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -1130,6 +1130,13 @@ entry! { } } +entry! { + pub fn _SSL_check_private_key(_ssl: *const SSL) -> c_int { + log::trace!("not implemented: _SSL_check_private_key, returning success"); + C_INT_SUCCESS + } +} + entry! { pub fn _SSL_get_servername(ssl: *const SSL, ty: c_int) -> *const c_char { if ty != TLSEXT_NAMETYPE_host_name { From 44ef3dcc62013cdf9a90152f40ab5962e66aa683 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Tue, 9 Apr 2024 17:56:14 +0100 Subject: [PATCH 22/29] Implement `SSL_use_PrivateKey_file` --- rustls-libssl/MATRIX.md | 2 +- rustls-libssl/build.rs | 1 + rustls-libssl/src/entry.rs | 106 ++++++++++++++++++++++++------------- 3 files changed, 70 insertions(+), 39 deletions(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index f283a18..3d2ae9f 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -487,7 +487,7 @@ | `SSL_up_ref` | | | :white_check_mark: | | `SSL_use_PrivateKey` | | :white_check_mark: | :white_check_mark: | | `SSL_use_PrivateKey_ASN1` | | | | -| `SSL_use_PrivateKey_file` | | | | +| `SSL_use_PrivateKey_file` | | | :white_check_mark: | | `SSL_use_RSAPrivateKey` [^deprecatedin_3_0] | | | | | `SSL_use_RSAPrivateKey_ASN1` [^deprecatedin_3_0] | | | | | `SSL_use_RSAPrivateKey_file` [^deprecatedin_3_0] | | | | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index e6eb2ed..cb83490 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -181,6 +181,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_up_ref", "SSL_use_certificate", "SSL_use_PrivateKey", + "SSL_use_PrivateKey_file", "SSL_want", "SSL_write", "SSL_write_early_data", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index 9026455..971ed04 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -432,6 +432,47 @@ entry! { } } +fn use_private_key_file(file_name: &str, file_type: c_int) -> Result { + let der_data = match file_type { + FILETYPE_PEM => { + let mut file_reader = match fs::File::open(file_name) { + Ok(content) => io::BufReader::new(content), + Err(err) => return Err(Error::from_io(err)), + }; + + match rustls_pemfile::private_key(&mut file_reader) { + Ok(Some(key)) => key, + Ok(None) => { + log::trace!("No keys found in {file_name:?}"); + return Err(Error::bad_data("pem file")); + } + Err(err) => { + log::trace!("Failed to read {file_name:?}: {err:?}"); + return Err(Error::from_io(err)); + } + } + } + FILETYPE_DER => { + let mut data = vec![]; + match fs::File::open(file_name).and_then(|mut f| f.read_to_end(&mut data)) { + Ok(_) => PrivatePkcs8KeyDer::from(data).into(), + Err(err) => { + log::trace!("Failed to read {file_name:?}: {err:?}"); + return Err(Error::from_io(err)); + } + } + } + _ => { + return Err(Error::not_supported("file_type not in (PEM, DER)")); + } + }; + + match EvpPkey::new_from_der_bytes(der_data) { + None => Err(Error::not_supported("invalid key format")), + Some(key) => Ok(key), + } +} + entry! { pub fn _SSL_CTX_use_PrivateKey_file( ctx: *mut SSL_CTX, @@ -441,47 +482,13 @@ entry! { let ctx = try_clone_arc!(ctx); let file_name = try_str!(file_name); - let der_data = match file_type { - FILETYPE_PEM => { - let mut file_reader = match fs::File::open(file_name) { - Ok(content) => io::BufReader::new(content), - Err(err) => return Error::from_io(err).raise().into(), - }; - - match rustls_pemfile::private_key(&mut file_reader) { - Ok(Some(key)) => key, - Ok(None) => { - log::trace!("No keys found in {file_name:?}"); - return Error::bad_data("pem file").raise().into(); - } - Err(err) => { - log::trace!("Failed to read {file_name:?}: {err:?}"); - return Error::from_io(err).raise().into(); - } - } - } - FILETYPE_DER => { - let mut data = vec![]; - match fs::File::open(file_name).and_then(|mut f| f.read_to_end(&mut data)) { - Ok(_) => PrivatePkcs8KeyDer::from(data).into(), - Err(err) => { - log::trace!("Failed to read {file_name:?}: {err:?}"); - return Error::from_io(err).raise().into(); - } - } - } - _ => { - return Error::not_supported("file_type not in (PEM, DER)") - .raise() - .into(); + let key = match use_private_key_file(file_name, file_type) { + Ok(key) => key, + Err(err) => { + return err.raise().into(); } }; - let key = match EvpPkey::new_from_der_bytes(der_data) { - None => return Error::not_supported("invalid key format").raise().into(), - Some(key) => key, - }; - match ctx.get_mut().commit_private_key(key) { Err(e) => e.raise().into(), Ok(()) => C_INT_SUCCESS, @@ -1130,6 +1137,29 @@ entry! { } } +entry! { + pub fn _SSL_use_PrivateKey_file( + ssl: *mut SSL, + file_name: *const c_char, + file_type: c_int, + ) -> c_int { + let ssl = try_clone_arc!(ssl); + let file_name = try_str!(file_name); + + let key = match use_private_key_file(file_name, file_type) { + Ok(key) => key, + Err(err) => { + return err.raise().into(); + } + }; + + match ssl.get_mut().commit_private_key(key) { + Err(e) => e.raise().into(), + Ok(()) => C_INT_SUCCESS, + } + } +} + entry! { pub fn _SSL_check_private_key(_ssl: *const SSL) -> c_int { log::trace!("not implemented: _SSL_check_private_key, returning success"); From 2bbbca97ae88ab4928a689c45f604a4f2a3fa445 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Tue, 9 Apr 2024 17:56:27 +0100 Subject: [PATCH 23/29] Implement `SSL_get_current_compression` This just returns NULL forever, because rustls does not do compression. That's the same behaviour as OpenSSL built with OPENSSL_NO_COMP. --- rustls-libssl/MATRIX.md | 2 +- rustls-libssl/build.rs | 1 + rustls-libssl/src/entry.rs | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index 3d2ae9f..e6cb66f 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -329,7 +329,7 @@ | `SSL_get_client_ciphers` | | | | | `SSL_get_client_random` | | | | | `SSL_get_current_cipher` | :white_check_mark: | :white_check_mark: | :white_check_mark: | -| `SSL_get_current_compression` | | | | +| `SSL_get_current_compression` | | | :white_check_mark: | | `SSL_get_current_expansion` | | | | | `SSL_get_default_passwd_cb` | | | | | `SSL_get_default_passwd_cb_userdata` | | | | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index cb83490..18f3b37 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -129,6 +129,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_get1_session", "SSL_get_certificate", "SSL_get_current_cipher", + "SSL_get_current_compression", "SSL_get_error", "SSL_get_ex_data", "SSL_get_options", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index 971ed04..7dce1da 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -1214,6 +1214,12 @@ entry! { } } +entry! { + pub fn _SSL_get_current_compression(_ssl: *const SSL) -> *const c_void { + ptr::null() + } +} + impl Castable for SSL { type Ownership = OwnershipArc; type RustType = NotThreadSafe; From 8a4a0fc9b180424b438eaf1522a06ad0413f0114 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Fri, 5 Apr 2024 15:54:28 +0100 Subject: [PATCH 24/29] Stub out `SSL_get_ex_data_X509_STORE_CTX_idx` Will be needed for verify callback support. --- rustls-libssl/MATRIX.md | 2 +- rustls-libssl/build.rs | 1 + rustls-libssl/src/entry.rs | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index e6cb66f..175ddbe 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -337,7 +337,7 @@ | `SSL_get_early_data_status` | | | | | `SSL_get_error` | :white_check_mark: | :white_check_mark: | :white_check_mark: | | `SSL_get_ex_data` | :white_check_mark: | :white_check_mark: | :white_check_mark: | -| `SSL_get_ex_data_X509_STORE_CTX_idx` | | :white_check_mark: | | +| `SSL_get_ex_data_X509_STORE_CTX_idx` | | :white_check_mark: | :exclamation: [^stub] | | `SSL_get_fd` | | | | | `SSL_get_finished` | | | | | `SSL_get_info_callback` | | | | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index 18f3b37..34fb6d2 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -132,6 +132,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_get_current_compression", "SSL_get_error", "SSL_get_ex_data", + "SSL_get_ex_data_X509_STORE_CTX_idx", "SSL_get_options", "SSL_get_peer_cert_chain", "SSL_get_privatekey", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index 7dce1da..76f6678 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -1466,6 +1466,10 @@ macro_rules! entry_stub { // things we support and should be able to implement to // some extent: +entry_stub! { + pub fn _SSL_get_ex_data_X509_STORE_CTX_idx() -> c_int; +} + entry_stub! { pub fn _SSL_set_session(_ssl: *mut SSL, _session: *mut SSL_SESSION) -> c_int; } From 531a397dec9e7fa9f25b583d188100aea5fee4fc Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Wed, 10 Apr 2024 09:59:56 +0100 Subject: [PATCH 25/29] Implement protocol versions configuration This provides: - `SSL_CTX_set_min_proto_version` - `SSL_CTX_set_max_proto_version` - `SSL_CTX_get_min_proto_version` - `SSL_CTX_get_max_proto_version` - `SSL_set_min_proto_version` - `SSL_set_max_proto_version` - `SSL_get_min_proto_version` - `SSL_get_max_proto_version` (All of those functions are routed into `SSL_CTX_ctrl` & `SSL_ctrl`.) --- rustls-libssl/src/entry.rs | 35 +++++++++-- rustls-libssl/src/lib.rs | 113 +++++++++++++++++++++++++++++++++-- rustls-libssl/tests/client.c | 12 ++++ 3 files changed, 151 insertions(+), 9 deletions(-) diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index 76f6678..9212e05 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -188,10 +188,22 @@ entry! { log::warn!("unimplemented SSL_CTX_set_msg_callback_arg()"); 0 } + Ok(SslCtrl::SetMinProtoVersion) => { + if larg < 0 || larg > u16::MAX.into() { + return 0; + } + ctx.get_mut().set_min_protocol_version(larg as u16); + C_INT_SUCCESS as c_long + } + Ok(SslCtrl::GetMinProtoVersion) => ctx.get().get_min_protocol_version().into(), Ok(SslCtrl::SetMaxProtoVersion) => { - log::warn!("unimplemented SSL_CTX_set_max_proto_version()"); - 1 + if larg < 0 || larg > u16::MAX.into() { + return 0; + } + ctx.get_mut().set_max_protocol_version(larg as u16); + C_INT_SUCCESS as c_long } + Ok(SslCtrl::GetMaxProtoVersion) => ctx.get().get_max_protocol_version().into(), Ok(SslCtrl::SetTlsExtHostname) | Ok(SslCtrl::SetTlsExtServerNameCallback) => { // not a defined operation in the OpenSSL API 0 @@ -688,10 +700,22 @@ entry! { log::warn!("unimplemented SSL_set_msg_callback_arg()"); 0 } + Ok(SslCtrl::SetMinProtoVersion) => { + if larg < 0 || larg > u16::MAX.into() { + return 0; + } + ssl.get_mut().set_min_protocol_version(larg as u16); + C_INT_SUCCESS as c_long + } + Ok(SslCtrl::GetMinProtoVersion) => ssl.get().get_min_protocol_version().into(), Ok(SslCtrl::SetMaxProtoVersion) => { - log::warn!("unimplemented SSL_set_max_proto_version()"); - 1 + if larg < 0 || larg > u16::MAX.into() { + return 0; + } + ssl.get_mut().set_max_protocol_version(larg as u16); + C_INT_SUCCESS as c_long } + Ok(SslCtrl::GetMaxProtoVersion) => ssl.get().get_max_protocol_version().into(), Ok(SslCtrl::SetTlsExtHostname) => { let hostname = try_str!(parg as *const c_char); ssl.get_mut().set_sni_hostname(hostname) as c_long @@ -1438,7 +1462,10 @@ num_enum! { SetTlsExtServerNameArg = 54, SetTlsExtHostname = 55, SetChain = 88, + SetMinProtoVersion = 123, SetMaxProtoVersion = 124, + GetMinProtoVersion = 130, + GetMaxProtoVersion = 131, } } diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs index a5a70a7..68b9ef7 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -15,7 +15,8 @@ use rustls::crypto::aws_lc_rs as provider; use rustls::pki_types::{CertificateDer, ServerName}; use rustls::server::{Accepted, Acceptor}; use rustls::{ - CipherSuite, ClientConfig, ClientConnection, Connection, RootCertStore, ServerConfig, + CipherSuite, ClientConfig, ClientConnection, Connection, ProtocolVersion, RootCertStore, + ServerConfig, SupportedProtocolVersion, }; use not_thread_safe::NotThreadSafe; @@ -52,8 +53,8 @@ mod x509; /// # Lifetime /// Functions that return SSL_METHOD, like `TLS_method()`, give static-lifetime pointers. pub struct SslMethod { - client_versions: &'static [&'static rustls::SupportedProtocolVersion], - server_versions: &'static [&'static rustls::SupportedProtocolVersion], + client_versions: &'static [&'static SupportedProtocolVersion], + server_versions: &'static [&'static SupportedProtocolVersion], } impl SslMethod { @@ -216,6 +217,7 @@ static TLS13_CHACHA20_POLY1305_SHA256: SslCipher = SslCipher { pub struct SslContext { method: &'static SslMethod, ex_data: ex_data::ExData, + versions: EnabledVersions, raw_options: u64, verify_mode: VerifyMode, verify_depth: c_int, @@ -236,6 +238,7 @@ impl SslContext { Self { method, ex_data: ex_data::ExData::default(), + versions: EnabledVersions::default(), raw_options: 0, verify_mode: VerifyMode::default(), verify_depth: -1, @@ -278,6 +281,36 @@ impl SslContext { self.raw_options } + fn set_min_protocol_version(&mut self, ver: u16) { + self.versions.min = match ver { + 0 => None, + _ => Some(ProtocolVersion::from(ver)), + }; + } + + fn get_min_protocol_version(&self) -> u16 { + self.versions + .min + .as_ref() + .map(|v| u16::from(*v)) + .unwrap_or_default() + } + + fn set_max_protocol_version(&mut self, ver: u16) { + self.versions.max = match ver { + 0 => None, + _ => Some(ProtocolVersion::from(ver)), + }; + } + + fn get_max_protocol_version(&self) -> u16 { + self.versions + .max + .as_ref() + .map(|v| u16::from(*v)) + .unwrap_or_default() + } + fn set_max_early_data(&mut self, max: u32) { self.max_early_data = max; } @@ -428,6 +461,7 @@ pub fn iter_alpn(mut slice: &[u8]) -> impl Iterator> { struct Ssl { ctx: Arc>, ex_data: ex_data::ExData, + versions: EnabledVersions, raw_options: u64, mode: ConnMode, verify_mode: VerifyMode, @@ -463,6 +497,7 @@ impl Ssl { Ok(Self { ctx, ex_data: ex_data::ExData::default(), + versions: inner.versions.clone(), raw_options: inner.raw_options, mode: inner.method.mode(), verify_mode: inner.verify_mode, @@ -519,6 +554,36 @@ impl Ssl { self.raw_options } + fn set_min_protocol_version(&mut self, ver: u16) { + self.versions.min = match ver { + 0 => None, + _ => Some(ProtocolVersion::from(ver)), + }; + } + + fn get_min_protocol_version(&self) -> u16 { + self.versions + .min + .as_ref() + .map(|v| u16::from(*v)) + .unwrap_or_default() + } + + fn set_max_protocol_version(&mut self, ver: u16) { + self.versions.max = match ver { + 0 => None, + _ => Some(ProtocolVersion::from(ver)), + }; + } + + fn get_max_protocol_version(&self) -> u16 { + self.versions + .max + .as_ref() + .map(|v| u16::from(*v)) + .unwrap_or_default() + } + fn set_alpn_offer(&mut self, alpn: Vec>) { self.alpn = alpn; } @@ -680,8 +745,10 @@ impl Ssl { &self.verify_server_name, )); + let versions = self.versions.reduce_versions(method.client_versions)?; + let wants_resolver = ClientConfig::builder_with_provider(provider) - .with_protocol_versions(method.client_versions) + .with_protocol_versions(&versions) .map_err(error::Error::from_rustls)? .dangerous() .with_custom_certificate_verifier(verifier.clone()); @@ -768,8 +835,10 @@ impl Ssl { .server_resolver() .ok_or_else(|| error::Error::bad_data("missing server keys"))?; + let versions = self.versions.reduce_versions(method.server_versions)?; + let mut config = ServerConfig::builder_with_provider(provider) - .with_protocol_versions(method.server_versions) + .with_protocol_versions(&versions) .map_err(error::Error::from_rustls)? .with_client_cert_verifier(verifier.clone()) .with_cert_resolver(resolver); @@ -1259,6 +1328,40 @@ impl From for i32 { } } +#[derive(Debug, Default, Clone)] +struct EnabledVersions { + min: Option, + max: Option, +} + +impl EnabledVersions { + fn reduce_versions( + &self, + method_versions: &'static [&'static SupportedProtocolVersion], + ) -> Result, error::Error> { + let acceptable: Vec<&'static SupportedProtocolVersion> = method_versions + .iter() + .cloned() + .filter(|v| self.satisfies(v.version)) + .collect(); + + if acceptable.is_empty() { + Err(error::Error::bad_data(&format!( + "no versions usable: method enabled {method_versions:?}, filter {self:?}" + ))) + } else { + Ok(acceptable) + } + } + + fn satisfies(&self, v: ProtocolVersion) -> bool { + let min = self.min.map(u16::from).unwrap_or(0); + let max = self.max.map(u16::from).unwrap_or(0xffff); + let v = u16::from(v); + min <= v && v <= max + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/rustls-libssl/tests/client.c b/rustls-libssl/tests/client.c index e353358..ab8416c 100644 --- a/rustls-libssl/tests/client.c +++ b/rustls-libssl/tests/client.c @@ -61,6 +61,16 @@ int main(int argc, char **argv) { } printf("SSL_CTX_get_verify_depth default %d\n", SSL_CTX_get_verify_depth(ctx)); + printf("SSL_CTX_get_min_proto_version default 0x%lx\n", + SSL_CTX_get_min_proto_version(ctx)); + printf("SSL_CTX_get_max_proto_version default 0x%lx\n", + SSL_CTX_get_max_proto_version(ctx)); + TRACE(SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION)); + TRACE(SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION)); + printf("SSL_CTX_get_min_proto_version 0x%lx\n", + SSL_CTX_get_min_proto_version(ctx)); + printf("SSL_CTX_get_max_proto_version 0x%lx\n", + SSL_CTX_get_max_proto_version(ctx)); X509 *client_cert = NULL; EVP_PKEY *client_key = NULL; @@ -82,6 +92,8 @@ int main(int argc, char **argv) { SSL_get_certificate(ssl) == client_cert ? "same as" : "differs to"); state(ssl); printf("SSL_get_verify_depth default %d\n", SSL_get_verify_depth(ssl)); + printf("SSL_get_min_proto_version 0x%lx\n", SSL_get_min_proto_version(ssl)); + printf("SSL_get_max_proto_version 0x%lx\n", SSL_get_max_proto_version(ssl)); printf("SSL_get_servername: %s (%d)\n", SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name), SSL_get_servername_type(ssl)); From ce39b95f335cecffcd0c8e65f4b2a6178195d72e Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Mon, 22 Apr 2024 10:54:11 +0100 Subject: [PATCH 26/29] Match OpenSSL default for TLS1.3 ticket issuance --- rustls-libssl/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs index 68b9ef7..d598eab 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -845,6 +845,7 @@ impl Ssl { config.alpn_protocols = mem::take(&mut self.alpn); config.max_early_data_size = self.max_early_data; + config.send_tls13_tickets = 2; // match OpenSSL default: see `man SSL_CTX_set_num_tickets` let accepted = match mem::replace(&mut self.conn, ConnState::Nothing) { ConnState::Accepted(accepted) => accepted, From 0db75bd1c14b1459a0198f526a9e6347d83092ae Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Wed, 17 Apr 2024 10:57:53 +0100 Subject: [PATCH 27/29] Implement `SSL_session_reused` --- rustls-libssl/MATRIX.md | 2 +- rustls-libssl/src/entry.rs | 10 ++++++---- rustls-libssl/src/lib.rs | 11 +++++++++-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index 175ddbe..f5afd18 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -410,7 +410,7 @@ | `SSL_rstate_string_long` | | | | | `SSL_select_next_proto` | | :white_check_mark: | :white_check_mark: | | `SSL_sendfile` | | | | -| `SSL_session_reused` | | :white_check_mark: | :exclamation: [^stub] | +| `SSL_session_reused` | | :white_check_mark: | :white_check_mark: | | `SSL_set0_CA_list` | | | | | `SSL_set0_rbio` | | | :white_check_mark: | | `SSL_set0_security_ex_data` | | | | diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index 9212e05..ca7cbd6 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -1244,6 +1244,12 @@ entry! { } } +entry! { + pub fn _SSL_session_reused(ssl: *const SSL) -> c_int { + try_clone_arc!(ssl).get().was_session_reused() as c_int + } +} + impl Castable for SSL { type Ownership = OwnershipArc; type RustType = NotThreadSafe; @@ -1501,10 +1507,6 @@ entry_stub! { pub fn _SSL_set_session(_ssl: *mut SSL, _session: *mut SSL_SESSION) -> c_int; } -entry_stub! { - pub fn _SSL_session_reused(_ssl: *const SSL) -> c_int; -} - entry_stub! { pub fn _SSL_get1_session(_ssl: *mut SSL) -> *mut SSL_SESSION; } diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs index d598eab..eb62060 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -15,8 +15,8 @@ use rustls::crypto::aws_lc_rs as provider; use rustls::pki_types::{CertificateDer, ServerName}; use rustls::server::{Accepted, Acceptor}; use rustls::{ - CipherSuite, ClientConfig, ClientConnection, Connection, ProtocolVersion, RootCertStore, - ServerConfig, SupportedProtocolVersion, + CipherSuite, ClientConfig, ClientConnection, Connection, HandshakeKind, ProtocolVersion, + RootCertStore, ServerConfig, SupportedProtocolVersion, }; use not_thread_safe::NotThreadSafe; @@ -1168,6 +1168,13 @@ impl Ssl { None => HandshakeState::Before, } } + + fn was_session_reused(&self) -> bool { + match self.conn() { + Some(conn) => conn.handshake_kind() == Some(HandshakeKind::Resumed), + None => false, + } + } } /// Encode rustls's internal representation in the wire format. From 141d79966eb8c2af573f82830fbd3ff29c14e657 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Tue, 23 Apr 2024 16:18:40 +0100 Subject: [PATCH 28/29] Ignore calls to `SSL_CTX_set_session_id_context` Later commits will implement this, and session resumption more generally. --- rustls-libssl/MATRIX.md | 2 +- rustls-libssl/src/entry.rs | 20 +++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index f5afd18..353c573 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -179,7 +179,7 @@ | `SSL_CTX_set_recv_max_early_data` | | | | | `SSL_CTX_set_security_callback` | | | | | `SSL_CTX_set_security_level` | | | | -| `SSL_CTX_set_session_id_context` | | :white_check_mark: | :exclamation: [^stub] | +| `SSL_CTX_set_session_id_context` | | :white_check_mark: | :white_check_mark: | | `SSL_CTX_set_session_ticket_cb` | | | | | `SSL_CTX_set_srp_cb_arg` [^deprecatedin_3_0] [^srp] | | | | | `SSL_CTX_set_srp_client_pwd_callback` [^deprecatedin_3_0] [^srp] | | | | diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index ca7cbd6..c1105d7 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -627,6 +627,17 @@ entry! { } } +entry! { + pub fn _SSL_CTX_set_session_id_context( + _ctx: *mut SSL_CTX, + _sid_ctx: *const c_uchar, + _sid_ctx_len: c_uint, + ) -> c_int { + log::warn!("SSL_CTX_set_session_id_context not yet implemented"); + C_INT_SUCCESS + } +} + impl Castable for SSL_CTX { type Ownership = OwnershipArc; type RustType = NotThreadSafe; @@ -1543,15 +1554,6 @@ pub type SSL_CTX_sess_remove_cb = Option; entry_stub! { - pub fn _SSL_CTX_set_session_id_context( - _ctx: *mut SSL_CTX, - _sid_ctx: *const c_uchar, - _sid_ctx_len: c_uint, - ) -> c_int; -} - -entry_stub! { - pub fn _SSL_set_session_id_context( _ssl: *mut SSL, _sid_ctx: *const c_uchar, From faff689651c1cf243b71436c925563f044bf2967 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Tue, 9 Apr 2024 13:12:21 +0100 Subject: [PATCH 29/29] Set up nginx integration test This uses the system nginx (assumed to be available) to start a server, then grabs a small html file and a larger 5MB download with the system curl (using system openssl). --- rustls-libssl/tests/nginx.conf | 47 ++++++++++++++++++++++++ rustls-libssl/tests/runner.rs | 67 +++++++++++++++++++++++++++++++++- 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 rustls-libssl/tests/nginx.conf diff --git a/rustls-libssl/tests/nginx.conf b/rustls-libssl/tests/nginx.conf new file mode 100644 index 0000000..4acc9ed --- /dev/null +++ b/rustls-libssl/tests/nginx.conf @@ -0,0 +1,47 @@ +daemon off; +master_process off; +pid nginx.pid; + +events { +} + +http { + ssl_protocols TLSv1.2 TLSv1.3; + access_log access.log; + + server { + listen 8443 ssl; + server_name localhost; + ssl_certificate ../../../test-ca/rsa/server.cert; + ssl_certificate_key ../../../test-ca/rsa/server.key; + + location = / { + return 200 "hello world\n"; + } + + location /ssl-agreed { + return 200 "protocol:$ssl_protocol,cipher:$ssl_cipher\n"; + } + + location /ssl-server-name { + return 200 "server-name:$ssl_server_name\n"; + } + + location /ssl-was-reused { + return 200 "reused:$ssl_session_reused\n"; + } + + # not currently implemented: + location /ssl-offer { + return 200 "ciphers:$ssl_ciphers,curves:$ssl_curves\n"; + } + + location /ssl-early-data { + return 200 "early-data:$ssl_early_data\n"; + } + + location /ssl-client-auth { + return 200 "s-dn:$ssl_client_s_dn\ni-dn:$ssl_client_i_dn\nserial:$ssl_client_serial\nfp:$ssl_client_fingerprint\nverify:$ssl_client_verify\nv-start:$ssl_client_v_start\nv-end:$ssl_client_v_end\nv-remain:$ssl_client_v_remain\ncert:\n$ssl_client_cert\n"; + } + } +} diff --git a/rustls-libssl/tests/runner.rs b/rustls-libssl/tests/runner.rs index ff3792a..03e6075 100644 --- a/rustls-libssl/tests/runner.rs +++ b/rustls-libssl/tests/runner.rs @@ -1,6 +1,6 @@ use std::io::Read; use std::process::{Child, Command, Output, Stdio}; -use std::{net, thread, time}; +use std::{fs, net, thread, time}; /* Note: * @@ -327,6 +327,71 @@ fn server() { assert_eq!(openssl_output, rustls_output); } +const NGINX_LOG_LEVEL: &str = "info"; + +#[test] +#[ignore] +fn nginx() { + fs::create_dir_all("target/nginx-tmp/basic/html").unwrap(); + fs::write( + "target/nginx-tmp/basic/server.conf", + include_str!("nginx.conf"), + ) + .unwrap(); + + let big_file = vec![b'a'; 5 * 1024 * 1024]; + fs::write("target/nginx-tmp/basic/html/large.html", &big_file).unwrap(); + + let nginx_server = KillOnDrop(Some( + Command::new("tests/maybe-valgrind.sh") + .args([ + "nginx", + "-g", + &format!("error_log stderr {NGINX_LOG_LEVEL};"), + "-p", + "./target/nginx-tmp/basic", + "-c", + "server.conf", + ]) + .spawn() + .unwrap(), + )); + wait_for_port(8443); + + // basic single request + assert_eq!( + Command::new("curl") + .env("LD_LIBRARY_PATH", "") + .args(["--cacert", "test-ca/rsa/ca.cert", "https://localhost:8443/"]) + .stdout(Stdio::piped()) + .output() + .map(print_output) + .unwrap() + .stdout, + b"hello world\n" + ); + + // big download (throttled by curl to ensure non-blocking writes work) + assert_eq!( + Command::new("curl") + .env("LD_LIBRARY_PATH", "") + .args([ + "--cacert", + "test-ca/rsa/ca.cert", + "--limit-rate", + "1M", + "https://localhost:8443/large.html" + ]) + .stdout(Stdio::piped()) + .output() + .unwrap() + .stdout, + big_file + ); + + drop(nginx_server); +} + struct KillOnDrop(Option); impl KillOnDrop {