Loading openssl-sys/src/lib.rs +10 −0 Original line number Diff line number Diff line Loading @@ -700,6 +700,16 @@ extern "C" { pub fn SSL_get_version(ssl: *mut SSL) -> *const c_char; pub fn SSL_state_string(ssl: *mut SSL) -> *const c_char; pub fn SSL_state_string_long(ssl: *mut SSL) -> *const c_char; pub fn SSL_set_verify(ssl: *mut SSL, mode: c_int, verify_callback: Option<extern fn(c_int, *mut X509_STORE_CTX) -> c_int>); pub fn SSL_get_ex_new_index(argl: c_long, argp: *const c_void, new_func: Option<CRYPTO_EX_new>, dup_func: Option<CRYPTO_EX_dup>, free_func: Option<CRYPTO_EX_free>) -> c_int; pub fn SSL_set_ex_data(ssl: *mut SSL, idx: c_int, data: *mut c_void) -> c_int; pub fn SSL_get_ex_data(ssl: *mut SSL, idx: c_int) -> *mut c_void; pub fn SSL_get_servername(ssl: *const SSL, name_type: c_long) -> *const c_char; Loading openssl/src/ssl/mod.rs +57 −0 Original line number Diff line number Diff line Loading @@ -227,6 +227,7 @@ bitflags! { 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 Loading @@ -236,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>>(); Loading Loading @@ -267,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(); Loading Loading @@ -311,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); Loading Loading @@ -928,6 +968,23 @@ impl Ssl { } } /// 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<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); Loading openssl/src/ssl/tests/mod.rs +30 −0 Original line number Diff line number Diff line Loading @@ -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 = "db400bb62f1b1f29c3b8f323b8f7d9dea724fdcd67104ef549c772ae3749655b"; let node_id = node_hash_str.from_hex().unwrap(); ssl.set_verify(SSL_VERIFY_PEER, move |_, x509| { CHECKED.store(1, Ordering::SeqCst); match x509.get_current_cert() { None => false, Some(cert) => { let fingerprint = cert.fingerprint(SHA256).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() { Loading Loading
openssl-sys/src/lib.rs +10 −0 Original line number Diff line number Diff line Loading @@ -700,6 +700,16 @@ extern "C" { pub fn SSL_get_version(ssl: *mut SSL) -> *const c_char; pub fn SSL_state_string(ssl: *mut SSL) -> *const c_char; pub fn SSL_state_string_long(ssl: *mut SSL) -> *const c_char; pub fn SSL_set_verify(ssl: *mut SSL, mode: c_int, verify_callback: Option<extern fn(c_int, *mut X509_STORE_CTX) -> c_int>); pub fn SSL_get_ex_new_index(argl: c_long, argp: *const c_void, new_func: Option<CRYPTO_EX_new>, dup_func: Option<CRYPTO_EX_dup>, free_func: Option<CRYPTO_EX_free>) -> c_int; pub fn SSL_set_ex_data(ssl: *mut SSL, idx: c_int, data: *mut c_void) -> c_int; pub fn SSL_get_ex_data(ssl: *mut SSL, idx: c_int) -> *mut c_void; pub fn SSL_get_servername(ssl: *const SSL, name_type: c_long) -> *const c_char; Loading
openssl/src/ssl/mod.rs +57 −0 Original line number Diff line number Diff line Loading @@ -227,6 +227,7 @@ bitflags! { 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 Loading @@ -236,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>>(); Loading Loading @@ -267,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(); Loading Loading @@ -311,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); Loading Loading @@ -928,6 +968,23 @@ impl Ssl { } } /// 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<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); Loading
openssl/src/ssl/tests/mod.rs +30 −0 Original line number Diff line number Diff line Loading @@ -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 = "db400bb62f1b1f29c3b8f323b8f7d9dea724fdcd67104ef549c772ae3749655b"; let node_id = node_hash_str.from_hex().unwrap(); ssl.set_verify(SSL_VERIFY_PEER, move |_, x509| { CHECKED.store(1, Ordering::SeqCst); match x509.get_current_cert() { None => false, Some(cert) => { let fingerprint = cert.fingerprint(SHA256).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() { Loading