Sketch of custom cert verification callback
This commit is contained in:
parent
162fff7c64
commit
6ee6589227
1
error.rs
1
error.rs
|
|
@ -2,6 +2,7 @@ use std::libc::c_ulong;
|
||||||
|
|
||||||
use super::ffi;
|
use super::ffi;
|
||||||
|
|
||||||
|
#[deriving(ToStr)]
|
||||||
pub enum SslError {
|
pub enum SslError {
|
||||||
StreamEof,
|
StreamEof,
|
||||||
SslSessionClosed,
|
SslSessionClosed,
|
||||||
|
|
|
||||||
30
ffi.rs
30
ffi.rs
|
|
@ -1,6 +1,6 @@
|
||||||
#[doc(hidden)];
|
#[doc(hidden)];
|
||||||
|
|
||||||
use std::libc::{c_int, c_void, c_ulong, c_char};
|
use std::libc::{c_int, c_void, c_long, c_ulong, c_char};
|
||||||
|
|
||||||
// openssl/ssl.h
|
// openssl/ssl.h
|
||||||
pub type SSL_CTX = c_void;
|
pub type SSL_CTX = c_void;
|
||||||
|
|
@ -9,6 +9,19 @@ pub type SSL = c_void;
|
||||||
pub type BIO = c_void;
|
pub type BIO = c_void;
|
||||||
pub type BIO_METHOD = c_void;
|
pub type BIO_METHOD = c_void;
|
||||||
pub type X509_STORE_CTX = c_void;
|
pub type X509_STORE_CTX = c_void;
|
||||||
|
pub type CRYPTO_EX_DATA = c_void;
|
||||||
|
|
||||||
|
pub type CRYPTO_EX_new = Option<extern "C" fn(parent: *c_void, ptr: *c_void,
|
||||||
|
ad: *CRYPTO_EX_DATA, idx: c_int,
|
||||||
|
argl: c_long, argp: *c_void)
|
||||||
|
-> c_int>;
|
||||||
|
pub type CRYPTO_EX_dup = extern "C" fn(to: *CRYPTO_EX_DATA,
|
||||||
|
from: *CRYPTO_EX_DATA, from_d: *c_void,
|
||||||
|
idx: c_int, argl: c_long, argp: *c_void)
|
||||||
|
-> c_int;
|
||||||
|
pub type CRYPTO_EX_free = extern "C" fn(parent: *c_void, ptr: *c_void,
|
||||||
|
ad: *CRYPTO_EX_DATA, idx: c_int,
|
||||||
|
argl: c_long, argp: *c_void);
|
||||||
|
|
||||||
pub static SSL_ERROR_NONE: c_int = 0;
|
pub static SSL_ERROR_NONE: c_int = 0;
|
||||||
pub static SSL_ERROR_SSL: c_int = 1;
|
pub static SSL_ERROR_SSL: c_int = 1;
|
||||||
|
|
@ -38,9 +51,20 @@ externfn!(fn SSLv23_method() -> *SSL_METHOD)
|
||||||
externfn!(fn SSL_CTX_new(method: *SSL_METHOD) -> *SSL_CTX)
|
externfn!(fn SSL_CTX_new(method: *SSL_METHOD) -> *SSL_CTX)
|
||||||
externfn!(fn SSL_CTX_free(ctx: *SSL_CTX))
|
externfn!(fn SSL_CTX_free(ctx: *SSL_CTX))
|
||||||
externfn!(fn SSL_CTX_set_verify(ctx: *SSL_CTX, mode: c_int,
|
externfn!(fn SSL_CTX_set_verify(ctx: *SSL_CTX, mode: c_int,
|
||||||
verify_callback: Option<extern "C" fn(int, *X509_STORE_CTX) -> c_int>))
|
verify_callback: Option<extern "C" fn(c_int, *X509_STORE_CTX) -> c_int>))
|
||||||
externfn!(fn SSL_CTX_load_verify_locations(ctx: *SSL_CTX, CAfile: *c_char,
|
externfn!(fn SSL_CTX_load_verify_locations(ctx: *SSL_CTX, CAfile: *c_char,
|
||||||
CApath: *c_char) -> c_int)
|
CApath: *c_char) -> c_int)
|
||||||
|
externfn!(fn SSL_CTX_get_ex_new_index(argl: c_long, argp: *c_void,
|
||||||
|
new_func: Option<CRYPTO_EX_new>,
|
||||||
|
dup_func: Option<CRYPTO_EX_dup>,
|
||||||
|
free_func: Option<CRYPTO_EX_free>)
|
||||||
|
-> c_int)
|
||||||
|
externfn!(fn SSL_CTX_set_ex_data(ctx: *SSL_CTX, idx: c_int, data: *c_void)
|
||||||
|
-> c_int)
|
||||||
|
externfn!(fn SSL_CTX_get_ex_data(ctx: *SSL_CTX, idx: c_int) -> *c_void)
|
||||||
|
|
||||||
|
externfn!(fn X509_STORE_CTX_get_ex_data(ctx: *X509_STORE_CTX, idx: c_int)
|
||||||
|
-> *c_void)
|
||||||
|
|
||||||
externfn!(fn SSL_new(ctx: *SSL_CTX) -> *SSL)
|
externfn!(fn SSL_new(ctx: *SSL_CTX) -> *SSL)
|
||||||
externfn!(fn SSL_free(ssl: *SSL))
|
externfn!(fn SSL_free(ssl: *SSL))
|
||||||
|
|
@ -53,6 +77,8 @@ externfn!(fn SSL_get_error(ssl: *SSL, ret: c_int) -> c_int)
|
||||||
externfn!(fn SSL_read(ssl: *SSL, buf: *c_void, num: c_int) -> c_int)
|
externfn!(fn SSL_read(ssl: *SSL, buf: *c_void, num: c_int) -> c_int)
|
||||||
externfn!(fn SSL_write(ssl: *SSL, buf: *c_void, num: c_int) -> c_int)
|
externfn!(fn SSL_write(ssl: *SSL, buf: *c_void, num: c_int) -> c_int)
|
||||||
externfn!(fn SSL_shutdown(ssl: *SSL) -> c_int)
|
externfn!(fn SSL_shutdown(ssl: *SSL) -> c_int)
|
||||||
|
externfn!(fn SSL_get_ex_data_X509_STORE_CTX_idx() -> c_int)
|
||||||
|
externfn!(fn SSL_get_SSL_CTX(ssl: *SSL) -> *SSL_CTX)
|
||||||
|
|
||||||
externfn!(fn BIO_s_mem() -> *BIO_METHOD)
|
externfn!(fn BIO_s_mem() -> *BIO_METHOD)
|
||||||
externfn!(fn BIO_new(type_: *BIO_METHOD) -> *BIO)
|
externfn!(fn BIO_new(type_: *BIO_METHOD) -> *BIO)
|
||||||
|
|
|
||||||
38
lib.rs
38
lib.rs
|
|
@ -1,7 +1,9 @@
|
||||||
|
use std::cast;
|
||||||
use std::libc::{c_int, c_void};
|
use std::libc::{c_int, c_void};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::task;
|
use std::task;
|
||||||
use std::unstable::atomics::{AtomicBool, INIT_ATOMIC_BOOL, Acquire, Release};
|
use std::unstable::atomics::{AtomicBool, INIT_ATOMIC_BOOL, AtomicInt,
|
||||||
|
INIT_ATOMIC_INT, Acquire, Release, SeqCst};
|
||||||
use std::rt::io::{Stream, Reader, Writer, Decorator};
|
use std::rt::io::{Stream, Reader, Writer, Decorator};
|
||||||
use std::vec;
|
use std::vec;
|
||||||
|
|
||||||
|
|
@ -17,6 +19,8 @@ mod ffi;
|
||||||
static mut STARTED_INIT: AtomicBool = INIT_ATOMIC_BOOL;
|
static mut STARTED_INIT: AtomicBool = INIT_ATOMIC_BOOL;
|
||||||
static mut FINISHED_INIT: AtomicBool = INIT_ATOMIC_BOOL;
|
static mut FINISHED_INIT: AtomicBool = INIT_ATOMIC_BOOL;
|
||||||
|
|
||||||
|
static mut VERIFY_IDX: AtomicInt = INIT_ATOMIC_INT;
|
||||||
|
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
unsafe {
|
unsafe {
|
||||||
if STARTED_INIT.swap(true, Acquire) {
|
if STARTED_INIT.swap(true, Acquire) {
|
||||||
|
|
@ -27,6 +31,11 @@ pub fn init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ffi::SSL_library_init();
|
ffi::SSL_library_init();
|
||||||
|
let verify_idx = ffi::SSL_CTX_get_ex_new_index(0, ptr::null(), None,
|
||||||
|
None, None);
|
||||||
|
assert!(verify_idx >= 0);
|
||||||
|
VERIFY_IDX.store(verify_idx as int, SeqCst);
|
||||||
|
|
||||||
FINISHED_INIT.store(true, Release);
|
FINISHED_INIT.store(true, Release);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -54,6 +63,25 @@ pub enum SslVerifyMode {
|
||||||
SslVerifyNone = ffi::SSL_VERIFY_NONE
|
SslVerifyNone = ffi::SSL_VERIFY_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" fn raw_verify(preverify_ok: c_int, x509_ctx: *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 idx = VERIFY_IDX.load(SeqCst) as c_int;
|
||||||
|
let verify = ffi::SSL_CTX_get_ex_data(ssl_ctx, idx);
|
||||||
|
let verify: Option<VerifyCallback> = cast::transmute(verify);
|
||||||
|
|
||||||
|
match verify {
|
||||||
|
None => preverify_ok,
|
||||||
|
Some(verify) => verify(preverify_ok != 0) as c_int
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type VerifyCallback = extern "Rust" fn(preverify_ok: bool) -> bool;
|
||||||
|
|
||||||
pub struct SslContext {
|
pub struct SslContext {
|
||||||
priv ctx: *ffi::SSL_CTX
|
priv ctx: *ffi::SSL_CTX
|
||||||
}
|
}
|
||||||
|
|
@ -84,9 +112,13 @@ impl SslContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: support callback (see SSL_CTX_set_ex_data)
|
// TODO: support callback (see SSL_CTX_set_ex_data)
|
||||||
pub fn set_verify(&mut self, mode: SslVerifyMode) {
|
pub fn set_verify(&mut self, mode: SslVerifyMode,
|
||||||
|
verify: Option<VerifyCallback>) {
|
||||||
unsafe {
|
unsafe {
|
||||||
ffi::SSL_CTX_set_verify(self.ctx, mode as c_int, None);
|
let idx = VERIFY_IDX.load(SeqCst) as c_int;
|
||||||
|
ffi::SSL_CTX_set_ex_data(self.ctx, idx,
|
||||||
|
cast::transmute(verify));
|
||||||
|
ffi::SSL_CTX_set_verify(self.ctx, mode as c_int, Some(raw_verify));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
56
tests.rs
56
tests.rs
|
|
@ -20,7 +20,7 @@ fn test_new_sslstream() {
|
||||||
fn test_verify_untrusted() {
|
fn test_verify_untrusted() {
|
||||||
let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap();
|
let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap();
|
||||||
let mut ctx = SslContext::new(Sslv23);
|
let mut ctx = SslContext::new(Sslv23);
|
||||||
ctx.set_verify(SslVerifyPeer);
|
ctx.set_verify(SslVerifyPeer, None);
|
||||||
match SslStream::try_new(&ctx, stream) {
|
match SslStream::try_new(&ctx, stream) {
|
||||||
Ok(_) => fail!("expected failure"),
|
Ok(_) => fail!("expected failure"),
|
||||||
Err(err) => println!("error {:?}", err)
|
Err(err) => println!("error {:?}", err)
|
||||||
|
|
@ -31,7 +31,7 @@ fn test_verify_untrusted() {
|
||||||
fn test_verify_trusted() {
|
fn test_verify_trusted() {
|
||||||
let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap();
|
let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap();
|
||||||
let mut ctx = SslContext::new(Sslv23);
|
let mut ctx = SslContext::new(Sslv23);
|
||||||
ctx.set_verify(SslVerifyPeer);
|
ctx.set_verify(SslVerifyPeer, None);
|
||||||
assert!(ctx.set_CA_file("cert.pem").is_none());
|
assert!(ctx.set_CA_file("cert.pem").is_none());
|
||||||
match SslStream::try_new(&ctx, stream) {
|
match SslStream::try_new(&ctx, stream) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
|
|
@ -39,6 +39,58 @@ fn test_verify_trusted() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_verify_untrusted_callback_override_ok() {
|
||||||
|
fn callback(_preverify_ok: bool) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap();
|
||||||
|
let mut ctx = SslContext::new(Sslv23);
|
||||||
|
ctx.set_verify(SslVerifyPeer, Some(callback));
|
||||||
|
match SslStream::try_new(&ctx, stream) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(err) => fail!("Expected success, got {:?}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_verify_untrusted_callback_override_bad() {
|
||||||
|
fn callback(_preverify_ok: bool) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap();
|
||||||
|
let mut ctx = SslContext::new(Sslv23);
|
||||||
|
ctx.set_verify(SslVerifyPeer, Some(callback));
|
||||||
|
assert!(SslStream::try_new(&ctx, stream).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_verify_trusted_callback_override_ok() {
|
||||||
|
fn callback(_preverify_ok: bool) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap();
|
||||||
|
let mut ctx = SslContext::new(Sslv23);
|
||||||
|
ctx.set_verify(SslVerifyPeer, Some(callback));
|
||||||
|
assert!(ctx.set_CA_file("cert.pem").is_none());
|
||||||
|
match SslStream::try_new(&ctx, stream) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(err) => fail!("Expected success, got {:?}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_verify_trusted_callback_override_bad() {
|
||||||
|
fn callback(_preverify_ok: bool) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap();
|
||||||
|
let mut ctx = SslContext::new(Sslv23);
|
||||||
|
ctx.set_verify(SslVerifyPeer, Some(callback));
|
||||||
|
assert!(ctx.set_CA_file("cert.pem").is_none());
|
||||||
|
assert!(SslStream::try_new(&ctx, stream).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_write() {
|
fn test_write() {
|
||||||
let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap();
|
let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue