Loading src/ffi.rs +1 −0 Original line number Diff line number Diff line Loading @@ -372,6 +372,7 @@ extern "C" { pub fn SSL_CTX_free(ctx: *mut SSL_CTX); pub fn SSL_CTX_set_verify(ctx: *mut SSL_CTX, mode: c_int, verify_callback: Option<extern fn(c_int, *mut X509_STORE_CTX) -> c_int>); pub fn SSL_CTX_set_verify_depth(ctx: *mut SSL_CTX, depth: c_int); pub fn SSL_CTX_load_verify_locations(ctx: *mut SSL_CTX, CAfile: *const c_char, CApath: *const c_char) -> c_int; pub fn SSL_CTX_get_ex_new_index(argl: c_long, argp: *const c_void, Loading src/ssl/mod.rs +82 −1 Original line number Diff line number Diff line use libc::{c_int, c_void, c_char}; use libc::{c_int, c_void, c_char, c_long}; use std::io::{IoResult, IoError, EndOfFile, Stream, Reader, Writer}; use std::mem; use std::ptr; Loading Loading @@ -82,6 +82,30 @@ pub enum SslVerifyMode { SslVerifyNone = ffi::SSL_VERIFY_NONE } // Creates a static index for user data of type T // Registers a destructor for the data which will be called // when context is freed fn get_verify_data_idx<T>() -> c_int { static mut VERIFY_DATA_IDX: c_int = -1; static mut INIT: Once = ONCE_INIT; extern 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) { let _: Box<T> = unsafe { mem::transmute(ptr) }; } unsafe { INIT.doit(|| { let idx = ffi::SSL_CTX_get_ex_new_index(0, ptr::null(), None, None, Some(free_data_box::<T>)); assert!(idx >= 0); VERIFY_DATA_IDX = idx; }); VERIFY_DATA_IDX } } extern fn locking_function(mode: c_int, n: c_int, _file: *const c_char, _line: c_int) { unsafe { Loading Loading @@ -113,10 +137,45 @@ extern fn raw_verify(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) } } extern fn raw_verify_with_data<T>(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(); let ssl = ffi::X509_STORE_CTX_get_ex_data(x509_ctx, idx); let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl); let verify = ffi::SSL_CTX_get_ex_data(ssl_ctx, VERIFY_IDX); let verify: Option<VerifyCallbackData<T>> = mem::transmute(verify); let data = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_verify_data_idx::<T>()); let data: Box<T> = mem::transmute(data); let ctx = X509StoreContext::new(x509_ctx); let res = match verify { None => preverify_ok, Some(verify) => verify(preverify_ok != 0, &ctx, &*data) as c_int }; // Since data might be required on the next verification // it is time to forget about it and avoid dropping // data will be freed once OpenSSL considers it is time // to free all context data mem::forget(data); res } } /// The signature of functions that can be used to manually verify certificates pub type VerifyCallback = fn(preverify_ok: bool, x509_ctx: &X509StoreContext) -> bool; /// The signature of functions that can be used to manually verify certificates /// when user-data should be carried for all verification process pub type VerifyCallbackData<T> = fn(preverify_ok: bool, x509_ctx: &X509StoreContext, data: &T) -> bool; // FIXME: macro may be instead of inlining? #[inline] fn wrap_ssl_result(res: c_int) -> Option<SslError> { Loading Loading @@ -161,6 +220,28 @@ impl SslContext { } } /// Configures the certificate verification method for new connections also /// carrying supplied data. pub fn set_verify_with_data<T>(&mut self, mode: SslVerifyMode, verify: Option<VerifyCallbackData<T>>, data: T) { let data = box data; unsafe { ffi::SSL_CTX_set_ex_data(self.ctx, VERIFY_IDX, mem::transmute(verify)); ffi::SSL_CTX_set_ex_data(self.ctx, get_verify_data_idx::<T>(), mem::transmute(data)); ffi::SSL_CTX_set_verify(self.ctx, mode as c_int, Some(raw_verify_with_data::<T>)); } } /// Sets verification depth pub fn set_verify_depth(&mut self, depth: uint) { unsafe { ffi::SSL_CTX_set_verify_depth(self.ctx, depth as c_int); } } #[allow(non_snake_case)] /// Specifies the file that contains trusted CA certificates. pub fn set_CA_file(&mut self, file: &str) -> Option<SslError> { Loading src/ssl/tests.rs +47 −2 Original line number Diff line number Diff line use std::io::Writer; use std::io::net::tcp::TcpStream; use std::num::FromStrRadix; use std::str; use crypto::hash::{SHA256}; use ssl::{Sslv23, SslContext, SslStream, SslVerifyPeer}; use x509::{X509Generator, X509, DigitalSignature, KeyEncipherment, ClientAuth, ServerAuth, X509StoreContext}; use ssl::{Sslv23, SslContext, SslStream, SslVerifyPeer, SslVerifyNone}; use x509::{X509Generator, DigitalSignature, KeyEncipherment, ClientAuth, ServerAuth, X509StoreContext}; #[test] fn test_new_ctx() { Loading Loading @@ -141,6 +142,50 @@ fn test_verify_trusted_get_error_err() { assert!(SslStream::new(&ctx, stream).is_err()); } fn hash_str_to_vec(s: &str) -> Vec<u8> { let mut res = Vec::new(); assert!(s.len() % 2 == 0, "Hash str should have len = 2 * n"); for i in range(0, s.len() / 2) { let substr = s.slice(i, i + 2); let t: Option<u8> = FromStrRadix::from_str_radix(substr, 16); assert!(t.is_some(), "Hash str must contain only hex digits, i.e. [0-9a-f]"); res.push(t.unwrap()); } res } #[test] fn test_verify_callback_data() { fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext, node_id: &Vec<u8>) -> bool { let cert = x509_ctx.get_current_cert(); match cert { None => false, Some(cert) => { let fingerprint = cert.fingerprint(SHA256).unwrap(); fingerprint.as_slice() == node_id.as_slice() } } } let stream = TcpStream::connect("127.0.0.1", 15418).unwrap(); let mut ctx = SslContext::new(Sslv23).unwrap(); // Node id was generated as SHA256 hash of certificate "test/cert.pem" // 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 = "6204f6617e1af7495394250655f43600cd483e2dfc2005e92d0fe439d0723c34"; let node_id = hash_str_to_vec(node_hash_str); ctx.set_verify_with_data(SslVerifyNone, Some(callback), node_id); ctx.set_verify_depth(1); match SslStream::new(&ctx, stream) { Ok(_) => (), Err(err) => fail!("Expected success, got {}", err) } } #[test] fn test_write() { let stream = TcpStream::connect("127.0.0.1", 15418).unwrap(); Loading src/x509/mod.rs 100755 → 100644 +1 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ pub enum X509FileType { ASN1 = ffi::X509_FILETYPE_ASN1, Default = ffi::X509_FILETYPE_DEFAULT } pub struct X509StoreContext { ctx: *mut ffi::X509_STORE_CTX } Loading Loading
src/ffi.rs +1 −0 Original line number Diff line number Diff line Loading @@ -372,6 +372,7 @@ extern "C" { pub fn SSL_CTX_free(ctx: *mut SSL_CTX); pub fn SSL_CTX_set_verify(ctx: *mut SSL_CTX, mode: c_int, verify_callback: Option<extern fn(c_int, *mut X509_STORE_CTX) -> c_int>); pub fn SSL_CTX_set_verify_depth(ctx: *mut SSL_CTX, depth: c_int); pub fn SSL_CTX_load_verify_locations(ctx: *mut SSL_CTX, CAfile: *const c_char, CApath: *const c_char) -> c_int; pub fn SSL_CTX_get_ex_new_index(argl: c_long, argp: *const c_void, Loading
src/ssl/mod.rs +82 −1 Original line number Diff line number Diff line use libc::{c_int, c_void, c_char}; use libc::{c_int, c_void, c_char, c_long}; use std::io::{IoResult, IoError, EndOfFile, Stream, Reader, Writer}; use std::mem; use std::ptr; Loading Loading @@ -82,6 +82,30 @@ pub enum SslVerifyMode { SslVerifyNone = ffi::SSL_VERIFY_NONE } // Creates a static index for user data of type T // Registers a destructor for the data which will be called // when context is freed fn get_verify_data_idx<T>() -> c_int { static mut VERIFY_DATA_IDX: c_int = -1; static mut INIT: Once = ONCE_INIT; extern 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) { let _: Box<T> = unsafe { mem::transmute(ptr) }; } unsafe { INIT.doit(|| { let idx = ffi::SSL_CTX_get_ex_new_index(0, ptr::null(), None, None, Some(free_data_box::<T>)); assert!(idx >= 0); VERIFY_DATA_IDX = idx; }); VERIFY_DATA_IDX } } extern fn locking_function(mode: c_int, n: c_int, _file: *const c_char, _line: c_int) { unsafe { Loading Loading @@ -113,10 +137,45 @@ extern fn raw_verify(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) } } extern fn raw_verify_with_data<T>(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(); let ssl = ffi::X509_STORE_CTX_get_ex_data(x509_ctx, idx); let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl); let verify = ffi::SSL_CTX_get_ex_data(ssl_ctx, VERIFY_IDX); let verify: Option<VerifyCallbackData<T>> = mem::transmute(verify); let data = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_verify_data_idx::<T>()); let data: Box<T> = mem::transmute(data); let ctx = X509StoreContext::new(x509_ctx); let res = match verify { None => preverify_ok, Some(verify) => verify(preverify_ok != 0, &ctx, &*data) as c_int }; // Since data might be required on the next verification // it is time to forget about it and avoid dropping // data will be freed once OpenSSL considers it is time // to free all context data mem::forget(data); res } } /// The signature of functions that can be used to manually verify certificates pub type VerifyCallback = fn(preverify_ok: bool, x509_ctx: &X509StoreContext) -> bool; /// The signature of functions that can be used to manually verify certificates /// when user-data should be carried for all verification process pub type VerifyCallbackData<T> = fn(preverify_ok: bool, x509_ctx: &X509StoreContext, data: &T) -> bool; // FIXME: macro may be instead of inlining? #[inline] fn wrap_ssl_result(res: c_int) -> Option<SslError> { Loading Loading @@ -161,6 +220,28 @@ impl SslContext { } } /// Configures the certificate verification method for new connections also /// carrying supplied data. pub fn set_verify_with_data<T>(&mut self, mode: SslVerifyMode, verify: Option<VerifyCallbackData<T>>, data: T) { let data = box data; unsafe { ffi::SSL_CTX_set_ex_data(self.ctx, VERIFY_IDX, mem::transmute(verify)); ffi::SSL_CTX_set_ex_data(self.ctx, get_verify_data_idx::<T>(), mem::transmute(data)); ffi::SSL_CTX_set_verify(self.ctx, mode as c_int, Some(raw_verify_with_data::<T>)); } } /// Sets verification depth pub fn set_verify_depth(&mut self, depth: uint) { unsafe { ffi::SSL_CTX_set_verify_depth(self.ctx, depth as c_int); } } #[allow(non_snake_case)] /// Specifies the file that contains trusted CA certificates. pub fn set_CA_file(&mut self, file: &str) -> Option<SslError> { Loading
src/ssl/tests.rs +47 −2 Original line number Diff line number Diff line use std::io::Writer; use std::io::net::tcp::TcpStream; use std::num::FromStrRadix; use std::str; use crypto::hash::{SHA256}; use ssl::{Sslv23, SslContext, SslStream, SslVerifyPeer}; use x509::{X509Generator, X509, DigitalSignature, KeyEncipherment, ClientAuth, ServerAuth, X509StoreContext}; use ssl::{Sslv23, SslContext, SslStream, SslVerifyPeer, SslVerifyNone}; use x509::{X509Generator, DigitalSignature, KeyEncipherment, ClientAuth, ServerAuth, X509StoreContext}; #[test] fn test_new_ctx() { Loading Loading @@ -141,6 +142,50 @@ fn test_verify_trusted_get_error_err() { assert!(SslStream::new(&ctx, stream).is_err()); } fn hash_str_to_vec(s: &str) -> Vec<u8> { let mut res = Vec::new(); assert!(s.len() % 2 == 0, "Hash str should have len = 2 * n"); for i in range(0, s.len() / 2) { let substr = s.slice(i, i + 2); let t: Option<u8> = FromStrRadix::from_str_radix(substr, 16); assert!(t.is_some(), "Hash str must contain only hex digits, i.e. [0-9a-f]"); res.push(t.unwrap()); } res } #[test] fn test_verify_callback_data() { fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext, node_id: &Vec<u8>) -> bool { let cert = x509_ctx.get_current_cert(); match cert { None => false, Some(cert) => { let fingerprint = cert.fingerprint(SHA256).unwrap(); fingerprint.as_slice() == node_id.as_slice() } } } let stream = TcpStream::connect("127.0.0.1", 15418).unwrap(); let mut ctx = SslContext::new(Sslv23).unwrap(); // Node id was generated as SHA256 hash of certificate "test/cert.pem" // 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 = "6204f6617e1af7495394250655f43600cd483e2dfc2005e92d0fe439d0723c34"; let node_id = hash_str_to_vec(node_hash_str); ctx.set_verify_with_data(SslVerifyNone, Some(callback), node_id); ctx.set_verify_depth(1); match SslStream::new(&ctx, stream) { Ok(_) => (), Err(err) => fail!("Expected success, got {}", err) } } #[test] fn test_write() { let stream = TcpStream::connect("127.0.0.1", 15418).unwrap(); Loading
src/x509/mod.rs 100755 → 100644 +1 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ pub enum X509FileType { ASN1 = ffi::X509_FILETYPE_ASN1, Default = ffi::X509_FILETYPE_DEFAULT } pub struct X509StoreContext { ctx: *mut ffi::X509_STORE_CTX } Loading