summaryrefslogtreecommitdiffstats
path: root/vendor/openssl/src/ssl
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
commit10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch)
treebdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/openssl/src/ssl
parentReleasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff)
downloadrustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz
rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/openssl/src/ssl')
-rw-r--r--vendor/openssl/src/ssl/bio.rs290
-rw-r--r--vendor/openssl/src/ssl/callbacks.rs698
-rw-r--r--vendor/openssl/src/ssl/connector.rs605
-rw-r--r--vendor/openssl/src/ssl/error.rs185
-rw-r--r--vendor/openssl/src/ssl/mod.rs4131
-rw-r--r--vendor/openssl/src/ssl/test/mod.rs1567
-rw-r--r--vendor/openssl/src/ssl/test/server.rs167
7 files changed, 7643 insertions, 0 deletions
diff --git a/vendor/openssl/src/ssl/bio.rs b/vendor/openssl/src/ssl/bio.rs
new file mode 100644
index 000000000..36cea23fd
--- /dev/null
+++ b/vendor/openssl/src/ssl/bio.rs
@@ -0,0 +1,290 @@
+use cfg_if::cfg_if;
+use ffi::{
+ self, BIO_clear_retry_flags, BIO_new, BIO_set_retry_read, BIO_set_retry_write, BIO,
+ BIO_CTRL_DGRAM_QUERY_MTU, BIO_CTRL_FLUSH,
+};
+use libc::{c_char, c_int, c_long, c_void, strlen};
+use std::any::Any;
+use std::io;
+use std::io::prelude::*;
+use std::panic::{catch_unwind, AssertUnwindSafe};
+use std::ptr;
+use std::slice;
+
+use crate::cvt_p;
+use crate::error::ErrorStack;
+
+pub struct StreamState<S> {
+ pub stream: S,
+ pub error: Option<io::Error>,
+ pub panic: Option<Box<dyn Any + Send>>,
+ pub dtls_mtu_size: c_long,
+}
+
+/// Safe wrapper for `BIO_METHOD`
+pub struct BioMethod(BIO_METHOD);
+
+impl BioMethod {
+ fn new<S: Read + Write>() -> Result<BioMethod, ErrorStack> {
+ BIO_METHOD::new::<S>().map(BioMethod)
+ }
+}
+
+unsafe impl Sync for BioMethod {}
+unsafe impl Send for BioMethod {}
+
+pub fn new<S: Read + Write>(stream: S) -> Result<(*mut BIO, BioMethod), ErrorStack> {
+ let method = BioMethod::new::<S>()?;
+
+ let state = Box::new(StreamState {
+ stream,
+ error: None,
+ panic: None,
+ dtls_mtu_size: 0,
+ });
+
+ unsafe {
+ let bio = cvt_p(BIO_new(method.0.get()))?;
+ BIO_set_data(bio, Box::into_raw(state) as *mut _);
+ BIO_set_init(bio, 1);
+
+ Ok((bio, method))
+ }
+}
+
+pub unsafe fn take_error<S>(bio: *mut BIO) -> Option<io::Error> {
+ let state = state::<S>(bio);
+ state.error.take()
+}
+
+pub unsafe fn take_panic<S>(bio: *mut BIO) -> Option<Box<dyn Any + Send>> {
+ let state = state::<S>(bio);
+ state.panic.take()
+}
+
+pub unsafe fn get_ref<'a, S: 'a>(bio: *mut BIO) -> &'a S {
+ let state = &*(BIO_get_data(bio) as *const StreamState<S>);
+ &state.stream
+}
+
+pub unsafe fn get_mut<'a, S: 'a>(bio: *mut BIO) -> &'a mut S {
+ &mut state(bio).stream
+}
+
+pub unsafe fn set_dtls_mtu_size<S>(bio: *mut BIO, mtu_size: usize) {
+ if mtu_size as u64 > c_long::max_value() as u64 {
+ panic!(
+ "Given MTU size {} can't be represented in a positive `c_long` range",
+ mtu_size
+ )
+ }
+ state::<S>(bio).dtls_mtu_size = mtu_size as c_long;
+}
+
+unsafe fn state<'a, S: 'a>(bio: *mut BIO) -> &'a mut StreamState<S> {
+ &mut *(BIO_get_data(bio) as *mut _)
+}
+
+unsafe extern "C" fn bwrite<S: Write>(bio: *mut BIO, buf: *const c_char, len: c_int) -> c_int {
+ BIO_clear_retry_flags(bio);
+
+ let state = state::<S>(bio);
+ let buf = slice::from_raw_parts(buf as *const _, len as usize);
+
+ match catch_unwind(AssertUnwindSafe(|| state.stream.write(buf))) {
+ Ok(Ok(len)) => len as c_int,
+ Ok(Err(err)) => {
+ if retriable_error(&err) {
+ BIO_set_retry_write(bio);
+ }
+ state.error = Some(err);
+ -1
+ }
+ Err(err) => {
+ state.panic = Some(err);
+ -1
+ }
+ }
+}
+
+unsafe extern "C" fn bread<S: Read>(bio: *mut BIO, buf: *mut c_char, len: c_int) -> c_int {
+ BIO_clear_retry_flags(bio);
+
+ let state = state::<S>(bio);
+ let buf = slice::from_raw_parts_mut(buf as *mut _, len as usize);
+
+ match catch_unwind(AssertUnwindSafe(|| state.stream.read(buf))) {
+ Ok(Ok(len)) => len as c_int,
+ Ok(Err(err)) => {
+ if retriable_error(&err) {
+ BIO_set_retry_read(bio);
+ }
+ state.error = Some(err);
+ -1
+ }
+ Err(err) => {
+ state.panic = Some(err);
+ -1
+ }
+ }
+}
+
+#[allow(clippy::match_like_matches_macro)] // matches macro requires rust 1.42.0
+fn retriable_error(err: &io::Error) -> bool {
+ match err.kind() {
+ io::ErrorKind::WouldBlock | io::ErrorKind::NotConnected => true,
+ _ => false,
+ }
+}
+
+unsafe extern "C" fn bputs<S: Write>(bio: *mut BIO, s: *const c_char) -> c_int {
+ bwrite::<S>(bio, s, strlen(s) as c_int)
+}
+
+unsafe extern "C" fn ctrl<S: Write>(
+ bio: *mut BIO,
+ cmd: c_int,
+ _num: c_long,
+ _ptr: *mut c_void,
+) -> c_long {
+ let state = state::<S>(bio);
+
+ if cmd == BIO_CTRL_FLUSH {
+ match catch_unwind(AssertUnwindSafe(|| state.stream.flush())) {
+ Ok(Ok(())) => 1,
+ Ok(Err(err)) => {
+ state.error = Some(err);
+ 0
+ }
+ Err(err) => {
+ state.panic = Some(err);
+ 0
+ }
+ }
+ } else if cmd == BIO_CTRL_DGRAM_QUERY_MTU {
+ state.dtls_mtu_size
+ } else {
+ 0
+ }
+}
+
+unsafe extern "C" fn create(bio: *mut BIO) -> c_int {
+ BIO_set_init(bio, 0);
+ BIO_set_num(bio, 0);
+ BIO_set_data(bio, ptr::null_mut());
+ BIO_set_flags(bio, 0);
+ 1
+}
+
+unsafe extern "C" fn destroy<S>(bio: *mut BIO) -> c_int {
+ if bio.is_null() {
+ return 0;
+ }
+
+ let data = BIO_get_data(bio);
+ assert!(!data.is_null());
+ let _ = Box::<StreamState<S>>::from_raw(data as *mut _);
+ BIO_set_data(bio, ptr::null_mut());
+ BIO_set_init(bio, 0);
+ 1
+}
+
+cfg_if! {
+ if #[cfg(any(ossl110, libressl273))] {
+ use ffi::{BIO_get_data, BIO_set_data, BIO_set_flags, BIO_set_init};
+ use crate::cvt;
+
+ #[allow(bad_style)]
+ unsafe fn BIO_set_num(_bio: *mut ffi::BIO, _num: c_int) {}
+
+ #[allow(bad_style, clippy::upper_case_acronyms)]
+ struct BIO_METHOD(*mut ffi::BIO_METHOD);
+
+ impl BIO_METHOD {
+ fn new<S: Read + Write>() -> Result<BIO_METHOD, ErrorStack> {
+ unsafe {
+ let ptr = cvt_p(ffi::BIO_meth_new(ffi::BIO_TYPE_NONE, b"rust\0".as_ptr() as *const _))?;
+ let method = BIO_METHOD(ptr);
+ cvt(ffi::BIO_meth_set_write__fixed_rust(method.0, Some(bwrite::<S>)))?;
+ cvt(ffi::BIO_meth_set_read__fixed_rust(method.0, Some(bread::<S>)))?;
+ cvt(ffi::BIO_meth_set_puts__fixed_rust(method.0, Some(bputs::<S>)))?;
+ cvt(ffi::BIO_meth_set_ctrl__fixed_rust(method.0, Some(ctrl::<S>)))?;
+ cvt(ffi::BIO_meth_set_create__fixed_rust(method.0, Some(create)))?;
+ cvt(ffi::BIO_meth_set_destroy__fixed_rust(method.0, Some(destroy::<S>)))?;
+ Ok(method)
+ }
+ }
+
+ 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);
+ }
+ }
+ }
+ } else {
+ #[allow(bad_style, clippy::upper_case_acronyms)]
+ struct BIO_METHOD(*mut ffi::BIO_METHOD);
+
+ impl BIO_METHOD {
+ fn new<S: Read + Write>() -> Result<BIO_METHOD, ErrorStack> {
+ let ptr = Box::new(ffi::BIO_METHOD {
+ type_: ffi::BIO_TYPE_NONE,
+ name: b"rust\0".as_ptr() as *const _,
+ bwrite: Some(bwrite::<S>),
+ bread: Some(bread::<S>),
+ bputs: Some(bputs::<S>),
+ bgets: None,
+ ctrl: Some(ctrl::<S>),
+ create: Some(create),
+ destroy: Some(destroy::<S>),
+ callback_ctrl: None,
+ });
+
+ Ok(BIO_METHOD(Box::into_raw(ptr)))
+ }
+
+ fn get(&self) -> *mut ffi::BIO_METHOD {
+ self.0
+ }
+ }
+
+ impl Drop for BIO_METHOD {
+ fn drop(&mut self) {
+ unsafe {
+ let _ = Box::<ffi::BIO_METHOD>::from_raw(self.0);
+ }
+ }
+ }
+
+ #[allow(bad_style)]
+ unsafe fn BIO_set_init(bio: *mut ffi::BIO, init: c_int) {
+ (*bio).init = init;
+ }
+
+ #[allow(bad_style)]
+ unsafe fn BIO_set_flags(bio: *mut ffi::BIO, flags: c_int) {
+ (*bio).flags = flags;
+ }
+
+ #[allow(bad_style)]
+ unsafe fn BIO_get_data(bio: *mut ffi::BIO) -> *mut c_void {
+ (*bio).ptr
+ }
+
+ #[allow(bad_style)]
+ unsafe fn BIO_set_data(bio: *mut ffi::BIO, data: *mut c_void) {
+ (*bio).ptr = data;
+ }
+
+ #[allow(bad_style)]
+ unsafe fn BIO_set_num(bio: *mut ffi::BIO, num: c_int) {
+ (*bio).num = num;
+ }
+ }
+}
diff --git a/vendor/openssl/src/ssl/callbacks.rs b/vendor/openssl/src/ssl/callbacks.rs
new file mode 100644
index 000000000..091b1fb77
--- /dev/null
+++ b/vendor/openssl/src/ssl/callbacks.rs
@@ -0,0 +1,698 @@
+use cfg_if::cfg_if;
+use foreign_types::ForeignType;
+use foreign_types::ForeignTypeRef;
+#[cfg(any(ossl111, not(osslconf = "OPENSSL_NO_PSK")))]
+use libc::c_char;
+#[cfg(ossl111)]
+use libc::size_t;
+use libc::{c_int, c_uchar, c_uint, c_void};
+#[cfg(any(ossl111, not(osslconf = "OPENSSL_NO_PSK")))]
+use std::ffi::CStr;
+use std::mem;
+use std::ptr;
+use std::slice;
+#[cfg(ossl111)]
+use std::str;
+use std::sync::Arc;
+
+use crate::dh::Dh;
+#[cfg(all(ossl101, not(ossl110)))]
+use crate::ec::EcKey;
+use crate::error::ErrorStack;
+use crate::pkey::Params;
+#[cfg(any(ossl102, libressl261))]
+use crate::ssl::AlpnError;
+use crate::ssl::{
+ try_get_session_ctx_index, SniError, Ssl, SslAlert, SslContext, SslContextRef, SslRef,
+ SslSession, SslSessionRef,
+};
+#[cfg(ossl111)]
+use crate::ssl::{ClientHelloResponse, ExtensionContext};
+#[cfg(ossl111)]
+use crate::util::ForeignTypeRefExt;
+#[cfg(ossl111)]
+use crate::x509::X509Ref;
+use crate::x509::{X509StoreContext, X509StoreContextRef};
+
+pub extern "C" fn raw_verify<F>(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int
+where
+ F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send,
+{
+ unsafe {
+ let ctx = X509StoreContextRef::from_ptr_mut(x509_ctx);
+ let ssl_idx = X509StoreContext::ssl_idx().expect("BUG: store context ssl index missing");
+ let verify_idx = SslContext::cached_ex_index::<F>();
+
+ // 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)
+ .expect("BUG: store context missing ssl")
+ .ssl_context()
+ .ex_data(verify_idx)
+ .expect("BUG: verify callback missing") as *const F;
+
+ (*verify)(preverify_ok != 0, ctx) as c_int
+ }
+}
+
+#[cfg(not(osslconf = "OPENSSL_NO_PSK"))]
+pub extern "C" fn raw_client_psk<F>(
+ ssl: *mut ffi::SSL,
+ hint: *const c_char,
+ identity: *mut c_char,
+ max_identity_len: c_uint,
+ psk: *mut c_uchar,
+ max_psk_len: c_uint,
+) -> c_uint
+where
+ F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8], &mut [u8]) -> Result<usize, ErrorStack>
+ + 'static
+ + Sync
+ + Send,
+{
+ unsafe {
+ let ssl = SslRef::from_ptr_mut(ssl);
+ let callback_idx = SslContext::cached_ex_index::<F>();
+
+ let callback = ssl
+ .ssl_context()
+ .ex_data(callback_idx)
+ .expect("BUG: psk callback missing") as *const F;
+ let hint = if !hint.is_null() {
+ Some(CStr::from_ptr(hint).to_bytes())
+ } else {
+ None
+ };
+ // Give the callback mutable slices into which it can write the identity and psk.
+ let identity_sl = slice::from_raw_parts_mut(identity as *mut u8, max_identity_len as usize);
+ let psk_sl = slice::from_raw_parts_mut(psk as *mut u8, max_psk_len as usize);
+ match (*callback)(ssl, hint, identity_sl, psk_sl) {
+ Ok(psk_len) => psk_len as u32,
+ Err(e) => {
+ e.put();
+ 0
+ }
+ }
+ }
+}
+
+#[cfg(not(osslconf = "OPENSSL_NO_PSK"))]
+pub extern "C" fn raw_server_psk<F>(
+ ssl: *mut ffi::SSL,
+ identity: *const c_char,
+ psk: *mut c_uchar,
+ max_psk_len: c_uint,
+) -> c_uint
+where
+ F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8]) -> Result<usize, ErrorStack>
+ + 'static
+ + Sync
+ + Send,
+{
+ unsafe {
+ let ssl = SslRef::from_ptr_mut(ssl);
+ let callback_idx = SslContext::cached_ex_index::<F>();
+
+ let callback = ssl
+ .ssl_context()
+ .ex_data(callback_idx)
+ .expect("BUG: psk callback missing") as *const F;
+ let identity = if identity.is_null() {
+ None
+ } else {
+ Some(CStr::from_ptr(identity).to_bytes())
+ };
+ // Give the callback mutable slices into which it can write the psk.
+ let psk_sl = slice::from_raw_parts_mut(psk as *mut u8, max_psk_len as usize);
+ match (*callback)(ssl, identity, psk_sl) {
+ Ok(psk_len) => psk_len as u32,
+ Err(e) => {
+ e.put();
+ 0
+ }
+ }
+ }
+}
+
+pub extern "C" fn ssl_raw_verify<F>(
+ preverify_ok: c_int,
+ x509_ctx: *mut ffi::X509_STORE_CTX,
+) -> c_int
+where
+ F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send,
+{
+ unsafe {
+ let ctx = X509StoreContextRef::from_ptr_mut(x509_ctx);
+ let ssl_idx = X509StoreContext::ssl_idx().expect("BUG: store context ssl index missing");
+ let callback_idx = Ssl::cached_ex_index::<Arc<F>>();
+
+ let callback = ctx
+ .ex_data(ssl_idx)
+ .expect("BUG: store context missing ssl")
+ .ex_data(callback_idx)
+ .expect("BUG: ssl verify callback missing")
+ .clone();
+
+ callback(preverify_ok != 0, ctx) as c_int
+ }
+}
+
+pub extern "C" fn raw_sni<F>(ssl: *mut ffi::SSL, al: *mut c_int, arg: *mut c_void) -> c_int
+where
+ F: Fn(&mut SslRef, &mut SslAlert) -> Result<(), SniError> + 'static + Sync + Send,
+{
+ unsafe {
+ let ssl = SslRef::from_ptr_mut(ssl);
+ let callback = arg as *const F;
+ let mut alert = SslAlert(*al);
+
+ let r = (*callback)(ssl, &mut alert);
+ *al = alert.0;
+ match r {
+ Ok(()) => ffi::SSL_TLSEXT_ERR_OK,
+ Err(e) => e.0,
+ }
+ }
+}
+
+#[cfg(any(ossl102, libressl261))]
+pub extern "C" fn raw_alpn_select<F>(
+ ssl: *mut ffi::SSL,
+ out: *mut *const c_uchar,
+ outlen: *mut c_uchar,
+ inbuf: *const c_uchar,
+ inlen: c_uint,
+ _arg: *mut c_void,
+) -> c_int
+where
+ F: for<'a> Fn(&mut SslRef, &'a [u8]) -> Result<&'a [u8], AlpnError> + 'static + Sync + Send,
+{
+ unsafe {
+ let ssl = SslRef::from_ptr_mut(ssl);
+ let callback = ssl
+ .ssl_context()
+ .ex_data(SslContext::cached_ex_index::<F>())
+ .expect("BUG: alpn callback missing") as *const F;
+ let protos = slice::from_raw_parts(inbuf as *const u8, inlen as usize);
+
+ match (*callback)(ssl, protos) {
+ Ok(proto) => {
+ *out = proto.as_ptr() as *const c_uchar;
+ *outlen = proto.len() as c_uchar;
+ ffi::SSL_TLSEXT_ERR_OK
+ }
+ Err(e) => e.0,
+ }
+ }
+}
+
+pub unsafe extern "C" fn raw_tmp_dh<F>(
+ ssl: *mut ffi::SSL,
+ is_export: c_int,
+ keylength: c_int,
+) -> *mut ffi::DH
+where
+ F: Fn(&mut SslRef, bool, u32) -> Result<Dh<Params>, ErrorStack> + 'static + Sync + Send,
+{
+ let ssl = SslRef::from_ptr_mut(ssl);
+ let callback = ssl
+ .ssl_context()
+ .ex_data(SslContext::cached_ex_index::<F>())
+ .expect("BUG: tmp dh callback missing") as *const F;
+
+ match (*callback)(ssl, is_export != 0, keylength as u32) {
+ Ok(dh) => {
+ let ptr = dh.as_ptr();
+ mem::forget(dh);
+ ptr
+ }
+ Err(e) => {
+ e.put();
+ ptr::null_mut()
+ }
+ }
+}
+
+#[cfg(all(ossl101, not(ossl110)))]
+pub unsafe extern "C" fn raw_tmp_ecdh<F>(
+ ssl: *mut ffi::SSL,
+ is_export: c_int,
+ keylength: c_int,
+) -> *mut ffi::EC_KEY
+where
+ F: Fn(&mut SslRef, bool, u32) -> Result<EcKey<Params>, ErrorStack> + 'static + Sync + Send,
+{
+ let ssl = SslRef::from_ptr_mut(ssl);
+ let callback = ssl
+ .ssl_context()
+ .ex_data(SslContext::cached_ex_index::<F>())
+ .expect("BUG: tmp ecdh callback missing") as *const F;
+
+ match (*callback)(ssl, is_export != 0, keylength as u32) {
+ Ok(ec_key) => {
+ let ptr = ec_key.as_ptr();
+ mem::forget(ec_key);
+ ptr
+ }
+ Err(e) => {
+ e.put();
+ ptr::null_mut()
+ }
+ }
+}
+
+pub unsafe extern "C" fn raw_tmp_dh_ssl<F>(
+ ssl: *mut ffi::SSL,
+ is_export: c_int,
+ keylength: c_int,
+) -> *mut ffi::DH
+where
+ F: Fn(&mut SslRef, bool, u32) -> Result<Dh<Params>, ErrorStack> + 'static + Sync + Send,
+{
+ let ssl = SslRef::from_ptr_mut(ssl);
+ let callback = ssl
+ .ex_data(Ssl::cached_ex_index::<Arc<F>>())
+ .expect("BUG: ssl tmp dh callback missing")
+ .clone();
+
+ match callback(ssl, is_export != 0, keylength as u32) {
+ Ok(dh) => {
+ let ptr = dh.as_ptr();
+ mem::forget(dh);
+ ptr
+ }
+ Err(e) => {
+ e.put();
+ ptr::null_mut()
+ }
+ }
+}
+
+#[cfg(all(ossl101, not(ossl110)))]
+pub unsafe extern "C" fn raw_tmp_ecdh_ssl<F>(
+ ssl: *mut ffi::SSL,
+ is_export: c_int,
+ keylength: c_int,
+) -> *mut ffi::EC_KEY
+where
+ F: Fn(&mut SslRef, bool, u32) -> Result<EcKey<Params>, ErrorStack> + 'static + Sync + Send,
+{
+ let ssl = SslRef::from_ptr_mut(ssl);
+ let callback = ssl
+ .ex_data(Ssl::cached_ex_index::<Arc<F>>())
+ .expect("BUG: ssl tmp ecdh callback missing")
+ .clone();
+
+ match callback(ssl, is_export != 0, keylength as u32) {
+ Ok(ec_key) => {
+ let ptr = ec_key.as_ptr();
+ mem::forget(ec_key);
+ ptr
+ }
+ Err(e) => {
+ e.put();
+ ptr::null_mut()
+ }
+ }
+}
+
+pub unsafe extern "C" fn raw_tlsext_status<F>(ssl: *mut ffi::SSL, _: *mut c_void) -> c_int
+where
+ F: Fn(&mut SslRef) -> Result<bool, ErrorStack> + 'static + Sync + Send,
+{
+ let ssl = SslRef::from_ptr_mut(ssl);
+ let callback = ssl
+ .ssl_context()
+ .ex_data(SslContext::cached_ex_index::<F>())
+ .expect("BUG: ocsp callback missing") as *const F;
+ let ret = (*callback)(ssl);
+
+ if ssl.is_server() {
+ match ret {
+ Ok(true) => ffi::SSL_TLSEXT_ERR_OK,
+ Ok(false) => ffi::SSL_TLSEXT_ERR_NOACK,
+ Err(e) => {
+ e.put();
+ ffi::SSL_TLSEXT_ERR_ALERT_FATAL
+ }
+ }
+ } else {
+ match ret {
+ Ok(true) => 1,
+ Ok(false) => 0,
+ Err(e) => {
+ e.put();
+ -1
+ }
+ }
+ }
+}
+
+pub unsafe extern "C" fn raw_new_session<F>(
+ ssl: *mut ffi::SSL,
+ session: *mut ffi::SSL_SESSION,
+) -> c_int
+where
+ F: Fn(&mut SslRef, SslSession) + 'static + Sync + Send,
+{
+ let session_ctx_index =
+ try_get_session_ctx_index().expect("BUG: session context index initialization failed");
+ let ssl = SslRef::from_ptr_mut(ssl);
+ let callback = ssl
+ .ex_data(*session_ctx_index)
+ .expect("BUG: session context missing")
+ .ex_data(SslContext::cached_ex_index::<F>())
+ .expect("BUG: new session callback missing") as *const F;
+ let session = SslSession::from_ptr(session);
+
+ (*callback)(ssl, session);
+
+ // the return code doesn't indicate error vs success, but whether or not we consumed the session
+ 1
+}
+
+pub unsafe extern "C" fn raw_remove_session<F>(
+ ctx: *mut ffi::SSL_CTX,
+ session: *mut ffi::SSL_SESSION,
+) where
+ F: Fn(&SslContextRef, &SslSessionRef) + 'static + Sync + Send,
+{
+ let ctx = SslContextRef::from_ptr(ctx);
+ let callback = ctx
+ .ex_data(SslContext::cached_ex_index::<F>())
+ .expect("BUG: remove session callback missing");
+ let session = SslSessionRef::from_ptr(session);
+
+ callback(ctx, session)
+}
+
+cfg_if! {
+ if #[cfg(any(ossl110, libressl280, boringssl))] {
+ type DataPtr = *const c_uchar;
+ } else {
+ type DataPtr = *mut c_uchar;
+ }
+}
+
+pub unsafe extern "C" fn raw_get_session<F>(
+ ssl: *mut ffi::SSL,
+ data: DataPtr,
+ len: c_int,
+ copy: *mut c_int,
+) -> *mut ffi::SSL_SESSION
+where
+ F: Fn(&mut SslRef, &[u8]) -> Option<SslSession> + 'static + Sync + Send,
+{
+ let session_ctx_index =
+ try_get_session_ctx_index().expect("BUG: session context index initialization failed");
+ let ssl = SslRef::from_ptr_mut(ssl);
+ let callback = ssl
+ .ex_data(*session_ctx_index)
+ .expect("BUG: session context missing")
+ .ex_data(SslContext::cached_ex_index::<F>())
+ .expect("BUG: get session callback missing") as *const F;
+ let data = slice::from_raw_parts(data as *const u8, len as usize);
+
+ match (*callback)(ssl, data) {
+ Some(session) => {
+ let p = session.as_ptr();
+ mem::forget(session);
+ *copy = 0;
+ p
+ }
+ None => ptr::null_mut(),
+ }
+}
+
+#[cfg(ossl111)]
+pub unsafe extern "C" fn raw_keylog<F>(ssl: *const ffi::SSL, line: *const c_char)
+where
+ F: Fn(&SslRef, &str) + 'static + Sync + Send,
+{
+ let ssl = SslRef::from_const_ptr(ssl);
+ let callback = ssl
+ .ssl_context()
+ .ex_data(SslContext::cached_ex_index::<F>())
+ .expect("BUG: get session callback missing");
+ let line = CStr::from_ptr(line).to_bytes();
+ let line = str::from_utf8_unchecked(line);
+
+ callback(ssl, line);
+}
+
+#[cfg(ossl111)]
+pub unsafe extern "C" fn raw_stateless_cookie_generate<F>(
+ ssl: *mut ffi::SSL,
+ cookie: *mut c_uchar,
+ cookie_len: *mut size_t,
+) -> c_int
+where
+ F: Fn(&mut SslRef, &mut [u8]) -> Result<usize, ErrorStack> + 'static + Sync + Send,
+{
+ let ssl = SslRef::from_ptr_mut(ssl);
+ let callback = ssl
+ .ssl_context()
+ .ex_data(SslContext::cached_ex_index::<F>())
+ .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);
+ match (*callback)(ssl, slice) {
+ Ok(len) => {
+ *cookie_len = len as size_t;
+ 1
+ }
+ Err(e) => {
+ e.put();
+ 0
+ }
+ }
+}
+
+#[cfg(ossl111)]
+pub unsafe extern "C" fn raw_stateless_cookie_verify<F>(
+ ssl: *mut ffi::SSL,
+ cookie: *const c_uchar,
+ cookie_len: size_t,
+) -> c_int
+where
+ F: Fn(&mut SslRef, &[u8]) -> bool + 'static + Sync + Send,
+{
+ let ssl = SslRef::from_ptr_mut(ssl);
+ let callback = ssl
+ .ssl_context()
+ .ex_data(SslContext::cached_ex_index::<F>())
+ .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);
+ (*callback)(ssl, slice) as c_int
+}
+
+#[cfg(not(boringssl))]
+pub extern "C" fn raw_cookie_generate<F>(
+ ssl: *mut ffi::SSL,
+ cookie: *mut c_uchar,
+ cookie_len: *mut c_uint,
+) -> c_int
+where
+ F: Fn(&mut SslRef, &mut [u8]) -> Result<usize, ErrorStack> + 'static + Sync + Send,
+{
+ unsafe {
+ let ssl = SslRef::from_ptr_mut(ssl);
+ let callback = ssl
+ .ssl_context()
+ .ex_data(SslContext::cached_ex_index::<F>())
+ .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
+ // compatibility. See comments in dtls1.h.
+ let slice =
+ slice::from_raw_parts_mut(cookie as *mut u8, ffi::DTLS1_COOKIE_LENGTH as usize - 1);
+ match (*callback)(ssl, slice) {
+ Ok(len) => {
+ *cookie_len = len as c_uint;
+ 1
+ }
+ Err(e) => {
+ e.put();
+ 0
+ }
+ }
+ }
+}
+
+#[cfg(not(boringssl))]
+cfg_if! {
+ if #[cfg(any(ossl110, libressl280))] {
+ type CookiePtr = *const c_uchar;
+ } else {
+ type CookiePtr = *mut c_uchar;
+ }
+}
+
+#[cfg(not(boringssl))]
+pub extern "C" fn raw_cookie_verify<F>(
+ ssl: *mut ffi::SSL,
+ cookie: CookiePtr,
+ cookie_len: c_uint,
+) -> c_int
+where
+ F: Fn(&mut SslRef, &[u8]) -> bool + 'static + Sync + Send,
+{
+ unsafe {
+ let ssl = SslRef::from_ptr_mut(ssl);
+ let callback = ssl
+ .ssl_context()
+ .ex_data(SslContext::cached_ex_index::<F>())
+ .expect("BUG: 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);
+ (*callback)(ssl, slice) as c_int
+ }
+}
+
+#[cfg(ossl111)]
+pub struct CustomExtAddState<T>(Option<T>);
+
+#[cfg(ossl111)]
+pub extern "C" fn raw_custom_ext_add<F, T>(
+ ssl: *mut ffi::SSL,
+ _: c_uint,
+ context: c_uint,
+ out: *mut *const c_uchar,
+ outlen: *mut size_t,
+ x: *mut ffi::X509,
+ chainidx: size_t,
+ al: *mut c_int,
+ _: *mut c_void,
+) -> c_int
+where
+ F: Fn(&mut SslRef, ExtensionContext, Option<(usize, &X509Ref)>) -> Result<Option<T>, SslAlert>
+ + 'static
+ + Sync
+ + Send,
+ T: AsRef<[u8]> + 'static + Sync + Send,
+{
+ unsafe {
+ let ssl = SslRef::from_ptr_mut(ssl);
+ let callback = ssl
+ .ssl_context()
+ .ex_data(SslContext::cached_ex_index::<F>())
+ .expect("BUG: custom ext add callback missing") as *const F;
+ let ectx = ExtensionContext::from_bits_truncate(context);
+ let cert = if ectx.contains(ExtensionContext::TLS1_3_CERTIFICATE) {
+ Some((chainidx, X509Ref::from_ptr(x)))
+ } else {
+ None
+ };
+ match (*callback)(ssl, ectx, cert) {
+ Ok(None) => 0,
+ Ok(Some(buf)) => {
+ *outlen = buf.as_ref().len();
+ *out = buf.as_ref().as_ptr();
+
+ let idx = Ssl::cached_ex_index::<CustomExtAddState<T>>();
+ let mut buf = Some(buf);
+ let new = match ssl.ex_data_mut(idx) {
+ Some(state) => {
+ state.0 = buf.take();
+ false
+ }
+ None => true,
+ };
+ if new {
+ ssl.set_ex_data(idx, CustomExtAddState(buf));
+ }
+ 1
+ }
+ Err(alert) => {
+ *al = alert.0;
+ -1
+ }
+ }
+ }
+}
+
+#[cfg(ossl111)]
+pub extern "C" fn raw_custom_ext_free<T>(
+ ssl: *mut ffi::SSL,
+ _: c_uint,
+ _: c_uint,
+ _: *const c_uchar,
+ _: *mut c_void,
+) where
+ T: 'static + Sync + Send,
+{
+ unsafe {
+ let ssl = SslRef::from_ptr_mut(ssl);
+ let idx = Ssl::cached_ex_index::<CustomExtAddState<T>>();
+ if let Some(state) = ssl.ex_data_mut(idx) {
+ state.0 = None;
+ }
+ }
+}
+
+#[cfg(ossl111)]
+pub extern "C" fn raw_custom_ext_parse<F>(
+ ssl: *mut ffi::SSL,
+ _: c_uint,
+ context: c_uint,
+ input: *const c_uchar,
+ inlen: size_t,
+ x: *mut ffi::X509,
+ chainidx: size_t,
+ al: *mut c_int,
+ _: *mut c_void,
+) -> c_int
+where
+ F: Fn(&mut SslRef, ExtensionContext, &[u8], Option<(usize, &X509Ref)>) -> Result<(), SslAlert>
+ + 'static
+ + Sync
+ + Send,
+{
+ unsafe {
+ let ssl = SslRef::from_ptr_mut(ssl);
+ let callback = ssl
+ .ssl_context()
+ .ex_data(SslContext::cached_ex_index::<F>())
+ .expect("BUG: custom ext parse callback missing") as *const F;
+ let ectx = ExtensionContext::from_bits_truncate(context);
+ let slice = slice::from_raw_parts(input as *const u8, inlen);
+ let cert = if ectx.contains(ExtensionContext::TLS1_3_CERTIFICATE) {
+ Some((chainidx, X509Ref::from_ptr(x)))
+ } else {
+ None
+ };
+ match (*callback)(ssl, ectx, slice, cert) {
+ Ok(()) => 1,
+ Err(alert) => {
+ *al = alert.0;
+ 0
+ }
+ }
+ }
+}
+
+#[cfg(ossl111)]
+pub unsafe extern "C" fn raw_client_hello<F>(
+ ssl: *mut ffi::SSL,
+ al: *mut c_int,
+ arg: *mut c_void,
+) -> c_int
+where
+ F: Fn(&mut SslRef, &mut SslAlert) -> Result<ClientHelloResponse, ErrorStack>
+ + 'static
+ + Sync
+ + Send,
+{
+ let ssl = SslRef::from_ptr_mut(ssl);
+ let callback = arg as *const F;
+ let mut alert = SslAlert(*al);
+
+ let r = (*callback)(ssl, &mut alert);
+ *al = alert.0;
+ match r {
+ Ok(c) => c.0,
+ Err(e) => {
+ e.put();
+ ffi::SSL_CLIENT_HELLO_ERROR
+ }
+ }
+}
diff --git a/vendor/openssl/src/ssl/connector.rs b/vendor/openssl/src/ssl/connector.rs
new file mode 100644
index 000000000..66d1bd893
--- /dev/null
+++ b/vendor/openssl/src/ssl/connector.rs
@@ -0,0 +1,605 @@
+use cfg_if::cfg_if;
+use std::io::{Read, Write};
+use std::ops::{Deref, DerefMut};
+
+use crate::dh::Dh;
+use crate::error::ErrorStack;
+#[cfg(any(ossl111, libressl340))]
+use crate::ssl::SslVersion;
+use crate::ssl::{
+ HandshakeError, Ssl, SslContext, SslContextBuilder, SslContextRef, SslMethod, SslMode,
+ SslOptions, SslRef, SslStream, SslVerifyMode,
+};
+use crate::version;
+use std::net::IpAddr;
+
+const FFDHE_2048: &str = "
+-----BEGIN DH PARAMETERS-----
+MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
++8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
+87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
+YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
+7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
+ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
+-----END DH PARAMETERS-----
+";
+
+#[allow(clippy::inconsistent_digit_grouping, clippy::unusual_byte_groupings)]
+fn ctx(method: SslMethod) -> Result<SslContextBuilder, ErrorStack> {
+ let mut ctx = SslContextBuilder::new(method)?;
+
+ cfg_if! {
+ if #[cfg(not(boringssl))] {
+ let mut opts = SslOptions::ALL
+ | SslOptions::NO_COMPRESSION
+ | SslOptions::NO_SSLV2
+ | SslOptions::NO_SSLV3
+ | SslOptions::SINGLE_DH_USE
+ | SslOptions::SINGLE_ECDH_USE;
+ opts &= !SslOptions::DONT_INSERT_EMPTY_FRAGMENTS;
+
+ ctx.set_options(opts);
+ }
+ }
+
+ let mut mode =
+ SslMode::AUTO_RETRY | SslMode::ACCEPT_MOVING_WRITE_BUFFER | SslMode::ENABLE_PARTIAL_WRITE;
+
+ // 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() >= 0x1_00_01_08_0 {
+ mode |= SslMode::RELEASE_BUFFERS;
+ }
+
+ ctx.set_mode(mode);
+
+ Ok(ctx)
+}
+
+/// A type which wraps client-side streams in a TLS session.
+///
+/// OpenSSL's default configuration is highly insecure. This connector manages the OpenSSL
+/// structures, configuring cipher suites, session options, hostname verification, and more.
+///
+/// OpenSSL's built-in hostname verification is used when linking against OpenSSL 1.0.2 or 1.1.0,
+/// and a custom implementation is used when linking against OpenSSL 1.0.1.
+#[derive(Clone, Debug)]
+pub struct SslConnector(SslContext);
+
+impl SslConnector {
+ /// Creates a new builder for TLS connections.
+ ///
+ /// The default configuration is subject to change, and is currently derived from Python.
+ pub fn builder(method: SslMethod) -> Result<SslConnectorBuilder, ErrorStack> {
+ let mut ctx = ctx(method)?;
+ ctx.set_default_verify_paths()?;
+ ctx.set_cipher_list(
+ "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK",
+ )?;
+ setup_verify(&mut ctx);
+
+ Ok(SslConnectorBuilder(ctx))
+ }
+
+ /// Initiates a client-side TLS session on a stream.
+ ///
+ /// The domain is used for SNI and hostname verification.
+ pub fn connect<S>(&self, domain: &str, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
+ where
+ S: Read + Write,
+ {
+ self.configure()?.connect(domain, stream)
+ }
+
+ /// Returns a structure allowing for configuration of a single TLS session before connection.
+ pub fn configure(&self) -> Result<ConnectConfiguration, ErrorStack> {
+ Ssl::new(&self.0).map(|ssl| ConnectConfiguration {
+ ssl,
+ sni: true,
+ verify_hostname: true,
+ })
+ }
+
+ /// Consumes the `SslConnector`, returning the inner raw `SslContext`.
+ pub fn into_context(self) -> SslContext {
+ self.0
+ }
+
+ /// Returns a shared reference to the inner raw `SslContext`.
+ pub fn context(&self) -> &SslContextRef {
+ &self.0
+ }
+}
+
+/// A builder for `SslConnector`s.
+pub struct SslConnectorBuilder(SslContextBuilder);
+
+impl SslConnectorBuilder {
+ /// Consumes the builder, returning an `SslConnector`.
+ pub fn build(self) -> SslConnector {
+ SslConnector(self.0.build())
+ }
+}
+
+impl Deref for SslConnectorBuilder {
+ type Target = SslContextBuilder;
+
+ fn deref(&self) -> &SslContextBuilder {
+ &self.0
+ }
+}
+
+impl DerefMut for SslConnectorBuilder {
+ fn deref_mut(&mut self) -> &mut SslContextBuilder {
+ &mut self.0
+ }
+}
+
+/// A type which allows for configuration of a client-side TLS session before connection.
+pub struct ConnectConfiguration {
+ ssl: Ssl,
+ sni: bool,
+ verify_hostname: bool,
+}
+
+impl ConnectConfiguration {
+ /// A builder-style version of `set_use_server_name_indication`.
+ pub fn use_server_name_indication(mut self, use_sni: bool) -> ConnectConfiguration {
+ self.set_use_server_name_indication(use_sni);
+ self
+ }
+
+ /// Configures the use of Server Name Indication (SNI) when connecting.
+ ///
+ /// Defaults to `true`.
+ pub fn set_use_server_name_indication(&mut self, use_sni: bool) {
+ self.sni = use_sni;
+ }
+
+ /// A builder-style version of `set_verify_hostname`.
+ pub fn verify_hostname(mut self, verify_hostname: bool) -> ConnectConfiguration {
+ self.set_verify_hostname(verify_hostname);
+ self
+ }
+
+ /// Configures the use of hostname verification when connecting.
+ ///
+ /// Defaults to `true`.
+ ///
+ /// # Warning
+ ///
+ /// You should think very carefully before you use this method. If hostname verification is not
+ /// used, *any* valid certificate for *any* site will be trusted for use from any other. This
+ /// introduces a significant vulnerability to man-in-the-middle attacks.
+ pub fn set_verify_hostname(&mut self, verify_hostname: bool) {
+ self.verify_hostname = verify_hostname;
+ }
+
+ /// Returns an `Ssl` configured to connect to the provided domain.
+ ///
+ /// The domain is used for SNI (if it is not an IP address) and hostname verification if enabled.
+ pub fn into_ssl(mut self, domain: &str) -> Result<Ssl, ErrorStack> {
+ if self.sni && domain.parse::<IpAddr>().is_err() {
+ self.ssl.set_hostname(domain)?;
+ }
+
+ if self.verify_hostname {
+ setup_verify_hostname(&mut self.ssl, domain)?;
+ }
+
+ Ok(self.ssl)
+ }
+
+ /// Initiates a client-side TLS session on a stream.
+ ///
+ /// The domain is used for SNI and hostname verification if enabled.
+ pub fn connect<S>(self, domain: &str, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
+ where
+ S: Read + Write,
+ {
+ self.into_ssl(domain)?.connect(stream)
+ }
+}
+
+impl Deref for ConnectConfiguration {
+ type Target = SslRef;
+
+ fn deref(&self) -> &SslRef {
+ &self.ssl
+ }
+}
+
+impl DerefMut for ConnectConfiguration {
+ fn deref_mut(&mut self) -> &mut SslRef {
+ &mut self.ssl
+ }
+}
+
+/// A type which wraps server-side streams in a TLS session.
+///
+/// OpenSSL's default configuration is highly insecure. This connector manages the OpenSSL
+/// structures, configuring cipher suites, session options, and more.
+#[derive(Clone)]
+pub struct SslAcceptor(SslContext);
+
+impl SslAcceptor {
+ /// Creates a new builder configured to connect to non-legacy clients. This should generally be
+ /// considered a reasonable default choice.
+ ///
+ /// This corresponds to the intermediate configuration of version 5 of Mozilla's server side TLS
+ /// recommendations. See its [documentation][docs] for more details on specifics.
+ ///
+ /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS
+ pub fn mozilla_intermediate_v5(method: SslMethod) -> Result<SslAcceptorBuilder, ErrorStack> {
+ let mut ctx = ctx(method)?;
+ ctx.set_options(SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1);
+ let dh = Dh::params_from_pem(FFDHE_2048.as_bytes())?;
+ ctx.set_tmp_dh(&dh)?;
+ setup_curves(&mut ctx)?;
+ ctx.set_cipher_list(
+ "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:\
+ ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\
+ DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
+ )?;
+ #[cfg(any(ossl111, libressl340))]
+ ctx.set_ciphersuites(
+ "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256",
+ )?;
+ Ok(SslAcceptorBuilder(ctx))
+ }
+
+ /// Creates a new builder configured to connect to modern clients.
+ ///
+ /// This corresponds to the modern configuration of version 5 of Mozilla's server side TLS recommendations.
+ /// See its [documentation][docs] for more details on specifics.
+ ///
+ /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
+ ///
+ /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS
+ #[cfg(any(ossl111, libressl340))]
+ pub fn mozilla_modern_v5(method: SslMethod) -> Result<SslAcceptorBuilder, ErrorStack> {
+ let mut ctx = ctx(method)?;
+ ctx.set_min_proto_version(Some(SslVersion::TLS1_3))?;
+ ctx.set_ciphersuites(
+ "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256",
+ )?;
+ Ok(SslAcceptorBuilder(ctx))
+ }
+
+ /// Creates a new builder configured to connect to non-legacy clients. This should generally be
+ /// considered a reasonable default choice.
+ ///
+ /// This corresponds to the intermediate configuration of version 4 of Mozilla's server side TLS
+ /// recommendations. See its [documentation][docs] for more details on specifics.
+ ///
+ /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS
+ // FIXME remove in next major version
+ pub fn mozilla_intermediate(method: SslMethod) -> Result<SslAcceptorBuilder, ErrorStack> {
+ let mut ctx = ctx(method)?;
+ ctx.set_options(SslOptions::CIPHER_SERVER_PREFERENCE);
+ #[cfg(any(ossl111, libressl340))]
+ ctx.set_options(SslOptions::NO_TLSV1_3);
+ let dh = Dh::params_from_pem(FFDHE_2048.as_bytes())?;
+ ctx.set_tmp_dh(&dh)?;
+ setup_curves(&mut ctx)?;
+ ctx.set_cipher_list(
+ "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:\
+ ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\
+ DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:\
+ ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:\
+ ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:\
+ DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:\
+ EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:\
+ AES256-SHA:DES-CBC3-SHA:!DSS",
+ )?;
+ Ok(SslAcceptorBuilder(ctx))
+ }
+
+ /// Creates a new builder configured to connect to modern clients.
+ ///
+ /// This corresponds to the modern configuration of version 4 of Mozilla's server side TLS recommendations.
+ /// See its [documentation][docs] for more details on specifics.
+ ///
+ /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS
+ // FIXME remove in next major version
+ pub fn mozilla_modern(method: SslMethod) -> Result<SslAcceptorBuilder, ErrorStack> {
+ let mut ctx = ctx(method)?;
+ ctx.set_options(
+ SslOptions::CIPHER_SERVER_PREFERENCE | SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1,
+ );
+ #[cfg(any(ossl111, libressl340))]
+ ctx.set_options(SslOptions::NO_TLSV1_3);
+ setup_curves(&mut ctx)?;
+ ctx.set_cipher_list(
+ "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:\
+ ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\
+ ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256",
+ )?;
+ Ok(SslAcceptorBuilder(ctx))
+ }
+
+ /// Initiates a server-side TLS session on a stream.
+ pub fn accept<S>(&self, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
+ where
+ S: Read + Write,
+ {
+ let ssl = Ssl::new(&self.0)?;
+ ssl.accept(stream)
+ }
+
+ /// Consumes the `SslAcceptor`, returning the inner raw `SslContext`.
+ pub fn into_context(self) -> SslContext {
+ self.0
+ }
+
+ /// Returns a shared reference to the inner raw `SslContext`.
+ pub fn context(&self) -> &SslContextRef {
+ &self.0
+ }
+}
+
+/// A builder for `SslAcceptor`s.
+pub struct SslAcceptorBuilder(SslContextBuilder);
+
+impl SslAcceptorBuilder {
+ /// Consumes the builder, returning a `SslAcceptor`.
+ pub fn build(self) -> SslAcceptor {
+ SslAcceptor(self.0.build())
+ }
+}
+
+impl Deref for SslAcceptorBuilder {
+ type Target = SslContextBuilder;
+
+ fn deref(&self) -> &SslContextBuilder {
+ &self.0
+ }
+}
+
+impl DerefMut for SslAcceptorBuilder {
+ fn deref_mut(&mut self) -> &mut SslContextBuilder {
+ &mut self.0
+ }
+}
+
+cfg_if! {
+ if #[cfg(ossl110)] {
+ #[allow(clippy::unnecessary_wraps)]
+ 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 crate::ec::EcKey;
+ use crate::nid::Nid;
+
+ let curve = EcKey::from_curve_name(Nid::X9_62_PRIME256V1)?;
+ ctx.set_tmp_ecdh(&curve)
+ }
+ }
+}
+
+cfg_if! {
+ if #[cfg(any(ossl102, libressl261))] {
+ fn setup_verify(ctx: &mut SslContextBuilder) {
+ ctx.set_verify(SslVerifyMode::PEER);
+ }
+
+ fn setup_verify_hostname(ssl: &mut SslRef, domain: &str) -> Result<(), ErrorStack> {
+ use crate::x509::verify::X509CheckFlags;
+
+ 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);
+ }
+
+ fn setup_verify_hostname(ssl: &mut Ssl, domain: &str) -> Result<(), ErrorStack> {
+ let domain = domain.to_string();
+ let hostname_idx = verify::try_get_hostname_idx()?;
+ ssl.set_ex_data(*hostname_idx, domain);
+ Ok(())
+ }
+
+ mod verify {
+ use std::net::IpAddr;
+ use std::str;
+ use once_cell::sync::OnceCell;
+
+ use crate::error::ErrorStack;
+ use crate::ex_data::Index;
+ use crate::nid::Nid;
+ use crate::ssl::Ssl;
+ use crate::stack::Stack;
+ use crate::x509::{
+ GeneralName, X509NameRef, X509Ref, X509StoreContext, X509StoreContextRef,
+ X509VerifyResult,
+ };
+
+ static HOSTNAME_IDX: OnceCell<Index<Ssl, String>> = OnceCell::new();
+
+ pub fn try_get_hostname_idx() -> Result<&'static Index<Ssl, String>, ErrorStack> {
+ HOSTNAME_IDX.get_or_try_init(Ssl::new_ex_index)
+ }
+
+ pub fn verify_callback(preverify_ok: bool, x509_ctx: &mut X509StoreContextRef) -> bool {
+ if !preverify_ok || x509_ctx.error_depth() != 0 {
+ return preverify_ok;
+ }
+
+ let hostname_idx =
+ try_get_hostname_idx().expect("failed to initialize hostname index");
+ 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);
+ }
+
+ ok
+ }
+
+ 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<GeneralName>) -> 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;
+ }
+ }
+ }
+ }
+ }
+
+ 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::<IpAddr>() {
+ Ok(ip) => pattern
+ .parse::<IpAddr>()
+ .ok()
+ .map_or(false, |pattern| pattern == ip),
+ Err(_) => matches_dns(pattern, domain),
+ }
+ }
+ 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];
+ }
+
+ matches_wildcard(pattern, hostname).unwrap_or_else(|| pattern.eq_ignore_ascii_case(hostname))
+ }
+
+ fn matches_wildcard(pattern: &str, hostname: &str) -> Option<bool> {
+ 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;
+ }
+
+ // Wildcards can only be in the first component, and must be the entire first label
+ if wildcard_location != 0 || wildcard_end != wildcard_location + 1 {
+ return None;
+ }
+
+ let hostname_label_end = match hostname.find('.') {
+ Some(l) => l,
+ None => return None,
+ };
+
+ let pattern_after_wildcard = &pattern[wildcard_end..];
+ let hostname_after_wildcard = &hostname[hostname_label_end..];
+
+ Some(pattern_after_wildcard.eq_ignore_ascii_case(hostname_after_wildcard))
+ }
+
+ fn matches_ip(expected: &IpAddr, actual: &[u8]) -> bool {
+ match *expected {
+ IpAddr::V4(ref addr) => actual == addr.octets(),
+ IpAddr::V6(ref addr) => actual == addr.octets(),
+ }
+ }
+
+ #[test]
+ fn test_dns_match() {
+ use crate::ssl::connector::verify::matches_dns;
+ assert!(matches_dns("website.tld", "website.tld")); // A name should match itself.
+ assert!(matches_dns("website.tld", "wEbSiTe.tLd")); // DNS name matching ignores case of hostname.
+ assert!(matches_dns("wEbSiTe.TlD", "website.tld")); // DNS name matching ignores case of subject.
+
+ assert!(matches_dns("xn--bcher-kva.tld", "xn--bcher-kva.tld")); // Likewise, nothing special to punycode names.
+ assert!(matches_dns("xn--bcher-kva.tld", "xn--BcHer-Kva.tLd")); // And punycode must be compared similarly case-insensitively.
+
+ assert!(matches_dns("*.example.com", "subdomain.example.com")); // Wildcard matching works.
+ assert!(matches_dns("*.eXaMpLe.cOm", "subdomain.example.com")); // Wildcard matching ignores case of subject.
+ assert!(matches_dns("*.example.com", "sUbDoMaIn.eXaMpLe.cOm")); // Wildcard matching ignores case of hostname.
+
+ assert!(!matches_dns("prefix*.example.com", "p.example.com")); // Prefix longer than the label works and does not match.
+ assert!(!matches_dns("*suffix.example.com", "s.example.com")); // Suffix longer than the label works and does not match.
+
+ assert!(!matches_dns("prefix*.example.com", "prefix.example.com")); // Partial wildcards do not work.
+ assert!(!matches_dns("*suffix.example.com", "suffix.example.com")); // Partial wildcards do not work.
+
+ assert!(!matches_dns("prefix*.example.com", "prefixdomain.example.com")); // Partial wildcards do not work.
+ assert!(!matches_dns("*suffix.example.com", "domainsuffix.example.com")); // Partial wildcards do not work.
+
+ assert!(!matches_dns("xn--*.example.com", "subdomain.example.com")); // Punycode domains with wildcard parts do not match.
+ assert!(!matches_dns("xN--*.example.com", "subdomain.example.com")); // And we can't bypass a punycode test with weird casing.
+ assert!(!matches_dns("Xn--*.example.com", "subdomain.example.com")); // And we can't bypass a punycode test with weird casing.
+ assert!(!matches_dns("XN--*.example.com", "subdomain.example.com")); // And we can't bypass a punycode test with weird casing.
+ }
+ }
+ }
+}
diff --git a/vendor/openssl/src/ssl/error.rs b/vendor/openssl/src/ssl/error.rs
new file mode 100644
index 000000000..556583354
--- /dev/null
+++ b/vendor/openssl/src/ssl/error.rs
@@ -0,0 +1,185 @@
+use libc::c_int;
+use std::error;
+use std::error::Error as StdError;
+use std::fmt;
+use std::io;
+
+use crate::error::ErrorStack;
+use crate::ssl::MidHandshakeSslStream;
+use crate::x509::X509VerifyResult;
+
+/// An error code returned from SSL functions.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct ErrorCode(c_int);
+
+impl ErrorCode {
+ /// The SSL session has been closed.
+ pub const ZERO_RETURN: ErrorCode = ErrorCode(ffi::SSL_ERROR_ZERO_RETURN);
+
+ /// An attempt to read data from the underlying socket returned `WouldBlock`.
+ ///
+ /// Wait for read readiness and retry the operation.
+ pub const WANT_READ: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_READ);
+
+ /// An attempt to write data to the underlying socket returned `WouldBlock`.
+ ///
+ /// Wait for write readiness and retry the operation.
+ pub const WANT_WRITE: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_WRITE);
+
+ /// A non-recoverable IO error occurred.
+ pub const SYSCALL: ErrorCode = ErrorCode(ffi::SSL_ERROR_SYSCALL);
+
+ /// An error occurred in the SSL library.
+ pub const SSL: ErrorCode = ErrorCode(ffi::SSL_ERROR_SSL);
+
+ /// The client hello callback indicated that it needed to be retried.
+ ///
+ /// Requires OpenSSL 1.1.1 or newer.
+ #[cfg(ossl111)]
+ pub const WANT_CLIENT_HELLO_CB: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_CLIENT_HELLO_CB);
+
+ pub fn from_raw(raw: c_int) -> ErrorCode {
+ ErrorCode(raw)
+ }
+
+ #[allow(clippy::trivially_copy_pass_by_ref)]
+ pub fn as_raw(&self) -> c_int {
+ self.0
+ }
+}
+
+#[derive(Debug)]
+pub(crate) enum InnerError {
+ Io(io::Error),
+ Ssl(ErrorStack),
+}
+
+/// An SSL error.
+#[derive(Debug)]
+pub struct Error {
+ pub(crate) code: ErrorCode,
+ pub(crate) cause: Option<InnerError>,
+}
+
+impl Error {
+ pub fn code(&self) -> ErrorCode {
+ self.code
+ }
+
+ pub fn io_error(&self) -> Option<&io::Error> {
+ match self.cause {
+ Some(InnerError::Io(ref e)) => Some(e),
+ _ => None,
+ }
+ }
+
+ pub fn into_io_error(self) -> Result<io::Error, Error> {
+ match self.cause {
+ Some(InnerError::Io(e)) => Ok(e),
+ _ => Err(self),
+ }
+ }
+
+ pub fn ssl_error(&self) -> Option<&ErrorStack> {
+ match self.cause {
+ Some(InnerError::Ssl(ref e)) => Some(e),
+ _ => None,
+ }
+ }
+}
+
+impl From<ErrorStack> for Error {
+ fn from(e: ErrorStack) -> Error {
+ Error {
+ code: ErrorCode::SSL,
+ cause: Some(InnerError::Ssl(e)),
+ }
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self.code {
+ ErrorCode::ZERO_RETURN => fmt.write_str("the SSL session has been shut down"),
+ ErrorCode::WANT_READ => match self.io_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, "{}", err),
+ None => fmt.write_str("unexpected EOF"),
+ },
+ 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),
+ }
+ }
+}
+
+impl error::Error for Error {
+ fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+ match self.cause {
+ Some(InnerError::Io(ref e)) => Some(e),
+ Some(InnerError::Ssl(ref e)) => Some(e),
+ None => None,
+ }
+ }
+}
+
+/// An error or intermediate state after a TLS handshake attempt.
+// FIXME overhaul
+#[derive(Debug)]
+pub enum HandshakeError<S> {
+ /// Setup failed.
+ SetupFailure(ErrorStack),
+ /// The handshake failed.
+ Failure(MidHandshakeSslStream<S>),
+ /// The handshake encountered a `WouldBlock` error midway through.
+ ///
+ /// This error will never be returned for blocking streams.
+ WouldBlock(MidHandshakeSslStream<S>),
+}
+
+impl<S: fmt::Debug> StdError for HandshakeError<S> {
+ fn source(&self) -> Option<&(dyn StdError + 'static)> {
+ match *self {
+ HandshakeError::SetupFailure(ref e) => Some(e),
+ HandshakeError::Failure(ref s) | HandshakeError::WouldBlock(ref s) => Some(s.error()),
+ }
+ }
+}
+
+impl<S: fmt::Debug> fmt::Display for HandshakeError<S> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ HandshakeError::SetupFailure(ref e) => write!(f, "stream setup failed: {}", e)?,
+ HandshakeError::Failure(ref s) => {
+ write!(f, "the handshake failed: {}", s.error())?;
+ let verify = s.ssl().verify_result();
+ if verify != X509VerifyResult::OK {
+ write!(f, ": {}", verify)?;
+ }
+ }
+ HandshakeError::WouldBlock(ref s) => {
+ write!(f, "the handshake was interrupted: {}", s.error())?;
+ let verify = s.ssl().verify_result();
+ if verify != X509VerifyResult::OK {
+ write!(f, ": {}", verify)?;
+ }
+ }
+ }
+ Ok(())
+ }
+}
+
+impl<S> From<ErrorStack> for HandshakeError<S> {
+ fn from(e: ErrorStack) -> HandshakeError<S> {
+ HandshakeError::SetupFailure(e)
+ }
+}
diff --git a/vendor/openssl/src/ssl/mod.rs b/vendor/openssl/src/ssl/mod.rs
new file mode 100644
index 000000000..6ef356d36
--- /dev/null
+++ b/vendor/openssl/src/ssl/mod.rs
@@ -0,0 +1,4131 @@
+//! SSL/TLS support.
+//!
+//! `SslConnector` and `SslAcceptor` should be used in most cases - they handle
+//! configuration of the OpenSSL primitives for you.
+//!
+//! # Examples
+//!
+//! To connect as a client to a remote server:
+//!
+//! ```no_run
+//! use openssl::ssl::{SslMethod, SslConnector};
+//! use std::io::{Read, Write};
+//! use std::net::TcpStream;
+//!
+//! let connector = SslConnector::builder(SslMethod::tls()).unwrap().build();
+//!
+//! let stream = TcpStream::connect("google.com:443").unwrap();
+//! let mut stream = connector.connect("google.com", stream).unwrap();
+//!
+//! stream.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap();
+//! let mut res = vec![];
+//! stream.read_to_end(&mut res).unwrap();
+//! println!("{}", String::from_utf8_lossy(&res));
+//! ```
+//!
+//! To accept connections as a server from remote clients:
+//!
+//! ```no_run
+//! use openssl::ssl::{SslMethod, SslAcceptor, SslStream, SslFiletype};
+//! use std::net::{TcpListener, TcpStream};
+//! use std::sync::Arc;
+//! use std::thread;
+//!
+//!
+//! let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
+//! acceptor.set_private_key_file("key.pem", SslFiletype::PEM).unwrap();
+//! acceptor.set_certificate_chain_file("certs.pem").unwrap();
+//! acceptor.check_private_key().unwrap();
+//! let acceptor = Arc::new(acceptor.build());
+//!
+//! let listener = TcpListener::bind("0.0.0.0:8443").unwrap();
+//!
+//! fn handle_client(stream: SslStream<TcpStream>) {
+//! // ...
+//! }
+//!
+//! for stream in listener.incoming() {
+//! match stream {
+//! Ok(stream) => {
+//! let acceptor = acceptor.clone();
+//! thread::spawn(move || {
+//! let stream = acceptor.accept(stream).unwrap();
+//! handle_client(stream);
+//! });
+//! }
+//! Err(e) => { /* connection failed */ }
+//! }
+//! }
+//! ```
+use crate::dh::{Dh, DhRef};
+#[cfg(all(ossl101, not(ossl110)))]
+use crate::ec::EcKey;
+use crate::ec::EcKeyRef;
+use crate::error::ErrorStack;
+use crate::ex_data::Index;
+#[cfg(ossl111)]
+use crate::hash::MessageDigest;
+#[cfg(any(ossl110, libressl270))]
+use crate::nid::Nid;
+use crate::pkey::{HasPrivate, PKeyRef, Params, Private};
+use crate::srtp::{SrtpProtectionProfile, SrtpProtectionProfileRef};
+use crate::ssl::bio::BioMethod;
+use crate::ssl::callbacks::*;
+use crate::ssl::error::InnerError;
+use crate::stack::{Stack, StackRef};
+use crate::util::{ForeignTypeExt, ForeignTypeRefExt};
+use crate::x509::store::{X509Store, X509StoreBuilderRef, X509StoreRef};
+#[cfg(any(ossl102, libressl261))]
+use crate::x509::verify::X509VerifyParamRef;
+use crate::x509::{X509Name, X509Ref, X509StoreContextRef, X509VerifyResult, X509};
+use crate::{cvt, cvt_n, cvt_p, init};
+use bitflags::bitflags;
+use cfg_if::cfg_if;
+use foreign_types::{ForeignType, ForeignTypeRef, Opaque};
+use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_void};
+use once_cell::sync::{Lazy, OnceCell};
+use openssl_macros::corresponds;
+use std::any::TypeId;
+use std::cmp;
+use std::collections::HashMap;
+use std::ffi::{CStr, CString};
+use std::fmt;
+use std::io;
+use std::io::prelude::*;
+use std::marker::PhantomData;
+use std::mem::{self, ManuallyDrop};
+use std::ops::{Deref, DerefMut};
+use std::panic::resume_unwind;
+use std::path::Path;
+use std::ptr;
+use std::slice;
+use std::str;
+use std::sync::{Arc, Mutex};
+
+pub use crate::ssl::connector::{
+ ConnectConfiguration, SslAcceptor, SslAcceptorBuilder, SslConnector, SslConnectorBuilder,
+};
+pub use crate::ssl::error::{Error, ErrorCode, HandshakeError};
+
+mod bio;
+mod callbacks;
+mod connector;
+mod error;
+#[cfg(test)]
+mod test;
+
+/// Returns the OpenSSL name of a cipher corresponding to an RFC-standard cipher name.
+///
+/// If the cipher has no corresponding OpenSSL name, the string `(NONE)` is returned.
+///
+/// Requires OpenSSL 1.1.1 or newer.
+#[corresponds(OPENSSL_cipher_name)]
+#[cfg(ossl111)]
+pub fn cipher_name(std_name: &str) -> &'static str {
+ unsafe {
+ ffi::init();
+
+ let s = CString::new(std_name).unwrap();
+ let ptr = ffi::OPENSSL_cipher_name(s.as_ptr());
+ CStr::from_ptr(ptr).to_str().unwrap()
+ }
+}
+
+cfg_if! {
+ if #[cfg(ossl300)] {
+ type SslOptionsRepr = u64;
+ } else if #[cfg(boringssl)] {
+ type SslOptionsRepr = u32;
+ } else {
+ type SslOptionsRepr = libc::c_ulong;
+ }
+}
+
+bitflags! {
+ /// Options controlling the behavior of an `SslContext`.
+ pub struct SslOptions: SslOptionsRepr {
+ /// Disables a countermeasure against an SSLv3/TLSv1.0 vulnerability affecting CBC ciphers.
+ const DONT_INSERT_EMPTY_FRAGMENTS = ffi::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS as SslOptionsRepr;
+
+ /// A "reasonable default" set of options which enables compatibility flags.
+ #[cfg(not(boringssl))]
+ const ALL = ffi::SSL_OP_ALL as SslOptionsRepr;
+
+ /// Do not query the MTU.
+ ///
+ /// Only affects DTLS connections.
+ const NO_QUERY_MTU = ffi::SSL_OP_NO_QUERY_MTU as SslOptionsRepr;
+
+ /// Enables Cookie Exchange as described in [RFC 4347 Section 4.2.1].
+ ///
+ /// Only affects DTLS connections.
+ ///
+ /// [RFC 4347 Section 4.2.1]: https://tools.ietf.org/html/rfc4347#section-4.2.1
+ #[cfg(not(boringssl))]
+ const COOKIE_EXCHANGE = ffi::SSL_OP_COOKIE_EXCHANGE as SslOptionsRepr;
+
+ /// Disables the use of session tickets for session resumption.
+ const NO_TICKET = ffi::SSL_OP_NO_TICKET as SslOptionsRepr;
+
+ /// Always start a new session when performing a renegotiation on the server side.
+ #[cfg(not(boringssl))]
+ const NO_SESSION_RESUMPTION_ON_RENEGOTIATION =
+ ffi::SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION as SslOptionsRepr;
+
+ /// Disables the use of TLS compression.
+ #[cfg(not(boringssl))]
+ const NO_COMPRESSION = ffi::SSL_OP_NO_COMPRESSION as SslOptionsRepr;
+
+ /// Allow legacy insecure renegotiation with servers or clients that do not support secure
+ /// renegotiation.
+ const ALLOW_UNSAFE_LEGACY_RENEGOTIATION =
+ ffi::SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION as SslOptionsRepr;
+
+ /// Creates a new key for each session when using ECDHE.
+ ///
+ /// This is always enabled in OpenSSL 1.1.0.
+ const SINGLE_ECDH_USE = ffi::SSL_OP_SINGLE_ECDH_USE as SslOptionsRepr;
+
+ /// Creates a new key for each session when using DHE.
+ ///
+ /// This is always enabled in OpenSSL 1.1.0.
+ const SINGLE_DH_USE = ffi::SSL_OP_SINGLE_DH_USE as SslOptionsRepr;
+
+ /// Use the server's preferences rather than the client's when selecting a cipher.
+ ///
+ /// This has no effect on the client side.
+ const CIPHER_SERVER_PREFERENCE = ffi::SSL_OP_CIPHER_SERVER_PREFERENCE as SslOptionsRepr;
+
+ /// Disables version rollback attach detection.
+ const TLS_ROLLBACK_BUG = ffi::SSL_OP_TLS_ROLLBACK_BUG as SslOptionsRepr;
+
+ /// Disables the use of SSLv2.
+ const NO_SSLV2 = ffi::SSL_OP_NO_SSLv2 as SslOptionsRepr;
+
+ /// Disables the use of SSLv3.
+ const NO_SSLV3 = ffi::SSL_OP_NO_SSLv3 as SslOptionsRepr;
+
+ /// Disables the use of TLSv1.0.
+ const NO_TLSV1 = ffi::SSL_OP_NO_TLSv1 as SslOptionsRepr;
+
+ /// Disables the use of TLSv1.1.
+ const NO_TLSV1_1 = ffi::SSL_OP_NO_TLSv1_1 as SslOptionsRepr;
+
+ /// Disables the use of TLSv1.2.
+ const NO_TLSV1_2 = ffi::SSL_OP_NO_TLSv1_2 as SslOptionsRepr;
+
+ /// Disables the use of TLSv1.3.
+ ///
+ /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
+ #[cfg(any(boringssl, ossl111, libressl340))]
+ const NO_TLSV1_3 = ffi::SSL_OP_NO_TLSv1_3 as SslOptionsRepr;
+
+ /// Disables the use of DTLSv1.0
+ ///
+ /// Requires OpenSSL 1.0.2 or LibreSSL 3.3.2 or newer.
+ #[cfg(any(boringssl, ossl102, ossl110, libressl332))]
+ const NO_DTLSV1 = ffi::SSL_OP_NO_DTLSv1 as SslOptionsRepr;
+
+ /// Disables the use of DTLSv1.2.
+ ///
+ /// Requires OpenSSL 1.0.2 or LibreSSL 3.3.2 or newer.
+ #[cfg(any(boringssl, ossl102, ossl110, libressl332))]
+ const NO_DTLSV1_2 = ffi::SSL_OP_NO_DTLSv1_2 as SslOptionsRepr;
+
+ /// Disables the use of all (D)TLS protocol versions.
+ ///
+ /// This can be used as a mask when whitelisting protocol versions.
+ ///
+ /// Requires OpenSSL 1.0.2 or newer.
+ ///
+ /// # Examples
+ ///
+ /// Only support TLSv1.2:
+ ///
+ /// ```rust
+ /// use openssl::ssl::SslOptions;
+ ///
+ /// let options = SslOptions::NO_SSL_MASK & !SslOptions::NO_TLSV1_2;
+ /// ```
+ #[cfg(any(ossl102, ossl110))]
+ const NO_SSL_MASK = ffi::SSL_OP_NO_SSL_MASK as SslOptionsRepr;
+
+ /// Disallow all renegotiation in TLSv1.2 and earlier.
+ ///
+ /// Requires OpenSSL 1.1.0h or newer.
+ #[cfg(any(boringssl, ossl110h))]
+ const NO_RENEGOTIATION = ffi::SSL_OP_NO_RENEGOTIATION as SslOptionsRepr;
+
+ /// Enable TLSv1.3 Compatibility mode.
+ ///
+ /// Requires OpenSSL 1.1.1 or newer. This is on by default in 1.1.1, but a future version
+ /// may have this disabled by default.
+ #[cfg(ossl111)]
+ const ENABLE_MIDDLEBOX_COMPAT = ffi::SSL_OP_ENABLE_MIDDLEBOX_COMPAT as SslOptionsRepr;
+
+ /// Prioritize ChaCha ciphers when preferred by clients.
+ ///
+ /// Temporarily reprioritize ChaCha20-Poly1305 ciphers to the top of the server cipher list
+ /// if a ChaCha20-Poly1305 cipher is at the top of the client cipher list. This helps those
+ /// clients (e.g. mobile) use ChaCha20-Poly1305 if that cipher is anywhere in the server
+ /// cipher list; but still allows other clients to use AES and other ciphers.
+ ///
+ /// Requires enable [`SslOptions::CIPHER_SERVER_PREFERENCE`].
+ /// Requires OpenSSL 1.1.1 or newer.
+ ///
+ /// [`SslOptions::CIPHER_SERVER_PREFERENCE`]: struct.SslOptions.html#associatedconstant.CIPHER_SERVER_PREFERENCE
+ #[cfg(ossl111)]
+ const PRIORITIZE_CHACHA = ffi::SSL_OP_PRIORITIZE_CHACHA as SslOptionsRepr;
+ }
+}
+
+bitflags! {
+ /// Options controlling the behavior of an `SslContext`.
+ pub struct SslMode: SslBitType {
+ /// Enables "short writes".
+ ///
+ /// Normally, a write in OpenSSL will always write out all of the requested data, even if it
+ /// requires more than one TLS record or write to the underlying stream. This option will
+ /// cause a write to return after writing a single TLS record instead.
+ const ENABLE_PARTIAL_WRITE = ffi::SSL_MODE_ENABLE_PARTIAL_WRITE;
+
+ /// Disables a check that the data buffer has not moved between calls when operating in a
+ /// non-blocking context.
+ const ACCEPT_MOVING_WRITE_BUFFER = ffi::SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER;
+
+ /// Enables automatic retries after TLS session events such as renegotiations or heartbeats.
+ ///
+ /// By default, OpenSSL will return a `WantRead` error after a renegotiation or heartbeat.
+ /// This option will cause OpenSSL to automatically continue processing the requested
+ /// operation instead.
+ ///
+ /// Note that `SslStream::read` and `SslStream::write` will automatically retry regardless
+ /// of the state of this option. It only affects `SslStream::ssl_read` and
+ /// `SslStream::ssl_write`.
+ const AUTO_RETRY = ffi::SSL_MODE_AUTO_RETRY;
+
+ /// Disables automatic chain building when verifying a peer's certificate.
+ ///
+ /// TLS peers are responsible for sending the entire certificate chain from the leaf to a
+ /// trusted root, but some will incorrectly not do so. OpenSSL will try to build the chain
+ /// out of certificates it knows of, and this option will disable that behavior.
+ const NO_AUTO_CHAIN = ffi::SSL_MODE_NO_AUTO_CHAIN;
+
+ /// Release memory buffers when the session does not need them.
+ ///
+ /// This saves ~34 KiB of memory for idle streams.
+ const RELEASE_BUFFERS = ffi::SSL_MODE_RELEASE_BUFFERS;
+
+ /// Sends the fake `TLS_FALLBACK_SCSV` cipher suite in the ClientHello message of a
+ /// handshake.
+ ///
+ /// This should only be enabled if a client has failed to connect to a server which
+ /// attempted to downgrade the protocol version of the session.
+ ///
+ /// Do not use this unless you know what you're doing!
+ #[cfg(not(libressl))]
+ const SEND_FALLBACK_SCSV = ffi::SSL_MODE_SEND_FALLBACK_SCSV;
+ }
+}
+
+/// A type specifying the kind of protocol an `SslContext` will speak.
+#[derive(Copy, Clone)]
+pub struct SslMethod(*const ffi::SSL_METHOD);
+
+impl SslMethod {
+ /// Support all versions of the TLS protocol.
+ #[corresponds(TLS_method)]
+ pub fn tls() -> SslMethod {
+ unsafe { SslMethod(TLS_method()) }
+ }
+
+ /// Support all versions of the DTLS protocol.
+ #[corresponds(DTLS_method)]
+ pub fn dtls() -> SslMethod {
+ unsafe { SslMethod(DTLS_method()) }
+ }
+
+ /// Support all versions of the TLS protocol, explicitly as a client.
+ #[corresponds(TLS_client_method)]
+ pub fn tls_client() -> SslMethod {
+ unsafe { SslMethod(TLS_client_method()) }
+ }
+
+ /// Support all versions of the TLS protocol, explicitly as a server.
+ #[corresponds(TLS_server_method)]
+ pub fn tls_server() -> SslMethod {
+ unsafe { SslMethod(TLS_server_method()) }
+ }
+
+ /// Constructs an `SslMethod` from a pointer to the underlying OpenSSL value.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure the pointer is valid.
+ pub unsafe fn from_ptr(ptr: *const ffi::SSL_METHOD) -> SslMethod {
+ SslMethod(ptr)
+ }
+
+ /// Returns a pointer to the underlying OpenSSL value.
+ #[allow(clippy::trivially_copy_pass_by_ref)]
+ pub fn as_ptr(&self) -> *const ffi::SSL_METHOD {
+ self.0
+ }
+}
+
+unsafe impl Sync for SslMethod {}
+unsafe impl Send for SslMethod {}
+
+bitflags! {
+ /// Options controlling the behavior of certificate verification.
+ pub struct SslVerifyMode: i32 {
+ /// Verifies that the peer's certificate is trusted.
+ ///
+ /// On the server side, this will cause OpenSSL to request a certificate from the client.
+ const PEER = ffi::SSL_VERIFY_PEER;
+
+ /// Disables verification of the peer's certificate.
+ ///
+ /// On the server side, this will cause OpenSSL to not request a certificate from the
+ /// client. On the client side, the certificate will be checked for validity, but the
+ /// negotiation will continue regardless of the result of that check.
+ const NONE = ffi::SSL_VERIFY_NONE;
+
+ /// On the server side, abort the handshake if the client did not send a certificate.
+ ///
+ /// This should be paired with `SSL_VERIFY_PEER`. It has no effect on the client side.
+ const FAIL_IF_NO_PEER_CERT = ffi::SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+ }
+}
+
+#[cfg(boringssl)]
+type SslBitType = c_int;
+#[cfg(not(boringssl))]
+type SslBitType = c_long;
+
+#[cfg(boringssl)]
+type SslTimeTy = u64;
+#[cfg(not(boringssl))]
+type SslTimeTy = c_long;
+
+bitflags! {
+ /// Options controlling the behavior of session caching.
+ pub struct SslSessionCacheMode: SslBitType {
+ /// No session caching for the client or server takes place.
+ const OFF = ffi::SSL_SESS_CACHE_OFF;
+
+ /// Enable session caching on the client side.
+ ///
+ /// OpenSSL has no way of identifying the proper session to reuse automatically, so the
+ /// application is responsible for setting it explicitly via [`SslRef::set_session`].
+ ///
+ /// [`SslRef::set_session`]: struct.SslRef.html#method.set_session
+ const CLIENT = ffi::SSL_SESS_CACHE_CLIENT;
+
+ /// Enable session caching on the server side.
+ ///
+ /// This is the default mode.
+ const SERVER = ffi::SSL_SESS_CACHE_SERVER;
+
+ /// Enable session caching on both the client and server side.
+ const BOTH = ffi::SSL_SESS_CACHE_BOTH;
+
+ /// Disable automatic removal of expired sessions from the session cache.
+ const NO_AUTO_CLEAR = ffi::SSL_SESS_CACHE_NO_AUTO_CLEAR;
+
+ /// Disable use of the internal session cache for session lookups.
+ const NO_INTERNAL_LOOKUP = ffi::SSL_SESS_CACHE_NO_INTERNAL_LOOKUP;
+
+ /// Disable use of the internal session cache for session storage.
+ const NO_INTERNAL_STORE = ffi::SSL_SESS_CACHE_NO_INTERNAL_STORE;
+
+ /// Disable use of the internal session cache for storage and lookup.
+ const NO_INTERNAL = ffi::SSL_SESS_CACHE_NO_INTERNAL;
+ }
+}
+
+#[cfg(ossl111)]
+bitflags! {
+ /// Which messages and under which conditions an extension should be added or expected.
+ pub struct ExtensionContext: c_uint {
+ /// This extension is only allowed in TLS
+ const TLS_ONLY = ffi::SSL_EXT_TLS_ONLY;
+ /// This extension is only allowed in DTLS
+ const DTLS_ONLY = ffi::SSL_EXT_DTLS_ONLY;
+ /// Some extensions may be allowed in DTLS but we don't implement them for it
+ const TLS_IMPLEMENTATION_ONLY = ffi::SSL_EXT_TLS_IMPLEMENTATION_ONLY;
+ /// Most extensions are not defined for SSLv3 but EXT_TYPE_renegotiate is
+ const SSL3_ALLOWED = ffi::SSL_EXT_SSL3_ALLOWED;
+ /// Extension is only defined for TLS1.2 and below
+ const TLS1_2_AND_BELOW_ONLY = ffi::SSL_EXT_TLS1_2_AND_BELOW_ONLY;
+ /// Extension is only defined for TLS1.3 and above
+ const TLS1_3_ONLY = ffi::SSL_EXT_TLS1_3_ONLY;
+ /// Ignore this extension during parsing if we are resuming
+ const IGNORE_ON_RESUMPTION = ffi::SSL_EXT_IGNORE_ON_RESUMPTION;
+ const CLIENT_HELLO = ffi::SSL_EXT_CLIENT_HELLO;
+ /// Really means TLS1.2 or below
+ const TLS1_2_SERVER_HELLO = ffi::SSL_EXT_TLS1_2_SERVER_HELLO;
+ const TLS1_3_SERVER_HELLO = ffi::SSL_EXT_TLS1_3_SERVER_HELLO;
+ const TLS1_3_ENCRYPTED_EXTENSIONS = ffi::SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS;
+ const TLS1_3_HELLO_RETRY_REQUEST = ffi::SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST;
+ const TLS1_3_CERTIFICATE = ffi::SSL_EXT_TLS1_3_CERTIFICATE;
+ const TLS1_3_NEW_SESSION_TICKET = ffi::SSL_EXT_TLS1_3_NEW_SESSION_TICKET;
+ const TLS1_3_CERTIFICATE_REQUEST = ffi::SSL_EXT_TLS1_3_CERTIFICATE_REQUEST;
+ }
+}
+
+/// An identifier of the format of a certificate or key file.
+#[derive(Copy, Clone)]
+pub struct SslFiletype(c_int);
+
+impl SslFiletype {
+ /// The PEM format.
+ ///
+ /// This corresponds to `SSL_FILETYPE_PEM`.
+ pub const PEM: SslFiletype = SslFiletype(ffi::SSL_FILETYPE_PEM);
+
+ /// The ASN1 format.
+ ///
+ /// This corresponds to `SSL_FILETYPE_ASN1`.
+ pub const ASN1: SslFiletype = SslFiletype(ffi::SSL_FILETYPE_ASN1);
+
+ /// Constructs an `SslFiletype` from a raw OpenSSL value.
+ pub fn from_raw(raw: c_int) -> SslFiletype {
+ SslFiletype(raw)
+ }
+
+ /// Returns the raw OpenSSL value represented by this type.
+ #[allow(clippy::trivially_copy_pass_by_ref)]
+ pub fn as_raw(&self) -> c_int {
+ self.0
+ }
+}
+
+/// An identifier of a certificate status type.
+#[derive(Copy, Clone)]
+pub struct StatusType(c_int);
+
+impl StatusType {
+ /// An OSCP status.
+ pub const OCSP: StatusType = StatusType(ffi::TLSEXT_STATUSTYPE_ocsp);
+
+ /// Constructs a `StatusType` from a raw OpenSSL value.
+ pub fn from_raw(raw: c_int) -> StatusType {
+ StatusType(raw)
+ }
+
+ /// Returns the raw OpenSSL value represented by this type.
+ #[allow(clippy::trivially_copy_pass_by_ref)]
+ pub fn as_raw(&self) -> c_int {
+ self.0
+ }
+}
+
+/// An identifier of a session name type.
+#[derive(Copy, Clone)]
+pub struct NameType(c_int);
+
+impl NameType {
+ /// A host name.
+ pub const HOST_NAME: NameType = NameType(ffi::TLSEXT_NAMETYPE_host_name);
+
+ /// Constructs a `StatusType` from a raw OpenSSL value.
+ pub fn from_raw(raw: c_int) -> StatusType {
+ StatusType(raw)
+ }
+
+ /// Returns the raw OpenSSL value represented by this type.
+ #[allow(clippy::trivially_copy_pass_by_ref)]
+ pub fn as_raw(&self) -> c_int {
+ self.0
+ }
+}
+
+static INDEXES: Lazy<Mutex<HashMap<TypeId, c_int>>> = Lazy::new(|| Mutex::new(HashMap::new()));
+static SSL_INDEXES: Lazy<Mutex<HashMap<TypeId, c_int>>> = Lazy::new(|| Mutex::new(HashMap::new()));
+static SESSION_CTX_INDEX: OnceCell<Index<Ssl, SslContext>> = OnceCell::new();
+
+fn try_get_session_ctx_index() -> Result<&'static Index<Ssl, SslContext>, ErrorStack> {
+ SESSION_CTX_INDEX.get_or_try_init(Ssl::new_ex_index)
+}
+
+unsafe extern "C" fn free_data_box<T>(
+ _parent: *mut c_void,
+ ptr: *mut c_void,
+ _ad: *mut ffi::CRYPTO_EX_DATA,
+ _idx: c_int,
+ _argl: c_long,
+ _argp: *mut c_void,
+) {
+ if !ptr.is_null() {
+ let _ = Box::<T>::from_raw(ptr as *mut T);
+ }
+}
+
+/// An error returned from the SNI callback.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct SniError(c_int);
+
+impl SniError {
+ /// Abort the handshake with a fatal alert.
+ pub const ALERT_FATAL: SniError = SniError(ffi::SSL_TLSEXT_ERR_ALERT_FATAL);
+
+ /// Send a warning alert to the client and continue the handshake.
+ pub const ALERT_WARNING: SniError = SniError(ffi::SSL_TLSEXT_ERR_ALERT_WARNING);
+
+ pub const NOACK: SniError = SniError(ffi::SSL_TLSEXT_ERR_NOACK);
+}
+
+/// An SSL/TLS alert.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct SslAlert(c_int);
+
+impl SslAlert {
+ /// Alert 112 - `unrecognized_name`.
+ pub const UNRECOGNIZED_NAME: SslAlert = SslAlert(ffi::SSL_AD_UNRECOGNIZED_NAME);
+ pub const ILLEGAL_PARAMETER: SslAlert = SslAlert(ffi::SSL_AD_ILLEGAL_PARAMETER);
+ pub const DECODE_ERROR: SslAlert = SslAlert(ffi::SSL_AD_DECODE_ERROR);
+}
+
+/// An error returned from an ALPN selection callback.
+///
+/// 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, libressl261))]
+impl AlpnError {
+ /// Terminate the handshake with a fatal alert.
+ ///
+ /// Requires OpenSSL 1.1.0 or newer.
+ #[cfg(any(ossl110))]
+ pub const ALERT_FATAL: AlpnError = AlpnError(ffi::SSL_TLSEXT_ERR_ALERT_FATAL);
+
+ /// Do not select a protocol, but continue the handshake.
+ pub const NOACK: AlpnError = AlpnError(ffi::SSL_TLSEXT_ERR_NOACK);
+}
+
+/// The result of a client hello callback.
+///
+/// Requires OpenSSL 1.1.1 or newer.
+#[cfg(ossl111)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct ClientHelloResponse(c_int);
+
+#[cfg(ossl111)]
+impl ClientHelloResponse {
+ /// Continue the handshake.
+ pub const SUCCESS: ClientHelloResponse = ClientHelloResponse(ffi::SSL_CLIENT_HELLO_SUCCESS);
+
+ /// Return from the handshake with an `ErrorCode::WANT_CLIENT_HELLO_CB` error.
+ pub const RETRY: ClientHelloResponse = ClientHelloResponse(ffi::SSL_CLIENT_HELLO_RETRY);
+}
+
+/// An SSL/TLS protocol version.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct SslVersion(c_int);
+
+impl SslVersion {
+ /// SSLv3
+ pub const SSL3: SslVersion = SslVersion(ffi::SSL3_VERSION);
+
+ /// TLSv1.0
+ pub const TLS1: SslVersion = SslVersion(ffi::TLS1_VERSION);
+
+ /// TLSv1.1
+ pub const TLS1_1: SslVersion = SslVersion(ffi::TLS1_1_VERSION);
+
+ /// TLSv1.2
+ pub const TLS1_2: SslVersion = SslVersion(ffi::TLS1_2_VERSION);
+
+ /// TLSv1.3
+ ///
+ /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
+ #[cfg(any(ossl111, libressl340))]
+ pub const TLS1_3: SslVersion = SslVersion(ffi::TLS1_3_VERSION);
+}
+
+cfg_if! {
+ if #[cfg(boringssl)] {
+ type SslCacheTy = i64;
+ type SslCacheSize = libc::c_ulong;
+ type MtuTy = u32;
+ type SizeTy = usize;
+ } else {
+ type SslCacheTy = i64;
+ type SslCacheSize = c_long;
+ type MtuTy = c_long;
+ type SizeTy = u32;
+ }
+}
+
+/// A standard implementation of protocol selection for Application Layer Protocol Negotiation
+/// (ALPN).
+///
+/// `server` should contain the server's list of supported protocols and `client` the client's. They
+/// must both be in the ALPN wire format. See the documentation for
+/// [`SslContextBuilder::set_alpn_protos`] for details.
+///
+/// It will select the first protocol supported by the server which is also supported by the client.
+///
+/// [`SslContextBuilder::set_alpn_protos`]: struct.SslContextBuilder.html#method.set_alpn_protos
+#[corresponds(SSL_select_next_proto)]
+pub fn select_next_proto<'a>(server: &[u8], client: &'a [u8]) -> Option<&'a [u8]> {
+ unsafe {
+ let mut out = ptr::null_mut();
+ let mut outlen = 0;
+ let r = ffi::SSL_select_next_proto(
+ &mut out,
+ &mut outlen,
+ server.as_ptr(),
+ server.len() as c_uint,
+ client.as_ptr(),
+ client.len() as c_uint,
+ );
+ if r == ffi::OPENSSL_NPN_NEGOTIATED {
+ Some(slice::from_raw_parts(out as *const u8, outlen as usize))
+ } else {
+ None
+ }
+ }
+}
+
+/// A builder for `SslContext`s.
+pub struct SslContextBuilder(SslContext);
+
+impl SslContextBuilder {
+ /// Creates a new `SslContextBuilder`.
+ #[corresponds(SSL_CTX_new)]
+ pub fn new(method: SslMethod) -> Result<SslContextBuilder, ErrorStack> {
+ unsafe {
+ init();
+ let ctx = cvt_p(ffi::SSL_CTX_new(method.as_ptr()))?;
+
+ Ok(SslContextBuilder::from_ptr(ctx))
+ }
+ }
+
+ /// Creates an `SslContextBuilder` from a pointer to a raw OpenSSL value.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that the pointer is valid and uniquely owned by the builder.
+ pub unsafe fn from_ptr(ctx: *mut ffi::SSL_CTX) -> SslContextBuilder {
+ SslContextBuilder(SslContext::from_ptr(ctx))
+ }
+
+ /// Returns a pointer to the raw OpenSSL value.
+ pub fn as_ptr(&self) -> *mut ffi::SSL_CTX {
+ self.0.as_ptr()
+ }
+
+ /// Configures the certificate verification method for new connections.
+ #[corresponds(SSL_CTX_set_verify)]
+ pub fn set_verify(&mut self, mode: SslVerifyMode) {
+ unsafe {
+ ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits as c_int, None);
+ }
+ }
+
+ /// Configures the certificate verification method for new connections and
+ /// registers a verification callback.
+ ///
+ /// The callback is passed a boolean indicating if OpenSSL's internal verification succeeded as
+ /// well as a reference to the `X509StoreContext` which can be used to examine the certificate
+ /// chain. It should return a boolean indicating if verification succeeded.
+ #[corresponds(SSL_CTX_set_verify)]
+ pub fn set_verify_callback<F>(&mut self, mode: SslVerifyMode, verify: F)
+ where
+ F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send,
+ {
+ unsafe {
+ self.set_ex_data(SslContext::cached_ex_index::<F>(), verify);
+ ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits as c_int, Some(raw_verify::<F>));
+ }
+ }
+
+ /// Configures the server name indication (SNI) callback for new connections.
+ ///
+ /// SNI is used to allow a single server to handle requests for multiple domains, each of which
+ /// has its own certificate chain and configuration.
+ ///
+ /// Obtain the server name with the `servername` method and then set the corresponding context
+ /// with `set_ssl_context`
+ #[corresponds(SSL_CTX_set_tlsext_servername_callback)]
+ // FIXME tlsext prefix?
+ pub fn set_servername_callback<F>(&mut self, callback: F)
+ where
+ F: Fn(&mut SslRef, &mut SslAlert) -> Result<(), SniError> + 'static + Sync + Send,
+ {
+ unsafe {
+ // The SNI callback is somewhat unique in that the callback associated with the original
+ // context associated with an SSL can be used even if the SSL's context has been swapped
+ // out. When that happens, we wouldn't be able to look up the callback's state in the
+ // context's ex data. Instead, pass the pointer directly as the servername arg. It's
+ // still stored in ex data to manage the lifetime.
+ let arg = self.set_ex_data_inner(SslContext::cached_ex_index::<F>(), callback);
+ ffi::SSL_CTX_set_tlsext_servername_arg(self.as_ptr(), arg);
+ #[cfg(boringssl)]
+ ffi::SSL_CTX_set_tlsext_servername_callback(self.as_ptr(), Some(raw_sni::<F>));
+ #[cfg(not(boringssl))]
+ ffi::SSL_CTX_set_tlsext_servername_callback__fixed_rust(
+ self.as_ptr(),
+ Some(raw_sni::<F>),
+ );
+ }
+ }
+
+ /// Sets the certificate verification depth.
+ ///
+ /// If the peer's certificate chain is longer than this value, verification will fail.
+ #[corresponds(SSL_CTX_set_verify_depth)]
+ pub fn set_verify_depth(&mut self, depth: u32) {
+ unsafe {
+ ffi::SSL_CTX_set_verify_depth(self.as_ptr(), depth as c_int);
+ }
+ }
+
+ /// Sets a custom certificate store for verifying peer certificates.
+ ///
+ /// Requires OpenSSL 1.0.2 or newer.
+ #[corresponds(SSL_CTX_set0_verify_cert_store)]
+ #[cfg(ossl102)]
+ pub fn set_verify_cert_store(&mut self, cert_store: X509Store) -> Result<(), ErrorStack> {
+ unsafe {
+ let ptr = cert_store.as_ptr();
+ cvt(ffi::SSL_CTX_set0_verify_cert_store(self.as_ptr(), ptr) as c_int)?;
+ mem::forget(cert_store);
+
+ Ok(())
+ }
+ }
+
+ /// Replaces the context's certificate store.
+ #[corresponds(SSL_CTX_set_cert_store)]
+ pub fn set_cert_store(&mut self, cert_store: X509Store) {
+ unsafe {
+ ffi::SSL_CTX_set_cert_store(self.as_ptr(), cert_store.as_ptr());
+ mem::forget(cert_store);
+ }
+ }
+
+ /// Controls read ahead behavior.
+ ///
+ /// If enabled, OpenSSL will read as much data as is available from the underlying stream,
+ /// instead of a single record at a time.
+ ///
+ /// It has no effect when used with DTLS.
+ #[corresponds(SSL_CTX_set_read_ahead)]
+ pub fn set_read_ahead(&mut self, read_ahead: bool) {
+ unsafe {
+ ffi::SSL_CTX_set_read_ahead(self.as_ptr(), read_ahead as SslBitType);
+ }
+ }
+
+ /// Sets the mode used by the context, returning the previous mode.
+ #[corresponds(SSL_CTX_set_mode)]
+ pub fn set_mode(&mut self, mode: SslMode) -> SslMode {
+ unsafe {
+ let bits = ffi::SSL_CTX_set_mode(self.as_ptr(), mode.bits() as MtuTy) as SslBitType;
+ SslMode { bits }
+ }
+ }
+
+ /// Sets the parameters to be used during ephemeral Diffie-Hellman key exchange.
+ #[corresponds(SSL_CTX_set_tmp_dh)]
+ pub fn set_tmp_dh(&mut self, dh: &DhRef<Params>) -> Result<(), ErrorStack> {
+ unsafe { cvt(ffi::SSL_CTX_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) }
+ }
+
+ /// Sets the callback which will generate parameters to be used during ephemeral Diffie-Hellman
+ /// key exchange.
+ ///
+ /// The callback is provided with a reference to the `Ssl` for the session, as well as a boolean
+ /// indicating if the selected cipher is export-grade, and the key length. The export and key
+ /// length options are archaic and should be ignored in almost all cases.
+ #[corresponds(SSL_CTX_set_tmp_dh_callback)]
+ pub fn set_tmp_dh_callback<F>(&mut self, callback: F)
+ where
+ F: Fn(&mut SslRef, bool, u32) -> Result<Dh<Params>, ErrorStack> + 'static + Sync + Send,
+ {
+ unsafe {
+ self.set_ex_data(SslContext::cached_ex_index::<F>(), callback);
+
+ #[cfg(not(boringssl))]
+ ffi::SSL_CTX_set_tmp_dh_callback__fixed_rust(self.as_ptr(), Some(raw_tmp_dh::<F>));
+ #[cfg(boringssl)]
+ ffi::SSL_CTX_set_tmp_dh_callback(self.as_ptr(), Some(raw_tmp_dh::<F>));
+ }
+ }
+
+ /// Sets the parameters to be used during ephemeral elliptic curve Diffie-Hellman key exchange.
+ #[corresponds(SSL_CTX_set_tmp_ecdh)]
+ pub fn set_tmp_ecdh(&mut self, key: &EcKeyRef<Params>) -> Result<(), ErrorStack> {
+ unsafe { cvt(ffi::SSL_CTX_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) }
+ }
+
+ /// Sets the callback which will generate parameters to be used during ephemeral elliptic curve
+ /// Diffie-Hellman key exchange.
+ ///
+ /// The callback is provided with a reference to the `Ssl` for the session, as well as a boolean
+ /// indicating if the selected cipher is export-grade, and the key length. The export and key
+ /// length options are archaic and should be ignored in almost all cases.
+ ///
+ /// Requires OpenSSL 1.0.1 or 1.0.2.
+ #[corresponds(SSL_CTX_set_tmp_ecdh_callback)]
+ #[cfg(all(ossl101, not(ossl110)))]
+ #[deprecated(note = "this function leaks memory and does not exist on newer OpenSSL versions")]
+ pub fn set_tmp_ecdh_callback<F>(&mut self, callback: F)
+ where
+ F: Fn(&mut SslRef, bool, u32) -> Result<EcKey<Params>, ErrorStack> + 'static + Sync + Send,
+ {
+ unsafe {
+ self.set_ex_data(SslContext::cached_ex_index::<F>(), callback);
+ ffi::SSL_CTX_set_tmp_ecdh_callback__fixed_rust(self.as_ptr(), Some(raw_tmp_ecdh::<F>));
+ }
+ }
+
+ /// Use the default locations of trusted certificates for verification.
+ ///
+ /// These locations are read from the `SSL_CERT_FILE` and `SSL_CERT_DIR` environment variables
+ /// if present, or defaults specified at OpenSSL build time otherwise.
+ #[corresponds(SSL_CTX_set_default_verify_paths)]
+ pub fn set_default_verify_paths(&mut self) -> Result<(), ErrorStack> {
+ unsafe { cvt(ffi::SSL_CTX_set_default_verify_paths(self.as_ptr())).map(|_| ()) }
+ }
+
+ /// Loads trusted root certificates from a file.
+ ///
+ /// The file should contain a sequence of PEM-formatted CA certificates.
+ #[corresponds(SSL_CTX_load_verify_locations)]
+ pub fn set_ca_file<P: AsRef<Path>>(&mut self, file: P) -> Result<(), ErrorStack> {
+ let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
+ unsafe {
+ cvt(ffi::SSL_CTX_load_verify_locations(
+ self.as_ptr(),
+ file.as_ptr() as *const _,
+ ptr::null(),
+ ))
+ .map(|_| ())
+ }
+ }
+
+ /// Sets the list of CA names sent to the client.
+ ///
+ /// The CA certificates must still be added to the trust root - they are not automatically set
+ /// as trusted by this method.
+ #[corresponds(SSL_CTX_set_client_CA_list)]
+ pub fn set_client_ca_list(&mut self, list: Stack<X509Name>) {
+ unsafe {
+ ffi::SSL_CTX_set_client_CA_list(self.as_ptr(), list.as_ptr());
+ mem::forget(list);
+ }
+ }
+
+ /// Add the provided CA certificate to the list sent by the server to the client when
+ /// requesting client-side TLS authentication.
+ #[corresponds(SSL_CTX_add_client_CA)]
+ pub fn add_client_ca(&mut self, cacert: &X509Ref) -> Result<(), ErrorStack> {
+ unsafe { cvt(ffi::SSL_CTX_add_client_CA(self.as_ptr(), cacert.as_ptr())).map(|_| ()) }
+ }
+
+ /// Set the context identifier for sessions.
+ ///
+ /// This value identifies the server's session cache to clients, telling them when they're
+ /// able to reuse sessions. It should be set to a unique value per server, unless multiple
+ /// servers share a session cache.
+ ///
+ /// This value should be set when using client certificates, or each request will fail its
+ /// handshake and need to be restarted.
+ #[corresponds(SSL_CTX_set_session_id_context)]
+ pub fn set_session_id_context(&mut self, sid_ctx: &[u8]) -> Result<(), ErrorStack> {
+ unsafe {
+ assert!(sid_ctx.len() <= c_uint::max_value() as usize);
+ cvt(ffi::SSL_CTX_set_session_id_context(
+ self.as_ptr(),
+ sid_ctx.as_ptr(),
+ sid_ctx.len() as SizeTy,
+ ))
+ .map(|_| ())
+ }
+ }
+
+ /// Loads a leaf certificate from a file.
+ ///
+ /// Only a single certificate will be loaded - use `add_extra_chain_cert` to add the remainder
+ /// of the certificate chain, or `set_certificate_chain_file` to load the entire chain from a
+ /// single file.
+ #[corresponds(SSL_CTX_use_certificate_file)]
+ pub fn set_certificate_file<P: AsRef<Path>>(
+ &mut self,
+ file: P,
+ file_type: SslFiletype,
+ ) -> Result<(), ErrorStack> {
+ let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
+ unsafe {
+ cvt(ffi::SSL_CTX_use_certificate_file(
+ self.as_ptr(),
+ file.as_ptr() as *const _,
+ file_type.as_raw(),
+ ))
+ .map(|_| ())
+ }
+ }
+
+ /// Loads a certificate chain from a file.
+ ///
+ /// The file should contain a sequence of PEM-formatted certificates, the first being the leaf
+ /// certificate, and the remainder forming the chain of certificates up to and including the
+ /// trusted root certificate.
+ #[corresponds(SSL_CTX_use_certificate_chain_file)]
+ pub fn set_certificate_chain_file<P: AsRef<Path>>(
+ &mut self,
+ file: P,
+ ) -> Result<(), ErrorStack> {
+ let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
+ unsafe {
+ cvt(ffi::SSL_CTX_use_certificate_chain_file(
+ self.as_ptr(),
+ file.as_ptr() as *const _,
+ ))
+ .map(|_| ())
+ }
+ }
+
+ /// Sets the leaf certificate.
+ ///
+ /// Use `add_extra_chain_cert` to add the remainder of the certificate chain.
+ #[corresponds(SSL_CTX_use_certificate)]
+ pub fn set_certificate(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> {
+ unsafe { cvt(ffi::SSL_CTX_use_certificate(self.as_ptr(), cert.as_ptr())).map(|_| ()) }
+ }
+
+ /// Appends a certificate to the certificate chain.
+ ///
+ /// This chain should contain all certificates necessary to go from the certificate specified by
+ /// `set_certificate` to a trusted root.
+ #[corresponds(SSL_CTX_add_extra_chain_cert)]
+ pub fn add_extra_chain_cert(&mut self, cert: X509) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::SSL_CTX_add_extra_chain_cert(self.as_ptr(), cert.as_ptr()) as c_int)?;
+ mem::forget(cert);
+ Ok(())
+ }
+ }
+
+ /// Loads the private key from a file.
+ #[corresponds(SSL_CTX_use_PrivateKey_file)]
+ pub fn set_private_key_file<P: AsRef<Path>>(
+ &mut self,
+ file: P,
+ file_type: SslFiletype,
+ ) -> Result<(), ErrorStack> {
+ let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
+ unsafe {
+ cvt(ffi::SSL_CTX_use_PrivateKey_file(
+ self.as_ptr(),
+ file.as_ptr() as *const _,
+ file_type.as_raw(),
+ ))
+ .map(|_| ())
+ }
+ }
+
+ /// Sets the private key.
+ #[corresponds(SSL_CTX_use_PrivateKey)]
+ pub fn set_private_key<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
+ where
+ T: HasPrivate,
+ {
+ unsafe { cvt(ffi::SSL_CTX_use_PrivateKey(self.as_ptr(), key.as_ptr())).map(|_| ()) }
+ }
+
+ /// Sets the list of supported ciphers for protocols before TLSv1.3.
+ ///
+ /// The `set_ciphersuites` method controls the cipher suites for TLSv1.3.
+ ///
+ /// See [`ciphers`] for details on the format.
+ ///
+ /// [`ciphers`]: https://www.openssl.org/docs/manmaster/apps/ciphers.html
+ #[corresponds(SSL_CTX_set_cipher_list)]
+ pub fn set_cipher_list(&mut self, cipher_list: &str) -> Result<(), ErrorStack> {
+ let cipher_list = CString::new(cipher_list).unwrap();
+ unsafe {
+ cvt(ffi::SSL_CTX_set_cipher_list(
+ self.as_ptr(),
+ cipher_list.as_ptr() as *const _,
+ ))
+ .map(|_| ())
+ }
+ }
+
+ /// Sets the list of supported ciphers for the TLSv1.3 protocol.
+ ///
+ /// The `set_cipher_list` method controls the cipher suites for protocols before TLSv1.3.
+ ///
+ /// The format consists of TLSv1.3 cipher suite names separated by `:` characters in order of
+ /// preference.
+ ///
+ /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
+ #[corresponds(SSL_CTX_set_ciphersuites)]
+ #[cfg(any(ossl111, libressl340))]
+ pub fn set_ciphersuites(&mut self, cipher_list: &str) -> Result<(), ErrorStack> {
+ let cipher_list = CString::new(cipher_list).unwrap();
+ unsafe {
+ cvt(ffi::SSL_CTX_set_ciphersuites(
+ self.as_ptr(),
+ cipher_list.as_ptr() as *const _,
+ ))
+ .map(|_| ())
+ }
+ }
+
+ /// Enables ECDHE key exchange with an automatically chosen curve list.
+ ///
+ /// Requires OpenSSL 1.0.2.
+ #[corresponds(SSL_CTX_set_ecdh_auto)]
+ #[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(|_| ()) }
+ }
+
+ /// Sets the options used by the context, returning the old set.
+ ///
+ /// # Note
+ ///
+ /// This *enables* the specified options, but does not disable unspecified options. Use
+ /// `clear_options` for that.
+ #[corresponds(SSL_CTX_set_options)]
+ pub fn set_options(&mut self, option: SslOptions) -> SslOptions {
+ let bits =
+ unsafe { ffi::SSL_CTX_set_options(self.as_ptr(), option.bits()) } as SslOptionsRepr;
+ SslOptions { bits }
+ }
+
+ /// Returns the options used by the context.
+ #[corresponds(SSL_CTX_get_options)]
+ pub fn options(&self) -> SslOptions {
+ let bits = unsafe { ffi::SSL_CTX_get_options(self.as_ptr()) } as SslOptionsRepr;
+ SslOptions { bits }
+ }
+
+ /// Clears the options used by the context, returning the old set.
+ #[corresponds(SSL_CTX_clear_options)]
+ pub fn clear_options(&mut self, option: SslOptions) -> SslOptions {
+ let bits =
+ unsafe { ffi::SSL_CTX_clear_options(self.as_ptr(), option.bits()) } as SslOptionsRepr;
+ SslOptions { bits }
+ }
+
+ /// Sets the minimum supported protocol version.
+ ///
+ /// A value of `None` will enable protocol versions down to the lowest version supported by
+ /// OpenSSL.
+ ///
+ /// Requires OpenSSL 1.1.0 or LibreSSL 2.6.1 or newer.
+ #[corresponds(SSL_CTX_set_min_proto_version)]
+ #[cfg(any(ossl110, libressl261))]
+ pub fn set_min_proto_version(&mut self, version: Option<SslVersion>) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::SSL_CTX_set_min_proto_version(
+ self.as_ptr(),
+ version.map_or(0, |v| v.0 as _),
+ ))
+ .map(|_| ())
+ }
+ }
+
+ /// Sets the maximum supported protocol version.
+ ///
+ /// A value of `None` will enable protocol versions up to the highest version supported by
+ /// OpenSSL.
+ ///
+ /// Requires OpenSSL 1.1.0 or or LibreSSL 2.6.1 or newer.
+ #[corresponds(SSL_CTX_set_max_proto_version)]
+ #[cfg(any(ossl110, libressl261))]
+ pub fn set_max_proto_version(&mut self, version: Option<SslVersion>) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::SSL_CTX_set_max_proto_version(
+ self.as_ptr(),
+ version.map_or(0, |v| v.0 as _),
+ ))
+ .map(|_| ())
+ }
+ }
+
+ /// Gets the minimum supported protocol version.
+ ///
+ /// A value of `None` indicates that all versions down to the lowest version supported by
+ /// OpenSSL are enabled.
+ ///
+ /// Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer.
+ #[corresponds(SSL_CTX_get_min_proto_version)]
+ #[cfg(any(ossl110g, libressl270))]
+ pub fn min_proto_version(&mut self) -> Option<SslVersion> {
+ 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 up to the highest version supported by
+ /// OpenSSL are enabled.
+ ///
+ /// Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer.
+ #[corresponds(SSL_CTX_get_max_proto_version)]
+ #[cfg(any(ossl110g, libressl270))]
+ pub fn max_proto_version(&mut self) -> Option<SslVersion> {
+ 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
+ /// names prefixed by their byte length. For example, the protocol list consisting of `spdy/1`
+ /// and `http/1.1` is encoded as `b"\x06spdy/1\x08http/1.1"`. The protocols are ordered by
+ /// preference.
+ ///
+ /// Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer.
+ #[corresponds(SSL_CTX_set_alpn_protos)]
+ #[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);
+ let r = ffi::SSL_CTX_set_alpn_protos(
+ self.as_ptr(),
+ protocols.as_ptr(),
+ protocols.len() as c_uint,
+ );
+ // fun fact, SSL_CTX_set_alpn_protos has a reversed return code D:
+ if r == 0 {
+ Ok(())
+ } else {
+ Err(ErrorStack::get())
+ }
+ }
+ }
+
+ /// Enables the DTLS extension "use_srtp" as defined in RFC5764.
+ #[corresponds(SSL_CTX_set_tlsext_use_srtp)]
+ pub fn set_tlsext_use_srtp(&mut self, protocols: &str) -> Result<(), ErrorStack> {
+ unsafe {
+ let cstr = CString::new(protocols).unwrap();
+
+ let r = ffi::SSL_CTX_set_tlsext_use_srtp(self.as_ptr(), cstr.as_ptr());
+ // fun fact, set_tlsext_use_srtp has a reversed return code D:
+ if r == 0 {
+ Ok(())
+ } else {
+ Err(ErrorStack::get())
+ }
+ }
+ }
+
+ /// Sets the callback used by a server to select a protocol for Application Layer Protocol
+ /// Negotiation (ALPN).
+ ///
+ /// The callback is provided with the client's protocol list in ALPN wire format. See the
+ /// documentation for [`SslContextBuilder::set_alpn_protos`] for details. It should return one
+ /// of those protocols on success. The [`select_next_proto`] function implements the standard
+ /// protocol selection algorithm.
+ ///
+ /// 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
+ #[corresponds(SSL_CTX_set_alpn_select_cb)]
+ #[cfg(any(ossl102, libressl261))]
+ pub fn set_alpn_select_callback<F>(&mut self, callback: F)
+ where
+ F: for<'a> Fn(&mut SslRef, &'a [u8]) -> Result<&'a [u8], AlpnError> + 'static + Sync + Send,
+ {
+ unsafe {
+ self.set_ex_data(SslContext::cached_ex_index::<F>(), callback);
+ ffi::SSL_CTX_set_alpn_select_cb__fixed_rust(
+ self.as_ptr(),
+ Some(callbacks::raw_alpn_select::<F>),
+ ptr::null_mut(),
+ );
+ }
+ }
+
+ /// Checks for consistency between the private key and certificate.
+ #[corresponds(SSL_CTX_check_private_key)]
+ pub fn check_private_key(&self) -> Result<(), ErrorStack> {
+ unsafe { cvt(ffi::SSL_CTX_check_private_key(self.as_ptr())).map(|_| ()) }
+ }
+
+ /// Returns a shared reference to the context's certificate store.
+ #[corresponds(SSL_CTX_get_cert_store)]
+ pub fn cert_store(&self) -> &X509StoreBuilderRef {
+ unsafe { X509StoreBuilderRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr())) }
+ }
+
+ /// Returns a mutable reference to the context's certificate store.
+ #[corresponds(SSL_CTX_get_cert_store)]
+ pub fn cert_store_mut(&mut self) -> &mut X509StoreBuilderRef {
+ unsafe { X509StoreBuilderRef::from_ptr_mut(ffi::SSL_CTX_get_cert_store(self.as_ptr())) }
+ }
+
+ /// Returns a reference to the X509 verification configuration.
+ ///
+ /// Requires OpenSSL 1.0.2 or newer.
+ #[corresponds(SSL_CTX_get0_param)]
+ #[cfg(any(ossl102, libressl261))]
+ pub fn verify_param(&self) -> &X509VerifyParamRef {
+ unsafe { X509VerifyParamRef::from_ptr(ffi::SSL_CTX_get0_param(self.as_ptr())) }
+ }
+
+ /// Returns a mutable reference to the X509 verification configuration.
+ ///
+ /// Requires OpenSSL 1.0.2 or newer.
+ #[corresponds(SSL_CTX_get0_param)]
+ #[cfg(any(ossl102, libressl261))]
+ pub fn verify_param_mut(&mut self) -> &mut X509VerifyParamRef {
+ unsafe { X509VerifyParamRef::from_ptr_mut(ffi::SSL_CTX_get0_param(self.as_ptr())) }
+ }
+
+ /// Sets the callback dealing with OCSP stapling.
+ ///
+ /// On the client side, this callback is responsible for validating the OCSP status response
+ /// returned by the server. The status may be retrieved with the `SslRef::ocsp_status` method.
+ /// A response of `Ok(true)` indicates that the OCSP status is valid, and a response of
+ /// `Ok(false)` indicates that the OCSP status is invalid and the handshake should be
+ /// terminated.
+ ///
+ /// On the server side, this callback is responsible for setting the OCSP status response to be
+ /// returned to clients. The status may be set with the `SslRef::set_ocsp_status` method. A
+ /// response of `Ok(true)` indicates that the OCSP status should be returned to the client, and
+ /// `Ok(false)` indicates that the status should not be returned to the client.
+ #[corresponds(SSL_CTX_set_tlsext_status_cb)]
+ pub fn set_status_callback<F>(&mut self, callback: F) -> Result<(), ErrorStack>
+ where
+ F: Fn(&mut SslRef) -> Result<bool, ErrorStack> + 'static + Sync + Send,
+ {
+ unsafe {
+ self.set_ex_data(SslContext::cached_ex_index::<F>(), callback);
+ cvt(
+ ffi::SSL_CTX_set_tlsext_status_cb(self.as_ptr(), Some(raw_tlsext_status::<F>))
+ as c_int,
+ )
+ .map(|_| ())
+ }
+ }
+
+ /// Sets the callback for providing an identity and pre-shared key for a TLS-PSK client.
+ ///
+ /// The callback will be called with the SSL context, an identity hint if one was provided
+ /// by the server, a mutable slice for each of the identity and pre-shared key bytes. The
+ /// identity must be written as a null-terminated C string.
+ #[corresponds(SSL_CTX_set_psk_client_callback)]
+ #[cfg(not(osslconf = "OPENSSL_NO_PSK"))]
+ pub fn set_psk_client_callback<F>(&mut self, callback: F)
+ where
+ F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8], &mut [u8]) -> Result<usize, ErrorStack>
+ + 'static
+ + Sync
+ + Send,
+ {
+ unsafe {
+ self.set_ex_data(SslContext::cached_ex_index::<F>(), callback);
+ ffi::SSL_CTX_set_psk_client_callback(self.as_ptr(), Some(raw_client_psk::<F>));
+ }
+ }
+
+ #[deprecated(since = "0.10.10", note = "renamed to `set_psk_client_callback`")]
+ #[cfg(not(osslconf = "OPENSSL_NO_PSK"))]
+ pub fn set_psk_callback<F>(&mut self, callback: F)
+ where
+ F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8], &mut [u8]) -> Result<usize, ErrorStack>
+ + 'static
+ + Sync
+ + Send,
+ {
+ self.set_psk_client_callback(callback)
+ }
+
+ /// Sets the callback for providing an identity and pre-shared key for a TLS-PSK server.
+ ///
+ /// The callback will be called with the SSL context, an identity provided by the client,
+ /// and, a mutable slice for the pre-shared key bytes. The callback returns the number of
+ /// bytes in the pre-shared key.
+ #[corresponds(SSL_CTX_set_psk_server_callback)]
+ #[cfg(not(osslconf = "OPENSSL_NO_PSK"))]
+ pub fn set_psk_server_callback<F>(&mut self, callback: F)
+ where
+ F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8]) -> Result<usize, ErrorStack>
+ + 'static
+ + Sync
+ + Send,
+ {
+ unsafe {
+ self.set_ex_data(SslContext::cached_ex_index::<F>(), callback);
+ ffi::SSL_CTX_set_psk_server_callback(self.as_ptr(), Some(raw_server_psk::<F>));
+ }
+ }
+
+ /// Sets the callback which is called when new sessions are negotiated.
+ ///
+ /// This can be used by clients to implement session caching. While in TLSv1.2 the session is
+ /// available to access via [`SslRef::session`] immediately after the handshake completes, this
+ /// is not the case for TLSv1.3. There, a session is not generally available immediately, and
+ /// the server may provide multiple session tokens to the client over a single session. The new
+ /// session callback is a portable way to deal with both cases.
+ ///
+ /// Note that session caching must be enabled for the callback to be invoked, and it defaults
+ /// off for clients. [`set_session_cache_mode`] controls that behavior.
+ ///
+ /// [`SslRef::session`]: struct.SslRef.html#method.session
+ /// [`set_session_cache_mode`]: #method.set_session_cache_mode
+ #[corresponds(SSL_CTX_sess_set_new_cb)]
+ pub fn set_new_session_callback<F>(&mut self, callback: F)
+ where
+ F: Fn(&mut SslRef, SslSession) + 'static + Sync + Send,
+ {
+ unsafe {
+ self.set_ex_data(SslContext::cached_ex_index::<F>(), callback);
+ ffi::SSL_CTX_sess_set_new_cb(self.as_ptr(), Some(callbacks::raw_new_session::<F>));
+ }
+ }
+
+ /// Sets the callback which is called when sessions are removed from the context.
+ ///
+ /// Sessions can be removed because they have timed out or because they are considered faulty.
+ #[corresponds(SSL_CTX_sess_set_remove_cb)]
+ pub fn set_remove_session_callback<F>(&mut self, callback: F)
+ where
+ F: Fn(&SslContextRef, &SslSessionRef) + 'static + Sync + Send,
+ {
+ unsafe {
+ self.set_ex_data(SslContext::cached_ex_index::<F>(), callback);
+ ffi::SSL_CTX_sess_set_remove_cb(
+ self.as_ptr(),
+ Some(callbacks::raw_remove_session::<F>),
+ );
+ }
+ }
+
+ /// Sets the callback which is called when a client proposed to resume a session but it was not
+ /// found in the internal cache.
+ ///
+ /// The callback is passed a reference to the session ID provided by the client. It should
+ /// return the session corresponding to that ID if available. This is only used for servers, not
+ /// clients.
+ ///
+ /// # Safety
+ ///
+ /// The returned `SslSession` must not be associated with a different `SslContext`.
+ #[corresponds(SSL_CTX_sess_set_get_cb)]
+ pub unsafe fn set_get_session_callback<F>(&mut self, callback: F)
+ where
+ F: Fn(&mut SslRef, &[u8]) -> Option<SslSession> + 'static + Sync + Send,
+ {
+ self.set_ex_data(SslContext::cached_ex_index::<F>(), callback);
+ ffi::SSL_CTX_sess_set_get_cb(self.as_ptr(), Some(callbacks::raw_get_session::<F>));
+ }
+
+ /// Sets the TLS key logging callback.
+ ///
+ /// The callback is invoked whenever TLS key material is generated, and is passed a line of NSS
+ /// SSLKEYLOGFILE-formatted text. This can be used by tools like Wireshark to decrypt message
+ /// traffic. The line does not contain a trailing newline.
+ ///
+ /// Requires OpenSSL 1.1.1 or newer.
+ #[corresponds(SSL_CTX_set_keylog_callback)]
+ #[cfg(ossl111)]
+ pub fn set_keylog_callback<F>(&mut self, callback: F)
+ where
+ F: Fn(&SslRef, &str) + 'static + Sync + Send,
+ {
+ unsafe {
+ self.set_ex_data(SslContext::cached_ex_index::<F>(), callback);
+ ffi::SSL_CTX_set_keylog_callback(self.as_ptr(), Some(callbacks::raw_keylog::<F>));
+ }
+ }
+
+ /// Sets the session caching mode use for connections made with the context.
+ ///
+ /// Returns the previous session caching mode.
+ #[corresponds(SSL_CTX_set_session_cache_mode)]
+ pub fn set_session_cache_mode(&mut self, mode: SslSessionCacheMode) -> SslSessionCacheMode {
+ unsafe {
+ let bits = ffi::SSL_CTX_set_session_cache_mode(self.as_ptr(), mode.bits());
+ SslSessionCacheMode { bits }
+ }
+ }
+
+ /// Sets the callback for generating an application cookie for TLS1.3
+ /// stateless handshakes.
+ ///
+ /// The callback will be called with the SSL context and a slice into which the cookie
+ /// should be written. The callback should return the number of bytes written.
+ #[corresponds(SSL_CTX_set_stateless_cookie_generate_cb)]
+ #[cfg(ossl111)]
+ pub fn set_stateless_cookie_generate_cb<F>(&mut self, callback: F)
+ where
+ F: Fn(&mut SslRef, &mut [u8]) -> Result<usize, ErrorStack> + 'static + Sync + Send,
+ {
+ unsafe {
+ self.set_ex_data(SslContext::cached_ex_index::<F>(), callback);
+ ffi::SSL_CTX_set_stateless_cookie_generate_cb(
+ self.as_ptr(),
+ Some(raw_stateless_cookie_generate::<F>),
+ );
+ }
+ }
+
+ /// Sets the callback for verifying an application cookie for TLS1.3
+ /// stateless handshakes.
+ ///
+ /// The callback will be called with the SSL context and the cookie supplied by the
+ /// client. It should return true if and only if the cookie is valid.
+ ///
+ /// Note that the OpenSSL implementation independently verifies the integrity of
+ /// application cookies using an HMAC before invoking the supplied callback.
+ #[corresponds(SSL_CTX_set_stateless_cookie_verify_cb)]
+ #[cfg(ossl111)]
+ pub fn set_stateless_cookie_verify_cb<F>(&mut self, callback: F)
+ where
+ F: Fn(&mut SslRef, &[u8]) -> bool + 'static + Sync + Send,
+ {
+ unsafe {
+ self.set_ex_data(SslContext::cached_ex_index::<F>(), callback);
+ ffi::SSL_CTX_set_stateless_cookie_verify_cb(
+ self.as_ptr(),
+ Some(raw_stateless_cookie_verify::<F>),
+ )
+ }
+ }
+
+ /// Sets the callback for generating a DTLSv1 cookie
+ ///
+ /// The callback will be called with the SSL context and a slice into which the cookie
+ /// should be written. The callback should return the number of bytes written.
+ #[corresponds(SSL_CTX_set_cookie_generate_cb)]
+ #[cfg(not(boringssl))]
+ pub fn set_cookie_generate_cb<F>(&mut self, callback: F)
+ where
+ F: Fn(&mut SslRef, &mut [u8]) -> Result<usize, ErrorStack> + 'static + Sync + Send,
+ {
+ unsafe {
+ self.set_ex_data(SslContext::cached_ex_index::<F>(), callback);
+ ffi::SSL_CTX_set_cookie_generate_cb(self.as_ptr(), Some(raw_cookie_generate::<F>));
+ }
+ }
+
+ /// Sets the callback for verifying a DTLSv1 cookie
+ ///
+ /// The callback will be called with the SSL context and the cookie supplied by the
+ /// client. It should return true if and only if the cookie is valid.
+ #[corresponds(SSL_CTX_set_cookie_verify_cb)]
+ #[cfg(not(boringssl))]
+ pub fn set_cookie_verify_cb<F>(&mut self, callback: F)
+ where
+ F: Fn(&mut SslRef, &[u8]) -> bool + 'static + Sync + Send,
+ {
+ unsafe {
+ self.set_ex_data(SslContext::cached_ex_index::<F>(), callback);
+ ffi::SSL_CTX_set_cookie_verify_cb(self.as_ptr(), Some(raw_cookie_verify::<F>));
+ }
+ }
+
+ /// Sets the extra data at the specified index.
+ ///
+ /// This can be used to provide data to callbacks registered with the context. Use the
+ /// `SslContext::new_ex_index` method to create an `Index`.
+ #[corresponds(SSL_CTX_set_ex_data)]
+ pub fn set_ex_data<T>(&mut self, index: Index<SslContext, T>, data: T) {
+ self.set_ex_data_inner(index, data);
+ }
+
+ fn set_ex_data_inner<T>(&mut self, index: Index<SslContext, T>, data: T) -> *mut c_void {
+ unsafe {
+ let data = Box::into_raw(Box::new(data)) as *mut c_void;
+ ffi::SSL_CTX_set_ex_data(self.as_ptr(), index.as_raw(), data);
+ data
+ }
+ }
+
+ /// Adds a custom extension for a TLS/DTLS client or server for all supported protocol versions.
+ ///
+ /// Requires OpenSSL 1.1.1 or newer.
+ #[corresponds(SSL_CTX_add_custom_ext)]
+ #[cfg(ossl111)]
+ pub fn add_custom_ext<AddFn, ParseFn, T>(
+ &mut self,
+ ext_type: u16,
+ context: ExtensionContext,
+ add_cb: AddFn,
+ parse_cb: ParseFn,
+ ) -> Result<(), ErrorStack>
+ where
+ AddFn: Fn(
+ &mut SslRef,
+ ExtensionContext,
+ Option<(usize, &X509Ref)>,
+ ) -> Result<Option<T>, SslAlert>
+ + 'static
+ + Sync
+ + Send,
+ T: AsRef<[u8]> + 'static + Sync + Send,
+ ParseFn: Fn(
+ &mut SslRef,
+ ExtensionContext,
+ &[u8],
+ Option<(usize, &X509Ref)>,
+ ) -> Result<(), SslAlert>
+ + 'static
+ + Sync
+ + Send,
+ {
+ let ret = unsafe {
+ self.set_ex_data(SslContext::cached_ex_index::<AddFn>(), add_cb);
+ self.set_ex_data(SslContext::cached_ex_index::<ParseFn>(), parse_cb);
+
+ ffi::SSL_CTX_add_custom_ext(
+ self.as_ptr(),
+ ext_type as c_uint,
+ context.bits(),
+ Some(raw_custom_ext_add::<AddFn, T>),
+ Some(raw_custom_ext_free::<T>),
+ ptr::null_mut(),
+ Some(raw_custom_ext_parse::<ParseFn>),
+ ptr::null_mut(),
+ )
+ };
+ if ret == 1 {
+ Ok(())
+ } else {
+ Err(ErrorStack::get())
+ }
+ }
+
+ /// Sets the maximum amount of early data that will be accepted on incoming connections.
+ ///
+ /// Defaults to 0.
+ ///
+ /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
+ #[corresponds(SSL_CTX_set_max_early_data)]
+ #[cfg(any(ossl111, libressl340))]
+ 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())
+ }
+ }
+
+ /// Sets a callback which will be invoked just after the client's hello message is received.
+ ///
+ /// Requires OpenSSL 1.1.1 or newer.
+ #[corresponds(SSL_CTX_set_client_hello_cb)]
+ #[cfg(ossl111)]
+ pub fn set_client_hello_callback<F>(&mut self, callback: F)
+ where
+ F: Fn(&mut SslRef, &mut SslAlert) -> Result<ClientHelloResponse, ErrorStack>
+ + 'static
+ + Sync
+ + Send,
+ {
+ unsafe {
+ let ptr = self.set_ex_data_inner(SslContext::cached_ex_index::<F>(), callback);
+ ffi::SSL_CTX_set_client_hello_cb(
+ self.as_ptr(),
+ Some(callbacks::raw_client_hello::<F>),
+ ptr,
+ );
+ }
+ }
+
+ /// Sets the context's session cache size limit, returning the previous limit.
+ ///
+ /// A value of 0 means that the cache size is unbounded.
+ #[corresponds(SSL_CTX_sess_set_cache_size)]
+ #[allow(clippy::useless_conversion)]
+ pub fn set_session_cache_size(&mut self, size: i32) -> i64 {
+ unsafe {
+ ffi::SSL_CTX_sess_set_cache_size(self.as_ptr(), size as SslCacheSize) as SslCacheTy
+ }
+ }
+
+ /// Sets the context's supported signature algorithms.
+ ///
+ /// Requires OpenSSL 1.0.2 or newer.
+ #[corresponds(SSL_CTX_set1_sigalgs_list)]
+ #[cfg(ossl102)]
+ pub fn set_sigalgs_list(&mut self, sigalgs: &str) -> Result<(), ErrorStack> {
+ let sigalgs = CString::new(sigalgs).unwrap();
+ unsafe {
+ cvt(ffi::SSL_CTX_set1_sigalgs_list(self.as_ptr(), sigalgs.as_ptr()) as c_int)
+ .map(|_| ())
+ }
+ }
+
+ /// Sets the context's supported elliptic curve groups.
+ ///
+ /// Requires OpenSSL 1.1.1 or LibreSSL 2.5.1 or newer.
+ #[corresponds(SSL_CTX_set1_groups_list)]
+ #[cfg(any(ossl111, libressl251))]
+ pub fn set_groups_list(&mut self, groups: &str) -> Result<(), ErrorStack> {
+ let groups = CString::new(groups).unwrap();
+ unsafe {
+ cvt(ffi::SSL_CTX_set1_groups_list(self.as_ptr(), groups.as_ptr()) as c_int).map(|_| ())
+ }
+ }
+
+ /// Sets the number of TLS 1.3 session tickets that will be sent to a client after a full
+ /// handshake.
+ ///
+ /// Requires OpenSSL 1.1.1 or newer.
+ #[corresponds(SSL_CTX_set_num_tickets)]
+ #[cfg(ossl111)]
+ pub fn set_num_tickets(&mut self, num_tickets: usize) -> Result<(), ErrorStack> {
+ unsafe { cvt(ffi::SSL_CTX_set_num_tickets(self.as_ptr(), num_tickets)).map(|_| ()) }
+ }
+
+ /// Consumes the builder, returning a new `SslContext`.
+ pub fn build(self) -> SslContext {
+ self.0
+ }
+}
+
+foreign_type_and_impl_send_sync! {
+ type CType = ffi::SSL_CTX;
+ fn drop = ffi::SSL_CTX_free;
+
+ /// A context object for TLS streams.
+ ///
+ /// Applications commonly configure a single `SslContext` that is shared by all of its
+ /// `SslStreams`.
+ pub struct SslContext;
+
+ /// Reference to [`SslContext`]
+ ///
+ /// [`SslContext`]: struct.SslContext.html
+ pub struct SslContextRef;
+}
+
+impl Clone for SslContext {
+ fn clone(&self) -> Self {
+ (**self).to_owned()
+ }
+}
+
+impl ToOwned for SslContextRef {
+ type Owned = SslContext;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe {
+ SSL_CTX_up_ref(self.as_ptr());
+ SslContext::from_ptr(self.as_ptr())
+ }
+ }
+}
+
+// TODO: add useful info here
+impl fmt::Debug for SslContext {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(fmt, "SslContext")
+ }
+}
+
+impl SslContext {
+ /// Creates a new builder object for an `SslContext`.
+ pub fn builder(method: SslMethod) -> Result<SslContextBuilder, ErrorStack> {
+ SslContextBuilder::new(method)
+ }
+
+ /// Returns a new extra data index.
+ ///
+ /// Each invocation of this function is guaranteed to return a distinct index. These can be used
+ /// to store data in the context that can be retrieved later by callbacks, for example.
+ #[corresponds(SSL_CTX_get_ex_new_index)]
+ pub fn new_ex_index<T>() -> Result<Index<SslContext, T>, ErrorStack>
+ where
+ T: 'static + Sync + Send,
+ {
+ unsafe {
+ ffi::init();
+ #[cfg(boringssl)]
+ let idx = cvt_n(get_new_idx(Some(free_data_box::<T>)))?;
+ #[cfg(not(boringssl))]
+ let idx = cvt_n(get_new_idx(free_data_box::<T>))?;
+ Ok(Index::from_raw(idx))
+ }
+ }
+
+ // FIXME should return a result?
+ fn cached_ex_index<T>() -> Index<SslContext, T>
+ where
+ T: 'static + Sync + Send,
+ {
+ unsafe {
+ let idx = *INDEXES
+ .lock()
+ .unwrap_or_else(|e| e.into_inner())
+ .entry(TypeId::of::<T>())
+ .or_insert_with(|| SslContext::new_ex_index::<T>().unwrap().as_raw());
+ Index::from_raw(idx)
+ }
+ }
+}
+
+impl SslContextRef {
+ /// Returns the certificate associated with this `SslContext`, if present.
+ ///
+ /// Requires OpenSSL 1.0.2 or LibreSSL 2.7.0 or newer.
+ #[corresponds(SSL_CTX_get0_certificate)]
+ #[cfg(any(ossl102, libressl270))]
+ pub fn certificate(&self) -> Option<&X509Ref> {
+ unsafe {
+ let ptr = ffi::SSL_CTX_get0_certificate(self.as_ptr());
+ X509Ref::from_const_ptr_opt(ptr)
+ }
+ }
+
+ /// Returns the private key associated with this `SslContext`, if present.
+ ///
+ /// Requires OpenSSL 1.0.2 or LibreSSL 3.4.0 or newer.
+ #[corresponds(SSL_CTX_get0_privatekey)]
+ #[cfg(any(ossl102, libressl340))]
+ pub fn private_key(&self) -> Option<&PKeyRef<Private>> {
+ unsafe {
+ let ptr = ffi::SSL_CTX_get0_privatekey(self.as_ptr());
+ PKeyRef::from_const_ptr_opt(ptr)
+ }
+ }
+
+ /// Returns a shared reference to the certificate store used for verification.
+ #[corresponds(SSL_CTX_get_cert_store)]
+ pub fn cert_store(&self) -> &X509StoreRef {
+ unsafe { X509StoreRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr())) }
+ }
+
+ /// Returns a shared reference to the stack of certificates making up the chain from the leaf.
+ #[corresponds(SSL_CTX_get_extra_chain_certs)]
+ pub fn extra_chain_certs(&self) -> &StackRef<X509> {
+ unsafe {
+ let mut chain = ptr::null_mut();
+ ffi::SSL_CTX_get_extra_chain_certs(self.as_ptr(), &mut chain);
+ StackRef::from_const_ptr_opt(chain).expect("extra chain certs must not be null")
+ }
+ }
+
+ /// Returns a reference to the extra data at the specified index.
+ #[corresponds(SSL_CTX_get_ex_data)]
+ pub fn ex_data<T>(&self, index: Index<SslContext, T>) -> Option<&T> {
+ unsafe {
+ let data = ffi::SSL_CTX_get_ex_data(self.as_ptr(), index.as_raw());
+ if data.is_null() {
+ None
+ } else {
+ Some(&*(data as *const T))
+ }
+ }
+ }
+
+ /// Gets the maximum amount of early data that will be accepted on incoming connections.
+ ///
+ /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
+ #[corresponds(SSL_CTX_get_max_early_data)]
+ #[cfg(any(ossl111, libressl340))]
+ pub fn max_early_data(&self) -> u32 {
+ unsafe { ffi::SSL_CTX_get_max_early_data(self.as_ptr()) }
+ }
+
+ /// Adds a session to the context's cache.
+ ///
+ /// Returns `true` if the session was successfully added to the cache, and `false` if it was already present.
+ ///
+ /// # Safety
+ ///
+ /// The caller of this method is responsible for ensuring that the session has never been used with another
+ /// `SslContext` than this one.
+ #[corresponds(SSL_CTX_add_session)]
+ pub unsafe fn add_session(&self, session: &SslSessionRef) -> bool {
+ ffi::SSL_CTX_add_session(self.as_ptr(), session.as_ptr()) != 0
+ }
+
+ /// Removes a session from the context's cache and marks it as non-resumable.
+ ///
+ /// Returns `true` if the session was successfully found and removed, and `false` otherwise.
+ ///
+ /// # Safety
+ ///
+ /// The caller of this method is responsible for ensuring that the session has never been used with another
+ /// `SslContext` than this one.
+ #[corresponds(SSL_CTX_remove_session)]
+ pub unsafe fn remove_session(&self, session: &SslSessionRef) -> bool {
+ ffi::SSL_CTX_remove_session(self.as_ptr(), session.as_ptr()) != 0
+ }
+
+ /// Returns the context's session cache size limit.
+ ///
+ /// A value of 0 means that the cache size is unbounded.
+ #[corresponds(SSL_CTX_sess_get_cache_size)]
+ #[allow(clippy::unnecessary_cast)]
+ pub fn session_cache_size(&self) -> i64 {
+ unsafe { ffi::SSL_CTX_sess_get_cache_size(self.as_ptr()) as i64 }
+ }
+
+ /// Returns the verify mode that was set on this context from [`SslContextBuilder::set_verify`].
+ ///
+ /// [`SslContextBuilder::set_verify`]: struct.SslContextBuilder.html#method.set_verify
+ #[corresponds(SSL_CTX_get_verify_mode)]
+ pub fn verify_mode(&self) -> SslVerifyMode {
+ let mode = unsafe { ffi::SSL_CTX_get_verify_mode(self.as_ptr()) };
+ SslVerifyMode::from_bits(mode).expect("SSL_CTX_get_verify_mode returned invalid mode")
+ }
+
+ /// Gets the number of TLS 1.3 session tickets that will be sent to a client after a full
+ /// handshake.
+ ///
+ /// Requires OpenSSL 1.1.1 or newer.
+ #[corresponds(SSL_CTX_get_num_tickets)]
+ #[cfg(ossl111)]
+ pub fn num_tickets(&self) -> usize {
+ unsafe { ffi::SSL_CTX_get_num_tickets(self.as_ptr()) }
+ }
+}
+
+/// Information about the state of a cipher.
+pub struct CipherBits {
+ /// The number of secret bits used for the cipher.
+ pub secret: i32,
+
+ /// The number of bits processed by the chosen algorithm.
+ pub algorithm: i32,
+}
+
+/// Information about a cipher.
+pub struct SslCipher(*mut ffi::SSL_CIPHER);
+
+impl ForeignType for SslCipher {
+ type CType = ffi::SSL_CIPHER;
+ type Ref = SslCipherRef;
+
+ #[inline]
+ unsafe fn from_ptr(ptr: *mut ffi::SSL_CIPHER) -> SslCipher {
+ SslCipher(ptr)
+ }
+
+ #[inline]
+ fn as_ptr(&self) -> *mut ffi::SSL_CIPHER {
+ self.0
+ }
+}
+
+impl Deref for SslCipher {
+ type Target = SslCipherRef;
+
+ fn deref(&self) -> &SslCipherRef {
+ unsafe { SslCipherRef::from_ptr(self.0) }
+ }
+}
+
+impl DerefMut for SslCipher {
+ fn deref_mut(&mut self) -> &mut SslCipherRef {
+ unsafe { SslCipherRef::from_ptr_mut(self.0) }
+ }
+}
+
+/// Reference to an [`SslCipher`].
+///
+/// [`SslCipher`]: struct.SslCipher.html
+pub struct SslCipherRef(Opaque);
+
+impl ForeignTypeRef for SslCipherRef {
+ type CType = ffi::SSL_CIPHER;
+}
+
+impl SslCipherRef {
+ /// Returns the name of the cipher.
+ #[corresponds(SSL_CIPHER_get_name)]
+ pub fn name(&self) -> &'static str {
+ unsafe {
+ let ptr = ffi::SSL_CIPHER_get_name(self.as_ptr());
+ CStr::from_ptr(ptr).to_str().unwrap()
+ }
+ }
+
+ /// Returns the RFC-standard name of the cipher, if one exists.
+ ///
+ /// Requires OpenSSL 1.1.1 or newer.
+ #[corresponds(SSL_CIPHER_standard_name)]
+ #[cfg(ossl111)]
+ pub fn standard_name(&self) -> Option<&'static str> {
+ unsafe {
+ let ptr = ffi::SSL_CIPHER_standard_name(self.as_ptr());
+ if ptr.is_null() {
+ None
+ } else {
+ Some(CStr::from_ptr(ptr).to_str().unwrap())
+ }
+ }
+ }
+
+ /// Returns the SSL/TLS protocol version that first defined the cipher.
+ #[corresponds(SSL_CIPHER_get_version)]
+ pub fn version(&self) -> &'static str {
+ let version = unsafe {
+ let ptr = ffi::SSL_CIPHER_get_version(self.as_ptr());
+ CStr::from_ptr(ptr as *const _)
+ };
+
+ str::from_utf8(version.to_bytes()).unwrap()
+ }
+
+ /// Returns the number of bits used for the cipher.
+ #[corresponds(SSL_CIPHER_get_bits)]
+ #[allow(clippy::useless_conversion)]
+ pub fn bits(&self) -> CipherBits {
+ unsafe {
+ let mut algo_bits = 0;
+ let secret_bits = ffi::SSL_CIPHER_get_bits(self.as_ptr(), &mut algo_bits);
+ CipherBits {
+ secret: secret_bits.into(),
+ algorithm: algo_bits.into(),
+ }
+ }
+ }
+
+ /// Returns a textual description of the cipher.
+ #[corresponds(SSL_CIPHER_description)]
+ pub fn description(&self) -> String {
+ unsafe {
+ // SSL_CIPHER_description requires a buffer of at least 128 bytes.
+ let mut buf = [0; 128];
+ let ptr = ffi::SSL_CIPHER_description(self.as_ptr(), buf.as_mut_ptr(), 128);
+ String::from_utf8(CStr::from_ptr(ptr as *const _).to_bytes().to_vec()).unwrap()
+ }
+ }
+
+ /// Returns the handshake digest of the cipher.
+ ///
+ /// Requires OpenSSL 1.1.1 or newer.
+ #[corresponds(SSL_CIPHER_get_handshake_digest)]
+ #[cfg(ossl111)]
+ pub fn handshake_digest(&self) -> Option<MessageDigest> {
+ unsafe {
+ let ptr = ffi::SSL_CIPHER_get_handshake_digest(self.as_ptr());
+ if ptr.is_null() {
+ None
+ } else {
+ Some(MessageDigest::from_ptr(ptr))
+ }
+ }
+ }
+
+ /// Returns the NID corresponding to the cipher.
+ ///
+ /// Requires OpenSSL 1.1.0 or LibreSSL 2.7.0 or newer.
+ #[corresponds(SSL_CIPHER_get_cipher_nid)]
+ #[cfg(any(ossl110, libressl270))]
+ pub fn cipher_nid(&self) -> Option<Nid> {
+ let n = unsafe { ffi::SSL_CIPHER_get_cipher_nid(self.as_ptr()) };
+ if n == 0 {
+ None
+ } else {
+ Some(Nid::from_raw(n))
+ }
+ }
+}
+
+foreign_type_and_impl_send_sync! {
+ type CType = ffi::SSL_SESSION;
+ fn drop = ffi::SSL_SESSION_free;
+
+ /// An encoded SSL session.
+ ///
+ /// These can be cached to share sessions across connections.
+ pub struct SslSession;
+
+ /// Reference to [`SslSession`].
+ ///
+ /// [`SslSession`]: struct.SslSession.html
+ pub struct SslSessionRef;
+}
+
+impl Clone for SslSession {
+ fn clone(&self) -> SslSession {
+ SslSessionRef::to_owned(self)
+ }
+}
+
+impl SslSession {
+ from_der! {
+ /// Deserializes a DER-encoded session structure.
+ #[corresponds(d2i_SSL_SESSION)]
+ from_der,
+ SslSession,
+ ffi::d2i_SSL_SESSION
+ }
+}
+
+impl ToOwned for SslSessionRef {
+ type Owned = SslSession;
+
+ fn to_owned(&self) -> SslSession {
+ unsafe {
+ SSL_SESSION_up_ref(self.as_ptr());
+ SslSession(self.as_ptr())
+ }
+ }
+}
+
+impl SslSessionRef {
+ /// Returns the SSL session ID.
+ #[corresponds(SSL_SESSION_get_id)]
+ pub fn id(&self) -> &[u8] {
+ unsafe {
+ let mut len = 0;
+ let p = ffi::SSL_SESSION_get_id(self.as_ptr(), &mut len);
+ slice::from_raw_parts(p as *const u8, len as usize)
+ }
+ }
+
+ /// Returns the length of the master key.
+ #[corresponds(SSL_SESSION_get_master_key)]
+ pub fn master_key_len(&self) -> usize {
+ unsafe { SSL_SESSION_get_master_key(self.as_ptr(), ptr::null_mut(), 0) }
+ }
+
+ /// Copies the master key into the provided buffer.
+ ///
+ /// Returns the number of bytes written, or the size of the master key if the buffer is empty.
+ #[corresponds(SSL_SESSION_get_master_key)]
+ pub fn master_key(&self, buf: &mut [u8]) -> usize {
+ unsafe { 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 LibreSSL 3.4.0 or newer.
+ #[corresponds(SSL_SESSION_get_max_early_data)]
+ #[cfg(any(ossl111, libressl340))]
+ pub fn max_early_data(&self) -> u32 {
+ unsafe { ffi::SSL_SESSION_get_max_early_data(self.as_ptr()) }
+ }
+
+ /// Returns the time at which the session was established, in seconds since the Unix epoch.
+ #[corresponds(SSL_SESSION_get_time)]
+ #[allow(clippy::useless_conversion)]
+ pub fn time(&self) -> SslTimeTy {
+ unsafe { ffi::SSL_SESSION_get_time(self.as_ptr()) }
+ }
+
+ /// Returns the sessions timeout, in seconds.
+ ///
+ /// A session older than this time should not be used for session resumption.
+ #[corresponds(SSL_SESSION_get_timeout)]
+ #[allow(clippy::useless_conversion)]
+ pub fn timeout(&self) -> i64 {
+ unsafe { ffi::SSL_SESSION_get_timeout(self.as_ptr()).into() }
+ }
+
+ /// Returns the session's TLS protocol version.
+ ///
+ /// Requires OpenSSL 1.1.0 or LibreSSL 2.7.0 or newer.
+ #[corresponds(SSL_SESSION_get_protocol_version)]
+ #[cfg(any(ossl110, libressl270))]
+ pub fn protocol_version(&self) -> SslVersion {
+ unsafe {
+ let version = ffi::SSL_SESSION_get_protocol_version(self.as_ptr());
+ SslVersion(version)
+ }
+ }
+
+ to_der! {
+ /// Serializes the session into a DER-encoded structure.
+ #[corresponds(i2d_SSL_SESSION)]
+ to_der,
+ ffi::i2d_SSL_SESSION
+ }
+}
+
+foreign_type_and_impl_send_sync! {
+ type CType = ffi::SSL;
+ fn drop = ffi::SSL_free;
+
+ /// The state of an SSL/TLS session.
+ ///
+ /// `Ssl` objects are created from an [`SslContext`], which provides configuration defaults.
+ /// These defaults can be overridden on a per-`Ssl` basis, however.
+ ///
+ /// [`SslContext`]: struct.SslContext.html
+ pub struct Ssl;
+
+ /// Reference to an [`Ssl`].
+ ///
+ /// [`Ssl`]: struct.Ssl.html
+ pub struct SslRef;
+}
+
+impl fmt::Debug for Ssl {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Debug::fmt(&**self, fmt)
+ }
+}
+
+impl Ssl {
+ /// Returns a new extra data index.
+ ///
+ /// Each invocation of this function is guaranteed to return a distinct index. These can be used
+ /// to store data in the context that can be retrieved later by callbacks, for example.
+ #[corresponds(SSL_get_ex_new_index)]
+ pub fn new_ex_index<T>() -> Result<Index<Ssl, T>, ErrorStack>
+ where
+ T: 'static + Sync + Send,
+ {
+ unsafe {
+ ffi::init();
+ #[cfg(boringssl)]
+ let idx = cvt_n(get_new_ssl_idx(Some(free_data_box::<T>)))?;
+ #[cfg(not(boringssl))]
+ let idx = cvt_n(get_new_ssl_idx(free_data_box::<T>))?;
+ Ok(Index::from_raw(idx))
+ }
+ }
+
+ // FIXME should return a result?
+ fn cached_ex_index<T>() -> Index<Ssl, T>
+ where
+ T: 'static + Sync + Send,
+ {
+ unsafe {
+ let idx = *SSL_INDEXES
+ .lock()
+ .unwrap_or_else(|e| e.into_inner())
+ .entry(TypeId::of::<T>())
+ .or_insert_with(|| Ssl::new_ex_index::<T>().unwrap().as_raw());
+ Index::from_raw(idx)
+ }
+ }
+
+ /// Creates a new `Ssl`.
+ ///
+ /// This corresponds to [`SSL_new`].
+ ///
+ /// [`SSL_new`]: https://www.openssl.org/docs/manmaster/ssl/SSL_new.html
+ #[corresponds(SSL_new)]
+ pub fn new(ctx: &SslContextRef) -> Result<Ssl, ErrorStack> {
+ let session_ctx_index = try_get_session_ctx_index()?;
+ unsafe {
+ let ptr = cvt_p(ffi::SSL_new(ctx.as_ptr()))?;
+ let mut ssl = Ssl::from_ptr(ptr);
+ ssl.set_ex_data(*session_ctx_index, ctx.to_owned());
+
+ Ok(ssl)
+ }
+ }
+
+ /// Initiates a client-side TLS handshake.
+ ///
+ /// This corresponds to [`SSL_connect`].
+ ///
+ /// # Warning
+ ///
+ /// OpenSSL's default configuration is insecure. It is highly recommended to use
+ /// `SslConnector` rather than `Ssl` directly, as it manages that configuration.
+ ///
+ /// [`SSL_connect`]: https://www.openssl.org/docs/manmaster/man3/SSL_connect.html
+ #[corresponds(SSL_connect)]
+ #[allow(deprecated)]
+ pub fn connect<S>(self, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
+ where
+ S: Read + Write,
+ {
+ SslStreamBuilder::new(self, stream).connect()
+ }
+
+ /// Initiates a server-side TLS handshake.
+ ///
+ /// This corresponds to [`SSL_accept`].
+ ///
+ /// # Warning
+ ///
+ /// OpenSSL's default configuration is insecure. It is highly recommended to use
+ /// `SslAcceptor` rather than `Ssl` directly, as it manages that configuration.
+ ///
+ /// [`SSL_accept`]: https://www.openssl.org/docs/manmaster/man3/SSL_accept.html
+ #[corresponds(SSL_accept)]
+ #[allow(deprecated)]
+ pub fn accept<S>(self, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
+ where
+ S: Read + Write,
+ {
+ SslStreamBuilder::new(self, stream).accept()
+ }
+}
+
+impl fmt::Debug for SslRef {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt.debug_struct("Ssl")
+ .field("state", &self.state_string_long())
+ .field("verify_result", &self.verify_result())
+ .finish()
+ }
+}
+
+impl SslRef {
+ fn get_raw_rbio(&self) -> *mut ffi::BIO {
+ unsafe { ffi::SSL_get_rbio(self.as_ptr()) }
+ }
+
+ fn read(&mut self, buf: &mut [u8]) -> c_int {
+ let len = cmp::min(c_int::max_value() as usize, buf.len()) as c_int;
+ unsafe { ffi::SSL_read(self.as_ptr(), buf.as_ptr() as *mut c_void, len) }
+ }
+
+ fn peek(&mut self, buf: &mut [u8]) -> c_int {
+ let len = cmp::min(c_int::max_value() as usize, buf.len()) as c_int;
+ unsafe { ffi::SSL_peek(self.as_ptr(), buf.as_ptr() as *mut c_void, len) }
+ }
+
+ fn write(&mut self, buf: &[u8]) -> c_int {
+ let len = cmp::min(c_int::max_value() as usize, buf.len()) as c_int;
+ unsafe { ffi::SSL_write(self.as_ptr(), buf.as_ptr() as *const c_void, len) }
+ }
+
+ fn get_error(&self, ret: c_int) -> ErrorCode {
+ unsafe { ErrorCode::from_raw(ffi::SSL_get_error(self.as_ptr(), ret)) }
+ }
+
+ /// Configure as an outgoing stream from a client.
+ #[corresponds(SSL_set_connect_state)]
+ pub fn set_connect_state(&mut self) {
+ unsafe { ffi::SSL_set_connect_state(self.as_ptr()) }
+ }
+
+ /// Configure as an incoming stream to a server.
+ #[corresponds(SSL_set_accept_state)]
+ pub fn set_accept_state(&mut self) {
+ unsafe { ffi::SSL_set_accept_state(self.as_ptr()) }
+ }
+
+ /// Like [`SslContextBuilder::set_verify`].
+ ///
+ /// [`SslContextBuilder::set_verify`]: struct.SslContextBuilder.html#method.set_verify
+ #[corresponds(SSL_set_verify)]
+ pub fn set_verify(&mut self, mode: SslVerifyMode) {
+ unsafe { ffi::SSL_set_verify(self.as_ptr(), mode.bits as c_int, None) }
+ }
+
+ /// Returns the verify mode that was set using `set_verify`.
+ #[corresponds(SSL_set_verify_mode)]
+ pub fn verify_mode(&self) -> SslVerifyMode {
+ let mode = unsafe { ffi::SSL_get_verify_mode(self.as_ptr()) };
+ SslVerifyMode::from_bits(mode).expect("SSL_get_verify_mode returned invalid mode")
+ }
+
+ /// Like [`SslContextBuilder::set_verify_callback`].
+ ///
+ /// [`SslContextBuilder::set_verify_callback`]: struct.SslContextBuilder.html#method.set_verify_callback
+ #[corresponds(SSL_set_verify)]
+ pub fn set_verify_callback<F>(&mut self, mode: SslVerifyMode, verify: F)
+ where
+ F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send,
+ {
+ unsafe {
+ // this needs to be in an Arc since the callback can register a new callback!
+ self.set_ex_data(Ssl::cached_ex_index(), Arc::new(verify));
+ ffi::SSL_set_verify(self.as_ptr(), mode.bits as c_int, Some(ssl_raw_verify::<F>));
+ }
+ }
+
+ /// Like [`SslContextBuilder::set_tmp_dh`].
+ ///
+ /// [`SslContextBuilder::set_tmp_dh`]: struct.SslContextBuilder.html#method.set_tmp_dh
+ #[corresponds(SSL_set_tmp_dh)]
+ pub fn set_tmp_dh(&mut self, dh: &DhRef<Params>) -> Result<(), ErrorStack> {
+ unsafe { cvt(ffi::SSL_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) }
+ }
+
+ /// Like [`SslContextBuilder::set_tmp_dh_callback`].
+ ///
+ /// [`SslContextBuilder::set_tmp_dh_callback`]: struct.SslContextBuilder.html#method.set_tmp_dh_callback
+ #[corresponds(SSL_set_tmp_dh_callback)]
+ pub fn set_tmp_dh_callback<F>(&mut self, callback: F)
+ where
+ F: Fn(&mut SslRef, bool, u32) -> Result<Dh<Params>, ErrorStack> + 'static + Sync + Send,
+ {
+ unsafe {
+ // this needs to be in an Arc since the callback can register a new callback!
+ self.set_ex_data(Ssl::cached_ex_index(), Arc::new(callback));
+ #[cfg(boringssl)]
+ ffi::SSL_set_tmp_dh_callback(self.as_ptr(), Some(raw_tmp_dh_ssl::<F>));
+ #[cfg(not(boringssl))]
+ ffi::SSL_set_tmp_dh_callback__fixed_rust(self.as_ptr(), Some(raw_tmp_dh_ssl::<F>));
+ }
+ }
+
+ /// Like [`SslContextBuilder::set_tmp_ecdh`].
+ ///
+ /// [`SslContextBuilder::set_tmp_ecdh`]: struct.SslContextBuilder.html#method.set_tmp_ecdh
+ #[corresponds(SSL_set_tmp_ecdh)]
+ pub fn set_tmp_ecdh(&mut self, key: &EcKeyRef<Params>) -> Result<(), ErrorStack> {
+ unsafe { cvt(ffi::SSL_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) }
+ }
+
+ /// Like [`SslContextBuilder::set_tmp_ecdh_callback`].
+ ///
+ /// Requires OpenSSL 1.0.1 or 1.0.2.
+ #[corresponds(SSL_set_tmp_ecdh_callback)]
+ #[cfg(any(all(ossl101, not(ossl110))))]
+ #[deprecated(note = "this function leaks memory and does not exist on newer OpenSSL versions")]
+ pub fn set_tmp_ecdh_callback<F>(&mut self, callback: F)
+ where
+ F: Fn(&mut SslRef, bool, u32) -> Result<EcKey<Params>, ErrorStack> + 'static + Sync + Send,
+ {
+ unsafe {
+ // this needs to be in an Arc since the callback can register a new callback!
+ self.set_ex_data(Ssl::cached_ex_index(), Arc::new(callback));
+ ffi::SSL_set_tmp_ecdh_callback__fixed_rust(self.as_ptr(), Some(raw_tmp_ecdh_ssl::<F>));
+ }
+ }
+
+ /// Like [`SslContextBuilder::set_ecdh_auto`].
+ ///
+ /// Requires OpenSSL 1.0.2 or LibreSSL.
+ ///
+ /// [`SslContextBuilder::set_tmp_ecdh`]: struct.SslContextBuilder.html#method.set_tmp_ecdh
+ #[corresponds(SSL_set_ecdh_auto)]
+ #[cfg(any(all(ossl102, not(ossl110)), libressl))]
+ 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(|_| ()) }
+ }
+
+ /// Like [`SslContextBuilder::set_alpn_protos`].
+ ///
+ /// Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer.
+ ///
+ /// [`SslContextBuilder::set_alpn_protos`]: struct.SslContextBuilder.html#method.set_alpn_protos
+ #[corresponds(SSL_set_alpn_protos)]
+ #[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);
+ let r = ffi::SSL_set_alpn_protos(
+ self.as_ptr(),
+ protocols.as_ptr(),
+ protocols.len() as c_uint,
+ );
+ // fun fact, SSL_set_alpn_protos has a reversed return code D:
+ if r == 0 {
+ Ok(())
+ } else {
+ Err(ErrorStack::get())
+ }
+ }
+ }
+
+ /// Returns the current cipher if the session is active.
+ #[corresponds(SSL_get_current_cipher)]
+ pub fn current_cipher(&self) -> Option<&SslCipherRef> {
+ unsafe {
+ let ptr = ffi::SSL_get_current_cipher(self.as_ptr());
+
+ SslCipherRef::from_const_ptr_opt(ptr)
+ }
+ }
+
+ /// Returns a short string describing the state of the session.
+ #[corresponds(SSL_state_string)]
+ pub fn state_string(&self) -> &'static str {
+ let state = unsafe {
+ let ptr = ffi::SSL_state_string(self.as_ptr());
+ CStr::from_ptr(ptr as *const _)
+ };
+
+ str::from_utf8(state.to_bytes()).unwrap()
+ }
+
+ /// Returns a longer string describing the state of the session.
+ #[corresponds(SSL_state_string_long)]
+ pub fn state_string_long(&self) -> &'static str {
+ let state = unsafe {
+ let ptr = ffi::SSL_state_string_long(self.as_ptr());
+ CStr::from_ptr(ptr as *const _)
+ };
+
+ str::from_utf8(state.to_bytes()).unwrap()
+ }
+
+ /// Sets the host name to be sent to the server for Server Name Indication (SNI).
+ ///
+ /// It has no effect for a server-side connection.
+ #[corresponds(SSL_set_tlsext_host_name)]
+ pub fn set_hostname(&mut self, hostname: &str) -> Result<(), ErrorStack> {
+ let cstr = CString::new(hostname).unwrap();
+ unsafe {
+ cvt(ffi::SSL_set_tlsext_host_name(self.as_ptr(), cstr.as_ptr() as *mut _) as c_int)
+ .map(|_| ())
+ }
+ }
+
+ /// Returns the peer's certificate, if present.
+ #[corresponds(SSL_get_peer_certificate)]
+ pub fn peer_certificate(&self) -> Option<X509> {
+ unsafe {
+ let ptr = SSL_get1_peer_certificate(self.as_ptr());
+ X509::from_ptr_opt(ptr)
+ }
+ }
+
+ /// Returns the certificate chain of the peer, if present.
+ ///
+ /// On the client side, the chain includes the leaf certificate, but on the server side it does
+ /// not. Fun!
+ #[corresponds(SSL_get_peer_cert_chain)]
+ pub fn peer_cert_chain(&self) -> Option<&StackRef<X509>> {
+ unsafe {
+ let ptr = ffi::SSL_get_peer_cert_chain(self.as_ptr());
+ StackRef::from_const_ptr_opt(ptr)
+ }
+ }
+
+ /// Returns the verified certificate chain 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.
+ ///
+ /// [`verify_result`]: #method.verify_result
+ /// [`X509VerifyResult::OK`]: ../x509/struct.X509VerifyResult.html#associatedconstant.OK
+ #[corresponds(SSL_get0_verified_chain)]
+ #[cfg(ossl110)]
+ pub fn verified_chain(&self) -> Option<&StackRef<X509>> {
+ unsafe {
+ let ptr = ffi::SSL_get0_verified_chain(self.as_ptr());
+ StackRef::from_const_ptr_opt(ptr)
+ }
+ }
+
+ /// Like [`SslContext::certificate`].
+ #[corresponds(SSL_get_certificate)]
+ pub fn certificate(&self) -> Option<&X509Ref> {
+ unsafe {
+ let ptr = ffi::SSL_get_certificate(self.as_ptr());
+ X509Ref::from_const_ptr_opt(ptr)
+ }
+ }
+
+ /// Like [`SslContext::private_key`].
+ ///
+ /// [`SslContext::private_key`]: struct.SslContext.html#method.private_key
+ #[corresponds(SSL_get_privatekey)]
+ pub fn private_key(&self) -> Option<&PKeyRef<Private>> {
+ unsafe {
+ let ptr = ffi::SSL_get_privatekey(self.as_ptr());
+ PKeyRef::from_const_ptr_opt(ptr)
+ }
+ }
+
+ #[deprecated(since = "0.10.5", note = "renamed to `version_str`")]
+ pub fn version(&self) -> &str {
+ self.version_str()
+ }
+
+ /// Returns the protocol version of the session.
+ #[corresponds(SSL_version)]
+ pub fn version2(&self) -> Option<SslVersion> {
+ unsafe {
+ let r = ffi::SSL_version(self.as_ptr());
+ if r == 0 {
+ None
+ } else {
+ Some(SslVersion(r))
+ }
+ }
+ }
+
+ /// Returns a string describing the protocol version of the session.
+ #[corresponds(SSL_get_version)]
+ pub fn version_str(&self) -> &'static str {
+ let version = unsafe {
+ let ptr = ffi::SSL_get_version(self.as_ptr());
+ CStr::from_ptr(ptr as *const _)
+ };
+
+ str::from_utf8(version.to_bytes()).unwrap()
+ }
+
+ /// Returns the protocol selected via Application Layer Protocol Negotiation (ALPN).
+ ///
+ /// 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 LibreSSL 2.6.1 or newer.
+ #[corresponds(SSL_get0_alpn_selected)]
+ #[cfg(any(ossl102, libressl261))]
+ pub fn selected_alpn_protocol(&self) -> Option<&[u8]> {
+ unsafe {
+ let mut data: *const c_uchar = ptr::null();
+ let mut len: c_uint = 0;
+ // Get the negotiated protocol from the SSL instance.
+ // `data` will point at a `c_uchar` array; `len` will contain the length of this array.
+ ffi::SSL_get0_alpn_selected(self.as_ptr(), &mut data, &mut len);
+
+ if data.is_null() {
+ None
+ } else {
+ Some(slice::from_raw_parts(data, len as usize))
+ }
+ }
+ }
+
+ /// Enables the DTLS extension "use_srtp" as defined in RFC5764.
+ ///
+ /// This corresponds to [`SSL_set_tlsext_use_srtp`].
+ ///
+ /// [`SSL_set_tlsext_use_srtp`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_use_srtp.html
+ #[corresponds(SSL_set_tlsext_use_srtp)]
+ pub fn set_tlsext_use_srtp(&mut self, protocols: &str) -> Result<(), ErrorStack> {
+ unsafe {
+ let cstr = CString::new(protocols).unwrap();
+
+ let r = ffi::SSL_set_tlsext_use_srtp(self.as_ptr(), cstr.as_ptr());
+ // fun fact, set_tlsext_use_srtp has a reversed return code D:
+ if r == 0 {
+ Ok(())
+ } else {
+ Err(ErrorStack::get())
+ }
+ }
+ }
+
+ /// Gets all SRTP profiles that are enabled for handshake via set_tlsext_use_srtp
+ ///
+ /// DTLS extension "use_srtp" as defined in RFC5764 has to be enabled.
+ ///
+ /// This corresponds to [`SSL_get_srtp_profiles`].
+ ///
+ /// [`SSL_get_srtp_profiles`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_use_srtp.html
+ #[corresponds(SSL_get_srtp_profiles)]
+ pub fn srtp_profiles(&self) -> Option<&StackRef<SrtpProtectionProfile>> {
+ unsafe {
+ let chain = ffi::SSL_get_srtp_profiles(self.as_ptr());
+
+ StackRef::from_const_ptr_opt(chain)
+ }
+ }
+
+ /// Gets the SRTP profile selected by handshake.
+ ///
+ /// DTLS extension "use_srtp" as defined in RFC5764 has to be enabled.
+ #[corresponds(SSL_get_selected_srtp_profile)]
+ pub fn selected_srtp_profile(&self) -> Option<&SrtpProtectionProfileRef> {
+ unsafe {
+ let profile = ffi::SSL_get_selected_srtp_profile(self.as_ptr());
+
+ SrtpProtectionProfileRef::from_const_ptr_opt(profile)
+ }
+ }
+
+ /// Returns the number of bytes remaining in the currently processed TLS record.
+ ///
+ /// If this is greater than 0, the next call to `read` will not call down to the underlying
+ /// stream.
+ #[corresponds(SSL_pending)]
+ pub fn pending(&self) -> usize {
+ unsafe { ffi::SSL_pending(self.as_ptr()) as usize }
+ }
+
+ /// Returns the servername sent by the client via Server Name Indication (SNI).
+ ///
+ /// It is only useful on the server side.
+ ///
+ /// # 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
+ #[corresponds(SSL_get_servername)]
+ // 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.
+ ///
+ /// # Note
+ ///
+ /// Unlike `servername`, this method does not require the name be valid UTF-8.
+ #[corresponds(SSL_get_servername)]
+ pub fn servername_raw(&self, type_: NameType) -> Option<&[u8]> {
+ unsafe {
+ let name = ffi::SSL_get_servername(self.as_ptr(), type_.0);
+ if name.is_null() {
+ None
+ } else {
+ Some(CStr::from_ptr(name as *const _).to_bytes())
+ }
+ }
+ }
+
+ /// Changes the context corresponding to the current connection.
+ ///
+ /// It is most commonly used in the Server Name Indication (SNI) callback.
+ #[corresponds(SSL_set_SSL_CTX)]
+ pub fn set_ssl_context(&mut self, ctx: &SslContextRef) -> Result<(), ErrorStack> {
+ unsafe { cvt_p(ffi::SSL_set_SSL_CTX(self.as_ptr(), ctx.as_ptr())).map(|_| ()) }
+ }
+
+ /// Returns the context corresponding to the current connection.
+ #[corresponds(SSL_get_SSL_CTX)]
+ pub fn ssl_context(&self) -> &SslContextRef {
+ unsafe {
+ let ssl_ctx = ffi::SSL_get_SSL_CTX(self.as_ptr());
+ SslContextRef::from_ptr(ssl_ctx)
+ }
+ }
+
+ /// Returns a mutable reference to the X509 verification configuration.
+ ///
+ /// Requires OpenSSL 1.0.2 or newer.
+ #[corresponds(SSL_get0_param)]
+ #[cfg(any(ossl102, libressl261))]
+ pub fn param_mut(&mut self) -> &mut X509VerifyParamRef {
+ unsafe { X509VerifyParamRef::from_ptr_mut(ffi::SSL_get0_param(self.as_ptr())) }
+ }
+
+ /// Returns the certificate verification result.
+ #[corresponds(SSL_get_verify_result)]
+ pub fn verify_result(&self) -> X509VerifyResult {
+ unsafe { X509VerifyResult::from_raw(ffi::SSL_get_verify_result(self.as_ptr()) as c_int) }
+ }
+
+ /// Returns a shared reference to the SSL session.
+ #[corresponds(SSL_get_session)]
+ pub fn session(&self) -> Option<&SslSessionRef> {
+ unsafe {
+ let p = ffi::SSL_get_session(self.as_ptr());
+ SslSessionRef::from_const_ptr_opt(p)
+ }
+ }
+
+ /// Copies the `client_random` value sent by the client in the TLS handshake into a buffer.
+ ///
+ /// Returns the number of bytes copied, or if the buffer is empty, the size of the `client_random`
+ /// value.
+ ///
+ /// Requires OpenSSL 1.1.0 or LibreSSL 2.7.0 or newer.
+ #[corresponds(SSL_get_client_random)]
+ #[cfg(any(ossl110, libressl270))]
+ pub fn client_random(&self, buf: &mut [u8]) -> usize {
+ unsafe {
+ ffi::SSL_get_client_random(self.as_ptr(), buf.as_mut_ptr() as *mut c_uchar, buf.len())
+ }
+ }
+
+ /// Copies the `server_random` value sent by the server in the TLS handshake into a buffer.
+ ///
+ /// Returns the number of bytes copied, or if the buffer is empty, the size of the `server_random`
+ /// value.
+ ///
+ /// Requires OpenSSL 1.1.0 or LibreSSL 2.7.0 or newer.
+ #[corresponds(SSL_get_server_random)]
+ #[cfg(any(ossl110, libressl270))]
+ pub fn server_random(&self, buf: &mut [u8]) -> usize {
+ unsafe {
+ ffi::SSL_get_server_random(self.as_ptr(), buf.as_mut_ptr() as *mut c_uchar, buf.len())
+ }
+ }
+
+ /// Derives keying material for application use in accordance to RFC 5705.
+ #[corresponds(SSL_export_keying_material)]
+ pub fn export_keying_material(
+ &self,
+ out: &mut [u8],
+ label: &str,
+ context: Option<&[u8]>,
+ ) -> Result<(), ErrorStack> {
+ unsafe {
+ let (context, contextlen, use_context) = match context {
+ Some(context) => (context.as_ptr() as *const c_uchar, context.len(), 1),
+ None => (ptr::null(), 0, 0),
+ };
+ cvt(ffi::SSL_export_keying_material(
+ self.as_ptr(),
+ out.as_mut_ptr() as *mut c_uchar,
+ out.len(),
+ label.as_ptr() as *const c_char,
+ label.len(),
+ context,
+ contextlen,
+ use_context,
+ ))
+ .map(|_| ())
+ }
+ }
+
+ /// 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.
+ #[corresponds(SSL_export_keying_material_early)]
+ #[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
+ /// session. If the server is not willing to reuse the session, a new one will be transparently
+ /// negotiated.
+ ///
+ /// # Safety
+ ///
+ /// The caller of this method is responsible for ensuring that the session is associated
+ /// with the same `SslContext` as this `Ssl`.
+ #[corresponds(SSL_set_session)]
+ pub unsafe fn set_session(&mut self, session: &SslSessionRef) -> Result<(), ErrorStack> {
+ cvt(ffi::SSL_set_session(self.as_ptr(), session.as_ptr())).map(|_| ())
+ }
+
+ /// Determines if the session provided to `set_session` was successfully reused.
+ #[corresponds(SSL_session_reused)]
+ pub fn session_reused(&self) -> bool {
+ unsafe { ffi::SSL_session_reused(self.as_ptr()) != 0 }
+ }
+
+ /// Sets the status response a client wishes the server to reply with.
+ #[corresponds(SSL_set_tlsext_status_type)]
+ pub fn set_status_type(&mut self, type_: StatusType) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::SSL_set_tlsext_status_type(self.as_ptr(), type_.as_raw()) as c_int).map(|_| ())
+ }
+ }
+
+ /// Determines if current session used Extended Master Secret
+ ///
+ /// Returns `None` if the handshake is still in-progress.
+ #[corresponds(SSL_get_extms_support)]
+ #[cfg(ossl110)]
+ pub fn extms_support(&self) -> Option<bool> {
+ unsafe {
+ match ffi::SSL_get_extms_support(self.as_ptr()) {
+ -1 => None,
+ ret => Some(ret != 0),
+ }
+ }
+ }
+
+ /// Returns the server's OCSP response, if present.
+ #[corresponds(SSL_get_tlsext_status_ocsp_resp)]
+ #[cfg(not(boringssl))]
+ pub fn ocsp_status(&self) -> Option<&[u8]> {
+ unsafe {
+ let mut p = ptr::null_mut();
+ let len = ffi::SSL_get_tlsext_status_ocsp_resp(self.as_ptr(), &mut p);
+
+ if len < 0 {
+ None
+ } else {
+ Some(slice::from_raw_parts(p as *const u8, len as usize))
+ }
+ }
+ }
+
+ /// Sets the OCSP response to be returned to the client.
+ #[corresponds(SSL_set_tlsext_status_oscp_resp)]
+ #[cfg(not(boringssl))]
+ pub fn set_ocsp_status(&mut self, response: &[u8]) -> Result<(), ErrorStack> {
+ unsafe {
+ assert!(response.len() <= c_int::max_value() as usize);
+ let p = cvt_p(ffi::OPENSSL_malloc(response.len() as _))?;
+ ptr::copy_nonoverlapping(response.as_ptr(), p as *mut u8, response.len());
+ cvt(ffi::SSL_set_tlsext_status_ocsp_resp(
+ self.as_ptr(),
+ p as *mut c_uchar,
+ response.len() as c_long,
+ ) as c_int)
+ .map(|_| ())
+ .map_err(|e| {
+ ffi::OPENSSL_free(p);
+ e
+ })
+ }
+ }
+
+ /// Determines if this `Ssl` is configured for server-side or client-side use.
+ #[corresponds(SSL_is_server)]
+ pub fn is_server(&self) -> bool {
+ unsafe { SSL_is_server(self.as_ptr()) != 0 }
+ }
+
+ /// Sets the extra data at the specified index.
+ ///
+ /// This can be used to provide data to callbacks registered with the context. Use the
+ /// `Ssl::new_ex_index` method to create an `Index`.
+ #[corresponds(SSL_set_ex_data)]
+ pub fn set_ex_data<T>(&mut self, index: Index<Ssl, T>, data: T) {
+ unsafe {
+ let data = Box::new(data);
+ ffi::SSL_set_ex_data(
+ self.as_ptr(),
+ index.as_raw(),
+ Box::into_raw(data) as *mut c_void,
+ );
+ }
+ }
+
+ /// Returns a reference to the extra data at the specified index.
+ #[corresponds(SSL_get_ex_data)]
+ pub fn ex_data<T>(&self, index: Index<Ssl, T>) -> Option<&T> {
+ unsafe {
+ let data = ffi::SSL_get_ex_data(self.as_ptr(), index.as_raw());
+ if data.is_null() {
+ None
+ } else {
+ Some(&*(data as *const T))
+ }
+ }
+ }
+
+ /// Returns a mutable reference to the extra data at the specified index.
+ #[corresponds(SSL_get_ex_data)]
+ pub fn ex_data_mut<T>(&mut self, index: Index<Ssl, T>) -> Option<&mut T> {
+ unsafe {
+ let data = ffi::SSL_get_ex_data(self.as_ptr(), index.as_raw());
+ if data.is_null() {
+ None
+ } else {
+ Some(&mut *(data as *mut T))
+ }
+ }
+ }
+
+ /// Sets the maximum amount of early data that will be accepted on this connection.
+ ///
+ /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
+ #[corresponds(SSL_set_max_early_data)]
+ #[cfg(any(ossl111, libressl340))]
+ 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 LibreSSL 3.4.0 or newer.
+ #[corresponds(SSL_get_max_early_data)]
+ #[cfg(any(ossl111, libressl340))]
+ 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.
+ #[corresponds(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.
+ #[corresponds(SSL_get_peer_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())
+ }
+ }
+
+ /// Determines if the initial handshake has been completed.
+ #[corresponds(SSL_is_init_finished)]
+ #[cfg(ossl110)]
+ pub fn is_init_finished(&self) -> bool {
+ unsafe { ffi::SSL_is_init_finished(self.as_ptr()) != 0 }
+ }
+
+ /// Determines if the client's hello message is in the SSLv2 format.
+ ///
+ /// This can only be used inside of the client hello callback. Otherwise, `false` is returned.
+ ///
+ /// Requires OpenSSL 1.1.1 or newer.
+ #[corresponds(SSL_client_hello_isv2)]
+ #[cfg(ossl111)]
+ pub fn client_hello_isv2(&self) -> bool {
+ unsafe { ffi::SSL_client_hello_isv2(self.as_ptr()) != 0 }
+ }
+
+ /// Returns the legacy version field of the client's hello message.
+ ///
+ /// This can only be used inside of the client hello callback. Otherwise, `None` is returned.
+ ///
+ /// Requires OpenSSL 1.1.1 or newer.
+ #[corresponds(SSL_client_hello_get0_legacy_version)]
+ #[cfg(ossl111)]
+ pub fn client_hello_legacy_version(&self) -> Option<SslVersion> {
+ unsafe {
+ let version = ffi::SSL_client_hello_get0_legacy_version(self.as_ptr());
+ if version == 0 {
+ None
+ } else {
+ Some(SslVersion(version as c_int))
+ }
+ }
+ }
+
+ /// Returns the random field of the client's hello message.
+ ///
+ /// This can only be used inside of the client hello callback. Otherwise, `None` is returned.
+ ///
+ /// Requires OpenSSL 1.1.1 or newer.
+ #[corresponds(SSL_client_hello_get0_random)]
+ #[cfg(ossl111)]
+ pub fn client_hello_random(&self) -> Option<&[u8]> {
+ unsafe {
+ let mut ptr = ptr::null();
+ let len = ffi::SSL_client_hello_get0_random(self.as_ptr(), &mut ptr);
+ if len == 0 {
+ None
+ } else {
+ Some(slice::from_raw_parts(ptr, len))
+ }
+ }
+ }
+
+ /// Returns the session ID field of the client's hello message.
+ ///
+ /// This can only be used inside of the client hello callback. Otherwise, `None` is returned.
+ ///
+ /// Requires OpenSSL 1.1.1 or newer.
+ #[corresponds(SSL_client_hello_get0_session_id)]
+ #[cfg(ossl111)]
+ pub fn client_hello_session_id(&self) -> Option<&[u8]> {
+ unsafe {
+ let mut ptr = ptr::null();
+ let len = ffi::SSL_client_hello_get0_session_id(self.as_ptr(), &mut ptr);
+ if len == 0 {
+ None
+ } else {
+ Some(slice::from_raw_parts(ptr, len))
+ }
+ }
+ }
+
+ /// Returns the ciphers field of the client's hello message.
+ ///
+ /// This can only be used inside of the client hello callback. Otherwise, `None` is returned.
+ ///
+ /// Requires OpenSSL 1.1.1 or newer.
+ #[corresponds(SSL_client_hello_get0_ciphers)]
+ #[cfg(ossl111)]
+ pub fn client_hello_ciphers(&self) -> Option<&[u8]> {
+ unsafe {
+ let mut ptr = ptr::null();
+ let len = ffi::SSL_client_hello_get0_ciphers(self.as_ptr(), &mut ptr);
+ if len == 0 {
+ None
+ } else {
+ Some(slice::from_raw_parts(ptr, len))
+ }
+ }
+ }
+
+ /// Returns the compression methods field of the client's hello message.
+ ///
+ /// This can only be used inside of the client hello callback. Otherwise, `None` is returned.
+ ///
+ /// Requires OpenSSL 1.1.1 or newer.
+ #[corresponds(SSL_client_hello_get0_compression_methods)]
+ #[cfg(ossl111)]
+ pub fn client_hello_compression_methods(&self) -> Option<&[u8]> {
+ unsafe {
+ let mut ptr = ptr::null();
+ let len = ffi::SSL_client_hello_get0_compression_methods(self.as_ptr(), &mut ptr);
+ if len == 0 {
+ None
+ } else {
+ Some(slice::from_raw_parts(ptr, len))
+ }
+ }
+ }
+
+ /// Sets the MTU used for DTLS connections.
+ #[corresponds(SSL_set_mtu)]
+ pub fn set_mtu(&mut self, mtu: u32) -> Result<(), ErrorStack> {
+ unsafe { cvt(ffi::SSL_set_mtu(self.as_ptr(), mtu as MtuTy) as c_int).map(|_| ()) }
+ }
+
+ /// Returns the PSK identity hint used during connection setup.
+ ///
+ /// May return `None` if no PSK identity hint was used during the connection setup.
+ #[corresponds(SSL_get_psk_identity_hint)]
+ #[cfg(not(osslconf = "OPENSSL_NO_PSK"))]
+ pub fn psk_identity_hint(&self) -> Option<&[u8]> {
+ unsafe {
+ let ptr = ffi::SSL_get_psk_identity_hint(self.as_ptr());
+ if ptr.is_null() {
+ None
+ } else {
+ Some(CStr::from_ptr(ptr).to_bytes())
+ }
+ }
+ }
+
+ /// Returns the PSK identity used during connection setup.
+ #[corresponds(SSL_get_psk_identity)]
+ #[cfg(not(osslconf = "OPENSSL_NO_PSK"))]
+ pub fn psk_identity(&self) -> Option<&[u8]> {
+ unsafe {
+ let ptr = ffi::SSL_get_psk_identity(self.as_ptr());
+ if ptr.is_null() {
+ None
+ } else {
+ Some(CStr::from_ptr(ptr).to_bytes())
+ }
+ }
+ }
+
+ #[corresponds(SSL_add0_chain_cert)]
+ #[cfg(ossl102)]
+ pub fn add_chain_cert(&mut self, chain: X509) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::SSL_add0_chain_cert(self.as_ptr(), chain.as_ptr()) as c_int).map(|_| ())?;
+ mem::forget(chain);
+ }
+ Ok(())
+ }
+
+ /// Sets a new default TLS/SSL method for SSL objects
+ #[cfg(not(boringssl))]
+ pub fn set_method(&mut self, method: SslMethod) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::SSL_set_ssl_method(self.as_ptr(), method.as_ptr()))?;
+ };
+ Ok(())
+ }
+
+ /// Loads the private key from a file.
+ #[corresponds(SSL_use_Private_Key_file)]
+ pub fn set_private_key_file<P: AsRef<Path>>(
+ &mut self,
+ path: P,
+ ssl_file_type: SslFiletype,
+ ) -> Result<(), ErrorStack> {
+ let p = path.as_ref().as_os_str().to_str().unwrap();
+ let key_file = CString::new(p).unwrap();
+ unsafe {
+ cvt(ffi::SSL_use_PrivateKey_file(
+ self.as_ptr(),
+ key_file.as_ptr(),
+ ssl_file_type.as_raw(),
+ ))?;
+ };
+ Ok(())
+ }
+
+ /// Sets the private key.
+ #[corresponds(SSL_use_PrivateKey)]
+ pub fn set_private_key(&mut self, pkey: &PKeyRef<Private>) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::SSL_use_PrivateKey(self.as_ptr(), pkey.as_ptr()))?;
+ };
+ Ok(())
+ }
+
+ /// Sets the certificate
+ #[corresponds(SSL_use_certificate)]
+ pub fn set_certificate(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::SSL_use_certificate(self.as_ptr(), cert.as_ptr()))?;
+ };
+ Ok(())
+ }
+
+ /// Loads a certificate chain from a file.
+ ///
+ /// The file should contain a sequence of PEM-formatted certificates, the first being the leaf
+ /// certificate, and the remainder forming the chain of certificates up to and including the
+ /// trusted root certificate.
+ #[corresponds(SSL_use_certificate_chain_file)]
+ #[cfg(any(ossl110, libressl332))]
+ pub fn set_certificate_chain_file<P: AsRef<Path>>(
+ &mut self,
+ path: P,
+ ) -> Result<(), ErrorStack> {
+ let p = path.as_ref().as_os_str().to_str().unwrap();
+ let cert_file = CString::new(p).unwrap();
+ unsafe {
+ cvt(ffi::SSL_use_certificate_chain_file(
+ self.as_ptr(),
+ cert_file.as_ptr(),
+ ))?;
+ };
+ Ok(())
+ }
+
+ /// Sets ca certificate that client trusted
+ #[corresponds(SSL_add_client_CA)]
+ pub fn add_client_ca(&mut self, cacert: &X509Ref) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::SSL_add_client_CA(self.as_ptr(), cacert.as_ptr()))?;
+ };
+ Ok(())
+ }
+
+ // Sets the list of CAs sent to the client when requesting a client certificate for the chosen ssl
+ #[corresponds(SSL_set_client_CA_list)]
+ pub fn set_client_ca_list(&mut self, list: Stack<X509Name>) {
+ unsafe { ffi::SSL_set_client_CA_list(self.as_ptr(), list.as_ptr()) }
+ mem::forget(list);
+ }
+
+ /// Sets the minimum supported protocol version.
+ ///
+ /// A value of `None` will enable protocol versions down to the lowest version supported by
+ /// OpenSSL.
+ ///
+ /// Requires OpenSSL 1.1.0 or LibreSSL 2.6.1 or newer.
+ #[corresponds(SSL_set_min_proto_version)]
+ #[cfg(any(ossl110, libressl261))]
+ pub fn set_min_proto_version(&mut self, version: Option<SslVersion>) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::SSL_set_min_proto_version(
+ self.as_ptr(),
+ version.map_or(0, |v| v.0 as _),
+ ))
+ .map(|_| ())
+ }
+ }
+
+ /// Sets the maximum supported protocol version.
+ ///
+ /// A value of `None` will enable protocol versions up to the highest version supported by
+ /// OpenSSL.
+ ///
+ /// Requires OpenSSL 1.1.0 or or LibreSSL 2.6.1 or newer.
+ #[corresponds(SSL_set_max_proto_version)]
+ #[cfg(any(ossl110, libressl261))]
+ pub fn set_max_proto_version(&mut self, version: Option<SslVersion>) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::SSL_set_max_proto_version(
+ self.as_ptr(),
+ version.map_or(0, |v| v.0 as _),
+ ))
+ .map(|_| ())
+ }
+ }
+
+ /// Sets the list of supported ciphers for the TLSv1.3 protocol.
+ ///
+ /// The `set_cipher_list` method controls the cipher suites for protocols before TLSv1.3.
+ ///
+ /// The format consists of TLSv1.3 cipher suite names separated by `:` characters in order of
+ /// preference.
+ ///
+ /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
+ #[corresponds(SSL_set_ciphersuites)]
+ #[cfg(any(ossl111, libressl340))]
+ pub fn set_ciphersuites(&mut self, cipher_list: &str) -> Result<(), ErrorStack> {
+ let cipher_list = CString::new(cipher_list).unwrap();
+ unsafe {
+ cvt(ffi::SSL_set_ciphersuites(
+ self.as_ptr(),
+ cipher_list.as_ptr() as *const _,
+ ))
+ .map(|_| ())
+ }
+ }
+
+ /// Sets the list of supported ciphers for protocols before TLSv1.3.
+ ///
+ /// The `set_ciphersuites` method controls the cipher suites for TLSv1.3.
+ ///
+ /// See [`ciphers`] for details on the format.
+ ///
+ /// [`ciphers`]: https://www.openssl.org/docs/manmaster/apps/ciphers.html
+ #[corresponds(SSL_set_cipher_list)]
+ pub fn set_cipher_list(&mut self, cipher_list: &str) -> Result<(), ErrorStack> {
+ let cipher_list = CString::new(cipher_list).unwrap();
+ unsafe {
+ cvt(ffi::SSL_set_cipher_list(
+ self.as_ptr(),
+ cipher_list.as_ptr() as *const _,
+ ))
+ .map(|_| ())
+ }
+ }
+
+ /// Set the certificate store used for certificate verification
+ #[corresponds(SSL_set_cert_store)]
+ #[cfg(ossl102)]
+ pub fn set_verify_cert_store(&mut self, cert_store: X509Store) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::SSL_set0_verify_cert_store(self.as_ptr(), cert_store.as_ptr()) as c_int)?;
+ mem::forget(cert_store);
+ Ok(())
+ }
+ }
+
+ /// Sets the number of TLS 1.3 session tickets that will be sent to a client after a full
+ /// handshake.
+ ///
+ /// Requires OpenSSL 1.1.1 or newer.
+ #[corresponds(SSL_set_num_tickets)]
+ #[cfg(ossl111)]
+ pub fn set_num_tickets(&mut self, num_tickets: usize) -> Result<(), ErrorStack> {
+ unsafe { cvt(ffi::SSL_set_num_tickets(self.as_ptr(), num_tickets)).map(|_| ()) }
+ }
+
+ /// Gets the number of TLS 1.3 session tickets that will be sent to a client after a full
+ /// handshake.
+ ///
+ /// Requires OpenSSL 1.1.1 or newer.
+ #[corresponds(SSL_get_num_tickets)]
+ #[cfg(ossl111)]
+ pub fn num_tickets(&self) -> usize {
+ unsafe { ffi::SSL_get_num_tickets(self.as_ptr()) }
+ }
+}
+
+/// An SSL stream midway through the handshake process.
+#[derive(Debug)]
+pub struct MidHandshakeSslStream<S> {
+ stream: SslStream<S>,
+ error: Error,
+}
+
+impl<S> MidHandshakeSslStream<S> {
+ /// Returns a shared reference to the inner stream.
+ pub fn get_ref(&self) -> &S {
+ self.stream.get_ref()
+ }
+
+ /// Returns a mutable reference to the inner stream.
+ pub fn get_mut(&mut self) -> &mut S {
+ self.stream.get_mut()
+ }
+
+ /// Returns a shared reference to the `Ssl` of the stream.
+ pub fn ssl(&self) -> &SslRef {
+ self.stream.ssl()
+ }
+
+ /// Returns the underlying error which interrupted this handshake.
+ pub fn error(&self) -> &Error {
+ &self.error
+ }
+
+ /// Consumes `self`, returning its error.
+ pub fn into_error(self) -> Error {
+ self.error
+ }
+}
+
+impl<S> MidHandshakeSslStream<S>
+where
+ S: Read + Write,
+{
+ /// Restarts the handshake process.
+ ///
+ /// This corresponds to [`SSL_do_handshake`].
+ ///
+ /// [`SSL_do_handshake`]: https://www.openssl.org/docs/manmaster/man3/SSL_do_handshake.html
+ pub fn handshake(mut self) -> Result<SslStream<S>, HandshakeError<S>> {
+ match self.stream.do_handshake() {
+ Ok(()) => Ok(self.stream),
+ Err(error) => {
+ self.error = error;
+ match self.error.code() {
+ ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => {
+ Err(HandshakeError::WouldBlock(self))
+ }
+ _ => Err(HandshakeError::Failure(self)),
+ }
+ }
+ }
+ }
+}
+
+/// A TLS session over a stream.
+pub struct SslStream<S> {
+ ssl: ManuallyDrop<Ssl>,
+ method: ManuallyDrop<BioMethod>,
+ _p: PhantomData<S>,
+}
+
+impl<S> Drop for SslStream<S> {
+ fn drop(&mut self) {
+ // ssl holds a reference to method internally so it has to drop first
+ unsafe {
+ ManuallyDrop::drop(&mut self.ssl);
+ ManuallyDrop::drop(&mut self.method);
+ }
+ }
+}
+
+impl<S> fmt::Debug for SslStream<S>
+where
+ S: fmt::Debug,
+{
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt.debug_struct("SslStream")
+ .field("stream", &self.get_ref())
+ .field("ssl", &self.ssl())
+ .finish()
+ }
+}
+
+impl<S: Read + Write> SslStream<S> {
+ /// Creates a new `SslStream`.
+ ///
+ /// This function performs no IO; the stream will not have performed any part of the handshake
+ /// with the peer. If the `Ssl` was configured with [`SslRef::set_connect_state`] or
+ /// [`SslRef::set_accept_state`], the handshake can be performed automatically during the first
+ /// call to read or write. Otherwise the `connect` and `accept` methods can be used to
+ /// explicitly perform the handshake.
+ #[corresponds(SSL_set_bio)]
+ pub fn new(ssl: Ssl, stream: S) -> Result<Self, ErrorStack> {
+ let (bio, method) = bio::new(stream)?;
+ unsafe {
+ ffi::SSL_set_bio(ssl.as_ptr(), bio, bio);
+ }
+
+ Ok(SslStream {
+ ssl: ManuallyDrop::new(ssl),
+ method: ManuallyDrop::new(method),
+ _p: PhantomData,
+ })
+ }
+
+ /// Constructs an `SslStream` from a pointer to the underlying OpenSSL `SSL` struct.
+ ///
+ /// This is useful if the handshake has already been completed elsewhere.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure the pointer is valid.
+ #[deprecated(
+ since = "0.10.32",
+ note = "use Ssl::from_ptr and SslStream::new instead"
+ )]
+ pub unsafe fn from_raw_parts(ssl: *mut ffi::SSL, stream: S) -> Self {
+ let ssl = Ssl::from_ptr(ssl);
+ Self::new(ssl, stream).unwrap()
+ }
+
+ /// Read application data transmitted by a client before handshake completion.
+ ///
+ /// Useful for reducing latency, but vulnerable to replay attacks. Call
+ /// [`SslRef::set_accept_state`] first.
+ ///
+ /// Returns `Ok(0)` if all early data has been read.
+ ///
+ /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
+ #[corresponds(SSL_read_early_data)]
+ #[cfg(any(ossl111, libressl340))]
+ pub fn read_early_data(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
+ let mut read = 0;
+ let ret = unsafe {
+ ffi::SSL_read_early_data(
+ self.ssl.as_ptr(),
+ buf.as_ptr() as *mut c_void,
+ buf.len(),
+ &mut read,
+ )
+ };
+ match ret {
+ ffi::SSL_READ_EARLY_DATA_ERROR => Err(self.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
+ /// [`SslRef::set_connect_state`] first.
+ ///
+ /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
+ #[corresponds(SSL_write_early_data)]
+ #[cfg(any(ossl111, libressl340))]
+ pub fn write_early_data(&mut self, buf: &[u8]) -> Result<usize, Error> {
+ let mut written = 0;
+ let ret = unsafe {
+ ffi::SSL_write_early_data(
+ self.ssl.as_ptr(),
+ buf.as_ptr() as *const c_void,
+ buf.len(),
+ &mut written,
+ )
+ };
+ if ret > 0 {
+ Ok(written)
+ } else {
+ Err(self.make_error(ret))
+ }
+ }
+
+ /// Initiates a client-side TLS handshake.
+ ///
+ /// # Warning
+ ///
+ /// OpenSSL's default configuration is insecure. It is highly recommended to use
+ /// `SslConnector` rather than `Ssl` directly, as it manages that configuration.
+ #[corresponds(SSL_connect)]
+ pub fn connect(&mut self) -> Result<(), Error> {
+ let ret = unsafe { ffi::SSL_connect(self.ssl.as_ptr()) };
+ if ret > 0 {
+ Ok(())
+ } else {
+ Err(self.make_error(ret))
+ }
+ }
+
+ /// Initiates a server-side TLS handshake.
+ ///
+ /// # Warning
+ ///
+ /// OpenSSL's default configuration is insecure. It is highly recommended to use
+ /// `SslAcceptor` rather than `Ssl` directly, as it manages that configuration.
+ #[corresponds(SSL_accept)]
+ pub fn accept(&mut self) -> Result<(), Error> {
+ let ret = unsafe { ffi::SSL_accept(self.ssl.as_ptr()) };
+ if ret > 0 {
+ Ok(())
+ } else {
+ Err(self.make_error(ret))
+ }
+ }
+
+ /// Initiates the handshake.
+ ///
+ /// This will fail if `set_accept_state` or `set_connect_state` was not called first.
+ #[corresponds(SSL_do_handshake)]
+ pub fn do_handshake(&mut self) -> Result<(), Error> {
+ let ret = unsafe { ffi::SSL_do_handshake(self.ssl.as_ptr()) };
+ if ret > 0 {
+ Ok(())
+ } else {
+ Err(self.make_error(ret))
+ }
+ }
+
+ /// Perform a stateless server-side handshake.
+ ///
+ /// Requires that cookie generation and verification callbacks were
+ /// set on the SSL context.
+ ///
+ /// Returns `Ok(true)` if a complete ClientHello containing a valid cookie
+ /// was read, in which case the handshake should be continued via
+ /// `accept`. If a HelloRetryRequest containing a fresh cookie was
+ /// transmitted, `Ok(false)` is returned instead. If the handshake cannot
+ /// proceed at all, `Err` is returned.
+ #[corresponds(SSL_stateless)]
+ #[cfg(ossl111)]
+ pub fn stateless(&mut self) -> Result<bool, ErrorStack> {
+ match unsafe { ffi::SSL_stateless(self.ssl.as_ptr()) } {
+ 1 => Ok(true),
+ 0 => Ok(false),
+ -1 => Err(ErrorStack::get()),
+ _ => unreachable!(),
+ }
+ }
+
+ /// Like `read`, but returns an `ssl::Error` rather than an `io::Error`.
+ ///
+ /// It is particularly useful with a non-blocking socket, where the error value will identify if
+ /// OpenSSL is waiting on read or write readiness.
+ #[corresponds(SSL_read)]
+ pub fn ssl_read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
+ // The interpretation of the return code here is a little odd with a
+ // zero-length write. OpenSSL will likely correctly report back to us
+ // that it read zero bytes, but zero is also the sentinel for "error".
+ // To avoid that confusion short-circuit that logic and return quickly
+ // if `buf` has a length of zero.
+ if buf.is_empty() {
+ return Ok(0);
+ }
+
+ let ret = self.ssl.read(buf);
+ if ret > 0 {
+ Ok(ret as usize)
+ } else {
+ Err(self.make_error(ret))
+ }
+ }
+
+ /// Like `write`, but returns an `ssl::Error` rather than an `io::Error`.
+ ///
+ /// It is particularly useful with a non-blocking socket, where the error value will identify if
+ /// OpenSSL is waiting on read or write readiness.
+ #[corresponds(SSL_write)]
+ pub fn ssl_write(&mut self, buf: &[u8]) -> Result<usize, Error> {
+ // See above for why we short-circuit on zero-length buffers
+ if buf.is_empty() {
+ return Ok(0);
+ }
+
+ let ret = self.ssl.write(buf);
+ if ret > 0 {
+ Ok(ret as usize)
+ } else {
+ Err(self.make_error(ret))
+ }
+ }
+
+ /// Reads data from the stream, without removing it from the queue.
+ #[corresponds(SSL_peek)]
+ pub fn ssl_peek(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
+ // See above for why we short-circuit on zero-length buffers
+ if buf.is_empty() {
+ return Ok(0);
+ }
+
+ let ret = self.ssl.peek(buf);
+ if ret > 0 {
+ Ok(ret as usize)
+ } else {
+ Err(self.make_error(ret))
+ }
+ }
+
+ /// Shuts down the session.
+ ///
+ /// The shutdown process consists of two steps. The first step sends a close notify message to
+ /// the peer, after which `ShutdownResult::Sent` is returned. The second step awaits the receipt
+ /// of a close notify message from the peer, after which `ShutdownResult::Received` is returned.
+ ///
+ /// While the connection may be closed after the first step, it is recommended to fully shut the
+ /// session down. In particular, it must be fully shut down if the connection is to be used for
+ /// further communication in the future.
+ #[corresponds(SSL_shutdown)]
+ pub fn shutdown(&mut self) -> Result<ShutdownResult, Error> {
+ match unsafe { ffi::SSL_shutdown(self.ssl.as_ptr()) } {
+ 0 => Ok(ShutdownResult::Sent),
+ 1 => Ok(ShutdownResult::Received),
+ n => Err(self.make_error(n)),
+ }
+ }
+
+ /// Returns the session's shutdown state.
+ #[corresponds(SSL_get_shutdown)]
+ pub fn get_shutdown(&mut self) -> ShutdownState {
+ unsafe {
+ let bits = ffi::SSL_get_shutdown(self.ssl.as_ptr());
+ ShutdownState { bits }
+ }
+ }
+
+ /// Sets the session's shutdown state.
+ ///
+ /// This can be used to tell OpenSSL that the session should be cached even if a full two-way
+ /// shutdown was not completed.
+ #[corresponds(SSL_set_shutdown)]
+ pub fn set_shutdown(&mut self, state: ShutdownState) {
+ unsafe { ffi::SSL_set_shutdown(self.ssl.as_ptr(), state.bits()) }
+ }
+}
+
+impl<S> SslStream<S> {
+ fn make_error(&mut self, ret: c_int) -> Error {
+ self.check_panic();
+
+ let code = self.ssl.get_error(ret);
+
+ let cause = match code {
+ ErrorCode::SSL => Some(InnerError::Ssl(ErrorStack::get())),
+ ErrorCode::SYSCALL => {
+ let errs = ErrorStack::get();
+ if errs.errors().is_empty() {
+ self.get_bio_error().map(InnerError::Io)
+ } else {
+ Some(InnerError::Ssl(errs))
+ }
+ }
+ ErrorCode::ZERO_RETURN => None,
+ ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => {
+ self.get_bio_error().map(InnerError::Io)
+ }
+ _ => None,
+ };
+
+ Error { code, cause }
+ }
+
+ fn check_panic(&mut self) {
+ if let Some(err) = unsafe { bio::take_panic::<S>(self.ssl.get_raw_rbio()) } {
+ resume_unwind(err)
+ }
+ }
+
+ fn get_bio_error(&mut self) -> Option<io::Error> {
+ unsafe { bio::take_error::<S>(self.ssl.get_raw_rbio()) }
+ }
+
+ /// Returns a shared reference to the underlying stream.
+ pub fn get_ref(&self) -> &S {
+ unsafe {
+ let bio = self.ssl.get_raw_rbio();
+ bio::get_ref(bio)
+ }
+ }
+
+ /// Returns a mutable reference to the underlying stream.
+ ///
+ /// # Warning
+ ///
+ /// It is inadvisable to read from or write to the underlying stream as it
+ /// will most likely corrupt the SSL session.
+ pub fn get_mut(&mut self) -> &mut S {
+ unsafe {
+ let bio = self.ssl.get_raw_rbio();
+ bio::get_mut(bio)
+ }
+ }
+
+ /// Returns a shared reference to the `Ssl` object associated with this stream.
+ pub fn ssl(&self) -> &SslRef {
+ &self.ssl
+ }
+}
+
+impl<S: Read + Write> Read for SslStream<S> {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ loop {
+ match self.ssl_read(buf) {
+ Ok(n) => return Ok(n),
+ Err(ref e) if e.code() == ErrorCode::ZERO_RETURN => return Ok(0),
+ Err(ref e) if e.code() == ErrorCode::SYSCALL && e.io_error().is_none() => {
+ return Ok(0);
+ }
+ Err(ref e) if e.code() == ErrorCode::WANT_READ && e.io_error().is_none() => {}
+ Err(e) => {
+ return Err(e
+ .into_io_error()
+ .unwrap_or_else(|e| io::Error::new(io::ErrorKind::Other, e)));
+ }
+ }
+ }
+ }
+}
+
+impl<S: Read + Write> Write for SslStream<S> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ loop {
+ match self.ssl_write(buf) {
+ 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()
+ .unwrap_or_else(|e| io::Error::new(io::ErrorKind::Other, e)));
+ }
+ }
+ }
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.get_mut().flush()
+ }
+}
+
+/// A partially constructed `SslStream`, useful for unusual handshakes.
+#[deprecated(
+ since = "0.10.32",
+ note = "use the methods directly on Ssl/SslStream instead"
+)]
+pub struct SslStreamBuilder<S> {
+ inner: SslStream<S>,
+}
+
+#[allow(deprecated)]
+impl<S> SslStreamBuilder<S>
+where
+ S: Read + Write,
+{
+ /// Begin creating an `SslStream` atop `stream`
+ pub fn new(ssl: Ssl, stream: S) -> Self {
+ Self {
+ inner: SslStream::new(ssl, stream).unwrap(),
+ }
+ }
+
+ /// Perform a stateless server-side handshake
+ ///
+ /// Requires that cookie generation and verification callbacks were
+ /// set on the SSL context.
+ ///
+ /// Returns `Ok(true)` if a complete ClientHello containing a valid cookie
+ /// was read, in which case the handshake should be continued via
+ /// `accept`. If a HelloRetryRequest containing a fresh cookie was
+ /// transmitted, `Ok(false)` is returned instead. If the handshake cannot
+ /// proceed at all, `Err` is returned.
+ ///
+ /// This corresponds to [`SSL_stateless`]
+ ///
+ /// [`SSL_stateless`]: https://www.openssl.org/docs/manmaster/man3/SSL_stateless.html
+ #[cfg(ossl111)]
+ pub fn stateless(&mut self) -> Result<bool, ErrorStack> {
+ match unsafe { ffi::SSL_stateless(self.inner.ssl.as_ptr()) } {
+ 1 => Ok(true),
+ 0 => Ok(false),
+ -1 => Err(ErrorStack::get()),
+ _ => unreachable!(),
+ }
+ }
+
+ /// 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(mut self) -> Result<SslStream<S>, HandshakeError<S>> {
+ match self.inner.connect() {
+ Ok(()) => Ok(self.inner),
+ Err(error) => match error.code() {
+ ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => {
+ Err(HandshakeError::WouldBlock(MidHandshakeSslStream {
+ stream: self.inner,
+ error,
+ }))
+ }
+ _ => Err(HandshakeError::Failure(MidHandshakeSslStream {
+ stream: self.inner,
+ error,
+ })),
+ },
+ }
+ }
+
+ /// See `Ssl::accept`
+ pub fn accept(mut self) -> Result<SslStream<S>, HandshakeError<S>> {
+ match self.inner.accept() {
+ Ok(()) => Ok(self.inner),
+ Err(error) => match error.code() {
+ ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => {
+ Err(HandshakeError::WouldBlock(MidHandshakeSslStream {
+ stream: self.inner,
+ error,
+ }))
+ }
+ _ => Err(HandshakeError::Failure(MidHandshakeSslStream {
+ stream: self.inner,
+ error,
+ })),
+ },
+ }
+ }
+
+ /// 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(mut self) -> Result<SslStream<S>, HandshakeError<S>> {
+ match self.inner.do_handshake() {
+ Ok(()) => Ok(self.inner),
+ Err(error) => match error.code() {
+ ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => {
+ Err(HandshakeError::WouldBlock(MidHandshakeSslStream {
+ stream: self.inner,
+ error,
+ }))
+ }
+ _ => Err(HandshakeError::Failure(MidHandshakeSslStream {
+ stream: self.inner,
+ 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 LibreSSL 3.4.0 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(any(ossl111, libressl340))]
+ pub fn read_early_data(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
+ self.inner.read_early_data(buf)
+ }
+
+ /// 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 LibreSSL 3.4.0 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(any(ossl111, libressl340))]
+ pub fn write_early_data(&mut self, buf: &[u8]) -> Result<usize, Error> {
+ self.inner.write_early_data(buf)
+ }
+}
+
+#[allow(deprecated)]
+impl<S> SslStreamBuilder<S> {
+ /// Returns a shared reference to the underlying stream.
+ pub fn get_ref(&self) -> &S {
+ unsafe {
+ let bio = self.inner.ssl.get_raw_rbio();
+ bio::get_ref(bio)
+ }
+ }
+
+ /// Returns a mutable reference to the underlying stream.
+ ///
+ /// # Warning
+ ///
+ /// It is inadvisable to read from or write to the underlying stream as it
+ /// will most likely corrupt the SSL session.
+ pub fn get_mut(&mut self) -> &mut S {
+ unsafe {
+ let bio = self.inner.ssl.get_raw_rbio();
+ bio::get_mut(bio)
+ }
+ }
+
+ /// Returns a shared reference to the `Ssl` object associated with this builder.
+ pub fn ssl(&self) -> &SslRef {
+ &self.inner.ssl
+ }
+
+ /// Set the DTLS MTU size.
+ ///
+ /// It will be ignored if the value is smaller than the minimum packet size
+ /// the DTLS protocol requires.
+ ///
+ /// # Panics
+ /// This function panics if the given mtu size can't be represented in a positive `c_long` range
+ #[deprecated(note = "Use SslRef::set_mtu instead", since = "0.10.30")]
+ pub fn set_dtls_mtu_size(&mut self, mtu_size: usize) {
+ unsafe {
+ let bio = self.inner.ssl.get_raw_rbio();
+ bio::set_dtls_mtu_size::<S>(bio, mtu_size);
+ }
+ }
+}
+
+/// The result of a shutdown request.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum ShutdownResult {
+ /// A close notify message has been sent to the peer.
+ Sent,
+
+ /// A close notify response message has been received from the peer.
+ Received,
+}
+
+bitflags! {
+ /// The shutdown state of a session.
+ pub struct ShutdownState: c_int {
+ /// A close notify message has been sent to the peer.
+ const SENT = ffi::SSL_SENT_SHUTDOWN;
+ /// A close notify message has been received from the peer.
+ const RECEIVED = ffi::SSL_RECEIVED_SHUTDOWN;
+ }
+}
+
+cfg_if! {
+ if #[cfg(any(boringssl, ossl110, libressl273))] {
+ use ffi::{SSL_CTX_up_ref, SSL_SESSION_get_master_key, SSL_SESSION_up_ref, SSL_is_server};
+ } else {
+ #[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
+ }
+
+ #[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
+ }
+
+ #[allow(bad_style)]
+ pub unsafe fn SSL_is_server(s: *mut ffi::SSL) -> c_int {
+ (*s).server
+ }
+
+ #[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
+ }
+ }
+}
+
+cfg_if! {
+ if #[cfg(ossl300)] {
+ use ffi::SSL_get1_peer_certificate;
+ } else {
+ use ffi::SSL_get_peer_certificate as SSL_get1_peer_certificate;
+ }
+}
+cfg_if! {
+ if #[cfg(any(boringssl, ossl110, libressl291))] {
+ use ffi::{TLS_method, DTLS_method, TLS_client_method, TLS_server_method};
+ } else {
+ use ffi::{
+ SSLv23_method as TLS_method, DTLSv1_method as DTLS_method, SSLv23_client_method as TLS_client_method,
+ SSLv23_server_method as TLS_server_method,
+ };
+ }
+}
+cfg_if! {
+ if #[cfg(ossl110)] {
+ 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),
+ )
+ }
+
+ 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 std::sync::Once;
+
+ unsafe fn get_new_idx(f: ffi::CRYPTO_EX_free) -> c_int {
+ // hack around https://rt.openssl.org/Ticket/Display.html?id=3710&user=guest&pass=guest
+ static ONCE: Once = Once::new();
+ ONCE.call_once(|| {
+ cfg_if! {
+ if #[cfg(not(boringssl))] {
+ ffi::SSL_CTX_get_ex_new_index(0, ptr::null_mut(), None, None, None);
+ } else {
+ ffi::SSL_CTX_get_ex_new_index(0, ptr::null_mut(), ptr::null_mut(), None, None);
+ }
+ }
+ });
+
+ cfg_if! {
+ if #[cfg(not(boringssl))] {
+ ffi::SSL_CTX_get_ex_new_index(0, ptr::null_mut(), None, None, Some(f))
+ } else {
+ ffi::SSL_CTX_get_ex_new_index(0, ptr::null_mut(), ptr::null_mut(), None, f)
+ }
+ }
+ }
+
+ unsafe fn get_new_ssl_idx(f: ffi::CRYPTO_EX_free) -> c_int {
+ // hack around https://rt.openssl.org/Ticket/Display.html?id=3710&user=guest&pass=guest
+ static ONCE: Once = Once::new();
+ ONCE.call_once(|| {
+ #[cfg(not(boringssl))]
+ ffi::SSL_get_ex_new_index(0, ptr::null_mut(), None, None, None);
+ #[cfg(boringssl)]
+ ffi::SSL_get_ex_new_index(0, ptr::null_mut(), ptr::null_mut(), None, None);
+ });
+
+ #[cfg(not(boringssl))]
+ return ffi::SSL_get_ex_new_index(0, ptr::null_mut(), None, None, Some(f));
+ #[cfg(boringssl)]
+ return ffi::SSL_get_ex_new_index(0, ptr::null_mut(), ptr::null_mut(), None, f);
+ }
+ }
+}
diff --git a/vendor/openssl/src/ssl/test/mod.rs b/vendor/openssl/src/ssl/test/mod.rs
new file mode 100644
index 000000000..a34309a7d
--- /dev/null
+++ b/vendor/openssl/src/ssl/test/mod.rs
@@ -0,0 +1,1567 @@
+#![allow(unused_imports)]
+
+use std::env;
+use std::fs::File;
+use std::io::prelude::*;
+use std::io::{self, BufReader};
+use std::iter;
+use std::mem;
+use std::net::UdpSocket;
+use std::net::{SocketAddr, TcpListener, TcpStream};
+use std::path::Path;
+use std::process::{Child, ChildStdin, Command, Stdio};
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::thread;
+use std::time::Duration;
+
+use crate::dh::Dh;
+use crate::error::ErrorStack;
+use crate::hash::MessageDigest;
+#[cfg(not(boringssl))]
+use crate::ocsp::{OcspResponse, OcspResponseStatus};
+use crate::pkey::PKey;
+use crate::srtp::SrtpProfileId;
+use crate::ssl::test::server::Server;
+#[cfg(any(ossl110, ossl111, libressl261))]
+use crate::ssl::SslVersion;
+use crate::ssl::{self, NameType, SslConnectorBuilder};
+#[cfg(ossl111)]
+use crate::ssl::{ClientHelloResponse, ExtensionContext};
+use crate::ssl::{
+ Error, HandshakeError, MidHandshakeSslStream, ShutdownResult, ShutdownState, Ssl, SslAcceptor,
+ SslAcceptorBuilder, SslConnector, SslContext, SslContextBuilder, SslFiletype, SslMethod,
+ SslOptions, SslSessionCacheMode, SslStream, SslVerifyMode, StatusType,
+};
+#[cfg(ossl102)]
+use crate::x509::store::X509StoreBuilder;
+#[cfg(ossl102)]
+use crate::x509::verify::X509CheckFlags;
+use crate::x509::{X509Name, X509StoreContext, X509VerifyResult, X509};
+
+mod server;
+
+static ROOT_CERT: &[u8] = include_bytes!("../../../test/root-ca.pem");
+static CERT: &[u8] = include_bytes!("../../../test/cert.pem");
+static KEY: &[u8] = include_bytes!("../../../test/key.pem");
+
+#[test]
+fn verify_untrusted() {
+ let mut server = Server::builder();
+ server.should_error();
+ let server = server.build();
+
+ let mut client = server.client();
+ client.ctx().set_verify(SslVerifyMode::PEER);
+
+ client.connect_err();
+}
+
+#[test]
+fn verify_trusted() {
+ let server = Server::builder().build();
+
+ let mut client = server.client();
+ client.ctx().set_ca_file("test/root-ca.pem").unwrap();
+
+ client.connect();
+}
+
+#[test]
+#[cfg(ossl102)]
+fn verify_trusted_with_set_cert() {
+ let server = Server::builder().build();
+
+ let mut store = X509StoreBuilder::new().unwrap();
+ let x509 = X509::from_pem(ROOT_CERT).unwrap();
+ store.add_cert(x509).unwrap();
+
+ let mut client = server.client();
+ client.ctx().set_verify(SslVerifyMode::PEER);
+ client.ctx().set_verify_cert_store(store.build()).unwrap();
+
+ client.connect();
+}
+
+#[test]
+fn verify_untrusted_callback_override_ok() {
+ static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
+ let server = Server::builder().build();
+
+ let mut client = server.client();
+ client
+ .ctx()
+ .set_verify_callback(SslVerifyMode::PEER, |_, x509| {
+ CALLED_BACK.store(true, Ordering::SeqCst);
+ assert!(x509.current_cert().is_some());
+ true
+ });
+
+ client.connect();
+ assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+#[test]
+fn verify_untrusted_callback_override_bad() {
+ let mut server = Server::builder();
+ server.should_error();
+ let server = server.build();
+
+ let mut client = server.client();
+ client
+ .ctx()
+ .set_verify_callback(SslVerifyMode::PEER, |_, _| false);
+
+ client.connect_err();
+}
+
+#[test]
+fn verify_trusted_callback_override_ok() {
+ static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
+ let server = Server::builder().build();
+
+ let mut client = server.client();
+ client.ctx().set_ca_file("test/root-ca.pem").unwrap();
+ client
+ .ctx()
+ .set_verify_callback(SslVerifyMode::PEER, |_, x509| {
+ CALLED_BACK.store(true, Ordering::SeqCst);
+ assert!(x509.current_cert().is_some());
+ true
+ });
+
+ client.connect();
+ assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+#[test]
+fn verify_trusted_callback_override_bad() {
+ let mut server = Server::builder();
+ server.should_error();
+ let server = server.build();
+
+ let mut client = server.client();
+ client.ctx().set_ca_file("test/root-ca.pem").unwrap();
+ client
+ .ctx()
+ .set_verify_callback(SslVerifyMode::PEER, |_, _| false);
+
+ client.connect_err();
+}
+
+#[test]
+fn verify_callback_load_certs() {
+ static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
+ let server = Server::builder().build();
+
+ let mut client = server.client();
+ client
+ .ctx()
+ .set_verify_callback(SslVerifyMode::PEER, |_, x509| {
+ CALLED_BACK.store(true, Ordering::SeqCst);
+ assert!(x509.current_cert().is_some());
+ true
+ });
+
+ client.connect();
+ assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+#[test]
+fn verify_trusted_get_error_ok() {
+ static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
+ let server = Server::builder().build();
+
+ let mut client = server.client();
+ client.ctx().set_ca_file("test/root-ca.pem").unwrap();
+ client
+ .ctx()
+ .set_verify_callback(SslVerifyMode::PEER, |_, x509| {
+ CALLED_BACK.store(true, Ordering::SeqCst);
+ assert_eq!(x509.error(), X509VerifyResult::OK);
+ true
+ });
+
+ client.connect();
+ assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+#[test]
+fn verify_trusted_get_error_err() {
+ let mut server = Server::builder();
+ server.should_error();
+ let server = server.build();
+
+ let mut client = server.client();
+ client
+ .ctx()
+ .set_verify_callback(SslVerifyMode::PEER, |_, x509| {
+ assert_ne!(x509.error(), X509VerifyResult::OK);
+ false
+ });
+
+ client.connect_err();
+}
+
+#[test]
+fn verify_callback() {
+ static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
+ let server = Server::builder().build();
+
+ let mut client = server.client();
+ let expected = "59172d9313e84459bcff27f967e79e6e9217e584";
+ client
+ .ctx()
+ .set_verify_callback(SslVerifyMode::PEER, move |_, x509| {
+ CALLED_BACK.store(true, Ordering::SeqCst);
+ let cert = x509.current_cert().unwrap();
+ let digest = cert.digest(MessageDigest::sha1()).unwrap();
+ assert_eq!(hex::encode(digest), expected);
+ true
+ });
+
+ client.connect();
+ assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+#[test]
+fn ssl_verify_callback() {
+ static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
+ let server = Server::builder().build();
+
+ let mut client = server.client().build().builder();
+ let expected = "59172d9313e84459bcff27f967e79e6e9217e584";
+ client
+ .ssl()
+ .set_verify_callback(SslVerifyMode::PEER, move |_, x509| {
+ CALLED_BACK.store(true, Ordering::SeqCst);
+ let cert = x509.current_cert().unwrap();
+ let digest = cert.digest(MessageDigest::sha1()).unwrap();
+ assert_eq!(hex::encode(digest), expected);
+ true
+ });
+
+ client.connect();
+ assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+#[test]
+fn get_ctx_options() {
+ let ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ctx.options();
+}
+
+#[test]
+fn set_ctx_options() {
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ let opts = ctx.set_options(SslOptions::NO_TICKET);
+ assert!(opts.contains(SslOptions::NO_TICKET));
+}
+
+#[test]
+#[cfg(not(boringssl))]
+fn clear_ctx_options() {
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ctx.set_options(SslOptions::ALL);
+ let opts = ctx.clear_options(SslOptions::ALL);
+ assert!(!opts.contains(SslOptions::ALL));
+}
+
+#[test]
+fn zero_length_buffers() {
+ let server = Server::builder().build();
+
+ let mut s = server.client().connect();
+ assert_eq!(s.write(&[]).unwrap(), 0);
+ assert_eq!(s.read(&mut []).unwrap(), 0);
+}
+
+#[test]
+fn peer_certificate() {
+ let server = Server::builder().build();
+
+ let s = server.client().connect();
+ let cert = s.ssl().peer_certificate().unwrap();
+ let fingerprint = cert.digest(MessageDigest::sha1()).unwrap();
+ assert_eq!(
+ hex::encode(fingerprint),
+ "59172d9313e84459bcff27f967e79e6e9217e584"
+ );
+}
+
+#[test]
+fn pending() {
+ let mut server = Server::builder();
+ server.io_cb(|mut s| s.write_all(&[0; 10]).unwrap());
+ let server = server.build();
+
+ let mut s = server.client().connect();
+ s.read_exact(&mut [0]).unwrap();
+
+ assert_eq!(s.ssl().pending(), 9);
+ assert_eq!(s.read(&mut [0; 10]).unwrap(), 9);
+}
+
+#[test]
+fn state() {
+ let server = Server::builder().build();
+
+ let s = server.client().connect();
+ #[cfg(not(boringssl))]
+ assert_eq!(s.ssl().state_string().trim(), "SSLOK");
+ #[cfg(boringssl)]
+ assert_eq!(s.ssl().state_string(), "!!!!!!");
+ assert_eq!(
+ s.ssl().state_string_long(),
+ "SSL negotiation finished successfully"
+ );
+}
+
+/// Tests that when both the client as well as the server use SRTP and their
+/// lists of supported protocols have an overlap -- with only ONE protocol
+/// being valid for both.
+#[test]
+fn test_connect_with_srtp_ctx() {
+ let listener = TcpListener::bind("127.0.0.1:0").unwrap();
+ let addr = listener.local_addr().unwrap();
+
+ let guard = thread::spawn(move || {
+ let stream = listener.accept().unwrap().0;
+ let mut ctx = SslContext::builder(SslMethod::dtls()).unwrap();
+ ctx.set_tlsext_use_srtp("SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32")
+ .unwrap();
+ ctx.set_certificate_file(Path::new("test/cert.pem"), SslFiletype::PEM)
+ .unwrap();
+ ctx.set_private_key_file(Path::new("test/key.pem"), SslFiletype::PEM)
+ .unwrap();
+ let mut ssl = Ssl::new(&ctx.build()).unwrap();
+ ssl.set_mtu(1500).unwrap();
+ let mut stream = ssl.accept(stream).unwrap();
+
+ let mut buf = [0; 60];
+ stream
+ .ssl()
+ .export_keying_material(&mut buf, "EXTRACTOR-dtls_srtp", None)
+ .unwrap();
+
+ stream.write_all(&[0]).unwrap();
+
+ buf
+ });
+
+ let stream = TcpStream::connect(addr).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::dtls()).unwrap();
+ ctx.set_tlsext_use_srtp("SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32")
+ .unwrap();
+ let mut ssl = Ssl::new(&ctx.build()).unwrap();
+ ssl.set_mtu(1500).unwrap();
+ let mut stream = ssl.connect(stream).unwrap();
+
+ let mut buf = [1; 60];
+ {
+ let srtp_profile = stream.ssl().selected_srtp_profile().unwrap();
+ assert_eq!("SRTP_AES128_CM_SHA1_80", srtp_profile.name());
+ assert_eq!(SrtpProfileId::SRTP_AES128_CM_SHA1_80, srtp_profile.id());
+ }
+ stream
+ .ssl()
+ .export_keying_material(&mut buf, "EXTRACTOR-dtls_srtp", None)
+ .expect("extract");
+
+ stream.read_exact(&mut [0]).unwrap();
+
+ let buf2 = guard.join().unwrap();
+
+ assert_eq!(buf[..], buf2[..]);
+}
+
+/// Tests that when both the client as well as the server use SRTP and their
+/// lists of supported protocols have an overlap -- with only ONE protocol
+/// being valid for both.
+#[test]
+fn test_connect_with_srtp_ssl() {
+ let listener = TcpListener::bind("127.0.0.1:0").unwrap();
+ let addr = listener.local_addr().unwrap();
+
+ let guard = thread::spawn(move || {
+ let stream = listener.accept().unwrap().0;
+ let mut ctx = SslContext::builder(SslMethod::dtls()).unwrap();
+ ctx.set_certificate_file(Path::new("test/cert.pem"), SslFiletype::PEM)
+ .unwrap();
+ ctx.set_private_key_file(Path::new("test/key.pem"), SslFiletype::PEM)
+ .unwrap();
+ let mut ssl = Ssl::new(&ctx.build()).unwrap();
+ ssl.set_tlsext_use_srtp("SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32")
+ .unwrap();
+ let mut profilenames = String::new();
+ for profile in ssl.srtp_profiles().unwrap() {
+ if !profilenames.is_empty() {
+ profilenames.push(':');
+ }
+ profilenames += profile.name();
+ }
+ assert_eq!(
+ "SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32",
+ profilenames
+ );
+ ssl.set_mtu(1500).unwrap();
+ let mut stream = ssl.accept(stream).unwrap();
+
+ let mut buf = [0; 60];
+ stream
+ .ssl()
+ .export_keying_material(&mut buf, "EXTRACTOR-dtls_srtp", None)
+ .unwrap();
+
+ stream.write_all(&[0]).unwrap();
+
+ buf
+ });
+
+ let stream = TcpStream::connect(addr).unwrap();
+ let ctx = SslContext::builder(SslMethod::dtls()).unwrap();
+ let mut ssl = Ssl::new(&ctx.build()).unwrap();
+ ssl.set_tlsext_use_srtp("SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32")
+ .unwrap();
+ ssl.set_mtu(1500).unwrap();
+ let mut stream = ssl.connect(stream).unwrap();
+
+ let mut buf = [1; 60];
+ {
+ let srtp_profile = stream.ssl().selected_srtp_profile().unwrap();
+ assert_eq!("SRTP_AES128_CM_SHA1_80", srtp_profile.name());
+ assert_eq!(SrtpProfileId::SRTP_AES128_CM_SHA1_80, srtp_profile.id());
+ }
+ stream
+ .ssl()
+ .export_keying_material(&mut buf, "EXTRACTOR-dtls_srtp", None)
+ .expect("extract");
+
+ stream.read_exact(&mut [0]).unwrap();
+
+ let buf2 = guard.join().unwrap();
+
+ assert_eq!(buf[..], buf2[..]);
+}
+
+/// Tests that when the `SslStream` is created as a server stream, the protocols
+/// are correctly advertised to the client.
+#[test]
+#[cfg(any(ossl102, libressl261))]
+fn test_alpn_server_advertise_multiple() {
+ let mut server = Server::builder();
+ server.ctx().set_alpn_select_callback(|_, client| {
+ ssl::select_next_proto(b"\x08http/1.1\x08spdy/3.1", client).ok_or(ssl::AlpnError::NOACK)
+ });
+ let server = server.build();
+
+ let mut client = server.client();
+ client.ctx().set_alpn_protos(b"\x08spdy/3.1").unwrap();
+ let s = client.connect();
+ assert_eq!(s.ssl().selected_alpn_protocol(), Some(&b"spdy/3.1"[..]));
+}
+
+#[test]
+#[cfg(any(ossl110))]
+fn test_alpn_server_select_none_fatal() {
+ let mut server = Server::builder();
+ server.ctx().set_alpn_select_callback(|_, client| {
+ ssl::select_next_proto(b"\x08http/1.1\x08spdy/3.1", client)
+ .ok_or(ssl::AlpnError::ALERT_FATAL)
+ });
+ server.should_error();
+ let server = server.build();
+
+ let mut client = server.client();
+ client.ctx().set_alpn_protos(b"\x06http/2").unwrap();
+ client.connect_err();
+}
+
+#[test]
+#[cfg(any(ossl102, libressl261))]
+fn test_alpn_server_select_none() {
+ static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
+ let mut server = Server::builder();
+ server.ctx().set_alpn_select_callback(|_, client| {
+ CALLED_BACK.store(true, Ordering::SeqCst);
+ ssl::select_next_proto(b"\x08http/1.1\x08spdy/3.1", client).ok_or(ssl::AlpnError::NOACK)
+ });
+ let server = server.build();
+
+ let mut client = server.client();
+ client.ctx().set_alpn_protos(b"\x06http/2").unwrap();
+ let s = client.connect();
+ assert_eq!(None, s.ssl().selected_alpn_protocol());
+ assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+#[test]
+#[cfg(any(ossl102, libressl261))]
+fn test_alpn_server_unilateral() {
+ let server = Server::builder().build();
+
+ let mut client = server.client();
+ client.ctx().set_alpn_protos(b"\x06http/2").unwrap();
+ let s = client.connect();
+ assert_eq!(None, s.ssl().selected_alpn_protocol());
+}
+
+#[test]
+#[should_panic(expected = "blammo")]
+fn write_panic() {
+ struct ExplodingStream(TcpStream);
+
+ impl Read for ExplodingStream {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.read(buf)
+ }
+ }
+
+ impl Write for ExplodingStream {
+ fn write(&mut self, _: &[u8]) -> io::Result<usize> {
+ panic!("blammo");
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.0.flush()
+ }
+ }
+
+ let mut server = Server::builder();
+ server.should_error();
+ let server = server.build();
+
+ let stream = ExplodingStream(server.connect_tcp());
+
+ let ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ let _ = Ssl::new(&ctx.build()).unwrap().connect(stream);
+}
+
+#[test]
+#[should_panic(expected = "blammo")]
+fn read_panic() {
+ struct ExplodingStream(TcpStream);
+
+ impl Read for ExplodingStream {
+ fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
+ panic!("blammo");
+ }
+ }
+
+ impl Write for ExplodingStream {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.0.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.0.flush()
+ }
+ }
+
+ let mut server = Server::builder();
+ server.should_error();
+ let server = server.build();
+
+ let stream = ExplodingStream(server.connect_tcp());
+
+ let ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ let _ = Ssl::new(&ctx.build()).unwrap().connect(stream);
+}
+
+#[test]
+#[cfg_attr(all(libressl321, not(libressl340)), ignore)]
+#[should_panic(expected = "blammo")]
+fn flush_panic() {
+ struct ExplodingStream(TcpStream);
+
+ impl Read for ExplodingStream {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.read(buf)
+ }
+ }
+
+ impl Write for ExplodingStream {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.0.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ panic!("blammo");
+ }
+ }
+
+ let mut server = Server::builder();
+ server.should_error();
+ let server = server.build();
+
+ let stream = ExplodingStream(server.connect_tcp());
+
+ let ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ let _ = Ssl::new(&ctx.build()).unwrap().connect(stream);
+}
+
+#[test]
+fn refcount_ssl_context() {
+ let mut ssl = {
+ let ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ssl::Ssl::new(&ctx.build()).unwrap()
+ };
+
+ {
+ let new_ctx_a = SslContext::builder(SslMethod::tls()).unwrap().build();
+ ssl.set_ssl_context(&new_ctx_a).unwrap();
+ }
+}
+
+#[test]
+#[cfg_attr(libressl250, ignore)]
+#[cfg_attr(target_os = "windows", ignore)]
+#[cfg_attr(all(target_os = "macos", feature = "vendored"), ignore)]
+fn default_verify_paths() {
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ctx.set_default_verify_paths().unwrap();
+ ctx.set_verify(SslVerifyMode::PEER);
+ let ctx = ctx.build();
+ let s = match TcpStream::connect("google.com:443") {
+ Ok(s) => s,
+ Err(_) => return,
+ };
+ let mut ssl = Ssl::new(&ctx).unwrap();
+ ssl.set_hostname("google.com").unwrap();
+ let mut socket = ssl.connect(s).unwrap();
+
+ socket.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap();
+ let mut result = vec![];
+ socket.read_to_end(&mut result).unwrap();
+
+ println!("{}", String::from_utf8_lossy(&result));
+ assert!(result.starts_with(b"HTTP/1.0"));
+ assert!(result.ends_with(b"</HTML>\r\n") || result.ends_with(b"</html>"));
+}
+
+#[test]
+fn add_extra_chain_cert() {
+ let cert = X509::from_pem(CERT).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ctx.add_extra_chain_cert(cert).unwrap();
+}
+
+#[test]
+#[cfg(ossl102)]
+fn verify_valid_hostname() {
+ let server = Server::builder().build();
+
+ let mut client = server.client();
+ client.ctx().set_ca_file("test/root-ca.pem").unwrap();
+ client.ctx().set_verify(SslVerifyMode::PEER);
+
+ let mut client = client.build().builder();
+ client
+ .ssl()
+ .param_mut()
+ .set_hostflags(X509CheckFlags::NO_PARTIAL_WILDCARDS);
+ client.ssl().param_mut().set_host("foobar.com").unwrap();
+ client.connect();
+}
+
+#[test]
+#[cfg(ossl102)]
+fn verify_invalid_hostname() {
+ let mut server = Server::builder();
+ server.should_error();
+ let server = server.build();
+
+ let mut client = server.client();
+ client.ctx().set_ca_file("test/root-ca.pem").unwrap();
+ client.ctx().set_verify(SslVerifyMode::PEER);
+
+ let mut client = client.build().builder();
+ client
+ .ssl()
+ .param_mut()
+ .set_hostflags(X509CheckFlags::NO_PARTIAL_WILDCARDS);
+ client.ssl().param_mut().set_host("bogus.com").unwrap();
+ client.connect_err();
+}
+
+#[test]
+fn connector_valid_hostname() {
+ let server = Server::builder().build();
+
+ let mut connector = SslConnector::builder(SslMethod::tls()).unwrap();
+ connector.set_ca_file("test/root-ca.pem").unwrap();
+
+ let s = server.connect_tcp();
+ let mut s = connector.build().connect("foobar.com", s).unwrap();
+ s.read_exact(&mut [0]).unwrap();
+}
+
+#[test]
+fn connector_invalid_hostname() {
+ let mut server = Server::builder();
+ server.should_error();
+ let server = server.build();
+
+ let mut connector = SslConnector::builder(SslMethod::tls()).unwrap();
+ connector.set_ca_file("test/root-ca.pem").unwrap();
+
+ let s = server.connect_tcp();
+ connector.build().connect("bogus.com", s).unwrap_err();
+}
+
+#[test]
+fn connector_invalid_no_hostname_verification() {
+ let server = Server::builder().build();
+
+ let mut connector = SslConnector::builder(SslMethod::tls()).unwrap();
+ connector.set_ca_file("test/root-ca.pem").unwrap();
+
+ let s = server.connect_tcp();
+ let mut s = connector
+ .build()
+ .configure()
+ .unwrap()
+ .verify_hostname(false)
+ .connect("bogus.com", s)
+ .unwrap();
+ s.read_exact(&mut [0]).unwrap();
+}
+
+#[test]
+fn connector_no_hostname_still_verifies() {
+ let mut server = Server::builder();
+ server.should_error();
+ let server = server.build();
+
+ let connector = SslConnector::builder(SslMethod::tls()).unwrap().build();
+
+ let s = server.connect_tcp();
+ assert!(connector
+ .configure()
+ .unwrap()
+ .verify_hostname(false)
+ .connect("fizzbuzz.com", s)
+ .is_err());
+}
+
+#[test]
+fn connector_can_disable_verify() {
+ let server = Server::builder().build();
+
+ let mut connector = SslConnector::builder(SslMethod::tls()).unwrap();
+ connector.set_verify(SslVerifyMode::NONE);
+ let connector = connector.build();
+
+ let s = server.connect_tcp();
+ let mut s = connector
+ .configure()
+ .unwrap()
+ .connect("fizzbuzz.com", s)
+ .unwrap();
+ s.read_exact(&mut [0]).unwrap();
+}
+
+#[test]
+fn connector_does_use_sni_with_dnsnames() {
+ static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
+ let mut builder = Server::builder();
+ builder.ctx().set_servername_callback(|ssl, _| {
+ assert_eq!(ssl.servername(NameType::HOST_NAME), Some("foobar.com"));
+ CALLED_BACK.store(true, Ordering::SeqCst);
+ Ok(())
+ });
+ let server = builder.build();
+
+ let mut connector = SslConnector::builder(SslMethod::tls()).unwrap();
+ connector.set_ca_file("test/root-ca.pem").unwrap();
+
+ let s = server.connect_tcp();
+ let mut s = connector
+ .build()
+ .configure()
+ .unwrap()
+ .connect("foobar.com", s)
+ .unwrap();
+ s.read_exact(&mut [0]).unwrap();
+
+ assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+#[test]
+fn connector_doesnt_use_sni_with_ips() {
+ static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
+ let mut builder = Server::builder();
+ builder.ctx().set_servername_callback(|ssl, _| {
+ assert_eq!(ssl.servername(NameType::HOST_NAME), None);
+ CALLED_BACK.store(true, Ordering::SeqCst);
+ Ok(())
+ });
+ let server = builder.build();
+
+ let mut connector = SslConnector::builder(SslMethod::tls()).unwrap();
+ // The server's cert isn't issued for 127.0.0.1 but we don't care for this test.
+ connector.set_verify(SslVerifyMode::NONE);
+
+ let s = server.connect_tcp();
+ let mut s = connector
+ .build()
+ .configure()
+ .unwrap()
+ .connect("127.0.0.1", s)
+ .unwrap();
+ s.read_exact(&mut [0]).unwrap();
+
+ assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+fn test_mozilla_server(new: fn(SslMethod) -> Result<SslAcceptorBuilder, ErrorStack>) {
+ let listener = TcpListener::bind("127.0.0.1:0").unwrap();
+ let port = listener.local_addr().unwrap().port();
+
+ let t = thread::spawn(move || {
+ let key = PKey::private_key_from_pem(KEY).unwrap();
+ let cert = X509::from_pem(CERT).unwrap();
+ let mut acceptor = new(SslMethod::tls()).unwrap();
+ acceptor.set_private_key(&key).unwrap();
+ acceptor.set_certificate(&cert).unwrap();
+ let acceptor = acceptor.build();
+ let stream = listener.accept().unwrap().0;
+ let mut stream = acceptor.accept(stream).unwrap();
+
+ stream.write_all(b"hello").unwrap();
+ });
+
+ let mut connector = SslConnector::builder(SslMethod::tls()).unwrap();
+ connector.set_ca_file("test/root-ca.pem").unwrap();
+ let connector = connector.build();
+
+ let stream = TcpStream::connect(("127.0.0.1", port)).unwrap();
+ let mut stream = connector.connect("foobar.com", stream).unwrap();
+
+ let mut buf = [0; 5];
+ stream.read_exact(&mut buf).unwrap();
+ assert_eq!(b"hello", &buf);
+
+ t.join().unwrap();
+}
+
+#[test]
+fn connector_client_server_mozilla_intermediate() {
+ test_mozilla_server(SslAcceptor::mozilla_intermediate);
+}
+
+#[test]
+fn connector_client_server_mozilla_modern() {
+ test_mozilla_server(SslAcceptor::mozilla_modern);
+}
+
+#[test]
+fn connector_client_server_mozilla_intermediate_v5() {
+ test_mozilla_server(SslAcceptor::mozilla_intermediate_v5);
+}
+
+#[test]
+#[cfg(any(ossl111, libressl340))]
+fn connector_client_server_mozilla_modern_v5() {
+ test_mozilla_server(SslAcceptor::mozilla_modern_v5);
+}
+
+#[test]
+fn shutdown() {
+ let mut server = Server::builder();
+ server.io_cb(|mut s| {
+ assert_eq!(s.read(&mut [0]).unwrap(), 0);
+ assert_eq!(s.shutdown().unwrap(), ShutdownResult::Received);
+ });
+ let server = server.build();
+
+ let mut s = server.client().connect();
+
+ assert_eq!(s.get_shutdown(), ShutdownState::empty());
+ assert_eq!(s.shutdown().unwrap(), ShutdownResult::Sent);
+ assert_eq!(s.get_shutdown(), ShutdownState::SENT);
+ assert_eq!(s.shutdown().unwrap(), ShutdownResult::Received);
+ assert_eq!(
+ s.get_shutdown(),
+ ShutdownState::SENT | ShutdownState::RECEIVED
+ );
+}
+
+#[test]
+fn client_ca_list() {
+ let names = X509Name::load_client_ca_file("test/root-ca.pem").unwrap();
+ assert_eq!(names.len(), 1);
+
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ctx.set_client_ca_list(names);
+}
+
+#[test]
+fn cert_store() {
+ let server = Server::builder().build();
+
+ let mut client = server.client();
+ let cert = X509::from_pem(ROOT_CERT).unwrap();
+ client.ctx().cert_store_mut().add_cert(cert).unwrap();
+ client.ctx().set_verify(SslVerifyMode::PEER);
+
+ client.connect();
+}
+
+#[test]
+#[cfg_attr(any(all(libressl321, not(libressl340)), boringssl), ignore)]
+fn tmp_dh_callback() {
+ static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
+ let mut server = Server::builder();
+ server.ctx().set_tmp_dh_callback(|_, _, _| {
+ CALLED_BACK.store(true, Ordering::SeqCst);
+ let dh = include_bytes!("../../../test/dhparams.pem");
+ Dh::params_from_pem(dh)
+ });
+
+ let server = server.build();
+
+ let mut client = server.client();
+ // TLS 1.3 has no DH suites, so make sure we don't pick that version
+ #[cfg(any(ossl111, libressl340))]
+ client.ctx().set_options(super::SslOptions::NO_TLSV1_3);
+ client.ctx().set_cipher_list("EDH").unwrap();
+ client.connect();
+
+ assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+#[test]
+#[cfg(all(ossl101, not(ossl110)))]
+#[allow(deprecated)]
+fn tmp_ecdh_callback() {
+ use crate::ec::EcKey;
+ use crate::nid::Nid;
+
+ static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
+ let mut server = Server::builder();
+ server.ctx().set_tmp_ecdh_callback(|_, _, _| {
+ CALLED_BACK.store(true, Ordering::SeqCst);
+ EcKey::from_curve_name(Nid::X9_62_PRIME256V1)
+ });
+
+ let server = server.build();
+
+ let mut client = server.client();
+ client.ctx().set_cipher_list("ECDH").unwrap();
+ client.connect();
+
+ assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+#[test]
+#[cfg_attr(any(all(libressl321, not(libressl340)), boringssl), ignore)]
+fn tmp_dh_callback_ssl() {
+ static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
+ let mut server = Server::builder();
+ server.ssl_cb(|ssl| {
+ ssl.set_tmp_dh_callback(|_, _, _| {
+ CALLED_BACK.store(true, Ordering::SeqCst);
+ let dh = include_bytes!("../../../test/dhparams.pem");
+ Dh::params_from_pem(dh)
+ });
+ });
+
+ let server = server.build();
+
+ let mut client = server.client();
+ // TLS 1.3 has no DH suites, so make sure we don't pick that version
+ #[cfg(any(ossl111, libressl340))]
+ client.ctx().set_options(super::SslOptions::NO_TLSV1_3);
+ client.ctx().set_cipher_list("EDH").unwrap();
+ client.connect();
+
+ assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+#[test]
+#[cfg(all(ossl101, not(ossl110)))]
+#[allow(deprecated)]
+fn tmp_ecdh_callback_ssl() {
+ use crate::ec::EcKey;
+ use crate::nid::Nid;
+
+ static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
+ let mut server = Server::builder();
+ server.ssl_cb(|ssl| {
+ ssl.set_tmp_ecdh_callback(|_, _, _| {
+ CALLED_BACK.store(true, Ordering::SeqCst);
+ EcKey::from_curve_name(Nid::X9_62_PRIME256V1)
+ });
+ });
+
+ let server = server.build();
+
+ let mut client = server.client();
+ client.ctx().set_cipher_list("ECDH").unwrap();
+ client.connect();
+
+ assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+#[test]
+fn idle_session() {
+ let ctx = SslContext::builder(SslMethod::tls()).unwrap().build();
+ let ssl = Ssl::new(&ctx).unwrap();
+ assert!(ssl.session().is_none());
+}
+
+/// possible LibreSSL bug since 3.2.1
+#[test]
+#[cfg_attr(libressl321, ignore)]
+fn active_session() {
+ let server = Server::builder().build();
+
+ let s = server.client().connect();
+
+ let session = s.ssl().session().unwrap();
+ let len = session.master_key_len();
+ let mut buf = vec![0; len - 1];
+ let copied = session.master_key(&mut buf);
+ assert_eq!(copied, buf.len());
+ let mut buf = vec![0; len + 1];
+ let copied = session.master_key(&mut buf);
+ assert_eq!(copied, len);
+}
+
+#[test]
+#[cfg(not(boringssl))]
+fn status_callbacks() {
+ static CALLED_BACK_SERVER: AtomicBool = AtomicBool::new(false);
+ static CALLED_BACK_CLIENT: AtomicBool = AtomicBool::new(false);
+
+ let mut server = Server::builder();
+ server
+ .ctx()
+ .set_status_callback(|ssl| {
+ CALLED_BACK_SERVER.store(true, Ordering::SeqCst);
+ let response = OcspResponse::create(OcspResponseStatus::UNAUTHORIZED, None).unwrap();
+ let response = response.to_der().unwrap();
+ ssl.set_ocsp_status(&response).unwrap();
+ Ok(true)
+ })
+ .unwrap();
+
+ let server = server.build();
+
+ let mut client = server.client();
+ client
+ .ctx()
+ .set_status_callback(|ssl| {
+ CALLED_BACK_CLIENT.store(true, Ordering::SeqCst);
+ let response = OcspResponse::from_der(ssl.ocsp_status().unwrap()).unwrap();
+ assert_eq!(response.status(), OcspResponseStatus::UNAUTHORIZED);
+ Ok(true)
+ })
+ .unwrap();
+
+ let mut client = client.build().builder();
+ client.ssl().set_status_type(StatusType::OCSP).unwrap();
+
+ client.connect();
+
+ assert!(CALLED_BACK_SERVER.load(Ordering::SeqCst));
+ assert!(CALLED_BACK_CLIENT.load(Ordering::SeqCst));
+}
+
+/// possible LibreSSL bug since 3.2.1
+#[test]
+#[cfg_attr(libressl321, ignore)]
+fn new_session_callback() {
+ static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
+ let mut server = Server::builder();
+ server.ctx().set_session_id_context(b"foo").unwrap();
+
+ let server = server.build();
+
+ let mut client = server.client();
+
+ client
+ .ctx()
+ .set_session_cache_mode(SslSessionCacheMode::CLIENT | SslSessionCacheMode::NO_INTERNAL);
+ client
+ .ctx()
+ .set_new_session_callback(|_, _| CALLED_BACK.store(true, Ordering::SeqCst));
+
+ client.connect();
+
+ assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+/// possible LibreSSL bug since 3.2.1
+#[test]
+#[cfg_attr(libressl321, ignore)]
+fn new_session_callback_swapped_ctx() {
+ static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
+ let mut server = Server::builder();
+ server.ctx().set_session_id_context(b"foo").unwrap();
+
+ let server = server.build();
+
+ let mut client = server.client();
+
+ client
+ .ctx()
+ .set_session_cache_mode(SslSessionCacheMode::CLIENT | SslSessionCacheMode::NO_INTERNAL);
+ client
+ .ctx()
+ .set_new_session_callback(|_, _| CALLED_BACK.store(true, Ordering::SeqCst));
+
+ let mut client = client.build().builder();
+
+ let ctx = SslContextBuilder::new(SslMethod::tls()).unwrap().build();
+ client.ssl().set_ssl_context(&ctx).unwrap();
+
+ client.connect();
+
+ assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+#[test]
+fn keying_export() {
+ let listener = TcpListener::bind("127.0.0.1:0").unwrap();
+ let addr = listener.local_addr().unwrap();
+
+ let label = "EXPERIMENTAL test";
+ let context = b"my context";
+
+ let guard = thread::spawn(move || {
+ let stream = listener.accept().unwrap().0;
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ctx.set_certificate_file(Path::new("test/cert.pem"), SslFiletype::PEM)
+ .unwrap();
+ ctx.set_private_key_file(Path::new("test/key.pem"), SslFiletype::PEM)
+ .unwrap();
+ let ssl = Ssl::new(&ctx.build()).unwrap();
+ let mut stream = ssl.accept(stream).unwrap();
+
+ let mut buf = [0; 32];
+ stream
+ .ssl()
+ .export_keying_material(&mut buf, label, Some(context))
+ .unwrap();
+
+ stream.write_all(&[0]).unwrap();
+
+ buf
+ });
+
+ let stream = TcpStream::connect(addr).unwrap();
+ let ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ let ssl = Ssl::new(&ctx.build()).unwrap();
+ let mut stream = ssl.connect(stream).unwrap();
+
+ let mut buf = [1; 32];
+ stream
+ .ssl()
+ .export_keying_material(&mut buf, label, Some(context))
+ .unwrap();
+
+ stream.read_exact(&mut [0]).unwrap();
+
+ let buf2 = guard.join().unwrap();
+
+ assert_eq!(buf, buf2);
+}
+
+#[test]
+#[cfg(any(ossl110, libressl261))]
+fn no_version_overlap() {
+ let mut server = Server::builder();
+ server.ctx().set_min_proto_version(None).unwrap();
+ server
+ .ctx()
+ .set_max_proto_version(Some(SslVersion::TLS1_1))
+ .unwrap();
+ #[cfg(any(ossl110g, libressl270))]
+ assert_eq!(server.ctx().max_proto_version(), Some(SslVersion::TLS1_1));
+ server.should_error();
+ let server = server.build();
+
+ let mut client = server.client();
+ client
+ .ctx()
+ .set_min_proto_version(Some(SslVersion::TLS1_2))
+ .unwrap();
+ #[cfg(ossl110g)]
+ assert_eq!(client.ctx().min_proto_version(), Some(SslVersion::TLS1_2));
+ client.ctx().set_max_proto_version(None).unwrap();
+
+ client.connect_err();
+}
+
+#[test]
+#[cfg(ossl111)]
+fn custom_extensions() {
+ static FOUND_EXTENSION: AtomicBool = AtomicBool::new(false);
+
+ let mut server = Server::builder();
+ server
+ .ctx()
+ .add_custom_ext(
+ 12345,
+ ExtensionContext::CLIENT_HELLO,
+ |_, _, _| -> Result<Option<&'static [u8]>, _> { unreachable!() },
+ |_, _, data, _| {
+ FOUND_EXTENSION.store(data == b"hello", Ordering::SeqCst);
+ Ok(())
+ },
+ )
+ .unwrap();
+
+ let server = server.build();
+
+ let mut client = server.client();
+ client
+ .ctx()
+ .add_custom_ext(
+ 12345,
+ ssl::ExtensionContext::CLIENT_HELLO,
+ |_, _, _| Ok(Some(b"hello")),
+ |_, _, _, _| unreachable!(),
+ )
+ .unwrap();
+
+ client.connect();
+
+ assert!(FOUND_EXTENSION.load(Ordering::SeqCst));
+}
+
+fn _check_kinds() {
+ fn is_send<T: Send>() {}
+ fn is_sync<T: Sync>() {}
+
+ is_send::<SslStream<TcpStream>>();
+ is_sync::<SslStream<TcpStream>>();
+}
+
+#[test]
+#[cfg(ossl111)]
+fn stateless() {
+ use super::SslOptions;
+
+ #[derive(Debug)]
+ struct MemoryStream {
+ incoming: io::Cursor<Vec<u8>>,
+ outgoing: Vec<u8>,
+ }
+
+ impl MemoryStream {
+ pub fn new() -> Self {
+ Self {
+ incoming: io::Cursor::new(Vec::new()),
+ outgoing: Vec::new(),
+ }
+ }
+
+ pub fn extend_incoming(&mut self, data: &[u8]) {
+ self.incoming.get_mut().extend_from_slice(data);
+ }
+
+ pub fn take_outgoing(&mut self) -> Outgoing<'_> {
+ Outgoing(&mut self.outgoing)
+ }
+ }
+
+ impl Read for MemoryStream {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ let n = self.incoming.read(buf)?;
+ if self.incoming.position() == self.incoming.get_ref().len() as u64 {
+ self.incoming.set_position(0);
+ self.incoming.get_mut().clear();
+ }
+ if n == 0 {
+ return Err(io::Error::new(
+ io::ErrorKind::WouldBlock,
+ "no data available",
+ ));
+ }
+ Ok(n)
+ }
+ }
+
+ impl Write for MemoryStream {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.outgoing.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+ }
+
+ pub struct Outgoing<'a>(&'a mut Vec<u8>);
+
+ impl<'a> Drop for Outgoing<'a> {
+ fn drop(&mut self) {
+ self.0.clear();
+ }
+ }
+
+ impl<'a> ::std::ops::Deref for Outgoing<'a> {
+ type Target = [u8];
+ fn deref(&self) -> &[u8] {
+ self.0
+ }
+ }
+
+ impl<'a> AsRef<[u8]> for Outgoing<'a> {
+ fn as_ref(&self) -> &[u8] {
+ self.0
+ }
+ }
+
+ fn send(from: &mut MemoryStream, to: &mut MemoryStream) {
+ to.extend_incoming(&from.take_outgoing());
+ }
+
+ //
+ // Setup
+ //
+
+ let mut client_ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ client_ctx.clear_options(SslOptions::ENABLE_MIDDLEBOX_COMPAT);
+ let mut client_stream =
+ SslStream::new(Ssl::new(&client_ctx.build()).unwrap(), MemoryStream::new()).unwrap();
+
+ let mut server_ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ server_ctx
+ .set_certificate_file(Path::new("test/cert.pem"), SslFiletype::PEM)
+ .unwrap();
+ server_ctx
+ .set_private_key_file(Path::new("test/key.pem"), SslFiletype::PEM)
+ .unwrap();
+ const COOKIE: &[u8] = b"chocolate chip";
+ server_ctx.set_stateless_cookie_generate_cb(|_tls, buf| {
+ buf[0..COOKIE.len()].copy_from_slice(COOKIE);
+ Ok(COOKIE.len())
+ });
+ server_ctx.set_stateless_cookie_verify_cb(|_tls, buf| buf == COOKIE);
+ let mut server_stream =
+ SslStream::new(Ssl::new(&server_ctx.build()).unwrap(), MemoryStream::new()).unwrap();
+
+ //
+ // Handshake
+ //
+
+ // Initial ClientHello
+ client_stream.connect().unwrap_err();
+ send(client_stream.get_mut(), server_stream.get_mut());
+ // HelloRetryRequest
+ assert!(!server_stream.stateless().unwrap());
+ send(server_stream.get_mut(), client_stream.get_mut());
+ // Second ClientHello
+ client_stream.do_handshake().unwrap_err();
+ send(client_stream.get_mut(), server_stream.get_mut());
+ // OldServerHello
+ assert!(server_stream.stateless().unwrap());
+ server_stream.accept().unwrap_err();
+ send(server_stream.get_mut(), client_stream.get_mut());
+ // Finished
+ client_stream.do_handshake().unwrap();
+ send(client_stream.get_mut(), server_stream.get_mut());
+ server_stream.do_handshake().unwrap();
+}
+
+#[cfg(not(osslconf = "OPENSSL_NO_PSK"))]
+#[test]
+fn psk_ciphers() {
+ const CIPHER: &str = "PSK-AES256-CBC-SHA";
+ const PSK: &[u8] = b"thisisaverysecurekey";
+ const CLIENT_IDENT: &[u8] = b"thisisaclient";
+ static CLIENT_CALLED: AtomicBool = AtomicBool::new(false);
+ static SERVER_CALLED: AtomicBool = AtomicBool::new(false);
+
+ let mut server = Server::builder();
+ server.ctx().set_cipher_list(CIPHER).unwrap();
+ server.ctx().set_psk_server_callback(|_, identity, psk| {
+ assert!(identity.unwrap_or(&[]) == CLIENT_IDENT);
+ psk[..PSK.len()].copy_from_slice(PSK);
+ SERVER_CALLED.store(true, Ordering::SeqCst);
+ Ok(PSK.len())
+ });
+
+ let server = server.build();
+
+ let mut client = server.client();
+ // This test relies on TLS 1.2 suites
+ #[cfg(any(boringssl, ossl111))]
+ client.ctx().set_options(super::SslOptions::NO_TLSV1_3);
+ client.ctx().set_cipher_list(CIPHER).unwrap();
+ client
+ .ctx()
+ .set_psk_client_callback(move |_, _, identity, psk| {
+ identity[..CLIENT_IDENT.len()].copy_from_slice(CLIENT_IDENT);
+ identity[CLIENT_IDENT.len()] = 0;
+ psk[..PSK.len()].copy_from_slice(PSK);
+ CLIENT_CALLED.store(true, Ordering::SeqCst);
+ Ok(PSK.len())
+ });
+
+ client.connect();
+
+ assert!(SERVER_CALLED.load(Ordering::SeqCst));
+ assert!(CLIENT_CALLED.load(Ordering::SeqCst));
+}
+
+#[test]
+fn sni_callback_swapped_ctx() {
+ static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
+ let mut server = Server::builder();
+
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ctx.set_servername_callback(|_, _| {
+ CALLED_BACK.store(true, Ordering::SeqCst);
+ Ok(())
+ });
+
+ let keyed_ctx = mem::replace(server.ctx(), ctx).build();
+ server.ssl_cb(move |ssl| ssl.set_ssl_context(&keyed_ctx).unwrap());
+
+ let server = server.build();
+
+ server.client().connect();
+
+ assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+#[test]
+#[cfg(ossl111)]
+fn client_hello() {
+ static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
+ let mut server = Server::builder();
+ server.ctx().set_client_hello_callback(|ssl, _| {
+ assert!(!ssl.client_hello_isv2());
+ assert_eq!(ssl.client_hello_legacy_version(), Some(SslVersion::TLS1_2));
+ assert!(ssl.client_hello_random().is_some());
+ assert!(ssl.client_hello_session_id().is_some());
+ assert!(ssl.client_hello_ciphers().is_some());
+ assert!(ssl.client_hello_compression_methods().is_some());
+
+ CALLED_BACK.store(true, Ordering::SeqCst);
+ Ok(ClientHelloResponse::SUCCESS)
+ });
+
+ let server = server.build();
+ server.client().connect();
+
+ assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+#[test]
+#[cfg(ossl111)]
+fn openssl_cipher_name() {
+ assert_eq!(
+ super::cipher_name("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"),
+ "ECDHE-RSA-AES256-SHA384",
+ );
+
+ assert_eq!(super::cipher_name("asdf"), "(NONE)");
+}
+
+#[test]
+fn session_cache_size() {
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ctx.set_session_cache_size(1234);
+ let ctx = ctx.build();
+ assert_eq!(ctx.session_cache_size(), 1234);
+}
+
+#[test]
+#[cfg(ossl102)]
+fn add_chain_cert() {
+ let ctx = SslContext::builder(SslMethod::tls()).unwrap().build();
+ let cert = X509::from_pem(CERT).unwrap();
+ let mut ssl = Ssl::new(&ctx).unwrap();
+ assert!(ssl.add_chain_cert(cert).is_ok());
+}
+#[test]
+#[cfg(ossl111)]
+fn set_ssl_certificate_key_related_api() {
+ let cert_str: &str = include_str!("../../../test/cert.pem");
+ let key_str: &str = include_str!("../../../test/key.pem");
+ let ctx = SslContext::builder(SslMethod::tls()).unwrap().build();
+ let cert_x509 = X509::from_pem(CERT).unwrap();
+ let mut ssl = Ssl::new(&ctx).unwrap();
+ assert!(ssl.set_method(SslMethod::tls()).is_ok());
+ ssl.set_private_key_file("test/key.pem", SslFiletype::PEM)
+ .unwrap();
+ {
+ let pkey = String::from_utf8(
+ ssl.private_key()
+ .unwrap()
+ .private_key_to_pem_pkcs8()
+ .unwrap(),
+ )
+ .unwrap();
+ assert!(pkey.lines().eq(key_str.lines()));
+ }
+ let pkey = PKey::private_key_from_pem(KEY).unwrap();
+ ssl.set_private_key(pkey.as_ref()).unwrap();
+ {
+ let pkey = String::from_utf8(
+ ssl.private_key()
+ .unwrap()
+ .private_key_to_pem_pkcs8()
+ .unwrap(),
+ )
+ .unwrap();
+ assert!(pkey.lines().eq(key_str.lines()));
+ }
+ ssl.set_certificate(cert_x509.as_ref()).unwrap();
+ let cert = String::from_utf8(ssl.certificate().unwrap().to_pem().unwrap()).unwrap();
+ assert!(cert.lines().eq(cert_str.lines()));
+ ssl.add_client_ca(cert_x509.as_ref()).unwrap();
+ ssl.set_min_proto_version(Some(SslVersion::TLS1_2)).unwrap();
+ ssl.set_max_proto_version(Some(SslVersion::TLS1_3)).unwrap();
+ ssl.set_cipher_list("HIGH:!aNULL:!MD5").unwrap();
+ ssl.set_ciphersuites("TLS_AES_128_GCM_SHA256").unwrap();
+ let x509 = X509::from_pem(ROOT_CERT).unwrap();
+ let mut builder = X509StoreBuilder::new().unwrap();
+ builder.add_cert(x509).unwrap();
+ let store = builder.build();
+ ssl.set_verify_cert_store(store).unwrap();
+}
+
+#[test]
+#[cfg(ossl110)]
+fn test_ssl_set_cert_chain_file() {
+ let ctx = SslContext::builder(SslMethod::tls()).unwrap().build();
+ let mut ssl = Ssl::new(&ctx).unwrap();
+ ssl.set_certificate_chain_file("test/cert.pem").unwrap();
+}
+
+#[test]
+#[cfg(ossl111)]
+fn set_num_tickets() {
+ let mut ctx = SslContext::builder(SslMethod::tls_server()).unwrap();
+ ctx.set_num_tickets(3).unwrap();
+ let ctx = ctx.build();
+ assert_eq!(3, ctx.num_tickets());
+
+ let mut ssl = Ssl::new(&ctx).unwrap();
+ ssl.set_num_tickets(5).unwrap();
+ let ssl = ssl;
+ assert_eq!(5, ssl.num_tickets());
+}
diff --git a/vendor/openssl/src/ssl/test/server.rs b/vendor/openssl/src/ssl/test/server.rs
new file mode 100644
index 000000000..41677e576
--- /dev/null
+++ b/vendor/openssl/src/ssl/test/server.rs
@@ -0,0 +1,167 @@
+use std::io::{Read, Write};
+use std::net::{SocketAddr, TcpListener, TcpStream};
+use std::thread::{self, JoinHandle};
+
+use crate::ssl::{Ssl, SslContext, SslContextBuilder, SslFiletype, SslMethod, SslRef, SslStream};
+
+pub struct Server {
+ handle: Option<JoinHandle<()>>,
+ addr: SocketAddr,
+}
+
+impl Drop for Server {
+ fn drop(&mut self) {
+ if !thread::panicking() {
+ self.handle.take().unwrap().join().unwrap();
+ }
+ }
+}
+
+impl Server {
+ pub fn builder() -> Builder {
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ctx.set_certificate_chain_file("test/cert.pem").unwrap();
+ ctx.set_private_key_file("test/key.pem", SslFiletype::PEM)
+ .unwrap();
+
+ Builder {
+ ctx,
+ ssl_cb: Box::new(|_| {}),
+ io_cb: Box::new(|_| {}),
+ should_error: false,
+ }
+ }
+
+ pub fn client(&self) -> ClientBuilder {
+ ClientBuilder {
+ ctx: SslContext::builder(SslMethod::tls()).unwrap(),
+ addr: self.addr,
+ }
+ }
+
+ pub fn connect_tcp(&self) -> TcpStream {
+ TcpStream::connect(self.addr).unwrap()
+ }
+}
+
+pub struct Builder {
+ ctx: SslContextBuilder,
+ ssl_cb: Box<dyn FnMut(&mut SslRef) + Send>,
+ io_cb: Box<dyn FnMut(SslStream<TcpStream>) + Send>,
+ should_error: bool,
+}
+
+impl Builder {
+ pub fn ctx(&mut self) -> &mut SslContextBuilder {
+ &mut self.ctx
+ }
+
+ pub fn ssl_cb<F>(&mut self, cb: F)
+ where
+ F: 'static + FnMut(&mut SslRef) + Send,
+ {
+ self.ssl_cb = Box::new(cb);
+ }
+
+ pub fn io_cb<F>(&mut self, cb: F)
+ where
+ F: 'static + FnMut(SslStream<TcpStream>) + Send,
+ {
+ self.io_cb = Box::new(cb);
+ }
+
+ pub fn should_error(&mut self) {
+ self.should_error = true;
+ }
+
+ pub fn build(self) -> Server {
+ let ctx = self.ctx.build();
+ let socket = TcpListener::bind("127.0.0.1:0").unwrap();
+ let addr = socket.local_addr().unwrap();
+ let mut ssl_cb = self.ssl_cb;
+ let mut io_cb = self.io_cb;
+ let should_error = self.should_error;
+
+ let handle = thread::spawn(move || {
+ let socket = socket.accept().unwrap().0;
+ let mut ssl = Ssl::new(&ctx).unwrap();
+ ssl_cb(&mut ssl);
+ let r = ssl.accept(socket);
+ if should_error {
+ r.unwrap_err();
+ } else {
+ let mut socket = r.unwrap();
+ socket.write_all(&[0]).unwrap();
+ io_cb(socket);
+ }
+ });
+
+ Server {
+ handle: Some(handle),
+ addr,
+ }
+ }
+}
+
+pub struct ClientBuilder {
+ ctx: SslContextBuilder,
+ addr: SocketAddr,
+}
+
+impl ClientBuilder {
+ pub fn ctx(&mut self) -> &mut SslContextBuilder {
+ &mut self.ctx
+ }
+
+ pub fn build(self) -> Client {
+ Client {
+ ctx: self.ctx.build(),
+ addr: self.addr,
+ }
+ }
+
+ pub fn connect(self) -> SslStream<TcpStream> {
+ self.build().builder().connect()
+ }
+
+ pub fn connect_err(self) {
+ self.build().builder().connect_err();
+ }
+}
+
+pub struct Client {
+ ctx: SslContext,
+ addr: SocketAddr,
+}
+
+impl Client {
+ pub fn builder(&self) -> ClientSslBuilder {
+ ClientSslBuilder {
+ ssl: Ssl::new(&self.ctx).unwrap(),
+ addr: self.addr,
+ }
+ }
+}
+
+pub struct ClientSslBuilder {
+ ssl: Ssl,
+ addr: SocketAddr,
+}
+
+impl ClientSslBuilder {
+ pub fn ssl(&mut self) -> &mut SslRef {
+ &mut self.ssl
+ }
+
+ pub fn connect(self) -> SslStream<TcpStream> {
+ let socket = TcpStream::connect(self.addr).unwrap();
+ let mut s = self.ssl.connect(socket).unwrap();
+ s.read_exact(&mut [0]).unwrap();
+ s
+ }
+
+ pub fn connect_err(self) {
+ let socket = TcpStream::connect(self.addr).unwrap();
+ self.ssl.connect(socket).unwrap_err();
+ }
+}