From d991566f2b1b6803ad214fe6bf531d5870ab43fd Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 19 May 2018 19:43:02 -0700 Subject: Support min/max version in LibreSSL Their implementations of the accessors don't behave expected with no bounds, so we ignore those bits of the tests. --- openssl-sys/Cargo.toml | 1 + openssl-sys/build.rs | 584 --------------------------------------- openssl-sys/build/cfgs.rs | 45 +++ openssl-sys/build/main.rs | 572 ++++++++++++++++++++++++++++++++++++++ openssl-sys/src/lib.rs | 27 +- openssl-sys/src/libressl/mod.rs | 32 ++- openssl-sys/src/libressl/v251.rs | 89 ++++++ openssl-sys/src/libressl/v25x.rs | 89 ------ openssl/build.rs | 12 + openssl/src/ec.rs | 16 +- openssl/src/rsa.rs | 11 +- openssl/src/ssl/mod.rs | 26 +- openssl/src/ssl/test.rs | 8 +- openssl/src/x509/tests.rs | 13 +- systest/build.rs | 43 +-- 15 files changed, 821 insertions(+), 747 deletions(-) delete mode 100644 openssl-sys/build.rs create mode 100644 openssl-sys/build/cfgs.rs create mode 100644 openssl-sys/build/main.rs create mode 100644 openssl-sys/src/libressl/v251.rs delete mode 100644 openssl-sys/src/libressl/v25x.rs diff --git a/openssl-sys/Cargo.toml b/openssl-sys/Cargo.toml index 1153dd5a..3b76c416 100644 --- a/openssl-sys/Cargo.toml +++ b/openssl-sys/Cargo.toml @@ -9,6 +9,7 @@ repository = "https://github.com/sfackler/rust-openssl" readme = "README.md" categories = ["cryptography", "external-ffi-bindings"] links = "openssl" +build = "build/main.rs" [dependencies] libc = "0.2" diff --git a/openssl-sys/build.rs b/openssl-sys/build.rs deleted file mode 100644 index c5630556..00000000 --- a/openssl-sys/build.rs +++ /dev/null @@ -1,584 +0,0 @@ -extern crate cc; -extern crate pkg_config; -#[cfg(target_env = "msvc")] -extern crate vcpkg; - -use std::collections::HashSet; -use std::env; -use std::ffi::OsString; -use std::fs::File; -use std::io::{BufWriter, Write}; -use std::path::{Path, PathBuf}; -use std::process::Command; - -// The set of `OPENSSL_NO_`s that we care about. -const DEFINES: &'static [&'static str] = &[ - "OPENSSL_NO_BUF_FREELISTS", - "OPENSSL_NO_COMP", - "OPENSSL_NO_EC", - "OPENSSL_NO_EC2M", - "OPENSSL_NO_ENGINE", - "OPENSSL_NO_KRB5", - "OPENSSL_NO_NEXTPROTONEG", - "OPENSSL_NO_PSK", - "OPENSSL_NO_RFC3779", - "OPENSSL_NO_SHA", - "OPENSSL_NO_SRP", - "OPENSSL_NO_SSL3_METHOD", - "OPENSSL_NO_TLSEXT", -]; - -enum Version { - Openssl11x, - Openssl10x, - Libressl, -} - -fn env(name: &str) -> Option { - let prefix = env::var("TARGET").unwrap().to_uppercase().replace("-", "_"); - let prefixed = format!("{}_{}", prefix, name); - println!("cargo:rerun-if-env-changed={}", prefixed); - - if let Some(var) = env::var_os(&prefixed) { - return Some(var); - } - - println!("cargo:rerun-if-env-changed={}", name); - env::var_os(name) -} - -fn main() { - let target = env::var("TARGET").unwrap(); - - let lib_dir = env("OPENSSL_LIB_DIR").map(PathBuf::from); - let include_dir = env("OPENSSL_INCLUDE_DIR").map(PathBuf::from); - - let (lib_dir, include_dir) = if lib_dir.is_none() || include_dir.is_none() { - let openssl_dir = env("OPENSSL_DIR").unwrap_or_else(|| find_openssl_dir(&target)); - let openssl_dir = Path::new(&openssl_dir); - let lib_dir = lib_dir.unwrap_or_else(|| openssl_dir.join("lib")); - let include_dir = include_dir.unwrap_or_else(|| openssl_dir.join("include")); - (lib_dir, include_dir) - } else { - (lib_dir.unwrap(), include_dir.unwrap()) - }; - - if !Path::new(&lib_dir).exists() { - panic!( - "OpenSSL library directory does not exist: {}", - lib_dir.to_string_lossy() - ); - } - if !Path::new(&include_dir).exists() { - panic!( - "OpenSSL include directory does not exist: {}", - include_dir.to_string_lossy() - ); - } - - println!( - "cargo:rustc-link-search=native={}", - lib_dir.to_string_lossy() - ); - println!("cargo:include={}", include_dir.to_string_lossy()); - - let version = validate_headers(&[include_dir.clone().into()]); - - let libs_env = env("OPENSSL_LIBS"); - let libs = match libs_env.as_ref().and_then(|s| s.to_str()) { - Some(ref v) => v.split(":").collect(), - None => match version { - Version::Openssl10x if target.contains("windows") => vec!["ssleay32", "libeay32"], - Version::Openssl11x if target.contains("windows") => vec!["libssl", "libcrypto"], - _ => vec!["ssl", "crypto"], - }, - }; - - let kind = determine_mode(Path::new(&lib_dir), &libs); - for lib in libs.into_iter() { - println!("cargo:rustc-link-lib={}={}", kind, lib); - } -} - -fn find_openssl_dir(target: &str) -> OsString { - let host = env::var("HOST").unwrap(); - - if host == target && target.contains("apple-darwin") { - // Check up default Homebrew installation location first - // for quick resolution if possible. - let homebrew = Path::new("/usr/local/opt/openssl@1.1"); - if homebrew.exists() { - return homebrew.to_path_buf().into(); - } - let homebrew = Path::new("/usr/local/opt/openssl"); - if homebrew.exists() { - return homebrew.to_path_buf().into(); - } - // Calling `brew --prefix ` command usually slow and - // takes seconds, and will be used only as a last resort. - let output = execute_command_and_get_output("brew", &["--prefix", "openssl@1.1"]); - if let Some(ref output) = output { - let homebrew = Path::new(&output); - if homebrew.exists() { - return homebrew.to_path_buf().into(); - } - } - let output = execute_command_and_get_output("brew", &["--prefix", "openssl"]); - if let Some(ref output) = output { - let homebrew = Path::new(&output); - if homebrew.exists() { - return homebrew.to_path_buf().into(); - } - } - } - - try_pkg_config(); - try_vcpkg(); - - // FreeBSD ships with OpenSSL but doesn't include a pkg-config file :( - if host == target && target.contains("freebsd") { - return OsString::from("/usr"); - } - - let mut msg = format!( - " - -Could not find directory of OpenSSL installation, and this `-sys` crate cannot -proceed without this knowledge. If OpenSSL is installed and this crate had -trouble finding it, you can set the `OPENSSL_DIR` environment variable for the -compilation process. - -Make sure you also have the development packages of openssl installed. -For example, `libssl-dev` on Ubuntu or `openssl-devel` on Fedora. - -If you're in a situation where you think the directory *should* be found -automatically, please open a bug at https://github.com/sfackler/rust-openssl -and include information about your system as well as this message. - - $HOST = {} - $TARGET = {} - openssl-sys = {} - -", - host, - target, - env!("CARGO_PKG_VERSION") - ); - - if host.contains("apple-darwin") && target.contains("apple-darwin") { - let system = Path::new("/usr/lib/libssl.0.9.8.dylib"); - if system.exists() { - msg.push_str(&format!( - " - -It looks like you're compiling on macOS, where the system contains a version of -OpenSSL 0.9.8. This crate no longer supports OpenSSL 0.9.8. - -As a consumer of this crate, you can fix this error by using Homebrew to -install the `openssl` package, or as a maintainer you can use the openssl-sys -0.7 crate for support with OpenSSL 0.9.8. - -Unfortunately though the compile cannot continue, so aborting. - -" - )); - } - } - - if host.contains("unknown-linux") && target.contains("unknown-linux-gnu") { - if Command::new("pkg-config").output().is_err() { - msg.push_str(&format!( - " -It looks like you're compiling on Linux and also targeting Linux. Currently this -requires the `pkg-config` utility to find OpenSSL but unfortunately `pkg-config` -could not be found. If you have OpenSSL installed you can likely fix this by -installing `pkg-config`. - -" - )); - } - } - - if host.contains("windows") && target.contains("windows-gnu") { - msg.push_str(&format!( - " -It looks like you're compiling for MinGW but you may not have either OpenSSL or -pkg-config installed. You can install these two dependencies with: - - pacman -S openssl-devel pkg-config - -and try building this crate again. - -" - )); - } - - if host.contains("windows") && target.contains("windows-msvc") { - msg.push_str(&format!( - " -It looks like you're compiling for MSVC but we couldn't detect an OpenSSL -installation. If there isn't one installed then you can try the rust-openssl -README for more information about how to download precompiled binaries of -OpenSSL: - - https://github.com/sfackler/rust-openssl#windows - -" - )); - } - - panic!(msg); -} - -/// Attempt to find OpenSSL through pkg-config. -/// -/// Note that if this succeeds then the function does not return as pkg-config -/// typically tells us all the information that we need. -fn try_pkg_config() { - let target = env::var("TARGET").unwrap(); - let host = env::var("HOST").unwrap(); - - // If we're going to windows-gnu we can use pkg-config, but only so long as - // we're coming from a windows host. - // - // Otherwise if we're going to windows we probably can't use pkg-config. - if target.contains("windows-gnu") && host.contains("windows") { - env::set_var("PKG_CONFIG_ALLOW_CROSS", "1"); - } else if target.contains("windows") { - return; - } - - let lib = match pkg_config::Config::new() - .print_system_libs(false) - .find("openssl") - { - Ok(lib) => lib, - Err(e) => { - println!("run pkg_config fail: {:?}", e); - return; - } - }; - - validate_headers(&lib.include_paths); - - for include in lib.include_paths.iter() { - println!("cargo:include={}", include.display()); - } - - std::process::exit(0); -} - -/// Attempt to find OpenSSL through vcpkg. -/// -/// Note that if this succeeds then the function does not return as vcpkg -/// should emit all of the cargo metadata that we need. -#[cfg(target_env = "msvc")] -fn try_vcpkg() { - // vcpkg will not emit any metadata if it can not find libraries - // appropriate for the target triple with the desired linkage. - - let mut lib = vcpkg::Config::new() - .emit_includes(true) - .lib_name("libcrypto") - .lib_name("libssl") - .probe("openssl"); - - if let Err(e) = lib { - println!( - "note: vcpkg did not find openssl as libcrypto and libssl : {:?}", - e - ); - lib = vcpkg::Config::new() - .emit_includes(true) - .lib_name("libeay32") - .lib_name("ssleay32") - .probe("openssl"); - } - if let Err(e) = lib { - println!( - "note: vcpkg did not find openssl as ssleay32 and libeay32: {:?}", - e - ); - return; - } - - let lib = lib.unwrap(); - validate_headers(&lib.include_paths); - - println!("cargo:rustc-link-lib=user32"); - println!("cargo:rustc-link-lib=gdi32"); - println!("cargo:rustc-link-lib=crypt32"); - - std::process::exit(0); -} - -#[cfg(not(target_env = "msvc"))] -fn try_vcpkg() {} - -/// Validates the header files found in `include_dir` and then returns the -/// version string of OpenSSL. -fn validate_headers(include_dirs: &[PathBuf]) -> Version { - // This `*-sys` crate only works with OpenSSL 1.0.1, 1.0.2, and 1.1.0. To - // correctly expose the right API from this crate, take a look at - // `opensslv.h` to see what version OpenSSL claims to be. - // - // OpenSSL has a number of build-time configuration options which affect - // various structs and such. Since OpenSSL 1.1.0 this isn't really a problem - // as the library is much more FFI-friendly, but 1.0.{1,2} suffer this problem. - // - // To handle all this conditional compilation we slurp up the configuration - // file of OpenSSL, `opensslconf.h`, and then dump out everything it defines - // as our own #[cfg] directives. That way the `ossl10x.rs` bindings can - // account for compile differences and such. - let mut path = PathBuf::from(env::var_os("OUT_DIR").unwrap()); - path.push("expando.c"); - let mut file = BufWriter::new(File::create(&path).unwrap()); - - write!( - file, - "\ -#include -#include - -#define VERSION2(n, v) RUST_VERSION_ ## n ## _ ## v -#define VERSION(n, v) VERSION2(n, v) - -VERSION(OPENSSL, OPENSSL_VERSION_NUMBER) - -#ifdef LIBRESSL_VERSION_NUMBER -VERSION(LIBRESSL, LIBRESSL_VERSION_NUMBER) -#endif -" - ).unwrap(); - - for define in DEFINES { - write!( - file, - "\ -#ifdef {define} -RUST_CONF_{define} -#endif -", - define = define - ).unwrap(); - } - - file.flush().unwrap(); - drop(file); - - let mut gcc = cc::Build::new(); - for include_dir in include_dirs { - gcc.include(include_dir); - } - // https://github.com/alexcrichton/gcc-rs/issues/133 - let expanded = match gcc.file(&path).try_expand() { - Ok(expanded) => expanded, - Err(e) => { - panic!( - " -Header expansion error: -{:?} - -Failed to find OpenSSL development headers. - -You can try fixing this setting the `OPENSSL_DIR` environment variable -pointing to your OpenSSL installation or installing OpenSSL headers package -specific to your distribution: - - # On Ubuntu - sudo apt-get install libssl-dev - # On Arch Linux - sudo pacman -S openssl - # On Fedora - sudo dnf install openssl-devel - -See rust-openssl README for more information: - - https://github.com/sfackler/rust-openssl#linux -", - e - ); - } - }; - let expanded = String::from_utf8(expanded).unwrap(); - - let mut enabled = vec![]; - let mut openssl_version = None; - let mut libressl_version = None; - for line in expanded.lines() { - let line = line.trim(); - - let openssl_prefix = "RUST_VERSION_OPENSSL_"; - let libressl_prefix = "RUST_VERSION_LIBRESSL_"; - let conf_prefix = "RUST_CONF_"; - if line.starts_with(openssl_prefix) { - let version = &line[openssl_prefix.len()..]; - openssl_version = Some(parse_version(version)); - } else if line.starts_with(libressl_prefix) { - let version = &line[libressl_prefix.len()..]; - libressl_version = Some(parse_version(version)); - } else if line.starts_with(conf_prefix) { - enabled.push(&line[conf_prefix.len()..]); - } - } - - for enabled in &enabled { - println!("cargo:rustc-cfg=osslconf=\"{}\"", enabled); - } - println!("cargo:conf={}", enabled.join(",")); - - if let Some(libressl_version) = libressl_version { - println!("cargo:libressl_version_number={:x}", libressl_version); - - let minor = (libressl_version >> 20) as u8; - let fix = (libressl_version >> 12) as u8; - let (minor, fix) = match (minor, fix) { - (5, 0) => ('5', '0'), - (5, 1) => ('5', '1'), - (5, 2) => ('5', '2'), - (5, _) => ('5', 'x'), - (6, 0) => ('6', '0'), - (6, 1) => ('6', '1'), - (6, 2) => ('6', '2'), - (6, _) => ('6', 'x'), - (7, _) => ('7', 'x'), - _ => version_error(), - }; - - println!("cargo:rustc-cfg=libressl"); - println!("cargo:rustc-cfg=libressl2{}{}", minor, fix); - println!("cargo:libressl=true"); - println!("cargo:libressl_version=2{}{}", minor, fix); - println!("cargo:version=101"); - Version::Libressl - } else { - let openssl_version = openssl_version.unwrap(); - println!("cargo:version_number={:x}", openssl_version); - - if openssl_version >= 0x1_00_02_08_0 { - println!("cargo:rustc-cfg=ossl102h"); - } - - if openssl_version >= 0x1_01_00_07_0 { - println!("cargo:rustc-cfg=ossl110g"); - } - - if openssl_version >= 0x1_01_02_00_0 { - version_error() - } else if openssl_version >= 0x1_01_01_00_0 { - println!("cargo:rustc-cfg=ossl111"); - println!("cargo:rustc-cfg=ossl110"); - println!("cargo:version=111"); - Version::Openssl11x - } else if openssl_version >= 0x1_01_00_06_0 { - println!("cargo:rustc-cfg=ossl110"); - println!("cargo:rustc-cfg=ossl110f"); - println!("cargo:version=110"); - println!("cargo:patch=f"); - Version::Openssl11x - } else if openssl_version >= 0x1_01_00_00_0 { - println!("cargo:rustc-cfg=ossl110"); - println!("cargo:version=110"); - Version::Openssl11x - } else if openssl_version >= 0x1_00_02_00_0 { - println!("cargo:rustc-cfg=ossl102"); - println!("cargo:version=102"); - Version::Openssl10x - } else if openssl_version >= 0x1_00_01_00_0 { - println!("cargo:rustc-cfg=ossl101"); - println!("cargo:version=101"); - Version::Openssl10x - } else { - version_error() - } - } -} - -fn version_error() -> ! { - panic!( - " - -This crate is only compatible with OpenSSL 1.0.1 through 1.1.1, or LibreSSL 2.5 -through 2.7, but a different version of OpenSSL was found. The build is now aborting -due to this version mismatch. - -" - ); -} - -// parses a string that looks like "0x100020cfL" -fn parse_version(version: &str) -> u64 { - // cut off the 0x prefix - assert!(version.starts_with("0x")); - let version = &version[2..]; - - // and the type specifier suffix - let version = version.trim_right_matches(|c: char| match c { - '0'...'9' | 'a'...'f' | 'A'...'F' => false, - _ => true, - }); - - u64::from_str_radix(version, 16).unwrap() -} - -/// Given a libdir for OpenSSL (where artifacts are located) as well as the name -/// of the libraries we're linking to, figure out whether we should link them -/// statically or dynamically. -fn determine_mode(libdir: &Path, libs: &[&str]) -> &'static str { - // First see if a mode was explicitly requested - let kind = env("OPENSSL_STATIC"); - match kind.as_ref().and_then(|s| s.to_str()).map(|s| &s[..]) { - Some("0") => return "dylib", - Some(_) => return "static", - None => {} - } - - // Next, see what files we actually have to link against, and see what our - // possibilities even are. - let files = libdir - .read_dir() - .unwrap() - .map(|e| e.unwrap()) - .map(|e| e.file_name()) - .filter_map(|e| e.into_string().ok()) - .collect::>(); - let can_static = libs.iter() - .all(|l| files.contains(&format!("lib{}.a", l)) || files.contains(&format!("{}.lib", l))); - let can_dylib = libs.iter().all(|l| { - files.contains(&format!("lib{}.so", l)) || files.contains(&format!("{}.dll", l)) - || files.contains(&format!("lib{}.dylib", l)) - }); - match (can_static, can_dylib) { - (true, false) => return "static", - (false, true) => return "dylib", - (false, false) => { - panic!( - "OpenSSL libdir at `{}` does not contain the required files \ - to either statically or dynamically link OpenSSL", - libdir.display() - ); - } - (true, true) => {} - } - - // Ok, we've got not explicit preference and can *either* link statically or - // link dynamically. In the interest of "security upgrades" and/or "best - // practices with security libs", let's link dynamically. - "dylib" -} - - - -fn execute_command_and_get_output(cmd: &str, args: &[&str]) -> Option { - let out = Command::new(cmd).args(args).output(); - if let Ok(ref r1) = out { - if r1.status.success() { - let r2 = String::from_utf8(r1.stdout.clone()); - if let Ok(r3) = r2 { - return Some(r3.trim().to_string()); - } - } - } - return None; -} - diff --git a/openssl-sys/build/cfgs.rs b/openssl-sys/build/cfgs.rs new file mode 100644 index 00000000..3e063aa3 --- /dev/null +++ b/openssl-sys/build/cfgs.rs @@ -0,0 +1,45 @@ +pub fn get(openssl_version: Option, libressl_version: Option) -> Vec<&'static str> { + let mut cfgs = vec![]; + + if let Some(libressl_version) = libressl_version { + cfgs.push("libressl"); + + if libressl_version >= 0x2_05_01_00_0 { + cfgs.push("libressl251"); + } + + if libressl_version >= 0x2_06_01_00_0 { + cfgs.push("libressl261"); + } + + if libressl_version >= 0x2_07_00_00_0 { + cfgs.push("libressl270"); + } + } else { + let openssl_version = openssl_version.unwrap(); + + if openssl_version >= 0x1_00_02_08_0 { + cfgs.push("ossl102h"); + } + + if openssl_version >= 0x1_01_00_07_0 { + cfgs.push("ossl110g"); + } + + if openssl_version >= 0x1_01_01_00_0 { + cfgs.push("ossl111"); + cfgs.push("ossl110"); + } else if openssl_version >= 0x1_01_00_06_0 { + cfgs.push("ossl110"); + cfgs.push("ossl110f"); + } else if openssl_version >= 0x1_01_00_00_0 { + cfgs.push("ossl110"); + } else if openssl_version >= 0x1_00_02_00_0 { + cfgs.push("ossl102"); + } else if openssl_version >= 0x1_00_01_00_0 { + cfgs.push("ossl101"); + } + } + + cfgs +} diff --git a/openssl-sys/build/main.rs b/openssl-sys/build/main.rs new file mode 100644 index 00000000..e2b3108c --- /dev/null +++ b/openssl-sys/build/main.rs @@ -0,0 +1,572 @@ +extern crate cc; +extern crate pkg_config; +#[cfg(target_env = "msvc")] +extern crate vcpkg; + +use std::collections::HashSet; +use std::env; +use std::ffi::OsString; +use std::fs::File; +use std::io::{BufWriter, Write}; +use std::path::{Path, PathBuf}; +use std::process::Command; + +mod cfgs; + +// The set of `OPENSSL_NO_`s that we care about. +const DEFINES: &'static [&'static str] = &[ + "OPENSSL_NO_BUF_FREELISTS", + "OPENSSL_NO_COMP", + "OPENSSL_NO_EC", + "OPENSSL_NO_EC2M", + "OPENSSL_NO_ENGINE", + "OPENSSL_NO_KRB5", + "OPENSSL_NO_NEXTPROTONEG", + "OPENSSL_NO_PSK", + "OPENSSL_NO_RFC3779", + "OPENSSL_NO_SHA", + "OPENSSL_NO_SRP", + "OPENSSL_NO_SSL3_METHOD", + "OPENSSL_NO_TLSEXT", +]; + +enum Version { + Openssl11x, + Openssl10x, + Libressl, +} + +fn env(name: &str) -> Option { + let prefix = env::var("TARGET").unwrap().to_uppercase().replace("-", "_"); + let prefixed = format!("{}_{}", prefix, name); + println!("cargo:rerun-if-env-changed={}", prefixed); + + if let Some(var) = env::var_os(&prefixed) { + return Some(var); + } + + println!("cargo:rerun-if-env-changed={}", name); + env::var_os(name) +} + +fn main() { + let target = env::var("TARGET").unwrap(); + + let lib_dir = env("OPENSSL_LIB_DIR").map(PathBuf::from); + let include_dir = env("OPENSSL_INCLUDE_DIR").map(PathBuf::from); + + let (lib_dir, include_dir) = if lib_dir.is_none() || include_dir.is_none() { + let openssl_dir = env("OPENSSL_DIR").unwrap_or_else(|| find_openssl_dir(&target)); + let openssl_dir = Path::new(&openssl_dir); + let lib_dir = lib_dir.unwrap_or_else(|| openssl_dir.join("lib")); + let include_dir = include_dir.unwrap_or_else(|| openssl_dir.join("include")); + (lib_dir, include_dir) + } else { + (lib_dir.unwrap(), include_dir.unwrap()) + }; + + if !Path::new(&lib_dir).exists() { + panic!( + "OpenSSL library directory does not exist: {}", + lib_dir.to_string_lossy() + ); + } + if !Path::new(&include_dir).exists() { + panic!( + "OpenSSL include directory does not exist: {}", + include_dir.to_string_lossy() + ); + } + + println!( + "cargo:rustc-link-search=native={}", + lib_dir.to_string_lossy() + ); + println!("cargo:include={}", include_dir.to_string_lossy()); + + let version = validate_headers(&[include_dir.clone().into()]); + + let libs_env = env("OPENSSL_LIBS"); + let libs = match libs_env.as_ref().and_then(|s| s.to_str()) { + Some(ref v) => v.split(":").collect(), + None => match version { + Version::Openssl10x if target.contains("windows") => vec!["ssleay32", "libeay32"], + Version::Openssl11x if target.contains("windows") => vec!["libssl", "libcrypto"], + _ => vec!["ssl", "crypto"], + }, + }; + + let kind = determine_mode(Path::new(&lib_dir), &libs); + for lib in libs.into_iter() { + println!("cargo:rustc-link-lib={}={}", kind, lib); + } +} + +fn find_openssl_dir(target: &str) -> OsString { + let host = env::var("HOST").unwrap(); + + if host == target && target.contains("apple-darwin") { + // Check up default Homebrew installation location first + // for quick resolution if possible. + let homebrew = Path::new("/usr/local/opt/openssl@1.1"); + if homebrew.exists() { + return homebrew.to_path_buf().into(); + } + let homebrew = Path::new("/usr/local/opt/openssl"); + if homebrew.exists() { + return homebrew.to_path_buf().into(); + } + // Calling `brew --prefix ` command usually slow and + // takes seconds, and will be used only as a last resort. + let output = execute_command_and_get_output("brew", &["--prefix", "openssl@1.1"]); + if let Some(ref output) = output { + let homebrew = Path::new(&output); + if homebrew.exists() { + return homebrew.to_path_buf().into(); + } + } + let output = execute_command_and_get_output("brew", &["--prefix", "openssl"]); + if let Some(ref output) = output { + let homebrew = Path::new(&output); + if homebrew.exists() { + return homebrew.to_path_buf().into(); + } + } + } + + try_pkg_config(); + try_vcpkg(); + + // FreeBSD ships with OpenSSL but doesn't include a pkg-config file :( + if host == target && target.contains("freebsd") { + return OsString::from("/usr"); + } + + let mut msg = format!( + " + +Could not find directory of OpenSSL installation, and this `-sys` crate cannot +proceed without this knowledge. If OpenSSL is installed and this crate had +trouble finding it, you can set the `OPENSSL_DIR` environment variable for the +compilation process. + +Make sure you also have the development packages of openssl installed. +For example, `libssl-dev` on Ubuntu or `openssl-devel` on Fedora. + +If you're in a situation where you think the directory *should* be found +automatically, please open a bug at https://github.com/sfackler/rust-openssl +and include information about your system as well as this message. + + $HOST = {} + $TARGET = {} + openssl-sys = {} + +", + host, + target, + env!("CARGO_PKG_VERSION") + ); + + if host.contains("apple-darwin") && target.contains("apple-darwin") { + let system = Path::new("/usr/lib/libssl.0.9.8.dylib"); + if system.exists() { + msg.push_str(&format!( + " + +It looks like you're compiling on macOS, where the system contains a version of +OpenSSL 0.9.8. This crate no longer supports OpenSSL 0.9.8. + +As a consumer of this crate, you can fix this error by using Homebrew to +install the `openssl` package, or as a maintainer you can use the openssl-sys +0.7 crate for support with OpenSSL 0.9.8. + +Unfortunately though the compile cannot continue, so aborting. + +" + )); + } + } + + if host.contains("unknown-linux") && target.contains("unknown-linux-gnu") { + if Command::new("pkg-config").output().is_err() { + msg.push_str(&format!( + " +It looks like you're compiling on Linux and also targeting Linux. Currently this +requires the `pkg-config` utility to find OpenSSL but unfortunately `pkg-config` +could not be found. If you have OpenSSL installed you can likely fix this by +installing `pkg-config`. + +" + )); + } + } + + if host.contains("windows") && target.contains("windows-gnu") { + msg.push_str(&format!( + " +It looks like you're compiling for MinGW but you may not have either OpenSSL or +pkg-config installed. You can install these two dependencies with: + + pacman -S openssl-devel pkg-config + +and try building this crate again. + +" + )); + } + + if host.contains("windows") && target.contains("windows-msvc") { + msg.push_str(&format!( + " +It looks like you're compiling for MSVC but we couldn't detect an OpenSSL +installation. If there isn't one installed then you can try the rust-openssl +README for more information about how to download precompiled binaries of +OpenSSL: + + https://github.com/sfackler/rust-openssl#windows + +" + )); + } + + panic!(msg); +} + +/// Attempt to find OpenSSL through pkg-config. +/// +/// Note that if this succeeds then the function does not return as pkg-config +/// typically tells us all the information that we need. +fn try_pkg_config() { + let target = env::var("TARGET").unwrap(); + let host = env::var("HOST").unwrap(); + + // If we're going to windows-gnu we can use pkg-config, but only so long as + // we're coming from a windows host. + // + // Otherwise if we're going to windows we probably can't use pkg-config. + if target.contains("windows-gnu") && host.contains("windows") { + env::set_var("PKG_CONFIG_ALLOW_CROSS", "1"); + } else if target.contains("windows") { + return; + } + + let lib = match pkg_config::Config::new() + .print_system_libs(false) + .find("openssl") + { + Ok(lib) => lib, + Err(e) => { + println!("run pkg_config fail: {:?}", e); + return; + } + }; + + validate_headers(&lib.include_paths); + + for include in lib.include_paths.iter() { + println!("cargo:include={}", include.display()); + } + + std::process::exit(0); +} + +/// Attempt to find OpenSSL through vcpkg. +/// +/// Note that if this succeeds then the function does not return as vcpkg +/// should emit all of the cargo metadata that we need. +#[cfg(target_env = "msvc")] +fn try_vcpkg() { + // vcpkg will not emit any metadata if it can not find libraries + // appropriate for the target triple with the desired linkage. + + let mut lib = vcpkg::Config::new() + .emit_includes(true) + .lib_name("libcrypto") + .lib_name("libssl") + .probe("openssl"); + + if let Err(e) = lib { + println!( + "note: vcpkg did not find openssl as libcrypto and libssl : {:?}", + e + ); + lib = vcpkg::Config::new() + .emit_includes(true) + .lib_name("libeay32") + .lib_name("ssleay32") + .probe("openssl"); + } + if let Err(e) = lib { + println!( + "note: vcpkg did not find openssl as ssleay32 and libeay32: {:?}", + e + ); + return; + } + + let lib = lib.unwrap(); + validate_headers(&lib.include_paths); + + println!("cargo:rustc-link-lib=user32"); + println!("cargo:rustc-link-lib=gdi32"); + println!("cargo:rustc-link-lib=crypt32"); + + std::process::exit(0); +} + +#[cfg(not(target_env = "msvc"))] +fn try_vcpkg() {} + +/// Validates the header files found in `include_dir` and then returns the +/// version string of OpenSSL. +fn validate_headers(include_dirs: &[PathBuf]) -> Version { + // This `*-sys` crate only works with OpenSSL 1.0.1, 1.0.2, and 1.1.0. To + // correctly expose the right API from this crate, take a look at + // `opensslv.h` to see what version OpenSSL claims to be. + // + // OpenSSL has a number of build-time configuration options which affect + // various structs and such. Since OpenSSL 1.1.0 this isn't really a problem + // as the library is much more FFI-friendly, but 1.0.{1,2} suffer this problem. + // + // To handle all this conditional compilation we slurp up the configuration + // file of OpenSSL, `opensslconf.h`, and then dump out everything it defines + // as our own #[cfg] directives. That way the `ossl10x.rs` bindings can + // account for compile differences and such. + let mut path = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + path.push("expando.c"); + let mut file = BufWriter::new(File::create(&path).unwrap()); + + write!( + file, + "\ +#include +#include + +#define VERSION2(n, v) RUST_VERSION_ ## n ## _ ## v +#define VERSION(n, v) VERSION2(n, v) + +VERSION(OPENSSL, OPENSSL_VERSION_NUMBER) + +#ifdef LIBRESSL_VERSION_NUMBER +VERSION(LIBRESSL, LIBRESSL_VERSION_NUMBER) +#endif +" + ).unwrap(); + + for define in DEFINES { + write!( + file, + "\ +#ifdef {define} +RUST_CONF_{define} +#endif +", + define = define + ).unwrap(); + } + + file.flush().unwrap(); + drop(file); + + let mut gcc = cc::Build::new(); + for include_dir in include_dirs { + gcc.include(include_dir); + } + // https://github.com/alexcrichton/gcc-rs/issues/133 + let expanded = match gcc.file(&path).try_expand() { + Ok(expanded) => expanded, + Err(e) => { + panic!( + " +Header expansion error: +{:?} + +Failed to find OpenSSL development headers. + +You can try fixing this setting the `OPENSSL_DIR` environment variable +pointing to your OpenSSL installation or installing OpenSSL headers package +specific to your distribution: + + # On Ubuntu + sudo apt-get install libssl-dev + # On Arch Linux + sudo pacman -S openssl + # On Fedora + sudo dnf install openssl-devel + +See rust-openssl README for more information: + + https://github.com/sfackler/rust-openssl#linux +", + e + ); + } + }; + let expanded = String::from_utf8(expanded).unwrap(); + + let mut enabled = vec![]; + let mut openssl_version = None; + let mut libressl_version = None; + for line in expanded.lines() { + let line = line.trim(); + + let openssl_prefix = "RUST_VERSION_OPENSSL_"; + let libressl_prefix = "RUST_VERSION_LIBRESSL_"; + let conf_prefix = "RUST_CONF_"; + if line.starts_with(openssl_prefix) { + let version = &line[openssl_prefix.len()..]; + openssl_version = Some(parse_version(version)); + } else if line.starts_with(libressl_prefix) { + let version = &line[libressl_prefix.len()..]; + libressl_version = Some(parse_version(version)); + } else if line.starts_with(conf_prefix) { + enabled.push(&line[conf_prefix.len()..]); + } + } + + for enabled in &enabled { + println!("cargo:rustc-cfg=osslconf=\"{}\"", enabled); + } + println!("cargo:conf={}", enabled.join(",")); + + for cfg in cfgs::get(openssl_version, libressl_version) { + println!("cargo:rustc-cfg={}", cfg); + } + + if let Some(libressl_version) = libressl_version { + println!("cargo:libressl_version_number={:x}", libressl_version); + + let minor = (libressl_version >> 20) as u8; + let fix = (libressl_version >> 12) as u8; + let (minor, fix) = match (minor, fix) { + (5, 0) => ('5', '0'), + (5, 1) => ('5', '1'), + (5, 2) => ('5', '2'), + (5, _) => ('5', 'x'), + (6, 0) => ('6', '0'), + (6, 1) => ('6', '1'), + (6, 2) => ('6', '2'), + (6, _) => ('6', 'x'), + (7, _) => ('7', 'x'), + _ => version_error(), + }; + + println!("cargo:libressl=true"); + println!("cargo:libressl_version=2{}{}", minor, fix); + println!("cargo:version=101"); + Version::Libressl + } else { + let openssl_version = openssl_version.unwrap(); + println!("cargo:version_number={:x}", openssl_version); + + if openssl_version >= 0x1_01_02_00_0 { + version_error() + } else if openssl_version >= 0x1_01_01_00_0 { + println!("cargo:version=111"); + Version::Openssl11x + } else if openssl_version >= 0x1_01_00_06_0 { + println!("cargo:version=110"); + println!("cargo:patch=f"); + Version::Openssl11x + } else if openssl_version >= 0x1_01_00_00_0 { + println!("cargo:version=110"); + Version::Openssl11x + } else if openssl_version >= 0x1_00_02_00_0 { + println!("cargo:version=102"); + Version::Openssl10x + } else if openssl_version >= 0x1_00_01_00_0 { + println!("cargo:version=101"); + Version::Openssl10x + } else { + version_error() + } + } +} + +fn version_error() -> ! { + panic!( + " + +This crate is only compatible with OpenSSL 1.0.1 through 1.1.1, or LibreSSL 2.5 +through 2.7, but a different version of OpenSSL was found. The build is now aborting +due to this version mismatch. + +" + ); +} + +// parses a string that looks like "0x100020cfL" +fn parse_version(version: &str) -> u64 { + // cut off the 0x prefix + assert!(version.starts_with("0x")); + let version = &version[2..]; + + // and the type specifier suffix + let version = version.trim_right_matches(|c: char| match c { + '0'...'9' | 'a'...'f' | 'A'...'F' => false, + _ => true, + }); + + u64::from_str_radix(version, 16).unwrap() +} + +/// Given a libdir for OpenSSL (where artifacts are located) as well as the name +/// of the libraries we're linking to, figure out whether we should link them +/// statically or dynamically. +fn determine_mode(libdir: &Path, libs: &[&str]) -> &'static str { + // First see if a mode was explicitly requested + let kind = env("OPENSSL_STATIC"); + match kind.as_ref().and_then(|s| s.to_str()).map(|s| &s[..]) { + Some("0") => return "dylib", + Some(_) => return "static", + None => {} + } + + // Next, see what files we actually have to link against, and see what our + // possibilities even are. + let files = libdir + .read_dir() + .unwrap() + .map(|e| e.unwrap()) + .map(|e| e.file_name()) + .filter_map(|e| e.into_string().ok()) + .collect::>(); + let can_static = libs + .iter() + .all(|l| files.contains(&format!("lib{}.a", l)) || files.contains(&format!("{}.lib", l))); + let can_dylib = libs.iter().all(|l| { + files.contains(&format!("lib{}.so", l)) + || files.contains(&format!("{}.dll", l)) + || files.contains(&format!("lib{}.dylib", l)) + }); + match (can_static, can_dylib) { + (true, false) => return "static", + (false, true) => return "dylib", + (false, false) => { + panic!( + "OpenSSL libdir at `{}` does not contain the required files \ + to either statically or dynamically link OpenSSL", + libdir.display() + ); + } + (true, true) => {} + } + + // Ok, we've got not explicit preference and can *either* link statically or + // link dynamically. In the interest of "security upgrades" and/or "best + // practices with security libs", let's link dynamically. + "dylib" +} + +fn execute_command_and_get_output(cmd: &str, args: &[&str]) -> Option { + let out = Command::new(cmd).args(args).output(); + if let Ok(ref r1) = out { + if r1.status.success() { + let r2 = String::from_utf8(r1.stdout.clone()); + if let Ok(r3) = r2 { + return Some(r3.trim().to_string()); + } + } + } + return None; +} diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index 61e087d0..e78c24f4 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -236,8 +236,10 @@ pub const EVP_PKEY_OP_VERIFYCTX: c_int = 1 << 7; pub const EVP_PKEY_OP_ENCRYPT: c_int = 1 << 8; pub const EVP_PKEY_OP_DECRYPT: c_int = 1 << 9; -pub const EVP_PKEY_OP_TYPE_SIG: c_int = EVP_PKEY_OP_SIGN | EVP_PKEY_OP_VERIFY - | EVP_PKEY_OP_VERIFYRECOVER | EVP_PKEY_OP_SIGNCTX +pub const EVP_PKEY_OP_TYPE_SIG: c_int = EVP_PKEY_OP_SIGN + | EVP_PKEY_OP_VERIFY + | EVP_PKEY_OP_VERIFYRECOVER + | EVP_PKEY_OP_SIGNCTX | EVP_PKEY_OP_VERIFYCTX; pub const EVP_PKEY_OP_TYPE_CRYPT: c_int = EVP_PKEY_OP_ENCRYPT | EVP_PKEY_OP_DECRYPT; @@ -1259,21 +1261,23 @@ pub const SSL_VERIFY_NONE: c_int = 0; pub const SSL_VERIFY_PEER: c_int = 1; pub const SSL_VERIFY_FAIL_IF_NO_PEER_CERT: c_int = 2; -#[cfg(not(any(libressl261, libressl262, libressl26x, libressl27x, ossl101)))] +#[cfg(not(any(libressl261, ossl101)))] pub const SSL_OP_TLSEXT_PADDING: c_ulong = 0x00000010; -#[cfg(any(libressl261, libressl262, libressl26x, libressl27x))] +#[cfg(libressl261)] pub const SSL_OP_TLSEXT_PADDING: c_ulong = 0x0; pub const SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS: c_ulong = 0x00000800; -#[cfg(not(any(libressl261, libressl262, libressl26x, libressl27x)))] +#[cfg(not(libressl261))] pub const SSL_OP_CRYPTOPRO_TLSEXT_BUG: c_ulong = 0x80000000; -#[cfg(any(libressl261, libressl262, libressl26x, libressl27x))] +#[cfg(libressl261)] pub const SSL_OP_CRYPTOPRO_TLSEXT_BUG: c_ulong = 0x0; pub const SSL_OP_LEGACY_SERVER_CONNECT: c_ulong = 0x00000004; #[cfg(not(any(libressl, ossl110f, ossl111)))] pub const SSL_OP_ALL: c_ulong = 0x80000BFF; #[cfg(any(ossl110f, ossl111))] -pub const SSL_OP_ALL: c_ulong = SSL_OP_CRYPTOPRO_TLSEXT_BUG | SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS - | SSL_OP_LEGACY_SERVER_CONNECT | SSL_OP_TLSEXT_PADDING +pub const SSL_OP_ALL: c_ulong = SSL_OP_CRYPTOPRO_TLSEXT_BUG + | SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS + | SSL_OP_LEGACY_SERVER_CONNECT + | SSL_OP_TLSEXT_PADDING | SSL_OP_SAFARI_ECDHE_ECDSA_BUG; pub const SSL_OP_NO_QUERY_MTU: c_ulong = 0x00001000; pub const SSL_OP_COOKIE_EXCHANGE: c_ulong = 0x00002000; @@ -1289,8 +1293,11 @@ pub const SSL_OP_NO_TLSv1_2: c_ulong = 0x08000000; pub const SSL_OP_NO_SSL_MASK: c_ulong = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2; #[cfg(ossl111)] -pub const SSL_OP_NO_SSL_MASK: c_ulong = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 - | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2 +pub const SSL_OP_NO_SSL_MASK: c_ulong = SSL_OP_NO_SSLv2 + | SSL_OP_NO_SSLv3 + | SSL_OP_NO_TLSv1 + | SSL_OP_NO_TLSv1_1 + | SSL_OP_NO_TLSv1_2 | SSL_OP_NO_TLSv1_3; pub const SSL_FILETYPE_PEM: c_int = X509_FILETYPE_PEM; diff --git a/openssl-sys/src/libressl/mod.rs b/openssl-sys/src/libressl/mod.rs index 366d9502..5ae205bc 100644 --- a/openssl-sys/src/libressl/mod.rs +++ b/openssl-sys/src/libressl/mod.rs @@ -1,19 +1,18 @@ +use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong, c_void, size_t}; use std::mem; use std::ptr; use std::sync::{Mutex, MutexGuard}; use std::sync::{Once, ONCE_INIT}; -#[cfg(libressl250)] +#[cfg(not(libressl251))] pub use libressl::v250::*; -#[cfg(not(libressl250))] -pub use libressl::v25x::*; - -use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong, c_void, size_t}; +#[cfg(libressl251)] +pub use libressl::v251::*; -#[cfg(libressl250)] +#[cfg(not(libressl251))] mod v250; -#[cfg(not(libressl250))] -mod v25x; +#[cfg(libressl251)] +mod v251; #[repr(C)] pub struct stack_st_ASN1_OBJECT { @@ -337,9 +336,9 @@ pub const SSL_CTRL_OPTIONS: c_int = 32; pub const SSL_CTRL_CLEAR_OPTIONS: c_int = 77; pub const SSL_CTRL_SET_ECDH_AUTO: c_int = 94; -#[cfg(any(libressl261, libressl262, libressl26x, libressl27x))] +#[cfg(libressl261)] pub const SSL_OP_ALL: c_ulong = 0x4; -#[cfg(not(any(libressl261, libressl262, libressl26x, libressl27x)))] +#[cfg(not(libressl261))] pub const SSL_OP_ALL: c_ulong = 0x80000014; pub const SSL_OP_CISCO_ANYCONNECT: c_ulong = 0x0; pub const SSL_OP_NO_COMPRESSION: c_ulong = 0x0; @@ -352,9 +351,9 @@ pub const SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER: c_ulong = 0x0; pub const SSL_OP_SSLEAY_080_CLIENT_DH_BUG: c_ulong = 0x0; pub const SSL_OP_TLS_D5_BUG: c_ulong = 0x0; pub const SSL_OP_TLS_BLOCK_PADDING_BUG: c_ulong = 0x0; -#[cfg(any(libressl261, libressl262, libressl26x, libressl27x))] +#[cfg(libressl261)] pub const SSL_OP_SINGLE_ECDH_USE: c_ulong = 0x0; -#[cfg(not(any(libressl261, libressl262, libressl26x, libressl27x)))] +#[cfg(not(libressl261))] pub const SSL_OP_SINGLE_ECDH_USE: c_ulong = 0x00080000; pub const SSL_OP_SINGLE_DH_USE: c_ulong = 0x00100000; pub const SSL_OP_NO_SSLv2: c_ulong = 0x0; @@ -540,6 +539,15 @@ extern "C" { unsafe extern "C" fn(*mut ::SSL, *mut c_uchar, c_int, *mut c_int) -> *mut SSL_SESSION, >, ); + #[cfg(libressl261)] + pub fn SSL_CTX_set_min_proto_version(ctx: *mut ::SSL_CTX, version: u16) -> c_int; + #[cfg(libressl261)] + pub fn SSL_CTX_set_max_proto_version(ctx: *mut ::SSL_CTX, version: u16) -> c_int; + #[cfg(libressl270)] + pub fn SSL_CTX_get_min_proto_version(ctx: *mut ::SSL_CTX) -> c_int; + #[cfg(libressl270)] + pub fn SSL_CTX_get_max_proto_version(ctx: *mut ::SSL_CTX) -> c_int; + pub fn X509_get_subject_name(x: *mut ::X509) -> *mut ::X509_NAME; pub fn X509_get_issuer_name(x: *mut ::X509) -> *mut ::X509_NAME; pub fn X509_set_notAfter(x: *mut ::X509, tm: *const ::ASN1_TIME) -> c_int; diff --git a/openssl-sys/src/libressl/v251.rs b/openssl-sys/src/libressl/v251.rs new file mode 100644 index 00000000..541b61db --- /dev/null +++ b/openssl-sys/src/libressl/v251.rs @@ -0,0 +1,89 @@ +use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong, c_void, size_t, time_t}; + +use super::*; + +#[repr(C)] +pub struct SSL { + version: c_int, + method: *const ::SSL_METHOD, + rbio: *mut ::BIO, + wbio: *mut ::BIO, + bbio: *mut ::BIO, + pub server: c_int, + s3: *mut c_void, + d1: *mut c_void, + param: *mut c_void, + cipher_list: *mut stack_st_SSL_CIPHER, + cert: *mut c_void, + sid_ctx_length: c_uint, + sid_ctx: [c_uchar; ::SSL_MAX_SID_CTX_LENGTH as usize], + session: *mut ::SSL_SESSION, + verify_mode: c_int, + error: c_int, + error_code: c_int, + ctx: *mut ::SSL_CTX, + verify_result: c_long, + references: c_int, + client_version: c_int, + max_send_fragment: c_uint, + tlsext_hostname: *mut c_char, + tlsext_status_type: c_int, + initial_ctx: *mut ::SSL_CTX, + enc_read_ctx: *mut ::EVP_CIPHER_CTX, + read_hash: *mut EVP_MD_CTX, + internal: *mut c_void, +} + +#[repr(C)] +pub struct SSL_CTX { + method: *const ::SSL_METHOD, + cipher_list: *mut stack_st_SSL_CIPHER, + cert_store: *mut c_void, + session_timeout: c_long, + pub references: c_int, + extra_certs: *mut stack_st_X509, + verify_mode: c_int, + sid_ctx_length: c_uint, + sid_ctx: [c_uchar; ::SSL_MAX_SID_CTX_LENGTH as usize], + param: *mut ::X509_VERIFY_PARAM, + default_passwd_callback: *mut c_void, + default_passwd_callback_userdata: *mut c_void, + internal: *mut c_void, +} + +#[repr(C)] +pub struct SSL_SESSION { + ssl_version: c_int, + pub master_key_length: c_int, + pub master_key: [c_uchar; 48], + session_id_length: c_uint, + session_id: [c_uchar; ::SSL_MAX_SSL_SESSION_ID_LENGTH as usize], + sid_ctx_length: c_uint, + sid_ctx: [c_uchar; ::SSL_MAX_SID_CTX_LENGTH as usize], + peer: *mut ::X509, + verify_result: c_long, + timeout: c_long, + time: time_t, + pub references: c_int, + cipher: *const ::SSL_CIPHER, + cipher_id: c_long, + ciphers: *mut stack_st_SSL_CIPHER, + tlsext_hostname: *mut c_char, + tlsext_tick: *mut c_uchar, + tlsext_ticklen: size_t, + tlsext_tick_lifetime_int: c_long, + internal: *mut c_void, +} + +#[repr(C)] +pub struct X509_VERIFY_PARAM { + pub name: *mut c_char, + pub check_time: time_t, + pub inh_flags: c_ulong, + pub flags: c_ulong, + pub purpose: c_int, + pub trust: c_int, + pub depth: c_int, + pub policies: *mut stack_st_ASN1_OBJECT, + id: *mut c_void, +} diff --git a/openssl-sys/src/libressl/v25x.rs b/openssl-sys/src/libressl/v25x.rs deleted file mode 100644 index 7e7023ec..00000000 --- a/openssl-sys/src/libressl/v25x.rs +++ /dev/null @@ -1,89 +0,0 @@ -use libc::{c_int, c_char, c_void, c_long, c_uchar, size_t, c_uint, c_ulong, time_t}; - -use super::*; - -#[repr(C)] -pub struct SSL { - version: c_int, - method: *const ::SSL_METHOD, - rbio: *mut ::BIO, - wbio: *mut ::BIO, - bbio: *mut ::BIO, - pub server: c_int, - s3: *mut c_void, - d1: *mut c_void, - param: *mut c_void, - cipher_list: *mut stack_st_SSL_CIPHER, - cert: *mut c_void, - sid_ctx_length: c_uint, - sid_ctx: [c_uchar; ::SSL_MAX_SID_CTX_LENGTH as usize], - session: *mut ::SSL_SESSION, - verify_mode: c_int, - error: c_int, - error_code: c_int, - ctx: *mut ::SSL_CTX, - verify_result: c_long, - references: c_int, - client_version: c_int, - max_send_fragment: c_uint, - tlsext_hostname: *mut c_char, - tlsext_status_type: c_int, - initial_ctx: *mut ::SSL_CTX, - enc_read_ctx: *mut ::EVP_CIPHER_CTX, - read_hash: *mut EVP_MD_CTX, - internal: *mut c_void, -} - -#[repr(C)] -pub struct SSL_CTX { - method: *const ::SSL_METHOD, - cipher_list: *mut stack_st_SSL_CIPHER, - cert_store: *mut c_void, - session_timeout: c_long, - pub references: c_int, - extra_certs: *mut stack_st_X509, - verify_mode: c_int, - sid_ctx_length: c_uint, - sid_ctx: [c_uchar; ::SSL_MAX_SID_CTX_LENGTH as usize], - param: *mut ::X509_VERIFY_PARAM, - default_passwd_callback: *mut c_void, - default_passwd_callback_userdata: *mut c_void, - internal: *mut c_void, -} - -#[repr(C)] -pub struct SSL_SESSION { - ssl_version: c_int, - pub master_key_length: c_int, - pub master_key: [c_uchar; 48], - session_id_length: c_uint, - session_id: [c_uchar; ::SSL_MAX_SSL_SESSION_ID_LENGTH as usize], - sid_ctx_length: c_uint, - sid_ctx: [c_uchar; ::SSL_MAX_SID_CTX_LENGTH as usize], - peer: *mut ::X509, - verify_result: c_long, - timeout: c_long, - time: time_t, - pub references: c_int, - cipher: *const ::SSL_CIPHER, - cipher_id: c_long, - ciphers: *mut stack_st_SSL_CIPHER, - tlsext_hostname: *mut c_char, - tlsext_tick: *mut c_uchar, - tlsext_ticklen: size_t, - tlsext_tick_lifetime_int: c_long, - internal: *mut c_void, -} - -#[repr(C)] -pub struct X509_VERIFY_PARAM { - pub name: *mut c_char, - pub check_time: time_t, - pub inh_flags: c_ulong, - pub flags: c_ulong, - pub purpose: c_int, - pub trust: c_int, - pub depth: c_int, - policies: *mut stack_st_ASN1_OBJECT, - id: *mut c_void, -} diff --git a/openssl/build.rs b/openssl/build.rs index 612322fe..271fb662 100644 --- a/openssl/build.rs +++ b/openssl/build.rs @@ -41,4 +41,16 @@ fn main() { println!("cargo:rustc-cfg=ossl110g"); } } + + if let Ok(version) = env::var("DEP_OPENSSL_LIBRESSL_VERSION_NUMBER") { + let version = u64::from_str_radix(&version, 16).unwrap(); + + if version >= 0x2_06_01_00_0 { + println!("cargo:rustc-cfg=libressl261"); + } + + if version >= 0x2_07_00_00_0 { + println!("cargo:rustc-cfg=libressl270"); + } + } } diff --git a/openssl/src/ec.rs b/openssl/src/ec.rs index c4800c73..573d0c00 100644 --- a/openssl/src/ec.rs +++ b/openssl/src/ec.rs @@ -33,14 +33,14 @@ //! ``` use ffi; use foreign_types::{ForeignType, ForeignTypeRef}; -use std::ptr; use libc::c_int; +use std::ptr; -use {cvt, cvt_n, cvt_p, init}; use bn::{BigNumContextRef, BigNumRef}; use error::ErrorStack; use nid::Nid; use pkey::{HasParams, HasPrivate, HasPublic, Params, Private, Public}; +use {cvt, cvt_n, cvt_p, init}; /// Compressed or Uncompressed conversion /// @@ -803,10 +803,10 @@ impl Clone for EcKey { #[cfg(test)] mod test { + use super::*; use bn::{BigNum, BigNumContext}; - use nid::Nid; use data_encoding::BASE64URL_NOPAD; - use super::*; + use nid::Nid; #[test] fn key_new_by_curve_name() { @@ -823,7 +823,7 @@ mod test { fn dup() { let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); let key = EcKey::generate(&group).unwrap(); - key.clone(); + drop(key.clone()); } #[test] @@ -862,7 +862,8 @@ mod test { let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); let key = EcKey::generate(&group).unwrap(); let mut ctx = BigNumContext::new().unwrap(); - let bytes = key.public_key() + let bytes = key + .public_key() .to_bytes(&group, PointConversionForm::COMPRESSED, &mut ctx) .unwrap(); @@ -877,7 +878,8 @@ mod test { let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); let key = EcKey::generate(&group).unwrap(); - let dup_key = EcKey::from_private_components(&group, key.private_key(), key.public_key()).unwrap(); + let dup_key = + EcKey::from_private_components(&group, key.private_key(), key.public_key()).unwrap(); let res = dup_key.check_key().unwrap(); assert!(res == ()); diff --git a/openssl/src/rsa.rs b/openssl/src/rsa.rs index 0ad55b96..718ae59d 100644 --- a/openssl/src/rsa.rs +++ b/openssl/src/rsa.rs @@ -751,7 +751,8 @@ mod test { #[test] fn test_to_password() { let key = Rsa::generate(2048).unwrap(); - let pem = key.private_key_to_pem_passphrase(Cipher::aes_128_cbc(), b"foobar") + let pem = key + .private_key_to_pem_passphrase(Cipher::aes_128_cbc(), b"foobar") .unwrap(); Rsa::private_key_from_pem_passphrase(&pem, b"foobar").unwrap(); assert!(Rsa::private_key_from_pem_passphrase(&pem, b"fizzbuzz").is_err()); @@ -791,7 +792,8 @@ mod test { k0.private_encrypt(&msg, &mut emesg, Padding::PKCS1) .unwrap(); let mut dmesg = vec![0; k1.size() as usize]; - let len = k1.public_decrypt(&emesg, &mut dmesg, Padding::PKCS1) + let len = k1 + .public_decrypt(&emesg, &mut dmesg, Padding::PKCS1) .unwrap(); assert_eq!(msg, &dmesg[..len]); } @@ -807,7 +809,8 @@ mod test { let mut emesg = vec![0; k0.size() as usize]; k0.public_encrypt(&msg, &mut emesg, Padding::PKCS1).unwrap(); let mut dmesg = vec![0; k1.size() as usize]; - let len = k1.private_decrypt(&emesg, &mut dmesg, Padding::PKCS1) + let len = k1 + .private_decrypt(&emesg, &mut dmesg, Padding::PKCS1) .unwrap(); assert_eq!(msg, &dmesg[..len]); } @@ -883,6 +886,6 @@ mod test { #[test] fn clone() { let key = Rsa::generate(2048).unwrap(); - key.clone(); + drop(key.clone()); } } diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 8dc605ed..adec060c 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -1023,15 +1023,15 @@ impl SslContextBuilder { /// /// This corresponds to [`SSL_CTX_set_min_proto_version`]. /// - /// Requires OpenSSL 1.1.0 or newer. + /// Requires OpenSSL 1.1.0 or LibreSSL 2.6.1 or newer. /// /// [`SSL_CTX_set_min_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html - #[cfg(any(ossl110))] + #[cfg(any(ossl110, libressl261))] pub fn set_min_proto_version(&mut self, version: Option) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_set_min_proto_version( self.as_ptr(), - version.map_or(0, |v| v.0), + version.map_or(0, |v| v.0 as _), )).map(|_| ()) } } @@ -1043,15 +1043,15 @@ impl SslContextBuilder { /// /// This corresponds to [`SSL_CTX_set_max_proto_version`]. /// - /// Requires OpenSSL 1.1.0 or newer. + /// Requires OpenSSL 1.1.0 or or LibreSSL 2.6.1 or newer. /// /// [`SSL_CTX_set_max_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html - #[cfg(any(ossl110))] + #[cfg(any(ossl110, libressl261))] pub fn set_max_proto_version(&mut self, version: Option) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_set_max_proto_version( self.as_ptr(), - version.map_or(0, |v| v.0), + version.map_or(0, |v| v.0 as _), )).map(|_| ()) } } @@ -1063,10 +1063,10 @@ impl SslContextBuilder { /// /// This corresponds to [`SSL_CTX_get_min_proto_version`]. /// - /// Requires OpenSSL 1.1.0g or newer. + /// Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer. /// /// [`SSL_CTX_get_min_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html - #[cfg(any(ossl110g))] + #[cfg(any(ossl110g, libressl270))] pub fn min_proto_version(&mut self) -> Option { unsafe { let r = ffi::SSL_CTX_get_min_proto_version(self.as_ptr()); @@ -1085,10 +1085,10 @@ impl SslContextBuilder { /// /// This corresponds to [`SSL_CTX_get_max_proto_version`]. /// - /// Requires OpenSSL 1.1.0g or newer. + /// Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer. /// /// [`SSL_CTX_get_max_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html - #[cfg(any(ossl110g))] + #[cfg(any(ossl110g, libressl270))] pub fn max_proto_version(&mut self) -> Option { unsafe { let r = ffi::SSL_CTX_get_max_proto_version(self.as_ptr()); @@ -2837,7 +2837,8 @@ impl Read for SslStream { } Err(ref e) if e.code() == ErrorCode::WANT_READ && e.io_error().is_none() => {} Err(e) => { - return Err(e.into_io_error() + return Err(e + .into_io_error() .unwrap_or_else(|e| io::Error::new(io::ErrorKind::Other, e))) } } @@ -2852,7 +2853,8 @@ impl Write for SslStream { Ok(n) => return Ok(n), Err(ref e) if e.code() == ErrorCode::WANT_READ && e.io_error().is_none() => {} Err(e) => { - return Err(e.into_io_error() + return Err(e + .into_io_error() .unwrap_or_else(|e| io::Error::new(io::ErrorKind::Other, e))) } } diff --git a/openssl/src/ssl/test.rs b/openssl/src/ssl/test.rs index 2e906728..b90199f0 100644 --- a/openssl/src/ssl/test.rs +++ b/openssl/src/ssl/test.rs @@ -19,7 +19,7 @@ use hash::MessageDigest; use ocsp::{OcspResponse, OcspResponseStatus}; use pkey::PKey; use ssl; -#[cfg(any(ossl110, ossl111))] +#[cfg(any(ossl110, ossl111, libressl261))] use ssl::SslVersion; use ssl::{ Error, HandshakeError, MidHandshakeSslStream, ShutdownResult, Ssl, SslAcceptor, SslConnector, @@ -1315,7 +1315,7 @@ fn keying_export() { } #[test] -#[cfg(any(ossl110))] +#[cfg(any(ossl110, libressl261))] fn no_version_overlap() { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); let addr = listener.local_addr().unwrap(); @@ -1330,7 +1330,7 @@ fn no_version_overlap() { ctx.set_max_proto_version(Some(SslVersion::TLS1_1)).unwrap(); #[cfg(ossl110g)] assert_eq!(ctx.min_proto_version(), None); - #[cfg(ossl110g)] + #[cfg(any(ossl110g, libressl270))] assert_eq!(ctx.max_proto_version(), Some(SslVersion::TLS1_1)); let ssl = Ssl::new(&ctx.build()).unwrap(); ssl.accept(stream).unwrap_err(); @@ -1339,7 +1339,7 @@ fn no_version_overlap() { let stream = TcpStream::connect(addr).unwrap(); let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); ctx.set_min_proto_version(Some(SslVersion::TLS1_2)).unwrap(); - #[cfg(ossl110g)] + #[cfg(any(ossl110g, libressl270))] assert_eq!(ctx.min_proto_version(), Some(SslVersion::TLS1_2)); #[cfg(ossl110g)] assert_eq!(ctx.max_proto_version(), None); diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs index 7ef4d160..a3c66e0c 100644 --- a/openssl/src/x509/tests.rs +++ b/openssl/src/x509/tests.rs @@ -7,10 +7,12 @@ use nid::Nid; use pkey::{PKey, Private}; use rsa::Rsa; use stack::Stack; -use x509::{X509, X509Name, X509Req, X509StoreContext, X509VerifyResult}; -use x509::extension::{AuthorityKeyIdentifier, BasicConstraints, ExtendedKeyUsage, KeyUsage, - SubjectAlternativeName, SubjectKeyIdentifier}; +use x509::extension::{ + AuthorityKeyIdentifier, BasicConstraints, ExtendedKeyUsage, KeyUsage, SubjectAlternativeName, + SubjectKeyIdentifier, +}; use x509::store::X509StoreBuilder; +use x509::{X509, X509Name, X509Req, X509StoreContext, X509VerifyResult}; fn pkey() -> PKey { let rsa = Rsa::generate(2048).unwrap(); @@ -197,7 +199,8 @@ fn x509_builder() { assert!(pkey.public_eq(&x509.public_key().unwrap())); - let cn = x509.subject_name() + let cn = x509 + .subject_name() .entries_by_nid(Nid::COMMONNAME) .next() .unwrap(); @@ -291,7 +294,7 @@ fn signature() { fn clone_x509() { let cert = include_bytes!("../../test/cert.pem"); let cert = X509::from_pem(cert).unwrap(); - cert.clone(); + drop(cert.clone()); } #[test] diff --git a/systest/build.rs b/systest/build.rs index 98e9ffb3..84c0a1ae 100644 --- a/systest/build.rs +++ b/systest/build.rs @@ -2,10 +2,12 @@ extern crate ctest; use std::env; +#[path = "../openssl-sys/build/cfgs.rs"] +mod cfgs; + fn main() { let mut cfg = ctest::TestGenerator::new(); let target = env::var("TARGET").unwrap(); - let mut is_libressl = false; if let Ok(out) = env::var("DEP_OPENSSL_INCLUDE") { cfg.include(&out); @@ -26,21 +28,17 @@ fn main() { cfg.define("WIN32_LEAN_AND_MEAN", None); } - if let Ok(_) = env::var("DEP_OPENSSL_LIBRESSL") { - cfg.cfg("libressl", None); - is_libressl = true; - } else if let Ok(version) = env::var("DEP_OPENSSL_VERSION") { - cfg.cfg(&format!("ossl{}", version), None); - if version == "111" { - cfg.cfg("ossl110", None); - } - } - if let (Ok(version), Ok(patch)) = ( - env::var("DEP_OPENSSL_VERSION"), - env::var("DEP_OPENSSL_PATCH"), - ) { - cfg.cfg(&format!("ossl{}{}", version, patch), None); + let openssl_version = env::var("DEP_OPENSSL_VERSION_NUMBER") + .ok() + .map(|v| u64::from_str_radix(&v, 16).unwrap()); + let libressl_version = env::var("DEP_OPENSSL_LIBRESSL_VERSION_NUMBER") + .ok() + .map(|v| u64::from_str_radix(&v, 16).unwrap()); + + for c in cfgs::get(openssl_version, libressl_version) { + cfg.cfg(c, None); } + if let Ok(vars) = env::var("DEP_OPENSSL_CONF") { for var in vars.split(",") { cfg.cfg("osslconf", Some(var)); @@ -65,7 +63,7 @@ fn main() { .header("openssl/ocsp.h") .header("openssl/evp.h"); - if !is_libressl { + if openssl_version.is_some() { cfg.header("openssl/cms.h"); } @@ -79,7 +77,8 @@ fn main() { } else if s == "_STACK" { format!("struct stack_st") // This logic should really be cleaned up - } else if is_struct && s != "point_conversion_form_t" + } else if is_struct + && s != "point_conversion_form_t" && s.chars().next().unwrap().is_lowercase() { format!("struct {}", s) @@ -111,9 +110,13 @@ fn main() { (s == "GENERAL_NAME" && field == "d") // union }); cfg.skip_signededness(|s| { - s.ends_with("_cb") || s.ends_with("_CB") || s.ends_with("_cb_fn") - || s.starts_with("CRYPTO_") || s == "PasswordCallback" - || s.ends_with("_cb_func") || s.ends_with("_cb_ex") + s.ends_with("_cb") + || s.ends_with("_CB") + || s.ends_with("_cb_fn") + || s.starts_with("CRYPTO_") + || s == "PasswordCallback" + || s.ends_with("_cb_func") + || s.ends_with("_cb_ex") }); cfg.field_name(|_s, field| { if field == "type_" { -- cgit v1.2.3