From 52a06adc08083a1720603f90b503a384616a616a Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Tue, 26 Dec 2017 20:54:34 -0700 Subject: Overhaul ssl error --- openssl/src/ssl/error.rs | 154 +++-- openssl/src/ssl/mod.rs | 133 ++-- openssl/src/ssl/test.rs | 1298 +++++++++++++++++++++++++++++++++++ openssl/src/ssl/tests/mod.rs | 1422 --------------------------------------- openssl/src/ssl/tests/select.rs | 74 -- 5 files changed, 1440 insertions(+), 1641 deletions(-) create mode 100644 openssl/src/ssl/test.rs delete mode 100644 openssl/src/ssl/tests/mod.rs delete mode 100644 openssl/src/ssl/tests/select.rs (limited to 'openssl/src') diff --git a/openssl/src/ssl/error.rs b/openssl/src/ssl/error.rs index b0641dfd..c0bc80ae 100644 --- a/openssl/src/ssl/error.rs +++ b/openssl/src/ssl/error.rs @@ -1,3 +1,5 @@ +use ffi; +use libc::c_int; use std::error; use std::error::Error as StdError; use std::fmt; @@ -7,90 +9,114 @@ use error::ErrorStack; use ssl::MidHandshakeSslStream; use x509::X509VerifyResult; -/// An SSL error. -// FIXME this is missing variants +/// An error code returned from SSL functions. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct ErrorCode(c_int); + +impl ErrorCode { + pub fn from_raw(raw: c_int) -> ErrorCode { + ErrorCode(raw) + } + + pub fn as_raw(&self) -> c_int { + self.0 + } + + /// The SSL session has been closed. + pub const ZERO_RETURN: ErrorCode = ErrorCode(ffi::SSL_ERROR_ZERO_RETURN); + + /// An attempt to read data from the underlying socket returned `WouldBlock`. + /// + /// Wait for read readiness and retry the operation. + pub const WANT_READ: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_READ); + + /// An attempt to write data to the underlying socket returned `WouldBlock`. + /// + /// Wait for write readiness and retry the operation. + pub const WANT_WRITE: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_WRITE); + + /// A non-recoverable IO error occurred. + pub const SYSCALL: ErrorCode = ErrorCode(ffi::SSL_ERROR_SYSCALL); + + /// An error occurred in the SSL library. + pub const SSL: ErrorCode = ErrorCode(ffi::SSL_ERROR_SSL); +} + #[derive(Debug)] -pub enum Error { - /// The SSL session has been closed by the other end - ZeroReturn, - /// An attempt to read data from the underlying socket returned - /// `WouldBlock`. Wait for read readiness and reattempt the operation. - WantRead(io::Error), - /// An attempt to write data from the underlying socket returned - /// `WouldBlock`. Wait for write readiness and reattempt the operation. - WantWrite(io::Error), - /// The client certificate callback requested to be called again. - WantX509Lookup, - /// An error reported by the underlying stream. - Stream(io::Error), - /// An error in the OpenSSL library. +pub(crate) enum InnerError { + Io(io::Error), Ssl(ErrorStack), } -impl fmt::Display for Error { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str(self.description())?; - if let Some(err) = self.cause() { - write!(fmt, ": {}", err) - } else { - Ok(()) - } - } +/// An SSL error. +#[derive(Debug)] +pub struct Error { + pub(crate) code: ErrorCode, + pub(crate) cause: Option, } -impl error::Error for Error { - fn description(&self) -> &str { - match *self { - Error::ZeroReturn => "The SSL session was closed by the other end", - Error::WantRead(_) => "A read attempt returned a `WouldBlock` error", - Error::WantWrite(_) => "A write attempt returned a `WouldBlock` error", - Error::WantX509Lookup => "The client certificate callback requested to be called again", - Error::Stream(_) => "The underlying stream reported an error", - Error::Ssl(_) => "The OpenSSL library reported an error", - } +impl Error { + pub fn code(&self) -> ErrorCode { + self.code } - fn cause(&self) -> Option<&error::Error> { - match *self { - Error::WantRead(ref err) => Some(err), - Error::WantWrite(ref err) => Some(err), - Error::Stream(ref err) => Some(err), - Error::Ssl(ref err) => Some(err), + pub fn io_error(&self) -> Option<&io::Error> { + match self.cause { + Some(InnerError::Io(ref e)) => Some(e), _ => None, } } -} -impl From for Error { - fn from(e: ErrorStack) -> Error { - Error::Ssl(e) + pub fn into_io_error(self) -> Result { + match self.cause { + Some(InnerError::Io(e)) => Ok(e), + _ => Err(self), + } } -} -/// An error indicating that the operation can be immediately retried. -/// -/// OpenSSL's [`SSL_read`] and [`SSL_write`] functions can return `SSL_ERROR_WANT_READ` even when -/// the underlying socket is performing blocking IO in certain cases. When this happens, the -/// the operation can be immediately retried. -/// -/// To signal this event, the `io::Error` inside of [`Error::WantRead`] will be constructed around -/// a `RetryError`. -/// -/// [`SSL_read`]: https://www.openssl.org/docs/manmaster/man3/SSL_read.html -/// [`SSL_write`]: https://www.openssl.org/docs/manmaster/man3/SSL_write.html -/// [`Error::WantRead`]: enum.Error.html#variant.WantRead -#[derive(Debug)] -pub struct RetryError; + pub fn ssl_error(&self) -> Option<&ErrorStack> { + match self.cause { + Some(InnerError::Ssl(ref e)) => Some(e), + _ => None, + } + } +} -impl fmt::Display for RetryError { +impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str(error::Error::description(self)) + match self.code { + ErrorCode::ZERO_RETURN => fmt.write_str("the SSL session has been shut down"), + ErrorCode::WANT_READ => match self.io_error() { + Some(_) => fmt.write_str("a nonblocking read call would have blocked"), + None => fmt.write_str("the operation should be retried"), + }, + ErrorCode::SYSCALL => match self.io_error() { + Some(err) => write!(fmt, "the inner stream returned an error: {}", err), + None => fmt.write_str("unexpected EOF"), + }, + ErrorCode::SSL => { + fmt.write_str("OpenSSL error")?; + if let Some(ref err) = self.ssl_error() { + write!(fmt, ": {}", err)? + } + Ok(()) + } + ErrorCode(code) => write!(fmt, "unknown error code {}", code), + } } } -impl error::Error for RetryError { +impl error::Error for Error { fn description(&self) -> &str { - "operation must be retried" + "an OpenSSL error" + } + + fn cause(&self) -> Option<&error::Error> { + match self.cause { + Some(InnerError::Io(ref e)) => Some(e), + Some(InnerError::Ssl(ref e)) => Some(e), + None => None, + } } } diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index c0ed5572..5ef04fca 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -97,18 +97,19 @@ use error::ErrorStack; use ex_data::Index; use stack::{Stack, StackRef}; use ssl::bio::BioMethod; +use ssl::error::InnerError; use ssl::callbacks::*; pub use ssl::connector::{ConnectConfiguration, SslAcceptor, SslAcceptorBuilder, SslConnector, SslConnectorBuilder}; -pub use ssl::error::{Error, HandshakeError, RetryError}; +pub use ssl::error::{Error, ErrorCode, HandshakeError}; mod error; mod callbacks; mod connector; mod bio; #[cfg(test)] -mod tests; +mod test; bitflags! { /// Options controlling the behavior of an `SslContext`. @@ -1463,8 +1464,8 @@ impl SslRef { unsafe { ffi::SSL_write(self.as_ptr(), buf.as_ptr() as *const c_void, len) } } - fn get_error(&self, ret: c_int) -> c_int { - unsafe { ffi::SSL_get_error(self.as_ptr(), ret) } + fn get_error(&self, ret: c_int) -> ErrorCode { + unsafe { ErrorCode::from_raw(ffi::SSL_get_error(self.as_ptr(), ret)) } } /// Like [`SslContextBuilder::set_verify`]. @@ -2053,16 +2054,14 @@ impl Ssl { if ret > 0 { Ok(stream) } else { - match stream.make_error(ret) { - e @ Error::WantWrite(_) | e @ Error::WantRead(_) => { - Err(HandshakeError::WouldBlock(MidHandshakeSslStream { - stream: stream, - error: e, - })) - } - err => Err(HandshakeError::Failure(MidHandshakeSslStream { - stream: stream, - error: err, + let error = stream.make_error(ret); + match error.code() { + ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => Err(HandshakeError::WouldBlock( + MidHandshakeSslStream { stream, error }, + )), + _ => Err(HandshakeError::Failure(MidHandshakeSslStream { + stream, + error, })), } } @@ -2087,16 +2086,14 @@ impl Ssl { if ret > 0 { Ok(stream) } else { - match stream.make_error(ret) { - e @ Error::WantWrite(_) | e @ Error::WantRead(_) => { - Err(HandshakeError::WouldBlock(MidHandshakeSslStream { - stream: stream, - error: e, - })) - } - err => Err(HandshakeError::Failure(MidHandshakeSslStream { - stream: stream, - error: err, + let error = stream.make_error(ret); + match error.code() { + ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => Err(HandshakeError::WouldBlock( + MidHandshakeSslStream { stream, error }, + )), + _ => Err(HandshakeError::Failure(MidHandshakeSslStream { + stream, + error, })), } } @@ -2146,15 +2143,12 @@ impl MidHandshakeSslStream { if ret > 0 { Ok(self.stream) } else { - match self.stream.make_error(ret) { - e @ Error::WantWrite(_) | e @ Error::WantRead(_) => { - self.error = e; + self.error = self.stream.make_error(ret); + match self.error.code() { + ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => { Err(HandshakeError::WouldBlock(self)) } - err => { - self.error = err; - Err(HandshakeError::Failure(self)) - } + _ => Err(HandshakeError::Failure(self)), } } } @@ -2225,12 +2219,7 @@ impl SslStream { if ret > 0 { Ok(ret as usize) } else { - match self.make_error(ret) { - // FIXME only do this in read - // Don't treat unexpected EOFs as errors when reading - Error::Stream(ref e) if e.kind() == io::ErrorKind::ConnectionAborted => Ok(0), - e => Err(e), - } + Err(self.make_error(ret)) } } @@ -2280,45 +2269,26 @@ impl SslStream { fn make_error(&mut self, ret: c_int) -> Error { self.check_panic(); - match self.ssl.get_error(ret) { - ffi::SSL_ERROR_SSL => Error::Ssl(ErrorStack::get()), - ffi::SSL_ERROR_SYSCALL => { + let code = self.ssl.get_error(ret); + + let cause = match code { + ErrorCode::SSL => Some(InnerError::Ssl(ErrorStack::get())), + ErrorCode::SYSCALL => { let errs = ErrorStack::get(); if errs.errors().is_empty() { - match self.get_bio_error() { - Some(err) => Error::Stream(err), - None => Error::Stream(io::Error::new( - io::ErrorKind::ConnectionAborted, - "unexpected EOF observed", - )), - } + self.get_bio_error().map(InnerError::Io) } else { - Error::Ssl(errs) + Some(InnerError::Ssl(errs)) } } - ffi::SSL_ERROR_ZERO_RETURN => Error::ZeroReturn, - ffi::SSL_ERROR_WANT_WRITE => { - let err = match self.get_bio_error() { - Some(err) => err, - None => io::Error::new( - io::ErrorKind::Other, - "BUG: got an SSL_ERROR_WANT_WRITE with no error in the BIO", - ), - }; - Error::WantWrite(err) + ErrorCode::ZERO_RETURN => None, + ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => { + self.get_bio_error().map(InnerError::Io) } - ffi::SSL_ERROR_WANT_READ => { - let err = match self.get_bio_error() { - Some(err) => err, - None => io::Error::new(io::ErrorKind::Other, RetryError), - }; - Error::WantRead(err) - } - err => Error::Stream(io::Error::new( - io::ErrorKind::InvalidData, - format!("unexpected error {}", err), - )), - } + _ => None, + }; + + Error { code, cause } } fn check_panic(&mut self) { @@ -2363,13 +2333,15 @@ impl Read for SslStream { loop { match self.ssl_read(buf) { Ok(n) => return Ok(n), - Err(Error::ZeroReturn) => return Ok(0), - Err(Error::WantRead(ref e)) - if e.get_ref().map_or(false, |e| e.is::()) => {} - Err(Error::Stream(e)) | Err(Error::WantRead(e)) | Err(Error::WantWrite(e)) => { - return Err(e); + Err(ref e) if e.code() == ErrorCode::ZERO_RETURN => return Ok(0), + Err(ref e) if e.code() == ErrorCode::SYSCALL && e.io_error().is_none() => { + return Ok(0) + } + Err(ref e) if e.code() == ErrorCode::WANT_READ && e.io_error().is_none() => {} + Err(e) => { + return Err(e.into_io_error() + .unwrap_or_else(|e| io::Error::new(io::ErrorKind::Other, e))) } - Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)), } } } @@ -2380,12 +2352,11 @@ impl Write for SslStream { loop { match self.ssl_write(buf) { Ok(n) => return Ok(n), - Err(Error::WantRead(ref e)) - if e.get_ref().map_or(false, |e| e.is::()) => {} - Err(Error::Stream(e)) | Err(Error::WantRead(e)) | Err(Error::WantWrite(e)) => { - return Err(e); + Err(ref e) if e.code() == ErrorCode::WANT_READ && e.io_error().is_none() => {} + Err(e) => { + return Err(e.into_io_error() + .unwrap_or_else(|e| io::Error::new(io::ErrorKind::Other, e))) } - Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)), } } } diff --git a/openssl/src/ssl/test.rs b/openssl/src/ssl/test.rs new file mode 100644 index 00000000..3beaf846 --- /dev/null +++ b/openssl/src/ssl/test.rs @@ -0,0 +1,1298 @@ +#![allow(unused_imports)] + +use std::env; +use std::fs::File; +use std::io::prelude::*; +use std::io::{self, BufReader}; +use std::iter; +use std::mem; +use std::net::{SocketAddr, TcpListener, TcpStream}; +use std::path::Path; +use std::process::{Child, ChildStdin, Command, Stdio}; +use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT}; +use std::thread; +use std::time::Duration; +use tempdir::TempDir; + +use dh::Dh; +use hash::MessageDigest; +use ocsp::{OcspResponse, OcspResponseStatus}; +use ssl; +use ssl::{Error, HandshakeError, ShutdownResult, Ssl, SslAcceptor, SslConnector, SslContext, + SslMethod, SslStream, SslVerifyMode, StatusType}; +use x509::{X509, X509Filetype, X509Name, X509StoreContext, X509VerifyResult}; +#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] +use x509::verify::X509CheckFlags; +use pkey::PKey; + +use std::net::UdpSocket; + +static ROOT_CERT: &'static [u8] = include_bytes!("../../test/root-ca.pem"); +static CERT: &'static [u8] = include_bytes!("../../test/cert.pem"); +static KEY: &'static [u8] = include_bytes!("../../test/key.pem"); + +fn next_addr() -> SocketAddr { + use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; + static PORT: AtomicUsize = ATOMIC_USIZE_INIT; + let port = 15411 + PORT.fetch_add(1, Ordering::SeqCst); + + format!("127.0.0.1:{}", port).parse().unwrap() +} + +struct Server { + p: Child, + _temp: TempDir, +} + +impl Server { + fn spawn(args: &[&str], input: Option>) -> (Server, SocketAddr) { + let td = TempDir::new("openssl").unwrap(); + let cert = td.path().join("cert.pem"); + let key = td.path().join("key.pem"); + File::create(&cert).unwrap().write_all(CERT).unwrap(); + File::create(&key).unwrap().write_all(KEY).unwrap(); + + let addr = next_addr(); + let mut child = Command::new("openssl") + .arg("s_server") + .arg("-accept") + .arg(addr.port().to_string()) + .args(args) + .arg("-cert") + .arg(&cert) + .arg("-key") + .arg(&key) + .arg("-no_dhe") + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .stdin(Stdio::piped()) + .spawn() + .unwrap(); + let stdin = child.stdin.take().unwrap(); + if let Some(mut input) = input { + thread::spawn(move || input(stdin)); + } + ( + Server { + p: child, + _temp: td, + }, + addr, + ) + } + + fn new_tcp(args: &[&str]) -> (Server, TcpStream) { + let (server, addr) = Server::spawn(args, None); + for _ in 0..20 { + match TcpStream::connect(&addr) { + Ok(s) => return (server, s), + Err(ref e) if e.kind() == io::ErrorKind::ConnectionRefused => { + thread::sleep(Duration::from_millis(100)); + } + Err(e) => panic!("wut: {}", e), + } + } + panic!("server never came online"); + } + + fn new() -> (Server, TcpStream) { + Server::new_tcp(&["-www"]) + } + + #[allow(dead_code)] + fn new_alpn() -> (Server, TcpStream) { + Server::new_tcp(&[ + "-www", + "-nextprotoneg", + "http/1.1,spdy/3.1", + "-alpn", + "http/1.1,spdy/3.1", + ]) + } +} + +impl Drop for Server { + fn drop(&mut self) { + let _ = self.p.kill(); + let _ = self.p.wait(); + } +} + +macro_rules! run_test( + ($module:ident, $blk:expr) => ( + #[cfg(test)] + mod $module { + use std::io; + use std::io::prelude::*; + use std::path::Path; + use std::net::UdpSocket; + use std::net::TcpStream; + use ssl; + use ssl::SslMethod; + use ssl::{SslContext, Ssl, SslStream, SslVerifyMode, SslOptions}; + use hash::MessageDigest; + use x509::{X509StoreContext, X509VerifyResult}; + #[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] + use x509::X509; + #[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] + use x509::store::X509StoreBuilder; + use hex::FromHex; + use foreign_types::ForeignTypeRef; + use super::Server; + #[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] + use super::ROOT_CERT; + + #[test] + fn sslv23() { + let (_s, stream) = Server::new(); + $blk(SslMethod::tls(), stream); + } + } + ); +); + +run_test!(new_ctx, |method, _| { + SslContext::builder(method).unwrap(); +}); + +run_test!(verify_untrusted, |method, stream| { + let mut ctx = SslContext::builder(method).unwrap(); + ctx.set_verify(SslVerifyMode::PEER); + + match Ssl::new(&ctx.build()).unwrap().connect(stream) { + Ok(_) => panic!("expected failure"), + Err(err) => println!("error {:?}", err), + } +}); + +run_test!(verify_trusted, |method, stream| { + let mut ctx = SslContext::builder(method).unwrap(); + ctx.set_verify(SslVerifyMode::PEER); + + match ctx.set_ca_file(&Path::new("test/root-ca.pem")) { + Ok(_) => {} + Err(err) => panic!("Unexpected error {:?}", err), + } + match Ssl::new(&ctx.build()).unwrap().connect(stream) { + Ok(_) => (), + Err(err) => panic!("Expected success, got {:?}", err), + } +}); + +#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] +run_test!(verify_trusted_with_set_cert, |method, stream| { + let x509 = X509::from_pem(ROOT_CERT).unwrap(); + let mut store = X509StoreBuilder::new().unwrap(); + store.add_cert(x509).unwrap(); + + let mut ctx = SslContext::builder(method).unwrap(); + ctx.set_verify(SslVerifyMode::PEER); + + match ctx.set_verify_cert_store(store.build()) { + Ok(_) => {} + Err(err) => panic!("Unexpected error {:?}", err), + } + match Ssl::new(&ctx.build()).unwrap().connect(stream) { + Ok(_) => (), + Err(err) => panic!("Expected success, got {:?}", err), + } +}); + +run_test!(verify_untrusted_callback_override_ok, |method, stream| { + let mut ctx = SslContext::builder(method).unwrap(); + ctx.set_verify_callback(SslVerifyMode::PEER, |_, _| true); + + match Ssl::new(&ctx.build()).unwrap().connect(stream) { + Ok(_) => (), + Err(err) => panic!("Expected success, got {:?}", err), + } +}); + +run_test!(verify_untrusted_callback_override_bad, |method, stream| { + let mut ctx = SslContext::builder(method).unwrap(); + ctx.set_verify_callback(SslVerifyMode::PEER, |_, _| false); + + assert!(Ssl::new(&ctx.build()).unwrap().connect(stream).is_err()); +}); + +run_test!(verify_trusted_callback_override_ok, |method, stream| { + let mut ctx = SslContext::builder(method).unwrap(); + ctx.set_verify_callback(SslVerifyMode::PEER, |_, _| true); + + match ctx.set_ca_file(&Path::new("test/cert.pem")) { + Ok(_) => {} + Err(err) => panic!("Unexpected error {:?}", err), + } + match Ssl::new(&ctx.build()).unwrap().connect(stream) { + Ok(_) => (), + Err(err) => panic!("Expected success, got {:?}", err), + } +}); + +run_test!(verify_trusted_callback_override_bad, |method, stream| { + let mut ctx = SslContext::builder(method).unwrap(); + ctx.set_verify_callback(SslVerifyMode::PEER, |_, _| false); + + match ctx.set_ca_file(&Path::new("test/cert.pem")) { + Ok(_) => {} + Err(err) => panic!("Unexpected error {:?}", err), + } + assert!(Ssl::new(&ctx.build()).unwrap().connect(stream).is_err()); +}); + +run_test!(verify_callback_load_certs, |method, stream| { + let mut ctx = SslContext::builder(method).unwrap(); + ctx.set_verify_callback(SslVerifyMode::PEER, |_, x509_ctx| { + assert!(x509_ctx.current_cert().is_some()); + true + }); + + assert!(Ssl::new(&ctx.build()).unwrap().connect(stream).is_ok()); +}); + +run_test!(verify_trusted_get_error_ok, |method, stream| { + let mut ctx = SslContext::builder(method).unwrap(); + ctx.set_verify_callback(SslVerifyMode::PEER, |_, x509_ctx| { + assert!(x509_ctx.error() == X509VerifyResult::OK); + true + }); + + match ctx.set_ca_file(&Path::new("test/root-ca.pem")) { + Ok(_) => {} + Err(err) => panic!("Unexpected error {:?}", err), + } + assert!(Ssl::new(&ctx.build()).unwrap().connect(stream).is_ok()); +}); + +run_test!(verify_trusted_get_error_err, |method, stream| { + let mut ctx = SslContext::builder(method).unwrap(); + ctx.set_verify_callback(SslVerifyMode::PEER, |_, x509_ctx| { + assert_ne!(x509_ctx.error(), X509VerifyResult::OK); + false + }); + + assert!(Ssl::new(&ctx.build()).unwrap().connect(stream).is_err()); +}); + +run_test!(verify_callback_data, |method, stream| { + let mut ctx = SslContext::builder(method).unwrap(); + + // Node id was generated as SHA256 hash of certificate "test/cert.pem" + // in DER format. + // Command: openssl x509 -in test/cert.pem -outform DER | openssl dgst -sha256 + // Please update if "test/cert.pem" will ever change + let node_hash_str = "59172d9313e84459bcff27f967e79e6e9217e584"; + let node_id = Vec::from_hex(node_hash_str).unwrap(); + ctx.set_verify_callback(SslVerifyMode::PEER, move |_preverify_ok, x509_ctx| { + let cert = x509_ctx.current_cert(); + match cert { + None => false, + Some(cert) => { + let fingerprint = cert.fingerprint(MessageDigest::sha1()).unwrap(); + fingerprint == node_id + } + } + }); + ctx.set_verify_depth(1); + + match Ssl::new(&ctx.build()).unwrap().connect(stream) { + Ok(_) => (), + Err(err) => panic!("Expected success, got {:?}", err), + } +}); + +run_test!(ssl_verify_callback, |method, stream| { + use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; + + static CHECKED: AtomicUsize = ATOMIC_USIZE_INIT; + + let ctx = SslContext::builder(method).unwrap(); + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + + let node_hash_str = "59172d9313e84459bcff27f967e79e6e9217e584"; + let node_id = Vec::from_hex(node_hash_str).unwrap(); + ssl.set_verify_callback(SslVerifyMode::PEER, move |_, x509| { + CHECKED.store(1, Ordering::SeqCst); + match x509.current_cert() { + None => false, + Some(cert) => { + let fingerprint = cert.fingerprint(MessageDigest::sha1()).unwrap(); + fingerprint == node_id + } + } + }); + + match ssl.connect(stream) { + Ok(_) => (), + Err(err) => panic!("Expected success, got {:?}", err), + } + + assert_eq!(CHECKED.load(Ordering::SeqCst), 1); +}); + +// Make sure every write call translates to a write call to the underlying socket. +#[test] +fn test_write_hits_stream() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = listener.local_addr().unwrap(); + + let guard = thread::spawn(move || { + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let stream = TcpStream::connect(addr).unwrap(); + let mut stream = Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap(); + + stream.write_all(b"hello").unwrap(); + stream + }); + + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_verify(SslVerifyMode::PEER); + ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM) + .unwrap(); + ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM) + .unwrap(); + let stream = listener.accept().unwrap().0; + let mut stream = Ssl::new(&ctx.build()).unwrap().accept(stream).unwrap(); + + let mut buf = [0; 5]; + assert_eq!(5, stream.read(&mut buf).unwrap()); + assert_eq!(&b"hello"[..], &buf[..]); + guard.join().unwrap(); +} + +#[test] +fn test_set_certificate_and_private_key() { + let key = include_bytes!("../../test/key.pem"); + let key = PKey::private_key_from_pem(key).unwrap(); + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_private_key(&key).unwrap(); + ctx.set_certificate(&cert).unwrap(); + + assert!(ctx.check_private_key().is_ok()); +} + +run_test!(get_ctx_options, |method, _| { + let ctx = SslContext::builder(method).unwrap(); + ctx.options(); +}); + +run_test!(set_ctx_options, |method, _| { + let mut ctx = SslContext::builder(method).unwrap(); + let opts = ctx.set_options(SslOptions::NO_TICKET); + assert!(opts.contains(SslOptions::NO_TICKET)); +}); + +run_test!(clear_ctx_options, |method, _| { + let mut ctx = SslContext::builder(method).unwrap(); + ctx.set_options(SslOptions::ALL); + let opts = ctx.clear_options(SslOptions::ALL); + assert!(!opts.contains(SslOptions::ALL)); +}); + +#[test] +fn test_write() { + let (_s, stream) = Server::new(); + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let mut stream = Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap(); + stream.write_all("hello".as_bytes()).unwrap(); + stream.flush().unwrap(); + stream.write_all(" there".as_bytes()).unwrap(); + stream.flush().unwrap(); +} + +#[test] +fn zero_length_buffers() { + let (_s, stream) = Server::new(); + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let mut stream = Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap(); + + assert_eq!(stream.write(b"").unwrap(), 0); + assert_eq!(stream.read(&mut []).unwrap(), 0); +} + +run_test!(get_peer_certificate, |method, stream| { + let ctx = SslContext::builder(method).unwrap(); + let stream = Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap(); + let cert = stream.ssl().peer_certificate().unwrap(); + let fingerprint = cert.fingerprint(MessageDigest::sha1()).unwrap(); + let node_hash_str = "59172d9313e84459bcff27f967e79e6e9217e584"; + let node_id = Vec::from_hex(node_hash_str).unwrap(); + assert_eq!(node_id, fingerprint) +}); + +#[test] +fn test_read() { + let (_s, tcp) = Server::new(); + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let mut stream = Ssl::new(&ctx.build()).unwrap().connect(tcp).unwrap(); + stream.write_all("GET /\r\n\r\n".as_bytes()).unwrap(); + stream.flush().unwrap(); + io::copy(&mut stream, &mut io::sink()) + .ok() + .expect("read error"); +} + +#[test] +fn test_pending() { + let (_s, tcp) = Server::new(); + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let mut stream = Ssl::new(&ctx.build()).unwrap().connect(tcp).unwrap(); + stream.write_all("GET /\r\n\r\n".as_bytes()).unwrap(); + stream.flush().unwrap(); + + // wait for the response and read first byte... + let mut buf = [0u8; 16 * 1024]; + stream.read(&mut buf[..1]).unwrap(); + + let pending = stream.ssl().pending(); + let len = stream.read(&mut buf[1..]).unwrap(); + + assert_eq!(pending, len); + + stream.read(&mut buf[..1]).unwrap(); + + let pending = stream.ssl().pending(); + let len = stream.read(&mut buf[1..]).unwrap(); + assert_eq!(pending, len); +} + +#[test] +fn test_state() { + let (_s, tcp) = Server::new(); + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let stream = Ssl::new(&ctx.build()).unwrap().connect(tcp).unwrap(); + assert_eq!(stream.ssl().state_string(), "SSLOK "); + assert_eq!( + stream.ssl().state_string_long(), + "SSL negotiation finished successfully" + ); +} + +/// Tests that connecting with the client using ALPN, but the server not does not +/// break the existing connection behavior. +#[test] +#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] +fn test_connect_with_unilateral_alpn() { + let (_s, stream) = Server::new(); + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_verify(SslVerifyMode::PEER); + ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]).unwrap(); + match ctx.set_ca_file(&Path::new("test/root-ca.pem")) { + Ok(_) => {} + Err(err) => panic!("Unexpected error {:?}", err), + } + let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) { + Ok(stream) => stream, + Err(err) => panic!("Expected success, got {:?}", err), + }; + // Since the socket to which we connected is not configured to use ALPN, + // there should be no selected protocol... + assert!(stream.ssl().selected_alpn_protocol().is_none()); +} + +/// Tests that connecting with the client using NPN, but the server not does not +/// break the existing connection behavior. +#[test] +#[cfg(not(any(libressl261, libressl262, libressl26x)))] +fn test_connect_with_unilateral_npn() { + let (_s, stream) = Server::new(); + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_verify(SslVerifyMode::PEER); + ctx.set_npn_protocols(&[b"http/1.1", b"spdy/3.1"]).unwrap(); + match ctx.set_ca_file(&Path::new("test/root-ca.pem")) { + Ok(_) => {} + Err(err) => panic!("Unexpected error {:?}", err), + } + let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) { + Ok(stream) => stream, + Err(err) => panic!("Expected success, got {:?}", err), + }; + // Since the socket to which we connected is not configured to use NPN, + // there should be no selected protocol... + assert!(stream.ssl().selected_npn_protocol().is_none()); +} + +/// Tests that when both the client as well as the server use ALPN and their +/// lists of supported protocols have an overlap, the correct protocol is chosen. +#[test] +#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] +fn test_connect_with_alpn_successful_multiple_matching() { + let (_s, stream) = Server::new_alpn(); + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_verify(SslVerifyMode::PEER); + ctx.set_alpn_protocols(&[b"spdy/3.1", b"http/1.1"]).unwrap(); + match ctx.set_ca_file(&Path::new("test/root-ca.pem")) { + Ok(_) => {} + Err(err) => panic!("Unexpected error {:?}", err), + } + let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) { + Ok(stream) => stream, + Err(err) => panic!("Expected success, got {:?}", err), + }; + // The server prefers "http/1.1", so that is chosen, even though the client + // would prefer "spdy/3.1" + assert_eq!(b"http/1.1", stream.ssl().selected_alpn_protocol().unwrap()); +} + +/// Tests that when both the client as well as the server use NPN and their +/// lists of supported protocols have an overlap, the correct protocol is chosen. +#[test] +#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] +fn test_connect_with_npn_successful_multiple_matching() { + let (_s, stream) = Server::new_alpn(); + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_verify(SslVerifyMode::PEER); + ctx.set_npn_protocols(&[b"spdy/3.1", b"http/1.1"]).unwrap(); + match ctx.set_ca_file(&Path::new("test/root-ca.pem")) { + Ok(_) => {} + Err(err) => panic!("Unexpected error {:?}", err), + } + let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) { + Ok(stream) => stream, + Err(err) => panic!("Expected success, got {:?}", err), + }; + // The server prefers "http/1.1", so that is chosen, even though the client + // would prefer "spdy/3.1" + assert_eq!(b"http/1.1", stream.ssl().selected_npn_protocol().unwrap()); +} + +/// Tests that when both the client as well as the server use ALPN and their +/// lists of supported protocols have an overlap -- with only ONE protocol +/// being valid for both. +#[test] +#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] +fn test_connect_with_alpn_successful_single_match() { + let (_s, stream) = Server::new_alpn(); + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_verify(SslVerifyMode::PEER); + ctx.set_alpn_protocols(&[b"spdy/3.1"]).unwrap(); + match ctx.set_ca_file(&Path::new("test/root-ca.pem")) { + Ok(_) => {} + Err(err) => panic!("Unexpected error {:?}", err), + } + let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) { + Ok(stream) => stream, + Err(err) => panic!("Expected success, got {:?}", err), + }; + // The client now only supports one of the server's protocols, so that one + // is used. + assert_eq!(b"spdy/3.1", stream.ssl().selected_alpn_protocol().unwrap()); +} + +/// Tests that when both the client as well as the server use NPN and their +/// lists of supported protocols have an overlap -- with only ONE protocol +/// being valid for both. +#[test] +#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] +fn test_connect_with_npn_successful_single_match() { + let (_s, stream) = Server::new_alpn(); + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_verify(SslVerifyMode::PEER); + ctx.set_npn_protocols(&[b"spdy/3.1"]).unwrap(); + match ctx.set_ca_file(&Path::new("test/root-ca.pem")) { + Ok(_) => {} + Err(err) => panic!("Unexpected error {:?}", err), + } + let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) { + Ok(stream) => stream, + Err(err) => panic!("Expected success, got {:?}", err), + }; + // The client now only supports one of the server's protocols, so that one + // is used. + assert_eq!(b"spdy/3.1", stream.ssl().selected_npn_protocol().unwrap()); +} + +/// Tests that when the `SslStream` is created as a server stream, the protocols +/// are correctly advertised to the client. +#[test] +#[cfg(not(any(libressl261, libressl262, libressl26x)))] +fn test_npn_server_advertise_multiple() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let localhost = listener.local_addr().unwrap(); + // We create a different context instance for the server... + let listener_ctx = { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_verify(SslVerifyMode::PEER); + ctx.set_npn_protocols(&[b"http/1.1", b"spdy/3.1"]).unwrap(); + assert!( + ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM) + .is_ok() + ); + ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM) + .unwrap(); + ctx.build() + }; + // Have the listener wait on the connection in a different thread. + thread::spawn(move || { + let (stream, _) = listener.accept().unwrap(); + Ssl::new(&listener_ctx).unwrap().accept(stream).unwrap(); + }); + + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_verify(SslVerifyMode::PEER); + ctx.set_npn_protocols(&[b"spdy/3.1"]).unwrap(); + match ctx.set_ca_file(&Path::new("test/root-ca.pem")) { + Ok(_) => {} + Err(err) => panic!("Unexpected error {:?}", err), + } + // Now connect to the socket and make sure the protocol negotiation works... + let stream = TcpStream::connect(localhost).unwrap(); + let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) { + Ok(stream) => stream, + Err(err) => panic!("Expected success, got {:?}", err), + }; + // SPDY is selected since that's the only thing the client supports. + assert_eq!(b"spdy/3.1", stream.ssl().selected_npn_protocol().unwrap()); +} + +/// Tests that when the `SslStream` is created as a server stream, the protocols +/// are correctly advertised to the client. +#[test] +#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] +fn test_alpn_server_advertise_multiple() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let localhost = listener.local_addr().unwrap(); + // We create a different context instance for the server... + let listener_ctx = { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_verify(SslVerifyMode::PEER); + ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]).unwrap(); + assert!( + ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM) + .is_ok() + ); + ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM) + .unwrap(); + ctx.build() + }; + // Have the listener wait on the connection in a different thread. + thread::spawn(move || { + let (stream, _) = listener.accept().unwrap(); + Ssl::new(&listener_ctx).unwrap().accept(stream).unwrap(); + }); + + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_verify(SslVerifyMode::PEER); + ctx.set_alpn_protocols(&[b"spdy/3.1"]).unwrap(); + match ctx.set_ca_file(&Path::new("test/root-ca.pem")) { + Ok(_) => {} + Err(err) => panic!("Unexpected error {:?}", err), + } + // Now connect to the socket and make sure the protocol negotiation works... + let stream = TcpStream::connect(localhost).unwrap(); + let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) { + Ok(stream) => stream, + Err(err) => panic!("Expected success, got {:?}", err), + }; + // SPDY is selected since that's the only thing the client supports. + assert_eq!(b"spdy/3.1", stream.ssl().selected_alpn_protocol().unwrap()); +} + +/// Test that Servers supporting ALPN don't report a protocol when none of their protocols match +/// the client's reported protocol. +#[test] +#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] +fn test_alpn_server_select_none() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let localhost = listener.local_addr().unwrap(); + // We create a different context instance for the server... + let listener_ctx = { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_verify(SslVerifyMode::PEER); + ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]).unwrap(); + assert!( + ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM) + .is_ok() + ); + ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM) + .unwrap(); + ctx.build() + }; + // Have the listener wait on the connection in a different thread. + thread::spawn(move || { + let (stream, _) = listener.accept().unwrap(); + Ssl::new(&listener_ctx).unwrap().accept(stream).unwrap(); + }); + + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_verify(SslVerifyMode::PEER); + ctx.set_alpn_protocols(&[b"http/2"]).unwrap(); + ctx.set_ca_file(&Path::new("test/root-ca.pem")).unwrap(); + // Now connect to the socket and make sure the protocol negotiation works... + let stream = TcpStream::connect(localhost).unwrap(); + let stream = Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap(); + + // Since the protocols from the server and client don't overlap at all, no protocol is selected + assert_eq!(None, stream.ssl().selected_alpn_protocol()); +} + +#[test] +#[should_panic(expected = "blammo")] +fn write_panic() { + struct ExplodingStream(TcpStream); + + impl Read for ExplodingStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + } + + impl Write for ExplodingStream { + fn write(&mut self, _: &[u8]) -> io::Result { + panic!("blammo"); + } + + fn flush(&mut self) -> io::Result<()> { + self.0.flush() + } + } + + let (_s, stream) = Server::new(); + let stream = ExplodingStream(stream); + + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let _ = Ssl::new(&ctx.build()).unwrap().connect(stream); +} + +#[test] +#[should_panic(expected = "blammo")] +fn read_panic() { + struct ExplodingStream(TcpStream); + + impl Read for ExplodingStream { + fn read(&mut self, _: &mut [u8]) -> io::Result { + panic!("blammo"); + } + } + + impl Write for ExplodingStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.0.flush() + } + } + + let (_s, stream) = Server::new(); + let stream = ExplodingStream(stream); + + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let _ = Ssl::new(&ctx.build()).unwrap().connect(stream); +} + +#[test] +#[should_panic(expected = "blammo")] +fn flush_panic() { + struct ExplodingStream(TcpStream); + + impl Read for ExplodingStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + } + + impl Write for ExplodingStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + panic!("blammo"); + } + } + + let (_s, stream) = Server::new(); + let stream = ExplodingStream(stream); + + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let mut stream = Ssl::new(&ctx.build()) + .unwrap() + .connect(stream) + .ok() + .unwrap(); + let _ = stream.flush(); +} + +#[test] +fn refcount_ssl_context() { + let mut ssl = { + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ssl::Ssl::new(&ctx.build()).unwrap() + }; + + { + let new_ctx_a = SslContext::builder(SslMethod::tls()).unwrap().build(); + let _new_ctx_b = ssl.set_ssl_context(&new_ctx_a); + } +} + +#[test] +fn default_verify_paths() { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_default_verify_paths().unwrap(); + ctx.set_verify(SslVerifyMode::PEER); + let s = TcpStream::connect("google.com:443").unwrap(); + let mut socket = Ssl::new(&ctx.build()).unwrap().connect(s).unwrap(); + + socket.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap(); + let mut result = vec![]; + socket.read_to_end(&mut result).unwrap(); + + println!("{}", String::from_utf8_lossy(&result)); + assert!(result.starts_with(b"HTTP/1.0")); + assert!(result.ends_with(b"\r\n") || result.ends_with(b"")); +} + +#[test] +fn add_extra_chain_cert() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.add_extra_chain_cert(cert).unwrap(); +} + +#[test] +#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] +fn verify_valid_hostname() { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_default_verify_paths().unwrap(); + ctx.set_verify(SslVerifyMode::PEER); + + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.param_mut() + .set_hostflags(X509CheckFlags::NO_PARTIAL_WILDCARDS); + ssl.param_mut().set_host("google.com").unwrap(); + + let s = TcpStream::connect("google.com:443").unwrap(); + let mut socket = ssl.connect(s).unwrap(); + + socket.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap(); + let mut result = vec![]; + socket.read_to_end(&mut result).unwrap(); + + println!("{}", String::from_utf8_lossy(&result)); + assert!(result.starts_with(b"HTTP/1.0")); + assert!(result.ends_with(b"\r\n") || result.ends_with(b"")); +} + +#[test] +#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] +fn verify_invalid_hostname() { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_default_verify_paths().unwrap(); + ctx.set_verify(SslVerifyMode::PEER); + + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.param_mut() + .set_hostflags(X509CheckFlags::NO_PARTIAL_WILDCARDS); + ssl.param_mut().set_host("foobar.com").unwrap(); + + let s = TcpStream::connect("google.com:443").unwrap(); + assert!(ssl.connect(s).is_err()); +} + +#[test] +fn connector_valid_hostname() { + let connector = SslConnector::builder(SslMethod::tls()).unwrap().build(); + + let s = TcpStream::connect("google.com:443").unwrap(); + let mut socket = connector.connect("google.com", s).unwrap(); + + socket.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap(); + let mut result = vec![]; + socket.read_to_end(&mut result).unwrap(); + + println!("{}", String::from_utf8_lossy(&result)); + assert!(result.starts_with(b"HTTP/1.0")); + assert!(result.ends_with(b"\r\n") || result.ends_with(b"")); +} + +#[test] +fn connector_invalid_hostname() { + let connector = SslConnector::builder(SslMethod::tls()).unwrap().build(); + + let s = TcpStream::connect("google.com:443").unwrap(); + assert!(connector.connect("foobar.com", s).is_err()); +} + +#[test] +fn connector_invalid_no_hostname_verification() { + let connector = SslConnector::builder(SslMethod::tls()).unwrap().build(); + + let s = TcpStream::connect("google.com:443").unwrap(); + connector + .configure() + .unwrap() + .use_server_name_indication(false) + .verify_hostname(false) + .connect("foobar.com", s) + .unwrap(); +} + +#[test] +fn connector_no_hostname_still_verifies() { + let (_s, tcp) = Server::new(); + + let connector = SslConnector::builder(SslMethod::tls()).unwrap().build(); + + assert!( + connector + .configure() + .unwrap() + .verify_hostname(false) + .connect("fizzbuzz.com", tcp) + .is_err() + ); +} + +#[test] +fn connector_no_hostname_can_disable_verify() { + let (_s, tcp) = Server::new(); + + let mut connector = SslConnector::builder(SslMethod::tls()).unwrap(); + connector.set_verify(SslVerifyMode::NONE); + let connector = connector.build(); + + connector + .configure() + .unwrap() + .verify_hostname(false) + .connect("foobar.com", tcp) + .unwrap(); +} + +#[test] +fn connector_client_server_mozilla_intermediate() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let port = listener.local_addr().unwrap().port(); + + let t = thread::spawn(move || { + let key = PKey::private_key_from_pem(KEY).unwrap(); + let cert = X509::from_pem(CERT).unwrap(); + let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); + acceptor.set_private_key(&key).unwrap(); + acceptor.set_certificate(&cert).unwrap(); + let acceptor = acceptor.build(); + let stream = listener.accept().unwrap().0; + let mut stream = acceptor.accept(stream).unwrap(); + + stream.write_all(b"hello").unwrap(); + }); + + let mut connector = SslConnector::builder(SslMethod::tls()).unwrap(); + connector.set_ca_file("test/root-ca.pem").unwrap(); + let connector = connector.build(); + + let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); + let mut stream = connector.connect("foobar.com", stream).unwrap(); + + let mut buf = [0; 5]; + stream.read_exact(&mut buf).unwrap(); + assert_eq!(b"hello", &buf); + + t.join().unwrap(); +} + +#[test] +fn connector_client_server_mozilla_modern() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let port = listener.local_addr().unwrap().port(); + + let t = thread::spawn(move || { + let key = PKey::private_key_from_pem(KEY).unwrap(); + let cert = X509::from_pem(CERT).unwrap(); + let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); + acceptor.set_private_key(&key).unwrap(); + acceptor.set_certificate(&cert).unwrap(); + let acceptor = acceptor.build(); + let stream = listener.accept().unwrap().0; + let mut stream = acceptor.accept(stream).unwrap(); + + stream.write_all(b"hello").unwrap(); + }); + + let mut connector = SslConnector::builder(SslMethod::tls()).unwrap(); + connector.set_ca_file("test/root-ca.pem").unwrap(); + let connector = connector.build(); + + let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); + let mut stream = connector.connect("foobar.com", stream).unwrap(); + + let mut buf = [0; 5]; + stream.read_exact(&mut buf).unwrap(); + assert_eq!(b"hello", &buf); + + t.join().unwrap(); +} + +#[test] +fn shutdown() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let port = listener.local_addr().unwrap().port(); + + thread::spawn(move || { + let stream = listener.accept().unwrap().0; + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM) + .unwrap(); + ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM) + .unwrap(); + let ssl = Ssl::new(&ctx.build()).unwrap(); + let mut stream = ssl.accept(stream).unwrap(); + + stream.write_all(b"hello").unwrap(); + let mut buf = [0; 1]; + assert_eq!(stream.read(&mut buf).unwrap(), 0); + assert_eq!(stream.shutdown().unwrap(), ShutdownResult::Received); + }); + + let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let ssl = Ssl::new(&ctx.build()).unwrap(); + let mut stream = ssl.connect(stream).unwrap(); + + let mut buf = [0; 5]; + stream.read_exact(&mut buf).unwrap(); + assert_eq!(b"hello", &buf); + + assert_eq!(stream.shutdown().unwrap(), ShutdownResult::Sent); + assert_eq!(stream.shutdown().unwrap(), ShutdownResult::Received); +} + +#[test] +fn client_ca_list() { + let names = X509Name::load_client_ca_file("test/root-ca.pem").unwrap(); + assert_eq!(names.len(), 1); + + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_client_ca_list(names); +} + +#[test] +fn cert_store() { + let (_s, tcp) = Server::new(); + + let cert = X509::from_pem(ROOT_CERT).unwrap(); + + let mut ctx = SslConnector::builder(SslMethod::tls()).unwrap(); + ctx.cert_store_mut().add_cert(cert).unwrap(); + let ctx = ctx.build(); + + ctx.connect("foobar.com", tcp).unwrap(); +} + +#[test] +fn tmp_dh_callback() { + static CALLED_BACK: AtomicBool = ATOMIC_BOOL_INIT; + + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let port = listener.local_addr().unwrap().port(); + + thread::spawn(move || { + let stream = listener.accept().unwrap().0; + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM) + .unwrap(); + ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM) + .unwrap(); + ctx.set_tmp_dh_callback(|_, _, _| { + CALLED_BACK.store(true, Ordering::SeqCst); + let dh = include_bytes!("../../test/dhparams.pem"); + Dh::from_pem(dh) + }); + let ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.accept(stream).unwrap(); + }); + + let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_cipher_list("EDH").unwrap(); + let ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.connect(stream).unwrap(); + + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +#[cfg(any(all(feature = "v101", ossl101, not(any(libressl261, libressl262, libressl26x))), + all(feature = "v102", ossl102)))] +fn tmp_ecdh_callback() { + use ec::EcKey; + use nid::Nid; + + static CALLED_BACK: AtomicBool = ATOMIC_BOOL_INIT; + + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let port = listener.local_addr().unwrap().port(); + + thread::spawn(move || { + let stream = listener.accept().unwrap().0; + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM) + .unwrap(); + ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM) + .unwrap(); + ctx.set_tmp_ecdh_callback(|_, _, _| { + CALLED_BACK.store(true, Ordering::SeqCst); + EcKey::from_curve_name(Nid::X9_62_PRIME256V1) + }); + let ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.accept(stream).unwrap(); + }); + + let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_cipher_list("ECDH").unwrap(); + let ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.connect(stream).unwrap(); + + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +fn tmp_dh_callback_ssl() { + static CALLED_BACK: AtomicBool = ATOMIC_BOOL_INIT; + + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let port = listener.local_addr().unwrap().port(); + + thread::spawn(move || { + let stream = listener.accept().unwrap().0; + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM) + .unwrap(); + ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM) + .unwrap(); + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.set_tmp_dh_callback(|_, _, _| { + CALLED_BACK.store(true, Ordering::SeqCst); + let dh = include_bytes!("../../test/dhparams.pem"); + Dh::from_pem(dh) + }); + ssl.accept(stream).unwrap(); + }); + + let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_cipher_list("EDH").unwrap(); + let ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.connect(stream).unwrap(); + + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +#[cfg(any(all(feature = "v101", ossl101, not(any(libressl261, libressl262, libressl26x))), + all(feature = "v102", ossl102)))] +fn tmp_ecdh_callback_ssl() { + use ec::EcKey; + use nid::Nid; + + static CALLED_BACK: AtomicBool = ATOMIC_BOOL_INIT; + + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let port = listener.local_addr().unwrap().port(); + + thread::spawn(move || { + let stream = listener.accept().unwrap().0; + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM) + .unwrap(); + ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM) + .unwrap(); + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.set_tmp_ecdh_callback(|_, _, _| { + CALLED_BACK.store(true, Ordering::SeqCst); + EcKey::from_curve_name(Nid::X9_62_PRIME256V1) + }); + ssl.accept(stream).unwrap(); + }); + + let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_cipher_list("ECDH").unwrap(); + let ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.connect(stream).unwrap(); + + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +fn idle_session() { + let ctx = SslContext::builder(SslMethod::tls()).unwrap().build(); + let ssl = Ssl::new(&ctx).unwrap(); + assert!(ssl.session().is_none()); +} + +#[test] +fn active_session() { + let connector = SslConnector::builder(SslMethod::tls()).unwrap().build(); + + let s = TcpStream::connect("google.com:443").unwrap(); + let socket = connector.connect("google.com", s).unwrap(); + let session = socket.ssl().session().unwrap(); + let len = session.master_key_len(); + let mut buf = vec![0; len - 1]; + let copied = session.master_key(&mut buf); + assert_eq!(copied, buf.len()); + let mut buf = vec![0; len + 1]; + let copied = session.master_key(&mut buf); + assert_eq!(copied, len); +} + +#[test] +fn status_callbacks() { + static CALLED_BACK_SERVER: AtomicBool = ATOMIC_BOOL_INIT; + static CALLED_BACK_CLIENT: AtomicBool = ATOMIC_BOOL_INIT; + + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let port = listener.local_addr().unwrap().port(); + + let guard = thread::spawn(move || { + let stream = listener.accept().unwrap().0; + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM) + .unwrap(); + ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM) + .unwrap(); + ctx.set_status_callback(|ssl| { + CALLED_BACK_SERVER.store(true, Ordering::SeqCst); + let response = OcspResponse::create(OcspResponseStatus::UNAUTHORIZED, None).unwrap(); + let response = response.to_der().unwrap(); + ssl.set_ocsp_status(&response).unwrap(); + Ok(true) + }).unwrap(); + let ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.accept(stream).unwrap(); + }); + + let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_status_callback(|ssl| { + CALLED_BACK_CLIENT.store(true, Ordering::SeqCst); + let response = OcspResponse::from_der(ssl.ocsp_status().unwrap()).unwrap(); + assert_eq!(response.status(), OcspResponseStatus::UNAUTHORIZED); + Ok(true) + }).unwrap(); + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.set_status_type(StatusType::OCSP).unwrap(); + ssl.connect(stream).unwrap(); + + assert!(CALLED_BACK_SERVER.load(Ordering::SeqCst)); + assert!(CALLED_BACK_CLIENT.load(Ordering::SeqCst)); + + guard.join().unwrap(); +} + +fn _check_kinds() { + fn is_send() {} + fn is_sync() {} + + is_send::>(); + is_sync::>(); +} diff --git a/openssl/src/ssl/tests/mod.rs b/openssl/src/ssl/tests/mod.rs deleted file mode 100644 index c618a704..00000000 --- a/openssl/src/ssl/tests/mod.rs +++ /dev/null @@ -1,1422 +0,0 @@ -#![allow(unused_imports)] - -use std::env; -use std::fs::File; -use std::io::prelude::*; -use std::io::{self, BufReader}; -use std::iter; -use std::mem; -use std::net::{SocketAddr, TcpListener, TcpStream}; -use std::path::Path; -use std::process::{Child, ChildStdin, Command, Stdio}; -use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT}; -use std::thread; -use std::time::Duration; -use tempdir::TempDir; - -use dh::Dh; -use hash::MessageDigest; -use ocsp::{OcspResponse, OcspResponseStatus}; -use ssl; -use ssl::{Error, HandshakeError, ShutdownResult, Ssl, SslAcceptor, SslConnector, SslContext, - SslMethod, SslStream, SslVerifyMode, StatusType}; -use x509::{X509, X509Filetype, X509Name, X509StoreContext, X509VerifyResult}; -#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] -use x509::verify::X509CheckFlags; -use pkey::PKey; - -use std::net::UdpSocket; - -mod select; - -static ROOT_CERT: &'static [u8] = include_bytes!("../../../test/root-ca.pem"); -static CERT: &'static [u8] = include_bytes!("../../../test/cert.pem"); -static KEY: &'static [u8] = include_bytes!("../../../test/key.pem"); - -fn next_addr() -> SocketAddr { - use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; - static PORT: AtomicUsize = ATOMIC_USIZE_INIT; - let port = 15411 + PORT.fetch_add(1, Ordering::SeqCst); - - format!("127.0.0.1:{}", port).parse().unwrap() -} - -struct Server { - p: Child, - _temp: TempDir, -} - -impl Server { - fn spawn(args: &[&str], input: Option>) -> (Server, SocketAddr) { - let td = TempDir::new("openssl").unwrap(); - let cert = td.path().join("cert.pem"); - let key = td.path().join("key.pem"); - File::create(&cert).unwrap().write_all(CERT).unwrap(); - File::create(&key).unwrap().write_all(KEY).unwrap(); - - let addr = next_addr(); - let mut child = Command::new("openssl") - .arg("s_server") - .arg("-accept") - .arg(addr.port().to_string()) - .args(args) - .arg("-cert") - .arg(&cert) - .arg("-key") - .arg(&key) - .arg("-no_dhe") - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .stdin(Stdio::piped()) - .spawn() - .unwrap(); - let stdin = child.stdin.take().unwrap(); - if let Some(mut input) = input { - thread::spawn(move || input(stdin)); - } - ( - Server { - p: child, - _temp: td, - }, - addr, - ) - } - - fn new_tcp(args: &[&str]) -> (Server, TcpStream) { - let (server, addr) = Server::spawn(args, None); - for _ in 0..20 { - match TcpStream::connect(&addr) { - Ok(s) => return (server, s), - Err(ref e) if e.kind() == io::ErrorKind::ConnectionRefused => { - thread::sleep(Duration::from_millis(100)); - } - Err(e) => panic!("wut: {}", e), - } - } - panic!("server never came online"); - } - - fn new() -> (Server, TcpStream) { - Server::new_tcp(&["-www"]) - } - - #[allow(dead_code)] - fn new_alpn() -> (Server, TcpStream) { - Server::new_tcp(&[ - "-www", - "-nextprotoneg", - "http/1.1,spdy/3.1", - "-alpn", - "http/1.1,spdy/3.1", - ]) - } -} - -impl Drop for Server { - fn drop(&mut self) { - let _ = self.p.kill(); - let _ = self.p.wait(); - } -} - -macro_rules! run_test( - ($module:ident, $blk:expr) => ( - #[cfg(test)] - mod $module { - use std::io; - use std::io::prelude::*; - use std::path::Path; - use std::net::UdpSocket; - use std::net::TcpStream; - use ssl; - use ssl::SslMethod; - use ssl::{SslContext, Ssl, SslStream, SslVerifyMode, SslOptions}; - use hash::MessageDigest; - use x509::{X509StoreContext, X509VerifyResult}; - #[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] - use x509::X509; - #[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] - use x509::store::X509StoreBuilder; - use hex::FromHex; - use foreign_types::ForeignTypeRef; - use super::Server; - #[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] - use super::ROOT_CERT; - - #[test] - fn sslv23() { - let (_s, stream) = Server::new(); - $blk(SslMethod::tls(), stream); - } - } - ); -); - -run_test!(new_ctx, |method, _| { - SslContext::builder(method).unwrap(); -}); - -run_test!(verify_untrusted, |method, stream| { - let mut ctx = SslContext::builder(method).unwrap(); - ctx.set_verify(SslVerifyMode::PEER); - - match Ssl::new(&ctx.build()).unwrap().connect(stream) { - Ok(_) => panic!("expected failure"), - Err(err) => println!("error {:?}", err), - } -}); - -run_test!(verify_trusted, |method, stream| { - let mut ctx = SslContext::builder(method).unwrap(); - ctx.set_verify(SslVerifyMode::PEER); - - match ctx.set_ca_file(&Path::new("test/root-ca.pem")) { - Ok(_) => {} - Err(err) => panic!("Unexpected error {:?}", err), - } - match Ssl::new(&ctx.build()).unwrap().connect(stream) { - Ok(_) => (), - Err(err) => panic!("Expected success, got {:?}", err), - } -}); - -#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] -run_test!(verify_trusted_with_set_cert, |method, stream| { - let x509 = X509::from_pem(ROOT_CERT).unwrap(); - let mut store = X509StoreBuilder::new().unwrap(); - store.add_cert(x509).unwrap(); - - let mut ctx = SslContext::builder(method).unwrap(); - ctx.set_verify(SslVerifyMode::PEER); - - match ctx.set_verify_cert_store(store.build()) { - Ok(_) => {} - Err(err) => panic!("Unexpected error {:?}", err), - } - match Ssl::new(&ctx.build()).unwrap().connect(stream) { - Ok(_) => (), - Err(err) => panic!("Expected success, got {:?}", err), - } -}); - -run_test!(verify_untrusted_callback_override_ok, |method, stream| { - let mut ctx = SslContext::builder(method).unwrap(); - ctx.set_verify_callback(SslVerifyMode::PEER, |_, _| true); - - match Ssl::new(&ctx.build()).unwrap().connect(stream) { - Ok(_) => (), - Err(err) => panic!("Expected success, got {:?}", err), - } -}); - -run_test!(verify_untrusted_callback_override_bad, |method, stream| { - let mut ctx = SslContext::builder(method).unwrap(); - ctx.set_verify_callback(SslVerifyMode::PEER, |_, _| false); - - assert!(Ssl::new(&ctx.build()).unwrap().connect(stream).is_err()); -}); - -run_test!(verify_trusted_callback_override_ok, |method, stream| { - let mut ctx = SslContext::builder(method).unwrap(); - ctx.set_verify_callback(SslVerifyMode::PEER, |_, _| true); - - match ctx.set_ca_file(&Path::new("test/cert.pem")) { - Ok(_) => {} - Err(err) => panic!("Unexpected error {:?}", err), - } - match Ssl::new(&ctx.build()).unwrap().connect(stream) { - Ok(_) => (), - Err(err) => panic!("Expected success, got {:?}", err), - } -}); - -run_test!(verify_trusted_callback_override_bad, |method, stream| { - let mut ctx = SslContext::builder(method).unwrap(); - ctx.set_verify_callback(SslVerifyMode::PEER, |_, _| false); - - match ctx.set_ca_file(&Path::new("test/cert.pem")) { - Ok(_) => {} - Err(err) => panic!("Unexpected error {:?}", err), - } - assert!(Ssl::new(&ctx.build()).unwrap().connect(stream).is_err()); -}); - -run_test!(verify_callback_load_certs, |method, stream| { - let mut ctx = SslContext::builder(method).unwrap(); - ctx.set_verify_callback(SslVerifyMode::PEER, |_, x509_ctx| { - assert!(x509_ctx.current_cert().is_some()); - true - }); - - assert!(Ssl::new(&ctx.build()).unwrap().connect(stream).is_ok()); -}); - -run_test!(verify_trusted_get_error_ok, |method, stream| { - let mut ctx = SslContext::builder(method).unwrap(); - ctx.set_verify_callback(SslVerifyMode::PEER, |_, x509_ctx| { - assert!(x509_ctx.error() == X509VerifyResult::OK); - true - }); - - match ctx.set_ca_file(&Path::new("test/root-ca.pem")) { - Ok(_) => {} - Err(err) => panic!("Unexpected error {:?}", err), - } - assert!(Ssl::new(&ctx.build()).unwrap().connect(stream).is_ok()); -}); - -run_test!(verify_trusted_get_error_err, |method, stream| { - let mut ctx = SslContext::builder(method).unwrap(); - ctx.set_verify_callback(SslVerifyMode::PEER, |_, x509_ctx| { - assert_ne!(x509_ctx.error(), X509VerifyResult::OK); - false - }); - - assert!(Ssl::new(&ctx.build()).unwrap().connect(stream).is_err()); -}); - -run_test!(verify_callback_data, |method, stream| { - let mut ctx = SslContext::builder(method).unwrap(); - - // Node id was generated as SHA256 hash of certificate "test/cert.pem" - // in DER format. - // Command: openssl x509 -in test/cert.pem -outform DER | openssl dgst -sha256 - // Please update if "test/cert.pem" will ever change - let node_hash_str = "59172d9313e84459bcff27f967e79e6e9217e584"; - let node_id = Vec::from_hex(node_hash_str).unwrap(); - ctx.set_verify_callback(SslVerifyMode::PEER, move |_preverify_ok, x509_ctx| { - let cert = x509_ctx.current_cert(); - match cert { - None => false, - Some(cert) => { - let fingerprint = cert.fingerprint(MessageDigest::sha1()).unwrap(); - fingerprint == node_id - } - } - }); - ctx.set_verify_depth(1); - - match Ssl::new(&ctx.build()).unwrap().connect(stream) { - Ok(_) => (), - Err(err) => panic!("Expected success, got {:?}", err), - } -}); - -run_test!(ssl_verify_callback, |method, stream| { - use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; - - static CHECKED: AtomicUsize = ATOMIC_USIZE_INIT; - - let ctx = SslContext::builder(method).unwrap(); - let mut ssl = Ssl::new(&ctx.build()).unwrap(); - - let node_hash_str = "59172d9313e84459bcff27f967e79e6e9217e584"; - let node_id = Vec::from_hex(node_hash_str).unwrap(); - ssl.set_verify_callback(SslVerifyMode::PEER, move |_, x509| { - CHECKED.store(1, Ordering::SeqCst); - match x509.current_cert() { - None => false, - Some(cert) => { - let fingerprint = cert.fingerprint(MessageDigest::sha1()).unwrap(); - fingerprint == node_id - } - } - }); - - match ssl.connect(stream) { - Ok(_) => (), - Err(err) => panic!("Expected success, got {:?}", err), - } - - assert_eq!(CHECKED.load(Ordering::SeqCst), 1); -}); - -// Make sure every write call translates to a write call to the underlying socket. -#[test] -fn test_write_hits_stream() { - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - let addr = listener.local_addr().unwrap(); - - let guard = thread::spawn(move || { - let ctx = SslContext::builder(SslMethod::tls()).unwrap(); - let stream = TcpStream::connect(addr).unwrap(); - let mut stream = Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap(); - - stream.write_all(b"hello").unwrap(); - stream - }); - - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_verify(SslVerifyMode::PEER); - ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM) - .unwrap(); - ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM) - .unwrap(); - let stream = listener.accept().unwrap().0; - let mut stream = Ssl::new(&ctx.build()).unwrap().accept(stream).unwrap(); - - let mut buf = [0; 5]; - assert_eq!(5, stream.read(&mut buf).unwrap()); - assert_eq!(&b"hello"[..], &buf[..]); - guard.join().unwrap(); -} - -#[test] -fn test_set_certificate_and_private_key() { - let key = include_bytes!("../../../test/key.pem"); - let key = PKey::private_key_from_pem(key).unwrap(); - let cert = include_bytes!("../../../test/cert.pem"); - let cert = X509::from_pem(cert).unwrap(); - - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_private_key(&key).unwrap(); - ctx.set_certificate(&cert).unwrap(); - - assert!(ctx.check_private_key().is_ok()); -} - -run_test!(get_ctx_options, |method, _| { - let ctx = SslContext::builder(method).unwrap(); - ctx.options(); -}); - -run_test!(set_ctx_options, |method, _| { - let mut ctx = SslContext::builder(method).unwrap(); - let opts = ctx.set_options(SslOptions::NO_TICKET); - assert!(opts.contains(SslOptions::NO_TICKET)); -}); - -run_test!(clear_ctx_options, |method, _| { - let mut ctx = SslContext::builder(method).unwrap(); - ctx.set_options(SslOptions::ALL); - let opts = ctx.clear_options(SslOptions::ALL); - assert!(!opts.contains(SslOptions::ALL)); -}); - -#[test] -fn test_write() { - let (_s, stream) = Server::new(); - let ctx = SslContext::builder(SslMethod::tls()).unwrap(); - let mut stream = Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap(); - stream.write_all("hello".as_bytes()).unwrap(); - stream.flush().unwrap(); - stream.write_all(" there".as_bytes()).unwrap(); - stream.flush().unwrap(); -} - -#[test] -fn zero_length_buffers() { - let (_s, stream) = Server::new(); - let ctx = SslContext::builder(SslMethod::tls()).unwrap(); - let mut stream = Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap(); - - assert_eq!(stream.write(b"").unwrap(), 0); - assert_eq!(stream.read(&mut []).unwrap(), 0); -} - -run_test!(get_peer_certificate, |method, stream| { - let ctx = SslContext::builder(method).unwrap(); - let stream = Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap(); - let cert = stream.ssl().peer_certificate().unwrap(); - let fingerprint = cert.fingerprint(MessageDigest::sha1()).unwrap(); - let node_hash_str = "59172d9313e84459bcff27f967e79e6e9217e584"; - let node_id = Vec::from_hex(node_hash_str).unwrap(); - assert_eq!(node_id, fingerprint) -}); - -#[test] -fn test_read() { - let (_s, tcp) = Server::new(); - let ctx = SslContext::builder(SslMethod::tls()).unwrap(); - let mut stream = Ssl::new(&ctx.build()).unwrap().connect(tcp).unwrap(); - stream.write_all("GET /\r\n\r\n".as_bytes()).unwrap(); - stream.flush().unwrap(); - io::copy(&mut stream, &mut io::sink()) - .ok() - .expect("read error"); -} - -#[test] -fn test_pending() { - let (_s, tcp) = Server::new(); - let ctx = SslContext::builder(SslMethod::tls()).unwrap(); - let mut stream = Ssl::new(&ctx.build()).unwrap().connect(tcp).unwrap(); - stream.write_all("GET /\r\n\r\n".as_bytes()).unwrap(); - stream.flush().unwrap(); - - // wait for the response and read first byte... - let mut buf = [0u8; 16 * 1024]; - stream.read(&mut buf[..1]).unwrap(); - - let pending = stream.ssl().pending(); - let len = stream.read(&mut buf[1..]).unwrap(); - - assert_eq!(pending, len); - - stream.read(&mut buf[..1]).unwrap(); - - let pending = stream.ssl().pending(); - let len = stream.read(&mut buf[1..]).unwrap(); - assert_eq!(pending, len); -} - -#[test] -fn test_state() { - let (_s, tcp) = Server::new(); - let ctx = SslContext::builder(SslMethod::tls()).unwrap(); - let stream = Ssl::new(&ctx.build()).unwrap().connect(tcp).unwrap(); - assert_eq!(stream.ssl().state_string(), "SSLOK "); - assert_eq!( - stream.ssl().state_string_long(), - "SSL negotiation finished successfully" - ); -} - -/// Tests that connecting with the client using ALPN, but the server not does not -/// break the existing connection behavior. -#[test] -#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] -fn test_connect_with_unilateral_alpn() { - let (_s, stream) = Server::new(); - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_verify(SslVerifyMode::PEER); - ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]).unwrap(); - match ctx.set_ca_file(&Path::new("test/root-ca.pem")) { - Ok(_) => {} - Err(err) => panic!("Unexpected error {:?}", err), - } - let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) { - Ok(stream) => stream, - Err(err) => panic!("Expected success, got {:?}", err), - }; - // Since the socket to which we connected is not configured to use ALPN, - // there should be no selected protocol... - assert!(stream.ssl().selected_alpn_protocol().is_none()); -} - -/// Tests that connecting with the client using NPN, but the server not does not -/// break the existing connection behavior. -#[test] -#[cfg(not(any(libressl261, libressl262, libressl26x)))] -fn test_connect_with_unilateral_npn() { - let (_s, stream) = Server::new(); - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_verify(SslVerifyMode::PEER); - ctx.set_npn_protocols(&[b"http/1.1", b"spdy/3.1"]).unwrap(); - match ctx.set_ca_file(&Path::new("test/root-ca.pem")) { - Ok(_) => {} - Err(err) => panic!("Unexpected error {:?}", err), - } - let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) { - Ok(stream) => stream, - Err(err) => panic!("Expected success, got {:?}", err), - }; - // Since the socket to which we connected is not configured to use NPN, - // there should be no selected protocol... - assert!(stream.ssl().selected_npn_protocol().is_none()); -} - -/// Tests that when both the client as well as the server use ALPN and their -/// lists of supported protocols have an overlap, the correct protocol is chosen. -#[test] -#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] -fn test_connect_with_alpn_successful_multiple_matching() { - let (_s, stream) = Server::new_alpn(); - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_verify(SslVerifyMode::PEER); - ctx.set_alpn_protocols(&[b"spdy/3.1", b"http/1.1"]).unwrap(); - match ctx.set_ca_file(&Path::new("test/root-ca.pem")) { - Ok(_) => {} - Err(err) => panic!("Unexpected error {:?}", err), - } - let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) { - Ok(stream) => stream, - Err(err) => panic!("Expected success, got {:?}", err), - }; - // The server prefers "http/1.1", so that is chosen, even though the client - // would prefer "spdy/3.1" - assert_eq!(b"http/1.1", stream.ssl().selected_alpn_protocol().unwrap()); -} - -/// Tests that when both the client as well as the server use NPN and their -/// lists of supported protocols have an overlap, the correct protocol is chosen. -#[test] -#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] -fn test_connect_with_npn_successful_multiple_matching() { - let (_s, stream) = Server::new_alpn(); - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_verify(SslVerifyMode::PEER); - ctx.set_npn_protocols(&[b"spdy/3.1", b"http/1.1"]).unwrap(); - match ctx.set_ca_file(&Path::new("test/root-ca.pem")) { - Ok(_) => {} - Err(err) => panic!("Unexpected error {:?}", err), - } - let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) { - Ok(stream) => stream, - Err(err) => panic!("Expected success, got {:?}", err), - }; - // The server prefers "http/1.1", so that is chosen, even though the client - // would prefer "spdy/3.1" - assert_eq!(b"http/1.1", stream.ssl().selected_npn_protocol().unwrap()); -} - -/// Tests that when both the client as well as the server use ALPN and their -/// lists of supported protocols have an overlap -- with only ONE protocol -/// being valid for both. -#[test] -#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] -fn test_connect_with_alpn_successful_single_match() { - let (_s, stream) = Server::new_alpn(); - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_verify(SslVerifyMode::PEER); - ctx.set_alpn_protocols(&[b"spdy/3.1"]).unwrap(); - match ctx.set_ca_file(&Path::new("test/root-ca.pem")) { - Ok(_) => {} - Err(err) => panic!("Unexpected error {:?}", err), - } - let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) { - Ok(stream) => stream, - Err(err) => panic!("Expected success, got {:?}", err), - }; - // The client now only supports one of the server's protocols, so that one - // is used. - assert_eq!(b"spdy/3.1", stream.ssl().selected_alpn_protocol().unwrap()); -} - -/// Tests that when both the client as well as the server use NPN and their -/// lists of supported protocols have an overlap -- with only ONE protocol -/// being valid for both. -#[test] -#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] -fn test_connect_with_npn_successful_single_match() { - let (_s, stream) = Server::new_alpn(); - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_verify(SslVerifyMode::PEER); - ctx.set_npn_protocols(&[b"spdy/3.1"]).unwrap(); - match ctx.set_ca_file(&Path::new("test/root-ca.pem")) { - Ok(_) => {} - Err(err) => panic!("Unexpected error {:?}", err), - } - let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) { - Ok(stream) => stream, - Err(err) => panic!("Expected success, got {:?}", err), - }; - // The client now only supports one of the server's protocols, so that one - // is used. - assert_eq!(b"spdy/3.1", stream.ssl().selected_npn_protocol().unwrap()); -} - -/// Tests that when the `SslStream` is created as a server stream, the protocols -/// are correctly advertised to the client. -#[test] -#[cfg(not(any(libressl261, libressl262, libressl26x)))] -fn test_npn_server_advertise_multiple() { - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - let localhost = listener.local_addr().unwrap(); - // We create a different context instance for the server... - let listener_ctx = { - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_verify(SslVerifyMode::PEER); - ctx.set_npn_protocols(&[b"http/1.1", b"spdy/3.1"]).unwrap(); - assert!( - ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM) - .is_ok() - ); - ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM) - .unwrap(); - ctx.build() - }; - // Have the listener wait on the connection in a different thread. - thread::spawn(move || { - let (stream, _) = listener.accept().unwrap(); - Ssl::new(&listener_ctx).unwrap().accept(stream).unwrap(); - }); - - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_verify(SslVerifyMode::PEER); - ctx.set_npn_protocols(&[b"spdy/3.1"]).unwrap(); - match ctx.set_ca_file(&Path::new("test/root-ca.pem")) { - Ok(_) => {} - Err(err) => panic!("Unexpected error {:?}", err), - } - // Now connect to the socket and make sure the protocol negotiation works... - let stream = TcpStream::connect(localhost).unwrap(); - let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) { - Ok(stream) => stream, - Err(err) => panic!("Expected success, got {:?}", err), - }; - // SPDY is selected since that's the only thing the client supports. - assert_eq!(b"spdy/3.1", stream.ssl().selected_npn_protocol().unwrap()); -} - -/// Tests that when the `SslStream` is created as a server stream, the protocols -/// are correctly advertised to the client. -#[test] -#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] -fn test_alpn_server_advertise_multiple() { - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - let localhost = listener.local_addr().unwrap(); - // We create a different context instance for the server... - let listener_ctx = { - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_verify(SslVerifyMode::PEER); - ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]).unwrap(); - assert!( - ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM) - .is_ok() - ); - ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM) - .unwrap(); - ctx.build() - }; - // Have the listener wait on the connection in a different thread. - thread::spawn(move || { - let (stream, _) = listener.accept().unwrap(); - Ssl::new(&listener_ctx).unwrap().accept(stream).unwrap(); - }); - - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_verify(SslVerifyMode::PEER); - ctx.set_alpn_protocols(&[b"spdy/3.1"]).unwrap(); - match ctx.set_ca_file(&Path::new("test/root-ca.pem")) { - Ok(_) => {} - Err(err) => panic!("Unexpected error {:?}", err), - } - // Now connect to the socket and make sure the protocol negotiation works... - let stream = TcpStream::connect(localhost).unwrap(); - let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) { - Ok(stream) => stream, - Err(err) => panic!("Expected success, got {:?}", err), - }; - // SPDY is selected since that's the only thing the client supports. - assert_eq!(b"spdy/3.1", stream.ssl().selected_alpn_protocol().unwrap()); -} - -/// Test that Servers supporting ALPN don't report a protocol when none of their protocols match -/// the client's reported protocol. -#[test] -#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] -fn test_alpn_server_select_none() { - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - let localhost = listener.local_addr().unwrap(); - // We create a different context instance for the server... - let listener_ctx = { - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_verify(SslVerifyMode::PEER); - ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]).unwrap(); - assert!( - ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM) - .is_ok() - ); - ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM) - .unwrap(); - ctx.build() - }; - // Have the listener wait on the connection in a different thread. - thread::spawn(move || { - let (stream, _) = listener.accept().unwrap(); - Ssl::new(&listener_ctx).unwrap().accept(stream).unwrap(); - }); - - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_verify(SslVerifyMode::PEER); - ctx.set_alpn_protocols(&[b"http/2"]).unwrap(); - ctx.set_ca_file(&Path::new("test/root-ca.pem")).unwrap(); - // Now connect to the socket and make sure the protocol negotiation works... - let stream = TcpStream::connect(localhost).unwrap(); - let stream = Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap(); - - // Since the protocols from the server and client don't overlap at all, no protocol is selected - assert_eq!(None, stream.ssl().selected_alpn_protocol()); -} - -fn wait_io(stream: &TcpStream, read: bool, timeout_ms: u32) -> bool { - unsafe { - let mut set: select::fd_set = mem::zeroed(); - select::fd_set(&mut set, stream); - - let write = if read { - 0 as *mut _ - } else { - &mut set as *mut _ - }; - let read = if !read { - 0 as *mut _ - } else { - &mut set as *mut _ - }; - select::select(stream, read, write, 0 as *mut _, timeout_ms).unwrap() - } -} - -fn handshake(res: Result, HandshakeError>) -> SslStream { - match res { - Ok(s) => s, - Err(HandshakeError::WouldBlock(s)) => { - wait_io(s.get_ref(), true, 1_000); - handshake(s.handshake()) - } - Err(err) => panic!("error on handshake {:?}", err), - } -} - -#[test] -fn test_write_nonblocking() { - let (_s, stream) = Server::new(); - stream.set_nonblocking(true).unwrap(); - let cx = SslContext::builder(SslMethod::tls()).unwrap().build(); - let mut stream = handshake(Ssl::new(&cx).unwrap().connect(stream)); - - let mut iterations = 0; - loop { - iterations += 1; - if iterations > 7 { - // Probably a safe assumption for the foreseeable future of - // openssl. - panic!("Too many read/write round trips in handshake!!"); - } - let result = stream.ssl_write(b"hello"); - match result { - Ok(_) => { - break; - } - Err(Error::WantRead(_)) => { - assert!(wait_io(stream.get_ref(), true, 1000)); - } - Err(Error::WantWrite(_)) => { - assert!(wait_io(stream.get_ref(), false, 1000)); - } - Err(other) => { - panic!("Unexpected SSL Error: {:?}", other); - } - } - } - - // Second write should succeed immediately--plenty of space in kernel - // buffer, and handshake just completed. - stream.write(" there".as_bytes()).unwrap(); -} - -#[test] -#[cfg_attr(any(libressl, windows, target_arch = "arm"), ignore)] // FIXME(#467) -fn test_read_nonblocking() { - let (_s, stream) = Server::new(); - stream.set_nonblocking(true).unwrap(); - let cx = SslContext::builder(SslMethod::tls()).unwrap().build(); - let mut stream = handshake(Ssl::new(&cx).unwrap().connect(stream)); - - let mut iterations = 0; - loop { - iterations += 1; - if iterations > 7 { - // Probably a safe assumption for the foreseeable future of - // openssl. - panic!("Too many read/write round trips in handshake!!"); - } - let result = stream.ssl_write(b"GET /\r\n\r\n"); - match result { - Ok(n) => { - assert_eq!(n, 9); - break; - } - Err(Error::WantRead(..)) => { - assert!(wait_io(stream.get_ref(), true, 1000)); - } - Err(Error::WantWrite(..)) => { - assert!(wait_io(stream.get_ref(), false, 1000)); - } - Err(other) => { - panic!("Unexpected SSL Error: {:?}", other); - } - } - } - let mut input_buffer = [0u8; 1500]; - let result = stream.ssl_read(&mut input_buffer); - let bytes_read = match result { - Ok(n) => { - // This branch is unlikely, but on an overloaded VM with - // unlucky context switching, the response could actually - // be in the receive buffer before we issue the read() syscall... - n - } - Err(Error::WantRead(..)) => { - assert!(wait_io(stream.get_ref(), true, 3000)); - // Second read should return application data. - stream.read(&mut input_buffer).unwrap() - } - Err(other) => { - panic!("Unexpected SSL Error: {:?}", other); - } - }; - assert!(bytes_read >= 5); - assert_eq!(&input_buffer[..5], b"HTTP/"); -} - -#[test] -#[should_panic(expected = "blammo")] -fn write_panic() { - struct ExplodingStream(TcpStream); - - impl Read for ExplodingStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - } - - impl Write for ExplodingStream { - fn write(&mut self, _: &[u8]) -> io::Result { - panic!("blammo"); - } - - fn flush(&mut self) -> io::Result<()> { - self.0.flush() - } - } - - let (_s, stream) = Server::new(); - let stream = ExplodingStream(stream); - - let ctx = SslContext::builder(SslMethod::tls()).unwrap(); - let _ = Ssl::new(&ctx.build()).unwrap().connect(stream); -} - -#[test] -#[should_panic(expected = "blammo")] -fn read_panic() { - struct ExplodingStream(TcpStream); - - impl Read for ExplodingStream { - fn read(&mut self, _: &mut [u8]) -> io::Result { - panic!("blammo"); - } - } - - impl Write for ExplodingStream { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.0.flush() - } - } - - let (_s, stream) = Server::new(); - let stream = ExplodingStream(stream); - - let ctx = SslContext::builder(SslMethod::tls()).unwrap(); - let _ = Ssl::new(&ctx.build()).unwrap().connect(stream); -} - -#[test] -#[should_panic(expected = "blammo")] -fn flush_panic() { - struct ExplodingStream(TcpStream); - - impl Read for ExplodingStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - } - - impl Write for ExplodingStream { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - panic!("blammo"); - } - } - - let (_s, stream) = Server::new(); - let stream = ExplodingStream(stream); - - let ctx = SslContext::builder(SslMethod::tls()).unwrap(); - let mut stream = Ssl::new(&ctx.build()) - .unwrap() - .connect(stream) - .ok() - .unwrap(); - let _ = stream.flush(); -} - -#[test] -fn refcount_ssl_context() { - let mut ssl = { - let ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ssl::Ssl::new(&ctx.build()).unwrap() - }; - - { - let new_ctx_a = SslContext::builder(SslMethod::tls()).unwrap().build(); - let _new_ctx_b = ssl.set_ssl_context(&new_ctx_a); - } -} - -#[test] -fn default_verify_paths() { - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_default_verify_paths().unwrap(); - ctx.set_verify(SslVerifyMode::PEER); - let s = TcpStream::connect("google.com:443").unwrap(); - let mut socket = Ssl::new(&ctx.build()).unwrap().connect(s).unwrap(); - - socket.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap(); - let mut result = vec![]; - socket.read_to_end(&mut result).unwrap(); - - println!("{}", String::from_utf8_lossy(&result)); - assert!(result.starts_with(b"HTTP/1.0")); - assert!(result.ends_with(b"\r\n") || result.ends_with(b"")); -} - -#[test] -fn add_extra_chain_cert() { - let cert = include_bytes!("../../../test/cert.pem"); - let cert = X509::from_pem(cert).unwrap(); - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.add_extra_chain_cert(cert).unwrap(); -} - -#[test] -#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] -fn verify_valid_hostname() { - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_default_verify_paths().unwrap(); - ctx.set_verify(SslVerifyMode::PEER); - - let mut ssl = Ssl::new(&ctx.build()).unwrap(); - ssl.param_mut() - .set_hostflags(X509CheckFlags::NO_PARTIAL_WILDCARDS); - ssl.param_mut().set_host("google.com").unwrap(); - - let s = TcpStream::connect("google.com:443").unwrap(); - let mut socket = ssl.connect(s).unwrap(); - - socket.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap(); - let mut result = vec![]; - socket.read_to_end(&mut result).unwrap(); - - println!("{}", String::from_utf8_lossy(&result)); - assert!(result.starts_with(b"HTTP/1.0")); - assert!(result.ends_with(b"\r\n") || result.ends_with(b"")); -} - -#[test] -#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] -fn verify_invalid_hostname() { - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_default_verify_paths().unwrap(); - ctx.set_verify(SslVerifyMode::PEER); - - let mut ssl = Ssl::new(&ctx.build()).unwrap(); - ssl.param_mut() - .set_hostflags(X509CheckFlags::NO_PARTIAL_WILDCARDS); - ssl.param_mut().set_host("foobar.com").unwrap(); - - let s = TcpStream::connect("google.com:443").unwrap(); - assert!(ssl.connect(s).is_err()); -} - -#[test] -fn connector_valid_hostname() { - let connector = SslConnector::builder(SslMethod::tls()).unwrap().build(); - - let s = TcpStream::connect("google.com:443").unwrap(); - let mut socket = connector.connect("google.com", s).unwrap(); - - socket.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap(); - let mut result = vec![]; - socket.read_to_end(&mut result).unwrap(); - - println!("{}", String::from_utf8_lossy(&result)); - assert!(result.starts_with(b"HTTP/1.0")); - assert!(result.ends_with(b"\r\n") || result.ends_with(b"")); -} - -#[test] -fn connector_invalid_hostname() { - let connector = SslConnector::builder(SslMethod::tls()).unwrap().build(); - - let s = TcpStream::connect("google.com:443").unwrap(); - assert!(connector.connect("foobar.com", s).is_err()); -} - -#[test] -fn connector_invalid_no_hostname_verification() { - let connector = SslConnector::builder(SslMethod::tls()).unwrap().build(); - - let s = TcpStream::connect("google.com:443").unwrap(); - connector - .configure() - .unwrap() - .use_server_name_indication(false) - .verify_hostname(false) - .connect("foobar.com", s) - .unwrap(); -} - -#[test] -fn connector_no_hostname_still_verifies() { - let (_s, tcp) = Server::new(); - - let connector = SslConnector::builder(SslMethod::tls()).unwrap().build(); - - assert!( - connector - .configure() - .unwrap() - .verify_hostname(false) - .connect("fizzbuzz.com", tcp) - .is_err() - ); -} - -#[test] -fn connector_no_hostname_can_disable_verify() { - let (_s, tcp) = Server::new(); - - let mut connector = SslConnector::builder(SslMethod::tls()).unwrap(); - connector.set_verify(SslVerifyMode::NONE); - let connector = connector.build(); - - connector - .configure() - .unwrap() - .verify_hostname(false) - .connect("foobar.com", tcp) - .unwrap(); -} - -#[test] -fn connector_client_server_mozilla_intermediate() { - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - let port = listener.local_addr().unwrap().port(); - - let t = thread::spawn(move || { - let key = PKey::private_key_from_pem(KEY).unwrap(); - let cert = X509::from_pem(CERT).unwrap(); - let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); - acceptor.set_private_key(&key).unwrap(); - acceptor.set_certificate(&cert).unwrap(); - let acceptor = acceptor.build(); - let stream = listener.accept().unwrap().0; - let mut stream = acceptor.accept(stream).unwrap(); - - stream.write_all(b"hello").unwrap(); - }); - - let mut connector = SslConnector::builder(SslMethod::tls()).unwrap(); - connector.set_ca_file("test/root-ca.pem").unwrap(); - let connector = connector.build(); - - let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); - let mut stream = connector.connect("foobar.com", stream).unwrap(); - - let mut buf = [0; 5]; - stream.read_exact(&mut buf).unwrap(); - assert_eq!(b"hello", &buf); - - t.join().unwrap(); -} - -#[test] -fn connector_client_server_mozilla_modern() { - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - let port = listener.local_addr().unwrap().port(); - - let t = thread::spawn(move || { - let key = PKey::private_key_from_pem(KEY).unwrap(); - let cert = X509::from_pem(CERT).unwrap(); - let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); - acceptor.set_private_key(&key).unwrap(); - acceptor.set_certificate(&cert).unwrap(); - let acceptor = acceptor.build(); - let stream = listener.accept().unwrap().0; - let mut stream = acceptor.accept(stream).unwrap(); - - stream.write_all(b"hello").unwrap(); - }); - - let mut connector = SslConnector::builder(SslMethod::tls()).unwrap(); - connector.set_ca_file("test/root-ca.pem").unwrap(); - let connector = connector.build(); - - let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); - let mut stream = connector.connect("foobar.com", stream).unwrap(); - - let mut buf = [0; 5]; - stream.read_exact(&mut buf).unwrap(); - assert_eq!(b"hello", &buf); - - t.join().unwrap(); -} - -#[test] -fn shutdown() { - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - let port = listener.local_addr().unwrap().port(); - - thread::spawn(move || { - let stream = listener.accept().unwrap().0; - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM) - .unwrap(); - ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM) - .unwrap(); - let ssl = Ssl::new(&ctx.build()).unwrap(); - let mut stream = ssl.accept(stream).unwrap(); - - stream.write_all(b"hello").unwrap(); - let mut buf = [0; 1]; - assert_eq!(stream.read(&mut buf).unwrap(), 0); - assert_eq!(stream.shutdown().unwrap(), ShutdownResult::Received); - }); - - let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); - let ctx = SslContext::builder(SslMethod::tls()).unwrap(); - let ssl = Ssl::new(&ctx.build()).unwrap(); - let mut stream = ssl.connect(stream).unwrap(); - - let mut buf = [0; 5]; - stream.read_exact(&mut buf).unwrap(); - assert_eq!(b"hello", &buf); - - assert_eq!(stream.shutdown().unwrap(), ShutdownResult::Sent); - assert_eq!(stream.shutdown().unwrap(), ShutdownResult::Received); -} - -#[test] -fn client_ca_list() { - let names = X509Name::load_client_ca_file("test/root-ca.pem").unwrap(); - assert_eq!(names.len(), 1); - - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_client_ca_list(names); -} - -#[test] -fn cert_store() { - let (_s, tcp) = Server::new(); - - let cert = X509::from_pem(ROOT_CERT).unwrap(); - - let mut ctx = SslConnector::builder(SslMethod::tls()).unwrap(); - ctx.cert_store_mut().add_cert(cert).unwrap(); - let ctx = ctx.build(); - - ctx.connect("foobar.com", tcp).unwrap(); -} - -#[test] -fn tmp_dh_callback() { - static CALLED_BACK: AtomicBool = ATOMIC_BOOL_INIT; - - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - let port = listener.local_addr().unwrap().port(); - - thread::spawn(move || { - let stream = listener.accept().unwrap().0; - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM) - .unwrap(); - ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM) - .unwrap(); - ctx.set_tmp_dh_callback(|_, _, _| { - CALLED_BACK.store(true, Ordering::SeqCst); - let dh = include_bytes!("../../../test/dhparams.pem"); - Dh::from_pem(dh) - }); - let ssl = Ssl::new(&ctx.build()).unwrap(); - ssl.accept(stream).unwrap(); - }); - - let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_cipher_list("EDH").unwrap(); - let ssl = Ssl::new(&ctx.build()).unwrap(); - ssl.connect(stream).unwrap(); - - assert!(CALLED_BACK.load(Ordering::SeqCst)); -} - -#[test] -#[cfg(any(all(feature = "v101", ossl101, not(any(libressl261, libressl262, libressl26x))), - all(feature = "v102", ossl102)))] -fn tmp_ecdh_callback() { - use ec::EcKey; - use nid::Nid; - - static CALLED_BACK: AtomicBool = ATOMIC_BOOL_INIT; - - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - let port = listener.local_addr().unwrap().port(); - - thread::spawn(move || { - let stream = listener.accept().unwrap().0; - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM) - .unwrap(); - ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM) - .unwrap(); - ctx.set_tmp_ecdh_callback(|_, _, _| { - CALLED_BACK.store(true, Ordering::SeqCst); - EcKey::from_curve_name(Nid::X9_62_PRIME256V1) - }); - let ssl = Ssl::new(&ctx.build()).unwrap(); - ssl.accept(stream).unwrap(); - }); - - let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_cipher_list("ECDH").unwrap(); - let ssl = Ssl::new(&ctx.build()).unwrap(); - ssl.connect(stream).unwrap(); - - assert!(CALLED_BACK.load(Ordering::SeqCst)); -} - -#[test] -fn tmp_dh_callback_ssl() { - static CALLED_BACK: AtomicBool = ATOMIC_BOOL_INIT; - - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - let port = listener.local_addr().unwrap().port(); - - thread::spawn(move || { - let stream = listener.accept().unwrap().0; - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM) - .unwrap(); - ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM) - .unwrap(); - let mut ssl = Ssl::new(&ctx.build()).unwrap(); - ssl.set_tmp_dh_callback(|_, _, _| { - CALLED_BACK.store(true, Ordering::SeqCst); - let dh = include_bytes!("../../../test/dhparams.pem"); - Dh::from_pem(dh) - }); - ssl.accept(stream).unwrap(); - }); - - let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_cipher_list("EDH").unwrap(); - let ssl = Ssl::new(&ctx.build()).unwrap(); - ssl.connect(stream).unwrap(); - - assert!(CALLED_BACK.load(Ordering::SeqCst)); -} - -#[test] -#[cfg(any(all(feature = "v101", ossl101, not(any(libressl261, libressl262, libressl26x))), - all(feature = "v102", ossl102)))] -fn tmp_ecdh_callback_ssl() { - use ec::EcKey; - use nid::Nid; - - static CALLED_BACK: AtomicBool = ATOMIC_BOOL_INIT; - - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - let port = listener.local_addr().unwrap().port(); - - thread::spawn(move || { - let stream = listener.accept().unwrap().0; - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM) - .unwrap(); - ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM) - .unwrap(); - let mut ssl = Ssl::new(&ctx.build()).unwrap(); - ssl.set_tmp_ecdh_callback(|_, _, _| { - CALLED_BACK.store(true, Ordering::SeqCst); - EcKey::from_curve_name(Nid::X9_62_PRIME256V1) - }); - ssl.accept(stream).unwrap(); - }); - - let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_cipher_list("ECDH").unwrap(); - let ssl = Ssl::new(&ctx.build()).unwrap(); - ssl.connect(stream).unwrap(); - - assert!(CALLED_BACK.load(Ordering::SeqCst)); -} - -#[test] -fn idle_session() { - let ctx = SslContext::builder(SslMethod::tls()).unwrap().build(); - let ssl = Ssl::new(&ctx).unwrap(); - assert!(ssl.session().is_none()); -} - -#[test] -fn active_session() { - let connector = SslConnector::builder(SslMethod::tls()).unwrap().build(); - - let s = TcpStream::connect("google.com:443").unwrap(); - let socket = connector.connect("google.com", s).unwrap(); - let session = socket.ssl().session().unwrap(); - let len = session.master_key_len(); - let mut buf = vec![0; len - 1]; - let copied = session.master_key(&mut buf); - assert_eq!(copied, buf.len()); - let mut buf = vec![0; len + 1]; - let copied = session.master_key(&mut buf); - assert_eq!(copied, len); -} - -#[test] -fn status_callbacks() { - static CALLED_BACK_SERVER: AtomicBool = ATOMIC_BOOL_INIT; - static CALLED_BACK_CLIENT: AtomicBool = ATOMIC_BOOL_INIT; - - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - let port = listener.local_addr().unwrap().port(); - - let guard = thread::spawn(move || { - let stream = listener.accept().unwrap().0; - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_certificate_file(&Path::new("test/cert.pem"), X509Filetype::PEM) - .unwrap(); - ctx.set_private_key_file(&Path::new("test/key.pem"), X509Filetype::PEM) - .unwrap(); - ctx.set_status_callback(|ssl| { - CALLED_BACK_SERVER.store(true, Ordering::SeqCst); - let response = OcspResponse::create(OcspResponseStatus::UNAUTHORIZED, None).unwrap(); - let response = response.to_der().unwrap(); - ssl.set_ocsp_status(&response).unwrap(); - Ok(true) - }).unwrap(); - let ssl = Ssl::new(&ctx.build()).unwrap(); - ssl.accept(stream).unwrap(); - }); - - let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_status_callback(|ssl| { - CALLED_BACK_CLIENT.store(true, Ordering::SeqCst); - let response = OcspResponse::from_der(ssl.ocsp_status().unwrap()).unwrap(); - assert_eq!(response.status(), OcspResponseStatus::UNAUTHORIZED); - Ok(true) - }).unwrap(); - let mut ssl = Ssl::new(&ctx.build()).unwrap(); - ssl.set_status_type(StatusType::OCSP).unwrap(); - ssl.connect(stream).unwrap(); - - assert!(CALLED_BACK_SERVER.load(Ordering::SeqCst)); - assert!(CALLED_BACK_CLIENT.load(Ordering::SeqCst)); - - guard.join().unwrap(); -} - -fn _check_kinds() { - fn is_send() {} - fn is_sync() {} - - is_send::>(); - is_sync::>(); -} diff --git a/openssl/src/ssl/tests/select.rs b/openssl/src/ssl/tests/select.rs deleted file mode 100644 index 36d5ed49..00000000 --- a/openssl/src/ssl/tests/select.rs +++ /dev/null @@ -1,74 +0,0 @@ -use libc; -pub use self::imp::*; - -#[cfg(unix)] -mod imp { - use std::os::unix::prelude::*; - use std::io; - use libc; - - pub use libc::fd_set; - - pub fn fd_set(set: &mut fd_set, f: &F) { - unsafe { - libc::FD_SET(f.as_raw_fd(), set); - } - } - - pub unsafe fn select( - max: &F, - read: *mut fd_set, - write: *mut fd_set, - error: *mut fd_set, - timeout_ms: u32, - ) -> io::Result { - let mut timeout = libc::timeval { - tv_sec: (timeout_ms / 1000) as libc::time_t, - tv_usec: (timeout_ms % 1000 * 1000) as libc::suseconds_t, - }; - let rc = libc::select(max.as_raw_fd() + 1, read, write, error, &mut timeout); - if rc < 0 { - Err(io::Error::last_os_error()) - } else { - Ok(rc != 0) - } - } -} - -#[cfg(windows)] -mod imp { - extern crate winapi; - extern crate ws2_32; - - use std::os::windows::prelude::*; - use std::io; - use libc::{c_uint, c_long}; - use self::winapi::SOCKET; - use self::winapi::winsock2; - - pub use self::winapi::winsock2::fd_set; - - pub fn fd_set(set: &mut fd_set, f: &F) { - set.fd_array[set.fd_count as usize] = f.as_raw_socket(); - set.fd_count += 1; - } - - pub unsafe fn select( - _max: &F, - read: *mut fd_set, - write: *mut fd_set, - error: *mut fd_set, - timeout_ms: u32, - ) -> io::Result { - let mut timeout = winsock2::timeval { - tv_sec: (timeout_ms / 1000) as c_long, - tv_usec: (timeout_ms % 1000 * 1000) as c_long, - }; - let rc = ws2_32::select(1, read, write, error, &mut timeout); - if rc < 0 { - Err(io::Error::last_os_error()) - } else { - Ok(rc != 0) - } - } -} -- cgit v1.2.3