From aeefa364b7fd386584d90040592ae9b4b3dc42b4 Mon Sep 17 00:00:00 2001 From: Jethro Beekman Date: Wed, 1 Jul 2015 21:49:11 -0700 Subject: [PATCH] Decouple C SSL Option bit flags from Rust version The OpenSSL "SSL_OP_*" flags are in constant flux between different OpenSSL versions. To avoid having to change the Rust definitions, we implement our own numbering system in Rust, and use an automatically-generated C shim to convert the bitflags at runtime. --- openssl-sys/build.rs | 64 +++++++++++++++++++++++++++++ openssl-sys/src/lib.rs | 32 ++++++++++++--- openssl-sys/src/ssl_options.rs | 46 +++++++++++++++++++++ openssl/src/ssl/mod.rs | 75 +++++++++++++++++++++------------- 4 files changed, 182 insertions(+), 35 deletions(-) create mode 100644 openssl-sys/src/ssl_options.rs diff --git a/openssl-sys/build.rs b/openssl-sys/build.rs index c1f120344..841c7eec9 100644 --- a/openssl-sys/build.rs +++ b/openssl-sys/build.rs @@ -2,7 +2,10 @@ extern crate pkg_config; extern crate gcc; use std::env; +use std::fmt::Write as FmtWrite; use std::path::PathBuf; +use std::fs::File; +use std::io::Write; fn main() { let target = env::var("TARGET").unwrap(); @@ -65,7 +68,67 @@ fn main() { build_openssl_shim(&include_dirs); } +macro_rules! import_options { + ( $( $name:ident $val:expr )* ) => { + &[ $( (stringify!($name),$val), )* ] + }; +} + +fn generate_options_shim() -> PathBuf { + let options: &[(&'static str,u64)]=include!("src/ssl_options.rs"); + let mut shim = String::new(); + writeln!(shim,"#include ").unwrap(); + writeln!(shim,"#include ").unwrap(); + + for &(name,value) in options { + writeln!(shim,"#define RUST_{} UINT64_C({})",name,value).unwrap(); + writeln!(shim,"#ifndef {}",name).unwrap(); + writeln!(shim,"# define {} 0",name).unwrap(); + writeln!(shim,"#endif").unwrap(); + } + + writeln!(shim,"#define COPY_MASK ( \\").unwrap(); + + let mut it=options.iter().peekable(); + while let Some(&(name,_))=it.next() { + let eol=match it.peek() { + Some(_) => " | \\", + None => " )" + }; + writeln!(shim," ((RUST_{0}==(uint64_t)(uint32_t){0})?RUST_{0}:UINT64_C(0)){1}",name,eol).unwrap(); + } + + writeln!(shim,"long rust_openssl_ssl_ctx_options_rust_to_c(uint64_t rustval) {{").unwrap(); + writeln!(shim," long cval=rustval©_MASK;").unwrap(); + for &(name,_) in options { + writeln!(shim,"#if RUST_{0}!={0}",name).unwrap(); + writeln!(shim," if (rustval&RUST_{0}) cval|={0};",name).unwrap(); + writeln!(shim,"#endif").unwrap(); + } + writeln!(shim," return cval;").unwrap(); + writeln!(shim,"}}").unwrap(); + + writeln!(shim,"uint64_t rust_openssl_ssl_ctx_options_c_to_rust(long cval) {{").unwrap(); + writeln!(shim," uint64_t rustval=cval©_MASK;").unwrap(); + for &(name,_) in options { + writeln!(shim,"#if RUST_{0}!={0}",name).unwrap(); + writeln!(shim," if (cval&{0}) rustval|=RUST_{0};",name).unwrap(); + writeln!(shim,"#endif").unwrap(); + } + writeln!(shim," return rustval;").unwrap(); + writeln!(shim,"}}").unwrap(); + + let out_dir = env::var("OUT_DIR").unwrap(); + let dest_file = PathBuf::from(&out_dir).join("ssl_ctx_options_shim.c"); + let mut f = File::create(&dest_file).unwrap(); + + f.write_all(shim.as_bytes()).unwrap(); + + dest_file +} + fn build_openssl_shim(include_paths: &[PathBuf]) { + let options_shim_file = generate_options_shim(); let mut config = gcc::Config::new(); for path in include_paths { @@ -73,6 +136,7 @@ fn build_openssl_shim(include_paths: &[PathBuf]) { } config.file("src/openssl_shim.c") + .file(options_shim_file) .compile("libopenssl_shim.a"); } diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index 9f2041a4b..08b2199d7 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -155,6 +155,14 @@ pub const SSL_TLSEXT_ERR_ALERT_WARNING: c_int = 1; pub const SSL_TLSEXT_ERR_ALERT_FATAL: c_int = 2; pub const SSL_TLSEXT_ERR_NOACK: c_int = 3; +macro_rules! import_options { + ( $( $name:ident $val:expr )* ) => { + $( pub const $name: u64 = $val; )* + }; +} + +include!("ssl_options.rs"); + #[cfg(feature = "npn")] pub const OPENSSL_NPN_UNSUPPORTED: c_int = 0; #[cfg(feature = "npn")] @@ -262,8 +270,23 @@ pub fn init() { } } +pub unsafe fn SSL_CTX_set_options(ssl: *mut SSL_CTX, op: u64) -> u64 { + rust_openssl_ssl_ctx_options_c_to_rust(SSL_CTX_set_options_shim(ssl, rust_openssl_ssl_ctx_options_rust_to_c(op))) +} + +pub unsafe fn SSL_CTX_get_options(ssl: *mut SSL_CTX) -> u64 { + rust_openssl_ssl_ctx_options_c_to_rust(SSL_CTX_get_options_shim(ssl)) +} + +pub unsafe fn SSL_CTX_clear_options(ssl: *mut SSL_CTX, op: u64) -> u64 { + rust_openssl_ssl_ctx_options_c_to_rust(SSL_CTX_clear_options_shim(ssl, rust_openssl_ssl_ctx_options_rust_to_c(op))) +} + // True functions extern "C" { + fn rust_openssl_ssl_ctx_options_rust_to_c(rustval: u64) -> c_long; + fn rust_openssl_ssl_ctx_options_c_to_rust(cval: c_long) -> u64; + pub fn ASN1_INTEGER_set(dest: *mut ASN1_INTEGER, value: c_long) -> c_int; pub fn ASN1_STRING_type_new(ty: c_int) -> *mut ASN1_STRING; pub fn ASN1_TIME_free(tm: *mut ASN1_TIME); @@ -615,12 +638,9 @@ extern "C" { pub fn BIO_eof(b: *mut BIO) -> c_int; #[link_name = "BIO_set_mem_eof_return_shim"] pub fn BIO_set_mem_eof_return(b: *mut BIO, v: c_int); - #[link_name = "SSL_CTX_set_options_shim"] - pub fn SSL_CTX_set_options(ctx: *mut SSL_CTX, options: c_long) -> c_long; - #[link_name = "SSL_CTX_get_options_shim"] - pub fn SSL_CTX_get_options(ctx: *mut SSL_CTX) -> c_long; - #[link_name = "SSL_CTX_clear_options_shim"] - pub fn SSL_CTX_clear_options(ctx: *mut SSL_CTX, options: c_long) -> c_long; + pub fn SSL_CTX_set_options_shim(ctx: *mut SSL_CTX, options: c_long) -> c_long; + pub fn SSL_CTX_get_options_shim(ctx: *mut SSL_CTX) -> c_long; + pub fn SSL_CTX_clear_options_shim(ctx: *mut SSL_CTX, options: c_long) -> c_long; #[link_name = "SSL_CTX_add_extra_chain_cert_shim"] pub fn SSL_CTX_add_extra_chain_cert(ctx: *mut SSL_CTX, x509: *mut X509) -> c_long; #[link_name = "SSL_CTX_set_read_ahead_shim"] diff --git a/openssl-sys/src/ssl_options.rs b/openssl-sys/src/ssl_options.rs new file mode 100644 index 000000000..a1c778acf --- /dev/null +++ b/openssl-sys/src/ssl_options.rs @@ -0,0 +1,46 @@ +import_options!{ +// The following values are directly from recent OpenSSL +SSL_OP_MICROSOFT_SESS_ID_BUG 0x00000001 +SSL_OP_NETSCAPE_CHALLENGE_BUG 0x00000002 +SSL_OP_LEGACY_SERVER_CONNECT 0x00000004 +SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG 0x00000008 +SSL_OP_TLSEXT_PADDING 0x00000010 +SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER 0x00000020 +SSL_OP_SAFARI_ECDHE_ECDSA_BUG 0x00000040 +SSL_OP_SSLEAY_080_CLIENT_DH_BUG 0x00000080 +SSL_OP_TLS_D5_BUG 0x00000100 +SSL_OP_TLS_BLOCK_PADDING_BUG 0x00000200 +// unused: 0x00000400 +SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS 0x00000800 +SSL_OP_NO_QUERY_MTU 0x00001000 +SSL_OP_COOKIE_EXCHANGE 0x00002000 +SSL_OP_NO_TICKET 0x00004000 +SSL_OP_CISCO_ANYCONNECT 0x00008000 +SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION 0x00010000 +SSL_OP_NO_COMPRESSION 0x00020000 +SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0x00040000 +SSL_OP_SINGLE_ECDH_USE 0x00080000 +SSL_OP_SINGLE_DH_USE 0x00100000 +// unused: 0x00200000 +SSL_OP_CIPHER_SERVER_PREFERENCE 0x00400000 +SSL_OP_TLS_ROLLBACK_BUG 0x00800000 +SSL_OP_NO_SSLv2 0x01000000 +SSL_OP_NO_SSLv3 0x02000000 +SSL_OP_NO_DTLSv1 0x04000000 +SSL_OP_NO_TLSv1 0x04000000 +SSL_OP_NO_DTLSv1_2 0x08000000 +SSL_OP_NO_TLSv1_2 0x08000000 +SSL_OP_NO_TLSv1_1 0x10000000 +SSL_OP_NETSCAPE_CA_DN_BUG 0x20000000 +SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG 0x40000000 +SSL_OP_CRYPTOPRO_TLSEXT_BUG 0x80000000 + +// The following values were in 32-bit range in old OpenSSL +SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG 0x100000000 +SSL_OP_MSIE_SSLV2_RSA_PADDING 0x200000000 +SSL_OP_PKCS1_CHECK_1 0x400000000 +SSL_OP_PKCS1_CHECK_2 0x800000000 + +// The following values were redefined to 0 for security reasons +SSL_OP_EPHEMERAL_RSA 0x0 +} diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 88ba9af44..1338b1cb8 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -46,35 +46,52 @@ fn init() { } bitflags! { - flags SslContextOptions: c_long { - const SSL_OP_LEGACY_SERVER_CONNECT = 0x00000004, - const SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG = 0x00000008, - const SSL_OP_TLSEXT_PADDING = 0x00000010, - const SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER = 0x00000020, - const SSL_OP_SAFARI_ECDHE_ECDSA_BUG = 0x00000040, - const SSL_OP_SSLEAY_080_CLIENT_DH_BUG = 0x00000080, - const SSL_OP_TLS_D5_BUG = 0x00000100, - const SSL_OP_TLS_BLOCK_PADDING_BUG = 0x00000200, - const SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS = 0x00000800, - const SSL_OP_ALL = 0x80000BFF, - const SSL_OP_NO_QUERY_MTU = 0x00001000, - const SSL_OP_COOKIE_EXCHANGE = 0x00002000, - const SSL_OP_NO_TICKET = 0x00004000, - const SSL_OP_CISCO_ANYCONNECT = 0x00008000, - const SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = 0x00010000, - const SSL_OP_NO_COMPRESSION = 0x00020000, - const SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = 0x00040000, - const SSL_OP_SINGLE_ECDH_USE = 0x00080000, - const SSL_OP_SINGLE_DH_USE = 0x00100000, - const SSL_OP_CIPHER_SERVER_PREFERENCE = 0x00400000, - const SSL_OP_TLS_ROLLBACK_BUG = 0x00800000, - const SSL_OP_NO_SSLV2 = 0x00000000, - const SSL_OP_NO_SSLV3 = 0x02000000, - const SSL_OP_NO_TLSV1 = 0x04000000, - const SSL_OP_NO_TLSV1_2 = 0x08000000, - const SSL_OP_NO_TLSV1_1 = 0x10000000, - const SSL_OP_NO_DTLSV1 = 0x04000000, - const SSL_OP_NO_DTLSV1_2 = 0x08000000 + flags SslContextOptions: u64 { + 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_LEGACY_SERVER_CONNECT = ffi::SSL_OP_LEGACY_SERVER_CONNECT, + 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_SAFARI_ECDHE_ECDSA_BUG = ffi::SSL_OP_SAFARI_ECDHE_ECDSA_BUG, + 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, + const SSL_OP_TLS_BLOCK_PADDING_BUG = ffi::SSL_OP_TLS_BLOCK_PADDING_BUG, + const SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS = ffi::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS, + const SSL_OP_NO_QUERY_MTU = ffi::SSL_OP_NO_QUERY_MTU, + const SSL_OP_COOKIE_EXCHANGE = ffi::SSL_OP_COOKIE_EXCHANGE, + const SSL_OP_NO_TICKET = ffi::SSL_OP_NO_TICKET, + const SSL_OP_CISCO_ANYCONNECT = ffi::SSL_OP_CISCO_ANYCONNECT, + const SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = ffi::SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION, + const SSL_OP_NO_COMPRESSION = ffi::SSL_OP_NO_COMPRESSION, + const SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = ffi::SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, + const SSL_OP_SINGLE_ECDH_USE = ffi::SSL_OP_SINGLE_ECDH_USE, + const SSL_OP_SINGLE_DH_USE = ffi::SSL_OP_SINGLE_DH_USE, + const SSL_OP_CIPHER_SERVER_PREFERENCE = ffi::SSL_OP_CIPHER_SERVER_PREFERENCE, + const SSL_OP_TLS_ROLLBACK_BUG = ffi::SSL_OP_TLS_ROLLBACK_BUG, + const SSL_OP_NO_SSLV2 = ffi::SSL_OP_NO_SSLv2, + const SSL_OP_NO_SSLV3 = ffi::SSL_OP_NO_SSLv3, + const SSL_OP_NO_DTLSV1 = ffi::SSL_OP_NO_DTLSv1, + const SSL_OP_NO_TLSV1 = ffi::SSL_OP_NO_TLSv1, + const SSL_OP_NO_DTLSV1_2 = ffi::SSL_OP_NO_DTLSv1_2, + const SSL_OP_NO_TLSV1_2 = ffi::SSL_OP_NO_TLSv1_2, + const SSL_OP_NO_TLSV1_1 = ffi::SSL_OP_NO_TLSv1_1, + const SSL_OP_NETSCAPE_CA_DN_BUG = ffi::SSL_OP_NETSCAPE_CA_DN_BUG, + const SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG = ffi::SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG, + const SSL_OP_CRYPTOPRO_TLSEXT_BUG = ffi::SSL_OP_CRYPTOPRO_TLSEXT_BUG, + const SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG = ffi::SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG, + const SSL_OP_MSIE_SSLV2_RSA_PADDING = ffi::SSL_OP_MSIE_SSLV2_RSA_PADDING, + const SSL_OP_PKCS1_CHECK_1 = ffi::SSL_OP_PKCS1_CHECK_1, + const SSL_OP_PKCS1_CHECK_2 = ffi::SSL_OP_PKCS1_CHECK_2, + const SSL_OP_EPHEMERAL_RSA = ffi::SSL_OP_EPHEMERAL_RSA, + const SSL_OP_ALL = SSL_OP_MICROSOFT_SESS_ID_BUG.bits|SSL_OP_NETSCAPE_CHALLENGE_BUG.bits + |SSL_OP_LEGACY_SERVER_CONNECT.bits|SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG.bits + |SSL_OP_TLSEXT_PADDING.bits|SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER.bits + |SSL_OP_SAFARI_ECDHE_ECDSA_BUG.bits|SSL_OP_SSLEAY_080_CLIENT_DH_BUG.bits + |SSL_OP_TLS_D5_BUG.bits|SSL_OP_TLS_BLOCK_PADDING_BUG.bits + |SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS.bits|SSL_OP_CRYPTOPRO_TLSEXT_BUG.bits, + const SSL_OP_NO_SSL_MASK = SSL_OP_NO_SSLV2.bits|SSL_OP_NO_SSLV3.bits|SSL_OP_NO_TLSV1.bits + |SSL_OP_NO_TLSV1_1.bits|SSL_OP_NO_TLSV1_2.bits, } } -- GitLab