Commit a8d328d0 authored by Steven Fackler's avatar Steven Fackler Committed by GitHub
Browse files

Merge pull request #496 from sfackler/connectors

Implement Connector types
parents c0cf4ab1 23fe1e85
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
environment:
  SSL_CERT_FILE: "C:\\OpenSSL\\cacert.pem"
  matrix:
    # 1.1.0, 64/32 bit
    - TARGET: i686-pc-windows-gnu
@@ -23,6 +24,7 @@ install:
  # install OpenSSL
  - ps: Start-FileDownload "http://slproweb.com/download/Win${env:BITS}OpenSSL-${env:OPENSSL_VERSION}.exe"
  - Win%BITS%OpenSSL-%OPENSSL_VERSION%.exe /SILENT /VERYSILENT /SP- /DIR="C:\OpenSSL"
  - ps: Start-FileDownload "https://curl.haxx.se/ca/cacert.pem" -FileName "C:\OpenSSL\cacert.pem"

  # Install Rust
  - curl -sSf -o rustup-init.exe https://win.rustup.rs/
+2 −0
Original line number Diff line number Diff line
@@ -42,6 +42,8 @@ pub mod ssl;
pub mod symm;
pub mod version;
pub mod x509;
#[cfg(any(ossl102, ossl110))]
mod verify;

pub fn cvt_p<T>(r: *mut T) -> Result<*mut T, ErrorStack> {
    if r.is_null() {
+65 −44
Original line number Diff line number Diff line
use libc::{c_void, c_char, c_int};
use std::ptr;
use std::mem;
use std::ops::Deref;
use ffi;

use {cvt, cvt_p};
@@ -9,13 +10,66 @@ use dsa::Dsa;
use rsa::Rsa;
use error::ErrorStack;
use util::{CallbackState, invoke_passwd_cb};
use opaque::Opaque;

/// A borrowed `PKey`.
pub struct PKeyRef(Opaque);

impl PKeyRef {
    pub unsafe fn from_ptr<'a>(ptr: *mut ffi::EVP_PKEY) -> &'a PKeyRef {
        &*(ptr as *mut _)
    }

    pub fn as_ptr(&self) -> *mut ffi::EVP_PKEY {
        self as *const _ as *mut _
    }

    /// Get a reference to the interal RSA key for direct access to the key components
    pub fn rsa(&self) -> Result<Rsa, ErrorStack> {
        unsafe {
            let rsa = try!(cvt_p(ffi::EVP_PKEY_get1_RSA(self.as_ptr())));
            // this is safe as the ffi increments a reference counter to the internal key
            Ok(Rsa::from_ptr(rsa))
        }
    }

    /// Stores private key as a PEM
    // FIXME: also add password and encryption
    pub fn private_key_to_pem(&self) -> Result<Vec<u8>, ErrorStack> {
        let mem_bio = try!(MemBio::new());
        unsafe {
            try!(cvt(ffi::PEM_write_bio_PrivateKey(mem_bio.as_ptr(),
                                                   self.as_ptr(),
                                                   ptr::null(),
                                                   ptr::null_mut(),
                                                   -1,
                                                   None,
                                                   ptr::null_mut())));

        }
        Ok(mem_bio.get_buf().to_owned())
    }

    /// Stores public key as a PEM
    pub fn public_key_to_pem(&self) -> Result<Vec<u8>, ErrorStack> {
        let mem_bio = try!(MemBio::new());
        unsafe {
            try!(cvt(ffi::PEM_write_bio_PUBKEY(mem_bio.as_ptr(), self.as_ptr())));
        }
        Ok(mem_bio.get_buf().to_owned())
    }

    pub fn public_eq(&self, other: &PKeyRef) -> bool {
        unsafe { ffi::EVP_PKEY_cmp(self.as_ptr(), other.as_ptr()) == 1 }
    }
}

/// Represents a public key, optionally with a private key attached.
pub struct PKey(*mut ffi::EVP_PKEY);

unsafe impl Send for PKey {}
unsafe impl Sync for PKey {}

/// Represents a public key, optionally with a private key attached.
impl PKey {
    /// Create a new `PKey` containing an RSA key.
    pub fn from_rsa(rsa: Rsa) -> Result<PKey, ErrorStack> {
@@ -101,7 +155,7 @@ impl PKey {
        }
    }

    /// assign RSA key to this pkey
    /// Assign an RSA key to this pkey.
    pub fn set_rsa(&mut self, rsa: &Rsa) -> Result<(), ErrorStack> {
        unsafe {
            // this needs to be a reference as the set1_RSA ups the reference count
@@ -110,55 +164,22 @@ impl PKey {
            Ok(())
        }
    }

    /// Get a reference to the interal RSA key for direct access to the key components
    pub fn rsa(&self) -> Result<Rsa, ErrorStack> {
        unsafe {
            let rsa = try!(cvt_p(ffi::EVP_PKEY_get1_RSA(self.0)));
            // this is safe as the ffi increments a reference counter to the internal key
            Ok(Rsa::from_ptr(rsa))
        }
    }

    /// Stores private key as a PEM
    // FIXME: also add password and encryption
    pub fn private_key_to_pem(&self) -> Result<Vec<u8>, ErrorStack> {
        let mem_bio = try!(MemBio::new());
        unsafe {
            try!(cvt(ffi::PEM_write_bio_PrivateKey(mem_bio.as_ptr(),
                                                   self.0,
                                                   ptr::null(),
                                                   ptr::null_mut(),
                                                   -1,
                                                   None,
                                                   ptr::null_mut())));

        }
        Ok(mem_bio.get_buf().to_owned())
}

    /// Stores public key as a PEM
    pub fn public_key_to_pem(&self) -> Result<Vec<u8>, ErrorStack> {
        let mem_bio = try!(MemBio::new());
impl Drop for PKey {
    fn drop(&mut self) {
        unsafe {
            try!(cvt(ffi::PEM_write_bio_PUBKEY(mem_bio.as_ptr(), self.0)));
            ffi::EVP_PKEY_free(self.0);
        }
        Ok(mem_bio.get_buf().to_owned())
    }

    pub fn as_ptr(&self) -> *mut ffi::EVP_PKEY {
        return self.0;
}

    pub fn public_eq(&self, other: &PKey) -> bool {
        unsafe { ffi::EVP_PKEY_cmp(self.0, other.0) == 1 }
    }
}
impl Deref for PKey {
    type Target = PKeyRef;

impl Drop for PKey {
    fn drop(&mut self) {
    fn deref(&self) -> &PKeyRef {
        unsafe {
            ffi::EVP_PKEY_free(self.0);
            PKeyRef::from_ptr(self.0)
        }
    }
}
+359 −0
Original line number Diff line number Diff line
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;

// apps/dh2048.pem
const DHPARAM_PEM: &'static str = r#"
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEA///////////JD9qiIWjCNMTGYouA3BzRKQJOCIpnzHQCC76mOxOb
IlFKCHmONATd75UZs806QxswKwpt8l8UN0/hNW1tUcJF5IW1dmJefsb0TELppjft
awv/XLb0Brft7jhr+1qJn6WunyQRfEsf5kkoZlHs5Fs9wgB8uKFjvwWY2kg2HFXT
mmkWP6j9JM9fg2VdI9yjrZYcYvNWIIVSu57VKQdwlpZtZww1Tkq8mATxdGwIyhgh
fDKQXkYuNs474553LBgOhgObJ4Oi7Aeij7XFXfBvTFLJ3ivL9pVYFxg5lUl86pVq
5RXSJhiY+gUQFXKOWoqsqmj//////////wIBAg==
-----END DH PARAMETERS-----

These are the 2048-bit DH parameters from "More Modular Exponential
(MODP) Diffie-Hellman groups for Internet Key Exchange (IKE)":
https://tools.ietf.org/html/rfc3526

See https://tools.ietf.org/html/rfc2412 for how they were generated."#;

fn ctx(method: SslMethod) -> Result<SslContextBuilder, ErrorStack> {
    let mut ctx = try!(SslContextBuilder::new(method));

    // options to enable and cipher list lifted from libcurl
    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;
    ctx.set_options(opts);

    Ok(ctx)
}

/// A builder for `ClientConnector`s.
pub struct ClientConnectorBuilder(SslContextBuilder);

impl ClientConnectorBuilder {
    /// Creates a new builder for TLS connections.
    ///
    /// The default configuration is based off of libcurl's and is subject to change.
    pub fn tls() -> Result<ClientConnectorBuilder, ErrorStack> {
        ClientConnectorBuilder::new(SslMethod::tls())
    }

    fn new(method: SslMethod) -> Result<ClientConnectorBuilder, ErrorStack> {
        let mut ctx = try!(ctx(method));
        try!(ctx.set_default_verify_paths());
        try!(ctx.set_cipher_list("ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH"));

        Ok(ClientConnectorBuilder(ctx))
    }

    /// Returns a shared reference to the inner `SslContextBuilder`.
    pub fn context(&self) -> &SslContextBuilder {
        &self.0
    }

    /// Returns a mutable reference to the inner `SslContextBuilder`.
    pub fn context_mut(&mut self) -> &mut SslContextBuilder {
        &mut self.0
    }

    /// Consumes the builder, returning a `ClientConnector`.
    pub fn build(self) -> ClientConnector {
        ClientConnector(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 ClientConnector(SslContext);

impl ClientConnector {
    /// 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 `ServerConnector`s.
pub struct ServerConnectorBuilder(SslContextBuilder);

impl ServerConnectorBuilder {
    /// Creates a new builder for server-side TLS connections.
    ///
    /// The default configuration is based off of the intermediate profile of Mozilla's SSL
    /// Configuration Generator, and is subject to change.
    pub fn tls<I, T>(private_key: &PKeyRef,
                     certificate: &X509Ref,
                     chain: I)
                     -> Result<ServerConnectorBuilder, ErrorStack>
        where I: IntoIterator<Item = T>,
              T: AsRef<X509Ref>
    {
        ServerConnectorBuilder::new(SslMethod::tls(), private_key, certificate, chain)
    }

    fn new<I, T>(method: SslMethod,
                 private_key: &PKeyRef,
                 certificate: &X509Ref,
                 chain: I)
                 -> Result<ServerConnectorBuilder, ErrorStack>
        where I: IntoIterator<Item = T>,
              T: AsRef<X509Ref>
    {
        let mut ctx = try!(ctx(method));
        ctx.set_options(ssl::SSL_OP_SINGLE_DH_USE | ssl::SSL_OP_CIPHER_SERVER_PREFERENCE);
        let dh = try!(Dh::from_pem(DHPARAM_PEM.as_bytes()));
        try!(ctx.set_tmp_dh(&dh));
        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"));
        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(ServerConnectorBuilder(ctx))
    }

    /// Returns a shared reference to the inner `SslContextBuilder`.
    pub fn context(&self) -> &SslContextBuilder {
        &self.0
    }

    /// Returns a mutable reference to the inner `SslContextBuilder`.
    pub fn context_mut(&mut self) -> &mut SslContextBuilder {
        &mut self.0
    }

    /// Consumes the builder, returning a `ServerConnector`.
    pub fn build(self) -> ServerConnector {
        ServerConnector(self.0.build())
    }
}

/// 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 ServerConnector(SslContext);

impl ServerConnector {
    /// Initiates a server-side TLS session on a stream.
    pub fn connect<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(not(any(ossl102, ossl110)))]
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(not(any(ossl102, ossl110)))]
mod verify {
    use std::net::IpAddr;

    use nid;
    use x509::{X509StoreContextRef, X509Ref, GeneralNames, X509NameRef};

    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: &GeneralNames) -> 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.text_by_nid(nid::COMMONNAME) {
            // 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,
        }
    }
}
+104 −4
Original line number Diff line number Diff line
//! SSL/TLS support.
//!
//! The `ClientConnector` and `ServerConnector` 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::ClientConnectorBuilder;
//! use std::io::{Read, Write};
//! use std::net::TcpStream;
//!
//! let connector = ClientConnectorBuilder::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::{ServerConnectorBuilder, 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 connector = ServerConnectorBuilder::tls(&identity.pkey, &identity.cert, &identity.chain)
//!     .unwrap()
//!     .build();
//! let connector = Arc::new(connector);
//!
//! 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 connector = connector.clone();
//!             thread::spawn(move || {
//!                 let stream = connector.connect(stream).unwrap();
//!                 handle_client(stream);
//!             });
//!         }
//!         Err(e) => { /* connection failed */ }
//!     }
//! }
//! ```
use libc::{c_int, c_void, c_long, c_ulong};
use std::any::Any;
use std::any::TypeId;
@@ -22,19 +91,22 @@ use ffi;
use {init, cvt, cvt_p};
use dh::Dh;
use x509::{X509StoreContextRef, X509FileType, X509, X509Ref, X509VerifyError};
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
use x509::verify::X509VerifyParamRef;
use pkey::PKey;
#[cfg(any(ossl102, ossl110))]
use verify::X509VerifyParamRef;
use pkey::PKeyRef;
use error::ErrorStack;
use opaque::Opaque;

pub mod error;
mod connector;
mod bio;
#[cfg(test)]
mod tests;

use self::bio::BioMethod;

pub use ssl::connector::{ClientConnectorBuilder, ClientConnector, ServerConnectorBuilder,
                         ServerConnector};
#[doc(inline)]
pub use ssl::error::Error;

@@ -339,6 +411,7 @@ pub enum SniError {
    NoAck,
}

/// A builder for `SslContext`s.
pub struct SslContextBuilder(*mut ffi::SSL_CTX);

impl Drop for SslContextBuilder {
@@ -529,7 +602,7 @@ impl SslContextBuilder {
    }

    /// Specifies the private key
    pub fn set_private_key(&mut self, key: &PKey) -> Result<(), ErrorStack> {
    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(|_| ())
        }
@@ -790,6 +863,7 @@ impl SslCipherRef {
    }
}

/// A reference to an `Ssl`.
pub struct SslRef(Opaque);

unsafe impl Send for SslRef {}
@@ -1030,6 +1104,11 @@ impl SslRef {
    /// 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()
    }

    #[cfg(any(ossl102, ossl110))]
    fn _param_mut(&mut self) -> &mut X509VerifyParamRef {
        unsafe {
            X509VerifyParamRef::from_ptr_mut(ffi::SSL_get0_param(self.as_ptr()))
        }
@@ -1096,6 +1175,11 @@ impl Ssl {
    }

    /// Creates an SSL/TLS client operating over the provided stream.
    ///
    /// # Warning
    ///
    /// OpenSSL's default configuration is insecure. It is highly recommended to use
    /// `ClientConnector` 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
    {
@@ -1123,6 +1207,11 @@ impl Ssl {
    }

    /// Creates an SSL/TLS server operating over the provided stream.
    ///
    /// # Warning
    ///
    /// OpenSSL's default configuration is insecure. It is highly recommended to use
    /// `ServerConnector` 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
    {
@@ -1153,6 +1242,8 @@ impl Ssl {
/// 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.
@@ -1162,6 +1253,7 @@ pub enum HandshakeError<S> {
impl<S: Any + fmt::Debug> stderror::Error 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",
        }
@@ -1169,6 +1261,7 @@ impl<S: Any + fmt::Debug> stderror::Error for HandshakeError<S> {

    fn cause(&self) -> Option<&stderror::Error> {
        match *self {
            HandshakeError::SetupFailure(ref e) => Some(e),
            HandshakeError::Failure(ref s) | HandshakeError::Interrupted(ref s) => Some(s.error()),
        }
    }
@@ -1178,6 +1271,7 @@ 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)));
        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() {
@@ -1189,6 +1283,12 @@ impl<S: Any + fmt::Debug> fmt::Display for HandshakeError<S> {
    }
}

impl<S> From<ErrorStack> for HandshakeError<S> {
    fn from(e: ErrorStack) -> HandshakeError<S> {
        HandshakeError::SetupFailure(e)
    }
}

/// An SSL stream midway through the handshake process.
#[derive(Debug)]
pub struct MidHandshakeSslStream<S> {
Loading