aboutsummaryrefslogtreecommitdiff
path: root/openssl/src/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'openssl/src/ssl')
-rw-r--r--openssl/src/ssl/bio.rs169
-rw-r--r--openssl/src/ssl/connector.rs452
-rw-r--r--openssl/src/ssl/error.rs55
-rw-r--r--openssl/src/ssl/mod.rs1159
-rw-r--r--openssl/src/ssl/tests/mod.rs690
5 files changed, 1654 insertions, 871 deletions
diff --git a/openssl/src/ssl/bio.rs b/openssl/src/ssl/bio.rs
index c5663eb1..486b4dba 100644
--- a/openssl/src/ssl/bio.rs
+++ b/openssl/src/ssl/bio.rs
@@ -1,14 +1,14 @@
use libc::{c_char, c_int, c_long, c_void, strlen};
-use ffi::{self, BIO, BIO_CTRL_FLUSH, BIO_TYPE_NONE, BIO_new, BIO_clear_retry_flags,
- BIO_set_retry_read, BIO_set_retry_write};
+use ffi::{BIO, BIO_CTRL_FLUSH, BIO_new, BIO_clear_retry_flags, BIO_set_retry_read,
+ BIO_set_retry_write};
use std::any::Any;
use std::io;
use std::io::prelude::*;
use std::mem;
use std::ptr;
use std::slice;
-use std::sync::Arc;
+use cvt_p;
use error::ErrorStack;
pub struct StreamState<S> {
@@ -18,29 +18,19 @@ pub struct StreamState<S> {
}
/// Safe wrapper for BIO_METHOD
-pub struct BioMethod(ffi::BIO_METHOD);
+pub struct BioMethod(compat::BIO_METHOD);
impl BioMethod {
- pub fn new<S: Read + Write>() -> BioMethod {
- BioMethod(ffi::BIO_METHOD {
- type_: BIO_TYPE_NONE,
- name: b"rust\0".as_ptr() as *const _,
- bwrite: Some(bwrite::<S>),
- bread: Some(bread::<S>),
- bputs: Some(bputs::<S>),
- bgets: None,
- ctrl: Some(ctrl::<S>),
- create: Some(create),
- destroy: Some(destroy::<S>),
- callback_ctrl: None,
- })
+ fn new<S: Read + Write>() -> BioMethod {
+ BioMethod(compat::BIO_METHOD::new::<S>())
}
}
+unsafe impl Sync for BioMethod {}
unsafe impl Send for BioMethod {}
-pub fn new<S: Read + Write>(stream: S) -> Result<(*mut BIO, Arc<BioMethod>), ErrorStack> {
- let method = Arc::new(BioMethod::new::<S>());
+pub fn new<S: Read + Write>(stream: S) -> Result<(*mut BIO, BioMethod), ErrorStack> {
+ let method = BioMethod::new::<S>();
let state = Box::new(StreamState {
stream: stream,
@@ -49,9 +39,9 @@ pub fn new<S: Read + Write>(stream: S) -> Result<(*mut BIO, Arc<BioMethod>), Err
});
unsafe {
- let bio = try_ssl_null!(BIO_new(&method.0));
- (*bio).ptr = Box::into_raw(state) as *mut _;
- (*bio).init = 1;
+ let bio = try!(cvt_p(BIO_new(method.0.get())));
+ compat::BIO_set_data(bio, Box::into_raw(state) as *mut _);
+ compat::BIO_set_init(bio, 1);
return Ok((bio, method));
}
@@ -62,14 +52,13 @@ pub unsafe fn take_error<S>(bio: *mut BIO) -> Option<io::Error> {
state.error.take()
}
-#[cfg_attr(not(feature = "nightly"), allow(dead_code))]
pub unsafe fn take_panic<S>(bio: *mut BIO) -> Option<Box<Any + Send>> {
let state = state::<S>(bio);
state.panic.take()
}
pub unsafe fn get_ref<'a, S: 'a>(bio: *mut BIO) -> &'a S {
- let state: &'a StreamState<S> = mem::transmute((*bio).ptr);
+ let state: &'a StreamState<S> = mem::transmute(compat::BIO_get_data(bio));
&state.stream
}
@@ -78,23 +67,15 @@ pub unsafe fn get_mut<'a, S: 'a>(bio: *mut BIO) -> &'a mut S {
}
unsafe fn state<'a, S: 'a>(bio: *mut BIO) -> &'a mut StreamState<S> {
- mem::transmute((*bio).ptr)
+ mem::transmute(compat::BIO_get_data(bio))
}
-#[cfg(feature = "nightly")]
fn catch_unwind<F, T>(f: F) -> Result<T, Box<Any + Send>>
where F: FnOnce() -> T
{
::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(f))
}
-#[cfg(not(feature = "nightly"))]
-fn catch_unwind<F, T>(f: F) -> Result<T, Box<Any + Send>>
- where F: FnOnce() -> T
-{
- Ok(f())
-}
-
unsafe extern "C" fn bwrite<S: Write>(bio: *mut BIO, buf: *const c_char, len: c_int) -> c_int {
BIO_clear_retry_flags(bio);
@@ -176,10 +157,10 @@ unsafe extern "C" fn ctrl<S: Write>(bio: *mut BIO,
}
unsafe extern "C" fn create(bio: *mut BIO) -> c_int {
- (*bio).init = 0;
- (*bio).num = 0;
- (*bio).ptr = ptr::null_mut();
- (*bio).flags = 0;
+ compat::BIO_set_init(bio, 0);
+ compat::BIO_set_num(bio, 0);
+ compat::BIO_set_data(bio, ptr::null_mut());
+ compat::BIO_set_flags(bio, 0);
1
}
@@ -188,9 +169,115 @@ unsafe extern "C" fn destroy<S>(bio: *mut BIO) -> c_int {
return 0;
}
- assert!(!(*bio).ptr.is_null());
- Box::<StreamState<S>>::from_raw((*bio).ptr as *mut _);
- (*bio).ptr = ptr::null_mut();
- (*bio).init = 0;
+ let data = compat::BIO_get_data(bio);
+ assert!(!data.is_null());
+ Box::<StreamState<S>>::from_raw(data as *mut _);
+ compat::BIO_set_data(bio, ptr::null_mut());
+ compat::BIO_set_init(bio, 0);
1
}
+
+#[cfg(ossl110)]
+#[allow(bad_style)]
+mod compat {
+ use std::io::{Read, Write};
+
+ use libc::c_int;
+ use ffi;
+ pub use ffi::{BIO_set_init, BIO_set_flags, BIO_set_data, BIO_get_data};
+
+ pub unsafe fn BIO_set_num(_bio: *mut ffi::BIO, _num: c_int) {}
+
+ pub struct BIO_METHOD(*mut ffi::BIO_METHOD);
+
+ impl BIO_METHOD {
+ pub fn new<S: Read + Write>() -> BIO_METHOD {
+ unsafe {
+ let ptr = ffi::BIO_meth_new(ffi::BIO_TYPE_NONE, b"rust\0".as_ptr() as *const _);
+ assert!(!ptr.is_null());
+ let ret = BIO_METHOD(ptr);
+ assert!(ffi::BIO_meth_set_write(ptr, super::bwrite::<S>) != 0);
+ assert!(ffi::BIO_meth_set_read(ptr, super::bread::<S>) != 0);
+ assert!(ffi::BIO_meth_set_puts(ptr, super::bputs::<S>) != 0);
+ assert!(ffi::BIO_meth_set_ctrl(ptr, super::ctrl::<S>) != 0);
+ assert!(ffi::BIO_meth_set_create(ptr, super::create) != 0);
+ assert!(ffi::BIO_meth_set_destroy(ptr, super::destroy::<S>) != 0);
+ return ret;
+ }
+ }
+
+ pub fn get(&self) -> *mut ffi::BIO_METHOD {
+ self.0
+ }
+ }
+
+ impl Drop for BIO_METHOD {
+ fn drop(&mut self) {
+ unsafe {
+ ffi::BIO_meth_free(self.0);
+ }
+ }
+ }
+}
+
+#[cfg(ossl10x)]
+#[allow(bad_style)]
+mod compat {
+ use std::io::{Read, Write};
+
+ use ffi;
+ use libc::{c_int, c_void};
+
+ pub struct BIO_METHOD(*mut ffi::BIO_METHOD);
+
+ impl BIO_METHOD {
+ pub fn new<S: Read + Write>() -> BIO_METHOD {
+ let ptr = Box::new(ffi::BIO_METHOD {
+ type_: ffi::BIO_TYPE_NONE,
+ name: b"rust\0".as_ptr() as *const _,
+ bwrite: Some(super::bwrite::<S>),
+ bread: Some(super::bread::<S>),
+ bputs: Some(super::bputs::<S>),
+ bgets: None,
+ ctrl: Some(super::ctrl::<S>),
+ create: Some(super::create),
+ destroy: Some(super::destroy::<S>),
+ callback_ctrl: None,
+ });
+
+ BIO_METHOD(Box::into_raw(ptr))
+ }
+
+ pub fn get(&self) -> *mut ffi::BIO_METHOD {
+ self.0
+ }
+ }
+
+ impl Drop for BIO_METHOD {
+ fn drop(&mut self) {
+ unsafe {
+ Box::<ffi::BIO_METHOD>::from_raw(self.0);
+ }
+ }
+ }
+
+ pub unsafe fn BIO_set_init(bio: *mut ffi::BIO, init: c_int) {
+ (*bio).init = init;
+ }
+
+ pub unsafe fn BIO_set_flags(bio: *mut ffi::BIO, flags: c_int) {
+ (*bio).flags = flags;
+ }
+
+ pub unsafe fn BIO_get_data(bio: *mut ffi::BIO) -> *mut c_void {
+ (*bio).ptr
+ }
+
+ pub unsafe fn BIO_set_data(bio: *mut ffi::BIO, data: *mut c_void) {
+ (*bio).ptr = data;
+ }
+
+ pub unsafe fn BIO_set_num(bio: *mut ffi::BIO, num: c_int) {
+ (*bio).num = num;
+ }
+}
diff --git a/openssl/src/ssl/connector.rs b/openssl/src/ssl/connector.rs
new file mode 100644
index 00000000..0d92529d
--- /dev/null
+++ b/openssl/src/ssl/connector.rs
@@ -0,0 +1,452 @@
+use std::io::{Read, Write};
+
+use dh::Dh;
+use error::ErrorStack;
+use ssl::{self, SslMethod, SslContextBuilder, SslContext, Ssl, SSL_VERIFY_PEER, SslStream,
+ HandshakeError};
+use pkey::PKeyRef;
+use x509::X509Ref;
+
+// Serialized form of DH_get_2048_256
+#[cfg(any(ossl101, all(test, any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))))]
+const DHPARAM_PEM: &'static str = r#"
+-----BEGIN DH PARAMETERS-----
+MIICCQKCAQEAh6jmHbS2Zjz/u9GcZRlZmYzu9ghmDdDyXSzu1ENeOwDgDfjx1hlX
+1Pr330VhsqowFsPZETQJb6o79Cltgw6afCCeDGSXUXq9WoqdMGvPZ+2R+eZyW0dY
+wCLgse9Cdb97bFv8EdRfkIi5QfVOseWbuLw5oL8SMH9cT9twxYGyP3a2Osrhyqa3
+kC1SUmc1SIoO8TxtmlG/pKs62DR3llJNjvahZ7WkGCXZZ+FE5RQFZCUcysuD5rSG
+9rPKP3lxUGAmwLhX9omWKFbe1AEKvQvmIcOjlgpU5xDDdfJjddcBQQOktUMwwZiv
+EmEW0iduEXFfaTh3+tfvCcrbCUrpHhoVlwKCAQA/syybcxNNCy53UGZg7b1ITKex
+jyHvIFQH9Hk6GguhJRDbwVB3vkY//0/tSqwLtVW+OmwbDGtHsbw3c79+jG9ikBIo
++MKMuxilWuMTQQAKZQGW+THHelfy3fRj5ensFEt3feYqqrioYorDdtKC1u04ZOZ5
+gkKOvIMdFDSPby+Rk7UEWvJ2cWTh38lnwfs/LlWkvRv/6DucgNBSuYXRguoK2yo7
+cxPT/hTISEseBSWIubfSu9LfAWGZ7NBuFVfNCRWzNTu7ZODsN3/QKDcN+StSx4kU
+KM3GfrYYS1I9HbJGwy9jB4SQ8A741kfRSNR5VFFeIyfP75jFgmZLTA9sxBZZ
+-----END DH PARAMETERS-----
+"#;
+
+fn ctx(method: SslMethod) -> Result<SslContextBuilder, ErrorStack> {
+ let mut ctx = try!(SslContextBuilder::new(method));
+
+ let mut opts = ssl::SSL_OP_ALL;
+ opts |= ssl::SSL_OP_NO_TICKET;
+ opts |= ssl::SSL_OP_NO_COMPRESSION;
+ opts &= !ssl::SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG;
+ opts &= !ssl::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
+ opts |= ssl::SSL_OP_NO_SSLV2;
+ opts |= ssl::SSL_OP_NO_SSLV3;
+ opts |= ssl::SSL_OP_SINGLE_DH_USE;
+ opts |= ssl::SSL_OP_SINGLE_ECDH_USE;
+ opts |= ssl::SSL_OP_CIPHER_SERVER_PREFERENCE;
+ ctx.set_options(opts);
+
+ let mode = ssl::SSL_MODE_AUTO_RETRY | ssl::SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER |
+ ssl::SSL_MODE_ENABLE_PARTIAL_WRITE;
+ ctx.set_mode(mode);
+
+ Ok(ctx)
+}
+
+/// A builder for `SslConnector`s.
+pub struct SslConnectorBuilder(SslContextBuilder);
+
+impl SslConnectorBuilder {
+ /// Creates a new builder for TLS connections.
+ ///
+ /// The default configuration is subject to change, and is currently derived from Python.
+ pub fn new(method: SslMethod) -> Result<SslConnectorBuilder, ErrorStack> {
+ let mut ctx = try!(ctx(method));
+ try!(ctx.set_default_verify_paths());
+ // From https://github.com/python/cpython/blob/c30098c8c6014f3340a369a31df9c74bdbacc269/Lib/ssl.py#L191
+ try!(ctx.set_cipher_list("ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:DH+CHACHA20:ECDH+AES256:\
+ DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:RSA+AESGCM:\
+ RSA+AES:RSA+HIGH:!aNULL:!eNULL:!MD5:!3DES"));
+
+ Ok(SslConnectorBuilder(ctx))
+ }
+
+ /// Returns a shared reference to the inner `SslContextBuilder`.
+ pub fn builder(&self) -> &SslContextBuilder {
+ &self.0
+ }
+
+ /// Returns a mutable reference to the inner `SslContextBuilder`.
+ pub fn builder_mut(&mut self) -> &mut SslContextBuilder {
+ &mut self.0
+ }
+
+ /// Consumes the builder, returning a `SslConnector`.
+ pub fn build(self) -> SslConnector {
+ SslConnector(self.0.build())
+ }
+}
+
+/// A type which wraps client-side streams in a TLS session.
+///
+/// OpenSSL's default configuration is highly insecure. This connector manages the OpenSSL
+/// structures, configuring cipher suites, session options, hostname verification, and more.
+///
+/// OpenSSL's built in hostname verification is used when linking against OpenSSL 1.0.2 or 1.1.0,
+/// and a custom implementation is used when linking against OpenSSL 1.0.1.
+pub struct SslConnector(SslContext);
+
+impl SslConnector {
+ /// Initiates a client-side TLS session on a stream.
+ ///
+ /// The domain is used for SNI and hostname verification.
+ pub fn connect<S>(&self, domain: &str, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
+ where S: Read + Write
+ {
+ let mut ssl = try!(Ssl::new(&self.0));
+ try!(ssl.set_hostname(domain));
+ try!(setup_verify(&mut ssl, domain));
+
+ ssl.connect(stream)
+ }
+}
+
+/// A builder for `SslAcceptor`s.
+pub struct SslAcceptorBuilder(SslContextBuilder);
+
+impl SslAcceptorBuilder {
+ /// Creates a new builder configured to connect to non-legacy clients. This should generally be
+ /// considered a reasonable default choice.
+ ///
+ /// This corresponds to the intermediate configuration of Mozilla's server side TLS
+ /// recommendations. See its [documentation][docs] for more details on specifics.
+ ///
+ /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS
+ pub fn mozilla_intermediate<I>(method: SslMethod,
+ private_key: &PKeyRef,
+ certificate: &X509Ref,
+ chain: I)
+ -> Result<SslAcceptorBuilder, ErrorStack>
+ where I: IntoIterator,
+ I::Item: AsRef<X509Ref>
+ {
+ let mut ctx = try!(ctx(method));
+ let dh = try!(get_dh());
+ try!(ctx.set_tmp_dh(&dh));
+ try!(setup_curves(&mut ctx));
+ try!(ctx.set_cipher_list("ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\
+ ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\
+ ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\
+ DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:\
+ ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:\
+ ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:\
+ ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:\
+ ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:\
+ DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:\
+ DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:\
+ ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:\
+ EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:\
+ AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:\
+ DES-CBC3-SHA:!DSS"));
+ SslAcceptorBuilder::finish_setup(ctx, private_key, certificate, chain)
+ }
+
+ /// Creates a new builder configured to connect to modern clients.
+ ///
+ /// This corresponds to the modern configuration of Mozilla's server side TLS recommendations.
+ /// See its [documentation][docs] for more details on specifics.
+ ///
+ /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS
+ pub fn mozilla_modern<I>(method: SslMethod,
+ private_key: &PKeyRef,
+ certificate: &X509Ref,
+ chain: I)
+ -> Result<SslAcceptorBuilder, ErrorStack>
+ where I: IntoIterator,
+ I::Item: AsRef<X509Ref>
+ {
+ let mut ctx = try!(ctx(method));
+ try!(setup_curves(&mut ctx));
+ try!(ctx.set_cipher_list("ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\
+ ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\
+ ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\
+ ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:\
+ ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256"));
+ SslAcceptorBuilder::finish_setup(ctx, private_key, certificate, chain)
+ }
+
+ fn finish_setup<I>(mut ctx: SslContextBuilder,
+ private_key: &PKeyRef,
+ certificate: &X509Ref,
+ chain: I)
+ -> Result<SslAcceptorBuilder, ErrorStack>
+ where I: IntoIterator,
+ I::Item: AsRef<X509Ref>
+ {
+ try!(ctx.set_private_key(private_key));
+ try!(ctx.set_certificate(certificate));
+ try!(ctx.check_private_key());
+ for cert in chain {
+ try!(ctx.add_extra_chain_cert(cert.as_ref().to_owned()));
+ }
+ Ok(SslAcceptorBuilder(ctx))
+ }
+
+ /// Returns a shared reference to the inner `SslContextBuilder`.
+ pub fn builder(&self) -> &SslContextBuilder {
+ &self.0
+ }
+
+ /// Returns a mutable reference to the inner `SslContextBuilder`.
+ pub fn builder_mut(&mut self) -> &mut SslContextBuilder {
+ &mut self.0
+ }
+
+ /// Consumes the builder, returning a `SslAcceptor`.
+ pub fn build(self) -> SslAcceptor {
+ SslAcceptor(self.0.build())
+ }
+}
+
+#[cfg(ossl101)]
+fn get_dh() -> Result<Dh, ErrorStack> {
+ Dh::from_pem(DHPARAM_PEM.as_bytes())
+}
+
+#[cfg(not(ossl101))]
+fn get_dh() -> Result<Dh, ErrorStack> {
+ use ffi;
+
+ use cvt_p;
+ use types::OpenSslType;
+
+ // manually call into ffi to avoid forcing the features
+ unsafe { cvt_p(ffi::DH_get_2048_256()).map(|p| Dh::from_ptr(p)) }
+}
+
+#[cfg(ossl101)]
+fn setup_curves(ctx: &mut SslContextBuilder) -> Result<(), ErrorStack> {
+ use ec_key::EcKey;
+ use nid;
+
+ let curve = try!(EcKey::new_by_curve_name(nid::X9_62_PRIME256V1));
+ ctx.set_tmp_ecdh(&curve)
+}
+
+#[cfg(ossl102)]
+fn setup_curves(ctx: &mut SslContextBuilder) -> Result<(), ErrorStack> {
+ ctx._set_ecdh_auto(true)
+}
+
+#[cfg(ossl110)]
+fn setup_curves(_: &mut SslContextBuilder) -> Result<(), ErrorStack> {
+ Ok(())
+}
+
+/// A type which wraps server-side streams in a TLS session.
+///
+/// OpenSSL's default configuration is highly insecure. This connector manages the OpenSSL
+/// structures, configuring cipher suites, session options, and more.
+pub struct SslAcceptor(SslContext);
+
+impl SslAcceptor {
+ /// Initiates a server-side TLS session on a stream.
+ pub fn accept<S>(&self, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
+ where S: Read + Write
+ {
+ let ssl = try!(Ssl::new(&self.0));
+ ssl.accept(stream)
+ }
+}
+
+#[cfg(any(ossl102, ossl110))]
+fn setup_verify(ssl: &mut Ssl, domain: &str) -> Result<(), ErrorStack> {
+ ssl.set_verify(SSL_VERIFY_PEER);
+ let param = ssl._param_mut();
+ param.set_hostflags(::verify::X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
+ param.set_host(domain)
+}
+
+#[cfg(ossl101)]
+fn setup_verify(ssl: &mut Ssl, domain: &str) -> Result<(), ErrorStack> {
+ let domain = domain.to_owned();
+ ssl.set_verify_callback(SSL_VERIFY_PEER,
+ move |p, x| verify::verify_callback(&domain, p, x));
+ Ok(())
+}
+
+#[cfg(ossl101)]
+mod verify {
+ use std::net::IpAddr;
+ use std::str;
+
+ use nid;
+ use x509::{X509StoreContextRef, X509Ref, X509NameRef, GeneralName};
+ use stack::Stack;
+ use types::OpenSslTypeRef;
+
+ pub fn verify_callback(domain: &str,
+ preverify_ok: bool,
+ x509_ctx: &X509StoreContextRef)
+ -> bool {
+ if !preverify_ok || x509_ctx.error_depth() != 0 {
+ return preverify_ok;
+ }
+
+ match x509_ctx.current_cert() {
+ Some(x509) => verify_hostname(domain, &x509),
+ None => true,
+ }
+ }
+
+ fn verify_hostname(domain: &str, cert: &X509Ref) -> bool {
+ match cert.subject_alt_names() {
+ Some(names) => verify_subject_alt_names(domain, names),
+ None => verify_subject_name(domain, &cert.subject_name()),
+ }
+ }
+
+ fn verify_subject_alt_names(domain: &str, names: Stack<GeneralName>) -> bool {
+ let ip = domain.parse();
+
+ for name in &names {
+ match ip {
+ Ok(ip) => {
+ if let Some(actual) = name.ipaddress() {
+ if matches_ip(&ip, actual) {
+ return true;
+ }
+ }
+ }
+ Err(_) => {
+ if let Some(pattern) = name.dnsname() {
+ if matches_dns(pattern, domain, false) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ false
+ }
+
+ fn verify_subject_name(domain: &str, subject_name: &X509NameRef) -> bool {
+ if let Some(pattern) = subject_name.entries_by_nid(nid::COMMONNAME).next() {
+ let pattern = match str::from_utf8(pattern.data().as_slice()) {
+ Ok(pattern) => pattern,
+ Err(_) => return false,
+ };
+
+ // Unlike with SANs, IP addresses in the subject name don't have a
+ // different encoding. We need to pass this down to matches_dns to
+ // disallow wildcard matches with bogus patterns like *.0.0.1
+ let is_ip = domain.parse::<IpAddr>().is_ok();
+
+ if matches_dns(&pattern, domain, is_ip) {
+ return true;
+ }
+ }
+
+ false
+ }
+
+ fn matches_dns(mut pattern: &str, mut hostname: &str, is_ip: bool) -> bool {
+ // first strip trailing . off of pattern and hostname to normalize
+ if pattern.ends_with('.') {
+ pattern = &pattern[..pattern.len() - 1];
+ }
+ if hostname.ends_with('.') {
+ hostname = &hostname[..hostname.len() - 1];
+ }
+
+ matches_wildcard(pattern, hostname, is_ip).unwrap_or_else(|| pattern == hostname)
+ }
+
+ fn matches_wildcard(pattern: &str, hostname: &str, is_ip: bool) -> Option<bool> {
+ // IP addresses and internationalized domains can't involved in wildcards
+ if is_ip || pattern.starts_with("xn--") {
+ return None;
+ }
+
+ let wildcard_location = match pattern.find('*') {
+ Some(l) => l,
+ None => return None,
+ };
+
+ let mut dot_idxs = pattern.match_indices('.').map(|(l, _)| l);
+ let wildcard_end = match dot_idxs.next() {
+ Some(l) => l,
+ None => return None,
+ };
+
+ // Never match wildcards if the pattern has less than 2 '.'s (no *.com)
+ //
+ // This is a bit dubious, as it doesn't disallow other TLDs like *.co.uk.
+ // Chrome has a black- and white-list for this, but Firefox (via NSS) does
+ // the same thing we do here.
+ //
+ // The Public Suffix (https://www.publicsuffix.org/) list could
+ // potentically be used here, but it's both huge and updated frequently
+ // enough that management would be a PITA.
+ if dot_idxs.next().is_none() {
+ return None;
+ }
+
+ // Wildcards can only be in the first component
+ if wildcard_location > wildcard_end {
+ return None;
+ }
+
+ let hostname_label_end = match hostname.find('.') {
+ Some(l) => l,
+ None => return None,
+ };
+
+ // check that the non-wildcard parts are identical
+ if pattern[wildcard_end..] != hostname[hostname_label_end..] {
+ return Some(false);
+ }
+
+ let wildcard_prefix = &pattern[..wildcard_location];
+ let wildcard_suffix = &pattern[wildcard_location + 1..wildcard_end];
+
+ let hostname_label = &hostname[..hostname_label_end];
+
+ // check the prefix of the first label
+ if !hostname_label.starts_with(wildcard_prefix) {
+ return Some(false);
+ }
+
+ // and the suffix
+ if !hostname_label[wildcard_prefix.len()..].ends_with(wildcard_suffix) {
+ return Some(false);
+ }
+
+ Some(true)
+ }
+
+ fn matches_ip(expected: &IpAddr, actual: &[u8]) -> bool {
+ match (expected, actual.len()) {
+ (&IpAddr::V4(ref addr), 4) => actual == addr.octets(),
+ (&IpAddr::V6(ref addr), 16) => {
+ let segments = [((actual[0] as u16) << 8) | actual[1] as u16,
+ ((actual[2] as u16) << 8) | actual[3] as u16,
+ ((actual[4] as u16) << 8) | actual[5] as u16,
+ ((actual[6] as u16) << 8) | actual[7] as u16,
+ ((actual[8] as u16) << 8) | actual[9] as u16,
+ ((actual[10] as u16) << 8) | actual[11] as u16,
+ ((actual[12] as u16) << 8) | actual[13] as u16,
+ ((actual[14] as u16) << 8) | actual[15] as u16];
+ segments == addr.segments()
+ }
+ _ => false,
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ #[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
+ #[test]
+ fn check_dhparam() {
+ use dh::Dh;
+
+ let expected = String::from_utf8(Dh::get_2048_256().unwrap().to_pem().unwrap()).unwrap();
+ assert_eq!(expected.trim(), super::DHPARAM_PEM.trim());
+ }
+}
diff --git a/openssl/src/ssl/error.rs b/openssl/src/ssl/error.rs
index 95213361..518ae90f 100644
--- a/openssl/src/ssl/error.rs
+++ b/openssl/src/ssl/error.rs
@@ -1,8 +1,11 @@
+use std::any::Any;
use std::error;
use std::error::Error as StdError;
use std::fmt;
use std::io;
+
use error::ErrorStack;
+use ssl::MidHandshakeSslStream;
/// An SSL error.
#[derive(Debug)]
@@ -62,3 +65,55 @@ impl From<ErrorStack> for Error {
Error::Ssl(e)
}
}
+
+/// An error or intermediate state after a TLS handshake attempt.
+#[derive(Debug)]
+pub enum HandshakeError<S> {
+ /// Setup failed.
+ SetupFailure(ErrorStack),
+ /// The handshake failed.
+ Failure(MidHandshakeSslStream<S>),
+ /// The handshake was interrupted midway through.
+ Interrupted(MidHandshakeSslStream<S>),
+}
+
+impl<S: Any + fmt::Debug> StdError for HandshakeError<S> {
+ fn description(&self) -> &str {
+ match *self {
+ HandshakeError::SetupFailure(_) => "stream setup failed",
+ HandshakeError::Failure(_) => "the handshake failed",
+ HandshakeError::Interrupted(_) => "the handshake was interrupted",
+ }
+ }
+
+ fn cause(&self) -> Option<&StdError> {
+ match *self {
+ HandshakeError::SetupFailure(ref e) => Some(e),
+ HandshakeError::Failure(ref s) |
+ HandshakeError::Interrupted(ref s) => Some(s.error()),
+ }
+ }
+}
+
+impl<S: Any + fmt::Debug> fmt::Display for HandshakeError<S> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ try!(f.write_str(StdError::description(self)));
+ match *self {
+ HandshakeError::SetupFailure(ref e) => try!(write!(f, ": {}", e)),
+ HandshakeError::Failure(ref s) |
+ HandshakeError::Interrupted(ref s) => {
+ try!(write!(f, ": {}", s.error()));
+ if let Some(err) = s.ssl().verify_result() {
+ try!(write!(f, ": {}", err));
+ }
+ }
+ }
+ Ok(())
+ }
+}
+
+impl<S> From<ErrorStack> for HandshakeError<S> {
+ fn from(e: ErrorStack) -> HandshakeError<S> {
+ HandshakeError::SetupFailure(e)
+ }
+}
diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs
index 6e365af6..9a477993 100644
--- a/openssl/src/ssl/mod.rs
+++ b/openssl/src/ssl/mod.rs
@@ -1,49 +1,123 @@
-use libc::{c_int, c_void, c_long};
+//! SSL/TLS support.
+//!
+//! `SslConnector` and `SslAcceptor` should be used in most cases - they handle
+//! configuration of the OpenSSL primitives for you.
+//!
+//! # Examples
+//!
+//! To connect as a client to a remote server:
+//!
+//! ```
+//! use openssl::ssl::{SslMethod, SslConnectorBuilder};
+//! use std::io::{Read, Write};
+//! use std::net::TcpStream;
+//!
+//! let connector = SslConnectorBuilder::new(SslMethod::tls()).unwrap().build();
+//!
+//! let stream = TcpStream::connect("google.com:443").unwrap();
+//! let mut stream = connector.connect("google.com", stream).unwrap();
+//!
+//! stream.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap();
+//! let mut res = vec![];
+//! stream.read_to_end(&mut res).unwrap();
+//! println!("{}", String::from_utf8_lossy(&res));
+//! ```
+//!
+//! To accept connections as a server from remote clients:
+//!
+//! ```no_run
+//! use openssl::pkcs12::Pkcs12;
+//! use openssl::ssl::{SslMethod, SslAcceptorBuilder, SslStream};
+//! use std::fs::File;
+//! use std::io::{Read, Write};
+//! use std::net::{TcpListener, TcpStream};
+//! use std::sync::Arc;
+//! use std::thread;
+//!
+//! // In this example we retrieve our keypair and certificate chain from a PKCS #12 archive,
+//! // but but they can also be retrieved from, for example, individual PEM- or DER-formatted
+//! // files. See the documentation for the `PKey` and `X509` types for more details.
+//! let mut file = File::open("identity.pfx").unwrap();
+//! let mut pkcs12 = vec![];
+//! file.read_to_end(&mut pkcs12).unwrap();
+//! let pkcs12 = Pkcs12::from_der(&pkcs12).unwrap();
+//! let identity = pkcs12.parse("password123").unwrap();
+//!
+//! let acceptor = SslAcceptorBuilder::mozilla_intermediate(SslMethod::tls(),
+//! &identity.pkey,
+//! &identity.cert,
+//! &identity.chain)
+//! .unwrap()
+//! .build();
+//! let acceptor = Arc::new(acceptor);
+//!
+//! let listener = TcpListener::bind("0.0.0.0:8443").unwrap();
+//!
+//! fn handle_client(stream: SslStream<TcpStream>) {
+//! // ...
+//! }
+//!
+//! for stream in listener.incoming() {
+//! match stream {
+//! Ok(stream) => {
+//! let acceptor = acceptor.clone();
+//! thread::spawn(move || {
+//! let stream = acceptor.accept(stream).unwrap();
+//! handle_client(stream);
+//! });
+//! }
+//! Err(e) => { /* connection failed */ }
+//! }
+//! }
+//! ```
+use ffi;
+use libc::{c_int, c_void, c_long, c_ulong};
+use libc::{c_uchar, c_uint};
use std::any::Any;
use std::any::TypeId;
use std::cmp;
use std::collections::HashMap;
-use std::error as stderror;
use std::ffi::{CStr, CString};
use std::fmt;
use std::io;
use std::io::prelude::*;
+use std::marker::PhantomData;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::path::Path;
use std::ptr;
-use std::str;
-use std::sync::{Mutex, Arc};
-#[cfg(any(feature = "npn", feature = "alpn"))]
-use libc::{c_uchar, c_uint};
-#[cfg(any(feature = "npn", feature = "alpn"))]
use std::slice;
-use std::marker::PhantomData;
-use ffi;
-
-use init;
-use dh::DH;
-use x509::{X509StoreContext, X509FileType, X509, X509Ref};
-use crypto::pkey::PKey;
+use std::str;
+use std::sync::Mutex;
+
+use {init, cvt, cvt_p};
+use dh::DhRef;
+use ec_key::EcKeyRef;
+use x509::{X509StoreContextRef, X509FileType, X509, X509Ref, X509VerifyError};
+#[cfg(any(ossl102, ossl110))]
+use verify::X509VerifyParamRef;
+use pkey::PKeyRef;
use error::ErrorStack;
+use types::{OpenSslType, OpenSslTypeRef};
+use util::Opaque;
-pub mod error;
+mod error;
+mod connector;
mod bio;
#[cfg(test)]
mod tests;
use self::bio::BioMethod;
-#[doc(inline)]
-pub use ssl::error::Error;
+pub use ssl::connector::{SslConnectorBuilder, SslConnector, SslAcceptorBuilder, SslAcceptor};
+pub use ssl::error::{Error, HandshakeError};
bitflags! {
- pub flags SslContextOptions: c_long {
+ pub flags SslOption: c_ulong {
const SSL_OP_MICROSOFT_SESS_ID_BUG = ffi::SSL_OP_MICROSOFT_SESS_ID_BUG,
const SSL_OP_NETSCAPE_CHALLENGE_BUG = ffi::SSL_OP_NETSCAPE_CHALLENGE_BUG,
const SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG =
ffi::SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG,
- const SSL_OP_TLSEXT_PADDING = ffi::SSL_OP_TLSEXT_PADDING,
const SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER = ffi::SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER,
const SSL_OP_SSLEAY_080_CLIENT_DH_BUG = ffi::SSL_OP_SSLEAY_080_CLIENT_DH_BUG,
const SSL_OP_TLS_D5_BUG = ffi::SSL_OP_TLS_D5_BUG,
@@ -66,80 +140,59 @@ bitflags! {
const SSL_OP_NO_SSLV2 = ffi::SSL_OP_NO_SSLv2,
const SSL_OP_NO_SSLV3 = ffi::SSL_OP_NO_SSLv3,
const SSL_OP_NO_TLSV1 = ffi::SSL_OP_NO_TLSv1,
+ const SSL_OP_NO_TLSV1_2 = ffi::SSL_OP_NO_TLSv1_2,
+ const SSL_OP_NO_TLSV1_1 = ffi::SSL_OP_NO_TLSv1_1,
+ /// Requires the `v102` or `v110` features and OpenSSL 1.0.2 or OpenSSL 1.1.0.
+ #[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
+ const SSL_OP_NO_DTLSV1 = ffi::SSL_OP_NO_DTLSv1,
+ /// Requires the `v102` or `v110` features and OpenSSL 1.0.2 or OpenSSL 1.1.0.
+ #[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
+ const SSL_OP_NO_DTLSV1_2 = ffi::SSL_OP_NO_DTLSv1_2,
+ /// Requires the `v102` or `v110` features and OpenSSL 1.0.2 or OpenSSL 1.1.0.
+ #[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
+ const SSL_OP_NO_SSL_MASK = ffi::SSL_OP_NO_SSL_MASK,
}
}
-/// Determines the SSL method supported
-#[allow(non_camel_case_types)]
-#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
-pub enum SslMethod {
- #[cfg(feature = "sslv2")]
- /// Only support the SSLv2 protocol, requires the `sslv2` feature.
- Sslv2,
- /// Support the SSLv2, SSLv3, TLSv1, TLSv1.1, and TLSv1.2 protocols depending on what the
- /// linked OpenSSL library supports.
- Sslv23,
- #[cfg(feature = "sslv3")]
- /// Only support the SSLv3 protocol.
- Sslv3,
- /// Only support the TLSv1 protocol.
- Tlsv1,
- #[cfg(feature = "tlsv1_1")]
- /// Support TLSv1.1 protocol, requires the `tlsv1_1` feature.
- Tlsv1_1,
- #[cfg(feature = "tlsv1_2")]
- /// Support TLSv1.2 protocol, requires the `tlsv1_2` feature.
- Tlsv1_2,
- #[cfg(feature = "dtlsv1")]
- /// Support DTLSv1 protocol, requires the `dtlsv1` feature.
- Dtlsv1,
- #[cfg(feature = "dtlsv1_2")]
- /// Support DTLSv1.2 protocol, requires the `dtlsv1_2` feature.
- Dtlsv1_2,
+bitflags! {
+ pub flags SslMode: c_long {
+ const SSL_MODE_ENABLE_PARTIAL_WRITE = ffi::SSL_MODE_ENABLE_PARTIAL_WRITE,
+ const SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER = ffi::SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER,
+ const SSL_MODE_AUTO_RETRY = ffi::SSL_MODE_AUTO_RETRY,
+ const SSL_MODE_NO_AUTO_CHAIN = ffi::SSL_MODE_NO_AUTO_CHAIN,
+ const SSL_MODE_RELEASE_BUFFERS = ffi::SSL_MODE_RELEASE_BUFFERS,
+ const SSL_MODE_SEND_CLIENTHELLO_TIME = ffi::SSL_MODE_SEND_CLIENTHELLO_TIME,
+ const SSL_MODE_SEND_SERVERHELLO_TIME = ffi::SSL_MODE_SEND_SERVERHELLO_TIME,
+ const SSL_MODE_SEND_FALLBACK_SCSV = ffi::SSL_MODE_SEND_FALLBACK_SCSV,
+ }
}
+#[derive(Copy, Clone)]
+pub struct SslMethod(*const ffi::SSL_METHOD);
+
impl SslMethod {
- fn to_raw(&self) -> *const ffi::SSL_METHOD {
- unsafe {
- match *self {
- #[cfg(feature = "sslv2")]
- SslMethod::Sslv2 => ffi::SSLv2_method(),
- #[cfg(feature = "sslv3")]
- SslMethod::Sslv3 => ffi::SSLv3_method(),
- SslMethod::Tlsv1 => ffi::TLSv1_method(),
- SslMethod::Sslv23 => ffi::SSLv23_method(),
- #[cfg(feature = "tlsv1_1")]
- SslMethod::Tlsv1_1 => ffi::TLSv1_1_method(),
- #[cfg(feature = "tlsv1_2")]
- SslMethod::Tlsv1_2 => ffi::TLSv1_2_method(),
- #[cfg(feature = "dtlsv1")]
- SslMethod::Dtlsv1 => ffi::DTLSv1_method(),
- #[cfg(feature = "dtlsv1_2")]
- SslMethod::Dtlsv1_2 => ffi::DTLSv1_2_method(),
- }
- }
+ /// Support all versions of the TLS protocol.
+ ///
+ /// This corresponds to `TLS_method` on OpenSSL 1.1.0 and `SSLv23_method`
+ /// on OpenSSL 1.0.x.
+ pub fn tls() -> SslMethod {
+ SslMethod(compat::tls_method())
}
- fn from_raw(method: *const ffi::SSL_METHOD) -> Option<SslMethod> {
- unsafe {
- match method {
- #[cfg(feature = "sslv2")]
- x if x == ffi::SSLv2_method() => Some(SslMethod::Sslv2),
- #[cfg(feature = "sslv3")]
- x if x == ffi::SSLv3_method() => Some(SslMethod::Sslv3),
- x if x == ffi::TLSv1_method() => Some(SslMethod::Tlsv1),
- x if x == ffi::SSLv23_method() => Some(SslMethod::Sslv23),
- #[cfg(feature = "tlsv1_1")]
- x if x == ffi::TLSv1_1_method() => Some(SslMethod::Tlsv1_1),
- #[cfg(feature = "tlsv1_2")]
- x if x == ffi::TLSv1_2_method() => Some(SslMethod::Tlsv1_2),
- #[cfg(feature = "dtlsv1")]
- x if x == ffi::DTLSv1_method() => Some(SslMethod::Dtlsv1),
- #[cfg(feature = "dtlsv1_2")]
- x if x == ffi::DTLSv1_2_method() => Some(SslMethod::Dtlsv1_2),
- _ => None,
- }
- }
+ /// Support all versions of the DTLS protocol.
+ ///
+ /// This corresponds to `DTLS_method` on OpenSSL 1.1.0 and `DTLSv1_method`
+ /// on OpenSSL 1.0.x.
+ pub fn dtls() -> SslMethod {
+ SslMethod(compat::dtls_method())
+ }
+
+ pub unsafe fn from_ptr(ptr: *const ffi::SSL_METHOD) -> SslMethod {
+ SslMethod(ptr)
+ }
+
+ pub fn as_ptr(&self) -> *const ffi::SSL_METHOD {
+ self.0
}
}
@@ -172,85 +225,72 @@ fn get_ssl_verify_data_idx<T: Any + 'static>() -> c_int {
*SSL_INDEXES.lock().unwrap().entry(TypeId::of::<T>()).or_insert_with(|| get_new_ssl_idx::<T>())
}
-#[cfg(feature = "npn")]
lazy_static! {
static ref NPN_PROTOS_IDX: c_int = get_new_idx::<Vec<u8>>();
}
-#[cfg(feature = "alpn")]
+
+#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
lazy_static! {
static ref ALPN_PROTOS_IDX: c_int = get_new_idx::<Vec<u8>>();
}
+unsafe extern "C" fn free_data_box<T>(_parent: *mut c_void,
+ ptr: *mut c_void,
+ _ad: *mut ffi::CRYPTO_EX_DATA,
+ _idx: c_int,
+ _argl: c_long,
+ _argp: *mut c_void) {
+ if !ptr.is_null() {
+ Box::<T>::from_raw(ptr as *mut T);
+ }
+}
+
/// Determine a new index to use for SSL CTX ex data.
/// Registers a destruct for the data which will be called by openssl when the context is freed.
fn get_new_idx<T>() -> c_int {
- extern "C" fn free_data_box<T>(_parent: *mut c_void,
- ptr: *mut c_void,
- _ad: *mut ffi::CRYPTO_EX_DATA,
- _idx: c_int,
- _argl: c_long,
- _argp: *mut c_void) {
- if !ptr.is_null() {
- let _: Box<T> = unsafe { mem::transmute(ptr) };
- }
- }
-
unsafe {
- let f: ffi::CRYPTO_EX_free = free_data_box::<T>;
- let idx = ffi::SSL_CTX_get_ex_new_index(0, ptr::null(), None, None, Some(f));
+ let idx = compat::get_new_idx(free_data_box::<T>);
assert!(idx >= 0);
idx
}
}
fn get_new_ssl_idx<T>() -> c_int {
- extern "C" fn free_data_box<T>(_parent: *mut c_void,
- ptr: *mut c_void,
- _ad: *mut ffi::CRYPTO_EX_DATA,
- _idx: c_int,
- _argl: c_long,
- _argp: *mut c_void) {
- if !ptr.is_null() {
- let _: Box<T> = unsafe { mem::transmute(ptr) };
- }
- }
-
unsafe {
- let f: ffi::CRYPTO_EX_free = free_data_box::<T>;
- let idx = ffi::SSL_get_ex_new_index(0, ptr::null(), None, None, Some(f));
+ let idx = compat::get_new_ssl_idx(free_data_box::<T>);
assert!(idx >= 0);
idx
}
}
extern "C" fn raw_verify<F>(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int
- where F: Fn(bool, &X509StoreContext) -> bool + Any + 'static + Sync + Send
+ where F: Fn(bool, &X509StoreContextRef) -> bool + Any + 'static + Sync + Send
{
unsafe {
let idx = ffi::SSL_get_ex_data_X509_STORE_CTX_idx();
let ssl = ffi::X509_STORE_CTX_get_ex_data(x509_ctx, idx);
- let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl);
+ let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl as *const _);
let verify = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_verify_data_idx::<F>());
- let verify: &F = mem::transmute(verify);
+ let verify: &F = &*(verify as *mut F);
- let ctx = X509StoreContext::new(x509_ctx);
+ let ctx = X509StoreContextRef::from_ptr(x509_ctx);
- verify(preverify_ok != 0, &ctx) as c_int
+ verify(preverify_ok != 0, ctx) as c_int
}
}
extern "C" fn ssl_raw_verify<F>(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int
- where F: Fn(bool, &X509StoreContext) -> bool + Any + 'static + Sync + Send
+ where F: Fn(bool, &X509StoreContextRef) -> bool + Any + 'static + Sync + Send
{
unsafe {
let idx = ffi::SSL_get_ex_data_X509_STORE_CTX_idx();
let ssl = ffi::X509_STORE_CTX_get_ex_data(x509_ctx, idx);
- let verify = ffi::SSL_get_ex_data(ssl, get_ssl_verify_data_idx::<F>());
- let verify: &F = mem::transmute(verify);
+ let verify = ffi::SSL_get_ex_data(ssl as *const _, get_ssl_verify_data_idx::<F>());
+ let verify: &F = &*(verify as *mut F);
- let ctx = X509StoreContext::new(x509_ctx);
+ let ctx = X509StoreContextRef::from_ptr(x509_ctx);
- verify(preverify_ok != 0, &ctx) as c_int
+ verify(preverify_ok != 0, ctx) as c_int
}
}
@@ -260,10 +300,10 @@ extern "C" fn raw_sni<F>(ssl: *mut ffi::SSL, al: *mut c_int, _arg: *mut c_void)
unsafe {
let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl);
let callback = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_verify_data_idx::<F>());
- let callback: &F = mem::transmute(callback);
- let mut ssl = SslRef::from_ptr(ssl);
+ let callback: &F = &*(callback as *mut F);
+ let ssl = SslRef::from_ptr_mut(ssl);
- match callback(&mut ssl) {
+ match callback(ssl) {
Ok(()) => ffi::SSL_TLSEXT_ERR_OK,
Err(SniError::Fatal(e)) => {
*al = e;
@@ -278,7 +318,6 @@ extern "C" fn raw_sni<F>(ssl: *mut ffi::SSL, al: *mut c_int, _arg: *mut c_void)
}
}
-#[cfg(any(feature = "npn", feature = "alpn"))]
unsafe fn select_proto_using(ssl: *mut ffi::SSL,
out: *mut *mut c_uchar,
outlen: *mut c_uchar,
@@ -291,7 +330,7 @@ unsafe fn select_proto_using(ssl: *mut ffi::SSL,
// extra data.
let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl);
let protocols = ffi::SSL_CTX_get_ex_data(ssl_ctx, ex_data);
- let protocols: &Vec<u8> = mem::transmute(protocols);
+ let protocols: &Vec<u8> = &*(protocols as *mut Vec<u8>);
// Prepare the client list parameters to be passed to the OpenSSL function...
let client = protocols.as_ptr();
let client_len = protocols.len() as c_uint;
@@ -311,7 +350,6 @@ unsafe fn select_proto_using(ssl: *mut ffi::SSL,
/// supported by the server. It achieves this by delegating to the `SSL_select_next_proto`
/// function. The list of protocols supported by the client is found in the extra data of the
/// OpenSSL context.
-#[cfg(feature = "npn")]
extern "C" fn raw_next_proto_select_cb(ssl: *mut ffi::SSL,
out: *mut *mut c_uchar,
outlen: *mut c_uchar,
@@ -322,15 +360,15 @@ extern "C" fn raw_next_proto_select_cb(ssl: *mut ffi::SSL,
unsafe { select_proto_using(ssl, out, outlen, inbuf, inlen, *NPN_PROTOS_IDX) }
}
-#[cfg(feature = "alpn")]
+#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
extern "C" fn raw_alpn_select_cb(ssl: *mut ffi::SSL,
- out: *mut *mut c_uchar,
+ out: *mut *const c_uchar,
outlen: *mut c_uchar,
inbuf: *const c_uchar,
inlen: c_uint,
_arg: *mut c_void)
-> c_int {
- unsafe { select_proto_using(ssl, out, outlen, inbuf, inlen, *ALPN_PROTOS_IDX) }
+ unsafe { select_proto_using(ssl, out as *mut _, outlen, inbuf, inlen, *ALPN_PROTOS_IDX) }
}
/// The function is given as the callback to `SSL_CTX_set_next_protos_advertised_cb`.
@@ -340,7 +378,6 @@ extern "C" fn raw_alpn_select_cb(ssl: *mut ffi::SSL,
/// that it supports.
/// The list of supported protocols is found in the extra data of the OpenSSL
/// context.
-#[cfg(feature = "npn")]
extern "C" fn raw_next_protos_advertise_cb(ssl: *mut ffi::SSL,
out: *mut *const c_uchar,
outlen: *mut c_uint,
@@ -356,7 +393,7 @@ extern "C" fn raw_next_protos_advertise_cb(ssl: *mut ffi::SSL,
} else {
// If the pointer is valid, put the pointer to the actual byte array into the
// output parameter `out`, as well as its length into `outlen`.
- let protocols: &Vec<u8> = mem::transmute(protocols);
+ let protocols: &Vec<u8> = &*(protocols as *mut Vec<u8>);
*out = protocols.as_ptr();
*outlen = protocols.len() as c_uint;
}
@@ -367,7 +404,6 @@ extern "C" fn raw_next_protos_advertise_cb(ssl: *mut ffi::SSL,
/// Convert a set of byte slices into a series of byte strings encoded for SSL. Encoding is a byte
/// containing the length followed by the string.
-#[cfg(any(feature = "npn", feature = "alpn"))]
fn ssl_encode_byte_strings(strings: &[&[u8]]) -> Vec<u8> {
let mut enc = Vec::new();
for string in strings {
@@ -389,22 +425,30 @@ pub enum SniError {
NoAck,
}
-// FIXME: macro may be instead of inlining?
-#[inline]
-fn wrap_ssl_result(res: c_int) -> Result<(), ErrorStack> {
- if res == 0 {
- Err(ErrorStack::get())
- } else {
- Ok(())
+/// A builder for `SslContext`s.
+pub struct SslContextBuilder(*mut ffi::SSL_CTX);
+
+unsafe impl Sync for SslContextBuilder {}
+unsafe impl Send for SslContextBuilder {}
+
+impl Drop for SslContextBuilder {
+ fn drop(&mut self) {
+ unsafe { ffi::SSL_CTX_free(self.as_ptr()) }
}
}
-/// A borrowed SSL context object.
-pub struct SslContextRef<'a>(*mut ffi::SSL_CTX, PhantomData<&'a ()>);
+impl SslContextBuilder {
+ pub fn new(method: SslMethod) -> Result<SslContextBuilder, ErrorStack> {
+ unsafe {
+ init();
+ let ctx = try!(cvt_p(ffi::SSL_CTX_new(method.as_ptr())));
-impl<'a> SslContextRef<'a> {
- pub unsafe fn from_ptr(ctx: *mut ffi::SSL_CTX) -> SslContextRef<'a> {
- SslContextRef(ctx, PhantomData)
+ Ok(SslContextBuilder::from_ptr(ctx))
+ }
+ }
+
+ pub unsafe fn from_ptr(ctx: *mut ffi::SSL_CTX) -> SslContextBuilder {
+ SslContextBuilder(ctx)
}
pub fn as_ptr(&self) -> *mut ffi::SSL_CTX {
@@ -421,11 +465,13 @@ impl<'a> SslContextRef<'a> {
/// Configures the certificate verification method for new connections and
/// registers a verification callback.
pub fn set_verify_callback<F>(&mut self, mode: SslVerifyMode, verify: F)
- where F: Fn(bool, &X509StoreContext) -> bool + Any + 'static + Sync + Send
+ where F: Fn(bool, &X509StoreContextRef) -> bool + Any + 'static + Sync + Send
{
unsafe {
let verify = Box::new(verify);
- ffi::SSL_CTX_set_ex_data(self.as_ptr(), get_verify_data_idx::<F>(), mem::transmute(verify));
+ ffi::SSL_CTX_set_ex_data(self.as_ptr(),
+ get_verify_data_idx::<F>(),
+ mem::transmute(verify));
ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits as c_int, Some(raw_verify::<F>));
}
}
@@ -455,18 +501,25 @@ impl<'a> SslContextRef<'a> {
}
}
- pub fn set_read_ahead(&mut self, m: u32) {
+ pub fn set_read_ahead(&mut self, read_ahead: bool) {
unsafe {
- ffi::SSL_CTX_set_read_ahead(self.as_ptr(), m as c_long);
+ ffi::SSL_CTX_set_read_ahead(self.as_ptr(), read_ahead as c_long);
}
}
- fn set_mode(&mut self, mode: c_long) -> Result<(), ErrorStack> {
- wrap_ssl_result(unsafe { ffi::SSL_CTX_set_mode(self.as_ptr(), mode) as c_int })
+ pub fn set_mode(&mut self, mode: SslMode) -> SslMode {
+ unsafe {
+ let mode = ffi::SSL_CTX_set_mode(self.as_ptr(), mode.bits());
+ SslMode::from_bits(mode).unwrap()
+ }
+ }
+
+ pub fn set_tmp_dh(&mut self, dh: &DhRef) -> Result<(), ErrorStack> {
+ unsafe { cvt(ffi::SSL_CTX_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) }
}
- pub fn set_tmp_dh(&mut self, dh: &DH) -> Result<(), ErrorStack> {
- wrap_ssl_result(unsafe { ffi::SSL_CTX_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as i32 })
+ pub fn set_tmp_ecdh(&mut self, key: &EcKeyRef) -> Result<(), ErrorStack> {
+ unsafe { cvt(ffi::SSL_CTX_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) }
}
/// Use the default locations of trusted certificates for verification.
@@ -475,16 +528,18 @@ impl<'a> SslContextRef<'a> {
/// environment variables if present, or defaults specified at OpenSSL
/// build time otherwise.
pub fn set_default_verify_paths(&mut self) -> Result<(), ErrorStack> {
- wrap_ssl_result(unsafe { ffi::SSL_CTX_set_default_verify_paths(self.as_ptr()) })
+ unsafe { cvt(ffi::SSL_CTX_set_default_verify_paths(self.as_ptr())).map(|_| ()) }
}
- #[allow(non_snake_case)]
/// Specifies the file that contains trusted CA certificates.
- pub fn set_CA_file<P: AsRef<Path>>(&mut self, file: P) -> Result<(), ErrorStack> {
- let file = CString::new(file.as_ref().as_os_str().to_str().expect("invalid utf8")).unwrap();
- wrap_ssl_result(unsafe {
- ffi::SSL_CTX_load_verify_locations(self.as_ptr(), file.as_ptr() as *const _, ptr::null())
- })
+ pub fn set_ca_file<P: AsRef<Path>>(&mut self, file: P) -> Result<(), ErrorStack> {
+ let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
+ unsafe {
+ cvt(ffi::SSL_CTX_load_verify_locations(self.as_ptr(),
+ file.as_ptr() as *const _,
+ ptr::null()))
+ .map(|_| ())
+ }
}
/// Set the context identifier for sessions
@@ -496,9 +551,13 @@ impl<'a> SslContextRef<'a> {
/// This value should be set when using client certificates, or each request will fail
/// handshake and need to be restarted.
pub fn set_session_id_context(&mut self, sid_ctx: &[u8]) -> Result<(), ErrorStack> {
- wrap_ssl_result(unsafe {
- ffi::SSL_CTX_set_session_id_context(self.as_ptr(), sid_ctx.as_ptr(), sid_ctx.len() as u32)
- })
+ unsafe {
+ assert!(sid_ctx.len() <= c_uint::max_value() as usize);
+ cvt(ffi::SSL_CTX_set_session_id_context(self.as_ptr(),
+ sid_ctx.as_ptr(),
+ sid_ctx.len() as c_uint))
+ .map(|_| ())
+ }
}
/// Specifies the file that contains certificate
@@ -506,43 +565,39 @@ impl<'a> SslContextRef<'a> {
file: P,
file_type: X509FileType)
-> Result<(), ErrorStack> {
- let file = CString::new(file.as_ref().as_os_str().to_str().expect("invalid utf8")).unwrap();
- wrap_ssl_result(unsafe {
- ffi::SSL_CTX_use_certificate_file(self.as_ptr(),
- file.as_ptr() as *const _,
- file_type as c_int)
- })
+ let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
+ unsafe {
+ cvt(ffi::SSL_CTX_use_certificate_file(self.as_ptr(),
+ file.as_ptr() as *const _,
+ file_type.as_raw()))
+ .map(|_| ())
+ }
}
/// Specifies the file that contains certificate chain
pub fn set_certificate_chain_file<P: AsRef<Path>>(&mut self,
- file: P,
- file_type: X509FileType)
+ file: P)
-> Result<(), ErrorStack> {
- let file = CString::new(file.as_ref().as_os_str().to_str().expect("invalid utf8")).unwrap();
- wrap_ssl_result(unsafe {
- ffi::SSL_CTX_use_certificate_chain_file(self.as_ptr(),
- file.as_ptr() as *const _,
- file_type as c_int)
- })
+ let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
+ unsafe {
+ cvt(ffi::SSL_CTX_use_certificate_chain_file(self.as_ptr(), file.as_ptr() as *const _))
+ .map(|_| ())
+ }
}
/// Specifies the certificate
pub fn set_certificate(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> {
- wrap_ssl_result(unsafe { ffi::SSL_CTX_use_certificate(self.as_ptr(), cert.as_ptr()) })
+ unsafe { cvt(ffi::SSL_CTX_use_certificate(self.as_ptr(), cert.as_ptr())).map(|_| ()) }
}
/// Adds a certificate to the certificate chain presented together with the
/// certificate specified using set_certificate()
- pub fn add_extra_chain_cert(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> {
- // FIXME this should really just take an X509 by value
- let der = try!(cert.to_der());
- let cert = try!(X509::from_der(&der));
+ pub fn add_extra_chain_cert(&mut self, cert: X509) -> Result<(), ErrorStack> {
unsafe {
- try_ssl!(ffi::SSL_CTX_add_extra_chain_cert(self.as_ptr(), cert.as_ptr()));
+ try!(cvt(ffi::SSL_CTX_add_extra_chain_cert(self.as_ptr(), cert.as_ptr()) as c_int));
+ mem::forget(cert);
+ Ok(())
}
- mem::forget(cert);
- Ok(())
}
/// Specifies the file that contains private key
@@ -550,61 +605,61 @@ impl<'a> SslContextRef<'a> {
file: P,
file_type: X509FileType)
-> Result<(), ErrorStack> {
- let file = CString::new(file.as_ref().as_os_str().to_str().expect("invalid utf8")).unwrap();
- wrap_ssl_result(unsafe {
- ffi::SSL_CTX_use_PrivateKey_file(self.as_ptr(),
- file.as_ptr() as *const _,
- file_type as c_int)
- })
+ let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
+ unsafe {
+ cvt(ffi::SSL_CTX_use_PrivateKey_file(self.as_ptr(),
+ file.as_ptr() as *const _,
+ file_type.as_raw()))
+ .map(|_| ())
+ }
}
/// Specifies the private key
- pub fn set_private_key(&mut self, key: &PKey) -> Result<(), ErrorStack> {
- wrap_ssl_result(unsafe { ffi::SSL_CTX_use_PrivateKey(self.as_ptr(), key.as_ptr()) })
- }
-
- /// Check consistency of private key and certificate
- pub fn check_private_key(&mut self) -> Result<(), ErrorStack> {
- wrap_ssl_result(unsafe { ffi::SSL_CTX_check_private_key(self.as_ptr()) })
+ pub fn set_private_key(&mut self, key: &PKeyRef) -> Result<(), ErrorStack> {
+ unsafe { cvt(ffi::SSL_CTX_use_PrivateKey(self.as_ptr(), key.as_ptr())).map(|_| ()) }
}
pub fn set_cipher_list(&mut self, cipher_list: &str) -> Result<(), ErrorStack> {
- wrap_ssl_result(unsafe {
- let cipher_list = CString::new(cipher_list).unwrap();
- ffi::SSL_CTX_set_cipher_list(self.as_ptr(), cipher_list.as_ptr() as *const _)
- })
+ let cipher_list = CString::new(cipher_list).unwrap();
+ unsafe {
+ cvt(ffi::SSL_CTX_set_cipher_list(self.as_ptr(), cipher_list.as_ptr() as *const _))
+ .map(|_| ())
+ }
}
- /// If `onoff` is set to `true`, enable ECDHE for key exchange with compatible
- /// clients, and automatically select an appropriate elliptic curve.
+ /// If `onoff` is set to `true`, enable ECDHE for key exchange with
+ /// compatible clients, and automatically select an appropriate elliptic
+ /// curve.
///
- /// This method requires OpenSSL >= 1.0.2 or LibreSSL and the `ecdh_auto` feature.
- #[cfg(feature = "ecdh_auto")]
+ /// Requires the `v102` feature and OpenSSL 1.0.2.
+ #[cfg(all(feature = "v102", ossl102))]
pub fn set_ecdh_auto(&mut self, onoff: bool) -> Result<(), ErrorStack> {
- wrap_ssl_result(unsafe { ffi::SSL_CTX_set_ecdh_auto(self.as_ptr(), onoff as c_long) as c_int })
+ self._set_ecdh_auto(onoff)
+ }
+
+ #[cfg(ossl102)]
+ fn _set_ecdh_auto(&mut self, onoff: bool) -> Result<(), ErrorStack> {
+ unsafe { cvt(ffi::SSL_CTX_set_ecdh_auto(self.as_ptr(), onoff as c_int)).map(|_| ()) }
}
- pub fn set_options(&mut self, option: SslContextOptions) -> SslContextOptions {
- let ret = unsafe { ffi::SSL_CTX_set_options(self.as_ptr(), option.bits()) };
- SslContextOptions::from_bits(ret).unwrap()
+ pub fn set_options(&mut self, option: SslOption) -> SslOption {
+ let ret = unsafe { compat::SSL_CTX_set_options(self.as_ptr(), option.bits()) };
+ SslOption::from_bits(ret).unwrap()
}
- pub fn options(&self) -> SslContextOptions {
- let ret = unsafe { ffi::SSL_CTX_get_options(self.as_ptr()) };
- SslContextOptions::from_bits(ret).unwrap()
+ pub fn options(&self) -> SslOption {
+ let ret = unsafe { compat::SSL_CTX_get_options(self.as_ptr()) };
+ SslOption::from_bits(ret).unwrap()
}
- pub fn clear_options(&mut self, option: SslContextOptions) -> SslContextOptions {
- let ret = unsafe { ffi::SSL_CTX_clear_options(self.as_ptr(), option.bits()) };
- SslContextOptions::from_bits(ret).unwrap()
+ pub fn clear_options(&mut self, option: SslOption) -> SslOption {
+ let ret = unsafe { compat::SSL_CTX_clear_options(self.as_ptr(), option.bits()) };
+ SslOption::from_bits(ret).unwrap()
}
/// Set the protocols to be used during Next Protocol Negotiation (the protocols
/// supported by the application).
- ///
- /// This method needs the `npn` feature.
- #[cfg(feature = "npn")]
- pub fn set_npn_protocols(&mut self, protocols: &[&[u8]]) {
+ pub fn set_npn_protocols(&mut self, protocols: &[&[u8]]) -> Result<(), ErrorStack> {
// Firstly, convert the list of protocols to a byte-array that can be passed to OpenSSL
// APIs -- a list of length-prefixed strings.
let protocols: Box<Vec<u8>> = Box::new(ssl_encode_byte_strings(protocols));
@@ -612,7 +667,9 @@ impl<'a> SslContextRef<'a> {
unsafe {
// Attach the protocol list to the OpenSSL context structure,
// so that we can refer to it within the callback.
- ffi::SSL_CTX_set_ex_data(self.as_ptr(), *NPN_PROTOS_IDX, mem::transmute(protocols));
+ try!(cvt(ffi::SSL_CTX_set_ex_data(self.as_ptr(),
+ *NPN_PROTOS_IDX,
+ Box::into_raw(protocols) as *mut c_void)));
// Now register the callback that performs the default protocol
// matching based on the client-supported list of protocols that
// has been saved.
@@ -624,6 +681,7 @@ impl<'a> SslContextRef<'a> {
ffi::SSL_CTX_set_next_protos_advertised_cb(self.as_ptr(),
raw_next_protos_advertise_cb,
ptr::null_mut());
+ Ok(())
}
}
@@ -634,40 +692,58 @@ impl<'a> SslContextRef<'a> {
///
/// Note that ordering of the protocols controls the priority with which they are chosen.
///
- /// This method needs the `alpn` feature.
- #[cfg(feature = "alpn")]
- pub fn set_alpn_protocols(&mut self, protocols: &[&[u8]]) {
+ /// Requires the `v102` or `v110` features and OpenSSL 1.0.2 or OpenSSL 1.1.0.
+ #[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
+ pub fn set_alpn_protocols(&mut self, protocols: &[&[u8]]) -> Result<(), ErrorStack> {
let protocols: Box<Vec<u8>> = Box::new(ssl_encode_byte_strings(protocols));
unsafe {
// Set the context's internal protocol list for use if we are a server
- ffi::SSL_CTX_set_alpn_protos(self.as_ptr(), protocols.as_ptr(), protocols.len() as c_uint);
+ let r = ffi::SSL_CTX_set_alpn_protos(self.as_ptr(),
+ protocols.as_ptr(),
+ protocols.len() as c_uint);
+ // fun fact, SSL_CTX_set_alpn_protos has a reversed return code D:
+ if r != 0 {
+ return Err(ErrorStack::get());
+ }
// Rather than use the argument to the callback to contain our data, store it in the
// ssl ctx's ex_data so that we can configure a function to free it later. In the
// future, it might make sense to pull this into our internal struct Ssl instead of
// leaning on openssl and using function pointers.
- ffi::SSL_CTX_set_ex_data(self.as_ptr(), *ALPN_PROTOS_IDX, mem::transmute(protocols));
+ try!(cvt(ffi::SSL_CTX_set_ex_data(self.as_ptr(),
+ *ALPN_PROTOS_IDX,
+ Box::into_raw(protocols) as *mut c_void)));
// Now register the callback that performs the default protocol
// matching based on the client-supported list of protocols that
// has been saved.
ffi::SSL_CTX_set_alpn_select_cb(self.as_ptr(), raw_alpn_select_cb, ptr::null_mut());
+
+ Ok(())
}
}
+
+ /// Checks consistency between the private key and certificate.
+ pub fn check_private_key(&self) -> Result<(), ErrorStack> {
+ unsafe { cvt(ffi::SSL_CTX_check_private_key(self.as_ptr())).map(|_| ()) }
+ }
+
+ pub fn build(self) -> SslContext {
+ let ctx = SslContext(self.0);
+ mem::forget(self);
+ ctx
+ }
}
-/// An owned SSL context object.
-pub struct SslContext(SslContextRef<'static>);
+type_!(SslContext, SslContextRef, ffi::SSL_CTX, ffi::SSL_CTX_free);
unsafe impl Send for SslContext {}
unsafe impl Sync for SslContext {}
-#[cfg(feature = "ssl_context_clone")]
impl Clone for SslContext {
- /// Requires the `ssl_context_clone` feature.
fn clone(&self) -> Self {
unsafe {
- ::c_helpers::rust_0_8_SSL_CTX_clone(self.as_ptr());
+ compat::SSL_CTX_up_ref(self.as_ptr());
SslContext::from_ptr(self.as_ptr())
}
}
@@ -680,78 +756,57 @@ impl fmt::Debug for SslContext {
}
}
-impl Drop for SslContext {
- fn drop(&mut self) {
- unsafe { ffi::SSL_CTX_free(self.as_ptr()) }
+impl SslContext {
+ pub fn builder(method: SslMethod) -> Result<SslContextBuilder, ErrorStack> {
+ SslContextBuilder::new(method)
}
}
-impl Deref for SslContext {
- type Target = SslContextRef<'static>;
- fn deref(&self) -> &SslContextRef<'static> {
- &self.0
- }
-}
+pub struct CipherBits {
+ /// The number of secret bits used for the cipher.
+ pub secret: i32,
-impl DerefMut for SslContext {
- fn deref_mut(&mut self) -> &mut SslContextRef<'static> {
- &mut self.0
- }
+ /// The number of bits processed by the chosen algorithm.
+ pub algorithm: i32,
}
-impl SslContext {
- /// Creates a new SSL context.
- pub fn new(method: SslMethod) -> Result<SslContext, ErrorStack> {
- init();
+pub struct SslCipher(*mut ffi::SSL_CIPHER);
- let mut ctx = unsafe {
- let ctx = try_ssl_null!(ffi::SSL_CTX_new(method.to_raw()));
- SslContext::from_ptr(ctx)
- };
+impl OpenSslType for SslCipher {
+ type CType = ffi::SSL_CIPHER;
+ type Ref = SslCipherRef;
- match method {
- #[cfg(feature = "dtlsv1")]
- SslMethod::Dtlsv1 => ctx.set_read_ahead(1),
- #[cfg(feature = "dtlsv1_2")]
- SslMethod::Dtlsv1_2 => ctx.set_read_ahead(1),
- _ => {}
- }
- // this is a bit dubious (?)
- try!(ctx.set_mode(ffi::SSL_MODE_AUTO_RETRY | ffi::SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER));
-
- Ok(ctx)
+ unsafe fn from_ptr(ptr: *mut ffi::SSL_CIPHER) -> SslCipher {
+ SslCipher(ptr)
}
+}
- pub unsafe fn from_ptr(ctx: *mut ffi::SSL_CTX) -> SslContext {
- SslContext(SslContextRef::from_ptr(ctx))
- }
+impl Deref for SslCipher {
+ type Target = SslCipherRef;
- pub fn as_ptr(&self) -> *mut ffi::SSL_CTX {
- (**self).as_ptr()
+ fn deref(&self) -> &SslCipherRef {
+ unsafe { SslCipherRef::from_ptr(self.0) }
}
}
-
-pub struct CipherBits {
- /// The number of secret bits used for the cipher.
- pub secret: i32,
- /// The number of bits processed by the chosen algorithm, if not None.
- pub algorithm: Option<i32>,
- _p: (),
+impl DerefMut for SslCipher {
+ fn deref_mut(&mut self) -> &mut SslCipherRef {
+ unsafe { SslCipherRef::from_ptr_mut(self.0) }
+ }
}
+pub struct SslCipherRef(Opaque);
-pub struct SslCipher<'a> {
- cipher: *const ffi::SSL_CIPHER,
- ph: PhantomData<&'a ()>,
+impl OpenSslTypeRef for SslCipherRef {
+ type CType = ffi::SSL_CIPHER;
}
-impl<'a> SslCipher<'a> {
+impl SslCipherRef {
/// Returns the name of cipher.
- pub fn name(&self) -> &'static str {
+ pub fn name(&self) -> &str {
let name = unsafe {
- let ptr = ffi::SSL_CIPHER_get_name(self.cipher);
+ let ptr = ffi::SSL_CIPHER_get_name(self.as_ptr());
CStr::from_ptr(ptr as *const _)
};
@@ -759,9 +814,9 @@ impl<'a> SslCipher<'a> {
}
/// Returns the SSL/TLS protocol version that first defined the cipher.
- pub fn version(&self) -> &'static str {
+ pub fn version(&self) -> &str {
let version = unsafe {
- let ptr = ffi::SSL_CIPHER_get_version(self.cipher);
+ let ptr = ffi::SSL_CIPHER_get_version(self.as_ptr());
CStr::from_ptr(ptr as *const _)
};
@@ -771,78 +826,44 @@ impl<'a> SslCipher<'a> {
/// Returns the number of bits used for the cipher.
pub fn bits(&self) -> CipherBits {
unsafe {
- let algo_bits: *mut c_int = ptr::null_mut();
- let secret_bits = ffi::SSL_CIPHER_get_bits(self.cipher, algo_bits);
- if !algo_bits.is_null() {
- CipherBits {
- secret: secret_bits,
- algorithm: Some(*algo_bits),
- _p: (),
- }
- } else {
- CipherBits {
- secret: secret_bits,
- algorithm: None,
- _p: (),
- }
+ let mut algo_bits = 0;
+ let secret_bits = ffi::SSL_CIPHER_get_bits(self.as_ptr(), &mut algo_bits);
+ CipherBits {
+ secret: secret_bits.into(),
+ algorithm: algo_bits.into(),
}
}
}
/// Returns a textual description of the cipher used
- pub fn description(&self) -> Option<String> {
+ pub fn description(&self) -> String {
unsafe {
// SSL_CIPHER_description requires a buffer of at least 128 bytes.
let mut buf = [0; 128];
- let desc_ptr = ffi::SSL_CIPHER_description(self.cipher, buf.as_mut_ptr(), 128);
-
- if !desc_ptr.is_null() {
- String::from_utf8(CStr::from_ptr(desc_ptr as *const _).to_bytes().to_vec()).ok()
- } else {
- None
- }
+ let ptr = ffi::SSL_CIPHER_description(self.as_ptr(), buf.as_mut_ptr(), 128);
+ String::from_utf8(CStr::from_ptr(ptr as *const _).to_bytes().to_vec()).unwrap()
}
}
}
-pub struct SslRef<'a>(*mut ffi::SSL, PhantomData<&'a ()>);
+type_!(Ssl, SslRef, ffi::SSL, ffi::SSL_free);
-unsafe impl<'a> Send for SslRef<'a> {}
-unsafe impl<'a> Sync for SslRef<'a> {}
-
-impl<'a> fmt::Debug for SslRef<'a> {
+impl fmt::Debug for SslRef {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
- fmt.debug_struct("SslRef")
- .field("state", &self.state_string_long())
- .finish()
+ let mut builder = fmt.debug_struct("Ssl");
+ builder.field("state", &self.state_string_long());
+ if let Some(err) = self.verify_result() {
+ builder.field("verify_result", &err);
+ }
+ builder.finish()
}
}
-impl<'a> SslRef<'a> {
- pub unsafe fn from_ptr(ssl: *mut ffi::SSL) -> SslRef<'a> {
- SslRef(ssl, PhantomData)
- }
-
- pub fn as_ptr(&self) -> *mut ffi::SSL {
- self.0
- }
-
+impl SslRef {
fn get_raw_rbio(&self) -> *mut ffi::BIO {
unsafe { ffi::SSL_get_rbio(self.as_ptr()) }
}
- fn connect(&mut self) -> c_int {
- unsafe { ffi::SSL_connect(self.as_ptr()) }
- }
-
- fn accept(&mut self) -> c_int {
- unsafe { ffi::SSL_accept(self.as_ptr()) }
- }
-
- fn handshake(&mut self) -> c_int {
- unsafe { ffi::SSL_do_handshake(self.as_ptr()) }
- }
-
fn read(&mut self, buf: &mut [u8]) -> c_int {
let len = cmp::min(c_int::max_value() as usize, buf.len()) as c_int;
unsafe { ffi::SSL_read(self.as_ptr(), buf.as_ptr() as *mut c_void, len) }
@@ -872,7 +893,7 @@ impl<'a> SslRef<'a> {
/// to the certificate chain. It should return `true` if the certificate
/// chain is valid and `false` otherwise.
pub fn set_verify_callback<F>(&mut self, mode: SslVerifyMode, verify: F)
- where F: Fn(bool, &X509StoreContext) -> bool + Any + 'static + Sync + Send
+ where F: Fn(bool, &X509StoreContextRef) -> bool + Any + 'static + Sync + Send
{
unsafe {
let verify = Box::new(verify);
@@ -883,17 +904,14 @@ impl<'a> SslRef<'a> {
}
}
- pub fn current_cipher(&self) -> Option<SslCipher<'a>> {
+ pub fn current_cipher(&self) -> Option<&SslCipherRef> {
unsafe {
let ptr = ffi::SSL_get_current_cipher(self.as_ptr());
if ptr.is_null() {
None
} else {
- Some(SslCipher {
- cipher: ptr,
- ph: PhantomData,
- })
+ Some(SslCipherRef::from_ptr(ptr as *mut _))
}
}
}
@@ -919,15 +937,9 @@ impl<'a> SslRef<'a> {
/// Sets the host name to be used with SNI (Server Name Indication).
pub fn set_hostname(&mut self, hostname: &str) -> Result<(), ErrorStack> {
let cstr = CString::new(hostname).unwrap();
- let ret = unsafe {
- ffi::SSL_set_tlsext_host_name(self.as_ptr(), cstr.as_ptr() as *mut _)
- };
-
- // For this case, 0 indicates failure.
- if ret == 0 {
- Err(ErrorStack::get())
- } else {
- Ok(())
+ unsafe {
+ cvt(ffi::SSL_set_tlsext_host_name(self.as_ptr(), cstr.as_ptr() as *mut _) as c_int)
+ .map(|_| ())
}
}
@@ -957,9 +969,6 @@ impl<'a> SslRef<'a> {
///
/// The protocol's name is returned is an opaque sequence of bytes. It is up to the client
/// to interpret it.
- ///
- /// This method needs the `npn` feature.
- #[cfg(feature = "npn")]
pub fn selected_npn_protocol(&self) -> Option<&[u8]> {
unsafe {
let mut data: *const c_uchar = ptr::null();
@@ -981,8 +990,8 @@ impl<'a> SslRef<'a> {
/// The protocol's name is returned is an opaque sequence of bytes. It is up to the client
/// to interpret it.
///
- /// This method needs the `alpn` feature.
- #[cfg(feature = "alpn")]
+ /// Requires the `v102` or `v110` features and OpenSSL 1.0.2 or OpenSSL 1.1.0.
+ #[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
pub fn selected_alpn_protocol(&self) -> Option<&[u8]> {
unsafe {
let mut data: *const c_uchar = ptr::null();
@@ -1009,139 +1018,99 @@ impl<'a> SslRef<'a> {
///
/// The result will be either None, indicating no compression is in use, or
/// a string with the compression name.
- pub fn compression(&self) -> Option<String> {
- let ptr = unsafe { ffi::SSL_get_current_compression(self.as_ptr()) };
- if ptr == ptr::null() {
- return None;
- }
-
- let meth = unsafe { ffi::SSL_COMP_get_name(ptr) };
- let s = unsafe {
- String::from_utf8(CStr::from_ptr(meth as *const _).to_bytes().to_vec()).unwrap()
- };
-
- Some(s)
+ pub fn compression(&self) -> Option<&str> {
+ self._compression()
}
- pub fn ssl_method(&self) -> SslMethod {
+ #[cfg(not(osslconf = "OPENSSL_NO_COMP"))]
+ fn _compression(&self) -> Option<&str> {
unsafe {
- let method = ffi::SSL_get_ssl_method(self.as_ptr());
- SslMethod::from_raw(method).unwrap()
+ let ptr = ffi::SSL_get_current_compression(self.as_ptr());
+ if ptr == ptr::null() {
+ return None;
+ }
+ let meth = ffi::SSL_COMP_get_name(ptr);
+ Some(str::from_utf8(CStr::from_ptr(meth as *const _).to_bytes()).unwrap())
}
}
+ #[cfg(osslconf = "OPENSSL_NO_COMP")]
+ fn _compression(&self) -> Option<&str> {
+ None
+ }
+
/// Returns the server's name for the current connection
- pub fn servername(&self) -> Option<String> {
- let name = unsafe { ffi::SSL_get_servername(self.as_ptr(), ffi::TLSEXT_NAMETYPE_host_name) };
- if name == ptr::null() {
- return None;
- }
+ pub fn servername(&self) -> Option<&str> {
+ unsafe {
+ let name = ffi::SSL_get_servername(self.as_ptr(), ffi::TLSEXT_NAMETYPE_host_name);
+ if name == ptr::null() {
+ return None;
+ }
- unsafe { String::from_utf8(CStr::from_ptr(name as *const _).to_bytes().to_vec()).ok() }
+ Some(str::from_utf8(CStr::from_ptr(name as *const _).to_bytes()).unwrap())
+ }
}
/// Changes the context corresponding to the current connection.
pub fn set_ssl_context(&mut self, ctx: &SslContextRef) -> Result<(), ErrorStack> {
- unsafe {
- try_ssl_null!(ffi::SSL_set_SSL_CTX(self.as_ptr(), ctx.as_ptr()));
- }
- Ok(())
+ unsafe { cvt_p(ffi::SSL_set_SSL_CTX(self.as_ptr(), ctx.as_ptr())).map(|_| ()) }
}
/// Returns the context corresponding to the current connection
- pub fn ssl_context(&self) -> SslContextRef<'a> {
+ pub fn ssl_context(&self) -> &SslContextRef {
unsafe {
let ssl_ctx = ffi::SSL_get_SSL_CTX(self.as_ptr());
SslContextRef::from_ptr(ssl_ctx)
}
}
-}
-pub struct Ssl(SslRef<'static>);
-
-impl fmt::Debug for Ssl {
- fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
- fmt.debug_struct("Ssl")
- .field("state", &self.state_string_long())
- .finish()
+ /// Returns the X509 verification configuration.
+ ///
+ /// Requires the `v102` or `v110` features and OpenSSL 1.0.2 or 1.1.0.
+ #[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
+ pub fn param_mut(&mut self) -> &mut X509VerifyParamRef {
+ self._param_mut()
}
-}
-impl Drop for Ssl {
- fn drop(&mut self) {
- unsafe { ffi::SSL_free(self.as_ptr()) }
+ #[cfg(any(ossl102, ossl110))]
+ fn _param_mut(&mut self) -> &mut X509VerifyParamRef {
+ unsafe { X509VerifyParamRef::from_ptr_mut(ffi::SSL_get0_param(self.as_ptr())) }
}
-}
-
-impl Deref for Ssl {
- type Target = SslRef<'static>;
- fn deref(&self) -> &SslRef<'static> {
- &self.0
+ /// Returns the result of X509 certificate verification.
+ pub fn verify_result(&self) -> Option<X509VerifyError> {
+ unsafe { X509VerifyError::from_raw(ffi::SSL_get_verify_result(self.as_ptr())) }
}
}
-impl DerefMut for Ssl {
- fn deref_mut(&mut self) -> &mut SslRef<'static> {
- &mut self.0
+unsafe impl Sync for Ssl {}
+unsafe impl Send for Ssl {}
+
+impl fmt::Debug for Ssl {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Debug::fmt(&**self, fmt)
}
}
impl Ssl {
pub fn new(ctx: &SslContext) -> Result<Ssl, ErrorStack> {
unsafe {
- let ssl = try_ssl_null!(ffi::SSL_new(ctx.as_ptr()));
+ let ssl = try!(cvt_p(ffi::SSL_new(ctx.as_ptr())));
Ok(Ssl::from_ptr(ssl))
}
}
- pub unsafe fn from_ptr(ssl: *mut ffi::SSL) -> Ssl {
- Ssl(SslRef::from_ptr(ssl))
- }
-}
-
-/// A stream wrapper which handles SSL encryption for an underlying stream.
-pub struct SslStream<S> {
- ssl: Ssl,
- _method: Arc<BioMethod>, // NOTE: this *must* be after the Ssl field so things drop right
- _p: PhantomData<S>,
-}
-
-unsafe impl<S: Send> Send for SslStream<S> {}
-
-impl<S> fmt::Debug for SslStream<S>
- where S: fmt::Debug
-{
- fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
- fmt.debug_struct("SslStream")
- .field("stream", &self.get_ref())
- .field("ssl", &self.ssl())
- .finish()
- }
-}
-
-impl<S: Read + Write> SslStream<S> {
- fn new_base(ssl: Ssl, stream: S) -> Self {
- unsafe {
- let (bio, method) = bio::new(stream).unwrap();
- ffi::SSL_set_bio(ssl.as_ptr(), bio, bio);
-
- SslStream {
- ssl: ssl,
- _method: method,
- _p: PhantomData,
- }
- }
- }
-
/// Creates an SSL/TLS client operating over the provided stream.
- pub fn connect<T: IntoSsl>(ssl: T, stream: S)
- -> Result<Self, HandshakeError<S>>{
- let ssl = try!(ssl.into_ssl().map_err(|e| {
- HandshakeError::Failure(Error::Ssl(e))
- }));
- let mut stream = Self::new_base(ssl, stream);
- let ret = stream.ssl.connect();
+ ///
+ /// # Warning
+ ///
+ /// OpenSSL's default configuration is insecure. It is highly recommended to use
+ /// `SslConnector` rather than `Ssl` directly, as it manages that configuration.
+ pub fn connect<S>(self, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
+ where S: Read + Write
+ {
+ let mut stream = SslStream::new_base(self, stream);
+ let ret = unsafe { ffi::SSL_connect(stream.ssl.as_ptr()) };
if ret > 0 {
Ok(stream)
} else {
@@ -1153,19 +1122,27 @@ impl<S: Read + Write> SslStream<S> {
error: e,
}))
}
- err => Err(HandshakeError::Failure(err)),
+ err => {
+ Err(HandshakeError::Failure(MidHandshakeSslStream {
+ stream: stream,
+ error: err,
+ }))
+ }
}
}
}
/// Creates an SSL/TLS server operating over the provided stream.
- pub fn accept<T: IntoSsl>(ssl: T, stream: S)
- -> Result<Self, HandshakeError<S>> {
- let ssl = try!(ssl.into_ssl().map_err(|e| {
- HandshakeError::Failure(Error::Ssl(e))
- }));
- let mut stream = Self::new_base(ssl, stream);
- let ret = stream.ssl.accept();
+ ///
+ /// # Warning
+ ///
+ /// OpenSSL's default configuration is insecure. It is highly recommended to use
+ /// `SslAcceptor` rather than `Ssl` directly, as it manages that configuration.
+ pub fn accept<S>(self, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
+ where S: Read + Write
+ {
+ let mut stream = SslStream::new_base(self, stream);
+ let ret = unsafe { ffi::SSL_accept(stream.ssl.as_ptr()) };
if ret > 0 {
Ok(stream)
} else {
@@ -1177,71 +1154,15 @@ impl<S: Read + Write> SslStream<S> {
error: e,
}))
}
- err => Err(HandshakeError::Failure(err)),
+ err => {
+ Err(HandshakeError::Failure(MidHandshakeSslStream {
+ stream: stream,
+ error: err,
+ }))
+ }
}
}
}
-
- /// Like `read`, but returns an `ssl::Error` rather than an `io::Error`.
- ///
- /// This is particularly useful with a nonblocking socket, where the error
- /// value will identify if OpenSSL is waiting on read or write readiness.
- pub fn ssl_read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
- let ret = self.ssl.read(buf);
- if ret >= 0 {
- Ok(ret as usize)
- } else {
- Err(self.make_error(ret))
- }
- }
-
- /// Like `write`, but returns an `ssl::Error` rather than an `io::Error`.
- ///
- /// This is particularly useful with a nonblocking socket, where the error
- /// value will identify if OpenSSL is waiting on read or write readiness.
- pub fn ssl_write(&mut self, buf: &[u8]) -> Result<usize, Error> {
- let ret = self.ssl.write(buf);
- if ret >= 0 {
- Ok(ret as usize)
- } else {
- Err(self.make_error(ret))
- }
- }
-}
-
-/// An error or intermediate state after a TLS handshake attempt.
-#[derive(Debug)]
-pub enum HandshakeError<S> {
- /// The handshake failed.
- Failure(Error),
- /// The handshake was interrupted midway through.
- Interrupted(MidHandshakeSslStream<S>),
-}
-
-impl<S: Any + fmt::Debug> stderror::Error for HandshakeError<S> {
- fn description(&self) -> &str {
- match *self {
- HandshakeError::Failure(ref e) => e.description(),
- HandshakeError::Interrupted(ref e) => e.error.description(),
- }
- }
-
- fn cause(&self) -> Option<&stderror::Error> {
- match *self {
- HandshakeError::Failure(ref e) => Some(e),
- HandshakeError::Interrupted(ref e) => Some(&e.error),
- }
- }
-}
-
-impl<S: Any + fmt::Debug> fmt::Display for HandshakeError<S> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- try!(f.write_str(stderror::Error::description(self)));
- if let Some(e) = stderror::Error::cause(self) {
- try!(write!(f, ": {}", e));
- }
- Ok(())
- }
}
/// An SSL stream midway through the handshake process.
@@ -1263,7 +1184,7 @@ impl<S> MidHandshakeSslStream<S> {
}
/// Returns a shared reference to the `Ssl` of the stream.
- pub fn ssl(&self) -> &Ssl {
+ pub fn ssl(&self) -> &SslRef {
self.stream.ssl()
}
@@ -1272,9 +1193,14 @@ impl<S> MidHandshakeSslStream<S> {
&self.error
}
+ /// Consumes `self`, returning its error.
+ pub fn into_error(self) -> Error {
+ self.error
+ }
+
/// Restarts the handshake process.
pub fn handshake(mut self) -> Result<SslStream<S>, HandshakeError<S>> {
- let ret = self.stream.ssl.handshake();
+ let ret = unsafe { ffi::SSL_do_handshake(self.stream.ssl.as_ptr()) };
if ret > 0 {
Ok(self.stream)
} else {
@@ -1284,12 +1210,94 @@ impl<S> MidHandshakeSslStream<S> {
self.error = e;
Err(HandshakeError::Interrupted(self))
}
- err => Err(HandshakeError::Failure(err)),
+ err => {
+ self.error = err;
+ Err(HandshakeError::Failure(self))
+ }
}
}
}
}
+/// A stream wrapper which handles SSL encryption for an underlying stream.
+pub struct SslStream<S> {
+ ssl: Ssl,
+ _method: BioMethod, // NOTE: this *must* be after the Ssl field so things drop right
+ _p: PhantomData<S>,
+}
+
+impl<S> fmt::Debug for SslStream<S>
+ where S: fmt::Debug
+{
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ fmt.debug_struct("SslStream")
+ .field("stream", &self.get_ref())
+ .field("ssl", &self.ssl())
+ .finish()
+ }
+}
+
+impl<S: Read + Write> SslStream<S> {
+ fn new_base(ssl: Ssl, stream: S) -> Self {
+ unsafe {
+ let (bio, method) = bio::new(stream).unwrap();
+ ffi::SSL_set_bio(ssl.as_ptr(), bio, bio);
+
+ SslStream {
+ ssl: ssl,
+ _method: method,
+ _p: PhantomData,
+ }
+ }
+ }
+
+ /// Like `read`, but returns an `ssl::Error` rather than an `io::Error`.
+ ///
+ /// This is particularly useful with a nonblocking socket, where the error
+ /// value will identify if OpenSSL is waiting on read or write readiness.
+ pub fn ssl_read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
+ let ret = self.ssl.read(buf);
+ if ret >= 0 {
+ Ok(ret as usize)
+ } else {
+ Err(self.make_error(ret))
+ }
+ }
+
+ /// Like `write`, but returns an `ssl::Error` rather than an `io::Error`.
+ ///
+ /// This is particularly useful with a nonblocking socket, where the error
+ /// value will identify if OpenSSL is waiting on read or write readiness.
+ pub fn ssl_write(&mut self, buf: &[u8]) -> Result<usize, Error> {
+ let ret = self.ssl.write(buf);
+ if ret >= 0 {
+ Ok(ret as usize)
+ } else {
+ Err(self.make_error(ret))
+ }
+ }
+
+ /// Shuts down the session.
+ ///
+ /// The shutdown process consists of two steps. The first step sends a
+ /// close notify message to the peer, after which `ShutdownResult::Sent`
+ /// is returned. The second step awaits the receipt of a close notify
+ /// message from the peer, after which `ShutdownResult::Received` is
+ /// returned.
+ ///
+ /// While the connection may be closed after the first step, it is
+ /// recommended to fully shut the session down. In particular, it must
+ /// be fully shut down if the connection is to be used for further
+ /// communication in the future.
+ pub fn shutdown(&mut self) -> Result<ShutdownResult, Error> {
+ match unsafe { ffi::SSL_shutdown(self.ssl.as_ptr()) } {
+ 0 => Ok(ShutdownResult::Sent),
+ 1 => Ok(ShutdownResult::Received),
+ n => Err(self.make_error(n)),
+ }
+ }
+}
+
impl<S> SslStream<S> {
fn make_error(&mut self, ret: c_int) -> Error {
self.check_panic();
@@ -1319,16 +1327,12 @@ impl<S> SslStream<S> {
}
}
- #[cfg(feature = "nightly")]
fn check_panic(&mut self) {
if let Some(err) = unsafe { bio::take_panic::<S>(self.ssl.get_raw_rbio()) } {
::std::panic::resume_unwind(err)
}
}
- #[cfg(not(feature = "nightly"))]
- fn check_panic(&mut self) {}
-
fn get_bio_error(&mut self) -> io::Error {
let error = unsafe { bio::take_error::<S>(self.ssl.get_raw_rbio()) };
match error {
@@ -1362,7 +1366,7 @@ impl<S> SslStream<S> {
}
/// Returns the OpenSSL `Ssl` object associated with this stream.
- pub fn ssl(&self) -> &Ssl {
+ pub fn ssl(&self) -> &SslRef {
&self.ssl
}
}
@@ -1397,18 +1401,101 @@ impl<S: Read + Write> Write for SslStream<S> {
}
}
-pub trait IntoSsl {
- fn into_ssl(self) -> Result<Ssl, ErrorStack>;
+/// The result of a shutdown request.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum ShutdownResult {
+ /// A close notify message has been sent to the peer.
+ Sent,
+
+ /// A close notify response message has been received from the peer.
+ Received,
}
-impl IntoSsl for Ssl {
- fn into_ssl(self) -> Result<Ssl, ErrorStack> {
- Ok(self)
+#[cfg(ossl110)]
+mod compat {
+ use std::ptr;
+
+ use ffi;
+ use libc::c_int;
+
+ pub use ffi::{SSL_CTX_get_options, SSL_CTX_set_options};
+ pub use ffi::{SSL_CTX_clear_options, SSL_CTX_up_ref};
+
+ pub unsafe fn get_new_idx(f: ffi::CRYPTO_EX_free) -> c_int {
+ ffi::CRYPTO_get_ex_new_index(ffi::CRYPTO_EX_INDEX_SSL_CTX,
+ 0,
+ ptr::null_mut(),
+ None,
+ None,
+ Some(f))
+ }
+
+ pub unsafe fn get_new_ssl_idx(f: ffi::CRYPTO_EX_free) -> c_int {
+ ffi::CRYPTO_get_ex_new_index(ffi::CRYPTO_EX_INDEX_SSL,
+ 0,
+ ptr::null_mut(),
+ None,
+ None,
+ Some(f))
+ }
+
+ pub fn tls_method() -> *const ffi::SSL_METHOD {
+ unsafe { ffi::TLS_method() }
+ }
+
+ pub fn dtls_method() -> *const ffi::SSL_METHOD {
+ unsafe { ffi::DTLS_method() }
}
}
-impl<'a> IntoSsl for &'a SslContext {
- fn into_ssl(self) -> Result<Ssl, ErrorStack> {
- Ssl::new(self)
+#[cfg(ossl10x)]
+#[allow(bad_style)]
+mod compat {
+ use std::ptr;
+
+ use ffi;
+ use libc::{self, c_long, c_ulong, c_int};
+
+ pub unsafe fn SSL_CTX_get_options(ctx: *const ffi::SSL_CTX) -> c_ulong {
+ ffi::SSL_CTX_ctrl(ctx as *mut _, ffi::SSL_CTRL_OPTIONS, 0, ptr::null_mut()) as c_ulong
+ }
+
+ pub unsafe fn SSL_CTX_set_options(ctx: *const ffi::SSL_CTX, op: c_ulong) -> c_ulong {
+ ffi::SSL_CTX_ctrl(ctx as *mut _,
+ ffi::SSL_CTRL_OPTIONS,
+ op as c_long,
+ ptr::null_mut()) as c_ulong
+ }
+
+ pub unsafe fn SSL_CTX_clear_options(ctx: *const ffi::SSL_CTX, op: c_ulong) -> c_ulong {
+ ffi::SSL_CTX_ctrl(ctx as *mut _,
+ ffi::SSL_CTRL_CLEAR_OPTIONS,
+ op as c_long,
+ ptr::null_mut()) as c_ulong
+ }
+
+ pub unsafe fn get_new_idx(f: ffi::CRYPTO_EX_free) -> c_int {
+ ffi::SSL_CTX_get_ex_new_index(0, ptr::null_mut(), None, None, Some(f))
+ }
+
+ pub unsafe fn get_new_ssl_idx(f: ffi::CRYPTO_EX_free) -> c_int {
+ ffi::SSL_get_ex_new_index(0, ptr::null_mut(), None, None, Some(f))
+ }
+
+ pub unsafe fn SSL_CTX_up_ref(ssl: *mut ffi::SSL_CTX) -> libc::c_int {
+ ffi::CRYPTO_add_lock(&mut (*ssl).references,
+ 1,
+ ffi::CRYPTO_LOCK_SSL_CTX,
+ "mod.rs\0".as_ptr() as *const _,
+ line!() as libc::c_int);
+ 0
+ }
+
+ pub fn tls_method() -> *const ffi::SSL_METHOD {
+ unsafe { ffi::SSLv23_method() }
+ }
+
+ pub fn dtls_method() -> *const ffi::SSL_METHOD {
+ unsafe { ffi::DTLSv1_method() }
}
}
diff --git a/openssl/src/ssl/tests/mod.rs b/openssl/src/ssl/tests/mod.rs
index 3bbbed03..a84f6b25 100644
--- a/openssl/src/ssl/tests/mod.rs
+++ b/openssl/src/ssl/tests/mod.rs
@@ -1,5 +1,6 @@
#![allow(unused_imports)]
+use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io::{self, BufReader};
@@ -11,31 +12,26 @@ use std::process::{Command, Child, Stdio, ChildStdin};
use std::thread;
use std::time::Duration;
-use net2::TcpStreamExt;
+use tempdir::TempDir;
-use crypto::hash::Type::SHA256;
+use hash::MessageDigest;
use ssl;
use ssl::SSL_VERIFY_PEER;
-use ssl::SslMethod::Sslv23;
use ssl::{SslMethod, HandshakeError};
-use ssl::error::Error;
-use ssl::{SslContext, SslStream};
-use x509::X509StoreContext;
-use x509::X509FileType;
-use x509::X509;
-use crypto::pkey::PKey;
-
-#[cfg(feature="dtlsv1")]
+use ssl::{SslContext, SslStream, Ssl, ShutdownResult, SslConnectorBuilder, SslAcceptorBuilder,
+ Error};
+use x509::{X509StoreContext, X509, X509_FILETYPE_PEM};
+#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
+use x509::verify::X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS;
+use pkey::PKey;
+
use std::net::UdpSocket;
-#[cfg(feature="dtlsv1")]
-use ssl::SslMethod::Dtlsv1;
-#[cfg(feature="sslv2")]
-use ssl::SslMethod::Sslv2;
-#[cfg(feature="dtlsv1")]
-use net2::UdpSocketExt;
mod select;
+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, ATOMIC_USIZE_INIT, Ordering};
static PORT: AtomicUsize = ATOMIC_USIZE_INIT;
@@ -46,32 +42,42 @@ fn next_addr() -> SocketAddr {
struct Server {
p: Child,
+ _temp: TempDir,
}
impl Server {
fn spawn(args: &[&str], input: Option<Box<FnMut(ChildStdin) + Send>>) -> (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.pem")
- .arg("-key")
- .arg("key.pem")
- .arg("-no_dhe")
- .current_dir("test")
- .stdout(Stdio::null())
- .stderr(Stdio::null())
- .stdin(Stdio::piped())
- .spawn()
- .unwrap();
+ .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 }, addr)
+ (Server {
+ p: child,
+ _temp: td,
+ },
+ addr)
}
fn new_tcp(args: &[&str]) -> (Server, TcpStream) {
@@ -92,7 +98,6 @@ impl Server {
Server::new_tcp(&["-www"])
}
- #[cfg(any(feature = "alpn", feature = "npn"))]
fn new_alpn() -> (Server, TcpStream) {
Server::new_tcp(&["-www",
"-nextprotoneg",
@@ -101,7 +106,6 @@ impl Server {
"http/1.1,spdy/3.1"])
}
- #[cfg(feature = "dtlsv1")]
fn new_dtlsv1<I>(input: I) -> (Server, UdpConnected)
where I: IntoIterator<Item = &'static str>,
I::IntoIter: Send + 'static
@@ -109,17 +113,17 @@ impl Server {
let mut input = input.into_iter();
let (s, addr) = Server::spawn(&["-dtls1"],
Some(Box::new(move |mut io| {
- for s in input.by_ref() {
- if io.write_all(s.as_bytes()).is_err() {
- break;
- }
- }
- })));
+ for s in input.by_ref() {
+ if io.write_all(s.as_bytes()).is_err() {
+ break;
+ }
+ }
+ })));
// Need to wait for the UDP socket to get bound in our child process,
// but don't currently have a great way to do that so just wait for a
// bit.
thread::sleep(Duration::from_millis(100));
- let socket = UdpSocket::bind(next_addr()).unwrap();
+ let socket = UdpSocket::bind("127.0.0.1:0").unwrap();
socket.connect(&addr).unwrap();
(s, UdpConnected(socket))
}
@@ -132,51 +136,18 @@ impl Drop for Server {
}
}
-#[cfg(feature = "dtlsv1")]
#[derive(Debug)]
struct UdpConnected(UdpSocket);
-#[cfg(feature = "dtlsv1")]
impl Read for UdpConnected {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
- self.0.recv_from(buf).map(|(s, _)| s)
+ self.0.recv(buf)
}
}
-#[cfg(feature = "dtlsv1")]
impl Write for UdpConnected {
- #[cfg(unix)]
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- use std::os::unix::prelude::*;
- use libc;
- let n = unsafe {
- libc::send(self.0.as_raw_fd(),
- buf.as_ptr() as *const _,
- buf.len() as libc::size_t,
- 0)
- };
- if n < 0 {
- Err(io::Error::last_os_error())
- } else {
- Ok(n as usize)
- }
- }
-
- #[cfg(windows)]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- use std::os::windows::prelude::*;
- use libc;
- let n = unsafe {
- libc::send(self.0.as_raw_socket(),
- buf.as_ptr() as *const _,
- buf.len() as libc::c_int,
- 0)
- };
- if n < 0 {
- Err(io::Error::last_os_error())
- } else {
- Ok(n as usize)
- }
+ self.0.send(buf)
}
fn flush(&mut self) -> io::Result<()> {
@@ -197,147 +168,139 @@ macro_rules! run_test(
use ssl::SslMethod;
use ssl::{SslContext, Ssl, SslStream};
use ssl::SSL_VERIFY_PEER;
- use crypto::hash::Type::{SHA1, SHA256};
+ use hash::MessageDigest;
use x509::X509StoreContext;
use serialize::hex::FromHex;
+ use types::OpenSslTypeRef;
use super::Server;
#[test]
fn sslv23() {
let (_s, stream) = Server::new();
- $blk(SslMethod::Sslv23, stream);
+ $blk(SslMethod::tls(), stream);
}
#[test]
- #[cfg(feature="dtlsv1")]
+ #[cfg_attr(any(windows, target_arch = "arm"), ignore)] // FIXME(#467)
fn dtlsv1() {
let (_s, stream) = Server::new_dtlsv1(Some("hello"));
- $blk(SslMethod::Dtlsv1, stream);
+ $blk(SslMethod::dtls(), stream);
}
}
);
);
run_test!(new_ctx, |method, _| {
- SslContext::new(method).unwrap();
-});
-
-run_test!(new_sslstream, |method, stream| {
- SslStream::connect(&SslContext::new(method).unwrap(), stream).unwrap();
-});
-
-run_test!(get_ssl_method, |method, _| {
- let ssl = Ssl::new(&SslContext::new(method).unwrap()).unwrap();
- assert_eq!(ssl.ssl_method(), method);
+ SslContext::builder(method).unwrap();
});
run_test!(verify_untrusted, |method, stream| {
- let mut ctx = SslContext::new(method).unwrap();
+ let mut ctx = SslContext::builder(method).unwrap();
ctx.set_verify(SSL_VERIFY_PEER);
- match SslStream::connect(&ctx, stream) {
+ 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::new(method).unwrap();
+ let mut ctx = SslContext::builder(method).unwrap();
ctx.set_verify(SSL_VERIFY_PEER);
- match ctx.set_CA_file(&Path::new("test/root-ca.pem")) {
+ match ctx.set_ca_file(&Path::new("test/root-ca.pem")) {
Ok(_) => {}
Err(err) => panic!("Unexpected error {:?}", err),
}
- match SslStream::connect(&ctx, stream) {
+ 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::new(method).unwrap();
+ let mut ctx = SslContext::builder(method).unwrap();
ctx.set_verify_callback(SSL_VERIFY_PEER, |_, _| true);
- match SslStream::connect(&ctx, stream) {
+ 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::new(method).unwrap();
+ let mut ctx = SslContext::builder(method).unwrap();
ctx.set_verify_callback(SSL_VERIFY_PEER, |_, _| false);
- assert!(SslStream::connect(&ctx, stream).is_err());
+ assert!(Ssl::new(&ctx.build()).unwrap().connect(stream).is_err());
});
run_test!(verify_trusted_callback_override_ok, |method, stream| {
- let mut ctx = SslContext::new(method).unwrap();
+ let mut ctx = SslContext::builder(method).unwrap();
ctx.set_verify_callback(SSL_VERIFY_PEER, |_, _| true);
- match ctx.set_CA_file(&Path::new("test/cert.pem")) {
+ match ctx.set_ca_file(&Path::new("test/cert.pem")) {
Ok(_) => {}
Err(err) => panic!("Unexpected error {:?}", err),
}
- match SslStream::connect(&ctx, stream) {
+ 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::new(method).unwrap();
+ let mut ctx = SslContext::builder(method).unwrap();
ctx.set_verify_callback(SSL_VERIFY_PEER, |_, _| false);
- match ctx.set_CA_file(&Path::new("test/cert.pem")) {
+ match ctx.set_ca_file(&Path::new("test/cert.pem")) {
Ok(_) => {}
Err(err) => panic!("Unexpected error {:?}", err),
}
- assert!(SslStream::connect(&ctx, stream).is_err());
+ assert!(Ssl::new(&ctx.build()).unwrap().connect(stream).is_err());
});
run_test!(verify_callback_load_certs, |method, stream| {
- let mut ctx = SslContext::new(method).unwrap();
+ let mut ctx = SslContext::builder(method).unwrap();
ctx.set_verify_callback(SSL_VERIFY_PEER, |_, x509_ctx| {
assert!(x509_ctx.current_cert().is_some());
true
});
- assert!(SslStream::connect(&ctx, stream).is_ok());
+ assert!(Ssl::new(&ctx.build()).unwrap().connect(stream).is_ok());
});
run_test!(verify_trusted_get_error_ok, |method, stream| {
- let mut ctx = SslContext::new(method).unwrap();
+ let mut ctx = SslContext::builder(method).unwrap();
ctx.set_verify_callback(SSL_VERIFY_PEER, |_, x509_ctx| {
assert!(x509_ctx.error().is_none());
true
});
- match ctx.set_CA_file(&Path::new("test/root-ca.pem")) {
+ match ctx.set_ca_file(&Path::new("test/root-ca.pem")) {
Ok(_) => {}
Err(err) => panic!("Unexpected error {:?}", err),
}
- assert!(SslStream::connect(&ctx, stream).is_ok());
+ assert!(Ssl::new(&ctx.build()).unwrap().connect(stream).is_ok());
});
run_test!(verify_trusted_get_error_err, |method, stream| {
- let mut ctx = SslContext::new(method).unwrap();
+ let mut ctx = SslContext::builder(method).unwrap();
ctx.set_verify_callback(SSL_VERIFY_PEER, |_, x509_ctx| {
assert!(x509_ctx.error().is_some());
false
});
- assert!(SslStream::connect(&ctx, stream).is_err());
+ assert!(Ssl::new(&ctx.build()).unwrap().connect(stream).is_err());
});
run_test!(verify_callback_data, |method, stream| {
- let mut ctx = SslContext::new(method).unwrap();
+ 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
+// 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 = node_hash_str.from_hex().unwrap();
ctx.set_verify_callback(SSL_VERIFY_PEER, move |_preverify_ok, x509_ctx| {
@@ -345,14 +308,14 @@ run_test!(verify_callback_data, |method, stream| {
match cert {
None => false,
Some(cert) => {
- let fingerprint = cert.fingerprint(SHA1).unwrap();
+ let fingerprint = cert.fingerprint(MessageDigest::sha1()).unwrap();
fingerprint == node_id
}
}
});
ctx.set_verify_depth(1);
- match SslStream::connect(&ctx, stream) {
+ match Ssl::new(&ctx.build()).unwrap().connect(stream) {
Ok(_) => (),
Err(err) => panic!("Expected success, got {:?}", err),
}
@@ -360,12 +323,11 @@ run_test!(verify_callback_data, |method, stream| {
run_test!(ssl_verify_callback, |method, stream| {
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
- use ssl::IntoSsl;
static CHECKED: AtomicUsize = ATOMIC_USIZE_INIT;
- let ctx = SslContext::new(method).unwrap();
- let mut ssl = ctx.into_ssl().unwrap();
+ let ctx = SslContext::builder(method).unwrap();
+ let mut ssl = Ssl::new(&ctx.build()).unwrap();
let node_hash_str = "59172d9313e84459bcff27f967e79e6e9217e584";
let node_id = node_hash_str.from_hex().unwrap();
@@ -374,13 +336,13 @@ run_test!(ssl_verify_callback, |method, stream| {
match x509.current_cert() {
None => false,
Some(cert) => {
- let fingerprint = cert.fingerprint(SHA1).unwrap();
+ let fingerprint = cert.fingerprint(MessageDigest::sha1()).unwrap();
fingerprint == node_id
}
}
});
- match SslStream::connect(ssl, stream) {
+ match ssl.connect(stream) {
Ok(_) => (),
Err(err) => panic!("Expected success, got {:?}", err),
}
@@ -391,24 +353,24 @@ run_test!(ssl_verify_callback, |method, stream| {
// Make sure every write call translates to a write call to the underlying socket.
#[test]
fn test_write_hits_stream() {
- let listener = TcpListener::bind(next_addr()).unwrap();
+ let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
let guard = thread::spawn(move || {
- let ctx = SslContext::new(Sslv23).unwrap();
+ let ctx = SslContext::builder(SslMethod::tls()).unwrap();
let stream = TcpStream::connect(addr).unwrap();
- let mut stream = SslStream::connect(&ctx, stream).unwrap();
+ let mut stream = Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap();
stream.write_all(b"hello").unwrap();
stream
});
- let mut ctx = SslContext::new(Sslv23).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
ctx.set_verify(SSL_VERIFY_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();
+ ctx.set_certificate_file(&Path::new("test/cert.pem"), X509_FILETYPE_PEM).unwrap();
+ ctx.set_private_key_file(&Path::new("test/key.pem"), X509_FILETYPE_PEM).unwrap();
let stream = listener.accept().unwrap().0;
- let mut stream = SslStream::accept(&ctx, stream).unwrap();
+ let mut stream = Ssl::new(&ctx.build()).unwrap().accept(stream).unwrap();
let mut buf = [0; 5];
assert_eq!(5, stream.read(&mut buf).unwrap());
@@ -423,7 +385,7 @@ fn test_set_certificate_and_private_key() {
let cert = include_bytes!("../../../test/cert.pem");
let cert = X509::from_pem(cert).unwrap();
- let mut ctx = SslContext::new(Sslv23).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
ctx.set_private_key(&key).unwrap();
ctx.set_certificate(&cert).unwrap();
@@ -431,18 +393,18 @@ fn test_set_certificate_and_private_key() {
}
run_test!(get_ctx_options, |method, _| {
- let ctx = SslContext::new(method).unwrap();
+ let ctx = SslContext::builder(method).unwrap();
ctx.options();
});
run_test!(set_ctx_options, |method, _| {
- let mut ctx = SslContext::new(method).unwrap();
+ let mut ctx = SslContext::builder(method).unwrap();
let opts = ctx.set_options(ssl::SSL_OP_NO_TICKET);
assert!(opts.contains(ssl::SSL_OP_NO_TICKET));
});
run_test!(clear_ctx_options, |method, _| {
- let mut ctx = SslContext::new(method).unwrap();
+ let mut ctx = SslContext::builder(method).unwrap();
ctx.set_options(ssl::SSL_OP_ALL);
let opts = ctx.clear_options(ssl::SSL_OP_ALL);
assert!(!opts.contains(ssl::SSL_OP_ALL));
@@ -451,17 +413,8 @@ run_test!(clear_ctx_options, |method, _| {
#[test]
fn test_write() {
let (_s, stream) = Server::new();
- let mut stream = SslStream::connect(&SslContext::new(Sslv23).unwrap(), stream).unwrap();
- stream.write_all("hello".as_bytes()).unwrap();
- stream.flush().unwrap();
- stream.write_all(" there".as_bytes()).unwrap();
- stream.flush().unwrap();
-}
-
-#[test]
-fn test_write_direct() {
- let (_s, stream) = Server::new();
- let mut stream = SslStream::connect(&SslContext::new(Sslv23).unwrap(), stream).unwrap();
+ 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();
@@ -469,20 +422,21 @@ fn test_write_direct() {
}
run_test!(get_peer_certificate, |method, stream| {
- let stream = SslStream::connect(&SslContext::new(method).unwrap(), stream).unwrap();
+ 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(SHA1).unwrap();
+ let fingerprint = cert.fingerprint(MessageDigest::sha1()).unwrap();
let node_hash_str = "59172d9313e84459bcff27f967e79e6e9217e584";
let node_id = node_hash_str.from_hex().unwrap();
assert_eq!(node_id, fingerprint)
});
#[test]
-#[cfg(feature = "dtlsv1")]
+#[cfg_attr(any(windows, target_arch = "arm"), ignore)] // FIXME(#467)
fn test_write_dtlsv1() {
let (_s, stream) = Server::new_dtlsv1(iter::repeat("y\n"));
-
- let mut stream = SslStream::connect(&SslContext::new(Dtlsv1).unwrap(), stream).unwrap();
+ let ctx = SslContext::builder(SslMethod::dtls()).unwrap();
+ let mut stream = Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap();
stream.write_all(b"hello").unwrap();
stream.flush().unwrap();
stream.write_all(b" there").unwrap();
@@ -492,16 +446,8 @@ fn test_write_dtlsv1() {
#[test]
fn test_read() {
let (_s, tcp) = Server::new();
- let mut stream = SslStream::connect(&SslContext::new(Sslv23).unwrap(), 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_read_direct() {
- let (_s, tcp) = Server::new();
- let mut stream = SslStream::connect(&SslContext::new(Sslv23).unwrap(), tcp).unwrap();
+ 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");
@@ -510,7 +456,8 @@ fn test_read_direct() {
#[test]
fn test_pending() {
let (_s, tcp) = Server::new();
- let mut stream = SslStream::connect(&SslContext::new(Sslv23).unwrap(), tcp).unwrap();
+ 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();
@@ -533,7 +480,8 @@ fn test_pending() {
#[test]
fn test_state() {
let (_s, tcp) = Server::new();
- let stream = SslStream::connect(&SslContext::new(Sslv23).unwrap(), tcp).unwrap();
+ 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");
@@ -542,17 +490,17 @@ fn test_state() {
/// Tests that connecting with the client using ALPN, but the server not does not
/// break the existing connection behavior.
#[test]
-#[cfg(feature = "alpn")]
+#[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::new(Sslv23).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
ctx.set_verify(SSL_VERIFY_PEER);
- ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]);
- match ctx.set_CA_file(&Path::new("test/root-ca.pem")) {
+ 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 SslStream::connect(&ctx, stream) {
+ let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) {
Ok(stream) => stream,
Err(err) => panic!("Expected success, got {:?}", err),
};
@@ -564,17 +512,16 @@ fn test_connect_with_unilateral_alpn() {
/// Tests that connecting with the client using NPN, but the server not does not
/// break the existing connection behavior.
#[test]
-#[cfg(feature = "npn")]
fn test_connect_with_unilateral_npn() {
let (_s, stream) = Server::new();
- let mut ctx = SslContext::new(Sslv23).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
ctx.set_verify(SSL_VERIFY_PEER);
- ctx.set_npn_protocols(&[b"http/1.1", b"spdy/3.1"]);
- match ctx.set_CA_file(&Path::new("test/root-ca.pem")) {
+ 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 SslStream::connect(&ctx, stream) {
+ let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) {
Ok(stream) => stream,
Err(err) => panic!("Expected success, got {:?}", err),
};
@@ -586,17 +533,17 @@ fn test_connect_with_unilateral_npn() {
/// 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(feature = "alpn")]
+#[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::new(Sslv23).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
ctx.set_verify(SSL_VERIFY_PEER);
- ctx.set_alpn_protocols(&[b"spdy/3.1", b"http/1.1"]);
- match ctx.set_CA_file(&Path::new("test/root-ca.pem")) {
+ 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 SslStream::connect(&ctx, stream) {
+ let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) {
Ok(stream) => stream,
Err(err) => panic!("Expected success, got {:?}", err),
};
@@ -608,17 +555,17 @@ fn test_connect_with_alpn_successful_multiple_matching() {
/// 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(feature = "npn")]
+#[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::new(Sslv23).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
ctx.set_verify(SSL_VERIFY_PEER);
- ctx.set_npn_protocols(&[b"spdy/3.1", b"http/1.1"]);
- match ctx.set_CA_file(&Path::new("test/root-ca.pem")) {
+ 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 SslStream::connect(&ctx, stream) {
+ let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) {
Ok(stream) => stream,
Err(err) => panic!("Expected success, got {:?}", err),
};
@@ -631,17 +578,17 @@ fn test_connect_with_npn_successful_multiple_matching() {
/// lists of supported protocols have an overlap -- with only ONE protocol
/// being valid for both.
#[test]
-#[cfg(feature = "alpn")]
+#[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::new(Sslv23).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
ctx.set_verify(SSL_VERIFY_PEER);
- ctx.set_alpn_protocols(&[b"spdy/3.1"]);
- match ctx.set_CA_file(&Path::new("test/root-ca.pem")) {
+ 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 SslStream::connect(&ctx, stream) {
+ let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) {
Ok(stream) => stream,
Err(err) => panic!("Expected success, got {:?}", err),
};
@@ -655,17 +602,17 @@ fn test_connect_with_alpn_successful_single_match() {
/// lists of supported protocols have an overlap -- with only ONE protocol
/// being valid for both.
#[test]
-#[cfg(feature = "npn")]
+#[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::new(Sslv23).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
ctx.set_verify(SSL_VERIFY_PEER);
- ctx.set_npn_protocols(&[b"spdy/3.1"]);
- match ctx.set_CA_file(&Path::new("test/root-ca.pem")) {
+ 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 SslStream::connect(&ctx, stream) {
+ let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) {
Ok(stream) => stream,
Err(err) => panic!("Expected success, got {:?}", err),
};
@@ -677,37 +624,36 @@ fn test_connect_with_npn_successful_single_match() {
/// Tests that when the `SslStream` is created as a server stream, the protocols
/// are correctly advertised to the client.
#[test]
-#[cfg(feature = "npn")]
fn test_npn_server_advertise_multiple() {
- let listener = TcpListener::bind(next_addr()).unwrap();
+ 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::new(Sslv23).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
ctx.set_verify(SSL_VERIFY_PEER);
- ctx.set_npn_protocols(&[b"http/1.1", b"spdy/3.1"]);
- assert!(ctx.set_certificate_file(&Path::new("test/cert.pem"), X509FileType::PEM)
+ ctx.set_npn_protocols(&[b"http/1.1", b"spdy/3.1"]).unwrap();
+ assert!(ctx.set_certificate_file(&Path::new("test/cert.pem"), X509_FILETYPE_PEM)
.is_ok());
- ctx.set_private_key_file(&Path::new("test/key.pem"), X509FileType::PEM)
- .unwrap();
- ctx
+ ctx.set_private_key_file(&Path::new("test/key.pem"), X509_FILETYPE_PEM)
+ .unwrap();
+ ctx.build()
};
// Have the listener wait on the connection in a different thread.
thread::spawn(move || {
let (stream, _) = listener.accept().unwrap();
- let _ = SslStream::accept(&listener_ctx, stream).unwrap();
+ Ssl::new(&listener_ctx).unwrap().accept(stream).unwrap();
});
- let mut ctx = SslContext::new(Sslv23).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
ctx.set_verify(SSL_VERIFY_PEER);
- ctx.set_npn_protocols(&[b"spdy/3.1"]);
- match ctx.set_CA_file(&Path::new("test/root-ca.pem")) {
+ 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 SslStream::connect(&ctx, stream) {
+ let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) {
Ok(stream) => stream,
Err(err) => panic!("Expected success, got {:?}", err),
};
@@ -718,37 +664,37 @@ fn test_npn_server_advertise_multiple() {
/// Tests that when the `SslStream` is created as a server stream, the protocols
/// are correctly advertised to the client.
#[test]
-#[cfg(feature = "alpn")]
+#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
fn test_alpn_server_advertise_multiple() {
- let listener = TcpListener::bind(next_addr()).unwrap();
+ 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::new(Sslv23).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
ctx.set_verify(SSL_VERIFY_PEER);
- ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]);
- assert!(ctx.set_certificate_file(&Path::new("test/cert.pem"), X509FileType::PEM)
+ ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]).unwrap();
+ assert!(ctx.set_certificate_file(&Path::new("test/cert.pem"), X509_FILETYPE_PEM)
.is_ok());
- ctx.set_private_key_file(&Path::new("test/key.pem"), X509FileType::PEM)
- .unwrap();
- ctx
+ ctx.set_private_key_file(&Path::new("test/key.pem"), X509_FILETYPE_PEM)
+ .unwrap();
+ ctx.build()
};
// Have the listener wait on the connection in a different thread.
thread::spawn(move || {
let (stream, _) = listener.accept().unwrap();
- let _ = SslStream::accept(&listener_ctx, stream).unwrap();
+ Ssl::new(&listener_ctx).unwrap().accept(stream).unwrap();
});
- let mut ctx = SslContext::new(Sslv23).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
ctx.set_verify(SSL_VERIFY_PEER);
- ctx.set_alpn_protocols(&[b"spdy/3.1"]);
- match ctx.set_CA_file(&Path::new("test/root-ca.pem")) {
+ 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 SslStream::connect(&ctx, stream) {
+ let stream = match Ssl::new(&ctx.build()).unwrap().connect(stream) {
Ok(stream) => stream,
Err(err) => panic!("Expected success, got {:?}", err),
};
@@ -759,87 +705,82 @@ fn test_alpn_server_advertise_multiple() {
/// Test that Servers supporting ALPN don't report a protocol when none of their protocols match
/// the client's reported protocol.
#[test]
-#[cfg(feature = "alpn")]
+#[cfg(all(feature = "v102", ossl102))]
fn test_alpn_server_select_none() {
- let listener = TcpListener::bind(next_addr()).unwrap();
+ 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::new(Sslv23).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
ctx.set_verify(SSL_VERIFY_PEER);
- ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]);
- assert!(ctx.set_certificate_file(&Path::new("test/cert.pem"), X509FileType::PEM)
+ ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]).unwrap();
+ assert!(ctx.set_certificate_file(&Path::new("test/cert.pem"), X509_FILETYPE_PEM)
.is_ok());
- ctx.set_private_key_file(&Path::new("test/key.pem"), X509FileType::PEM)
- .unwrap();
- ctx
+ ctx.set_private_key_file(&Path::new("test/key.pem"), X509_FILETYPE_PEM)
+ .unwrap();
+ ctx.build()
};
// Have the listener wait on the connection in a different thread.
thread::spawn(move || {
let (stream, _) = listener.accept().unwrap();
- let _ = SslStream::accept(&listener_ctx, stream).unwrap();
+ Ssl::new(&listener_ctx).unwrap().accept(stream).unwrap();
});
- let mut ctx = SslContext::new(Sslv23).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
ctx.set_verify(SSL_VERIFY_PEER);
- ctx.set_alpn_protocols(&[b"http/2"]);
- match ctx.set_CA_file(&Path::new("test/root-ca.pem")) {
- Ok(_) => {}
- Err(err) => panic!("Unexpected error {:?}", err),
- }
+ 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 = match SslStream::connect(&ctx, stream) {
- Ok(stream) => stream,
- Err(err) => panic!("Expected success, got {:?}", err),
- };
+ 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());
}
+// In 1.1.0, ALPN negotiation failure is a fatal error
+#[test]
+#[cfg(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(SSL_VERIFY_PEER);
+ ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]).unwrap();
+ assert!(ctx.set_certificate_file(&Path::new("test/cert.pem"), X509_FILETYPE_PEM)
+ .is_ok());
+ ctx.set_private_key_file(&Path::new("test/key.pem"), X509_FILETYPE_PEM)
+ .unwrap();
+ ctx.build()
+ };
+ // Have the listener wait on the connection in a different thread.
+ thread::spawn(move || {
+ let (stream, _) = listener.accept().unwrap();
+ assert!(Ssl::new(&listener_ctx).unwrap().accept(stream).is_err());
+ });
-#[cfg(feature="dtlsv1")]
-#[cfg(test)]
-mod dtlsv1 {
- use serialize::hex::FromHex;
- use std::net::TcpStream;
- use std::thread;
-
- use crypto::hash::Type::SHA256;
- use ssl::SslMethod;
- use ssl::SslMethod::Dtlsv1;
- use ssl::{SslContext, SslStream};
- use ssl::SSL_VERIFY_PEER;
- use x509::X509StoreContext;
-
- const PROTOCOL: SslMethod = Dtlsv1;
-
- #[test]
- fn test_new_ctx() {
- SslContext::new(PROTOCOL).unwrap();
- }
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ctx.set_verify(SSL_VERIFY_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();
+ assert!(Ssl::new(&ctx.build()).unwrap().connect(stream).is_err());
}
#[test]
-#[cfg(feature = "dtlsv1")]
+#[cfg_attr(any(windows, target_arch = "arm"), ignore)] // FIXME(#467)
fn test_read_dtlsv1() {
let (_s, stream) = Server::new_dtlsv1(Some("hello"));
- let mut stream = SslStream::connect(&SslContext::new(Dtlsv1).unwrap(), stream).unwrap();
+ let ctx = SslContext::builder(SslMethod::dtls()).unwrap();
+ let mut stream = Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap();
let mut buf = [0u8; 100];
assert!(stream.read(&mut buf).is_ok());
}
-#[test]
-#[cfg(feature = "sslv2")]
-fn test_sslv2_connect_failure() {
- let (_s, tcp) = Server::new_tcp(&["-no_ssl2", "-www"]);
- SslStream::connect(&SslContext::new(Sslv2).unwrap(), tcp)
- .err()
- .unwrap();
-}
-
fn wait_io(stream: &TcpStream, read: bool, timeout_ms: u32) -> bool {
unsafe {
let mut set: select::fd_set = mem::zeroed();
@@ -859,8 +800,7 @@ fn wait_io(stream: &TcpStream, read: bool, timeout_ms: u32) -> bool {
}
}
-fn handshake(res: Result<SslStream<TcpStream>, HandshakeError<TcpStream>>)
- -> SslStream<TcpStream> {
+fn handshake(res: Result<SslStream<TcpStream>, HandshakeError<TcpStream>>) -> SslStream<TcpStream> {
match res {
Ok(s) => s,
Err(HandshakeError::Interrupted(s)) => {
@@ -875,8 +815,8 @@ fn handshake(res: Result<SslStream<TcpStream>, HandshakeError<TcpStream>>)
fn test_write_nonblocking() {
let (_s, stream) = Server::new();
stream.set_nonblocking(true).unwrap();
- let cx = SslContext::new(Sslv23).unwrap();
- let mut stream = handshake(SslStream::connect(&cx, stream));
+ let cx = SslContext::builder(SslMethod::tls()).unwrap().build();
+ let mut stream = handshake(Ssl::new(&cx).unwrap().connect(stream));
let mut iterations = 0;
loop {
@@ -909,12 +849,12 @@ fn test_write_nonblocking() {
}
#[test]
-#[cfg_attr(windows, ignore)] // FIXME flickers on appveyor
+#[cfg_attr(any(windows, target_arch = "arm"), ignore)] // FIXME(#467)
fn test_read_nonblocking() {
let (_s, stream) = Server::new();
stream.set_nonblocking(true).unwrap();
- let cx = SslContext::new(Sslv23).unwrap();
- let mut stream = handshake(SslStream::connect(&cx, stream));
+ let cx = SslContext::builder(SslMethod::tls()).unwrap().build();
+ let mut stream = handshake(Ssl::new(&cx).unwrap().connect(stream));
let mut iterations = 0;
loop {
@@ -965,7 +905,6 @@ fn test_read_nonblocking() {
#[test]
#[should_panic(expected = "blammo")]
-#[cfg(feature = "nightly")]
fn write_panic() {
struct ExplodingStream(TcpStream);
@@ -988,13 +927,12 @@ fn write_panic() {
let (_s, stream) = Server::new();
let stream = ExplodingStream(stream);
- let ctx = SslContext::new(SslMethod::Sslv23).unwrap();
- let _ = SslStream::connect(&ctx, stream);
+ let ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ let _ = Ssl::new(&ctx.build()).unwrap().connect(stream);
}
#[test]
#[should_panic(expected = "blammo")]
-#[cfg(feature = "nightly")]
fn read_panic() {
struct ExplodingStream(TcpStream);
@@ -1017,13 +955,12 @@ fn read_panic() {
let (_s, stream) = Server::new();
let stream = ExplodingStream(stream);
- let ctx = SslContext::new(SslMethod::Sslv23).unwrap();
- let _ = SslStream::connect(&ctx, stream);
+ let ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ let _ = Ssl::new(&ctx.build()).unwrap().connect(stream);
}
#[test]
#[should_panic(expected = "blammo")]
-#[cfg(feature = "nightly")]
fn flush_panic() {
struct ExplodingStream(TcpStream);
@@ -1046,32 +983,31 @@ fn flush_panic() {
let (_s, stream) = Server::new();
let stream = ExplodingStream(stream);
- let ctx = SslContext::new(SslMethod::Sslv23).unwrap();
- let mut stream = SslStream::connect(&ctx, stream).unwrap();
+ 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::new(SslMethod::Sslv23).unwrap();
- ssl::Ssl::new(&ctx).unwrap()
+ let ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ssl::Ssl::new(&ctx.build()).unwrap()
};
{
- let new_ctx_a = SslContext::new(SslMethod::Sslv23).unwrap();
+ let new_ctx_a = SslContext::builder(SslMethod::tls()).unwrap().build();
let _new_ctx_b = ssl.set_ssl_context(&new_ctx_a);
}
}
#[test]
-#[cfg_attr(windows, ignore)] // don't have a trusted CA list easily available :(
fn default_verify_paths() {
- let mut ctx = SslContext::new(SslMethod::Sslv23).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
ctx.set_default_verify_paths().unwrap();
ctx.set_verify(SSL_VERIFY_PEER);
let s = TcpStream::connect("google.com:443").unwrap();
- let mut socket = SslStream::connect(&ctx, s).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![];
@@ -1086,6 +1022,172 @@ fn default_verify_paths() {
fn add_extra_chain_cert() {
let cert = include_bytes!("../../../test/cert.pem");
let cert = X509::from_pem(cert).unwrap();
- let mut ctx = SslContext::new(SslMethod::Sslv23).unwrap();
- ctx.add_extra_chain_cert(&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(SSL_VERIFY_PEER);
+
+ let mut ssl = Ssl::new(&ctx.build()).unwrap();
+ ssl.param_mut().set_hostflags(X509_CHECK_FLAG_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"</HTML>\r\n") || result.ends_with(b"</html>"));
+}
+
+#[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(SSL_VERIFY_PEER);
+
+ let mut ssl = Ssl::new(&ctx.build()).unwrap();
+ ssl.param_mut().set_hostflags(X509_CHECK_FLAG_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 = SslConnectorBuilder::new(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"</HTML>\r\n") || result.ends_with(b"</html>"));
+}
+
+#[test]
+fn connector_invalid_hostname() {
+ let connector = SslConnectorBuilder::new(SslMethod::tls()).unwrap().build();
+
+ let s = TcpStream::connect("google.com:443").unwrap();
+ assert!(connector.connect("foobar.com", s).is_err());
+}
+
+#[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 connector =
+ SslAcceptorBuilder::mozilla_intermediate(SslMethod::tls(), &key, &cert, None::<X509>)
+ .unwrap()
+ .build();
+ let stream = listener.accept().unwrap().0;
+ let mut stream = connector.accept(stream).unwrap();
+
+ stream.write_all(b"hello").unwrap();
+ });
+
+ let mut connector = SslConnectorBuilder::new(SslMethod::tls()).unwrap();
+ connector.builder_mut().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 connector =
+ SslAcceptorBuilder::mozilla_modern(SslMethod::tls(), &key, &cert, None::<X509>)
+ .unwrap()
+ .build();
+ let stream = listener.accept().unwrap().0;
+ let mut stream = connector.accept(stream).unwrap();
+
+ stream.write_all(b"hello").unwrap();
+ });
+
+ let mut connector = SslConnectorBuilder::new(SslMethod::tls()).unwrap();
+ connector.builder_mut().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"), X509_FILETYPE_PEM).unwrap();
+ ctx.set_private_key_file(&Path::new("test/key.pem"), X509_FILETYPE_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);
+}
+
+fn _check_kinds() {
+ fn is_send<T: Send>() {}
+ fn is_sync<T: Sync>() {}
+
+ is_send::<SslStream<TcpStream>>();
+ is_sync::<SslStream<TcpStream>>();
}