User-provided data in verify
This commit is contained in:
parent
73885dad02
commit
fbb359720b
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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…
Reference in New Issue