diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index b4fb3df..c5122a0 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: | :exclamation: [^stub] | | `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 91ee171..acf0ddc 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -20,6 +20,7 @@ use crate::bio::{Bio, BIO, BIO_METHOD}; use crate::callbacks::Callbacks; use crate::error::{ffi_panic_boundary, Error, MysteriouslyOppositeReturnValue}; use crate::evp_pkey::EvpPkey; +use crate::ex_data::ExData; use crate::ffi::{ 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, @@ -108,12 +109,28 @@ 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(Mutex::new(crate::SslContext::new(method))) + let out = to_arc_mut_ptr(Mutex::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 point must be valid and + // the lock must be non-poisoned. + clone_arc(out) + .unwrap() + .lock() + .map(|mut ctx| ctx.install_ex_data(ex_data)) + .unwrap(); + out } } @@ -131,6 +148,32 @@ entry! { } } +entry! { + pub fn _SSL_CTX_set_ex_data(ctx: *mut SSL_CTX, idx: c_int, data: *mut c_void) -> c_int { + let ctx = try_clone_arc!(ctx); + + match ctx + .lock() + .map_err(|_| Error::cannot_lock()) + .and_then(|mut ctx| ctx.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 { + let ctx = try_clone_arc!(ctx); + + ctx.lock() + .ok() + .map(|mut ctx| ctx.get_ex_data(idx)) + .unwrap_or_else(ptr::null_mut) + } +} + entry! { pub fn _SSL_CTX_get_options(ctx: *const SSL_CTX) -> u64 { let ctx = try_clone_arc!(ctx); @@ -695,7 +738,23 @@ entry! { None => return ptr::null_mut(), }; - to_arc_mut_ptr(Mutex::new(ssl)) + let out = to_arc_mut_ptr(Mutex::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 point must be valid and + // the lock must be non-poisoned. + clone_arc(out) + .unwrap() + .lock() + .map(|mut ssl| ssl.install_ex_data(ex_data)) + .unwrap(); + out } } @@ -713,6 +772,32 @@ entry! { } } +entry! { + pub fn _SSL_set_ex_data(ssl: *mut SSL, idx: c_int, data: *mut c_void) -> c_int { + let ssl = try_clone_arc!(ssl); + + match ssl + .lock() + .map_err(|_| Error::cannot_lock()) + .and_then(|mut ssl| ssl.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 { + let ssl = try_clone_arc!(ssl); + + ssl.lock() + .ok() + .map(|mut ssl| ssl.get_ex_data(idx)) + .unwrap_or_else(ptr::null_mut) + } +} + 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); @@ -1629,22 +1714,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_get_ex_data_X509_STORE_CTX_idx() -> 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 3f1bcf0..b11458f 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -33,6 +33,7 @@ mod constants; mod entry; mod error; mod evp_pkey; +mod ex_data; #[macro_use] #[allow(unused_macros, dead_code, unused_imports)] mod ffi; @@ -211,6 +212,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, @@ -230,6 +232,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, @@ -246,6 +249,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(&mut self, idx: c_int) -> *mut c_void { + self.ex_data.get(idx) + } + fn get_options(&self) -> u64 { self.raw_options } @@ -417,6 +432,7 @@ fn encode_alpn<'a>(iter: impl Iterator) -> Vec { struct Ssl { ctx: Arc>, + ex_data: ex_data::ExData, raw_options: u64, mode: ConnMode, verify_mode: VerifyMode, @@ -451,6 +467,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, @@ -473,6 +490,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(&mut 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 cb07f7c..6f6826d 100644 --- a/rustls-libssl/tests/server.c +++ b/rustls-libssl/tests/server.c @@ -47,6 +47,9 @@ static void state(const SSL *s) { SSL_in_init(s), SSL_is_init_finished(s)); } +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, @@ -54,6 +57,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, @@ -72,6 +77,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; } @@ -85,6 +92,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; } @@ -124,6 +133,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(); @@ -151,6 +167,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(); @@ -230,6 +252,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);