diff options
| author | Valerii Hiora <[email protected]> | 2014-09-30 11:06:37 +0300 |
|---|---|---|
| committer | Valerii Hiora <[email protected]> | 2014-10-02 10:15:50 +0300 |
| commit | fbb359720b24266938284edcd5cec46744379f1c (patch) | |
| tree | 69648c3ed31e085403cd2a653fc869dc281eaba7 | |
| parent | Merge pull request #57 from vhbit/mut-cleanup (diff) | |
| download | rust-openssl-fbb359720b24266938284edcd5cec46744379f1c.tar.xz rust-openssl-fbb359720b24266938284edcd5cec46744379f1c.zip | |
User-provided data in verify
| -rw-r--r-- | src/ffi.rs | 1 | ||||
| -rw-r--r-- | src/ssl/mod.rs | 83 | ||||
| -rw-r--r-- | src/ssl/tests.rs | 49 | ||||
| -rw-r--r--[-rwxr-xr-x] | src/x509/mod.rs | 1 |
4 files changed, 131 insertions, 3 deletions
@@ -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, diff --git a/src/ssl/mod.rs b/src/ssl/mod.rs index f034ed37..e1f35724 100644 --- a/src/ssl/mod.rs +++ b/src/ssl/mod.rs @@ -1,4 +1,4 @@ -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; @@ -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 { @@ -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> { @@ -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> { diff --git a/src/ssl/tests.rs b/src/ssl/tests.rs index 82effee0..9dd90ae7 100644 --- a/src/ssl/tests.rs +++ b/src/ssl/tests.rs @@ -1,10 +1,11 @@ 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() { @@ -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(); diff --git a/src/x509/mod.rs b/src/x509/mod.rs index c46edc3d..8c20ac8f 100755..100644 --- a/src/x509/mod.rs +++ b/src/x509/mod.rs @@ -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 } |