Unverified Commit 42ec251b authored by Steven Fackler's avatar Steven Fackler Committed by GitHub
Browse files

Merge pull request #853 from sfackler/min-max-version

Add min/max protocol version support
parents d5dd6575 b7ba5773
Loading
Loading
Loading
Loading
+56 −8
Original line number Diff line number Diff line
@@ -33,9 +33,10 @@ pub enum X509_ALGOR {}
pub enum X509_VERIFY_PARAM {}
pub enum X509_REQ {}

#[cfg(ossl111)]
pub type SSL_CTX_keylog_cb_func =
    Option<unsafe extern "C" fn(ssl: *const SSL, line: *const c_char)>;
pub const SSL_CTRL_SET_MIN_PROTO_VERSION: c_int = 123;
pub const SSL_CTRL_SET_MAX_PROTO_VERSION: c_int = 124;
pub const SSL_CTRL_GET_MIN_PROTO_VERSION: c_int = 130;
pub const SSL_CTRL_GET_MAX_PROTO_VERSION: c_int = 131;

pub const SSL_OP_MICROSOFT_SESS_ID_BUG: c_ulong = 0x00000000;
pub const SSL_OP_NETSCAPE_CHALLENGE_BUG: c_ulong = 0x00000000;
@@ -48,9 +49,6 @@ pub const SSL_OP_SINGLE_ECDH_USE: c_ulong = 0x00000000;
pub const SSL_OP_SINGLE_DH_USE: c_ulong = 0x00000000;
pub const SSL_OP_NO_SSLv2: c_ulong = 0x00000000;

#[cfg(ossl111)]
pub const TLS1_3_VERSION: c_int = 0x304;

pub const OPENSSL_VERSION: c_int = 0;
pub const OPENSSL_CFLAGS: c_int = 1;
pub const OPENSSL_BUILT_ON: c_int = 2;
@@ -81,6 +79,58 @@ pub fn init() {
    })
}

pub unsafe fn SSL_CTX_set_min_proto_version(ctx: *mut ::SSL_CTX, version: c_int) -> c_int {
    ::SSL_CTX_ctrl(
        ctx,
        SSL_CTRL_SET_MIN_PROTO_VERSION,
        version as c_long,
        ptr::null_mut(),
    ) as c_int
}

pub unsafe fn SSL_CTX_set_max_proto_version(ctx: *mut ::SSL_CTX, version: c_int) -> c_int {
    ::SSL_CTX_ctrl(
        ctx,
        SSL_CTRL_SET_MAX_PROTO_VERSION,
        version as c_long,
        ptr::null_mut(),
    ) as c_int
}

pub unsafe fn SSL_CTX_get_min_proto_version(ctx: *mut ::SSL_CTX) -> c_int {
    ::SSL_CTX_ctrl(ctx, SSL_CTRL_GET_MIN_PROTO_VERSION, 0, ptr::null_mut()) as c_int
}

pub unsafe fn SSL_CTX_get_max_proto_version(ctx: *mut ::SSL_CTX) -> c_int {
    ::SSL_CTX_ctrl(ctx, SSL_CTRL_GET_MAX_PROTO_VERSION, 0, ptr::null_mut()) as c_int
}

pub unsafe fn SSL_set_min_proto_version(s: *mut ::SSL, version: c_int) -> c_int {
    ::SSL_ctrl(
        s,
        SSL_CTRL_SET_MIN_PROTO_VERSION,
        version as c_long,
        ptr::null_mut(),
    ) as c_int
}

pub unsafe fn SSL_set_max_proto_version(s: *mut ::SSL, version: c_int) -> c_int {
    ::SSL_ctrl(
        s,
        SSL_CTRL_SET_MAX_PROTO_VERSION,
        version as c_long,
        ptr::null_mut(),
    ) as c_int
}

pub unsafe fn SSL_get_min_proto_version(s: *mut ::SSL) -> c_int {
    ::SSL_ctrl(s, SSL_CTRL_GET_MIN_PROTO_VERSION, 0, ptr::null_mut()) as c_int
}

pub unsafe fn SSL_get_max_proto_version(s: *mut ::SSL) -> c_int {
    ::SSL_ctrl(s, SSL_CTRL_GET_MAX_PROTO_VERSION, 0, ptr::null_mut()) as c_int
}

extern "C" {
    pub fn BIO_new(type_: *const BIO_METHOD) -> *mut BIO;
    pub fn BIO_s_file() -> *const BIO_METHOD;
@@ -221,8 +271,6 @@ extern "C" {
    );
    pub fn SSL_get_client_random(ssl: *const SSL, out: *mut c_uchar, len: size_t) -> size_t;
    pub fn SSL_get_server_random(ssl: *const SSL, out: *mut c_uchar, len: size_t) -> size_t;
    #[cfg(ossl111)]
    pub fn SSL_CTX_set_keylog_callback(ctx: *mut ::SSL_CTX, cb: SSL_CTX_keylog_cb_func);
    pub fn X509_getm_notAfter(x: *const ::X509) -> *mut ::ASN1_TIME;
    pub fn X509_getm_notBefore(x: *const ::X509) -> *mut ::ASN1_TIME;
    pub fn X509_get0_signature(
+7 −3
Original line number Diff line number Diff line
use libc::{c_int, c_ulong};
use libc::{c_char, c_int, c_ulong};

use ossl110::*;
pub type SSL_CTX_keylog_cb_func =
    Option<unsafe extern "C" fn(ssl: *const ::SSL, line: *const c_char)>;

pub const SSL_COOKIE_LENGTH: c_int = 255;

pub const SSL_OP_ENABLE_MIDDLEBOX_COMPAT: c_ulong = 0x00100000;

pub const TLS1_3_VERSION: c_int = 0x304;

extern "C" {
    pub fn SSL_stateless(s: *mut SSL) -> c_int;
    pub fn SSL_CTX_set_keylog_callback(ctx: *mut ::SSL_CTX, cb: SSL_CTX_keylog_cb_func);
    pub fn SSL_stateless(s: *mut ::SSL) -> c_int;
}
+100 −4
Original line number Diff line number Diff line
@@ -1007,6 +1007,11 @@ impl SslContextBuilder {
    ///
    /// This corresponds to [`SSL_CTX_set_options`].
    ///
    /// # Note
    ///
    /// This *enables* the specified options, but does not disable unspecified options. Use
    /// `clear_options` for that.
    ///
    /// [`SSL_CTX_set_options`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_options.html
    pub fn set_options(&mut self, option: SslOptions) -> SslOptions {
        let bits = unsafe { compat::SSL_CTX_set_options(self.as_ptr(), option.bits()) };
@@ -1033,6 +1038,90 @@ impl SslContextBuilder {
        SslOptions { bits }
    }

    /// Sets the minimum supported protocol version.
    ///
    /// A value of `None` will enable protocol versions down the the lowest version supported by
    /// OpenSSL.
    ///
    /// This corresponds to [`SSL_CTX_set_min_proto_version`].
    ///
    /// Requires OpenSSL 1.1.0 or 1.1.1 and the corresponding Cargo feature.
    ///
    /// [`SSL_CTX_set_min_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html
    #[cfg(any(all(feature = "v110", ossl110), all(feature = "v111", ossl111)))]
    pub fn set_min_proto_version(&mut self, version: Option<SslVersion>) -> Result<(), ErrorStack> {
        unsafe {
            cvt(ffi::SSL_CTX_set_min_proto_version(
                self.as_ptr(),
                version.map_or(0, |v| v.0),
            )).map(|_| ())
        }
    }

    /// Sets the maximum supported protocol version.
    ///
    /// A value of `None` will enable protocol versions down the the highest version supported by
    /// OpenSSL.
    ///
    /// This corresponds to [`SSL_CTX_set_max_proto_version`].
    ///
    /// Requires OpenSSL 1.1.0 or 1.1.1 and the corresponding Cargo feature.
    ///
    /// [`SSL_CTX_set_max_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html
    #[cfg(any(all(feature = "v110", ossl110), all(feature = "v111", ossl111)))]
    pub fn set_max_proto_version(&mut self, version: Option<SslVersion>) -> Result<(), ErrorStack> {
        unsafe {
            cvt(ffi::SSL_CTX_set_max_proto_version(
                self.as_ptr(),
                version.map_or(0, |v| v.0),
            )).map(|_| ())
        }
    }

    /// Gets the minimum supported protocol version.
    ///
    /// A value of `None` indicates that all versions down the the lowest version supported by
    /// OpenSSL are enabled.
    ///
    /// This corresponds to [`SSL_CTX_get_min_proto_version`].
    ///
    /// Requires OpenSSL 1.1.0 or 1.1.1 and the corresponding Cargo feature.
    ///
    /// [`SSL_CTX_get_min_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html
    #[cfg(any(all(feature = "v110", ossl110), all(feature = "v111", ossl111)))]
    pub fn min_proto_version(&mut self) -> Option<SslVersion> {
        unsafe {
            let r = ffi::SSL_CTX_get_min_proto_version(self.as_ptr());
            if r == 0 {
                None
            } else {
                Some(SslVersion(r))
            }
        }
    }

    /// Gets the maximum supported protocol version.
    ///
    /// A value of `None` indicates that all versions down the the highest version supported by
    /// OpenSSL are enabled.
    ///
    /// This corresponds to [`SSL_CTX_get_max_proto_version`].
    ///
    /// Requires OpenSSL 1.1.0 or 1.1.1 and the corresponding Cargo feature.
    ///
    /// [`SSL_CTX_get_max_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html
    #[cfg(any(all(feature = "v110", ossl110), all(feature = "v111", ossl111)))]
    pub fn max_proto_version(&mut self) -> Option<SslVersion> {
        unsafe {
            let r = ffi::SSL_CTX_get_max_proto_version(self.as_ptr());
            if r == 0 {
                None
            } else {
                Some(SslVersion(r))
            }
        }
    }

    /// Sets the protocols to sent to the server for Application Layer Protocol Negotiation (ALPN).
    ///
    /// The input must be in ALPN "wire format". It consists of a sequence of supported protocol
@@ -1319,7 +1408,7 @@ impl SslContextBuilder {
    /// This corresponds to `SSL_CTX_set_cookie_generate_cb`.
    pub fn set_cookie_generate_cb<F>(&mut self, callback: F)
    where
        F: Fn(&mut SslRef, &mut [u8]) -> Result<usize, ErrorStack> + 'static + Sync + Send
        F: Fn(&mut SslRef, &mut [u8]) -> Result<usize, ErrorStack> + 'static + Sync + Send,
    {
        unsafe {
            let callback = Box::new(callback);
@@ -1343,7 +1432,7 @@ impl SslContextBuilder {
    /// This corresponds to `SSL_CTX_set_cookie_verify_cb`.
    pub fn set_cookie_verify_cb<F>(&mut self, callback: F)
    where
        F: Fn(&mut SslRef, &[u8]) -> bool + 'static + Sync + Send
        F: Fn(&mut SslRef, &[u8]) -> bool + 'static + Sync + Send,
    {
        unsafe {
            let callback = Box::new(callback);
@@ -2011,8 +2100,15 @@ impl SslRef {
    /// This corresponds to [`SSL_version`].
    ///
    /// [`SSL_version`]: https://www.openssl.org/docs/manmaster/man3/SSL_version.html
    pub fn version2(&self) -> SslVersion {
        unsafe { SslVersion(ffi::SSL_version(self.as_ptr())) }
    pub fn version2(&self) -> Option<SslVersion> {
        unsafe {
            let r = ffi::SSL_version(self.as_ptr());
            if r == 0 {
                None
            } else {
                Some(SslVersion(r))
            }
        }
    }

    /// Returns a string describing the protocol version of the session.
+33 −0
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ use ocsp::{OcspResponse, OcspResponseStatus};
use ssl;
use ssl::{Error, HandshakeError, ShutdownResult, Ssl, SslAcceptor, SslConnector, SslContext,
          SslFiletype, SslMethod, SslSessionCacheMode, SslStream, SslVerifyMode, StatusType};
#[cfg(any(all(feature = "v110", ossl110), all(feature = "v111", ossl111)))]
use ssl::SslVersion;
use x509::{X509, X509Name, X509StoreContext, X509VerifyResult};
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110),
          all(feature = "v111", ossl111)))]
@@ -1320,6 +1322,37 @@ fn keying_export() {
    assert_eq!(buf, buf2);
}

#[test]
#[cfg(any(all(feature = "v110", ossl110), all(feature = "v111", ossl111)))]
fn no_version_overlap() {
    let listener = TcpListener::bind("127.0.0.1:0").unwrap();
    let addr = listener.local_addr().unwrap();

    let guard = thread::spawn(move || {
        let stream = listener.accept().unwrap().0;
        let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
        ctx.set_certificate_file(&Path::new("test/cert.pem"), SslFiletype::PEM)
            .unwrap();
        ctx.set_private_key_file(&Path::new("test/key.pem"), SslFiletype::PEM)
            .unwrap();
        ctx.set_max_proto_version(Some(SslVersion::TLS1_1)).unwrap();
        assert_eq!(ctx.min_proto_version(), None);
        assert_eq!(ctx.max_proto_version(), Some(SslVersion::TLS1_1));
        let ssl = Ssl::new(&ctx.build()).unwrap();
        ssl.accept(stream).unwrap_err();
    });

    let stream = TcpStream::connect(addr).unwrap();
    let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
    ctx.set_min_proto_version(Some(SslVersion::TLS1_2)).unwrap();
    assert_eq!(ctx.min_proto_version(), Some(SslVersion::TLS1_2));
    assert_eq!(ctx.max_proto_version(), None);
    let ssl = Ssl::new(&ctx.build()).unwrap();
    ssl.connect(stream).unwrap_err();

    guard.join().unwrap();
}

fn _check_kinds() {
    fn is_send<T: Send>() {}
    fn is_sync<T: Sync>() {}