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

Add min/max protocol version support
This commit is contained in:
Steven Fackler 2018-02-26 11:39:31 -08:00 committed by GitHub
commit 42ec251b55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 196 additions and 15 deletions

View File

@ -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(

View File

@ -1,11 +1,15 @@
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;
}

View File

@ -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.

View File

@ -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>() {}