aboutsummaryrefslogtreecommitdiff
path: root/openssl/src/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'openssl/src/ssl')
-rw-r--r--openssl/src/ssl/bio.rs100
-rw-r--r--openssl/src/ssl/mod.rs185
-rw-r--r--openssl/src/ssl/tests/mod.rs58
3 files changed, 221 insertions, 122 deletions
diff --git a/openssl/src/ssl/bio.rs b/openssl/src/ssl/bio.rs
index aa445562..e53545d7 100644
--- a/openssl/src/ssl/bio.rs
+++ b/openssl/src/ssl/bio.rs
@@ -1,5 +1,5 @@
use libc::{c_char, c_int, c_long, c_void, strlen};
-use ffi::{BIO, BIO_METHOD, BIO_CTRL_FLUSH, BIO_TYPE_NONE, BIO_new};
+use ffi::{self, BIO, BIO_CTRL_FLUSH, BIO_TYPE_NONE, BIO_new};
use ffi_extras::{BIO_clear_retry_flags, BIO_set_retry_read, BIO_set_retry_write};
use std::any::Any;
use std::io;
@@ -11,28 +11,36 @@ use std::sync::Arc;
use ssl::error::SslError;
-// "rust"
-const NAME: [c_char; 5] = [114, 117, 115, 116, 0];
-
pub struct StreamState<S> {
pub stream: S,
pub error: Option<io::Error>,
pub panic: Option<Box<Any + Send>>,
}
-pub fn new<S: Read + Write>(stream: S) -> Result<(*mut BIO, Arc<BIO_METHOD>), SslError> {
- let method = Arc::new(BIO_METHOD {
- type_: BIO_TYPE_NONE,
- name: &NAME[0],
- 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,
- });
+/// Safe wrapper for BIO_METHOD
+pub struct BioMethod(ffi::BIO_METHOD);
+
+impl BioMethod {
+ pub fn new<S: Read + Write>() -> BioMethod {
+ BioMethod(ffi::BIO_METHOD {
+ type_: 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,
+ })
+ }
+}
+
+unsafe impl Send for BioMethod {}
+
+pub fn new<S: Read + Write>(stream: S) -> Result<(*mut BIO, Arc<BioMethod>), SslError> {
+ let method = Arc::new(BioMethod::new::<S>());
let state = Box::new(StreamState {
stream: stream,
@@ -41,7 +49,7 @@ pub fn new<S: Read + Write>(stream: S) -> Result<(*mut BIO, Arc<BIO_METHOD>), Ss
});
unsafe {
- let bio = try_ssl_null!(BIO_new(&*method));
+ let bio = try_ssl_null!(BIO_new(&method.0));
(*bio).ptr = Box::into_raw(state) as *mut _;
(*bio).init = 1;
@@ -74,56 +82,22 @@ unsafe fn state<'a, S: 'a>(bio: *mut BIO) -> &'a mut StreamState<S> {
}
#[cfg(feature = "nightly")]
-fn recover<F, T>(f: F) -> Result<T, Box<Any + Send>> where F: FnOnce() -> T + ::std::panic::RecoverSafe {
- ::std::panic::recover(f)
+fn catch_unwind<F, T>(f: F) -> Result<T, Box<Any + Send>> where F: FnOnce() -> T {
+ ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(f))
}
#[cfg(not(feature = "nightly"))]
-fn recover<F, T>(f: F) -> Result<T, Box<Any + Send>> where F: FnOnce() -> T {
+fn catch_unwind<F, T>(f: F) -> Result<T, Box<Any + Send>> where F: FnOnce() -> T {
Ok(f())
}
-#[cfg(feature = "nightly")]
-use std::panic::AssertRecoverSafe;
-
-#[cfg(not(feature = "nightly"))]
-struct AssertRecoverSafe<T>(T);
-
-#[cfg(not(feature = "nightly"))]
-impl<T> AssertRecoverSafe<T> {
- fn new(t: T) -> Self {
- AssertRecoverSafe(t)
- }
-}
-
-#[cfg(not(feature = "nightly"))]
-impl<T> ::std::ops::Deref for AssertRecoverSafe<T> {
- type Target = T;
-
- fn deref(&self) -> &T {
- &self.0
- }
-}
-
-#[cfg(not(feature = "nightly"))]
-impl<T> ::std::ops::DerefMut for AssertRecoverSafe<T> {
- fn deref_mut(&mut self) -> &mut T {
- &mut self.0
- }
-}
-
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);
- let result = {
- let mut youre_not_my_supervisor = AssertRecoverSafe::new(&mut *state);
- recover(move || youre_not_my_supervisor.stream.write(buf))
- };
-
- match result {
+ match catch_unwind(|| state.stream.write(buf)) {
Ok(Ok(len)) => len as c_int,
Ok(Err(err)) => {
if retriable_error(&err) {
@@ -145,13 +119,7 @@ unsafe extern "C" fn bread<S: Read>(bio: *mut BIO, buf: *mut c_char, len: c_int)
let state = state::<S>(bio);
let buf = slice::from_raw_parts_mut(buf as *mut _, len as usize);
- let result = {
- let mut youre_not_my_supervisor = AssertRecoverSafe::new(&mut *state);
- let mut fuuuu = AssertRecoverSafe::new(buf);
- recover(move || youre_not_my_supervisor.stream.read(&mut *fuuuu))
- };
-
- match result {
+ match catch_unwind(|| state.stream.read(buf)) {
Ok(Ok(len)) => len as c_int,
Ok(Err(err)) => {
if retriable_error(&err) {
@@ -185,12 +153,8 @@ unsafe extern "C" fn ctrl<S: Write>(bio: *mut BIO,
-> c_long {
if cmd == BIO_CTRL_FLUSH {
let state = state::<S>(bio);
- let result = {
- let mut youre_not_my_supervisor = AssertRecoverSafe::new(&mut *state);
- recover(move || youre_not_my_supervisor.stream.flush())
- };
- match result {
+ match catch_unwind(|| state.stream.flush()) {
Ok(Ok(())) => 1,
Ok(Err(err)) => {
state.error = Some(err);
diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs
index 574a324b..aa785142 100644
--- a/openssl/src/ssl/mod.rs
+++ b/openssl/src/ssl/mod.rs
@@ -35,6 +35,8 @@ mod bio;
#[cfg(test)]
mod tests;
+use self::bio::BioMethod;
+
#[doc(inline)]
pub use ssl::error::Error;
@@ -67,44 +69,44 @@ pub fn init() {
}
bitflags! {
- flags SslContextOptions: u64 {
- const SSL_OP_MICROSOFT_SESS_ID_BUG = ffi_extras::SSL_OP_MICROSOFT_SESS_ID_BUG,
- const SSL_OP_NETSCAPE_CHALLENGE_BUG = ffi_extras::SSL_OP_NETSCAPE_CHALLENGE_BUG,
- const SSL_OP_LEGACY_SERVER_CONNECT = ffi_extras::SSL_OP_LEGACY_SERVER_CONNECT,
- const SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG = ffi_extras::SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG,
- const SSL_OP_TLSEXT_PADDING = ffi_extras::SSL_OP_TLSEXT_PADDING,
- const SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER = ffi_extras::SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER,
- const SSL_OP_SAFARI_ECDHE_ECDSA_BUG = ffi_extras::SSL_OP_SAFARI_ECDHE_ECDSA_BUG,
- const SSL_OP_SSLEAY_080_CLIENT_DH_BUG = ffi_extras::SSL_OP_SSLEAY_080_CLIENT_DH_BUG,
- const SSL_OP_TLS_D5_BUG = ffi_extras::SSL_OP_TLS_D5_BUG,
- const SSL_OP_TLS_BLOCK_PADDING_BUG = ffi_extras::SSL_OP_TLS_BLOCK_PADDING_BUG,
- const SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS = ffi_extras::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS,
- const SSL_OP_NO_QUERY_MTU = ffi_extras::SSL_OP_NO_QUERY_MTU,
- const SSL_OP_COOKIE_EXCHANGE = ffi_extras::SSL_OP_COOKIE_EXCHANGE,
- const SSL_OP_NO_TICKET = ffi_extras::SSL_OP_NO_TICKET,
- const SSL_OP_CISCO_ANYCONNECT = ffi_extras::SSL_OP_CISCO_ANYCONNECT,
- const SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = ffi_extras::SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION,
- const SSL_OP_NO_COMPRESSION = ffi_extras::SSL_OP_NO_COMPRESSION,
- const SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = ffi_extras::SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION,
- const SSL_OP_SINGLE_ECDH_USE = ffi_extras::SSL_OP_SINGLE_ECDH_USE,
- const SSL_OP_SINGLE_DH_USE = ffi_extras::SSL_OP_SINGLE_DH_USE,
- const SSL_OP_CIPHER_SERVER_PREFERENCE = ffi_extras::SSL_OP_CIPHER_SERVER_PREFERENCE,
- const SSL_OP_TLS_ROLLBACK_BUG = ffi_extras::SSL_OP_TLS_ROLLBACK_BUG,
- const SSL_OP_NO_SSLV2 = ffi_extras::SSL_OP_NO_SSLv2,
- const SSL_OP_NO_SSLV3 = ffi_extras::SSL_OP_NO_SSLv3,
- const SSL_OP_NO_DTLSV1 = ffi_extras::SSL_OP_NO_DTLSv1,
- const SSL_OP_NO_TLSV1 = ffi_extras::SSL_OP_NO_TLSv1,
- const SSL_OP_NO_DTLSV1_2 = ffi_extras::SSL_OP_NO_DTLSv1_2,
- const SSL_OP_NO_TLSV1_2 = ffi_extras::SSL_OP_NO_TLSv1_2,
- const SSL_OP_NO_TLSV1_1 = ffi_extras::SSL_OP_NO_TLSv1_1,
- const SSL_OP_NETSCAPE_CA_DN_BUG = ffi_extras::SSL_OP_NETSCAPE_CA_DN_BUG,
- const SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG = ffi_extras::SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG,
- const SSL_OP_CRYPTOPRO_TLSEXT_BUG = ffi_extras::SSL_OP_CRYPTOPRO_TLSEXT_BUG,
- const SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG = ffi_extras::SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG,
- const SSL_OP_MSIE_SSLV2_RSA_PADDING = ffi_extras::SSL_OP_MSIE_SSLV2_RSA_PADDING,
- const SSL_OP_PKCS1_CHECK_1 = ffi_extras::SSL_OP_PKCS1_CHECK_1,
- const SSL_OP_PKCS1_CHECK_2 = ffi_extras::SSL_OP_PKCS1_CHECK_2,
- const SSL_OP_EPHEMERAL_RSA = ffi_extras::SSL_OP_EPHEMERAL_RSA,
+ pub flags SslContextOptions: u64 {
+ const SSL_OP_MICROSOFT_SESS_ID_BUG = ::ffi_extras::SSL_OP_MICROSOFT_SESS_ID_BUG,
+ const SSL_OP_NETSCAPE_CHALLENGE_BUG = ::ffi_extras::SSL_OP_NETSCAPE_CHALLENGE_BUG,
+ const SSL_OP_LEGACY_SERVER_CONNECT = ::ffi_extras::SSL_OP_LEGACY_SERVER_CONNECT,
+ const SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG = ::ffi_extras::SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG,
+ const SSL_OP_TLSEXT_PADDING = ::ffi_extras::SSL_OP_TLSEXT_PADDING,
+ const SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER = ::ffi_extras::SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER,
+ const SSL_OP_SAFARI_ECDHE_ECDSA_BUG = ::ffi_extras::SSL_OP_SAFARI_ECDHE_ECDSA_BUG,
+ const SSL_OP_SSLEAY_080_CLIENT_DH_BUG = ::ffi_extras::SSL_OP_SSLEAY_080_CLIENT_DH_BUG,
+ const SSL_OP_TLS_D5_BUG = ::ffi_extras::SSL_OP_TLS_D5_BUG,
+ const SSL_OP_TLS_BLOCK_PADDING_BUG = ::ffi_extras::SSL_OP_TLS_BLOCK_PADDING_BUG,
+ const SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS = ::ffi_extras::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS,
+ const SSL_OP_NO_QUERY_MTU = ::ffi_extras::SSL_OP_NO_QUERY_MTU,
+ const SSL_OP_COOKIE_EXCHANGE = ::ffi_extras::SSL_OP_COOKIE_EXCHANGE,
+ const SSL_OP_NO_TICKET = ::ffi_extras::SSL_OP_NO_TICKET,
+ const SSL_OP_CISCO_ANYCONNECT = ::ffi_extras::SSL_OP_CISCO_ANYCONNECT,
+ const SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = ::ffi_extras::SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION,
+ const SSL_OP_NO_COMPRESSION = ::ffi_extras::SSL_OP_NO_COMPRESSION,
+ const SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = ::ffi_extras::SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION,
+ const SSL_OP_SINGLE_ECDH_USE = ::ffi_extras::SSL_OP_SINGLE_ECDH_USE,
+ const SSL_OP_SINGLE_DH_USE = ::ffi_extras::SSL_OP_SINGLE_DH_USE,
+ const SSL_OP_CIPHER_SERVER_PREFERENCE = ::ffi_extras::SSL_OP_CIPHER_SERVER_PREFERENCE,
+ const SSL_OP_TLS_ROLLBACK_BUG = ::ffi_extras::SSL_OP_TLS_ROLLBACK_BUG,
+ const SSL_OP_NO_SSLV2 = ::ffi_extras::SSL_OP_NO_SSLv2,
+ const SSL_OP_NO_SSLV3 = ::ffi_extras::SSL_OP_NO_SSLv3,
+ const SSL_OP_NO_DTLSV1 = ::ffi_extras::SSL_OP_NO_DTLSv1,
+ const SSL_OP_NO_TLSV1 = ::ffi_extras::SSL_OP_NO_TLSv1,
+ const SSL_OP_NO_DTLSV1_2 = ::ffi_extras::SSL_OP_NO_DTLSv1_2,
+ const SSL_OP_NO_TLSV1_2 = ::ffi_extras::SSL_OP_NO_TLSv1_2,
+ const SSL_OP_NO_TLSV1_1 = ::ffi_extras::SSL_OP_NO_TLSv1_1,
+ const SSL_OP_NETSCAPE_CA_DN_BUG = ::ffi_extras::SSL_OP_NETSCAPE_CA_DN_BUG,
+ const SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG = ::ffi_extras::SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG,
+ const SSL_OP_CRYPTOPRO_TLSEXT_BUG = ::ffi_extras::SSL_OP_CRYPTOPRO_TLSEXT_BUG,
+ const SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG = ::ffi_extras::SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG,
+ const SSL_OP_MSIE_SSLV2_RSA_PADDING = ::ffi_extras::SSL_OP_MSIE_SSLV2_RSA_PADDING,
+ const SSL_OP_PKCS1_CHECK_1 = ::ffi_extras::SSL_OP_PKCS1_CHECK_1,
+ const SSL_OP_PKCS1_CHECK_2 = ::ffi_extras::SSL_OP_PKCS1_CHECK_2,
+ const SSL_OP_EPHEMERAL_RSA = ::ffi_extras::SSL_OP_EPHEMERAL_RSA,
const SSL_OP_ALL = SSL_OP_MICROSOFT_SESS_ID_BUG.bits|SSL_OP_NETSCAPE_CHALLENGE_BUG.bits
|SSL_OP_LEGACY_SERVER_CONNECT.bits|SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG.bits
|SSL_OP_TLSEXT_PADDING.bits|SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER.bits
@@ -212,19 +214,20 @@ impl SslMethod {
/// Determines the type of certificate verification used
bitflags! {
- flags SslVerifyMode: i32 {
+ pub flags SslVerifyMode: i32 {
/// Verify that the server's certificate is trusted
- const SSL_VERIFY_PEER = ffi::SSL_VERIFY_PEER,
+ const SSL_VERIFY_PEER = ::ffi::SSL_VERIFY_PEER,
/// Do not verify the server's certificate
- const SSL_VERIFY_NONE = ffi::SSL_VERIFY_NONE,
+ const SSL_VERIFY_NONE = ::ffi::SSL_VERIFY_NONE,
/// Terminate handshake if client did not return a certificate.
/// Use together with SSL_VERIFY_PEER.
- const SSL_VERIFY_FAIL_IF_NO_PEER_CERT = ffi::SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+ const SSL_VERIFY_FAIL_IF_NO_PEER_CERT = ::ffi::SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
}
}
lazy_static! {
static ref INDEXES: Mutex<HashMap<TypeId, c_int>> = Mutex::new(HashMap::new());
+ static ref SSL_INDEXES: Mutex<HashMap<TypeId, c_int>> = Mutex::new(HashMap::new());
}
// Creates a static index for user data of type T
@@ -234,6 +237,10 @@ fn get_verify_data_idx<T: Any + 'static>() -> c_int {
*INDEXES.lock().unwrap().entry(TypeId::of::<T>()).or_insert_with(|| get_new_idx::<T>())
}
+fn get_ssl_verify_data_idx<T: Any + 'static>() -> c_int {
+ *SSL_INDEXES.lock().unwrap().entry(TypeId::of::<T>()).or_insert_with(|| get_new_ssl_idx::<T>())
+}
+
#[cfg(feature = "npn")]
lazy_static! {
static ref NPN_PROTOS_IDX: c_int = get_new_idx::<Vec<u8>>();
@@ -265,6 +272,26 @@ fn get_new_idx<T>() -> c_int {
}
}
+fn get_new_ssl_idx<T>() -> c_int {
+ 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> = unsafe { mem::transmute(ptr) };
+ }
+ }
+
+ unsafe {
+ let f: ffi::CRYPTO_EX_free = free_data_box::<T>;
+ let idx = ffi::SSL_get_ex_new_index(0, ptr::null(), None, None, Some(f));
+ assert!(idx >= 0);
+ idx
+ }
+}
+
extern "C" fn raw_verify(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int {
unsafe {
let idx = ffi::SSL_get_ex_data_X509_STORE_CTX_idx();
@@ -309,6 +336,21 @@ extern "C" fn raw_verify_with_data<T>(preverify_ok: c_int,
}
}
+extern "C" fn ssl_raw_verify<F>(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int
+ where F: Fn(bool, &X509StoreContext) -> bool + Any + 'static + Sync + Send
+{
+ unsafe {
+ let idx = ffi::SSL_get_ex_data_X509_STORE_CTX_idx();
+ let ssl = ffi::X509_STORE_CTX_get_ex_data(x509_ctx, idx);
+ let verify = ffi::SSL_get_ex_data(ssl, get_ssl_verify_data_idx::<F>());
+ let verify: &F = mem::transmute(verify);
+
+ let ctx = X509StoreContext::new(x509_ctx);
+
+ verify(preverify_ok != 0, &ctx) as c_int
+ }
+}
+
extern "C" fn raw_sni(ssl: *mut ffi::SSL, ad: &mut c_int, _arg: *mut c_void) -> c_int {
unsafe {
let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl);
@@ -610,6 +652,15 @@ impl SslContext {
wrap_ssl_result(unsafe { ffi_extras::SSL_CTX_set_tmp_dh(self.ctx, dh.raw()) as i32 })
}
+ /// 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.
+ pub fn set_default_verify_paths(&mut self) -> Result<(), SslError> {
+ wrap_ssl_result(unsafe { ffi::SSL_CTX_set_default_verify_paths(self.ctx) })
+ }
+
#[allow(non_snake_case)]
/// Specifies the file that contains trusted CA certificates.
pub fn set_CA_file<P: AsRef<Path>>(&mut self, file: P) -> Result<(), SslError> {
@@ -619,6 +670,20 @@ impl SslContext {
})
}
+ /// Set the context identifier for sessions
+ ///
+ /// This value identifies the server's session cache to a clients, telling them when they're
+ /// able to reuse sessions. 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
+ /// handshake and need to be restarted.
+ pub fn set_session_id_context(&mut self, sid_ctx: &[u8]) -> Result<(), SslError> {
+ wrap_ssl_result(unsafe {
+ ffi::SSL_CTX_set_session_id_context(self.ctx, sid_ctx.as_ptr(), sid_ctx.len() as u32)
+ })
+ }
+
/// Specifies the file that contains certificate
pub fn set_certificate_file<P: AsRef<Path>>(&mut self,
file: P,
@@ -823,11 +888,11 @@ impl <'a> SslCipher<'a> {
pub fn description(&self) -> Option<String> {
unsafe {
// SSL_CIPHER_description requires a buffer of at least 128 bytes.
- let mut buf = [0i8; 128];
- let desc_ptr = ffi::SSL_CIPHER_description(self.cipher, &mut buf[0], 128);
+ let mut buf = [0; 128];
+ let desc_ptr = ffi::SSL_CIPHER_description(self.cipher, buf.as_mut_ptr(), 128);
if !desc_ptr.is_null() {
- String::from_utf8(CStr::from_ptr(desc_ptr).to_bytes().to_vec()).ok()
+ String::from_utf8(CStr::from_ptr(desc_ptr as *const _).to_bytes().to_vec()).ok()
} else {
None
}
@@ -903,6 +968,30 @@ impl Ssl {
}
}
+ /// Sets the verification mode to be used during the handshake process.
+ ///
+ /// Use `set_verify_callback` to additionally add a callback.
+ pub fn set_verify(&mut self, mode: SslVerifyMode) {
+ unsafe { ffi::SSL_set_verify(self.ssl, mode.bits as c_int, None) }
+ }
+
+ /// Sets the certificate verification callback to be used during the
+ /// handshake process.
+ ///
+ /// The callback is provided with a boolean indicating if the
+ /// preveification process was successful, and an object providing access
+ /// to the certificate chain. It should return `true` if the certificate
+ /// chain is valid and `false` otherwise.
+ pub fn set_verify_callback<F>(&mut self, mode: SslVerifyMode, verify: F)
+ where F: Fn(bool, &X509StoreContext) -> bool + Any + 'static + Sync + Send
+ {
+ unsafe {
+ let verify = Box::new(verify);
+ ffi::SSL_set_ex_data(self.ssl, get_ssl_verify_data_idx::<F>(), mem::transmute(verify));
+ ffi::SSL_set_verify(self.ssl, mode.bits as c_int, Some(ssl_raw_verify::<F>));
+ }
+ }
+
pub fn get_current_cipher<'a>(&'a self) -> Option<SslCipher<'a>> {
unsafe {
let ptr = ffi::SSL_get_current_cipher(self.ssl);
@@ -1117,12 +1206,10 @@ make_LibSslError! {
/// A stream wrapper which handles SSL encryption for an underlying stream.
pub struct SslStream<S> {
ssl: Ssl,
- _method: Arc<ffi::BIO_METHOD>, // NOTE: this *must* be after the Ssl field so things drop right
+ _method: Arc<BioMethod>, // NOTE: this *must* be after the Ssl field so things drop right
_p: PhantomData<S>,
}
-unsafe impl<S: Send> Send for SslStream<S> {}
-
/// # Deprecated
///
/// This method does not behave as expected and will be removed in a future
@@ -1309,7 +1396,7 @@ impl<S> SslStream<S> {
#[cfg(feature = "nightly")]
fn check_panic(&mut self) {
if let Some(err) = unsafe { bio::take_panic::<S>(self.ssl.get_raw_rbio()) } {
- ::std::panic::propagate(err)
+ ::std::panic::resume_unwind(err)
}
}
diff --git a/openssl/src/ssl/tests/mod.rs b/openssl/src/ssl/tests/mod.rs
index be35d7ef..ccdc44e4 100644
--- a/openssl/src/ssl/tests/mod.rs
+++ b/openssl/src/ssl/tests/mod.rs
@@ -196,7 +196,7 @@ macro_rules! run_test(
use ssl::SslMethod;
use ssl::{SslContext, Ssl, SslStream, VerifyCallback};
use ssl::SSL_VERIFY_PEER;
- use crypto::hash::Type::SHA256;
+ use crypto::hash::Type::SHA1;
use x509::X509StoreContext;
use serialize::hex::FromHex;
use super::Server;
@@ -359,7 +359,7 @@ run_test!(verify_callback_data, |method, stream| {
match cert {
None => false,
Some(cert) => {
- let fingerprint = cert.fingerprint(SHA256).unwrap();
+ let fingerprint = cert.fingerprint(SHA1).unwrap();
&fingerprint == node_id
}
}
@@ -370,7 +370,7 @@ run_test!(verify_callback_data, |method, stream| {
// in DER format.
// Command: openssl x509 -in test/cert.pem -outform DER | openssl dgst -sha256
// Please update if "test/cert.pem" will ever change
- let node_hash_str = "db400bb62f1b1f29c3b8f323b8f7d9dea724fdcd67104ef549c772ae3749655b";
+ let node_hash_str = "E19427DAC79FBE758394945276A6E4F15F0BEBE6";
let node_id = node_hash_str.from_hex().unwrap();
ctx.set_verify_with_data(SSL_VERIFY_PEER, callback, node_id);
ctx.set_verify_depth(1);
@@ -381,6 +381,36 @@ run_test!(verify_callback_data, |method, stream| {
}
});
+run_test!(ssl_verify_callback, |method, stream| {
+ use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
+ use ssl::IntoSsl;
+
+ static CHECKED: AtomicUsize = ATOMIC_USIZE_INIT;
+
+ let ctx = SslContext::new(method).unwrap();
+ let mut ssl = ctx.into_ssl().unwrap();
+
+ let node_hash_str = "E19427DAC79FBE758394945276A6E4F15F0BEBE6";
+ let node_id = node_hash_str.from_hex().unwrap();
+ ssl.set_verify_callback(SSL_VERIFY_PEER, move |_, x509| {
+ CHECKED.store(1, Ordering::SeqCst);
+ match x509.get_current_cert() {
+ None => false,
+ Some(cert) => {
+ let fingerprint = cert.fingerprint(SHA1).unwrap();
+ fingerprint == node_id
+ }
+ }
+ });
+
+ match SslStream::connect_generic(ssl, stream) {
+ Ok(_) => (),
+ Err(err) => panic!("Expected success, got {:?}", err)
+ }
+
+ assert_eq!(CHECKED.load(Ordering::SeqCst), 1);
+});
+
// Make sure every write call translates to a write call to the underlying socket.
#[test]
fn test_write_hits_stream() {
@@ -472,8 +502,8 @@ run_test!(get_peer_certificate, |method, stream| {
let stream = SslStream::connect_generic(&SslContext::new(method).unwrap(),
stream).unwrap();
let cert = stream.ssl().peer_certificate().unwrap();
- let fingerprint = cert.fingerprint(SHA256).unwrap();
- let node_hash_str = "db400bb62f1b1f29c3b8f323b8f7d9dea724fdcd67104ef549c772ae3749655b";
+ let fingerprint = cert.fingerprint(SHA1).unwrap();
+ let node_hash_str = "E19427DAC79FBE758394945276A6E4F15F0BEBE6";
let node_id = node_hash_str.from_hex().unwrap();
assert_eq!(node_id, fingerprint)
});
@@ -1059,3 +1089,21 @@ fn refcount_ssl_context() {
let _new_ctx_b = ssl.set_ssl_context(&new_ctx_a);
}
}
+
+#[test]
+#[cfg_attr(windows, ignore)] // don't have a trusted CA list easily available :(
+fn default_verify_paths() {
+ let mut ctx = SslContext::new(SslMethod::Sslv23).unwrap();
+ ctx.set_default_verify_paths().unwrap();
+ ctx.set_verify(SSL_VERIFY_PEER, None);
+ let s = TcpStream::connect("google.com:443").unwrap();
+ let mut socket = SslStream::connect(&ctx, 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>"));
+}