From 97713dfaf590af1ee9106601de32707ccbd428c0 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Fri, 4 Oct 2013 20:45:49 -0700 Subject: [PATCH 01/49] Initial commit --- LICENSE | 20 ++++++++++++++++++++ README.md | 2 ++ 2 files changed, 22 insertions(+) create mode 100644 LICENSE create mode 100644 README.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..9772ab9c --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Steven Fackler + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..2a55142d --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +rust-ssl +======== From 6afafafe60123be9540ee02109b42d1c291ab1af Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Fri, 4 Oct 2013 20:46:27 -0700 Subject: [PATCH 02/49] Basic library initialization --- .gitignore | 4 ++++ src/ssl/lib.rs | 18 ++++++++++++++++++ src/ssl/test.rs | 6 ++++++ 3 files changed, 28 insertions(+) create mode 100644 .gitignore create mode 100644 src/ssl/lib.rs create mode 100644 src/ssl/test.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..1d7e5da7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/.rust/ +/bin/ +/build/ +/lib/ diff --git a/src/ssl/lib.rs b/src/ssl/lib.rs new file mode 100644 index 00000000..d52a0cfa --- /dev/null +++ b/src/ssl/lib.rs @@ -0,0 +1,18 @@ + +mod ffi { + use std::libc::{c_int}; + + #[link_args = "-lssl"] + extern "C" { + fn SSL_library_init() -> c_int; + fn SSL_load_error_strings(); + } +} + +#[fixed_stack_segment] +pub fn init() { + unsafe { + ffi::SSL_library_init(); + ffi::SSL_load_error_strings(); + } +} diff --git a/src/ssl/test.rs b/src/ssl/test.rs new file mode 100644 index 00000000..798f07f3 --- /dev/null +++ b/src/ssl/test.rs @@ -0,0 +1,6 @@ +extern mod ssl; + +#[test] +fn test_init_works() { + ssl::init(); +} From 58eb7ab5c4e29922e6cee4c21ca5420e3504a654 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 5 Oct 2013 10:56:36 -0700 Subject: [PATCH 03/49] Create contexts --- src/ssl/ffi.rs | 14 +++++++++++++ src/ssl/lib.rs | 55 ++++++++++++++++++++++++++++++++++++++++++------- src/ssl/test.rs | 6 ++++-- 3 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 src/ssl/ffi.rs diff --git a/src/ssl/ffi.rs b/src/ssl/ffi.rs new file mode 100644 index 00000000..788765fb --- /dev/null +++ b/src/ssl/ffi.rs @@ -0,0 +1,14 @@ +use std::libc::{c_int, c_void}; + +pub type SSL_CTX = c_void; +pub type SSL_METHOD = c_void; + +#[link_args = "-lssl"] +extern "C" { + fn SSL_library_init() -> c_int; + fn SSL_load_error_strings(); + + fn SSL_CTX_new(method: *SSL_METHOD) -> *SSL_CTX; + fn SSLv23_method() -> *SSL_METHOD; + fn SSL_CTX_free(ctx: *SSL_CTX); +} diff --git a/src/ssl/lib.rs b/src/ssl/lib.rs index d52a0cfa..a7c5588f 100644 --- a/src/ssl/lib.rs +++ b/src/ssl/lib.rs @@ -1,18 +1,57 @@ +use std::unstable::atomics::{AtomicBool, INIT_ATOMIC_BOOL, Acquire, Release}; +use std::task; -mod ffi { - use std::libc::{c_int}; +mod ffi; - #[link_args = "-lssl"] - extern "C" { - fn SSL_library_init() -> c_int; - fn SSL_load_error_strings(); - } -} +static mut STARTED_INIT: AtomicBool = INIT_ATOMIC_BOOL; +static mut FINISHED_INIT: AtomicBool = INIT_ATOMIC_BOOL; #[fixed_stack_segment] pub fn init() { unsafe { + if STARTED_INIT.swap(true, Acquire) { + while !FINISHED_INIT.load(Release) { + task::deschedule(); + } + return; + } + ffi::SSL_library_init(); ffi::SSL_load_error_strings(); + FINISHED_INIT.store(true, Release); + } +} + +pub enum SslMethod { + Sslv23 +} + +impl SslMethod { + #[fixed_stack_segment] + unsafe fn to_raw(&self) -> *ffi::SSL_METHOD { + match *self { + Sslv23 => ffi::SSLv23_method() + } + } +} + +pub struct SslCtx { + priv ctx: *ffi::SSL_CTX +} + +impl Drop for SslCtx { + #[fixed_stack_segment] + fn drop(&mut self) { + unsafe { ffi::SSL_CTX_free(self.ctx); } + } +} + +impl SslCtx { + #[fixed_stack_segment] + pub fn new(method: SslMethod) -> SslCtx { + init(); + SslCtx { + ctx: unsafe { ffi::SSL_CTX_new(method.to_raw()) } + } } } diff --git a/src/ssl/test.rs b/src/ssl/test.rs index 798f07f3..d54613ee 100644 --- a/src/ssl/test.rs +++ b/src/ssl/test.rs @@ -1,6 +1,8 @@ extern mod ssl; +use ssl::{Sslv23, SslCtx}; + #[test] -fn test_init_works() { - ssl::init(); +fn test_new_ctx() { + SslCtx::new(Sslv23); } From d4d5547a7990749b554cc8e2b8b42a172ee98e5e Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 5 Oct 2013 13:56:59 -0700 Subject: [PATCH 04/49] SslStream sketch --- src/ssl/ffi.rs | 29 ++++++++++---- src/ssl/lib.rs | 102 +++++++++++++++++++++++++++++++++++++++++++++--- src/ssl/test.rs | 10 ++++- 3 files changed, 128 insertions(+), 13 deletions(-) diff --git a/src/ssl/ffi.rs b/src/ssl/ffi.rs index 788765fb..c3144545 100644 --- a/src/ssl/ffi.rs +++ b/src/ssl/ffi.rs @@ -1,14 +1,29 @@ +#[doc(hidden)]; + use std::libc::{c_int, c_void}; pub type SSL_CTX = c_void; pub type SSL_METHOD = c_void; +pub type SSL = c_void; +pub type BIO = c_void; +pub type BIO_METHOD = c_void; #[link_args = "-lssl"] -extern "C" { - fn SSL_library_init() -> c_int; - fn SSL_load_error_strings(); +extern "C" { } - fn SSL_CTX_new(method: *SSL_METHOD) -> *SSL_CTX; - fn SSLv23_method() -> *SSL_METHOD; - fn SSL_CTX_free(ctx: *SSL_CTX); -} +externfn!(fn SSL_library_init() -> c_int) +externfn!(fn SSL_load_error_strings()) + +externfn!(fn SSLv23_method() -> *SSL_METHOD) +externfn!(fn SSL_CTX_new(method: *SSL_METHOD) -> *SSL_CTX) +externfn!(fn SSL_CTX_free(ctx: *SSL_CTX)) + +externfn!(fn SSL_new(ctx: *SSL_CTX) -> *SSL) +externfn!(fn SSL_free(ssl: *SSL)) +externfn!(fn SSL_set_bio(ssl: *SSL, rbio: *BIO, wbio: *BIO)) +externfn!(fn SSL_set_connect_state(ssl: *SSL)) +externfn!(fn SSL_do_handshake(ssl: *SSL)) + +externfn!(fn BIO_s_mem() -> *BIO_METHOD) +externfn!(fn BIO_new(type_: *BIO_METHOD) -> *BIO) +externfn!(fn BIO_free(a: *BIO) -> c_int) diff --git a/src/ssl/lib.rs b/src/ssl/lib.rs index a7c5588f..a65d8eac 100644 --- a/src/ssl/lib.rs +++ b/src/ssl/lib.rs @@ -1,12 +1,13 @@ +use std::rt::io::{Stream, Decorator}; use std::unstable::atomics::{AtomicBool, INIT_ATOMIC_BOOL, Acquire, Release}; use std::task; +use std::ptr; mod ffi; static mut STARTED_INIT: AtomicBool = INIT_ATOMIC_BOOL; static mut FINISHED_INIT: AtomicBool = INIT_ATOMIC_BOOL; -#[fixed_stack_segment] pub fn init() { unsafe { if STARTED_INIT.swap(true, Acquire) { @@ -27,7 +28,6 @@ pub enum SslMethod { } impl SslMethod { - #[fixed_stack_segment] unsafe fn to_raw(&self) -> *ffi::SSL_METHOD { match *self { Sslv23 => ffi::SSLv23_method() @@ -40,18 +40,110 @@ pub struct SslCtx { } impl Drop for SslCtx { - #[fixed_stack_segment] fn drop(&mut self) { unsafe { ffi::SSL_CTX_free(self.ctx); } } } impl SslCtx { - #[fixed_stack_segment] pub fn new(method: SslMethod) -> SslCtx { init(); + + let ctx = unsafe { ffi::SSL_CTX_new(method.to_raw()) }; + assert!(ctx != ptr::null()); + SslCtx { - ctx: unsafe { ffi::SSL_CTX_new(method.to_raw()) } + ctx: ctx } } } + +struct Ssl { + ssl: *ffi::SSL +} + +impl Drop for Ssl { + fn drop(&mut self) { + unsafe { ffi::SSL_free(self.ssl); } + } +} + +impl Ssl { + fn new(ctx: &SslCtx) -> Ssl { + let ssl = unsafe { ffi::SSL_new(ctx.ctx) }; + assert!(ssl != ptr::null()); + + Ssl { ssl: ssl } + } + + fn set_bio(&self, rbio: &MemBio, wbio: &MemBio) { + unsafe { ffi::SSL_set_bio(self.ssl, rbio.bio, wbio.bio); } + } + + fn set_connect_state(&self) { + unsafe { ffi::SSL_set_connect_state(self.ssl); } + } +} + +struct MemBio { + bio: *ffi::BIO +} + +impl Drop for MemBio { + fn drop(&mut self) { + unsafe { ffi::BIO_free(self.bio); } + } +} + +impl MemBio { + fn new() -> MemBio { + let bio = unsafe { ffi::BIO_new(ffi::BIO_s_mem()) }; + assert!(bio != ptr::null()); + + MemBio { bio: bio } + } +} + +pub struct SslStream { + priv ctx: SslCtx, + priv ssl: Ssl, + priv rbio: MemBio, + priv wbio: MemBio, + priv stream: S +} + +impl SslStream { + pub fn new(ctx: SslCtx, stream: S) -> SslStream { + let ssl = Ssl::new(&ctx); + + let rbio = MemBio::new(); + let wbio = MemBio::new(); + + ssl.set_bio(&rbio, &wbio); + ssl.set_connect_state(); + + let stream = SslStream { + ctx: ctx, + ssl: ssl, + rbio: rbio, + wbio: wbio, + stream: stream + } + + stream + } +} + +impl Decorator for SslStream { + fn inner(self) -> S { + self.stream + } + + fn inner_ref<'a>(&'a self) -> &'a S { + &self.stream + } + + fn inner_mut_ref<'a>(&'a mut self) -> &'a mut S { + &mut self.stream + } +} diff --git a/src/ssl/test.rs b/src/ssl/test.rs index d54613ee..501c8626 100644 --- a/src/ssl/test.rs +++ b/src/ssl/test.rs @@ -1,8 +1,16 @@ extern mod ssl; -use ssl::{Sslv23, SslCtx}; +use std::rt::io::net::tcp::TcpStream; + +use ssl::{Sslv23, SslCtx, SslStream}; #[test] fn test_new_ctx() { SslCtx::new(Sslv23); } + +#[test] +fn test_new_sslstream() { + let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()); + let stream = SslStream::new(SslCtx::new(Sslv23), stream); +} From d04c4ef435949622d6784d42c5b9ce1d4d5bc6d2 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 5 Oct 2013 15:49:46 -0700 Subject: [PATCH 05/49] Connect working --- src/ssl/ffi.rs | 18 ++++++++-- src/ssl/lib.rs | 94 +++++++++++++++++++++++++++++++++++++++++++++++-- src/ssl/test.rs | 2 +- 3 files changed, 109 insertions(+), 5 deletions(-) diff --git a/src/ssl/ffi.rs b/src/ssl/ffi.rs index c3144545..07591b78 100644 --- a/src/ssl/ffi.rs +++ b/src/ssl/ffi.rs @@ -1,13 +1,24 @@ #[doc(hidden)]; -use std::libc::{c_int, c_void}; +use std::libc::{c_int, c_long, c_void}; +// openssl/ssl.h pub type SSL_CTX = c_void; pub type SSL_METHOD = c_void; pub type SSL = c_void; pub type BIO = c_void; pub type BIO_METHOD = c_void; +pub static SSL_ERROR_NONE: c_int = 0; +pub static SSL_ERROR_SSL: c_int = 1; +pub static SSL_ERROR_WANT_READ: c_int = 2; +pub static SSL_ERROR_WANT_WRITE: c_int = 3; +pub static SSL_ERROR_WANT_X509_LOOKUP: c_int = 4; +pub static SSL_ERROR_SYSCALL: c_int = 5; +pub static SSL_ERROR_ZERO_RETURN: c_int = 6; +pub static SSL_ERROR_WANT_CONNECT: c_int = 7; +pub static SSL_ERROR_WANT_ACCEPT: c_int = 8; + #[link_args = "-lssl"] extern "C" { } @@ -22,8 +33,11 @@ externfn!(fn SSL_new(ctx: *SSL_CTX) -> *SSL) externfn!(fn SSL_free(ssl: *SSL)) externfn!(fn SSL_set_bio(ssl: *SSL, rbio: *BIO, wbio: *BIO)) externfn!(fn SSL_set_connect_state(ssl: *SSL)) -externfn!(fn SSL_do_handshake(ssl: *SSL)) +externfn!(fn SSL_connect(ssl: *SSL) -> c_int) +externfn!(fn SSL_get_error(ssl: *SSL, ret: c_int) -> c_int) externfn!(fn BIO_s_mem() -> *BIO_METHOD) externfn!(fn BIO_new(type_: *BIO_METHOD) -> *BIO) externfn!(fn BIO_free(a: *BIO) -> c_int) +externfn!(fn BIO_read(b: *BIO, buf: *c_void, len: c_int) -> c_int) +externfn!(fn BIO_write(b: *BIO, buf: *c_void, len: c_int) -> c_int) diff --git a/src/ssl/lib.rs b/src/ssl/lib.rs index a65d8eac..401a7b3d 100644 --- a/src/ssl/lib.rs +++ b/src/ssl/lib.rs @@ -2,6 +2,8 @@ use std::rt::io::{Stream, Decorator}; use std::unstable::atomics::{AtomicBool, INIT_ATOMIC_BOOL, Acquire, Release}; use std::task; use std::ptr; +use std::vec; +use std::libc::{c_int, c_void}; mod ffi; @@ -68,6 +70,17 @@ impl Drop for Ssl { } } +enum SslError { + ErrorNone, + ErrorSsl, + ErrorWantRead, + ErrorWantWrite, + ErrorWantX509Lookup, + ErrorZeroReturn, + ErrorWantConnect, + ErrorWantAccept, +} + impl Ssl { fn new(ctx: &SslCtx) -> Ssl { let ssl = unsafe { ffi::SSL_new(ctx.ctx) }; @@ -83,6 +96,24 @@ impl Ssl { fn set_connect_state(&self) { unsafe { ffi::SSL_set_connect_state(self.ssl); } } + + fn connect(&self) -> int { + unsafe { ffi::SSL_connect(self.ssl) as int } + } + + fn get_error(&self, ret: int) -> SslError { + match unsafe { ffi::SSL_get_error(self.ssl, ret as c_int) } { + ffi::SSL_ERROR_NONE => ErrorNone, + ffi::SSL_ERROR_SSL => ErrorSsl, + ffi::SSL_ERROR_WANT_READ => ErrorWantRead, + ffi::SSL_ERROR_WANT_WRITE => ErrorWantWrite, + ffi::SSL_ERROR_WANT_X509_LOOKUP => ErrorWantX509Lookup, + ffi::SSL_ERROR_ZERO_RETURN => ErrorZeroReturn, + ffi::SSL_ERROR_WANT_CONNECT => ErrorWantConnect, + ffi::SSL_ERROR_WANT_ACCEPT => ErrorWantAccept, + _ => unreachable!() + } + } } struct MemBio { @@ -102,11 +133,34 @@ impl MemBio { MemBio { bio: bio } } + + fn write(&self, buf: &[u8]) { + unsafe { + let ret = ffi::BIO_write(self.bio, + vec::raw::to_ptr(buf) as *c_void, + buf.len() as c_int); + if ret < 0 { + fail2!("write returned {}", ret); + } + } + } + + fn read(&self, buf: &[u8]) -> uint { + unsafe { + let ret = ffi::BIO_read(self.bio, vec::raw::to_ptr(buf) as *c_void, + buf.len() as c_int); + if ret < 0 { + fail2!("read returned {}", ret); + } + ret as uint + } + } } pub struct SslStream { priv ctx: SslCtx, priv ssl: Ssl, + priv buf: ~[u8], priv rbio: MemBio, priv wbio: MemBio, priv stream: S @@ -122,16 +176,52 @@ impl SslStream { ssl.set_bio(&rbio, &wbio); ssl.set_connect_state(); - let stream = SslStream { + let mut stream = SslStream { ctx: ctx, ssl: ssl, + buf: vec::from_elem(16 * 1024, 0u8), rbio: rbio, wbio: wbio, stream: stream - } + }; + + stream.connect(); stream } + + fn connect(&mut self) { + info!("in connect"); + loop { + let ret = self.ssl.connect(); + info2!("connect returned {}", ret); + if ret == 1 { + return; + } + + match self.ssl.get_error(ret) { + ErrorWantRead => { + info2!("want read"); + self.flush(); + match self.stream.read(self.buf) { + Some(len) => self.rbio.write(self.buf.slice_to(len)), + None => unreachable!() + } + } + ErrorWantWrite => { + info2!("want write"); + self.flush(); + } + _ => unreachable!() + } + } + } + + fn flush(&mut self) { + let len = self.wbio.read(self.buf); + self.stream.write(self.buf.slice_to(len)); + self.stream.flush(); + } } impl Decorator for SslStream { diff --git a/src/ssl/test.rs b/src/ssl/test.rs index 501c8626..a86772a7 100644 --- a/src/ssl/test.rs +++ b/src/ssl/test.rs @@ -12,5 +12,5 @@ fn test_new_ctx() { #[test] fn test_new_sslstream() { let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()); - let stream = SslStream::new(SslCtx::new(Sslv23), stream); + SslStream::new(SslCtx::new(Sslv23), stream); } From a7dab3624e8424bd6c498e7ef14509dfd2a2434f Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Thu, 19 Sep 2013 12:54:01 -0700 Subject: [PATCH 06/49] Sketched out implementation doesn't work --- src/ssl/ffi.rs | 4 ++- src/ssl/lib.rs | 93 +++++++++++++++++++++++++++++++++++++++---------- src/ssl/test.rs | 10 ++++++ 3 files changed, 87 insertions(+), 20 deletions(-) diff --git a/src/ssl/ffi.rs b/src/ssl/ffi.rs index 07591b78..e12bb3c2 100644 --- a/src/ssl/ffi.rs +++ b/src/ssl/ffi.rs @@ -1,6 +1,6 @@ #[doc(hidden)]; -use std::libc::{c_int, c_long, c_void}; +use std::libc::{c_int, c_void}; // openssl/ssl.h pub type SSL_CTX = c_void; @@ -35,6 +35,8 @@ externfn!(fn SSL_set_bio(ssl: *SSL, rbio: *BIO, wbio: *BIO)) externfn!(fn SSL_set_connect_state(ssl: *SSL)) externfn!(fn SSL_connect(ssl: *SSL) -> c_int) 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_write(ssl: *SSL, buf: *c_void, num: c_int) -> c_int) externfn!(fn BIO_s_mem() -> *BIO_METHOD) externfn!(fn BIO_new(type_: *BIO_METHOD) -> *BIO) diff --git a/src/ssl/lib.rs b/src/ssl/lib.rs index 401a7b3d..5cc59330 100644 --- a/src/ssl/lib.rs +++ b/src/ssl/lib.rs @@ -1,4 +1,4 @@ -use std::rt::io::{Stream, Decorator}; +use std::rt::io::{Reader, Writer, Stream, Decorator}; use std::unstable::atomics::{AtomicBool, INIT_ATOMIC_BOOL, Acquire, Release}; use std::task; use std::ptr; @@ -70,6 +70,7 @@ impl Drop for Ssl { } } +#[deriving(Eq, TotalEq, ToStr)] enum SslError { ErrorNone, ErrorSsl, @@ -114,6 +115,20 @@ impl Ssl { _ => unreachable!() } } + + fn read(&self, buf: &[u8]) -> int { + unsafe { + ffi::SSL_read(self.ssl, vec::raw::to_ptr(buf) as *c_void, + buf.len() as c_int) as int + } + } + + fn write(&self, buf: &[u8]) -> int { + unsafe { + ffi::SSL_write(self.ssl, vec::raw::to_ptr(buf) as *c_void, + buf.len() as c_int) as int + } + } } struct MemBio { @@ -150,9 +165,10 @@ impl MemBio { let ret = ffi::BIO_read(self.bio, vec::raw::to_ptr(buf) as *c_void, buf.len() as c_int); if ret < 0 { - fail2!("read returned {}", ret); + 0 + } else { + ret as uint } - ret as uint } } } @@ -179,47 +195,86 @@ impl SslStream { let mut stream = SslStream { ctx: ctx, ssl: ssl, + // Max record size for SSLv3/TLSv1 is 16k buf: vec::from_elem(16 * 1024, 0u8), rbio: rbio, wbio: wbio, stream: stream }; - stream.connect(); + do stream.in_retry_wrapper |ssl| { + ssl.ssl.connect() + }; stream } - fn connect(&mut self) { - info!("in connect"); + fn in_retry_wrapper(&mut self, blk: &fn(&mut SslStream) -> int) + -> Result { loop { - let ret = self.ssl.connect(); - info2!("connect returned {}", ret); - if ret == 1 { - return; + let ret = blk(self); + if ret > 0 { + return Ok(ret); } match self.ssl.get_error(ret) { ErrorWantRead => { - info2!("want read"); self.flush(); match self.stream.read(self.buf) { Some(len) => self.rbio.write(self.buf.slice_to(len)), - None => unreachable!() + None => unreachable!() // FIXME } } - ErrorWantWrite => { - info2!("want write"); - self.flush(); - } - _ => unreachable!() + ErrorWantWrite => self.flush(), + err => return Err(err) } } } + fn write_through(&mut self) { + loop { + let len = self.wbio.read(self.buf); + if len == 0 { + return; + } + self.stream.write(self.buf.slice_to(len)); + } + } +} + +impl Reader for SslStream { + fn read(&mut self, buf: &mut [u8]) -> Option { + let ret = do self.in_retry_wrapper |ssl| { + ssl.ssl.read(buf) + }; + + match ret { + Ok(num) => Some(num as uint), + Err(_) => None + } + } + + fn eof(&mut self) -> bool { + self.stream.eof() + } +} + +impl Writer for SslStream { + fn write(&mut self, buf: &[u8]) { + let ret = do self.in_retry_wrapper |ssl| { + ssl.ssl.write(buf) + }; + + match ret { + Ok(_) => (), + Err(err) => fail2!("Write error: {}", err.to_str()) + } + + self.write_through(); + } + fn flush(&mut self) { - let len = self.wbio.read(self.buf); - self.stream.write(self.buf.slice_to(len)); + self.write_through(); self.stream.flush(); } } diff --git a/src/ssl/test.rs b/src/ssl/test.rs index a86772a7..7a6dc997 100644 --- a/src/ssl/test.rs +++ b/src/ssl/test.rs @@ -1,6 +1,8 @@ extern mod ssl; +use std::rt::io::{Writer}; use std::rt::io::net::tcp::TcpStream; +use std::vec; use ssl::{Sslv23, SslCtx, SslStream}; @@ -14,3 +16,11 @@ fn test_new_sslstream() { let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()); SslStream::new(SslCtx::new(Sslv23), stream); } + +#[test] +fn test_write() { + let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()); + let mut stream = SslStream::new(SslCtx::new(Sslv23), stream); + stream.write("hello".as_bytes()); + stream.flush(); +} From e2be42aa53623b8c460ec247ec5d2df36503cd78 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 5 Oct 2013 20:36:45 -0700 Subject: [PATCH 07/49] Stuff roughly working We can't shutdown in Drop since generic destructors are broken :( --- src/ssl/ffi.rs | 2 +- src/ssl/lib.rs | 53 +++++++++++++++++++++++++++++++------------------ src/ssl/test.rs | 9 ++++++--- 3 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/ssl/ffi.rs b/src/ssl/ffi.rs index e12bb3c2..f87625ba 100644 --- a/src/ssl/ffi.rs +++ b/src/ssl/ffi.rs @@ -37,9 +37,9 @@ externfn!(fn SSL_connect(ssl: *SSL) -> c_int) 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_write(ssl: *SSL, buf: *c_void, num: c_int) -> c_int) +externfn!(fn SSL_shutdown(ssl: *SSL) -> c_int) externfn!(fn BIO_s_mem() -> *BIO_METHOD) externfn!(fn BIO_new(type_: *BIO_METHOD) -> *BIO) -externfn!(fn BIO_free(a: *BIO) -> c_int) externfn!(fn BIO_read(b: *BIO, buf: *c_void, len: c_int) -> c_int) externfn!(fn BIO_write(b: *BIO, buf: *c_void, len: c_int) -> c_int) diff --git a/src/ssl/lib.rs b/src/ssl/lib.rs index 5cc59330..4d79152b 100644 --- a/src/ssl/lib.rs +++ b/src/ssl/lib.rs @@ -1,3 +1,5 @@ +#[link(name="ssl")]; + use std::rt::io::{Reader, Writer, Stream, Decorator}; use std::unstable::atomics::{AtomicBool, INIT_ATOMIC_BOOL, Acquire, Release}; use std::task; @@ -60,6 +62,19 @@ impl SslCtx { } } +#[deriving(Eq, TotalEq, ToStr)] +enum SslError { + ErrorNone, + ErrorSsl, + ErrorWantRead, + ErrorWantWrite, + ErrorWantX509Lookup, + ErrorSyscall, + ErrorZeroReturn, + ErrorWantConnect, + ErrorWantAccept, +} + struct Ssl { ssl: *ffi::SSL } @@ -70,18 +85,6 @@ impl Drop for Ssl { } } -#[deriving(Eq, TotalEq, ToStr)] -enum SslError { - ErrorNone, - ErrorSsl, - ErrorWantRead, - ErrorWantWrite, - ErrorWantX509Lookup, - ErrorZeroReturn, - ErrorWantConnect, - ErrorWantAccept, -} - impl Ssl { fn new(ctx: &SslCtx) -> Ssl { let ssl = unsafe { ffi::SSL_new(ctx.ctx) }; @@ -109,10 +112,11 @@ impl Ssl { ffi::SSL_ERROR_WANT_READ => ErrorWantRead, ffi::SSL_ERROR_WANT_WRITE => ErrorWantWrite, ffi::SSL_ERROR_WANT_X509_LOOKUP => ErrorWantX509Lookup, + ffi::SSL_ERROR_SYSCALL => ErrorSyscall, ffi::SSL_ERROR_ZERO_RETURN => ErrorZeroReturn, ffi::SSL_ERROR_WANT_CONNECT => ErrorWantConnect, ffi::SSL_ERROR_WANT_ACCEPT => ErrorWantAccept, - _ => unreachable!() + err => fail2!("Unknown error {}", err) } } @@ -129,18 +133,17 @@ impl Ssl { buf.len() as c_int) as int } } + + fn shutdown(&self) -> int { + unsafe { ffi::SSL_shutdown(self.ssl) as int } + } } +// BIOs are freed by SSL_free struct MemBio { bio: *ffi::BIO } -impl Drop for MemBio { - fn drop(&mut self) { - unsafe { ffi::BIO_free(self.bio); } - } -} - impl MemBio { fn new() -> MemBio { let bio = unsafe { ffi::BIO_new(ffi::BIO_s_mem()) }; @@ -240,6 +243,18 @@ impl SslStream { self.stream.write(self.buf.slice_to(len)); } } + + pub fn shutdown(&mut self) { + loop { + let ret = do self.in_retry_wrapper |ssl| { + ssl.ssl.shutdown() + }; + + if ret != Ok(0) { + break; + } + } + } } impl Reader for SslStream { diff --git a/src/ssl/test.rs b/src/ssl/test.rs index 7a6dc997..36969e1b 100644 --- a/src/ssl/test.rs +++ b/src/ssl/test.rs @@ -1,6 +1,6 @@ extern mod ssl; -use std::rt::io::{Writer}; +use std::rt::io::{Writer, Reader}; use std::rt::io::net::tcp::TcpStream; use std::vec; @@ -13,14 +13,17 @@ fn test_new_ctx() { #[test] fn test_new_sslstream() { - let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()); + let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); SslStream::new(SslCtx::new(Sslv23), stream); } #[test] fn test_write() { - let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()); + let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); let mut stream = SslStream::new(SslCtx::new(Sslv23), stream); stream.write("hello".as_bytes()); stream.flush(); + stream.write(" there".as_bytes()); + stream.flush(); + stream.shutdown(); } From 709ba4e7b0580b32208384c6cedc9d2bd13da828 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Wed, 9 Oct 2013 21:01:38 -0700 Subject: [PATCH 08/49] Semi-fix EOF --- src/ssl/lib.rs | 2 +- src/ssl/test.rs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/ssl/lib.rs b/src/ssl/lib.rs index 4d79152b..6d08e168 100644 --- a/src/ssl/lib.rs +++ b/src/ssl/lib.rs @@ -225,7 +225,7 @@ impl SslStream { self.flush(); match self.stream.read(self.buf) { Some(len) => self.rbio.write(self.buf.slice_to(len)), - None => unreachable!() // FIXME + None => return Err(ErrorZeroReturn) // FIXME } } ErrorWantWrite => self.flush(), diff --git a/src/ssl/test.rs b/src/ssl/test.rs index 36969e1b..3432e2a7 100644 --- a/src/ssl/test.rs +++ b/src/ssl/test.rs @@ -1,8 +1,10 @@ extern mod ssl; use std::rt::io::{Writer, Reader}; +use std::rt::io::extensions::{ReaderUtil}; use std::rt::io::net::tcp::TcpStream; use std::vec; +use std::str; use ssl::{Sslv23, SslCtx, SslStream}; @@ -27,3 +29,13 @@ fn test_write() { stream.flush(); stream.shutdown(); } + +#[test] +fn test_read() { + let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); + let mut stream = SslStream::new(SslCtx::new(Sslv23), stream); + stream.write("GET /\r\n\r\n".as_bytes()); + stream.flush(); + let buf = stream.read_to_end(); + print!("{}", str::from_utf8(buf)); +} From 8e2d5242a3f498e3c75a3e45e7fe2255ac96298f Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 12 Oct 2013 21:48:08 -0700 Subject: [PATCH 09/49] Start of cert verification --- src/ssl/ffi.rs | 11 +++++++++-- src/ssl/lib.rs | 20 ++++++++++++++++---- src/ssl/test.rs | 19 +++++++++++++++---- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/ssl/ffi.rs b/src/ssl/ffi.rs index f87625ba..daa02731 100644 --- a/src/ssl/ffi.rs +++ b/src/ssl/ffi.rs @@ -1,6 +1,6 @@ #[doc(hidden)]; -use std::libc::{c_int, c_void}; +use std::libc::{c_int, c_void, c_ulong, c_char}; // openssl/ssl.h pub type SSL_CTX = c_void; @@ -8,6 +8,7 @@ pub type SSL_METHOD = c_void; pub type SSL = c_void; pub type BIO = c_void; pub type BIO_METHOD = c_void; +pub type X509_STORE_CTX = c_void; pub static SSL_ERROR_NONE: c_int = 0; pub static SSL_ERROR_SSL: c_int = 1; @@ -19,15 +20,21 @@ pub static SSL_ERROR_ZERO_RETURN: c_int = 6; pub static SSL_ERROR_WANT_CONNECT: c_int = 7; pub static SSL_ERROR_WANT_ACCEPT: c_int = 8; +pub static SSL_VERIFY_NONE: c_int = 0; +pub static SSL_VERIFY_PEER: c_int = 1; + #[link_args = "-lssl"] extern "C" { } +externfn!(fn ERR_get_error() -> c_ulong) + externfn!(fn SSL_library_init() -> c_int) -externfn!(fn SSL_load_error_strings()) externfn!(fn SSLv23_method() -> *SSL_METHOD) externfn!(fn SSL_CTX_new(method: *SSL_METHOD) -> *SSL_CTX) externfn!(fn SSL_CTX_free(ctx: *SSL_CTX)) +externfn!(fn SSL_CTX_set_verify(ctx: *SSL_CTX, mode: c_int, + verify_callback: Option)) externfn!(fn SSL_new(ctx: *SSL_CTX) -> *SSL) externfn!(fn SSL_free(ssl: *SSL)) diff --git a/src/ssl/lib.rs b/src/ssl/lib.rs index 6d08e168..faf9e651 100644 --- a/src/ssl/lib.rs +++ b/src/ssl/lib.rs @@ -22,7 +22,6 @@ pub fn init() { } ffi::SSL_library_init(); - ffi::SSL_load_error_strings(); FINISHED_INIT.store(true, Release); } } @@ -60,6 +59,15 @@ impl SslCtx { ctx: ctx } } + + pub fn set_verify(&mut self, mode: SslVerifyMode) { + unsafe { ffi::SSL_CTX_set_verify(self.ctx, mode as c_int, None) } + } +} + +pub enum SslVerifyMode { + SslVerifyNone = ffi::SSL_VERIFY_NONE, + SslVerifyPeer = ffi::SSL_VERIFY_PEER } #[deriving(Eq, TotalEq, ToStr)] @@ -186,7 +194,7 @@ pub struct SslStream { } impl SslStream { - pub fn new(ctx: SslCtx, stream: S) -> SslStream { + pub fn new(ctx: SslCtx, stream: S) -> Result, uint> { let ssl = Ssl::new(&ctx); let rbio = MemBio::new(); @@ -205,11 +213,15 @@ impl SslStream { stream: stream }; - do stream.in_retry_wrapper |ssl| { + let ret = do stream.in_retry_wrapper |ssl| { ssl.ssl.connect() }; - stream + match ret { + Ok(_) => Ok(stream), + // FIXME + Err(_err) => Err(unsafe { ffi::ERR_get_error() as uint }) + } } fn in_retry_wrapper(&mut self, blk: &fn(&mut SslStream) -> int) diff --git a/src/ssl/test.rs b/src/ssl/test.rs index 3432e2a7..b97e6b00 100644 --- a/src/ssl/test.rs +++ b/src/ssl/test.rs @@ -6,7 +6,7 @@ use std::rt::io::net::tcp::TcpStream; use std::vec; use std::str; -use ssl::{Sslv23, SslCtx, SslStream}; +use ssl::{Sslv23, SslCtx, SslStream, SslVerifyPeer}; #[test] fn test_new_ctx() { @@ -16,13 +16,24 @@ fn test_new_ctx() { #[test] fn test_new_sslstream() { let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); - SslStream::new(SslCtx::new(Sslv23), stream); + SslStream::new(SslCtx::new(Sslv23), stream).unwrap(); +} + +#[test] +fn test_verify() { + let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); + let mut ctx = SslCtx::new(Sslv23); + ctx.set_verify(SslVerifyPeer); + match SslStream::new(ctx, stream) { + Ok(_) => fail2!("expected failure"), + Err(err) => println!("error {}", err) + } } #[test] fn test_write() { let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); - let mut stream = SslStream::new(SslCtx::new(Sslv23), stream); + let mut stream = SslStream::new(SslCtx::new(Sslv23), stream).unwrap(); stream.write("hello".as_bytes()); stream.flush(); stream.write(" there".as_bytes()); @@ -33,7 +44,7 @@ fn test_write() { #[test] fn test_read() { let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); - let mut stream = SslStream::new(SslCtx::new(Sslv23), stream); + let mut stream = SslStream::new(SslCtx::new(Sslv23), stream).unwrap(); stream.write("GET /\r\n\r\n".as_bytes()); stream.flush(); let buf = stream.read_to_end(); From 0fac64705eb7cbd1a314d7e9fb7301dfda19b760 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 13 Oct 2013 21:56:57 -0700 Subject: [PATCH 10/49] Clean up SslError conversion --- src/ssl/ffi.rs | 2 +- src/ssl/lib.rs | 37 +++++++++++++++---------------------- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/src/ssl/ffi.rs b/src/ssl/ffi.rs index daa02731..b8ac3968 100644 --- a/src/ssl/ffi.rs +++ b/src/ssl/ffi.rs @@ -34,7 +34,7 @@ externfn!(fn SSLv23_method() -> *SSL_METHOD) externfn!(fn SSL_CTX_new(method: *SSL_METHOD) -> *SSL_CTX) externfn!(fn SSL_CTX_free(ctx: *SSL_CTX)) externfn!(fn SSL_CTX_set_verify(ctx: *SSL_CTX, mode: c_int, - verify_callback: Option)) + verify_callback: Option c_int>)) externfn!(fn SSL_new(ctx: *SSL_CTX) -> *SSL) externfn!(fn SSL_free(ssl: *SSL)) diff --git a/src/ssl/lib.rs b/src/ssl/lib.rs index faf9e651..50565602 100644 --- a/src/ssl/lib.rs +++ b/src/ssl/lib.rs @@ -70,17 +70,17 @@ pub enum SslVerifyMode { SslVerifyPeer = ffi::SSL_VERIFY_PEER } -#[deriving(Eq, TotalEq, ToStr)] +#[deriving(Eq, FromPrimitive)] enum SslError { - ErrorNone, - ErrorSsl, - ErrorWantRead, - ErrorWantWrite, - ErrorWantX509Lookup, - ErrorSyscall, - ErrorZeroReturn, - ErrorWantConnect, - ErrorWantAccept, + ErrorNone = ffi::SSL_ERROR_NONE, + ErrorSsl = ffi::SSL_ERROR_SSL, + ErrorWantRead = ffi::SSL_ERROR_WANT_READ, + ErrorWantWrite = ffi::SSL_ERROR_WANT_WRITE, + ErrorWantX509Lookup = ffi::SSL_ERROR_WANT_X509_LOOKUP, + ErrorSyscall = ffi::SSL_ERROR_SYSCALL, + ErrorZeroReturn = ffi::SSL_ERROR_ZERO_RETURN, + ErrorWantConnect = ffi::SSL_ERROR_WANT_CONNECT, + ErrorWantAccept = ffi::SSL_ERROR_WANT_ACCEPT, } struct Ssl { @@ -114,17 +114,10 @@ impl Ssl { } fn get_error(&self, ret: int) -> SslError { - match unsafe { ffi::SSL_get_error(self.ssl, ret as c_int) } { - ffi::SSL_ERROR_NONE => ErrorNone, - ffi::SSL_ERROR_SSL => ErrorSsl, - ffi::SSL_ERROR_WANT_READ => ErrorWantRead, - ffi::SSL_ERROR_WANT_WRITE => ErrorWantWrite, - ffi::SSL_ERROR_WANT_X509_LOOKUP => ErrorWantX509Lookup, - ffi::SSL_ERROR_SYSCALL => ErrorSyscall, - ffi::SSL_ERROR_ZERO_RETURN => ErrorZeroReturn, - ffi::SSL_ERROR_WANT_CONNECT => ErrorWantConnect, - ffi::SSL_ERROR_WANT_ACCEPT => ErrorWantAccept, - err => fail2!("Unknown error {}", err) + let err = unsafe { ffi::SSL_get_error(self.ssl, ret as c_int) }; + match FromPrimitive::from_int(err as int) { + Some(err) => err, + None => fail2!("Unknown error {}", err) } } @@ -294,7 +287,7 @@ impl Writer for SslStream { match ret { Ok(_) => (), - Err(err) => fail2!("Write error: {}", err.to_str()) + Err(err) => fail2!("Write error: {:?}", err) } self.write_through(); From cf8f82036661b5b3816bd2307d4af668bdce3897 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 13 Oct 2013 22:46:47 -0700 Subject: [PATCH 11/49] Support for loading a trusted CA file --- src/ssl/ffi.rs | 2 ++ src/ssl/lib.rs | 7 +++++++ src/ssl/test.rs | 19 +++++++++++++++---- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/ssl/ffi.rs b/src/ssl/ffi.rs index b8ac3968..6c8d5bb1 100644 --- a/src/ssl/ffi.rs +++ b/src/ssl/ffi.rs @@ -35,6 +35,8 @@ externfn!(fn SSL_CTX_new(method: *SSL_METHOD) -> *SSL_CTX) externfn!(fn SSL_CTX_free(ctx: *SSL_CTX)) externfn!(fn SSL_CTX_set_verify(ctx: *SSL_CTX, mode: c_int, verify_callback: Option c_int>)) +externfn!(fn SSL_CTX_load_verify_locations(ctx: *SSL_CTX, CAfile: *c_char, + CApath: *c_char) -> c_int) externfn!(fn SSL_new(ctx: *SSL_CTX) -> *SSL) externfn!(fn SSL_free(ssl: *SSL)) diff --git a/src/ssl/lib.rs b/src/ssl/lib.rs index 50565602..8acf5940 100644 --- a/src/ssl/lib.rs +++ b/src/ssl/lib.rs @@ -63,6 +63,13 @@ impl SslCtx { pub fn set_verify(&mut self, mode: SslVerifyMode) { unsafe { ffi::SSL_CTX_set_verify(self.ctx, mode as c_int, None) } } + + pub fn set_verify_locations(&mut self, CAfile: &str) { + do CAfile.with_c_str |CAfile| { + unsafe { ffi::SSL_CTX_load_verify_locations(self.ctx, CAfile, + ptr::null()); } + } + } } pub enum SslVerifyMode { diff --git a/src/ssl/test.rs b/src/ssl/test.rs index b97e6b00..557f6dec 100644 --- a/src/ssl/test.rs +++ b/src/ssl/test.rs @@ -1,9 +1,8 @@ extern mod ssl; -use std::rt::io::{Writer, Reader}; -use std::rt::io::extensions::{ReaderUtil}; +use std::rt::io::Writer; +use std::rt::io::extensions::ReaderUtil; use std::rt::io::net::tcp::TcpStream; -use std::vec; use std::str; use ssl::{Sslv23, SslCtx, SslStream, SslVerifyPeer}; @@ -20,7 +19,7 @@ fn test_new_sslstream() { } #[test] -fn test_verify() { +fn test_verify_untrusted() { let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); let mut ctx = SslCtx::new(Sslv23); ctx.set_verify(SslVerifyPeer); @@ -30,6 +29,18 @@ fn test_verify() { } } +#[test] +fn test_verify_trusted() { + let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); + let mut ctx = SslCtx::new(Sslv23); + ctx.set_verify(SslVerifyPeer); + ctx.set_verify_locations("cert.pem"); + match SslStream::new(ctx, stream) { + Ok(_) => (), + Err(err) => fail2!("Expected success, got {:?}", err) + } +} + #[test] fn test_write() { let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); From 4e54c159b69c3007ed83d9d44e68d8c83e61b504 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 20 Oct 2013 21:32:19 -0700 Subject: [PATCH 12/49] Remove folder indirection --- src/ssl/ffi.rs => ffi.rs | 0 src/ssl/lib.rs => lib.rs | 0 src/ssl/test.rs => test.rs | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename src/ssl/ffi.rs => ffi.rs (100%) rename src/ssl/lib.rs => lib.rs (100%) rename src/ssl/test.rs => test.rs (100%) diff --git a/src/ssl/ffi.rs b/ffi.rs similarity index 100% rename from src/ssl/ffi.rs rename to ffi.rs diff --git a/src/ssl/lib.rs b/lib.rs similarity index 100% rename from src/ssl/lib.rs rename to lib.rs diff --git a/src/ssl/test.rs b/test.rs similarity index 100% rename from src/ssl/test.rs rename to test.rs From 448e8ebbf7062c434c7964a6a392ade7c03a1c6f Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 20 Oct 2013 21:49:22 -0700 Subject: [PATCH 13/49] Move tests to in-binary and fix link args --- ffi.rs | 2 +- lib.rs | 3 +++ test.rs => tests.rs | 4 +--- 3 files changed, 5 insertions(+), 4 deletions(-) rename test.rs => tests.rs (96%) diff --git a/ffi.rs b/ffi.rs index 6c8d5bb1..e63c63a1 100644 --- a/ffi.rs +++ b/ffi.rs @@ -23,7 +23,7 @@ pub static SSL_ERROR_WANT_ACCEPT: c_int = 8; pub static SSL_VERIFY_NONE: c_int = 0; pub static SSL_VERIFY_PEER: c_int = 1; -#[link_args = "-lssl"] +#[link_args = "-lssl -lcrypto"] extern "C" { } externfn!(fn ERR_get_error() -> c_ulong) diff --git a/lib.rs b/lib.rs index 8acf5940..fdf827ca 100644 --- a/lib.rs +++ b/lib.rs @@ -9,6 +9,9 @@ use std::libc::{c_int, c_void}; mod ffi; +#[cfg(test)] +mod tests; + static mut STARTED_INIT: AtomicBool = INIT_ATOMIC_BOOL; static mut FINISHED_INIT: AtomicBool = INIT_ATOMIC_BOOL; diff --git a/test.rs b/tests.rs similarity index 96% rename from test.rs rename to tests.rs index 557f6dec..639ce1b1 100644 --- a/test.rs +++ b/tests.rs @@ -1,11 +1,9 @@ -extern mod ssl; - use std::rt::io::Writer; use std::rt::io::extensions::ReaderUtil; use std::rt::io::net::tcp::TcpStream; use std::str; -use ssl::{Sslv23, SslCtx, SslStream, SslVerifyPeer}; +use super::{Sslv23, SslCtx, SslStream, SslVerifyPeer}; #[test] fn test_new_ctx() { From a42d5261f95a081f8b530fd2c1827082b89f7807 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 20 Oct 2013 22:24:01 -0700 Subject: [PATCH 14/49] Fill out the context methods --- ffi.rs | 4 ++++ lib.rs | 12 ++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/ffi.rs b/ffi.rs index e63c63a1..57adfaf4 100644 --- a/ffi.rs +++ b/ffi.rs @@ -30,7 +30,11 @@ externfn!(fn ERR_get_error() -> c_ulong) externfn!(fn SSL_library_init() -> c_int) +externfn!(fn SSLv2_method() -> *SSL_METHOD) +externfn!(fn SSLv3_method() -> *SSL_METHOD) +externfn!(fn TLSv1_method() -> *SSL_METHOD) externfn!(fn SSLv23_method() -> *SSL_METHOD) + externfn!(fn SSL_CTX_new(method: *SSL_METHOD) -> *SSL_CTX) externfn!(fn SSL_CTX_free(ctx: *SSL_CTX)) externfn!(fn SSL_CTX_set_verify(ctx: *SSL_CTX, mode: c_int, diff --git a/lib.rs b/lib.rs index fdf827ca..9c5f5845 100644 --- a/lib.rs +++ b/lib.rs @@ -1,5 +1,3 @@ -#[link(name="ssl")]; - use std::rt::io::{Reader, Writer, Stream, Decorator}; use std::unstable::atomics::{AtomicBool, INIT_ATOMIC_BOOL, Acquire, Release}; use std::task; @@ -30,12 +28,18 @@ pub fn init() { } pub enum SslMethod { + Sslv2, + Sslv3, + Tlsv1, Sslv23 } impl SslMethod { - unsafe fn to_raw(&self) -> *ffi::SSL_METHOD { + unsafe fn to_fn(&self) -> *ffi::SSL_METHOD { match *self { + Sslv2 => ffi::SSLv2_method(), + Sslv3 => ffi::SSLv3_method(), + Tlsv1 => ffi::TLSv1_method(), Sslv23 => ffi::SSLv23_method() } } @@ -55,7 +59,7 @@ impl SslCtx { pub fn new(method: SslMethod) -> SslCtx { init(); - let ctx = unsafe { ffi::SSL_CTX_new(method.to_raw()) }; + let ctx = unsafe { ffi::SSL_CTX_new(method.to_fn()) }; assert!(ctx != ptr::null()); SslCtx { From 302590c2b5fc75da807157073e3bd393d89b385e Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 21 Oct 2013 22:51:18 -0700 Subject: [PATCH 15/49] Major rewrite for better error handling --- error.rs | 18 +++ ffi.rs | 3 + lib.rs | 369 ++++++++++++++++++++++++++++--------------------------- tests.rs | 23 ++-- 4 files changed, 223 insertions(+), 190 deletions(-) create mode 100644 error.rs diff --git a/error.rs b/error.rs new file mode 100644 index 00000000..b5fe0b6b --- /dev/null +++ b/error.rs @@ -0,0 +1,18 @@ +use std::libc::c_ulong; + +use super::ffi; + +pub enum SslError { + StreamEof, + SslSessionClosed, + UnknownError(c_ulong) +} + +impl SslError { + pub fn get() -> Option { + match unsafe { ffi::ERR_get_error() } { + 0 => None, + err => Some(UnknownError(err)) + } + } +} diff --git a/ffi.rs b/ffi.rs index 57adfaf4..7fec9ad0 100644 --- a/ffi.rs +++ b/ffi.rs @@ -45,6 +45,8 @@ externfn!(fn SSL_CTX_load_verify_locations(ctx: *SSL_CTX, CAfile: *c_char, externfn!(fn SSL_new(ctx: *SSL_CTX) -> *SSL) externfn!(fn SSL_free(ssl: *SSL)) externfn!(fn SSL_set_bio(ssl: *SSL, rbio: *BIO, wbio: *BIO)) +externfn!(fn SSL_get_rbio(ssl: *SSL) -> *BIO) +externfn!(fn SSL_get_wbio(ssl: *SSL) -> *BIO) externfn!(fn SSL_set_connect_state(ssl: *SSL)) externfn!(fn SSL_connect(ssl: *SSL) -> c_int) externfn!(fn SSL_get_error(ssl: *SSL, ret: c_int) -> c_int) @@ -54,5 +56,6 @@ externfn!(fn SSL_shutdown(ssl: *SSL) -> c_int) externfn!(fn BIO_s_mem() -> *BIO_METHOD) externfn!(fn BIO_new(type_: *BIO_METHOD) -> *BIO) +externfn!(fn BIO_free_all(a: *BIO)) externfn!(fn BIO_read(b: *BIO, buf: *c_void, len: c_int) -> c_int) externfn!(fn BIO_write(b: *BIO, buf: *c_void, len: c_int) -> c_int) diff --git a/lib.rs b/lib.rs index 9c5f5845..ce3cc8db 100644 --- a/lib.rs +++ b/lib.rs @@ -1,15 +1,19 @@ -use std::rt::io::{Reader, Writer, Stream, Decorator}; -use std::unstable::atomics::{AtomicBool, INIT_ATOMIC_BOOL, Acquire, Release}; -use std::task; -use std::ptr; -use std::vec; use std::libc::{c_int, c_void}; +use std::ptr; +use std::task; +use std::unstable::atomics::{AtomicBool, INIT_ATOMIC_BOOL, Acquire, Release}; +use std::rt::io::{Stream, Reader, Writer, Decorator}; +use std::vec; -mod ffi; +use error::{SslError, SslSessionClosed, StreamEof}; + +pub mod error; #[cfg(test)] mod tests; +mod ffi; + static mut STARTED_INIT: AtomicBool = INIT_ATOMIC_BOOL; static mut FINISHED_INIT: AtomicBool = INIT_ATOMIC_BOOL; @@ -35,7 +39,7 @@ pub enum SslMethod { } impl SslMethod { - unsafe fn to_fn(&self) -> *ffi::SSL_METHOD { + unsafe fn to_raw(&self) -> *ffi::SSL_METHOD { match *self { Sslv2 => ffi::SSLv2_method(), Sslv3 => ffi::SSLv3_method(), @@ -45,47 +49,140 @@ impl SslMethod { } } -pub struct SslCtx { +pub enum SslVerifyMode { + SslVerifyPeer = ffi::SSL_VERIFY_PEER, + SslVerifyNone = ffi::SSL_VERIFY_NONE +} + +pub struct SslContext { priv ctx: *ffi::SSL_CTX } -impl Drop for SslCtx { +impl Drop for SslContext { fn drop(&mut self) { - unsafe { ffi::SSL_CTX_free(self.ctx); } + unsafe { ffi::SSL_CTX_free(self.ctx) } } } -impl SslCtx { - pub fn new(method: SslMethod) -> SslCtx { +impl SslContext { + pub fn try_new(method: SslMethod) -> Result { init(); - let ctx = unsafe { ffi::SSL_CTX_new(method.to_fn()) }; - assert!(ctx != ptr::null()); + let ctx = unsafe { ffi::SSL_CTX_new(method.to_raw()) }; + if ctx == ptr::null() { + return Err(SslError::get().unwrap()); + } - SslCtx { - ctx: ctx + Ok(SslContext { ctx: ctx }) + } + + pub fn new(method: SslMethod) -> SslContext { + match SslContext::try_new(method) { + Ok(ctx) => ctx, + Err(err) => fail!("Error creating SSL context: {:?}", err) } } + // TODO: support callback (see SSL_CTX_set_ex_data) pub fn set_verify(&mut self, mode: SslVerifyMode) { - unsafe { ffi::SSL_CTX_set_verify(self.ctx, mode as c_int, None) } + unsafe { + ffi::SSL_CTX_set_verify(self.ctx, mode as c_int, None); + } } - pub fn set_verify_locations(&mut self, CAfile: &str) { - do CAfile.with_c_str |CAfile| { - unsafe { ffi::SSL_CTX_load_verify_locations(self.ctx, CAfile, - ptr::null()); } + pub fn set_CA_file(&mut self, file: &str) -> Option { + let ret = do file.with_c_str |file| { + unsafe { + ffi::SSL_CTX_load_verify_locations(self.ctx, file, ptr::null()) + } + }; + + if ret == 0 { + Some(SslError::get().unwrap()) + } else { + None } } } -pub enum SslVerifyMode { - SslVerifyNone = ffi::SSL_VERIFY_NONE, - SslVerifyPeer = ffi::SSL_VERIFY_PEER +struct Ssl { + ssl: *ffi::SSL } -#[deriving(Eq, FromPrimitive)] -enum SslError { +impl Drop for Ssl { + fn drop(&mut self) { + unsafe { ffi::SSL_free(self.ssl) } + } +} + +impl Ssl { + fn try_new(ctx: &SslContext) -> Result { + let ssl = unsafe { ffi::SSL_new(ctx.ctx) }; + if ssl == ptr::null() { + return Err(SslError::get().unwrap()); + } + let ssl = Ssl { ssl: ssl }; + + let rbio = unsafe { ffi::BIO_new(ffi::BIO_s_mem()) }; + if rbio == ptr::null() { + return Err(SslError::get().unwrap()); + } + + let wbio = unsafe { ffi::BIO_new(ffi::BIO_s_mem()) }; + if wbio == ptr::null() { + unsafe { ffi::BIO_free_all(rbio) } + return Err(SslError::get().unwrap()); + } + + unsafe { ffi::SSL_set_bio(ssl.ssl, rbio, wbio) } + Ok(ssl) + } + + fn get_rbio<'a>(&'a self) -> MemBio<'a> { + let bio = unsafe { ffi::SSL_get_rbio(self.ssl) }; + assert!(bio != ptr::null()); + + MemBio { + ssl: self, + bio: bio + } + } + + fn get_wbio<'a>(&'a self) -> MemBio<'a> { + let bio = unsafe { ffi::SSL_get_wbio(self.ssl) }; + assert!(bio != ptr::null()); + + MemBio { + ssl: self, + bio: bio + } + } + + fn connect(&self) -> c_int { + unsafe { ffi::SSL_connect(self.ssl) } + } + + fn read(&self, buf: &mut [u8]) -> c_int { + unsafe { ffi::SSL_read(self.ssl, vec::raw::to_ptr(buf) as *c_void, + buf.len() as c_int) } + } + + fn write(&self, buf: &[u8]) -> c_int { + unsafe { ffi::SSL_write(self.ssl, vec::raw::to_ptr(buf) as *c_void, + buf.len() as c_int) } + } + + fn get_error(&self, ret: c_int) -> LibSslError { + let err = unsafe { ffi::SSL_get_error(self.ssl, ret) }; + match FromPrimitive::from_int(err as int) { + Some(err) => err, + None => unreachable!() + } + } +} + +#[deriving(FromPrimitive)] +enum LibSslError { ErrorNone = ffi::SSL_ERROR_NONE, ErrorSsl = ffi::SSL_ERROR_SSL, ErrorWantRead = ffi::SSL_ERROR_WANT_READ, @@ -97,144 +194,72 @@ enum SslError { ErrorWantAccept = ffi::SSL_ERROR_WANT_ACCEPT, } -struct Ssl { - ssl: *ffi::SSL -} - -impl Drop for Ssl { - fn drop(&mut self) { - unsafe { ffi::SSL_free(self.ssl); } - } -} - -impl Ssl { - fn new(ctx: &SslCtx) -> Ssl { - let ssl = unsafe { ffi::SSL_new(ctx.ctx) }; - assert!(ssl != ptr::null()); - - Ssl { ssl: ssl } - } - - fn set_bio(&self, rbio: &MemBio, wbio: &MemBio) { - unsafe { ffi::SSL_set_bio(self.ssl, rbio.bio, wbio.bio); } - } - - fn set_connect_state(&self) { - unsafe { ffi::SSL_set_connect_state(self.ssl); } - } - - fn connect(&self) -> int { - unsafe { ffi::SSL_connect(self.ssl) as int } - } - - fn get_error(&self, ret: int) -> SslError { - let err = unsafe { ffi::SSL_get_error(self.ssl, ret as c_int) }; - match FromPrimitive::from_int(err as int) { - Some(err) => err, - None => fail2!("Unknown error {}", err) - } - } - - fn read(&self, buf: &[u8]) -> int { - unsafe { - ffi::SSL_read(self.ssl, vec::raw::to_ptr(buf) as *c_void, - buf.len() as c_int) as int - } - } - - fn write(&self, buf: &[u8]) -> int { - unsafe { - ffi::SSL_write(self.ssl, vec::raw::to_ptr(buf) as *c_void, - buf.len() as c_int) as int - } - } - - fn shutdown(&self) -> int { - unsafe { ffi::SSL_shutdown(self.ssl) as int } - } -} - -// BIOs are freed by SSL_free -struct MemBio { +struct MemBio<'self> { + ssl: &'self Ssl, bio: *ffi::BIO } -impl MemBio { - fn new() -> MemBio { - let bio = unsafe { ffi::BIO_new(ffi::BIO_s_mem()) }; - assert!(bio != ptr::null()); +impl<'self> MemBio<'self> { + fn read(&self, buf: &mut [u8]) -> Option { + let ret = unsafe { + ffi::BIO_read(self.bio, vec::raw::to_ptr(buf) as *c_void, + buf.len() as c_int) + }; - MemBio { bio: bio } + if ret < 0 { + None + } else { + Some(ret as uint) + } } fn write(&self, buf: &[u8]) { - unsafe { - let ret = ffi::BIO_write(self.bio, - vec::raw::to_ptr(buf) as *c_void, - buf.len() as c_int); - if ret < 0 { - fail2!("write returned {}", ret); - } - } - } - - fn read(&self, buf: &[u8]) -> uint { - unsafe { - let ret = ffi::BIO_read(self.bio, vec::raw::to_ptr(buf) as *c_void, - buf.len() as c_int); - if ret < 0 { - 0 - } else { - ret as uint - } - } + let ret = unsafe { + ffi::BIO_write(self.bio, vec::raw::to_ptr(buf) as *c_void, + buf.len() as c_int) + }; + assert_eq!(buf.len(), ret as uint); } } pub struct SslStream { - priv ctx: SslCtx, + priv stream: S, priv ssl: Ssl, - priv buf: ~[u8], - priv rbio: MemBio, - priv wbio: MemBio, - priv stream: S + priv buf: ~[u8] } impl SslStream { - pub fn new(ctx: SslCtx, stream: S) -> Result, uint> { - let ssl = Ssl::new(&ctx); + pub fn try_new(ctx: &SslContext, stream: S) -> Result, + SslError> { + let ssl = match Ssl::try_new(ctx) { + Ok(ssl) => ssl, + Err(err) => return Err(err) + }; - let rbio = MemBio::new(); - let wbio = MemBio::new(); - - ssl.set_bio(&rbio, &wbio); - ssl.set_connect_state(); - - let mut stream = SslStream { - ctx: ctx, + let mut ssl = SslStream { + stream: stream, ssl: ssl, - // Max record size for SSLv3/TLSv1 is 16k - buf: vec::from_elem(16 * 1024, 0u8), - rbio: rbio, - wbio: wbio, - stream: stream + // Maximum TLS record size is 16k + buf: vec::from_elem(16 * 1024, 0u8) }; - let ret = do stream.in_retry_wrapper |ssl| { - ssl.ssl.connect() - }; - - match ret { - Ok(_) => Ok(stream), - // FIXME - Err(_err) => Err(unsafe { ffi::ERR_get_error() as uint }) + match ssl.in_retry_wrapper(|ssl| { ssl.connect() }) { + Ok(_) => Ok(ssl), + Err(err) => Err(err) } } - fn in_retry_wrapper(&mut self, blk: &fn(&mut SslStream) -> int) - -> Result { + pub fn new(ctx: &SslContext, stream: S) -> SslStream { + match SslStream::try_new(ctx, stream) { + Ok(stream) => stream, + Err(err) => fail!("Error creating SSL stream: {:?}", err) + } + } + + fn in_retry_wrapper(&mut self, blk: &fn(&Ssl) -> c_int) + -> Result { loop { - let ret = blk(self); + let ret = blk(&self.ssl); if ret > 0 { return Ok(ret); } @@ -243,34 +268,24 @@ impl SslStream { ErrorWantRead => { self.flush(); match self.stream.read(self.buf) { - Some(len) => self.rbio.write(self.buf.slice_to(len)), - None => return Err(ErrorZeroReturn) // FIXME + Some(len) => + self.ssl.get_rbio().write(self.buf.slice_to(len)), + None => return Err(StreamEof) } } ErrorWantWrite => self.flush(), - err => return Err(err) + ErrorZeroReturn => return Err(SslSessionClosed), + ErrorSsl => return Err(SslError::get().unwrap()), + _ => unreachable!() } } } fn write_through(&mut self) { loop { - let len = self.wbio.read(self.buf); - if len == 0 { - return; - } - self.stream.write(self.buf.slice_to(len)); - } - } - - pub fn shutdown(&mut self) { - loop { - let ret = do self.in_retry_wrapper |ssl| { - ssl.ssl.shutdown() - }; - - if ret != Ok(0) { - break; + match self.ssl.get_wbio().read(self.buf) { + Some(len) => self.stream.write(self.buf.slice_to(len)), + None => break } } } @@ -278,13 +293,10 @@ impl SslStream { impl Reader for SslStream { fn read(&mut self, buf: &mut [u8]) -> Option { - let ret = do self.in_retry_wrapper |ssl| { - ssl.ssl.read(buf) - }; - - match ret { - Ok(num) => Some(num as uint), - Err(_) => None + match self.in_retry_wrapper(|ssl| { ssl.read(buf) }) { + Ok(len) => Some(len as uint), + Err(StreamEof) | Err(SslSessionClosed) => None, + _ => unreachable!() } } @@ -295,25 +307,26 @@ impl Reader for SslStream { impl Writer for SslStream { fn write(&mut self, buf: &[u8]) { - let ret = do self.in_retry_wrapper |ssl| { - ssl.ssl.write(buf) - }; - - match ret { - Ok(_) => (), - Err(err) => fail2!("Write error: {:?}", err) + let mut start = 0; + while start < buf.len() { + let ret = do self.in_retry_wrapper |ssl| { + ssl.write(buf.slice_from(start)) + }; + match ret { + Ok(len) => start += len as uint, + _ => unreachable!() + } + self.write_through(); } - - self.write_through(); } fn flush(&mut self) { self.write_through(); - self.stream.flush(); + self.stream.flush() } } -impl Decorator for SslStream { +impl Decorator for SslStream { fn inner(self) -> S { self.stream } diff --git a/tests.rs b/tests.rs index 639ce1b1..b167cda8 100644 --- a/tests.rs +++ b/tests.rs @@ -3,37 +3,37 @@ use std::rt::io::extensions::ReaderUtil; use std::rt::io::net::tcp::TcpStream; use std::str; -use super::{Sslv23, SslCtx, SslStream, SslVerifyPeer}; +use super::{Sslv23, SslContext, SslStream, SslVerifyPeer}; #[test] fn test_new_ctx() { - SslCtx::new(Sslv23); + SslContext::new(Sslv23); } #[test] fn test_new_sslstream() { let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); - SslStream::new(SslCtx::new(Sslv23), stream).unwrap(); + SslStream::new(&SslContext::new(Sslv23), stream); } #[test] fn test_verify_untrusted() { let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); - let mut ctx = SslCtx::new(Sslv23); + let mut ctx = SslContext::new(Sslv23); ctx.set_verify(SslVerifyPeer); - match SslStream::new(ctx, stream) { + match SslStream::try_new(&ctx, stream) { Ok(_) => fail2!("expected failure"), - Err(err) => println!("error {}", err) + Err(err) => println!("error {:?}", err) } } #[test] fn test_verify_trusted() { let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); - let mut ctx = SslCtx::new(Sslv23); + let mut ctx = SslContext::new(Sslv23); ctx.set_verify(SslVerifyPeer); - ctx.set_verify_locations("cert.pem"); - match SslStream::new(ctx, stream) { + assert!(ctx.set_CA_file("cert.pem").is_none()); + match SslStream::try_new(&ctx, stream) { Ok(_) => (), Err(err) => fail2!("Expected success, got {:?}", err) } @@ -42,18 +42,17 @@ fn test_verify_trusted() { #[test] fn test_write() { let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); - let mut stream = SslStream::new(SslCtx::new(Sslv23), stream).unwrap(); + let mut stream = SslStream::new(&SslContext::new(Sslv23), stream); stream.write("hello".as_bytes()); stream.flush(); stream.write(" there".as_bytes()); stream.flush(); - stream.shutdown(); } #[test] fn test_read() { let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); - let mut stream = SslStream::new(SslCtx::new(Sslv23), stream).unwrap(); + let mut stream = SslStream::new(&SslContext::new(Sslv23), stream); stream.write("GET /\r\n\r\n".as_bytes()); stream.flush(); let buf = stream.read_to_end(); From 162fff7c64f75ae3b0782c351ff185fecbb168dc Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Tue, 22 Oct 2013 21:12:22 -0700 Subject: [PATCH 16/49] Fix for macro changes --- tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests.rs b/tests.rs index b167cda8..4c05a2c2 100644 --- a/tests.rs +++ b/tests.rs @@ -22,7 +22,7 @@ fn test_verify_untrusted() { let mut ctx = SslContext::new(Sslv23); ctx.set_verify(SslVerifyPeer); match SslStream::try_new(&ctx, stream) { - Ok(_) => fail2!("expected failure"), + Ok(_) => fail!("expected failure"), Err(err) => println!("error {:?}", err) } } @@ -35,7 +35,7 @@ fn test_verify_trusted() { assert!(ctx.set_CA_file("cert.pem").is_none()); match SslStream::try_new(&ctx, stream) { Ok(_) => (), - Err(err) => fail2!("Expected success, got {:?}", err) + Err(err) => fail!("Expected success, got {:?}", err) } } From 6ee6589227a9a086a00cf8d759a9e56d23b7a83a Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Tue, 22 Oct 2013 22:22:09 -0700 Subject: [PATCH 17/49] Sketch of custom cert verification callback --- error.rs | 1 + ffi.rs | 30 ++++++++++++++++++++++++++++-- lib.rs | 38 +++++++++++++++++++++++++++++++++++--- tests.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 118 insertions(+), 7 deletions(-) diff --git a/error.rs b/error.rs index b5fe0b6b..90429996 100644 --- a/error.rs +++ b/error.rs @@ -2,6 +2,7 @@ use std::libc::c_ulong; use super::ffi; +#[deriving(ToStr)] pub enum SslError { StreamEof, SslSessionClosed, diff --git a/ffi.rs b/ffi.rs index 7fec9ad0..6a1ed0d0 100644 --- a/ffi.rs +++ b/ffi.rs @@ -1,6 +1,6 @@ #[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 pub type SSL_CTX = c_void; @@ -9,6 +9,19 @@ pub type SSL = c_void; pub type BIO = c_void; pub type BIO_METHOD = c_void; pub type X509_STORE_CTX = c_void; +pub type CRYPTO_EX_DATA = c_void; + +pub type CRYPTO_EX_new = Option 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_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_free(ctx: *SSL_CTX)) externfn!(fn SSL_CTX_set_verify(ctx: *SSL_CTX, mode: c_int, - verify_callback: Option c_int>)) + verify_callback: Option c_int>)) externfn!(fn SSL_CTX_load_verify_locations(ctx: *SSL_CTX, CAfile: *c_char, CApath: *c_char) -> c_int) +externfn!(fn SSL_CTX_get_ex_new_index(argl: c_long, argp: *c_void, + new_func: Option, + dup_func: Option, + free_func: Option) + -> 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_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_write(ssl: *SSL, buf: *c_void, num: c_int) -> 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_new(type_: *BIO_METHOD) -> *BIO) diff --git a/lib.rs b/lib.rs index ce3cc8db..461a8b0c 100644 --- a/lib.rs +++ b/lib.rs @@ -1,7 +1,9 @@ +use std::cast; use std::libc::{c_int, c_void}; use std::ptr; 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::vec; @@ -17,6 +19,8 @@ mod ffi; static mut STARTED_INIT: AtomicBool = INIT_ATOMIC_BOOL; static mut FINISHED_INIT: AtomicBool = INIT_ATOMIC_BOOL; +static mut VERIFY_IDX: AtomicInt = INIT_ATOMIC_INT; + pub fn init() { unsafe { if STARTED_INIT.swap(true, Acquire) { @@ -27,6 +31,11 @@ pub fn 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); } } @@ -54,6 +63,25 @@ pub enum SslVerifyMode { 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 = 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 { priv ctx: *ffi::SSL_CTX } @@ -84,9 +112,13 @@ impl SslContext { } // 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) { 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)); } } diff --git a/tests.rs b/tests.rs index 4c05a2c2..847990dc 100644 --- a/tests.rs +++ b/tests.rs @@ -20,7 +20,7 @@ fn test_new_sslstream() { fn test_verify_untrusted() { let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); let mut ctx = SslContext::new(Sslv23); - ctx.set_verify(SslVerifyPeer); + ctx.set_verify(SslVerifyPeer, None); match SslStream::try_new(&ctx, stream) { Ok(_) => fail!("expected failure"), Err(err) => println!("error {:?}", err) @@ -31,7 +31,7 @@ fn test_verify_untrusted() { fn test_verify_trusted() { let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); let mut ctx = SslContext::new(Sslv23); - ctx.set_verify(SslVerifyPeer); + ctx.set_verify(SslVerifyPeer, None); assert!(ctx.set_CA_file("cert.pem").is_none()); match SslStream::try_new(&ctx, stream) { 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] fn test_write() { let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); From 5f5a0b53297f03300ac8f92215aba0989841e371 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Tue, 22 Oct 2013 23:12:40 -0700 Subject: [PATCH 18/49] Travis CI infrastructure --- .travis.yml | 10 ++++++++++ test/cert.pem | 21 +++++++++++++++++++++ test/key.pem | 28 ++++++++++++++++++++++++++++ tests.rs | 6 +++--- 4 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 .travis.yml create mode 100644 test/cert.pem create mode 100644 test/key.pem diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..2fb7b213 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +before_install: + - yes | sudo add-apt-repository ppa:hansjorg/rust + - sudo apt-get update +install: + - sudo apt-get install rust-nightly +before_script: + - openssl s_server -accept 15418 -www -cert test/cert.pem -key test/key.pem & +script: + - rustc --test lib.rs + - ./lib diff --git a/test/cert.pem b/test/cert.pem new file mode 100644 index 00000000..a520dd24 --- /dev/null +++ b/test/cert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDXTCCAkWgAwIBAgIJAJUaA2QC829wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwHhcNMTMxMDIyMDUzODU5WhcNMTQxMDIyMDUzODU5WjBF +MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA2E9uYoLpX4eMGz6+l+1ZdD11Y1PQjNjqSA7/nq0Q6gogPLds99a+Ca7B +2w6mWGMpCQbJTf0tOkdF6Td/gwIBNYtHuCIAiPh2Gbm6oZErVIXWwWuTWC2r7myB +0ePga5ZAE9SqsFqMEuhWikEK1+ae1CCfmbogsQSXyl4+EVN7xAwdi6yUtRL/92nn +ImKdwDBhuqzdBpBODQW/VCn0KG54CvWdwT0iqg7CvLuXQGyxM8K17SAiBtn6N38S +0jeL4D9IrBfi9PCYGpAn3jKr4oBEJVRCNvvni3Q9ikJ+GEF5nvLLVM+I98/o0vCu +Y2o7+1LiwViGONJ1BfRgdFWMjz0BfQIDAQABo1AwTjAdBgNVHQ4EFgQU509vewJT +k6qdvjT8e52gTE4+V9EwHwYDVR0jBBgwFoAU509vewJTk6qdvjT8e52gTE4+V9Ew +DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAb0Sz/ixFc+pShXur1peh +J0n7sPxX29WyTN8IZ+Sl/Ks0vG6bmtO1kAudVigStdQKeBPco+c2/+BF1sL79pXf +Ao1hOWCkXVu6E/Nxl5rZlGNjkxwurKLNV5mZ6nXYkseYZH9A2ighGYQHj4YqspN2 +2+/K1DLNBZCsZF6XozhUiDysUGN/xVh6TE0gsPvL1A27Xe4mQ8vd8Mz7av4dek// +V9F+cXnVBTAqLPpEbAX+0+k1QZtOVnDGovshlRFZ8d17f0pJZ66n9k6TPriQNWV/ +RXjIOBLbXtPFL5w6MetkEBE+DsRckLT/50cf+f/y8CHv9v9iobu4LpTjx/8l0GuQ +hg== +-----END CERTIFICATE----- diff --git a/test/key.pem b/test/key.pem new file mode 100644 index 00000000..a3f80dd9 --- /dev/null +++ b/test/key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDYT25igulfh4wb +Pr6X7Vl0PXVjU9CM2OpIDv+erRDqCiA8t2z31r4JrsHbDqZYYykJBslN/S06R0Xp +N3+DAgE1i0e4IgCI+HYZubqhkStUhdbBa5NYLavubIHR4+BrlkAT1KqwWowS6FaK +QQrX5p7UIJ+ZuiCxBJfKXj4RU3vEDB2LrJS1Ev/3aeciYp3AMGG6rN0GkE4NBb9U +KfQobngK9Z3BPSKqDsK8u5dAbLEzwrXtICIG2fo3fxLSN4vgP0isF+L08JgakCfe +MqvigEQlVEI2++eLdD2KQn4YQXme8stUz4j3z+jS8K5jajv7UuLBWIY40nUF9GB0 +VYyPPQF9AgMBAAECggEBAJiYiH20bqA2xk8eF2SkSxvmk15r7U6/Y59L/WZaHvmM +BSvwFk5MzqmUACviDNWDtpookHCVL4fSae5ZeXnZOzMju4eZbRkzdlU1ogSCnbe1 +50dx9XMaXRUItRh1koczaqbSu0tHxVM9VneX5OdkSR3Kmezf0lourEpV66FbbI9i +1F1Q7u6TzldTuPSkQQgV/FHU9DvRPJ6HgSOyVr6Z9Ec0K6odmUXe3wX7+3PbKPtr +JIVQ0wGcc/sImgAr0uS+YbHNWM4qjFAAPteQ/+Df6usSFOkRoD3+XeZrJQQ98C3q +HHW4afaJM0YCsDwn7/E3KiY5fmXwtAHNRuUbsfReP8ECgYEA9JQICyP1N5bZ4uCc +jjTiHLcQX2dHy4dKatqWkV4F52qf4HCZ/pcvPBKNMzM4uTfkCgR+TMzW+A+escvR +8KmaSKmQHT+nUQfMoU2lpZbWSPTF8lLGx+Mf8JAMur0xcmazDB8uDFnvQg+TQY7y +cF6MMWKW3pp+3qI7wRkclXSLZG0CgYEA4mlzuzuB8e7UJ821N+zD8BBYY4EvpUIj +iparwKbM8vAZ1WZssRd+8oHroHJGbjXX4h7gvpUsVadSgs77W9T0zJ+5kJCpVAnO +nKdJkX1Zo1TaIIrRaJhiaPU4hKlnGnko3uv7SlV9PPUtcyBnXElobREmQv6eCmEf +Z7YP4+JoR1ECgYEA3RyrfO7gNYZyq3Mm9kWHGjDCY43q0W0ZcSr3LqrTKZkyuuTx +w8IImQWok9497O1Dg272hBY4ToFIljLPNQUQD5sER/0RFee4LygUlnSce86W2nHN +dk62xHRmnbiHaIbCXjYeGlqAPLf6CC3kroQ7uDYKcWs5Qatn3DYIqnF3x60CgYA/ +plWatUf6s6GA7xua9TzAKFgw4Qh79PP46hKuvjWvtkAM9hZoUqqlklCjcnzKTui5 +8ORNr7IfAkL38yhG0L9hJyYLth9kOL2U3JKaDBs/B4Oq0lu8g9pml0mkQdtyXc1X +ng+u/gmPMX3td5aXIyvwPXn8K4hScqtZhJ1C+0tFgQKBgQDtlBXw3mY3AMwT+wtx +ZiitDk7tAXj7wioCOziTFKkL01UMt4VIkNvQU7RUTcig6PBc0PxU6TZQpncOk/cP +eqQQP0TJGNzIK71EwAL5aFm9o9VoXsrwjVDZnzMr703MyU15QXO76kmxmh+rK5Qy +uldCJliojIW1UW30MXSXK96YXQ== +-----END PRIVATE KEY----- diff --git a/tests.rs b/tests.rs index 847990dc..38d977c7 100644 --- a/tests.rs +++ b/tests.rs @@ -32,7 +32,7 @@ fn test_verify_trusted() { let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); let mut ctx = SslContext::new(Sslv23); ctx.set_verify(SslVerifyPeer, None); - assert!(ctx.set_CA_file("cert.pem").is_none()); + assert!(ctx.set_CA_file("test/cert.pem").is_none()); match SslStream::try_new(&ctx, stream) { Ok(_) => (), Err(err) => fail!("Expected success, got {:?}", err) @@ -72,7 +72,7 @@ fn test_verify_trusted_callback_override_ok() { 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!(ctx.set_CA_file("test/cert.pem").is_none()); match SslStream::try_new(&ctx, stream) { Ok(_) => (), Err(err) => fail!("Expected success, got {:?}", err) @@ -87,7 +87,7 @@ fn test_verify_trusted_callback_override_bad() { 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!(ctx.set_CA_file("test/cert.pem").is_none()); assert!(SslStream::try_new(&ctx, stream).is_err()); } From 83d702df2b568ec4ad32fc15bf73abdc82497218 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Tue, 22 Oct 2013 23:23:57 -0700 Subject: [PATCH 19/49] Remove Sslv2 option Many distributions of OpenSSL disable this method as SSLv2 has known security issues. --- ffi.rs | 1 - lib.rs | 2 -- 2 files changed, 3 deletions(-) diff --git a/ffi.rs b/ffi.rs index 6a1ed0d0..deb552e7 100644 --- a/ffi.rs +++ b/ffi.rs @@ -43,7 +43,6 @@ externfn!(fn ERR_get_error() -> c_ulong) externfn!(fn SSL_library_init() -> c_int) -externfn!(fn SSLv2_method() -> *SSL_METHOD) externfn!(fn SSLv3_method() -> *SSL_METHOD) externfn!(fn TLSv1_method() -> *SSL_METHOD) externfn!(fn SSLv23_method() -> *SSL_METHOD) diff --git a/lib.rs b/lib.rs index 461a8b0c..4b5d4ba7 100644 --- a/lib.rs +++ b/lib.rs @@ -41,7 +41,6 @@ pub fn init() { } pub enum SslMethod { - Sslv2, Sslv3, Tlsv1, Sslv23 @@ -50,7 +49,6 @@ pub enum SslMethod { impl SslMethod { unsafe fn to_raw(&self) -> *ffi::SSL_METHOD { match *self { - Sslv2 => ffi::SSLv2_method(), Sslv3 => ffi::SSLv3_method(), Tlsv1 => ffi::TLSv1_method(), Sslv23 => ffi::SSLv23_method() From eea07ef137992c80ab80e17f28d71abf04ef9079 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Wed, 23 Oct 2013 21:10:38 -0700 Subject: [PATCH 20/49] Slightly better error handling --- .gitignore | 5 +---- .travis.yml | 2 +- error.rs | 24 ++++++++++++++++++++++-- lib.rs | 2 ++ tests.rs | 15 ++++++++++++--- 5 files changed, 38 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 1d7e5da7..502167fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1 @@ -/.rust/ -/bin/ -/build/ -/lib/ +/lib diff --git a/.travis.yml b/.travis.yml index 2fb7b213..55c9e432 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ before_install: install: - sudo apt-get install rust-nightly before_script: - - openssl s_server -accept 15418 -www -cert test/cert.pem -key test/key.pem & + - openssl s_server -accept 15418 -www -cert test/cert.pem -key test/key.pem >/dev/null & script: - rustc --test lib.rs - ./lib diff --git a/error.rs b/error.rs index 90429996..5e5479f8 100644 --- a/error.rs +++ b/error.rs @@ -6,14 +6,34 @@ use super::ffi; pub enum SslError { StreamEof, SslSessionClosed, - UnknownError(c_ulong) + UnknownError { + library: u8, + function: u16, + reason: u16 + } +} + +fn get_lib(err: c_ulong) -> u8 { + ((err >> 24) & 0xff) as u8 +} + +fn get_func(err: c_ulong) -> u16 { + ((err >> 12) & 0xfff) as u16 +} + +fn get_reason(err: c_ulong) -> u16 { + (err & 0xfff) as u16 } impl SslError { pub fn get() -> Option { match unsafe { ffi::ERR_get_error() } { 0 => None, - err => Some(UnknownError(err)) + err => Some(UnknownError { + library: get_lib(err), + function: get_func(err), + reason: get_reason(err) + }) } } } diff --git a/lib.rs b/lib.rs index 4b5d4ba7..a15b7964 100644 --- a/lib.rs +++ b/lib.rs @@ -1,3 +1,5 @@ +#[feature(struct_variant)]; + use std::cast; use std::libc::{c_int, c_void}; use std::ptr; diff --git a/tests.rs b/tests.rs index 38d977c7..9ea5c00c 100644 --- a/tests.rs +++ b/tests.rs @@ -32,7 +32,10 @@ fn test_verify_trusted() { let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); let mut ctx = SslContext::new(Sslv23); ctx.set_verify(SslVerifyPeer, None); - assert!(ctx.set_CA_file("test/cert.pem").is_none()); + match ctx.set_CA_file("test/cert.pem") { + None => {} + Some(err) => fail!("Unexpected error {:?}", err) + } match SslStream::try_new(&ctx, stream) { Ok(_) => (), Err(err) => fail!("Expected success, got {:?}", err) @@ -72,7 +75,10 @@ fn test_verify_trusted_callback_override_ok() { 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("test/cert.pem").is_none()); + match ctx.set_CA_file("test/cert.pem") { + None => {} + Some(err) => fail!("Unexpected error {:?}", err) + } match SslStream::try_new(&ctx, stream) { Ok(_) => (), Err(err) => fail!("Expected success, got {:?}", err) @@ -87,7 +93,10 @@ fn test_verify_trusted_callback_override_bad() { 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("test/cert.pem").is_none()); + match ctx.set_CA_file("test/cert.pem") { + None => {} + Some(err) => fail!("Unexpected error {:?}", err) + } assert!(SslStream::try_new(&ctx, stream).is_err()); } From 7ea442be94ac1450d5d9d6c5670c10b0e7a5b05a Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Wed, 23 Oct 2013 21:28:08 -0700 Subject: [PATCH 21/49] Ssl errors may return a stack --- error.rs | 25 +++++++++++++++++-------- lib.rs | 12 ++++++------ 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/error.rs b/error.rs index 5e5479f8..d009b38c 100644 --- a/error.rs +++ b/error.rs @@ -6,6 +6,11 @@ use super::ffi; pub enum SslError { StreamEof, SslSessionClosed, + OpenSslErrors(~[OpensslError]) +} + +#[deriving(ToStr)] +pub enum OpensslError { UnknownError { library: u8, function: u16, @@ -26,14 +31,18 @@ fn get_reason(err: c_ulong) -> u16 { } impl SslError { - pub fn get() -> Option { - match unsafe { ffi::ERR_get_error() } { - 0 => None, - err => Some(UnknownError { - library: get_lib(err), - function: get_func(err), - reason: get_reason(err) - }) + pub fn get() -> SslError { + let mut errs = ~[]; + loop { + match unsafe { ffi::ERR_get_error() } { + 0 => break, + err => errs.push(UnknownError { + library: get_lib(err), + function: get_func(err), + reason: get_reason(err) + }) + } } + OpenSslErrors(errs) } } diff --git a/lib.rs b/lib.rs index a15b7964..2742a48f 100644 --- a/lib.rs +++ b/lib.rs @@ -98,7 +98,7 @@ impl SslContext { let ctx = unsafe { ffi::SSL_CTX_new(method.to_raw()) }; if ctx == ptr::null() { - return Err(SslError::get().unwrap()); + return Err(SslError::get()); } Ok(SslContext { ctx: ctx }) @@ -130,7 +130,7 @@ impl SslContext { }; if ret == 0 { - Some(SslError::get().unwrap()) + Some(SslError::get()) } else { None } @@ -151,19 +151,19 @@ impl Ssl { fn try_new(ctx: &SslContext) -> Result { let ssl = unsafe { ffi::SSL_new(ctx.ctx) }; if ssl == ptr::null() { - return Err(SslError::get().unwrap()); + return Err(SslError::get()); } let ssl = Ssl { ssl: ssl }; let rbio = unsafe { ffi::BIO_new(ffi::BIO_s_mem()) }; if rbio == ptr::null() { - return Err(SslError::get().unwrap()); + return Err(SslError::get()); } let wbio = unsafe { ffi::BIO_new(ffi::BIO_s_mem()) }; if wbio == ptr::null() { unsafe { ffi::BIO_free_all(rbio) } - return Err(SslError::get().unwrap()); + return Err(SslError::get()); } unsafe { ffi::SSL_set_bio(ssl.ssl, rbio, wbio) } @@ -307,7 +307,7 @@ impl SslStream { } ErrorWantWrite => self.flush(), ErrorZeroReturn => return Err(SslSessionClosed), - ErrorSsl => return Err(SslError::get().unwrap()), + ErrorSsl => return Err(SslError::get()), _ => unreachable!() } } From 2e168ab820384c543faaa4eabf4004dfc1670851 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 9 Nov 2013 17:27:17 -0800 Subject: [PATCH 22/49] Update for latest master and fix segfault --- .gitignore | 2 +- ffi.rs | 4 ++-- lib.rs | 5 +---- tests.rs => test.rs | 7 +++++-- 4 files changed, 9 insertions(+), 9 deletions(-) rename tests.rs => test.rs (97%) diff --git a/.gitignore b/.gitignore index 502167fa..6706cdd9 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/lib +/.rust/ diff --git a/ffi.rs b/ffi.rs index deb552e7..7711032c 100644 --- a/ffi.rs +++ b/ffi.rs @@ -11,10 +11,10 @@ pub type BIO_METHOD = c_void; pub type X509_STORE_CTX = c_void; pub type CRYPTO_EX_DATA = c_void; -pub type CRYPTO_EX_new = Option c_int>; + -> 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) diff --git a/lib.rs b/lib.rs index 2742a48f..4ca8fd10 100644 --- a/lib.rs +++ b/lib.rs @@ -9,13 +9,10 @@ use std::unstable::atomics::{AtomicBool, INIT_ATOMIC_BOOL, AtomicInt, use std::rt::io::{Stream, Reader, Writer, Decorator}; use std::vec; -use error::{SslError, SslSessionClosed, StreamEof}; +use self::error::{SslError, SslSessionClosed, StreamEof}; pub mod error; -#[cfg(test)] -mod tests; - mod ffi; static mut STARTED_INIT: AtomicBool = INIT_ATOMIC_BOOL; diff --git a/tests.rs b/test.rs similarity index 97% rename from tests.rs rename to test.rs index 9ea5c00c..dbcb19db 100644 --- a/tests.rs +++ b/test.rs @@ -1,9 +1,12 @@ +#[feature(struct_variant)]; + use std::rt::io::Writer; -use std::rt::io::extensions::ReaderUtil; use std::rt::io::net::tcp::TcpStream; use std::str; -use super::{Sslv23, SslContext, SslStream, SslVerifyPeer}; +use lib::{Sslv23, SslContext, SslStream, SslVerifyPeer}; + +mod lib; #[test] fn test_new_ctx() { From f2f62be4149826fefec3c8ddd2ed0ac518ffc776 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 9 Nov 2013 17:34:38 -0800 Subject: [PATCH 23/49] Have traivs actually test --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 55c9e432..62352f53 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,5 +6,5 @@ install: before_script: - openssl s_server -accept 15418 -www -cert test/cert.pem -key test/key.pem >/dev/null & script: - - rustc --test lib.rs - - ./lib + - rustpkg build + - rustpkg test From 59c05483dabeec084b5d358f88daea10c0f6273a Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 9 Nov 2013 23:06:45 -0800 Subject: [PATCH 24/49] Some documentation --- error.rs | 11 +++++++++++ lib.rs | 21 ++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/error.rs b/error.rs index d009b38c..769cc768 100644 --- a/error.rs +++ b/error.rs @@ -2,18 +2,27 @@ use std::libc::c_ulong; use super::ffi; +/// An SSL error #[deriving(ToStr)] pub enum SslError { + /// The underlying stream has reported an EOF StreamEof, + /// The SSL session has been closed by the other end SslSessionClosed, + /// An error in the OpenSSL library OpenSslErrors(~[OpensslError]) } +/// An error from the OpenSSL library #[deriving(ToStr)] pub enum OpensslError { + /// An unknown error UnknownError { + /// The library reporting the error library: u8, + /// The function reporting the error function: u16, + /// The reason for the error reason: u16 } } @@ -31,6 +40,8 @@ fn get_reason(err: c_ulong) -> u16 { } impl SslError { + /// Creates a new `OpenSslErrors` with the current contents of the error + /// stack. pub fn get() -> SslError { let mut errs = ~[]; loop { diff --git a/lib.rs b/lib.rs index 4ca8fd10..0d1061bd 100644 --- a/lib.rs +++ b/lib.rs @@ -20,6 +20,10 @@ static mut FINISHED_INIT: AtomicBool = INIT_ATOMIC_BOOL; static mut VERIFY_IDX: AtomicInt = INIT_ATOMIC_INT; +/// Initializes the library. +/// +/// This does not need to be manually called. It will automatically be called +/// when needed. Can be safely called multiple times on different threads. pub fn init() { unsafe { if STARTED_INIT.swap(true, Acquire) { @@ -39,9 +43,13 @@ pub fn init() { } } +/// Determines the SSL method supported pub enum SslMethod { + /// Only support the SSLv3 protocol Sslv3, + /// Only support the TLSv1 protocol Tlsv1, + /// Support the SSLv2, SSLv3 and TLSv1 protocols Sslv23 } @@ -55,8 +63,11 @@ impl SslMethod { } } +/// Determines the type of certificate verification used pub enum SslVerifyMode { + /// Verify that the server's certificate is trusted SslVerifyPeer = ffi::SSL_VERIFY_PEER, + /// Do not verify the server's certificate SslVerifyNone = ffi::SSL_VERIFY_NONE } @@ -77,8 +88,10 @@ extern "C" fn raw_verify(preverify_ok: c_int, x509_ctx: *ffi::X509_STORE_CTX) } } +/// The signature of functions that can be used to manually verify certificates pub type VerifyCallback = extern "Rust" fn(preverify_ok: bool) -> bool; +/// An SSL context object pub struct SslContext { priv ctx: *ffi::SSL_CTX } @@ -90,6 +103,7 @@ impl Drop for SslContext { } impl SslContext { + /// Attempts to create a new SSL context. pub fn try_new(method: SslMethod) -> Result { init(); @@ -101,6 +115,7 @@ impl SslContext { Ok(SslContext { ctx: ctx }) } + /// A convenience wrapper around `try_new`. pub fn new(method: SslMethod) -> SslContext { match SslContext::try_new(method) { Ok(ctx) => ctx, @@ -108,7 +123,7 @@ impl SslContext { } } - // TODO: support callback (see SSL_CTX_set_ex_data) + /// Configures the certificate verification method for new connections. pub fn set_verify(&mut self, mode: SslVerifyMode, verify: Option) { unsafe { @@ -119,6 +134,7 @@ impl SslContext { } } + /// Specifies the file that contains trusted CA certificates. pub fn set_CA_file(&mut self, file: &str) -> Option { let ret = do file.with_c_str |file| { unsafe { @@ -251,6 +267,7 @@ impl<'self> MemBio<'self> { } } +/// A stream wrapper which handles SSL encryption for an underlying stream. pub struct SslStream { priv stream: S, priv ssl: Ssl, @@ -258,6 +275,7 @@ pub struct SslStream { } impl SslStream { + /// Attempts to create a new SSL stream pub fn try_new(ctx: &SslContext, stream: S) -> Result, SslError> { let ssl = match Ssl::try_new(ctx) { @@ -278,6 +296,7 @@ impl SslStream { } } + /// A convenience wrapper around `try_new`. pub fn new(ctx: &SslContext, stream: S) -> SslStream { match SslStream::try_new(ctx, stream) { Ok(stream) => stream, From 1b406ae3b8f29063eeca8f31c173ba787a8ed192 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 9 Nov 2013 23:08:32 -0800 Subject: [PATCH 25/49] Add attributes for rustdoc --- .gitignore | 1 + lib.rs | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 6706cdd9..9aef65b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /.rust/ +/doc/ diff --git a/lib.rs b/lib.rs index 0d1061bd..cc1d7ef6 100644 --- a/lib.rs +++ b/lib.rs @@ -1,5 +1,10 @@ #[feature(struct_variant)]; +// Needed for rustdoc-ng +#[link(name="rust-ssl", vers="0.1", + package_id="github.com/sfackler/rust-ssl")]; +#[doc(html_root_url="http://docs.octayn.net/rust-ssl/")]; + use std::cast; use std::libc::{c_int, c_void}; use std::ptr; From 8edf2234cefaaa4138fb7b6a214014793d542626 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 10 Nov 2013 13:52:49 -0800 Subject: [PATCH 26/49] Run tests single threaded This may help fix the flickering PEM cert loading. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 62352f53..d689551a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,4 +7,4 @@ before_script: - openssl s_server -accept 15418 -www -cert test/cert.pem -key test/key.pem >/dev/null & script: - rustpkg build - - rustpkg test + - env RUST_TEST_TASKS=1 rustpkg test From be9948ba07eea965758f4f9e0cd629bbb3d59147 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 11 Nov 2013 20:40:33 -0800 Subject: [PATCH 27/49] Remove externfn! --- ffi.rs | 82 +++++++++++++++++++++++++++++----------------------------- lib.rs | 6 +---- 2 files changed, 42 insertions(+), 46 deletions(-) diff --git a/ffi.rs b/ffi.rs index 7711032c..92de2442 100644 --- a/ffi.rs +++ b/ffi.rs @@ -37,50 +37,50 @@ pub static SSL_VERIFY_NONE: c_int = 0; pub static SSL_VERIFY_PEER: c_int = 1; #[link_args = "-lssl -lcrypto"] -extern "C" { } +extern "C" { + pub fn ERR_get_error() -> c_ulong; -externfn!(fn ERR_get_error() -> c_ulong) + pub fn SSL_library_init() -> c_int; -externfn!(fn SSL_library_init() -> c_int) + pub fn SSLv3_method() -> *SSL_METHOD; + pub fn TLSv1_method() -> *SSL_METHOD; + pub fn SSLv23_method() -> *SSL_METHOD; -externfn!(fn SSLv3_method() -> *SSL_METHOD) -externfn!(fn TLSv1_method() -> *SSL_METHOD) -externfn!(fn SSLv23_method() -> *SSL_METHOD) + pub fn SSL_CTX_new(method: *SSL_METHOD) -> *SSL_CTX; + pub fn SSL_CTX_free(ctx: *SSL_CTX); + pub fn SSL_CTX_set_verify(ctx: *SSL_CTX, mode: c_int, + verify_callback: Option c_int>); + pub fn SSL_CTX_load_verify_locations(ctx: *SSL_CTX, CAfile: *c_char, + CApath: *c_char) -> c_int; + pub fn SSL_CTX_get_ex_new_index(argl: c_long, argp: *c_void, + new_func: Option, + dup_func: Option, + free_func: Option) + -> c_int; + pub fn SSL_CTX_set_ex_data(ctx: *SSL_CTX, idx: c_int, data: *c_void) + -> c_int; + pub fn SSL_CTX_get_ex_data(ctx: *SSL_CTX, idx: c_int) -> *c_void; -externfn!(fn SSL_CTX_new(method: *SSL_METHOD) -> *SSL_CTX) -externfn!(fn SSL_CTX_free(ctx: *SSL_CTX)) -externfn!(fn SSL_CTX_set_verify(ctx: *SSL_CTX, mode: c_int, - verify_callback: Option c_int>)) -externfn!(fn SSL_CTX_load_verify_locations(ctx: *SSL_CTX, CAfile: *c_char, - CApath: *c_char) -> c_int) -externfn!(fn SSL_CTX_get_ex_new_index(argl: c_long, argp: *c_void, - new_func: Option, - dup_func: Option, - free_func: Option) - -> 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) + pub fn X509_STORE_CTX_get_ex_data(ctx: *X509_STORE_CTX, idx: c_int) + -> *c_void; -externfn!(fn X509_STORE_CTX_get_ex_data(ctx: *X509_STORE_CTX, idx: c_int) - -> *c_void) + pub fn SSL_new(ctx: *SSL_CTX) -> *SSL; + pub fn SSL_free(ssl: *SSL); + pub fn SSL_set_bio(ssl: *SSL, rbio: *BIO, wbio: *BIO); + pub fn SSL_get_rbio(ssl: *SSL) -> *BIO; + pub fn SSL_get_wbio(ssl: *SSL) -> *BIO; + pub fn SSL_set_connect_state(ssl: *SSL); + pub fn SSL_connect(ssl: *SSL) -> c_int; + pub fn SSL_get_error(ssl: *SSL, ret: c_int) -> c_int; + pub fn SSL_read(ssl: *SSL, buf: *c_void, num: c_int) -> c_int; + pub fn SSL_write(ssl: *SSL, buf: *c_void, num: c_int) -> c_int; + pub fn SSL_shutdown(ssl: *SSL) -> c_int; + pub fn SSL_get_ex_data_X509_STORE_CTX_idx() -> c_int; + pub fn SSL_get_SSL_CTX(ssl: *SSL) -> *SSL_CTX; -externfn!(fn SSL_new(ctx: *SSL_CTX) -> *SSL) -externfn!(fn SSL_free(ssl: *SSL)) -externfn!(fn SSL_set_bio(ssl: *SSL, rbio: *BIO, wbio: *BIO)) -externfn!(fn SSL_get_rbio(ssl: *SSL) -> *BIO) -externfn!(fn SSL_get_wbio(ssl: *SSL) -> *BIO) -externfn!(fn SSL_set_connect_state(ssl: *SSL)) -externfn!(fn SSL_connect(ssl: *SSL) -> c_int) -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_write(ssl: *SSL, buf: *c_void, num: c_int) -> 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_new(type_: *BIO_METHOD) -> *BIO) -externfn!(fn BIO_free_all(a: *BIO)) -externfn!(fn BIO_read(b: *BIO, buf: *c_void, len: c_int) -> c_int) -externfn!(fn BIO_write(b: *BIO, buf: *c_void, len: c_int) -> c_int) + pub fn BIO_s_mem() -> *BIO_METHOD; + pub fn BIO_new(type_: *BIO_METHOD) -> *BIO; + pub fn BIO_free_all(a: *BIO); + pub fn BIO_read(b: *BIO, buf: *c_void, len: c_int) -> c_int; + pub fn BIO_write(b: *BIO, buf: *c_void, len: c_int) -> c_int; +} diff --git a/lib.rs b/lib.rs index cc1d7ef6..f69fcf0e 100644 --- a/lib.rs +++ b/lib.rs @@ -25,11 +25,7 @@ static mut FINISHED_INIT: AtomicBool = INIT_ATOMIC_BOOL; static mut VERIFY_IDX: AtomicInt = INIT_ATOMIC_INT; -/// Initializes the library. -/// -/// This does not need to be manually called. It will automatically be called -/// when needed. Can be safely called multiple times on different threads. -pub fn init() { +fn init() { unsafe { if STARTED_INIT.swap(true, Acquire) { while !FINISHED_INIT.load(Release) { From f42e9afc2c7d8e2cc767d86e34a4001bd399b89c Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 11 Nov 2013 20:55:46 -0800 Subject: [PATCH 28/49] Fix indentation --- ffi.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/ffi.rs b/ffi.rs index 92de2442..80c30b4c 100644 --- a/ffi.rs +++ b/ffi.rs @@ -12,9 +12,8 @@ pub type X509_STORE_CTX = c_void; pub type CRYPTO_EX_DATA = c_void; pub type CRYPTO_EX_new = extern "C" fn(parent: *c_void, ptr: *c_void, - ad: *CRYPTO_EX_DATA, idx: c_int, - argl: c_long, argp: *c_void) - -> c_int; + 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) @@ -49,20 +48,20 @@ extern "C" { pub fn SSL_CTX_new(method: *SSL_METHOD) -> *SSL_CTX; pub fn SSL_CTX_free(ctx: *SSL_CTX); pub fn SSL_CTX_set_verify(ctx: *SSL_CTX, mode: c_int, - verify_callback: Option c_int>); + verify_callback: Option c_int>); pub fn SSL_CTX_load_verify_locations(ctx: *SSL_CTX, CAfile: *c_char, CApath: *c_char) -> c_int; pub fn SSL_CTX_get_ex_new_index(argl: c_long, argp: *c_void, - new_func: Option, - dup_func: Option, - free_func: Option) - -> c_int; + new_func: Option, + dup_func: Option, + free_func: Option) + -> c_int; pub fn SSL_CTX_set_ex_data(ctx: *SSL_CTX, idx: c_int, data: *c_void) - -> c_int; + -> c_int; pub fn SSL_CTX_get_ex_data(ctx: *SSL_CTX, idx: c_int) -> *c_void; pub fn X509_STORE_CTX_get_ex_data(ctx: *X509_STORE_CTX, idx: c_int) - -> *c_void; + -> *c_void; pub fn SSL_new(ctx: *SSL_CTX) -> *SSL; pub fn SSL_free(ssl: *SSL); From 2ebb3f084ad68a7031575e191cc8cc04d89a6aed Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 11 Nov 2013 21:39:36 -0800 Subject: [PATCH 29/49] Last indentation fix --- ffi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ffi.rs b/ffi.rs index 80c30b4c..ee6a3e1a 100644 --- a/ffi.rs +++ b/ffi.rs @@ -50,7 +50,7 @@ extern "C" { pub fn SSL_CTX_set_verify(ctx: *SSL_CTX, mode: c_int, verify_callback: Option c_int>); pub fn SSL_CTX_load_verify_locations(ctx: *SSL_CTX, CAfile: *c_char, - CApath: *c_char) -> c_int; + CApath: *c_char) -> c_int; pub fn SSL_CTX_get_ex_new_index(argl: c_long, argp: *c_void, new_func: Option, dup_func: Option, From c76e86f416c312923da6585ac5182b35807cc008 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Tue, 12 Nov 2013 21:32:12 -0800 Subject: [PATCH 30/49] Update for io move --- lib.rs | 2 +- test.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib.rs b/lib.rs index f69fcf0e..d5cfab7f 100644 --- a/lib.rs +++ b/lib.rs @@ -11,7 +11,7 @@ use std::ptr; use std::task; 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::io::{Stream, Reader, Writer, Decorator}; use std::vec; use self::error::{SslError, SslSessionClosed, StreamEof}; diff --git a/test.rs b/test.rs index dbcb19db..4167f9eb 100644 --- a/test.rs +++ b/test.rs @@ -1,7 +1,7 @@ #[feature(struct_variant)]; -use std::rt::io::Writer; -use std::rt::io::net::tcp::TcpStream; +use std::io::Writer; +use std::io::net::tcp::TcpStream; use std::str; use lib::{Sslv23, SslContext, SslStream, SslVerifyPeer}; From 5857889ceb62aebdd017dd20192d4a4b3006eba2 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Fri, 15 Nov 2013 19:55:43 -0800 Subject: [PATCH 31/49] Switch docs to sfackler.com octayn doesn't support rustpkg yet --- lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib.rs b/lib.rs index d5cfab7f..f834c14e 100644 --- a/lib.rs +++ b/lib.rs @@ -3,7 +3,7 @@ // Needed for rustdoc-ng #[link(name="rust-ssl", vers="0.1", package_id="github.com/sfackler/rust-ssl")]; -#[doc(html_root_url="http://docs.octayn.net/rust-ssl/")]; +#[doc(html_root_url="http://sfackler.com/doc/rust-ssl/")]; use std::cast; use std::libc::{c_int, c_void}; From f8be6415366927350c8874a4c3dc542622979408 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 16 Nov 2013 20:21:48 -0800 Subject: [PATCH 32/49] Start of x509 interface --- ffi.rs | 2 ++ lib.rs | 28 ++++++++++++++++++++++++++-- test.rs | 22 +++++++++++++++++----- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/ffi.rs b/ffi.rs index ee6a3e1a..cbb6e492 100644 --- a/ffi.rs +++ b/ffi.rs @@ -9,6 +9,7 @@ pub type SSL = c_void; pub type BIO = c_void; pub type BIO_METHOD = c_void; pub type X509_STORE_CTX = c_void; +pub type X509 = c_void; pub type CRYPTO_EX_DATA = c_void; pub type CRYPTO_EX_new = extern "C" fn(parent: *c_void, ptr: *c_void, @@ -62,6 +63,7 @@ extern "C" { pub fn X509_STORE_CTX_get_ex_data(ctx: *X509_STORE_CTX, idx: c_int) -> *c_void; + pub fn X509_STORE_CTX_get_current_cert(ct: *X509_STORE_CTX) -> *X509; pub fn SSL_new(ctx: *SSL_CTX) -> *SSL; pub fn SSL_free(ssl: *SSL); diff --git a/lib.rs b/lib.rs index f834c14e..fb223c4d 100644 --- a/lib.rs +++ b/lib.rs @@ -82,15 +82,18 @@ extern "C" fn raw_verify(preverify_ok: c_int, x509_ctx: *ffi::X509_STORE_CTX) let verify = ffi::SSL_CTX_get_ex_data(ssl_ctx, idx); let verify: Option = cast::transmute(verify); + let ctx = X509StoreContext { ctx: x509_ctx }; + match verify { None => preverify_ok, - Some(verify) => verify(preverify_ok != 0) as c_int + Some(verify) => verify(preverify_ok != 0, ctx) as c_int } } } /// The signature of functions that can be used to manually verify certificates -pub type VerifyCallback = extern "Rust" fn(preverify_ok: bool) -> bool; +pub type VerifyCallback = extern "Rust" fn(preverify_ok: bool, + x509_ctx: X509StoreContext) -> bool; /// An SSL context object pub struct SslContext { @@ -151,6 +154,27 @@ impl SslContext { } } +pub struct X509StoreContext { + priv ctx: *ffi::X509_STORE_CTX +} + +impl X509StoreContext { + pub fn get_current_cert(&self) -> Option { + let ptr = unsafe { ffi::X509_STORE_CTX_get_current_cert(self.ctx) }; + + if ptr.is_null() { + None + } else { + Some(X509 { x509: ptr }) + } + } +} + +/// A public key certificate +pub struct X509 { + priv x509: *ffi::X509 +} + struct Ssl { ssl: *ffi::SSL } diff --git a/test.rs b/test.rs index 4167f9eb..cc9e8bab 100644 --- a/test.rs +++ b/test.rs @@ -4,7 +4,7 @@ use std::io::Writer; use std::io::net::tcp::TcpStream; use std::str; -use lib::{Sslv23, SslContext, SslStream, SslVerifyPeer}; +use lib::{Sslv23, SslContext, SslStream, SslVerifyPeer, X509StoreContext}; mod lib; @@ -47,7 +47,7 @@ fn test_verify_trusted() { #[test] fn test_verify_untrusted_callback_override_ok() { - fn callback(_preverify_ok: bool) -> bool { + fn callback(_preverify_ok: bool, _x509_ctx: X509StoreContext) -> bool { true } let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); @@ -61,7 +61,7 @@ fn test_verify_untrusted_callback_override_ok() { #[test] fn test_verify_untrusted_callback_override_bad() { - fn callback(_preverify_ok: bool) -> bool { + fn callback(_preverify_ok: bool, _x509_ctx: X509StoreContext) -> bool { false } let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); @@ -72,7 +72,7 @@ fn test_verify_untrusted_callback_override_bad() { #[test] fn test_verify_trusted_callback_override_ok() { - fn callback(_preverify_ok: bool) -> bool { + fn callback(_preverify_ok: bool, _x509_ctx: X509StoreContext) -> bool { true } let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); @@ -90,7 +90,7 @@ fn test_verify_trusted_callback_override_ok() { #[test] fn test_verify_trusted_callback_override_bad() { - fn callback(_preverify_ok: bool) -> bool { + fn callback(_preverify_ok: bool, _x509_ctx: X509StoreContext) -> bool { false } let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); @@ -103,6 +103,18 @@ fn test_verify_trusted_callback_override_bad() { assert!(SslStream::try_new(&ctx, stream).is_err()); } +#[test] +fn test_verify_callback_load_certs() { + fn callback(_preverify_ok: bool, x509_ctx: X509StoreContext) -> bool { + assert!(x509_ctx.get_current_cert().is_some()); + 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!(SslStream::try_new(&ctx, stream).is_ok()); +} + #[test] fn test_write() { let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); From 0b287710c408a510adffbd471e227fd1f87e2780 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 16 Nov 2013 21:09:45 -0800 Subject: [PATCH 33/49] Certificate error codes --- ffi.rs | 66 ++++++++++++++++++++++++++++++++++++++++++- lib.rs | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- test.rs | 30 +++++++++++++++++++- 3 files changed, 181 insertions(+), 3 deletions(-) diff --git a/ffi.rs b/ffi.rs index cbb6e492..7f367605 100644 --- a/ffi.rs +++ b/ffi.rs @@ -2,7 +2,6 @@ use std::libc::{c_int, c_void, c_long, c_ulong, c_char}; -// openssl/ssl.h pub type SSL_CTX = c_void; pub type SSL_METHOD = c_void; pub type SSL = c_void; @@ -36,6 +35,70 @@ pub static SSL_ERROR_WANT_ACCEPT: c_int = 8; pub static SSL_VERIFY_NONE: c_int = 0; pub static SSL_VERIFY_PEER: c_int = 1; +pub static X509_V_OK: c_int = 0; +/* illegal error (for uninitialized values, to avoid X509_V_OK): 1 */ + +pub static X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: c_int = 2; +pub static X509_V_ERR_UNABLE_TO_GET_CRL: c_int = 3; +pub static X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: c_int = 4; +pub static X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: c_int = 5; +pub static X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: c_int = 6; +pub static X509_V_ERR_CERT_SIGNATURE_FAILURE: c_int = 7; +pub static X509_V_ERR_CRL_SIGNATURE_FAILURE: c_int = 8; +pub static X509_V_ERR_CERT_NOT_YET_VALID: c_int = 9; +pub static X509_V_ERR_CERT_HAS_EXPIRED: c_int = 10; +pub static X509_V_ERR_CRL_NOT_YET_VALID: c_int = 11; +pub static X509_V_ERR_CRL_HAS_EXPIRED: c_int = 12; +pub static X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: c_int = 13; +pub static X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: c_int = 14; +pub static X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: c_int = 15; +pub static X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: c_int = 16; +pub static X509_V_ERR_OUT_OF_MEM: c_int = 17; +pub static X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: c_int = 18; +pub static X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: c_int = 19; +pub static X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: c_int = 20; +pub static X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: c_int = 21; +pub static X509_V_ERR_CERT_CHAIN_TOO_LONG: c_int = 22; +pub static X509_V_ERR_CERT_REVOKED: c_int = 23; +pub static X509_V_ERR_INVALID_CA: c_int = 24; +pub static X509_V_ERR_PATH_LENGTH_EXCEEDED: c_int = 25; +pub static X509_V_ERR_INVALID_PURPOSE: c_int = 26; +pub static X509_V_ERR_CERT_UNTRUSTED: c_int = 27; +pub static X509_V_ERR_CERT_REJECTED: c_int = 28; +/* These are 'informational' when looking for issuer cert */ +pub static X509_V_ERR_SUBJECT_ISSUER_MISMATCH: c_int = 29; +pub static X509_V_ERR_AKID_SKID_MISMATCH: c_int = 30; +pub static X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: c_int = 31; +pub static X509_V_ERR_KEYUSAGE_NO_CERTSIGN: c_int = 32; + +pub static X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: c_int = 33; +pub static X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION: c_int = 34; +pub static X509_V_ERR_KEYUSAGE_NO_CRL_SIGN: c_int = 35; +pub static X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION: c_int = 36; +pub static X509_V_ERR_INVALID_NON_CA: c_int = 37; +pub static X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED: c_int = 38; +pub static X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE: c_int = 39; +pub static X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED: c_int = 40; + +pub static X509_V_ERR_INVALID_EXTENSION: c_int = 41; +pub static X509_V_ERR_INVALID_POLICY_EXTENSION: c_int = 42; +pub static X509_V_ERR_NO_EXPLICIT_POLICY: c_int = 43; +pub static X509_V_ERR_DIFFERENT_CRL_SCOPE: c_int = 44; +pub static X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE: c_int = 45; + +pub static X509_V_ERR_UNNESTED_RESOURCE: c_int = 46; + +pub static X509_V_ERR_PERMITTED_VIOLATION: c_int = 47; +pub static X509_V_ERR_EXCLUDED_VIOLATION: c_int = 48; +pub static X509_V_ERR_SUBTREE_MINMAX: c_int = 49; +pub static X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE: c_int = 51; +pub static X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX: c_int = 52; +pub static X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: c_int = 53; +pub static X509_V_ERR_CRL_PATH_VALIDATION_ERROR: c_int = 54; + +/* The application is not happy */ +pub static X509_V_ERR_APPLICATION_VERIFICATION: c_int = 50; + #[link_args = "-lssl -lcrypto"] extern "C" { pub fn ERR_get_error() -> c_ulong; @@ -64,6 +127,7 @@ extern "C" { pub fn X509_STORE_CTX_get_ex_data(ctx: *X509_STORE_CTX, idx: c_int) -> *c_void; pub fn X509_STORE_CTX_get_current_cert(ct: *X509_STORE_CTX) -> *X509; + pub fn X509_STORE_CTX_get_error(ctx: *X509_STORE_CTX) -> c_int; pub fn SSL_new(ctx: *SSL_CTX) -> *SSL; pub fn SSL_free(ssl: *SSL); diff --git a/lib.rs b/lib.rs index fb223c4d..6393ce95 100644 --- a/lib.rs +++ b/lib.rs @@ -1,4 +1,4 @@ -#[feature(struct_variant)]; +#[feature(struct_variant, macro_rules)]; // Needed for rustdoc-ng #[link(name="rust-ssl", vers="0.1", @@ -15,6 +15,7 @@ use std::io::{Stream, Reader, Writer, Decorator}; use std::vec; use self::error::{SslError, SslSessionClosed, StreamEof}; +use self::hack::X509ValidationError; pub mod error; @@ -159,6 +160,11 @@ pub struct X509StoreContext { } impl X509StoreContext { + pub fn get_error(&self) -> Option { + let err = unsafe { ffi::X509_STORE_CTX_get_error(self.ctx) }; + X509ValidationError::from_raw(err) + } + pub fn get_current_cert(&self) -> Option { let ptr = unsafe { ffi::X509_STORE_CTX_get_current_cert(self.ctx) }; @@ -175,6 +181,86 @@ pub struct X509 { priv x509: *ffi::X509 } +macro_rules! make_validation_error( + ($ok_val:ident, $($name:ident = $val:ident,)+) => ( + pub mod hack { + use std::libc::c_int; + + pub enum X509ValidationError { + $($name,)+ + X509UnknownError(c_int) + } + + impl X509ValidationError { + #[doc(hidden)] + pub fn from_raw(err: c_int) -> Option { + match err { + super::ffi::$ok_val => None, + $(super::ffi::$val => Some($name),)+ + err => Some(X509UnknownError(err)) + } + } + } + } + ) +) + +make_validation_error!(X509_V_OK, + X509UnableToGetIssuerCert = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT, + X509UnableToGetCrl = X509_V_ERR_UNABLE_TO_GET_CRL, + X509UnableToDecryptCertSignature = X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE, + X509UnableToDecryptCrlSignature = X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE, + X509UnableToDecodeIssuerPublicKey = X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY, + X509CertSignatureFailure = X509_V_ERR_CERT_SIGNATURE_FAILURE, + X509CrlSignatureFailure = X509_V_ERR_CRL_SIGNATURE_FAILURE, + X509CertNotYetValid = X509_V_ERR_CERT_NOT_YET_VALID, + X509CertHasExpired = X509_V_ERR_CERT_HAS_EXPIRED, + X509CrlNotYetValid = X509_V_ERR_CRL_NOT_YET_VALID, + X509CrlHasExpired = X509_V_ERR_CRL_HAS_EXPIRED, + X509ErrorInCertNotBeforeField = X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD, + X509ErrorInCertNotAfterField = X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD, + X509ErrorInCrlLastUpdateField = X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD, + X509ErrorInCrlNextUpdateField = X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD, + X509OutOfMem = X509_V_ERR_OUT_OF_MEM, + X509DepthZeroSelfSignedCert = X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT, + X509SelfSignedCertInChain = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN, + X509UnableToGetIssuerCertLocally = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, + X509UnableToVerifyLeafSignature = X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE, + X509CertChainTooLong = X509_V_ERR_CERT_CHAIN_TOO_LONG, + X509CertRevoked = X509_V_ERR_CERT_REVOKED, + X509InvalidCA = X509_V_ERR_INVALID_CA, + X509PathLengthExceeded = X509_V_ERR_PATH_LENGTH_EXCEEDED, + X509InvalidPurpose = X509_V_ERR_INVALID_PURPOSE, + X509CertUntrusted = X509_V_ERR_CERT_UNTRUSTED, + X509CertRejected = X509_V_ERR_CERT_REJECTED, + X509SubjectIssuerMismatch = X509_V_ERR_SUBJECT_ISSUER_MISMATCH, + X509AkidSkidMismatch = X509_V_ERR_AKID_SKID_MISMATCH, + X509AkidIssuerSerialMismatch = X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH, + X509KeyusageNoCertsign = X509_V_ERR_KEYUSAGE_NO_CERTSIGN, + X509UnableToGetCrlIssuer = X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER, + X509UnhandledCriticalExtension = X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION, + X509KeyusageNoCrlSign = X509_V_ERR_KEYUSAGE_NO_CRL_SIGN, + X509UnhandledCriticalCrlExtension = X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION, + X509InvalidNonCA = X509_V_ERR_INVALID_NON_CA, + X509ProxyPathLengthExceeded = X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED, + X509KeyusageNoDigitalSignature = X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE, + X509ProxyCertificatesNotAllowed = X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED, + X509InvalidExtension = X509_V_ERR_INVALID_EXTENSION, + X509InavlidPolicyExtension = X509_V_ERR_INVALID_POLICY_EXTENSION, + X509NoExplicitPolicy = X509_V_ERR_NO_EXPLICIT_POLICY, + X509DifferentCrlScope = X509_V_ERR_DIFFERENT_CRL_SCOPE, + X509UnsupportedExtensionFeature = X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE, + X509UnnestedResource = X509_V_ERR_UNNESTED_RESOURCE, + X509PermittedVolation = X509_V_ERR_PERMITTED_VIOLATION, + X509ExcludedViolation = X509_V_ERR_EXCLUDED_VIOLATION, + X509SubtreeMinmax = X509_V_ERR_SUBTREE_MINMAX, + X509UnsupportedConstraintType = X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE, + X509UnsupportedConstraintSyntax = X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX, + X509UnsupportedNameSyntax = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, + X509CrlPathValidationError= X509_V_ERR_CRL_PATH_VALIDATION_ERROR, + X509ApplicationVerification = X509_V_ERR_APPLICATION_VERIFICATION, +) + struct Ssl { ssl: *ffi::SSL } diff --git a/test.rs b/test.rs index cc9e8bab..b3c466c9 100644 --- a/test.rs +++ b/test.rs @@ -1,4 +1,4 @@ -#[feature(struct_variant)]; +#[feature(struct_variant, macro_rules)]; use std::io::Writer; use std::io::net::tcp::TcpStream; @@ -115,6 +115,34 @@ fn test_verify_callback_load_certs() { assert!(SslStream::try_new(&ctx, stream).is_ok()); } +#[test] +fn test_verify_trusted_get_error_ok() { + fn callback(_preverify_ok: bool, x509_ctx: X509StoreContext) -> bool { + assert!(x509_ctx.get_error().is_none()); + 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 ctx.set_CA_file("test/cert.pem") { + None => {} + Some(err) => fail!("Unexpected error {:?}", err) + } + assert!(SslStream::try_new(&ctx, stream).is_ok()); +} + +#[test] +fn test_verify_trusted_get_error_err() { + fn callback(_preverify_ok: bool, x509_ctx: X509StoreContext) -> bool { + assert!(x509_ctx.get_error().is_some()); + 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_write() { let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); From fc57ec0e43186c02ee780f077f8909e8f443c48e Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Tue, 19 Nov 2013 20:49:16 -0800 Subject: [PATCH 34/49] Fix locking --- .travis.yml | 2 +- ffi.rs | 8 ++++++++ lib.rs | 36 ++++++++++++++++++++++++++++++------ 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index d689551a..62352f53 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,4 +7,4 @@ before_script: - openssl s_server -accept 15418 -www -cert test/cert.pem -key test/key.pem >/dev/null & script: - rustpkg build - - env RUST_TEST_TASKS=1 rustpkg test + - rustpkg test diff --git a/ffi.rs b/ffi.rs index 7f367605..79a63826 100644 --- a/ffi.rs +++ b/ffi.rs @@ -22,6 +22,8 @@ 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 CRYPTO_LOCK: c_int = 1; + pub static SSL_ERROR_NONE: c_int = 0; pub static SSL_ERROR_SSL: c_int = 1; pub static SSL_ERROR_WANT_READ: c_int = 2; @@ -101,6 +103,12 @@ pub static X509_V_ERR_APPLICATION_VERIFICATION: c_int = 50; #[link_args = "-lssl -lcrypto"] extern "C" { + pub fn CRYPTO_num_locks() -> c_int; + pub fn CRYPTO_set_locking_callback(func: extern "C" fn(mode: c_int, + n: c_int, + file: *c_char, + line: c_int)); + pub fn ERR_get_error() -> c_ulong; pub fn SSL_library_init() -> c_int; diff --git a/lib.rs b/lib.rs index 6393ce95..f652eebf 100644 --- a/lib.rs +++ b/lib.rs @@ -6,11 +6,12 @@ #[doc(html_root_url="http://sfackler.com/doc/rust-ssl/")]; use std::cast; -use std::libc::{c_int, c_void}; +use std::libc::{c_int, c_void, c_char}; use std::ptr; use std::task; -use std::unstable::atomics::{AtomicBool, INIT_ATOMIC_BOOL, AtomicInt, - INIT_ATOMIC_INT, Acquire, Release, SeqCst}; +use std::unstable::atomics::{AtomicBool, INIT_ATOMIC_BOOL, AtomicUint, + INIT_ATOMIC_UINT, Acquire, Release, SeqCst}; +use std::unstable::mutex::Mutex; use std::io::{Stream, Reader, Writer, Decorator}; use std::vec; @@ -24,7 +25,10 @@ mod ffi; static mut STARTED_INIT: AtomicBool = INIT_ATOMIC_BOOL; static mut FINISHED_INIT: AtomicBool = INIT_ATOMIC_BOOL; -static mut VERIFY_IDX: AtomicInt = INIT_ATOMIC_INT; +static mut VERIFY_IDX: AtomicUint = INIT_ATOMIC_UINT; + +// actually a *~[Mutex] +static mut MUTEXES: AtomicUint = INIT_ATOMIC_UINT; fn init() { unsafe { @@ -39,7 +43,13 @@ fn 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); + VERIFY_IDX.store(verify_idx as uint, Release); + + let num_locks = ffi::CRYPTO_num_locks(); + let mutexes = ~vec::from_fn(num_locks as uint, |_| Mutex::new()); + MUTEXES.store(cast::transmute(mutexes), Release); + + ffi::CRYPTO_set_locking_callback(locking_function); FINISHED_INIT.store(true, Release); } @@ -73,13 +83,27 @@ pub enum SslVerifyMode { SslVerifyNone = ffi::SSL_VERIFY_NONE } +extern "C" fn locking_function(mode: c_int, n: c_int, _file: *c_char, + _line: c_int) { + unsafe { + let mutexes: *mut ~[Mutex] = cast::transmute(MUTEXES.load(Acquire)); + let mutex = &mut (*mutexes)[n as uint]; + + if mode & ffi::CRYPTO_LOCK != 0 { + mutex.lock(); + } else { + mutex.unlock(); + } + } +} + 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 idx = VERIFY_IDX.load(Acquire) as c_int; let verify = ffi::SSL_CTX_get_ex_data(ssl_ctx, idx); let verify: Option = cast::transmute(verify); From 2216f86bd3acbcf165d83cf617834c334168fe3b Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Wed, 20 Nov 2013 21:45:05 -0800 Subject: [PATCH 35/49] Make verification callbacks sound --- lib.rs | 8 ++++---- test.rs | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib.rs b/lib.rs index f652eebf..6e450cef 100644 --- a/lib.rs +++ b/lib.rs @@ -111,14 +111,14 @@ extern "C" fn raw_verify(preverify_ok: c_int, x509_ctx: *ffi::X509_STORE_CTX) match verify { None => preverify_ok, - Some(verify) => verify(preverify_ok != 0, ctx) as c_int + Some(verify) => verify(preverify_ok != 0, &ctx) as c_int } } } /// The signature of functions that can be used to manually verify certificates pub type VerifyCallback = extern "Rust" fn(preverify_ok: bool, - x509_ctx: X509StoreContext) -> bool; + x509_ctx: &X509StoreContext) -> bool; /// An SSL context object pub struct SslContext { @@ -189,7 +189,7 @@ impl X509StoreContext { X509ValidationError::from_raw(err) } - pub fn get_current_cert(&self) -> Option { + pub fn get_current_cert<'a>(&'a self) -> Option> { let ptr = unsafe { ffi::X509_STORE_CTX_get_current_cert(self.ctx) }; if ptr.is_null() { @@ -201,7 +201,7 @@ impl X509StoreContext { } /// A public key certificate -pub struct X509 { +pub struct X509<'ctx> { priv x509: *ffi::X509 } diff --git a/test.rs b/test.rs index b3c466c9..2655fa98 100644 --- a/test.rs +++ b/test.rs @@ -47,7 +47,7 @@ fn test_verify_trusted() { #[test] fn test_verify_untrusted_callback_override_ok() { - fn callback(_preverify_ok: bool, _x509_ctx: X509StoreContext) -> bool { + fn callback(_preverify_ok: bool, _x509_ctx: &X509StoreContext) -> bool { true } let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); @@ -61,7 +61,7 @@ fn test_verify_untrusted_callback_override_ok() { #[test] fn test_verify_untrusted_callback_override_bad() { - fn callback(_preverify_ok: bool, _x509_ctx: X509StoreContext) -> bool { + fn callback(_preverify_ok: bool, _x509_ctx: &X509StoreContext) -> bool { false } let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); @@ -72,7 +72,7 @@ fn test_verify_untrusted_callback_override_bad() { #[test] fn test_verify_trusted_callback_override_ok() { - fn callback(_preverify_ok: bool, _x509_ctx: X509StoreContext) -> bool { + fn callback(_preverify_ok: bool, _x509_ctx: &X509StoreContext) -> bool { true } let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); @@ -90,7 +90,7 @@ fn test_verify_trusted_callback_override_ok() { #[test] fn test_verify_trusted_callback_override_bad() { - fn callback(_preverify_ok: bool, _x509_ctx: X509StoreContext) -> bool { + fn callback(_preverify_ok: bool, _x509_ctx: &X509StoreContext) -> bool { false } let stream = TcpStream::connect(FromStr::from_str("127.0.0.1:15418").unwrap()).unwrap(); @@ -105,7 +105,7 @@ fn test_verify_trusted_callback_override_bad() { #[test] fn test_verify_callback_load_certs() { - fn callback(_preverify_ok: bool, x509_ctx: X509StoreContext) -> bool { + fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext) -> bool { assert!(x509_ctx.get_current_cert().is_some()); true } @@ -117,7 +117,7 @@ fn test_verify_callback_load_certs() { #[test] fn test_verify_trusted_get_error_ok() { - fn callback(_preverify_ok: bool, x509_ctx: X509StoreContext) -> bool { + fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext) -> bool { assert!(x509_ctx.get_error().is_none()); true } @@ -133,7 +133,7 @@ fn test_verify_trusted_get_error_ok() { #[test] fn test_verify_trusted_get_error_err() { - fn callback(_preverify_ok: bool, x509_ctx: X509StoreContext) -> bool { + fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext) -> bool { assert!(x509_ctx.get_error().is_some()); false } From 1bd57d7ffbddb406dca408093b65e7537bab4988 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Thu, 21 Nov 2013 23:15:47 -0800 Subject: [PATCH 36/49] More work on X509 functionality --- ffi.rs | 19 ++++++++++--------- lib.rs | 34 +++++++++++++++++++++++++++------- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/ffi.rs b/ffi.rs index 79a63826..0ff77b74 100644 --- a/ffi.rs +++ b/ffi.rs @@ -9,6 +9,7 @@ pub type BIO = c_void; pub type BIO_METHOD = c_void; pub type X509_STORE_CTX = c_void; pub type X509 = c_void; +pub type X509_NAME = c_void; pub type CRYPTO_EX_DATA = c_void; pub type CRYPTO_EX_new = extern "C" fn(parent: *c_void, ptr: *c_void, @@ -38,8 +39,6 @@ pub static SSL_VERIFY_NONE: c_int = 0; pub static SSL_VERIFY_PEER: c_int = 1; pub static X509_V_OK: c_int = 0; -/* illegal error (for uninitialized values, to avoid X509_V_OK): 1 */ - pub static X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: c_int = 2; pub static X509_V_ERR_UNABLE_TO_GET_CRL: c_int = 3; pub static X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: c_int = 4; @@ -67,12 +66,10 @@ pub static X509_V_ERR_PATH_LENGTH_EXCEEDED: c_int = 25; pub static X509_V_ERR_INVALID_PURPOSE: c_int = 26; pub static X509_V_ERR_CERT_UNTRUSTED: c_int = 27; pub static X509_V_ERR_CERT_REJECTED: c_int = 28; -/* These are 'informational' when looking for issuer cert */ pub static X509_V_ERR_SUBJECT_ISSUER_MISMATCH: c_int = 29; pub static X509_V_ERR_AKID_SKID_MISMATCH: c_int = 30; pub static X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: c_int = 31; pub static X509_V_ERR_KEYUSAGE_NO_CERTSIGN: c_int = 32; - pub static X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: c_int = 33; pub static X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION: c_int = 34; pub static X509_V_ERR_KEYUSAGE_NO_CRL_SIGN: c_int = 35; @@ -81,15 +78,12 @@ pub static X509_V_ERR_INVALID_NON_CA: c_int = 37; pub static X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED: c_int = 38; pub static X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE: c_int = 39; pub static X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED: c_int = 40; - pub static X509_V_ERR_INVALID_EXTENSION: c_int = 41; pub static X509_V_ERR_INVALID_POLICY_EXTENSION: c_int = 42; pub static X509_V_ERR_NO_EXPLICIT_POLICY: c_int = 43; pub static X509_V_ERR_DIFFERENT_CRL_SCOPE: c_int = 44; pub static X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE: c_int = 45; - pub static X509_V_ERR_UNNESTED_RESOURCE: c_int = 46; - pub static X509_V_ERR_PERMITTED_VIOLATION: c_int = 47; pub static X509_V_ERR_EXCLUDED_VIOLATION: c_int = 48; pub static X509_V_ERR_SUBTREE_MINMAX: c_int = 49; @@ -97,10 +91,12 @@ pub static X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE: c_int = 51; pub static X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX: c_int = 52; pub static X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: c_int = 53; pub static X509_V_ERR_CRL_PATH_VALIDATION_ERROR: c_int = 54; - -/* The application is not happy */ pub static X509_V_ERR_APPLICATION_VERIFICATION: c_int = 50; +pub static XN_FLAG_RFC2253: c_ulong = 0x1110317; +pub static XN_FLAG_ONELINE: c_ulong = 0x82031f; +pub static XN_FLAG_MULTILINE: c_ulong = 0x2a40006; + #[link_args = "-lssl -lcrypto"] extern "C" { pub fn CRYPTO_num_locks() -> c_int; @@ -137,6 +133,11 @@ extern "C" { pub fn X509_STORE_CTX_get_current_cert(ct: *X509_STORE_CTX) -> *X509; pub fn X509_STORE_CTX_get_error(ctx: *X509_STORE_CTX) -> c_int; + pub fn X509_get_subject_name(x: *X509) -> *X509_NAME; + + pub fn X509_NAME_print_ex(out: *BIO, nm: *X509_NAME, ident: c_int, + flags: c_ulong) -> c_int; + pub fn SSL_new(ctx: *SSL_CTX) -> *SSL; pub fn SSL_free(ssl: *SSL); pub fn SSL_set_bio(ssl: *SSL, rbio: *BIO, wbio: *BIO); diff --git a/lib.rs b/lib.rs index 6e450cef..dac9cd8f 100644 --- a/lib.rs +++ b/lib.rs @@ -205,6 +205,16 @@ pub struct X509<'ctx> { priv x509: *ffi::X509 } +pub struct X509Name<'x> { + priv name: *ffi::X509_NAME +} + +pub enum X509NameFormat { + Rfc2253 = ffi::XN_FLAG_RFC2253, + Oneline = ffi::XN_FLAG_ONELINE, + Multiline = ffi::XN_FLAG_MULTILINE +} + macro_rules! make_validation_error( ($ok_val:ident, $($name:ident = $val:ident,)+) => ( pub mod hack { @@ -323,8 +333,8 @@ impl Ssl { assert!(bio != ptr::null()); MemBio { - ssl: self, - bio: bio + bio: bio, + owned: false } } @@ -333,8 +343,8 @@ impl Ssl { assert!(bio != ptr::null()); MemBio { - ssl: self, - bio: bio + bio: bio, + owned: false } } @@ -374,9 +384,19 @@ enum LibSslError { ErrorWantAccept = ffi::SSL_ERROR_WANT_ACCEPT, } -struct MemBio<'self> { - ssl: &'self Ssl, - bio: *ffi::BIO +struct MemBio<'ssl> { + bio: *ffi::BIO, + owned: bool +} + +impl<'ssl> Drop for MemBio<'ssl> { + fn drop(&mut self) { + if self.owned { + unsafe { + ffi::BIO_free_all(self.bio); + } + } + } } impl<'self> MemBio<'self> { From 3a2800a2fa47c9722f4fe83047cc5cd5bb71cbfe Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Tue, 26 Nov 2013 18:12:12 -0800 Subject: [PATCH 37/49] Remove hacky macro module --- lib.rs | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/lib.rs b/lib.rs index dac9cd8f..60ac5905 100644 --- a/lib.rs +++ b/lib.rs @@ -16,7 +16,6 @@ use std::io::{Stream, Reader, Writer, Decorator}; use std::vec; use self::error::{SslError, SslSessionClosed, StreamEof}; -use self::hack::X509ValidationError; pub mod error; @@ -165,11 +164,11 @@ impl SslContext { /// Specifies the file that contains trusted CA certificates. pub fn set_CA_file(&mut self, file: &str) -> Option { - let ret = do file.with_c_str |file| { + let ret = file.with_c_str(|file| { unsafe { ffi::SSL_CTX_load_verify_locations(self.ctx, file, ptr::null()) } - }; + }); if ret == 0 { Some(SslError::get()) @@ -217,22 +216,18 @@ pub enum X509NameFormat { macro_rules! make_validation_error( ($ok_val:ident, $($name:ident = $val:ident,)+) => ( - pub mod hack { - use std::libc::c_int; + pub enum X509ValidationError { + $($name,)+ + X509UnknownError(c_int) + } - pub enum X509ValidationError { - $($name,)+ - X509UnknownError(c_int) - } - - impl X509ValidationError { - #[doc(hidden)] - pub fn from_raw(err: c_int) -> Option { - match err { - super::ffi::$ok_val => None, - $(super::ffi::$val => Some($name),)+ - err => Some(X509UnknownError(err)) - } + impl X509ValidationError { + #[doc(hidden)] + pub fn from_raw(err: c_int) -> Option { + match err { + self::ffi::$ok_val => None, + $(self::ffi::$val => Some($name),)+ + err => Some(X509UnknownError(err)) } } } @@ -459,7 +454,7 @@ impl SslStream { } } - fn in_retry_wrapper(&mut self, blk: &fn(&Ssl) -> c_int) + fn in_retry_wrapper(&mut self, blk: |&Ssl| -> c_int) -> Result { loop { let ret = blk(&self.ssl); @@ -512,9 +507,9 @@ impl Writer for SslStream { fn write(&mut self, buf: &[u8]) { let mut start = 0; while start < buf.len() { - let ret = do self.in_retry_wrapper |ssl| { + let ret = self.in_retry_wrapper(|ssl| { ssl.write(buf.slice_from(start)) - }; + }); match ret { Ok(len) => start += len as uint, _ => unreachable!() From 08ef3b5b04782f65f73825bb283fce95dfbabb48 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Tue, 3 Dec 2013 18:55:05 -0800 Subject: [PATCH 38/49] Switch over to new link attribute --- ffi.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ffi.rs b/ffi.rs index 0ff77b74..97c0554e 100644 --- a/ffi.rs +++ b/ffi.rs @@ -97,7 +97,8 @@ pub static XN_FLAG_RFC2253: c_ulong = 0x1110317; pub static XN_FLAG_ONELINE: c_ulong = 0x82031f; pub static XN_FLAG_MULTILINE: c_ulong = 0x2a40006; -#[link_args = "-lssl -lcrypto"] +#[link(name="ssl")] +#[link(name="crypto")] extern "C" { pub fn CRYPTO_num_locks() -> c_int; pub fn CRYPTO_set_locking_callback(func: extern "C" fn(mode: c_int, From 608d6558dea3962f796f9154f0eb2b1dc4390e54 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 8 Dec 2013 22:12:49 -0800 Subject: [PATCH 39/49] Add pointers to enforce lifetimes --- lib.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib.rs b/lib.rs index 60ac5905..4d0591a5 100644 --- a/lib.rs +++ b/lib.rs @@ -194,17 +194,26 @@ impl X509StoreContext { if ptr.is_null() { None } else { - Some(X509 { x509: ptr }) + Some(X509 { ctx: self, x509: ptr }) } } } /// A public key certificate pub struct X509<'ctx> { + priv ctx: &'ctx X509StoreContext, priv x509: *ffi::X509 } +impl<'ctx> X509<'ctx> { + pub fn subject_name<'a>(&'a self) -> X509Name<'a> { + let name = unsafe { ffi::X509_get_subject_name(self.x509) }; + X509Name { x509: self, name: name } + } +} + pub struct X509Name<'x> { + priv x509: &'x X509<'x>, priv name: *ffi::X509_NAME } From e72290b056cf753ffdab6e0a07c944d469f59c8c Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 8 Dec 2013 22:29:37 -0800 Subject: [PATCH 40/49] Make MemBio interface safe --- lib.rs | 48 +++++++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/lib.rs b/lib.rs index 4d0591a5..9b6e14f6 100644 --- a/lib.rs +++ b/lib.rs @@ -332,23 +332,22 @@ impl Ssl { Ok(ssl) } - fn get_rbio<'a>(&'a self) -> MemBio<'a> { - let bio = unsafe { ffi::SSL_get_rbio(self.ssl) }; - assert!(bio != ptr::null()); - - MemBio { - bio: bio, - owned: false - } + fn get_rbio<'a>(&'a self) -> MemBioRef<'a> { + unsafe { self.wrap_bio(ffi::SSL_get_rbio(self.ssl)) } } - fn get_wbio<'a>(&'a self) -> MemBio<'a> { - let bio = unsafe { ffi::SSL_get_wbio(self.ssl) }; - assert!(bio != ptr::null()); + fn get_wbio<'a>(&'a self) -> MemBioRef<'a> { + unsafe { self.wrap_bio(ffi::SSL_get_wbio(self.ssl)) } + } - MemBio { - bio: bio, - owned: false + fn wrap_bio<'a>(&'a self, bio: *ffi::BIO) -> MemBioRef<'a> { + assert!(bio != ptr::null()); + MemBioRef { + ssl: self, + bio: MemBio { + bio: bio, + owned: false + } } } @@ -388,12 +387,27 @@ enum LibSslError { ErrorWantAccept = ffi::SSL_ERROR_WANT_ACCEPT, } -struct MemBio<'ssl> { +struct MemBioRef<'ssl> { + ssl: &'ssl Ssl, + bio: MemBio, +} + +impl<'ssl> MemBioRef<'ssl> { + fn read(&self, buf: &mut [u8]) -> Option { + self.bio.read(buf) + } + + fn write(&self, buf: &[u8]) { + self.bio.write(buf) + } +} + +struct MemBio { bio: *ffi::BIO, owned: bool } -impl<'ssl> Drop for MemBio<'ssl> { +impl Drop for MemBio { fn drop(&mut self) { if self.owned { unsafe { @@ -403,7 +417,7 @@ impl<'ssl> Drop for MemBio<'ssl> { } } -impl<'self> MemBio<'self> { +impl MemBio { fn read(&self, buf: &mut [u8]) -> Option { let ret = unsafe { ffi::BIO_read(self.bio, vec::raw::to_ptr(buf) as *c_void, From bf911311cb931d10223a9ed6a5a3135bd10e9113 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Thu, 12 Dec 2013 21:46:48 -0800 Subject: [PATCH 41/49] Move docs to github --- lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib.rs b/lib.rs index 9b6e14f6..b55dd051 100644 --- a/lib.rs +++ b/lib.rs @@ -3,7 +3,7 @@ // Needed for rustdoc-ng #[link(name="rust-ssl", vers="0.1", package_id="github.com/sfackler/rust-ssl")]; -#[doc(html_root_url="http://sfackler.com/doc/rust-ssl/")]; +#[doc(html_root_url="http://sfackler.github.io/rust-ssl/doc/rust-ssl/")]; use std::cast; use std::libc::{c_int, c_void, c_char}; From 3047782f78f30d20941d2cff360e2b9599171d8f Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 15 Dec 2013 21:48:49 -0800 Subject: [PATCH 42/49] Fix for vec API changes --- lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib.rs b/lib.rs index b55dd051..8839897c 100644 --- a/lib.rs +++ b/lib.rs @@ -356,12 +356,12 @@ impl Ssl { } fn read(&self, buf: &mut [u8]) -> c_int { - unsafe { ffi::SSL_read(self.ssl, vec::raw::to_ptr(buf) as *c_void, + unsafe { ffi::SSL_read(self.ssl, buf.as_ptr() as *c_void, buf.len() as c_int) } } fn write(&self, buf: &[u8]) -> c_int { - unsafe { ffi::SSL_write(self.ssl, vec::raw::to_ptr(buf) as *c_void, + unsafe { ffi::SSL_write(self.ssl, buf.as_ptr() as *c_void, buf.len() as c_int) } } @@ -420,7 +420,7 @@ impl Drop for MemBio { impl MemBio { fn read(&self, buf: &mut [u8]) -> Option { let ret = unsafe { - ffi::BIO_read(self.bio, vec::raw::to_ptr(buf) as *c_void, + ffi::BIO_read(self.bio, buf.as_ptr() as *c_void, buf.len() as c_int) }; @@ -433,7 +433,7 @@ impl MemBio { fn write(&self, buf: &[u8]) { let ret = unsafe { - ffi::BIO_write(self.bio, vec::raw::to_ptr(buf) as *c_void, + ffi::BIO_write(self.bio, buf.as_ptr() as *c_void, buf.len() as c_int) }; assert_eq!(buf.len(), ret as uint); From d4b1c5bf55a6f0181cc5f647f5f93afb06e958ab Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 15 Dec 2013 22:00:30 -0800 Subject: [PATCH 43/49] Remove some unused bindings --- ffi.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/ffi.rs b/ffi.rs index 97c0554e..45192fd3 100644 --- a/ffi.rs +++ b/ffi.rs @@ -144,12 +144,10 @@ extern "C" { pub fn SSL_set_bio(ssl: *SSL, rbio: *BIO, wbio: *BIO); pub fn SSL_get_rbio(ssl: *SSL) -> *BIO; pub fn SSL_get_wbio(ssl: *SSL) -> *BIO; - pub fn SSL_set_connect_state(ssl: *SSL); pub fn SSL_connect(ssl: *SSL) -> c_int; pub fn SSL_get_error(ssl: *SSL, ret: c_int) -> c_int; pub fn SSL_read(ssl: *SSL, buf: *c_void, num: c_int) -> c_int; pub fn SSL_write(ssl: *SSL, buf: *c_void, num: c_int) -> c_int; - pub fn SSL_shutdown(ssl: *SSL) -> c_int; pub fn SSL_get_ex_data_X509_STORE_CTX_idx() -> c_int; pub fn SSL_get_SSL_CTX(ssl: *SSL) -> *SSL_CTX; From ce86e4a4c4005afcbf601abf28ec346024ee40b1 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 15 Dec 2013 22:05:33 -0800 Subject: [PATCH 44/49] Switch from link to pkgid --- lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib.rs b/lib.rs index 8839897c..c70da302 100644 --- a/lib.rs +++ b/lib.rs @@ -1,8 +1,5 @@ #[feature(struct_variant, macro_rules)]; - -// Needed for rustdoc-ng -#[link(name="rust-ssl", vers="0.1", - package_id="github.com/sfackler/rust-ssl")]; +#[pkgid="github.com/sfackler/rust-ssl"]; #[doc(html_root_url="http://sfackler.github.io/rust-ssl/doc/rust-ssl/")]; use std::cast; From f00d3e14bca12a46d84168408282db352c7d5816 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 15 Dec 2013 22:11:04 -0800 Subject: [PATCH 45/49] Fix doc root --- lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib.rs b/lib.rs index c70da302..ff759ec8 100644 --- a/lib.rs +++ b/lib.rs @@ -1,6 +1,6 @@ #[feature(struct_variant, macro_rules)]; #[pkgid="github.com/sfackler/rust-ssl"]; -#[doc(html_root_url="http://sfackler.github.io/rust-ssl/doc/rust-ssl/")]; +#[doc(html_root_url="http://sfackler.github.io/rust-ssl/doc/")]; use std::cast; use std::libc::{c_int, c_void, c_char}; From 213adb34328f9e0e4a195053fe4a6766bee77eb1 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Thu, 19 Dec 2013 23:13:53 -0800 Subject: [PATCH 46/49] pkgid -> crate_id --- lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib.rs b/lib.rs index ff759ec8..6f0d3e5a 100644 --- a/lib.rs +++ b/lib.rs @@ -1,5 +1,5 @@ #[feature(struct_variant, macro_rules)]; -#[pkgid="github.com/sfackler/rust-ssl"]; +#[crate_id="github.com/sfackler/rust-ssl"]; #[doc(html_root_url="http://sfackler.github.io/rust-ssl/doc/")]; use std::cast; From 7db74d5b4f43cd1ede4e2335012faa491cbf15d6 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Fri, 27 Dec 2013 07:48:26 -0700 Subject: [PATCH 47/49] Update for API change --- lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib.rs b/lib.rs index 6f0d3e5a..8725080c 100644 --- a/lib.rs +++ b/lib.rs @@ -6,8 +6,8 @@ use std::cast; use std::libc::{c_int, c_void, c_char}; use std::ptr; use std::task; -use std::unstable::atomics::{AtomicBool, INIT_ATOMIC_BOOL, AtomicUint, - INIT_ATOMIC_UINT, Acquire, Release, SeqCst}; +use std::sync::atomics::{AtomicBool, INIT_ATOMIC_BOOL, AtomicUint, + INIT_ATOMIC_UINT, Acquire, Release, SeqCst}; use std::unstable::mutex::Mutex; use std::io::{Stream, Reader, Writer, Decorator}; use std::vec; From f40a7d1663c6a63ad4ddd79156a5516606a768b6 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 28 Dec 2013 08:44:58 -0700 Subject: [PATCH 48/49] Relicense under Apache V2 --- LICENSE | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/LICENSE b/LICENSE index 9772ab9c..fea12d25 100644 --- a/LICENSE +++ b/LICENSE @@ -1,20 +1,13 @@ -The MIT License (MIT) +Copyright 2013 Steven Fackler -Copyright (c) 2013 Steven Fackler +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: + http://www.apache.org/licenses/LICENSE-2.0 -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. From 87da25240d71135400f38b6c0dbe666efd3be5a6 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 28 Dec 2013 16:57:54 -0700 Subject: [PATCH 49/49] Prepare rust-ssl to merge into rust-openssl --- LICENSE | 13 ------------- README.md | 2 -- error.rs => ssl/error.rs | 0 ffi.rs => ssl/ffi.rs | 0 lib.rs => ssl/lib.rs | 0 test.rs => ssl/test.rs | 0 {test => ssl/test}/cert.pem | 0 {test => ssl/test}/key.pem | 0 8 files changed, 15 deletions(-) delete mode 100644 LICENSE delete mode 100644 README.md rename error.rs => ssl/error.rs (100%) rename ffi.rs => ssl/ffi.rs (100%) rename lib.rs => ssl/lib.rs (100%) rename test.rs => ssl/test.rs (100%) rename {test => ssl/test}/cert.pem (100%) rename {test => ssl/test}/key.pem (100%) diff --git a/LICENSE b/LICENSE deleted file mode 100644 index fea12d25..00000000 --- a/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright 2013 Steven Fackler - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/README.md b/README.md deleted file mode 100644 index 2a55142d..00000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -rust-ssl -======== diff --git a/error.rs b/ssl/error.rs similarity index 100% rename from error.rs rename to ssl/error.rs diff --git a/ffi.rs b/ssl/ffi.rs similarity index 100% rename from ffi.rs rename to ssl/ffi.rs diff --git a/lib.rs b/ssl/lib.rs similarity index 100% rename from lib.rs rename to ssl/lib.rs diff --git a/test.rs b/ssl/test.rs similarity index 100% rename from test.rs rename to ssl/test.rs diff --git a/test/cert.pem b/ssl/test/cert.pem similarity index 100% rename from test/cert.pem rename to ssl/test/cert.pem diff --git a/test/key.pem b/ssl/test/key.pem similarity index 100% rename from test/key.pem rename to ssl/test/key.pem