From d5d414b16fe13d65938acd6c601445e1b3e02b55 Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Thu, 17 May 2018 03:23:30 -0700 Subject: Expose max TLS1.3 early data accessors --- openssl/src/ssl/mod.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) (limited to 'openssl/src/ssl') diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 8dc605ed..33ffa737 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -1489,6 +1489,24 @@ impl SslContextBuilder { } } + /// Sets the maximum amount of early data that will be accepted on incoming connections. + /// + /// Defaults to 0. + /// + /// Requires OpenSSL 1.1.1 or newer. + /// + /// This corresponds to [`SSL_CTX_set_max_early_data`]. + /// + /// [`SSL_CTX_set_max_early_data`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_max_early_data.html + #[cfg(ossl111)] + pub fn set_max_early_data(&mut self, bytes: u32) -> Result<(), ErrorStack> { + if unsafe { ffi::SSL_CTX_set_max_early_data(self.as_ptr(), bytes) } == 1 { + Ok(()) + } else { + Err(ErrorStack::get()) + } + } + /// Consumes the builder, returning a new `SslContext`. pub fn build(self) -> SslContext { self.0 @@ -1643,6 +1661,18 @@ impl SslContextRef { } } } + + /// Gets the maximum amount of early data that will be accepted on incoming connections. + /// + /// Requires OpenSSL 1.1.1 or newer. + /// + /// This corresponds to [`SSL_CTX_get_max_early_data`]. + /// + /// [`SSL_CTX_get_max_early_data`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_get_max_early_data.html + #[cfg(ossl111)] + pub fn max_early_data(&self) -> u32 { + unsafe { ffi::SSL_CTX_get_max_early_data(self.as_ptr()) } + } } /// Information about the state of a cipher. @@ -1873,6 +1903,18 @@ impl SslSessionRef { unsafe { compat::SSL_SESSION_get_master_key(self.as_ptr(), buf.as_mut_ptr(), buf.len()) } } + /// Gets the maximum amount of early data that can be sent on this session. + /// + /// Requires OpenSSL 1.1.1 or newer. + /// + /// This corresponds to [`SSL_SESSION_get_max_early_data`]. + /// + /// [`SSL_SESSION_get_max_early_data`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_SESSION_get_max_early_data.html + #[cfg(ossl111)] + pub fn max_early_data(&self) -> u32 { + unsafe { ffi::SSL_SESSION_get_max_early_data(self.as_ptr()) } + } + to_der! { /// Serializes the session into a DER-encoded structure. /// @@ -2594,6 +2636,34 @@ impl SslRef { } } } + + /// Sets the maximum amount of early data that will be accepted on this connection. + /// + /// Requires OpenSSL 1.1.1 or newer. + /// + /// This corresponds to [`SSL_set_max_early_data`]. + /// + /// [`SSL_set_max_early_data`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_set_max_early_data.html + #[cfg(ossl111)] + pub fn set_max_early_data(&mut self, bytes: u32) -> Result<(), ErrorStack> { + if unsafe { ffi::SSL_set_max_early_data(self.as_ptr(), bytes) } == 1 { + Ok(()) + } else { + Err(ErrorStack::get()) + } + } + + /// Gets the maximum amount of early data that can be sent on this connection. + /// + /// Requires OpenSSL 1.1.1 or newer. + /// + /// This corresponds to [`SSL_get_max_early_data`]. + /// + /// [`SSL_get_max_early_data`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_get_max_early_data.html + #[cfg(ossl111)] + pub fn max_early_data(&self) -> u32 { + unsafe { ffi::SSL_get_max_early_data(self.as_ptr()) } + } } /// An SSL stream midway through the handshake process. -- cgit v1.2.3 From 69c75a178bbc70dd10d0d69ac8bf9e842cf4ff1f Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Thu, 17 May 2018 13:16:41 -0700 Subject: Expose early keying material export --- openssl/src/ssl/mod.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'openssl/src/ssl') diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 33ffa737..78ae2267 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -2498,6 +2498,33 @@ impl SslRef { } } + /// Derives keying material for application use in accordance to RFC 5705. + /// + /// Requires OpenSSL 1.1.1 or newer. + /// + /// This corresponds to [`SSL_export_keying_material_early`]. + /// + /// [`SSL_export_keying_material_early`]: https://www.openssl.org/docs/manmaster/man3/SSL_export_keying_material_early.html + #[cfg(ossl111)] + pub fn export_keying_material_early( + &self, + out: &mut [u8], + label: &str, + context: &[u8], + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_export_keying_material_early( + self.as_ptr(), + out.as_mut_ptr() as *mut c_uchar, + out.len(), + label.as_ptr() as *const c_char, + label.len(), + context.as_ptr() as *const c_uchar, + context.len(), + )).map(|_| ()) + } + } + /// Sets the session to be used. /// /// This should be called before the handshake to attempt to reuse a previously established -- cgit v1.2.3 From d991566f2b1b6803ad214fe6bf531d5870ab43fd Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 19 May 2018 19:43:02 -0700 Subject: Support min/max version in LibreSSL Their implementations of the accessors don't behave expected with no bounds, so we ignore those bits of the tests. --- openssl/src/ssl/mod.rs | 26 ++++++++++++++------------ openssl/src/ssl/test.rs | 8 ++++---- 2 files changed, 18 insertions(+), 16 deletions(-) (limited to 'openssl/src/ssl') diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 8dc605ed..adec060c 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -1023,15 +1023,15 @@ impl SslContextBuilder { /// /// This corresponds to [`SSL_CTX_set_min_proto_version`]. /// - /// Requires OpenSSL 1.1.0 or newer. + /// Requires OpenSSL 1.1.0 or LibreSSL 2.6.1 or newer. /// /// [`SSL_CTX_set_min_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html - #[cfg(any(ossl110))] + #[cfg(any(ossl110, libressl261))] pub fn set_min_proto_version(&mut self, version: Option) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_set_min_proto_version( self.as_ptr(), - version.map_or(0, |v| v.0), + version.map_or(0, |v| v.0 as _), )).map(|_| ()) } } @@ -1043,15 +1043,15 @@ impl SslContextBuilder { /// /// This corresponds to [`SSL_CTX_set_max_proto_version`]. /// - /// Requires OpenSSL 1.1.0 or newer. + /// Requires OpenSSL 1.1.0 or or LibreSSL 2.6.1 or newer. /// /// [`SSL_CTX_set_max_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html - #[cfg(any(ossl110))] + #[cfg(any(ossl110, libressl261))] pub fn set_max_proto_version(&mut self, version: Option) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_set_max_proto_version( self.as_ptr(), - version.map_or(0, |v| v.0), + version.map_or(0, |v| v.0 as _), )).map(|_| ()) } } @@ -1063,10 +1063,10 @@ impl SslContextBuilder { /// /// This corresponds to [`SSL_CTX_get_min_proto_version`]. /// - /// Requires OpenSSL 1.1.0g or newer. + /// Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer. /// /// [`SSL_CTX_get_min_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html - #[cfg(any(ossl110g))] + #[cfg(any(ossl110g, libressl270))] pub fn min_proto_version(&mut self) -> Option { unsafe { let r = ffi::SSL_CTX_get_min_proto_version(self.as_ptr()); @@ -1085,10 +1085,10 @@ impl SslContextBuilder { /// /// This corresponds to [`SSL_CTX_get_max_proto_version`]. /// - /// Requires OpenSSL 1.1.0g or newer. + /// Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer. /// /// [`SSL_CTX_get_max_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html - #[cfg(any(ossl110g))] + #[cfg(any(ossl110g, libressl270))] pub fn max_proto_version(&mut self) -> Option { unsafe { let r = ffi::SSL_CTX_get_max_proto_version(self.as_ptr()); @@ -2837,7 +2837,8 @@ impl Read for SslStream { } Err(ref e) if e.code() == ErrorCode::WANT_READ && e.io_error().is_none() => {} Err(e) => { - return Err(e.into_io_error() + return Err(e + .into_io_error() .unwrap_or_else(|e| io::Error::new(io::ErrorKind::Other, e))) } } @@ -2852,7 +2853,8 @@ impl Write for SslStream { Ok(n) => return Ok(n), Err(ref e) if e.code() == ErrorCode::WANT_READ && e.io_error().is_none() => {} Err(e) => { - return Err(e.into_io_error() + return Err(e + .into_io_error() .unwrap_or_else(|e| io::Error::new(io::ErrorKind::Other, e))) } } diff --git a/openssl/src/ssl/test.rs b/openssl/src/ssl/test.rs index 2e906728..b90199f0 100644 --- a/openssl/src/ssl/test.rs +++ b/openssl/src/ssl/test.rs @@ -19,7 +19,7 @@ use hash::MessageDigest; use ocsp::{OcspResponse, OcspResponseStatus}; use pkey::PKey; use ssl; -#[cfg(any(ossl110, ossl111))] +#[cfg(any(ossl110, ossl111, libressl261))] use ssl::SslVersion; use ssl::{ Error, HandshakeError, MidHandshakeSslStream, ShutdownResult, Ssl, SslAcceptor, SslConnector, @@ -1315,7 +1315,7 @@ fn keying_export() { } #[test] -#[cfg(any(ossl110))] +#[cfg(any(ossl110, libressl261))] fn no_version_overlap() { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); let addr = listener.local_addr().unwrap(); @@ -1330,7 +1330,7 @@ fn no_version_overlap() { ctx.set_max_proto_version(Some(SslVersion::TLS1_1)).unwrap(); #[cfg(ossl110g)] assert_eq!(ctx.min_proto_version(), None); - #[cfg(ossl110g)] + #[cfg(any(ossl110g, libressl270))] assert_eq!(ctx.max_proto_version(), Some(SslVersion::TLS1_1)); let ssl = Ssl::new(&ctx.build()).unwrap(); ssl.accept(stream).unwrap_err(); @@ -1339,7 +1339,7 @@ fn no_version_overlap() { let stream = TcpStream::connect(addr).unwrap(); let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); ctx.set_min_proto_version(Some(SslVersion::TLS1_2)).unwrap(); - #[cfg(ossl110g)] + #[cfg(any(ossl110g, libressl270))] assert_eq!(ctx.min_proto_version(), Some(SslVersion::TLS1_2)); #[cfg(ossl110g)] assert_eq!(ctx.max_proto_version(), None); -- cgit v1.2.3 From a6fcef01c0aa71359f583342c813b8db5835178d Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 20 May 2018 11:27:45 -0700 Subject: Overhaul openssl cfgs Also expose hostname verification on libressl --- openssl/src/ssl/bio.rs | 204 +++++++++++------------ openssl/src/ssl/callbacks.rs | 68 +++++--- openssl/src/ssl/connector.rs | 388 ++++++++++++++++++++++--------------------- openssl/src/ssl/mod.rs | 233 +++++++++++--------------- openssl/src/ssl/test.rs | 4 +- 5 files changed, 438 insertions(+), 459 deletions(-) (limited to 'openssl/src/ssl') diff --git a/openssl/src/ssl/bio.rs b/openssl/src/ssl/bio.rs index 4b792a75..1a149b6d 100644 --- a/openssl/src/ssl/bio.rs +++ b/openssl/src/ssl/bio.rs @@ -1,11 +1,13 @@ +use ffi::{ + self, BIO_clear_retry_flags, BIO_new, BIO_set_retry_read, BIO_set_retry_write, BIO, + BIO_CTRL_FLUSH, +}; use libc::{c_char, c_int, c_long, c_void, strlen}; -use ffi::{BIO, BIO_CTRL_FLUSH, BIO_new, BIO_clear_retry_flags, BIO_set_retry_read, - BIO_set_retry_write}; use std::any::Any; use std::io; use std::io::prelude::*; use std::mem; -use std::panic::{AssertUnwindSafe, catch_unwind}; +use std::panic::{catch_unwind, AssertUnwindSafe}; use std::ptr; use std::slice; @@ -19,11 +21,11 @@ pub struct StreamState { } /// Safe wrapper for BIO_METHOD -pub struct BioMethod(compat::BIO_METHOD); +pub struct BioMethod(BIO_METHOD); impl BioMethod { fn new() -> BioMethod { - BioMethod(compat::BIO_METHOD::new::()) + BioMethod(BIO_METHOD::new::()) } } @@ -41,8 +43,8 @@ pub fn new(stream: S) -> Result<(*mut BIO, BioMethod), ErrorSta unsafe { let bio = cvt_p(BIO_new(method.0.get()))?; - compat::BIO_set_data(bio, Box::into_raw(state) as *mut _); - compat::BIO_set_init(bio, 1); + BIO_set_data(bio, Box::into_raw(state) as *mut _); + BIO_set_init(bio, 1); return Ok((bio, method)); } @@ -59,7 +61,7 @@ pub unsafe fn take_panic(bio: *mut BIO) -> Option> { } pub unsafe fn get_ref<'a, S: 'a>(bio: *mut BIO) -> &'a S { - let state: &'a StreamState = mem::transmute(compat::BIO_get_data(bio)); + let state: &'a StreamState = mem::transmute(BIO_get_data(bio)); &state.stream } @@ -68,7 +70,7 @@ pub unsafe fn get_mut<'a, S: 'a>(bio: *mut BIO) -> &'a mut S { } unsafe fn state<'a, S: 'a>(bio: *mut BIO) -> &'a mut StreamState { - &mut *(compat::BIO_get_data(bio) as *mut _) + &mut *(BIO_get_data(bio) as *mut _) } unsafe extern "C" fn bwrite(bio: *mut BIO, buf: *const c_char, len: c_int) -> c_int { @@ -117,8 +119,7 @@ unsafe extern "C" fn bread(bio: *mut BIO, buf: *mut c_char, len: c_int) fn retriable_error(err: &io::Error) -> bool { match err.kind() { - io::ErrorKind::WouldBlock | - io::ErrorKind::NotConnected => true, + io::ErrorKind::WouldBlock | io::ErrorKind::NotConnected => true, _ => false, } } @@ -153,10 +154,10 @@ unsafe extern "C" fn ctrl( } unsafe extern "C" fn create(bio: *mut BIO) -> c_int { - compat::BIO_set_init(bio, 0); - compat::BIO_set_num(bio, 0); - compat::BIO_set_data(bio, ptr::null_mut()); - compat::BIO_set_flags(bio, 0); + BIO_set_init(bio, 0); + BIO_set_num(bio, 0); + BIO_set_data(bio, ptr::null_mut()); + BIO_set_flags(bio, 0); 1 } @@ -165,115 +166,110 @@ unsafe extern "C" fn destroy(bio: *mut BIO) -> c_int { return 0; } - let data = compat::BIO_get_data(bio); + let data = BIO_get_data(bio); assert!(!data.is_null()); Box::>::from_raw(data as *mut _); - compat::BIO_set_data(bio, ptr::null_mut()); - compat::BIO_set_init(bio, 0); + BIO_set_data(bio, ptr::null_mut()); + BIO_set_init(bio, 0); 1 } -#[cfg(ossl110)] -#[allow(bad_style)] -mod compat { - use std::io::{Read, Write}; - - use libc::c_int; - use ffi; - pub use ffi::{BIO_set_init, BIO_set_flags, BIO_set_data, BIO_get_data}; - - pub unsafe fn BIO_set_num(_bio: *mut ffi::BIO, _num: c_int) {} - - pub struct BIO_METHOD(*mut ffi::BIO_METHOD); - - impl BIO_METHOD { - pub fn new() -> BIO_METHOD { - unsafe { - let ptr = ffi::BIO_meth_new(ffi::BIO_TYPE_NONE, b"rust\0".as_ptr() as *const _); - assert!(!ptr.is_null()); - let ret = BIO_METHOD(ptr); - assert!(ffi::BIO_meth_set_write(ptr, super::bwrite::) != 0); - assert!(ffi::BIO_meth_set_read(ptr, super::bread::) != 0); - assert!(ffi::BIO_meth_set_puts(ptr, super::bputs::) != 0); - assert!(ffi::BIO_meth_set_ctrl(ptr, super::ctrl::) != 0); - assert!(ffi::BIO_meth_set_create(ptr, super::create) != 0); - assert!(ffi::BIO_meth_set_destroy(ptr, super::destroy::) != 0); - return ret; +cfg_if! { + if #[cfg(ossl110)] { + use ffi::{BIO_get_data, BIO_set_data, BIO_set_flags, BIO_set_init}; + + #[allow(bad_style)] + unsafe fn BIO_set_num(_bio: *mut ffi::BIO, _num: c_int) {} + + #[allow(bad_style)] + struct BIO_METHOD(*mut ffi::BIO_METHOD); + + impl BIO_METHOD { + fn new() -> BIO_METHOD { + unsafe { + let ptr = ffi::BIO_meth_new(ffi::BIO_TYPE_NONE, b"rust\0".as_ptr() as *const _); + assert!(!ptr.is_null()); + let ret = BIO_METHOD(ptr); + assert!(ffi::BIO_meth_set_write(ptr, bwrite::) != 0); + assert!(ffi::BIO_meth_set_read(ptr, bread::) != 0); + assert!(ffi::BIO_meth_set_puts(ptr, bputs::) != 0); + assert!(ffi::BIO_meth_set_ctrl(ptr, ctrl::) != 0); + assert!(ffi::BIO_meth_set_create(ptr, create) != 0); + assert!(ffi::BIO_meth_set_destroy(ptr, destroy::) != 0); + return ret; + } } - } - - pub fn get(&self) -> *mut ffi::BIO_METHOD { - self.0 - } - } - impl Drop for BIO_METHOD { - fn drop(&mut self) { - unsafe { - ffi::BIO_meth_free(self.0); + fn get(&self) -> *mut ffi::BIO_METHOD { + self.0 } } - } -} -#[cfg(ossl10x)] -#[allow(bad_style)] -mod compat { - use std::io::{Read, Write}; - - use ffi; - use libc::{c_int, c_void}; - - pub struct BIO_METHOD(*mut ffi::BIO_METHOD); - - impl BIO_METHOD { - pub fn new() -> BIO_METHOD { - let ptr = Box::new(ffi::BIO_METHOD { - type_: ffi::BIO_TYPE_NONE, - name: b"rust\0".as_ptr() as *const _, - bwrite: Some(super::bwrite::), - bread: Some(super::bread::), - bputs: Some(super::bputs::), - bgets: None, - ctrl: Some(super::ctrl::), - create: Some(super::create), - destroy: Some(super::destroy::), - callback_ctrl: None, - }); - - BIO_METHOD(Box::into_raw(ptr)) + impl Drop for BIO_METHOD { + fn drop(&mut self) { + unsafe { + ffi::BIO_meth_free(self.0); + } + } } + } else { + #[allow(bad_style)] + struct BIO_METHOD(*mut ffi::BIO_METHOD); + + impl BIO_METHOD { + fn new() -> BIO_METHOD { + let ptr = Box::new(ffi::BIO_METHOD { + type_: ffi::BIO_TYPE_NONE, + name: b"rust\0".as_ptr() as *const _, + bwrite: Some(bwrite::), + bread: Some(bread::), + bputs: Some(bputs::), + bgets: None, + ctrl: Some(ctrl::), + create: Some(create), + destroy: Some(destroy::), + callback_ctrl: None, + }); + + BIO_METHOD(Box::into_raw(ptr)) + } - pub fn get(&self) -> *mut ffi::BIO_METHOD { - self.0 + fn get(&self) -> *mut ffi::BIO_METHOD { + self.0 + } } - } - impl Drop for BIO_METHOD { - fn drop(&mut self) { - unsafe { - Box::::from_raw(self.0); + impl Drop for BIO_METHOD { + fn drop(&mut self) { + unsafe { + Box::::from_raw(self.0); + } } } - } - pub unsafe fn BIO_set_init(bio: *mut ffi::BIO, init: c_int) { - (*bio).init = init; - } + #[allow(bad_style)] + unsafe fn BIO_set_init(bio: *mut ffi::BIO, init: c_int) { + (*bio).init = init; + } - pub unsafe fn BIO_set_flags(bio: *mut ffi::BIO, flags: c_int) { - (*bio).flags = flags; - } + #[allow(bad_style)] + unsafe fn BIO_set_flags(bio: *mut ffi::BIO, flags: c_int) { + (*bio).flags = flags; + } - pub unsafe fn BIO_get_data(bio: *mut ffi::BIO) -> *mut c_void { - (*bio).ptr - } + #[allow(bad_style)] + unsafe fn BIO_get_data(bio: *mut ffi::BIO) -> *mut c_void { + (*bio).ptr + } - pub unsafe fn BIO_set_data(bio: *mut ffi::BIO, data: *mut c_void) { - (*bio).ptr = data; - } + #[allow(bad_style)] + unsafe fn BIO_set_data(bio: *mut ffi::BIO, data: *mut c_void) { + (*bio).ptr = data; + } - pub unsafe fn BIO_set_num(bio: *mut ffi::BIO, num: c_int) { - (*bio).num = num; + #[allow(bad_style)] + unsafe fn BIO_set_num(bio: *mut ffi::BIO, num: c_int) { + (*bio).num = num; + } } } diff --git a/openssl/src/ssl/callbacks.rs b/openssl/src/ssl/callbacks.rs index fd5b7ef5..6ec9aef9 100644 --- a/openssl/src/ssl/callbacks.rs +++ b/openssl/src/ssl/callbacks.rs @@ -13,11 +13,11 @@ use std::str; use std::sync::Arc; use dh::Dh; -#[cfg(any(ossl101, ossl102))] +#[cfg(all(ossl101, not(ossl110)))] use ec::EcKey; use error::ErrorStack; use pkey::Params; -#[cfg(any(ossl102, ossl110))] +#[cfg(ossl102)] use ssl::AlpnError; #[cfg(ossl111)] use ssl::ExtensionContext; @@ -37,7 +37,8 @@ where // raw pointer shenanigans to break the borrow of ctx // the callback can't mess with its own ex_data slot so this is safe - let verify = ctx.ex_data(ssl_idx) + let verify = ctx + .ex_data(ssl_idx) .expect("BUG: store context missing ssl") .ssl_context() .ex_data(verify_idx) @@ -66,7 +67,8 @@ where let ssl = SslRef::from_ptr_mut(ssl); let callback_idx = SslContext::cached_ex_index::(); - let callback = ssl.ssl_context() + let callback = ssl + .ssl_context() .ex_data(callback_idx) .expect("BUG: psk callback missing") as *const F; let hint = if hint != ptr::null() { @@ -96,7 +98,8 @@ where let ssl_idx = X509StoreContext::ssl_idx().expect("BUG: store context ssl index missing"); let callback_idx = Ssl::cached_ex_index::>(); - let callback = ctx.ex_data(ssl_idx) + let callback = ctx + .ex_data(ssl_idx) .expect("BUG: store context missing ssl") .ex_data(callback_idx) .expect("BUG: ssl verify callback missing") @@ -112,7 +115,8 @@ where { unsafe { let ssl = SslRef::from_ptr_mut(ssl); - let callback = ssl.ssl_context() + let callback = ssl + .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: sni callback missing") as *const F; let mut alert = SslAlert(*al); @@ -140,7 +144,8 @@ where { unsafe { let ssl = SslRef::from_ptr_mut(ssl); - let callback = ssl.ssl_context() + let callback = ssl + .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: alpn callback missing") as *const F; let protos = slice::from_raw_parts(inbuf as *const u8, inlen as usize); @@ -165,7 +170,8 @@ where F: Fn(&mut SslRef, bool, u32) -> Result, ErrorStack> + 'static + Sync + Send, { let ssl = SslRef::from_ptr_mut(ssl); - let callback = ssl.ssl_context() + let callback = ssl + .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: tmp dh callback missing") as *const F; @@ -182,7 +188,7 @@ where } } -#[cfg(any(ossl101, ossl102))] +#[cfg(all(ossl101, not(ossl110)))] pub unsafe extern "C" fn raw_tmp_ecdh( ssl: *mut ffi::SSL, is_export: c_int, @@ -192,7 +198,8 @@ where F: Fn(&mut SslRef, bool, u32) -> Result, ErrorStack> + 'static + Sync + Send, { let ssl = SslRef::from_ptr_mut(ssl); - let callback = ssl.ssl_context() + let callback = ssl + .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: tmp ecdh callback missing") as *const F; @@ -218,7 +225,8 @@ where F: Fn(&mut SslRef, bool, u32) -> Result, ErrorStack> + 'static + Sync + Send, { let ssl = SslRef::from_ptr_mut(ssl); - let callback = ssl.ex_data(Ssl::cached_ex_index::>()) + let callback = ssl + .ex_data(Ssl::cached_ex_index::>()) .expect("BUG: ssl tmp dh callback missing") .clone(); @@ -235,7 +243,7 @@ where } } -#[cfg(any(ossl101, ossl102))] +#[cfg(all(ossl101, not(ossl110)))] pub unsafe extern "C" fn raw_tmp_ecdh_ssl( ssl: *mut ffi::SSL, is_export: c_int, @@ -245,7 +253,8 @@ where F: Fn(&mut SslRef, bool, u32) -> Result, ErrorStack> + 'static + Sync + Send, { let ssl = SslRef::from_ptr_mut(ssl); - let callback = ssl.ex_data(Ssl::cached_ex_index::>()) + let callback = ssl + .ex_data(Ssl::cached_ex_index::>()) .expect("BUG: ssl tmp ecdh callback missing") .clone(); @@ -267,7 +276,8 @@ where F: Fn(&mut SslRef) -> Result + 'static + Sync + Send, { let ssl = SslRef::from_ptr_mut(ssl); - let callback = ssl.ssl_context() + let callback = ssl + .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: ocsp callback missing") as *const F; let ret = (*callback)(ssl); @@ -301,7 +311,8 @@ where F: Fn(&mut SslRef, SslSession) + 'static + Sync + Send, { let ssl = SslRef::from_ptr_mut(ssl); - let callback = ssl.ssl_context() + let callback = ssl + .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: new session callback missing") as *const F; let session = SslSession::from_ptr(session); @@ -319,7 +330,8 @@ pub unsafe extern "C" fn raw_remove_session( F: Fn(&SslContextRef, &SslSessionRef) + 'static + Sync + Send, { let ctx = SslContextRef::from_ptr(ctx); - let callback = ctx.ex_data(SslContext::cached_ex_index::()) + let callback = ctx + .ex_data(SslContext::cached_ex_index::()) .expect("BUG: remove session callback missing"); let session = SslSessionRef::from_ptr(session); @@ -341,7 +353,8 @@ where F: Fn(&mut SslRef, &[u8]) -> Option + 'static + Sync + Send, { let ssl = SslRef::from_ptr_mut(ssl); - let callback = ssl.ssl_context() + let callback = ssl + .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: get session callback missing") as *const F; let data = slice::from_raw_parts(data as *const u8, len as usize); @@ -363,7 +376,8 @@ where F: Fn(&SslRef, &str) + 'static + Sync + Send, { let ssl = SslRef::from_ptr(ssl as *mut _); - let callback = ssl.ssl_context() + let callback = ssl + .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: get session callback missing"); let line = CStr::from_ptr(line).to_bytes(); @@ -382,7 +396,8 @@ where F: Fn(&mut SslRef, &mut [u8]) -> Result + 'static + Sync + Send, { let ssl = SslRef::from_ptr_mut(ssl); - let callback = ssl.ssl_context() + let callback = ssl + .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: stateless cookie generate callback missing") as *const F; let slice = slice::from_raw_parts_mut(cookie as *mut u8, ffi::SSL_COOKIE_LENGTH as usize); @@ -408,7 +423,8 @@ where F: Fn(&mut SslRef, &[u8]) -> bool + 'static + Sync + Send, { let ssl = SslRef::from_ptr_mut(ssl); - let callback = ssl.ssl_context() + let callback = ssl + .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: stateless cookie verify callback missing") as *const F; let slice = slice::from_raw_parts(cookie as *const c_uchar as *const u8, cookie_len as usize); @@ -425,7 +441,8 @@ where { unsafe { let ssl = SslRef::from_ptr_mut(ssl); - let callback = ssl.ssl_context() + let callback = ssl + .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: cookie generate callback missing") as *const F; // We subtract 1 from DTLS1_COOKIE_LENGTH as the ostensible value, 256, is erroneous but retained for @@ -461,7 +478,8 @@ where { unsafe { let ssl = SslRef::from_ptr_mut(ssl); - let callback = ssl.ssl_context() + let callback = ssl + .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: cookie verify callback missing") as *const F; let slice = @@ -494,7 +512,8 @@ where { unsafe { let ssl = SslRef::from_ptr_mut(ssl); - let callback = ssl.ssl_context() + let callback = ssl + .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: custom ext add callback missing") as *const F; let ectx = ExtensionContext::from_bits_truncate(context); @@ -570,7 +589,8 @@ where { unsafe { let ssl = SslRef::from_ptr_mut(ssl); - let callback = ssl.ssl_context() + let callback = ssl + .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: custom ext parse callback missing") as *const F; let ectx = ExtensionContext::from_bits_truncate(context); diff --git a/openssl/src/ssl/connector.rs b/openssl/src/ssl/connector.rs index 9966a5a0..f10a0e23 100644 --- a/openssl/src/ssl/connector.rs +++ b/openssl/src/ssl/connector.rs @@ -3,16 +3,22 @@ use std::ops::{Deref, DerefMut}; use dh::Dh; use error::ErrorStack; -use ssl::{HandshakeError, Ssl, SslContext, SslContextBuilder, SslMethod, SslMode, SslOptions, - SslRef, SslStream, SslVerifyMode}; +use ssl::{ + HandshakeError, Ssl, SslContext, SslContextBuilder, SslMethod, SslMode, SslOptions, SslRef, + SslStream, SslVerifyMode, +}; use version; fn ctx(method: SslMethod) -> Result { let mut ctx = SslContextBuilder::new(method)?; - let mut opts = SslOptions::ALL | SslOptions::NO_COMPRESSION | SslOptions::NO_SSLV2 - | SslOptions::NO_SSLV3 | SslOptions::SINGLE_DH_USE - | SslOptions::SINGLE_ECDH_USE | SslOptions::CIPHER_SERVER_PREFERENCE; + let mut opts = SslOptions::ALL + | SslOptions::NO_COMPRESSION + | SslOptions::NO_SSLV2 + | SslOptions::NO_SSLV3 + | SslOptions::SINGLE_DH_USE + | SslOptions::SINGLE_ECDH_USE + | SslOptions::CIPHER_SERVER_PREFERENCE; opts &= !SslOptions::DONT_INSERT_EMPTY_FRAGMENTS; ctx.set_options(opts); @@ -23,7 +29,7 @@ fn ctx(method: SslMethod) -> Result { // This is quite a useful optimization for saving memory, but historically // caused CVEs in OpenSSL pre-1.0.1h, according to // https://bugs.python.org/issue25672 - if version::number() >= 0x1000108f { + if version::number() >= 0x1_00_01_08_0 { mode |= SslMode::RELEASE_BUFFERS; } @@ -277,226 +283,228 @@ impl DerefMut for SslAcceptorBuilder { } } -#[cfg(ossl101)] -fn setup_curves(ctx: &mut SslContextBuilder) -> Result<(), ErrorStack> { - use ec::EcKey; - use nid::Nid; - - let curve = EcKey::from_curve_name(Nid::X9_62_PRIME256V1)?; - ctx.set_tmp_ecdh(&curve) -} - -#[cfg(ossl102)] -fn setup_curves(ctx: &mut SslContextBuilder) -> Result<(), ErrorStack> { - ctx.set_ecdh_auto(true) -} - -#[cfg(ossl110)] -fn setup_curves(_: &mut SslContextBuilder) -> Result<(), ErrorStack> { - Ok(()) -} - -#[cfg(any(ossl102, ossl110))] -fn setup_verify(ctx: &mut SslContextBuilder) { - ctx.set_verify(SslVerifyMode::PEER); -} - -#[cfg(ossl101)] -fn setup_verify(ctx: &mut SslContextBuilder) { - ctx.set_verify_callback(SslVerifyMode::PEER, verify::verify_callback); -} - -#[cfg(any(ossl102, ossl110))] -fn setup_verify_hostname(ssl: &mut Ssl, domain: &str) -> Result<(), ErrorStack> { - use x509::verify::X509CheckFlags; +cfg_if! { + if #[cfg(ossl110)] { + fn setup_curves(_: &mut SslContextBuilder) -> Result<(), ErrorStack> { + Ok(()) + } + } else if #[cfg(any(ossl102, libressl))] { + fn setup_curves(ctx: &mut SslContextBuilder) -> Result<(), ErrorStack> { + ctx.set_ecdh_auto(true) + } + } else { + fn setup_curves(ctx: &mut SslContextBuilder) -> Result<(), ErrorStack> { + use ec::EcKey; + use nid::Nid; - let param = ssl.param_mut(); - param.set_hostflags(X509CheckFlags::NO_PARTIAL_WILDCARDS); - match domain.parse() { - Ok(ip) => param.set_ip(ip), - Err(_) => param.set_host(domain), + let curve = EcKey::from_curve_name(Nid::X9_62_PRIME256V1)?; + ctx.set_tmp_ecdh(&curve) + } } } -#[cfg(ossl101)] -fn setup_verify_hostname(ssl: &mut Ssl, domain: &str) -> Result<(), ErrorStack> { - let domain = domain.to_string(); - ssl.set_ex_data(*verify::HOSTNAME_IDX, domain); - Ok(()) -} - -#[cfg(ossl101)] -mod verify { - use std::net::IpAddr; - use std::str; - - use ex_data::Index; - use nid::Nid; - use x509::{GeneralName, X509NameRef, X509Ref, X509StoreContext, X509StoreContextRef, - X509VerifyResult}; - use stack::Stack; - use ssl::Ssl; +cfg_if! { + if #[cfg(any(ossl102, libressl261))] { + fn setup_verify(ctx: &mut SslContextBuilder) { + ctx.set_verify(SslVerifyMode::PEER); + } - lazy_static! { - pub static ref HOSTNAME_IDX: Index = Ssl::new_ex_index().unwrap(); - } + fn setup_verify_hostname(ssl: &mut SslRef, domain: &str) -> Result<(), ErrorStack> { + use x509::verify::X509CheckFlags; - pub fn verify_callback(preverify_ok: bool, x509_ctx: &mut X509StoreContextRef) -> bool { - if !preverify_ok || x509_ctx.error_depth() != 0 { - return preverify_ok; + let param = ssl.param_mut(); + param.set_hostflags(X509CheckFlags::NO_PARTIAL_WILDCARDS); + match domain.parse() { + Ok(ip) => param.set_ip(ip), + Err(_) => param.set_host(domain), + } + } + } else { + fn setup_verify(ctx: &mut SslContextBuilder) { + ctx.set_verify_callback(SslVerifyMode::PEER, verify::verify_callback); } - let ok = match ( - x509_ctx.current_cert(), - X509StoreContext::ssl_idx() - .ok() - .and_then(|idx| x509_ctx.ex_data(idx)) - .and_then(|ssl| ssl.ex_data(*HOSTNAME_IDX)), - ) { - (Some(x509), Some(domain)) => verify_hostname(domain, &x509), - _ => true, - }; - - if !ok { - x509_ctx.set_error(X509VerifyResult::APPLICATION_VERIFICATION); + fn setup_verify_hostname(ssl: &mut Ssl, domain: &str) -> Result<(), ErrorStack> { + let domain = domain.to_string(); + ssl.set_ex_data(*verify::HOSTNAME_IDX, domain); + Ok(()) } - ok - } + mod verify { + use std::net::IpAddr; + use std::str; + + use ex_data::Index; + use nid::Nid; + use ssl::Ssl; + use stack::Stack; + use x509::{ + GeneralName, X509NameRef, X509Ref, X509StoreContext, X509StoreContextRef, + X509VerifyResult, + }; + + lazy_static! { + pub static ref HOSTNAME_IDX: Index = Ssl::new_ex_index().unwrap(); + } - fn verify_hostname(domain: &str, cert: &X509Ref) -> bool { - match cert.subject_alt_names() { - Some(names) => verify_subject_alt_names(domain, names), - None => verify_subject_name(domain, &cert.subject_name()), - } - } + pub fn verify_callback(preverify_ok: bool, x509_ctx: &mut X509StoreContextRef) -> bool { + if !preverify_ok || x509_ctx.error_depth() != 0 { + return preverify_ok; + } + + let ok = match ( + x509_ctx.current_cert(), + X509StoreContext::ssl_idx() + .ok() + .and_then(|idx| x509_ctx.ex_data(idx)) + .and_then(|ssl| ssl.ex_data(*HOSTNAME_IDX)), + ) { + (Some(x509), Some(domain)) => verify_hostname(domain, &x509), + _ => true, + }; - fn verify_subject_alt_names(domain: &str, names: Stack) -> bool { - let ip = domain.parse(); + if !ok { + x509_ctx.set_error(X509VerifyResult::APPLICATION_VERIFICATION); + } + + ok + } - for name in &names { - match ip { - Ok(ip) => { - if let Some(actual) = name.ipaddress() { - if matches_ip(&ip, actual) { - return true; + fn verify_hostname(domain: &str, cert: &X509Ref) -> bool { + match cert.subject_alt_names() { + Some(names) => verify_subject_alt_names(domain, names), + None => verify_subject_name(domain, &cert.subject_name()), + } + } + + fn verify_subject_alt_names(domain: &str, names: Stack) -> bool { + let ip = domain.parse(); + + for name in &names { + match ip { + Ok(ip) => { + if let Some(actual) = name.ipaddress() { + if matches_ip(&ip, actual) { + return true; + } + } + } + Err(_) => { + if let Some(pattern) = name.dnsname() { + if matches_dns(pattern, domain) { + return true; + } + } } } } - Err(_) => { - if let Some(pattern) = name.dnsname() { - if matches_dns(pattern, domain) { - return true; + + false + } + + fn verify_subject_name(domain: &str, subject_name: &X509NameRef) -> bool { + match subject_name.entries_by_nid(Nid::COMMONNAME).next() { + Some(pattern) => { + let pattern = match str::from_utf8(pattern.data().as_slice()) { + Ok(pattern) => pattern, + Err(_) => return false, + }; + + // Unlike SANs, IP addresses in the subject name don't have a + // different encoding. + match domain.parse::() { + Ok(ip) => pattern + .parse::() + .ok() + .map_or(false, |pattern| pattern == ip), + Err(_) => matches_dns(pattern, domain), } } + None => false, } } - } - false - } - - fn verify_subject_name(domain: &str, subject_name: &X509NameRef) -> bool { - match subject_name.entries_by_nid(Nid::COMMONNAME).next() { - Some(pattern) => { - let pattern = match str::from_utf8(pattern.data().as_slice()) { - Ok(pattern) => pattern, - Err(_) => return false, - }; - - // Unlike SANs, IP addresses in the subject name don't have a - // different encoding. - match domain.parse::() { - Ok(ip) => pattern - .parse::() - .ok() - .map_or(false, |pattern| pattern == ip), - Err(_) => matches_dns(pattern, domain), + fn matches_dns(mut pattern: &str, mut hostname: &str) -> bool { + // first strip trailing . off of pattern and hostname to normalize + if pattern.ends_with('.') { + pattern = &pattern[..pattern.len() - 1]; } + if hostname.ends_with('.') { + hostname = &hostname[..hostname.len() - 1]; + } + + matches_wildcard(pattern, hostname).unwrap_or_else(|| pattern == hostname) } - None => false, - } - } - fn matches_dns(mut pattern: &str, mut hostname: &str) -> bool { - // first strip trailing . off of pattern and hostname to normalize - if pattern.ends_with('.') { - pattern = &pattern[..pattern.len() - 1]; - } - if hostname.ends_with('.') { - hostname = &hostname[..hostname.len() - 1]; - } + fn matches_wildcard(pattern: &str, hostname: &str) -> Option { + // internationalized domains can't involved in wildcards + if pattern.starts_with("xn--") { + return None; + } - matches_wildcard(pattern, hostname).unwrap_or_else(|| pattern == hostname) - } + let wildcard_location = match pattern.find('*') { + Some(l) => l, + None => return None, + }; - fn matches_wildcard(pattern: &str, hostname: &str) -> Option { - // internationalized domains can't involved in wildcards - if pattern.starts_with("xn--") { - return None; - } + let mut dot_idxs = pattern.match_indices('.').map(|(l, _)| l); + let wildcard_end = match dot_idxs.next() { + Some(l) => l, + None => return None, + }; - let wildcard_location = match pattern.find('*') { - Some(l) => l, - None => return None, - }; - - let mut dot_idxs = pattern.match_indices('.').map(|(l, _)| l); - let wildcard_end = match dot_idxs.next() { - Some(l) => l, - None => return None, - }; - - // Never match wildcards if the pattern has less than 2 '.'s (no *.com) - // - // This is a bit dubious, as it doesn't disallow other TLDs like *.co.uk. - // Chrome has a black- and white-list for this, but Firefox (via NSS) does - // the same thing we do here. - // - // The Public Suffix (https://www.publicsuffix.org/) list could - // potentially be used here, but it's both huge and updated frequently - // enough that management would be a PITA. - if dot_idxs.next().is_none() { - return None; - } + // Never match wildcards if the pattern has less than 2 '.'s (no *.com) + // + // This is a bit dubious, as it doesn't disallow other TLDs like *.co.uk. + // Chrome has a black- and white-list for this, but Firefox (via NSS) does + // the same thing we do here. + // + // The Public Suffix (https://www.publicsuffix.org/) list could + // potentially be used here, but it's both huge and updated frequently + // enough that management would be a PITA. + if dot_idxs.next().is_none() { + return None; + } - // Wildcards can only be in the first component - if wildcard_location > wildcard_end { - return None; - } + // Wildcards can only be in the first component + if wildcard_location > wildcard_end { + return None; + } - let hostname_label_end = match hostname.find('.') { - Some(l) => l, - None => return None, - }; + let hostname_label_end = match hostname.find('.') { + Some(l) => l, + None => return None, + }; - // check that the non-wildcard parts are identical - if pattern[wildcard_end..] != hostname[hostname_label_end..] { - return Some(false); - } + // check that the non-wildcard parts are identical + if pattern[wildcard_end..] != hostname[hostname_label_end..] { + return Some(false); + } - let wildcard_prefix = &pattern[..wildcard_location]; - let wildcard_suffix = &pattern[wildcard_location + 1..wildcard_end]; + let wildcard_prefix = &pattern[..wildcard_location]; + let wildcard_suffix = &pattern[wildcard_location + 1..wildcard_end]; - let hostname_label = &hostname[..hostname_label_end]; + let hostname_label = &hostname[..hostname_label_end]; - // check the prefix of the first label - if !hostname_label.starts_with(wildcard_prefix) { - return Some(false); - } + // check the prefix of the first label + if !hostname_label.starts_with(wildcard_prefix) { + return Some(false); + } - // and the suffix - if !hostname_label[wildcard_prefix.len()..].ends_with(wildcard_suffix) { - return Some(false); - } + // and the suffix + if !hostname_label[wildcard_prefix.len()..].ends_with(wildcard_suffix) { + return Some(false); + } - Some(true) - } + Some(true) + } - fn matches_ip(expected: &IpAddr, actual: &[u8]) -> bool { - match *expected { - IpAddr::V4(ref addr) => actual == addr.octets(), - IpAddr::V6(ref addr) => actual == addr.octets(), + fn matches_ip(expected: &IpAddr, actual: &[u8]) -> bool { + match *expected { + IpAddr::V4(ref addr) => actual == addr.octets(), + IpAddr::V6(ref addr) => actual == addr.octets(), + } + } } } } diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index adec060c..ce9c4b1d 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -78,7 +78,7 @@ use std::str; use std::sync::{Arc, Mutex}; use dh::{Dh, DhRef}; -#[cfg(any(ossl101, ossl102))] +#[cfg(all(ossl101, not(ossl110)))] use ec::EcKey; use ec::EcKeyRef; use error::ErrorStack; @@ -91,10 +91,10 @@ use ssl::bio::BioMethod; use ssl::callbacks::*; use ssl::error::InnerError; use stack::{Stack, StackRef}; -#[cfg(any(ossl102, ossl110))] +#[cfg(ossl102)] use x509::store::X509Store; use x509::store::{X509StoreBuilderRef, X509StoreRef}; -#[cfg(any(ossl102, ossl110))] +#[cfg(any(ossl102, libressl261))] use x509::verify::X509VerifyParamRef; use x509::{X509, X509Name, X509Ref, X509StoreContextRef, X509VerifyResult}; use {cvt, cvt_n, cvt_p, init}; @@ -284,7 +284,7 @@ impl SslMethod { /// This corresponds to `TLS_method` on OpenSSL 1.1.0 and `SSLv23_method` /// on OpenSSL 1.0.x. pub fn tls() -> SslMethod { - SslMethod(compat::tls_method()) + unsafe { SslMethod(TLS_method()) } } /// Support all versions of the DTLS protocol. @@ -292,7 +292,7 @@ impl SslMethod { /// This corresponds to `DTLS_method` on OpenSSL 1.1.0 and `DTLSv1_method` /// on OpenSSL 1.0.x. pub fn dtls() -> SslMethod { - SslMethod(compat::dtls_method()) + unsafe { SslMethod(DTLS_method()) } } /// Constructs an `SslMethod` from a pointer to the underlying OpenSSL value. @@ -767,7 +767,7 @@ impl SslContextBuilder { /// Requires OpenSSL 1.0.1 or 1.0.2. /// /// This corresponds to `SSL_CTX_set_tmp_ecdh_callback`. - #[cfg(any(ossl101, ossl102))] + #[cfg(all(ossl101, not(ossl110)))] pub fn set_tmp_ecdh_callback(&mut self, callback: F) where F: Fn(&mut SslRef, bool, u32) -> Result, ErrorStack> + 'static + Sync + Send, @@ -976,7 +976,7 @@ impl SslContextBuilder { /// This corresponds to [`SSL_CTX_set_ecdh_auto`]. /// /// [`SSL_CTX_set_ecdh_auto`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_ecdh_auto.html - #[cfg(any(ossl102, libressl))] + #[cfg(any(libressl, all(ossl102, not(ossl110))))] pub fn set_ecdh_auto(&mut self, onoff: bool) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_set_ecdh_auto(self.as_ptr(), onoff as c_int)).map(|_| ()) } } @@ -992,7 +992,7 @@ impl SslContextBuilder { /// /// [`SSL_CTX_set_options`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_options.html pub fn set_options(&mut self, option: SslOptions) -> SslOptions { - let bits = unsafe { compat::SSL_CTX_set_options(self.as_ptr(), option.bits()) }; + let bits = unsafe { ffi::SSL_CTX_set_options(self.as_ptr(), option.bits()) }; SslOptions { bits } } @@ -1002,7 +1002,7 @@ impl SslContextBuilder { /// /// [`SSL_CTX_get_options`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_options.html pub fn options(&self) -> SslOptions { - let bits = unsafe { compat::SSL_CTX_get_options(self.as_ptr()) }; + let bits = unsafe { ffi::SSL_CTX_get_options(self.as_ptr()) }; SslOptions { bits } } @@ -1012,7 +1012,7 @@ impl SslContextBuilder { /// /// [`SSL_CTX_clear_options`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_options.html pub fn clear_options(&mut self, option: SslOptions) -> SslOptions { - let bits = unsafe { compat::SSL_CTX_clear_options(self.as_ptr(), option.bits()) }; + let bits = unsafe { ffi::SSL_CTX_clear_options(self.as_ptr(), option.bits()) }; SslOptions { bits } } @@ -1514,7 +1514,7 @@ foreign_type_and_impl_send_sync! { impl Clone for SslContext { fn clone(&self) -> Self { unsafe { - compat::SSL_CTX_up_ref(self.as_ptr()); + SSL_CTX_up_ref(self.as_ptr()); SslContext::from_ptr(self.as_ptr()) } } @@ -1547,7 +1547,7 @@ impl SslContext { { unsafe { ffi::init(); - let idx = cvt_n(compat::get_new_idx(free_data_box::))?; + let idx = cvt_n(get_new_idx(free_data_box::))?; Ok(Index::from_raw(idx)) } } @@ -1833,7 +1833,7 @@ impl ToOwned for SslSessionRef { fn to_owned(&self) -> SslSession { unsafe { - compat::SSL_SESSION_up_ref(self.as_ptr()); + SSL_SESSION_up_ref(self.as_ptr()); SslSession(self.as_ptr()) } } @@ -1859,7 +1859,7 @@ impl SslSessionRef { /// /// [`SSL_SESSION_get_master_key`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_SESSION_get_master_key.html pub fn master_key_len(&self) -> usize { - unsafe { compat::SSL_SESSION_get_master_key(self.as_ptr(), ptr::null_mut(), 0) } + unsafe { SSL_SESSION_get_master_key(self.as_ptr(), ptr::null_mut(), 0) } } /// Copies the master key into the provided buffer. @@ -1870,7 +1870,7 @@ impl SslSessionRef { /// /// [`SSL_SESSION_get_master_key`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_SESSION_get_master_key.html pub fn master_key(&self, buf: &mut [u8]) -> usize { - unsafe { compat::SSL_SESSION_get_master_key(self.as_ptr(), buf.as_mut_ptr(), buf.len()) } + unsafe { SSL_SESSION_get_master_key(self.as_ptr(), buf.as_mut_ptr(), buf.len()) } } to_der! { @@ -1926,7 +1926,7 @@ impl Ssl { { unsafe { ffi::init(); - let idx = cvt_n(compat::get_new_ssl_idx(free_data_box::))?; + let idx = cvt_n(get_new_ssl_idx(free_data_box::))?; Ok(Index::from_raw(idx)) } } @@ -2091,7 +2091,7 @@ impl SslRef { /// This corresponds to `SSL_set_tmp_ecdh_callback`. /// /// [`SslContextBuilder::set_tmp_ecdh_callback`]: struct.SslContextBuilder.html#method.set_tmp_ecdh_callback - #[cfg(any(ossl101, ossl102))] + #[cfg(any(all(ossl101, not(ossl110))))] pub fn set_tmp_ecdh_callback(&mut self, callback: F) where F: Fn(&mut SslRef, bool, u32) -> Result, ErrorStack> + 'static + Sync + Send, @@ -2111,7 +2111,7 @@ impl SslRef { /// /// [`SslContextBuilder::set_tmp_ecdh`]: struct.SslContextBuilder.html#method.set_tmp_ecdh /// [`SSL_set_ecdh_auto`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_ecdh_auto.html - #[cfg(ossl102)] + #[cfg(all(ossl102, not(ossl110)))] pub fn set_ecdh_auto(&mut self, onoff: bool) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_set_ecdh_auto(self.as_ptr(), onoff as c_int)).map(|_| ()) } } @@ -2363,7 +2363,7 @@ impl SslRef { /// This corresponds to [`SSL_get0_param`]. /// /// [`SSL_get0_param`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_get0_param.html - #[cfg(any(ossl102, ossl110))] + #[cfg(any(ossl102, libressl261))] pub fn param_mut(&mut self) -> &mut X509VerifyParamRef { unsafe { X509VerifyParamRef::from_ptr_mut(ffi::SSL_get0_param(self.as_ptr())) } } @@ -2541,7 +2541,7 @@ impl SslRef { /// /// [`SSL_is_server`]: https://www.openssl.org/docs/manmaster/man3/SSL_is_server.html pub fn is_server(&self) -> bool { - unsafe { compat::SSL_is_server(self.as_ptr()) != 0 } + unsafe { SSL_is_server(self.as_ptr()) != 0 } } /// Sets the extra data at the specified index. @@ -2987,133 +2987,88 @@ pub enum ShutdownResult { Received, } -#[cfg(ossl110)] -mod compat { - use std::ptr; - - use ffi; - use libc::c_int; - - pub use ffi::{ - SSL_CTX_clear_options, SSL_CTX_get_options, SSL_CTX_set_options, SSL_CTX_up_ref, - SSL_SESSION_get_master_key, SSL_SESSION_up_ref, SSL_is_server, - }; - - pub unsafe fn get_new_idx(f: ffi::CRYPTO_EX_free) -> c_int { - ffi::CRYPTO_get_ex_new_index( - ffi::CRYPTO_EX_INDEX_SSL_CTX, - 0, - ptr::null_mut(), - None, - None, - Some(f), - ) - } - - pub unsafe fn get_new_ssl_idx(f: ffi::CRYPTO_EX_free) -> c_int { - ffi::CRYPTO_get_ex_new_index( - ffi::CRYPTO_EX_INDEX_SSL, - 0, - ptr::null_mut(), - None, - None, - Some(f), - ) - } - - pub fn tls_method() -> *const ffi::SSL_METHOD { - unsafe { ffi::TLS_method() } - } - - pub fn dtls_method() -> *const ffi::SSL_METHOD { - unsafe { ffi::DTLS_method() } - } -} - -#[cfg(ossl10x)] -#[allow(bad_style)] -mod compat { - use std::ptr; - - use ffi; - use libc::{self, c_int, c_long, c_uchar, c_ulong, size_t}; - - pub unsafe fn SSL_CTX_get_options(ctx: *const ffi::SSL_CTX) -> c_ulong { - ffi::SSL_CTX_ctrl(ctx as *mut _, ffi::SSL_CTRL_OPTIONS, 0, ptr::null_mut()) as c_ulong - } - - pub unsafe fn SSL_CTX_set_options(ctx: *const ffi::SSL_CTX, op: c_ulong) -> c_ulong { - ffi::SSL_CTX_ctrl( - ctx as *mut _, - ffi::SSL_CTRL_OPTIONS, - op as c_long, - ptr::null_mut(), - ) as c_ulong - } - - pub unsafe fn SSL_CTX_clear_options(ctx: *const ffi::SSL_CTX, op: c_ulong) -> c_ulong { - ffi::SSL_CTX_ctrl( - ctx as *mut _, - ffi::SSL_CTRL_CLEAR_OPTIONS, - op as c_long, - ptr::null_mut(), - ) as c_ulong - } - - pub unsafe fn get_new_idx(f: ffi::CRYPTO_EX_free) -> c_int { - ffi::SSL_CTX_get_ex_new_index(0, ptr::null_mut(), None, None, Some(f)) - } +cfg_if! { + if #[cfg(ossl110)] { + use ffi::{ + SSL_CTX_up_ref, + SSL_SESSION_get_master_key, SSL_SESSION_up_ref, SSL_is_server, TLS_method, DTLS_method, + }; - pub unsafe fn get_new_ssl_idx(f: ffi::CRYPTO_EX_free) -> c_int { - ffi::SSL_get_ex_new_index(0, ptr::null_mut(), None, None, Some(f)) - } + pub unsafe fn get_new_idx(f: ffi::CRYPTO_EX_free) -> c_int { + ffi::CRYPTO_get_ex_new_index( + ffi::CRYPTO_EX_INDEX_SSL_CTX, + 0, + ptr::null_mut(), + None, + None, + Some(f), + ) + } - pub unsafe fn SSL_CTX_up_ref(ssl: *mut ffi::SSL_CTX) -> libc::c_int { - ffi::CRYPTO_add_lock( - &mut (*ssl).references, - 1, - ffi::CRYPTO_LOCK_SSL_CTX, - "mod.rs\0".as_ptr() as *const _, - line!() as libc::c_int, - ); - 0 - } + pub unsafe fn get_new_ssl_idx(f: ffi::CRYPTO_EX_free) -> c_int { + ffi::CRYPTO_get_ex_new_index( + ffi::CRYPTO_EX_INDEX_SSL, + 0, + ptr::null_mut(), + None, + None, + Some(f), + ) + } + } else { + use ffi::{SSLv23_method as TLS_method, DTLSv1_method as DTLS_method}; - pub unsafe fn SSL_SESSION_get_master_key( - session: *const ffi::SSL_SESSION, - out: *mut c_uchar, - mut outlen: size_t, - ) -> size_t { - if outlen == 0 { - return (*session).master_key_length as size_t; + pub unsafe fn get_new_idx(f: ffi::CRYPTO_EX_free) -> c_int { + ffi::SSL_CTX_get_ex_new_index(0, ptr::null_mut(), None, None, Some(f)) } - if outlen > (*session).master_key_length as size_t { - outlen = (*session).master_key_length as size_t; + + pub unsafe fn get_new_ssl_idx(f: ffi::CRYPTO_EX_free) -> c_int { + ffi::SSL_get_ex_new_index(0, ptr::null_mut(), None, None, Some(f)) } - ptr::copy_nonoverlapping((*session).master_key.as_ptr(), out, outlen); - outlen - } - pub fn tls_method() -> *const ffi::SSL_METHOD { - unsafe { ffi::SSLv23_method() } - } + #[allow(bad_style)] + pub unsafe fn SSL_CTX_up_ref(ssl: *mut ffi::SSL_CTX) -> c_int { + ffi::CRYPTO_add_lock( + &mut (*ssl).references, + 1, + ffi::CRYPTO_LOCK_SSL_CTX, + "mod.rs\0".as_ptr() as *const _, + line!() as c_int, + ); + 0 + } - pub fn dtls_method() -> *const ffi::SSL_METHOD { - unsafe { ffi::DTLSv1_method() } - } + #[allow(bad_style)] + pub unsafe fn SSL_SESSION_get_master_key( + session: *const ffi::SSL_SESSION, + out: *mut c_uchar, + mut outlen: usize, + ) -> usize { + if outlen == 0 { + return (*session).master_key_length as usize; + } + if outlen > (*session).master_key_length as usize { + outlen = (*session).master_key_length as usize; + } + ptr::copy_nonoverlapping((*session).master_key.as_ptr(), out, outlen); + outlen + } - pub unsafe fn SSL_is_server(s: *mut ffi::SSL) -> c_int { - (*s).server - } + #[allow(bad_style)] + pub unsafe fn SSL_is_server(s: *mut ffi::SSL) -> c_int { + (*s).server + } - pub unsafe fn SSL_SESSION_up_ref(ses: *mut ffi::SSL_SESSION) -> c_int { - ffi::CRYPTO_add_lock( - &mut (*ses).references, - 1, - ffi::CRYPTO_LOCK_SSL_CTX, - "mod.rs\0".as_ptr() as *const _, - line!() as libc::c_int, - ); - 0 + #[allow(bad_style)] + pub unsafe fn SSL_SESSION_up_ref(ses: *mut ffi::SSL_SESSION) -> c_int { + ffi::CRYPTO_add_lock( + &mut (*ses).references, + 1, + ffi::CRYPTO_LOCK_SSL_CTX, + "mod.rs\0".as_ptr() as *const _, + line!() as c_int, + ); + 0 + } } } diff --git a/openssl/src/ssl/test.rs b/openssl/src/ssl/test.rs index b90199f0..f2dc4a65 100644 --- a/openssl/src/ssl/test.rs +++ b/openssl/src/ssl/test.rs @@ -1063,7 +1063,7 @@ fn tmp_dh_callback() { } #[test] -#[cfg(any(all(ossl101, not(libressl)), ossl102))] +#[cfg(all(ossl101, not(ossl110)))] fn tmp_ecdh_callback() { use ec::EcKey; use nid::Nid; @@ -1137,7 +1137,7 @@ fn tmp_dh_callback_ssl() { } #[test] -#[cfg(any(all(ossl101, not(libressl)), ossl102))] +#[cfg(all(ossl101, not(ossl110)))] fn tmp_ecdh_callback_ssl() { use ec::EcKey; use nid::Nid; -- cgit v1.2.3 From 4c1fdf1d81e20ee2130e883bb9065af0d1d4de2a Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 20 May 2018 12:52:49 -0700 Subject: Support ALPN on libressl Closes #690 --- openssl/src/ssl/callbacks.rs | 9 ++++++--- openssl/src/ssl/mod.rs | 19 ++++++++++--------- openssl/src/ssl/test.rs | 10 +++++----- 3 files changed, 21 insertions(+), 17 deletions(-) (limited to 'openssl/src/ssl') diff --git a/openssl/src/ssl/callbacks.rs b/openssl/src/ssl/callbacks.rs index 6ec9aef9..b23c60e7 100644 --- a/openssl/src/ssl/callbacks.rs +++ b/openssl/src/ssl/callbacks.rs @@ -1,9 +1,12 @@ use ffi; use foreign_types::ForeignType; use foreign_types::ForeignTypeRef; +#[cfg(not(osslconf = "OPENSSL_NO_PSK"))] +use libc::c_char; #[cfg(ossl111)] use libc::size_t; -use libc::{c_char, c_int, c_uchar, c_uint, c_void}; +use libc::{c_int, c_uchar, c_uint, c_void}; +#[cfg(not(osslconf = "OPENSSL_NO_PSK"))] use std::ffi::CStr; use std::mem; use std::ptr; @@ -17,7 +20,7 @@ use dh::Dh; use ec::EcKey; use error::ErrorStack; use pkey::Params; -#[cfg(ossl102)] +#[cfg(any(ossl102, libressl261))] use ssl::AlpnError; #[cfg(ossl111)] use ssl::ExtensionContext; @@ -130,7 +133,7 @@ where } } -#[cfg(any(ossl102, ossl110))] +#[cfg(any(ossl102, libressl261))] pub extern "C" fn raw_alpn_select( ssl: *mut ffi::SSL, out: *mut *const c_uchar, diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index ce9c4b1d..08475888 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -85,6 +85,7 @@ use error::ErrorStack; use ex_data::Index; #[cfg(ossl111)] use hash::MessageDigest; +#[cfg(ossl110)] use nid::Nid; use pkey::{HasPrivate, PKeyRef, Params, Private}; use ssl::bio::BioMethod; @@ -506,12 +507,12 @@ impl SslAlert { /// An error returned from an ALPN selection callback. /// -/// Requires OpenSSL 1.0.2 or newer. -#[cfg(any(ossl102, ossl110))] +/// Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer. +#[cfg(any(ossl102, libressl261))] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct AlpnError(c_int); -#[cfg(any(ossl102, ossl110))] +#[cfg(any(ossl102, libressl261))] impl AlpnError { /// Terminate the handshake with a fatal alert. /// @@ -1109,10 +1110,10 @@ impl SslContextBuilder { /// /// This corresponds to [`SSL_CTX_set_alpn_protos`]. /// - /// Requires OpenSSL 1.0.2 or newer. + /// Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer. /// /// [`SSL_CTX_set_alpn_protos`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_alpn_protos.html - #[cfg(any(ossl102, ossl110))] + #[cfg(any(ossl102, libressl261))] pub fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> { unsafe { assert!(protocols.len() <= c_uint::max_value() as usize); @@ -1140,12 +1141,12 @@ impl SslContextBuilder { /// /// This corresponds to [`SSL_CTX_set_alpn_select_cb`]. /// - /// Requires OpenSSL 1.0.2 or newer. + /// Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer. /// /// [`SslContextBuilder::set_alpn_protos`]: struct.SslContextBuilder.html#method.set_alpn_protos /// [`select_next_proto`]: fn.select_next_proto.html /// [`SSL_CTX_set_alpn_select_cb`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_alpn_protos.html - #[cfg(any(ossl102, ossl110))] + #[cfg(any(ossl102, libressl261))] pub fn set_alpn_select_callback(&mut self, callback: F) where F: for<'a> Fn(&mut SslRef, &'a [u8]) -> Result<&'a [u8], AlpnError> + 'static + Sync + Send, @@ -2283,12 +2284,12 @@ impl SslRef { /// The protocol's name is returned is an opaque sequence of bytes. It is up to the client /// to interpret it. /// - /// Requires OpenSSL 1.0.2 or newer. + /// Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer. /// /// This corresponds to [`SSL_get0_alpn_selected`]. /// /// [`SSL_get0_alpn_selected`]: https://www.openssl.org/docs/manmaster/man3/SSL_get0_next_proto_negotiated.html - #[cfg(any(ossl102, ossl110))] + #[cfg(any(ossl102, libressl261))] pub fn selected_alpn_protocol(&self) -> Option<&[u8]> { unsafe { let mut data: *const c_uchar = ptr::null(); diff --git a/openssl/src/ssl/test.rs b/openssl/src/ssl/test.rs index f2dc4a65..0d418d2c 100644 --- a/openssl/src/ssl/test.rs +++ b/openssl/src/ssl/test.rs @@ -481,7 +481,7 @@ fn test_state() { /// Tests that connecting with the client using ALPN, but the server not does not /// break the existing connection behavior. #[test] -#[cfg(any(ossl102, ossl110))] +#[cfg(any(ossl102, libressl261))] fn test_connect_with_unilateral_alpn() { let (_s, stream) = Server::new(); let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); @@ -503,7 +503,7 @@ fn test_connect_with_unilateral_alpn() { /// Tests that when both the client as well as the server use ALPN and their /// lists of supported protocols have an overlap, the correct protocol is chosen. #[test] -#[cfg(any(ossl102, ossl110))] +#[cfg(any(ossl102, libressl261))] fn test_connect_with_alpn_successful_multiple_matching() { let (_s, stream) = Server::new_alpn(); let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); @@ -526,7 +526,7 @@ fn test_connect_with_alpn_successful_multiple_matching() { /// lists of supported protocols have an overlap -- with only ONE protocol /// being valid for both. #[test] -#[cfg(any(ossl102, ossl110))] +#[cfg(any(ossl102, libressl261))] fn test_connect_with_alpn_successful_single_match() { let (_s, stream) = Server::new_alpn(); let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); @@ -548,7 +548,7 @@ fn test_connect_with_alpn_successful_single_match() { /// Tests that when the `SslStream` is created as a server stream, the protocols /// are correctly advertised to the client. #[test] -#[cfg(any(ossl102, ossl110))] +#[cfg(any(ossl102, libressl261))] fn test_alpn_server_advertise_multiple() { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); let localhost = listener.local_addr().unwrap(); @@ -624,7 +624,7 @@ fn test_alpn_server_select_none_fatal() { } #[test] -#[cfg(any(ossl102, ossl110))] +#[cfg(any(ossl102, libressl261))] fn test_alpn_server_select_none() { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); let localhost = listener.local_addr().unwrap(); -- cgit v1.2.3 From f0347fbce8b09754bedd187cbb8a9c12d17ac92d Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 20 May 2018 19:37:19 -0700 Subject: Improve error Display impls --- openssl/src/ssl/error.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'openssl/src/ssl') diff --git a/openssl/src/ssl/error.rs b/openssl/src/ssl/error.rs index 18e44cd6..b7e29c15 100644 --- a/openssl/src/ssl/error.rs +++ b/openssl/src/ssl/error.rs @@ -99,17 +99,18 @@ impl fmt::Display for Error { Some(_) => fmt.write_str("a nonblocking read call would have blocked"), None => fmt.write_str("the operation should be retried"), }, + ErrorCode::WANT_WRITE => match self.io_error() { + Some(_) => fmt.write_str("a nonblocking write call would have blocked"), + None => fmt.write_str("the operation should be retried"), + }, ErrorCode::SYSCALL => match self.io_error() { - Some(err) => write!(fmt, "the inner stream returned an error: {}", err), + Some(err) => write!(fmt, "{}", err), None => fmt.write_str("unexpected EOF"), }, - ErrorCode::SSL => { - fmt.write_str("OpenSSL error")?; - if let Some(ref err) = self.ssl_error() { - write!(fmt, ": {}", err)? - } - Ok(()) - } + ErrorCode::SSL => match self.ssl_error() { + Some(e) => write!(fmt, "{}", e), + None => fmt.write_str("OpenSSL error"), + }, ErrorCode(code) => write!(fmt, "unknown error code {}", code), } } -- cgit v1.2.3 From b976b5fd52b62ecbd447551c562c2524f7f071da Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 20 May 2018 20:47:00 -0700 Subject: Move proto version accessors to SslContextRef Add a Derf impl for SslContextBuilder so existing use still works. --- openssl/src/ssl/mod.rs | 96 +++++++++++++++++++++++++++----------------------- 1 file changed, 52 insertions(+), 44 deletions(-) (limited to 'openssl/src/ssl') diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 08475888..e62c1dc9 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -1057,50 +1057,6 @@ impl SslContextBuilder { } } - /// Gets the minimum supported protocol version. - /// - /// A value of `None` indicates that all versions down the the lowest version supported by - /// OpenSSL are enabled. - /// - /// This corresponds to [`SSL_CTX_get_min_proto_version`]. - /// - /// Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer. - /// - /// [`SSL_CTX_get_min_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html - #[cfg(any(ossl110g, libressl270))] - pub fn min_proto_version(&mut self) -> Option { - unsafe { - let r = ffi::SSL_CTX_get_min_proto_version(self.as_ptr()); - if r == 0 { - None - } else { - Some(SslVersion(r)) - } - } - } - - /// Gets the maximum supported protocol version. - /// - /// A value of `None` indicates that all versions down the the highest version supported by - /// OpenSSL are enabled. - /// - /// This corresponds to [`SSL_CTX_get_max_proto_version`]. - /// - /// Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer. - /// - /// [`SSL_CTX_get_max_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html - #[cfg(any(ossl110g, libressl270))] - pub fn max_proto_version(&mut self) -> Option { - unsafe { - let r = ffi::SSL_CTX_get_max_proto_version(self.as_ptr()); - if r == 0 { - None - } else { - Some(SslVersion(r)) - } - } - } - /// Sets the protocols to sent to the server for Application Layer Protocol Negotiation (ALPN). /// /// The input must be in ALPN "wire format". It consists of a sequence of supported protocol @@ -1496,6 +1452,14 @@ impl SslContextBuilder { } } +impl Deref for SslContextBuilder { + type Target = SslContextRef; + + fn deref(&self) -> &SslContextRef { + &self.0 + } +} + foreign_type_and_impl_send_sync! { type CType = ffi::SSL_CTX; fn drop = ffi::SSL_CTX_free; @@ -1644,6 +1608,50 @@ impl SslContextRef { } } } + + /// Gets the minimum supported protocol version. + /// + /// A value of `None` indicates that all versions down the the lowest version supported by + /// OpenSSL are enabled. + /// + /// This corresponds to [`SSL_CTX_get_min_proto_version`]. + /// + /// Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer. + /// + /// [`SSL_CTX_get_min_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html + #[cfg(any(ossl110g, libressl270))] + pub fn min_proto_version(&self) -> Option { + unsafe { + let r = ffi::SSL_CTX_get_min_proto_version(self.as_ptr()); + if r == 0 { + None + } else { + Some(SslVersion(r)) + } + } + } + + /// Gets the maximum supported protocol version. + /// + /// A value of `None` indicates that all versions down the the highest version supported by + /// OpenSSL are enabled. + /// + /// This corresponds to [`SSL_CTX_get_max_proto_version`]. + /// + /// Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer. + /// + /// [`SSL_CTX_get_max_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html + #[cfg(any(ossl110g, libressl270))] + pub fn max_proto_version(&self) -> Option { + unsafe { + let r = ffi::SSL_CTX_get_max_proto_version(self.as_ptr()); + if r == 0 { + None + } else { + Some(SslVersion(r)) + } + } + } } /// Information about the state of a cipher. -- cgit v1.2.3 From 7a7f98a32c4b7d9bc4b9b6d60e13764a0e4e6d7e Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 20 May 2018 20:55:20 -0700 Subject: Revert "Move proto version accessors to SslContextRef" --- openssl/src/ssl/mod.rs | 96 +++++++++++++++++++++++--------------------------- 1 file changed, 44 insertions(+), 52 deletions(-) (limited to 'openssl/src/ssl') diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index e62c1dc9..08475888 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -1057,6 +1057,50 @@ impl SslContextBuilder { } } + /// Gets the minimum supported protocol version. + /// + /// A value of `None` indicates that all versions down the the lowest version supported by + /// OpenSSL are enabled. + /// + /// This corresponds to [`SSL_CTX_get_min_proto_version`]. + /// + /// Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer. + /// + /// [`SSL_CTX_get_min_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html + #[cfg(any(ossl110g, libressl270))] + pub fn min_proto_version(&mut self) -> Option { + unsafe { + let r = ffi::SSL_CTX_get_min_proto_version(self.as_ptr()); + if r == 0 { + None + } else { + Some(SslVersion(r)) + } + } + } + + /// Gets the maximum supported protocol version. + /// + /// A value of `None` indicates that all versions down the the highest version supported by + /// OpenSSL are enabled. + /// + /// This corresponds to [`SSL_CTX_get_max_proto_version`]. + /// + /// Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer. + /// + /// [`SSL_CTX_get_max_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html + #[cfg(any(ossl110g, libressl270))] + pub fn max_proto_version(&mut self) -> Option { + unsafe { + let r = ffi::SSL_CTX_get_max_proto_version(self.as_ptr()); + if r == 0 { + None + } else { + Some(SslVersion(r)) + } + } + } + /// Sets the protocols to sent to the server for Application Layer Protocol Negotiation (ALPN). /// /// The input must be in ALPN "wire format". It consists of a sequence of supported protocol @@ -1452,14 +1496,6 @@ impl SslContextBuilder { } } -impl Deref for SslContextBuilder { - type Target = SslContextRef; - - fn deref(&self) -> &SslContextRef { - &self.0 - } -} - foreign_type_and_impl_send_sync! { type CType = ffi::SSL_CTX; fn drop = ffi::SSL_CTX_free; @@ -1608,50 +1644,6 @@ impl SslContextRef { } } } - - /// Gets the minimum supported protocol version. - /// - /// A value of `None` indicates that all versions down the the lowest version supported by - /// OpenSSL are enabled. - /// - /// This corresponds to [`SSL_CTX_get_min_proto_version`]. - /// - /// Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer. - /// - /// [`SSL_CTX_get_min_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html - #[cfg(any(ossl110g, libressl270))] - pub fn min_proto_version(&self) -> Option { - unsafe { - let r = ffi::SSL_CTX_get_min_proto_version(self.as_ptr()); - if r == 0 { - None - } else { - Some(SslVersion(r)) - } - } - } - - /// Gets the maximum supported protocol version. - /// - /// A value of `None` indicates that all versions down the the highest version supported by - /// OpenSSL are enabled. - /// - /// This corresponds to [`SSL_CTX_get_max_proto_version`]. - /// - /// Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer. - /// - /// [`SSL_CTX_get_max_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html - #[cfg(any(ossl110g, libressl270))] - pub fn max_proto_version(&self) -> Option { - unsafe { - let r = ffi::SSL_CTX_get_max_proto_version(self.as_ptr()); - if r == 0 { - None - } else { - Some(SslVersion(r)) - } - } - } } /// Information about the state of a cipher. -- cgit v1.2.3 From 2e478fdcf47bcb69a098bd6c3ab6227b53d45a24 Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Thu, 17 May 2018 15:17:40 -0700 Subject: Expose early I/O --- openssl/src/ssl/mod.rs | 90 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) (limited to 'openssl/src/ssl') diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 78ae2267..623db801 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -2500,6 +2500,9 @@ impl SslRef { /// Derives keying material for application use in accordance to RFC 5705. /// + /// This function is only usable with TLSv1.3, wherein there is no distinction between an empty context and no + /// context. Therefore, unlike `export_keying_material`, `context` must always be supplied. + /// /// Requires OpenSSL 1.1.1 or newer. /// /// This corresponds to [`SSL_export_keying_material_early`]. @@ -3001,6 +3004,24 @@ where } } + /// Configure as an outgoing stream from a client. + /// + /// This corresponds to [`SSL_set_connect_state`]. + /// + /// [`SSL_set_connect_state`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_connect_state.html + pub fn set_connect_state(&mut self) { + unsafe { ffi::SSL_set_connect_state(self.inner.ssl.as_ptr()) } + } + + /// Configure as an incoming stream to a server. + /// + /// This corresponds to [`SSL_set_accept_state`]. + /// + /// [`SSL_set_accept_state`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_accept_state.html + pub fn set_accept_state(&mut self) { + unsafe { ffi::SSL_set_accept_state(self.inner.ssl.as_ptr()) } + } + /// See `Ssl::connect` pub fn connect(self) -> Result, HandshakeError> { let mut stream = self.inner; @@ -3041,7 +3062,74 @@ where } } - // Future work: early IO methods + /// Initiates the handshake. + /// + /// This will fail if `set_accept_state` or `set_connect_state` was not called first. + /// + /// This corresponds to [`SSL_do_handshake`]. + /// + /// [`SSL_do_handshake`]: https://www.openssl.org/docs/manmaster/man3/SSL_do_handshake.html + pub fn handshake(self) -> Result, HandshakeError> { + let mut stream = self.inner; + let ret = unsafe { ffi::SSL_do_handshake(stream.ssl.as_ptr()) }; + if ret > 0 { + Ok(stream) + } else { + let error = stream.make_error(ret); + match error.code() { + ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => { + Err(HandshakeError::WouldBlock(MidHandshakeSslStream { stream, error })) + } + _ => Err(HandshakeError::Failure(MidHandshakeSslStream { stream, error })), + } + } + } + + /// Read application data transmitted by a client before handshake + /// completion. + /// + /// Useful for reducing latency, but vulnerable to replay attacks. Call + /// `set_accept_state` first. + /// + /// Returns `Ok(0)` if all early data has been read. + /// + /// Requires OpenSSL 1.1.1 or newer. + /// + /// This corresponds to [`SSL_read_early_data`]. + /// + /// [`SSL_read_early_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_read_early_data.html + #[cfg(ossl111)] + pub fn read_early_data(&mut self, buf: &mut [u8]) -> Result { + let mut read = 0; + let ret = unsafe { ffi::SSL_read_early_data(self.inner.ssl.as_ptr(), buf.as_ptr() as *mut c_void, buf.len(), &mut read) }; + match ret { + ffi::SSL_READ_EARLY_DATA_ERROR => Err(self.inner.make_error(ret)), + ffi::SSL_READ_EARLY_DATA_SUCCESS => Ok(read), + ffi::SSL_READ_EARLY_DATA_FINISH => Ok(0), + _ => unreachable!(), + } + } + + /// Send data to the server without blocking on handshake completion. + /// + /// Useful for reducing latency, but vulnerable to replay attacks. Call + /// `set_connect_state` first. + /// + /// Requires OpenSSL 1.1.1 or newer. + /// + /// This corresponds to [`SSL_write_early_data`]. + /// + /// [`SSL_write_early_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_write_early_data.html + #[cfg(ossl111)] + pub fn write_early_data(&mut self, buf: &[u8]) -> Result { + let mut written = 0; + let ret = unsafe { ffi::SSL_write_early_data(self.inner.ssl.as_ptr(), buf.as_ptr() as *const c_void, buf.len(), &mut written) }; + if ret > 0 { + Ok(written as usize) + } else { + Err(self.inner.make_error(ret)) + } + } } impl SslStreamBuilder { -- cgit v1.2.3 From c0876cc8c65cf61d99006ce5e502a9a2d1acd70b Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Thu, 24 May 2018 20:00:28 -0700 Subject: Add bindings to SSL_get_finished and SSL_get_peer_finished These are used for the tls-unique SCRAM channel binding mode. --- openssl/src/ssl/mod.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 6 deletions(-) (limited to 'openssl/src/ssl') diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 444a50cc..017ba54c 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -2695,6 +2695,29 @@ impl SslRef { pub fn max_early_data(&self) -> u32 { unsafe { ffi::SSL_get_max_early_data(self.as_ptr()) } } + + /// Copies the contents of the last Finished message sent to the peer into the provided buffer. + /// + /// The total size of the message is returned, so this can be used to determine the size of the + /// buffer required. + /// + /// This corresponds to `SSL_get_finished`. + pub fn finished(&self, buf: &mut [u8]) -> usize { + unsafe { ffi::SSL_get_finished(self.as_ptr(), buf.as_mut_ptr() as *mut c_void, buf.len()) } + } + + /// Copies the contents of the last Finished message received from the peer into the provided + /// buffer. + /// + /// The total size of the message is returned, so this can be used to determine the size of the + /// buffer required. + /// + /// This corresponds to `SSL_get_finished`. + pub fn peer_finished(&self, buf: &mut [u8]) -> usize { + unsafe { + ffi::SSL_get_peer_finished(self.as_ptr(), buf.as_mut_ptr() as *mut c_void, buf.len()) + } + } } /// An SSL stream midway through the handshake process. @@ -3080,10 +3103,13 @@ where } else { let error = stream.make_error(ret); match error.code() { - ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => { - Err(HandshakeError::WouldBlock(MidHandshakeSslStream { stream, error })) - } - _ => Err(HandshakeError::Failure(MidHandshakeSslStream { stream, error })), + ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => Err(HandshakeError::WouldBlock( + MidHandshakeSslStream { stream, error }, + )), + _ => Err(HandshakeError::Failure(MidHandshakeSslStream { + stream, + error, + })), } } } @@ -3104,7 +3130,14 @@ where #[cfg(ossl111)] pub fn read_early_data(&mut self, buf: &mut [u8]) -> Result { let mut read = 0; - let ret = unsafe { ffi::SSL_read_early_data(self.inner.ssl.as_ptr(), buf.as_ptr() as *mut c_void, buf.len(), &mut read) }; + let ret = unsafe { + ffi::SSL_read_early_data( + self.inner.ssl.as_ptr(), + buf.as_ptr() as *mut c_void, + buf.len(), + &mut read, + ) + }; match ret { ffi::SSL_READ_EARLY_DATA_ERROR => Err(self.inner.make_error(ret)), ffi::SSL_READ_EARLY_DATA_SUCCESS => Ok(read), @@ -3126,7 +3159,14 @@ where #[cfg(ossl111)] pub fn write_early_data(&mut self, buf: &[u8]) -> Result { let mut written = 0; - let ret = unsafe { ffi::SSL_write_early_data(self.inner.ssl.as_ptr(), buf.as_ptr() as *const c_void, buf.len(), &mut written) }; + let ret = unsafe { + ffi::SSL_write_early_data( + self.inner.ssl.as_ptr(), + buf.as_ptr() as *const c_void, + buf.len(), + &mut written, + ) + }; if ret > 0 { Ok(written as usize) } else { -- cgit v1.2.3 From 3cd33cdd8bc4e18c01bc1f848de9956758bc37fb Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Thu, 24 May 2018 20:18:33 -0700 Subject: Don't panic on bogus servernames Also add a second version of the method to avoid filtering out non-utf8 names. Closes #930 --- openssl/src/ssl/mod.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) (limited to 'openssl/src/ssl') diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 017ba54c..e24c3498 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -2366,14 +2366,38 @@ impl SslRef { /// /// This corresponds to [`SSL_get_servername`]. /// + /// # Note + /// + /// While the SNI specification requires that servernames be valid domain names (and therefore + /// ASCII), OpenSSL does not enforce this restriction. If the servername provided by the client + /// is not valid UTF-8, this function will return `None`. The `servername_raw` method returns + /// the raw bytes and does not have this restriction. + /// /// [`SSL_get_servername`]: https://www.openssl.org/docs/manmaster/man3/SSL_get_servername.html + // FIXME maybe rethink in 0.11? pub fn servername(&self, type_: NameType) -> Option<&str> { + self.servername_raw(type_) + .and_then(|b| str::from_utf8(b).ok()) + } + + /// Returns the servername sent by the client via Server Name Indication (SNI). + /// + /// It is only useful on the server side. + /// + /// This corresponds to [`SSL_get_servername`]. + /// + /// # Note + /// + /// Unlike `servername`, this method does not require the name be valid UTF-8. + /// + /// [`SSL_get_servername`]: https://www.openssl.org/docs/manmaster/man3/SSL_get_servername.html + pub fn servername_raw(&self, type_: NameType) -> Option<&[u8]> { unsafe { let name = ffi::SSL_get_servername(self.as_ptr(), type_.0); if name == ptr::null() { None } else { - Some(str::from_utf8(CStr::from_ptr(name as *const _).to_bytes()).unwrap()) + Some(CStr::from_ptr(name as *const _).to_bytes()) } } } -- cgit v1.2.3 From a774c0c5f2867b33735bec9e1cb6c9870a5ece08 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Thu, 24 May 2018 20:35:06 -0700 Subject: Rename X509Ref::fingerprint to X509Ref::digest and avoid allocating --- openssl/src/ssl/test.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'openssl/src/ssl') diff --git a/openssl/src/ssl/test.rs b/openssl/src/ssl/test.rs index 0d418d2c..f5ec7b29 100644 --- a/openssl/src/ssl/test.rs +++ b/openssl/src/ssl/test.rs @@ -295,8 +295,8 @@ run_test!(verify_callback_data, |method, stream| { match cert { None => false, Some(cert) => { - let fingerprint = cert.fingerprint(MessageDigest::sha1()).unwrap(); - fingerprint == node_id + let fingerprint = cert.digest(MessageDigest::sha1()).unwrap(); + node_id == &*fingerprint } } }); @@ -323,8 +323,8 @@ run_test!(ssl_verify_callback, |method, stream| { match x509.current_cert() { None => false, Some(cert) => { - let fingerprint = cert.fingerprint(MessageDigest::sha1()).unwrap(); - fingerprint == node_id + let fingerprint = cert.digest(MessageDigest::sha1()).unwrap(); + node_id == &*fingerprint } } }); @@ -424,10 +424,10 @@ run_test!(get_peer_certificate, |method, stream| { let ctx = SslContext::builder(method).unwrap(); let stream = Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap(); let cert = stream.ssl().peer_certificate().unwrap(); - let fingerprint = cert.fingerprint(MessageDigest::sha1()).unwrap(); + let fingerprint = cert.digest(MessageDigest::sha1()).unwrap(); let node_hash_str = "59172d9313e84459bcff27f967e79e6e9217e584"; let node_id = Vec::from_hex(node_hash_str).unwrap(); - assert_eq!(node_id, fingerprint) + assert_eq!(node_id, &*fingerprint) }); #[test] -- cgit v1.2.3 From 3456add537d03aef8a5becc9cbaa77910a1ecb3f Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Tue, 29 May 2018 21:53:22 -0700 Subject: Add SslRef::verified_chain --- openssl/src/ssl/mod.rs | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) (limited to 'openssl/src/ssl') diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index e24c3498..0f3f9624 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -1822,7 +1822,7 @@ impl SslCipherRef { } } -foreign_type! { +foreign_type_and_impl_send_sync! { type CType = ffi::SSL_SESSION; fn drop = ffi::SSL_SESSION_free; @@ -1837,9 +1837,6 @@ foreign_type! { pub struct SslSessionRef; } -unsafe impl Sync for SslSession {} -unsafe impl Send for SslSession {} - impl Clone for SslSession { fn clone(&self) -> SslSession { SslSessionRef::to_owned(self) @@ -1927,7 +1924,7 @@ impl SslSessionRef { } } -foreign_type! { +foreign_type_and_impl_send_sync! { type CType = ffi::SSL; fn drop = ffi::SSL_free; @@ -1945,9 +1942,6 @@ foreign_type! { pub struct SslRef; } -unsafe impl Sync for Ssl {} -unsafe impl Send for Ssl {} - impl fmt::Debug for Ssl { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&**self, fmt) @@ -2254,6 +2248,30 @@ impl SslRef { } } + /// Returns the verified certificate chani of the peer, including the leaf certificate. + /// + /// If verification was not successful (i.e. [`verify_result`] does not return + /// [`X509VerifyResult::OK`]), this chain may be incomplete or invalid. + /// + /// Requires OpenSSL 1.1.0 or newer. + /// + /// This corresponds to [`SSL_get0_verified_chain`]. + /// + /// [`verify_result`]: #method.verify_result + /// [`X509VerifyResult::OK`]: ../x509/struct.X509VerifyResult.html#associatedconstant.OK + /// [`SSL_get0_verified_chain`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get0_verified_chain.html + #[cfg(ossl110)] + pub fn verified_chain(&self) -> Option<&StackRef> { + unsafe { + let ptr = ffi::SSL_get0_verified_chain(self.as_ptr()); + if ptr.is_null() { + None + } else { + Some(StackRef::from_ptr(ptr)) + } + } + } + /// Like [`SslContext::certificate`]. /// /// This corresponds to `SSL_get_certificate`. -- cgit v1.2.3