Skip to content

Commit 5ed3eaa

Browse files
committed
Add a feature to build against openssl-sys low-level bindings
Linking applications against different bindings to the same library (openssl) is undefined behavior in rust ecosystem. This is adding another feature to get the low-level binding from openssl-sys crate (enabled by default). Unfortunately, it does not expose all the constants and defines, that need to be defined manually. The direct static linking to libfips is preserved, as well as the old dynamic linking through our bindgen for now. Signed-off-by: Jakub Jelen <jjelen@redhat.com>
1 parent 8a29f39 commit 5ed3eaa

8 files changed

Lines changed: 357 additions & 109 deletions

File tree

.github/workflows/build.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
fail-fast: false
1919
matrix:
2020
name: [standard, i686, minimal, pqc, no_sha1]
21-
linking: [dynamic, static, fips]
21+
linking: [dynamic, static, fips, openssl-sys]
2222
build: [debug, release]
2323
db: [sqlitedb, nssdb]
2424
exclude:
@@ -162,6 +162,8 @@ jobs:
162162
FEATURES="${FEATURES},${FEATURE_SET},ossl400"
163163
elif [ "${{ matrix.linking }}" = "fips" ]; then
164164
FEATURES="${FEATURES},fips"
165+
elif [ "${{ matrix.linking }}" = "openssl-sys" ]; then
166+
FEATURES="${FEATURES},${FEATURE_SET},openssl-sys"
165167
fi
166168
167169
if [ "${{ matrix.build }}" = "release" ]; then

.github/workflows/rpm.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ jobs:
5656
'crate(num-integer/default)' 'crate(num-traits/default)' \
5757
'crate(pkg-config/default)' 'crate(rusqlite/default)' \
5858
'crate(serde/default)' 'crate(serde/derive)' 'crate(serde_json/default)' \
59-
'crate(serial_test/default)' \
59+
'crate(serial_test/default)' 'crate(openssl-sys/bindgen)' \
6060
'crate(toml)' 'crate(toml/display)' 'crate(toml/parse)' 'crate(toml/serde)' \
6161
'crate(uuid/default)' 'crate(uuid/v4)' 'crate(uuid/v8)' 'crate(cryptoki/default)' \
6262
'crate(vsprintf/default)' 'crate(quick-xml/default)' 'crate(quick-xml/serialize)'

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ fips = ["ossl/fips", "sqlitedb", "rusqlite/bundled", "aes", "ecc_all", "ffdh",
112112
"hash_all", "kdf_all", "rsa", "pqc", "dep:vsprintf"]
113113

114114
dynamic = ["ossl/dynamic"] # Builds against system libcrypto.so
115+
openssl-sys = ["ossl/openssl-sys"] # Builds against system libcrypto through openssl-sys
115116

116117
log = ["dep:log", "dep:simplelog", "ossl/log"] # Enables error/info tracing
117118
slow = [] # Enables slow tests

ossl/Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
[package]
22
name = "ossl"
3-
links = "openssl"
43
version.workspace = true
54
edition.workspace = true
65
description = "OpenSSL version 3+ bindings to modern EVP APIs"
@@ -16,19 +15,24 @@ bindgen = "0.72"
1615
pkg-config = "0.3"
1716

1817
[dependencies]
18+
bitflags = "2.4.1"
1919
cfg-if = "1.0.0"
2020
libc = "0.2.151"
2121
log = { version = "0.4.27", default-features = false, features = ["std"], optional = true }
2222
vsprintf = { version = "2.0.0", optional = true }
23+
openssl-sys = { version = "0.9", features = ["bindgen"], optional = true }
2324

2425
[dev-dependencies]
2526
hex = "0.4.3"
2627
serial_test = "3.1.1"
2728

2829
[features]
30+
default = ["openssl-sys"]
31+
2932
ossl320 = [] # Requires at a minimum OpenSSL 3.2.0
3033
ossl350 = [] # Requires at a minimum OpenSSL 3.5.0
3134
ossl400 = [] # Requires at minimum OpenSSL 4.0
35+
openssl-sys = ["dep:openssl-sys"]
3236
dynamic = [] # Builds against system libcrypto.so
3337
fips = ["ossl350"] # Builds against sources and libfips.a instead of libcrypto
3438
log = ["dep:log", "dep:vsprintf"] # Error tracing using log crate

ossl/build.rs

Lines changed: 60 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -12,66 +12,52 @@ const OPENSSL_3_2_0: i64 = 0x30200000;
1212
const OPENSSL_3_5_0: i64 = 0x30500000;
1313
const OPENSSL_4_0_0: i64 = 0x40000000;
1414

15+
fn check_ossl_version(version: i64) {
16+
if version < OPENSSL_4_0_0 {
17+
#[cfg(feature = "ossl400")]
18+
panic!("OpenSSL 4.0.0 or later is required");
19+
}
20+
if version < OPENSSL_3_5_0 {
21+
#[cfg(feature = "ossl350")]
22+
panic!("OpenSSL 3.5.0 or later is required");
23+
}
24+
if version < OPENSSL_3_2_0 {
25+
#[cfg(feature = "ossl320")]
26+
panic!("OpenSSL 3.2.0 or later is required");
27+
}
28+
if version < OPENSSL_3_0_7 {
29+
panic!(
30+
"OpenSSL 3.0.7 is the minimum viable version. Found {:x}",
31+
version
32+
);
33+
}
34+
/* Emit versions we found, versions stack, so code
35+
* just need to build conditionalized just to the older version
36+
* that introduced the desired feature */
37+
println!("cargo::rustc-cfg=ossl_v307");
38+
if version >= OPENSSL_3_2_0 {
39+
println!("cargo::rustc-cfg=ossl_v320");
40+
}
41+
if version >= OPENSSL_3_5_0 {
42+
println!("cargo::rustc-cfg=ossl_v350");
43+
}
44+
if version >= OPENSSL_4_0_0 {
45+
println!("cargo::rustc-cfg=ossl_v400");
46+
}
47+
}
48+
1549
impl bindgen::callbacks::ParseCallbacks for OsslCallbacks {
1650
fn int_macro(
1751
&self,
1852
name: &str,
1953
value: i64,
2054
) -> Option<bindgen::callbacks::IntKind> {
2155
if name == "OPENSSL_VERSION_NUMBER" {
22-
if value < OPENSSL_4_0_0 {
23-
#[cfg(feature = "ossl400")]
24-
panic!("OpenSSL 4.0.0 or later is required");
25-
}
26-
if value < OPENSSL_3_5_0 {
27-
#[cfg(feature = "ossl350")]
28-
panic!("OpenSSL 3.5.0 or later is required");
29-
}
30-
if value < OPENSSL_3_2_0 {
31-
#[cfg(feature = "ossl320")]
32-
panic!("OpenSSL 3.2.0 or later is required");
33-
}
34-
if value < OPENSSL_3_0_7 {
35-
panic!(
36-
"OpenSSL 3.0.7 is the minimum viable version. Found {:x}",
37-
value
38-
);
39-
}
40-
/* Emit versions we found, versions stack, so code
41-
* just need to build conditionalized just to the older version
42-
* that introduced the desired feature */
43-
println!("cargo::rustc-cfg=ossl_v307");
44-
if value >= OPENSSL_3_2_0 {
45-
println!("cargo::rustc-cfg=ossl_v320");
46-
}
47-
if value >= OPENSSL_3_5_0 {
48-
println!("cargo::rustc-cfg=ossl_v350");
49-
}
50-
if value >= OPENSSL_4_0_0 {
51-
println!("cargo::rustc-cfg=ossl_v400");
52-
}
56+
check_ossl_version(value);
5357
}
5458

5559
None
5660
}
57-
58-
fn str_macro(&self, name: &str, _value: &[u8]) {
59-
if name == "OSSL_PKEY_PARAM_SLH_DSA_SEED" {
60-
println!("cargo::rustc-cfg=ossl_slhdsa")
61-
}
62-
if name == "OSSL_PKEY_PARAM_ML_DSA_SEED" {
63-
println!("cargo::rustc-cfg=ossl_mldsa")
64-
}
65-
if name == "OSSL_PKEY_PARAM_ML_KEM_SEED" {
66-
println!("cargo::rustc-cfg=ossl_mlkem")
67-
}
68-
}
69-
70-
fn func_macro(&self, name: &str, _value: &[&[u8]]) {
71-
if name == "OSSL_PARAM_clear_free" {
72-
println!("cargo::rustc-cfg=param_clear_free")
73-
}
74-
}
7561
}
7662

7763
fn ossl_bindings(args: &mut Vec<String>, out_file: &Path) {
@@ -322,19 +308,37 @@ fn set_pretty_panic() {
322308
fn main() {
323309
set_pretty_panic();
324310

325-
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
326-
let ossl_bindings = out_path.join("ossl_bindings.rs");
327-
328311
/* Always emit known configs */
329312
println!(
330-
"cargo::rustc-check-cfg=cfg(ossl_v307,ossl_v320,ossl_v350,ossl_v400,ossl_mldsa,ossl_mlkem,ossl_slhdsa,param_clear_free)"
313+
"cargo::rustc-check-cfg=cfg(ossl_v307,ossl_v320,ossl_v350,ossl_v400)"
331314
);
332315

333316
/* OpenSSL Cryptography */
334-
if cfg!(feature = "dynamic") {
335-
use_system_ossl(&ossl_bindings);
317+
if cfg!(feature = "openssl-sys") {
318+
if let Ok(v) = env::var("DEP_OPENSSL_VERSION_NUMBER") {
319+
let version = i64::from_str_radix(&v, 16).unwrap();
320+
check_ossl_version(version);
321+
// Proxy it also to the code for the api_level() API
322+
println!("cargo:rustc-env=DEP_OPENSSL_VERSION_NUMBER={}", v);
323+
324+
// backward compatible OPENSSL_FULL_VERSION_STR
325+
let major = (version >> 28) & 0xF;
326+
let minor = (version >> 20) & 0xFF;
327+
let patch = (version >> 4) & 0xFF;
328+
let version_string = format!("{}.{}.{}", major, minor, patch);
329+
println!(
330+
"cargo:rustc-env=OPENSSL_FULL_VERSION_STR={}",
331+
version_string
332+
);
333+
}
336334
} else {
337-
build_ossl(&ossl_bindings);
335+
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
336+
let ossl_bindings = out_path.join("ossl_bindings.rs");
337+
if cfg!(feature = "dynamic") {
338+
use_system_ossl(&ossl_bindings);
339+
} else {
340+
build_ossl(&ossl_bindings);
341+
}
338342
}
339343

340344
println!("cargo:rerun-if-changed=build.rs");

ossl/src/lib.rs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
/// Part of this module is automatically generated by bindgen from the OpenSSL
99
/// Headers and includes a selection of functions and other items needed to
1010
/// access the libcrypto/libfips functions needed.
11+
#[cfg(not(feature = "openssl-sys"))]
1112
pub mod bindings {
1213
#![allow(non_upper_case_globals)]
1314
#![allow(non_camel_case_types)]
@@ -16,6 +17,15 @@ pub mod bindings {
1617
#![allow(unnecessary_transmutes)]
1718
include!(concat!(env!("OUT_DIR"), "/ossl_bindings.rs"));
1819
}
20+
#[cfg(feature = "openssl-sys")]
21+
pub mod bindings {
22+
#![allow(non_upper_case_globals)]
23+
include!("non-openssl-sys-bindings.rs");
24+
pub use openssl_sys::*;
25+
// backward compatible definitions
26+
pub const OPENSSL_FULL_VERSION_STR: &[u8] =
27+
concat!(env!("OPENSSL_FULL_VERSION_STR"), "\0").as_bytes();
28+
}
1929

2030
use std::borrow::Cow;
2131
use std::ffi::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong, c_void, CStr};
@@ -43,10 +53,27 @@ pub mod fips;
4353
/// This returns a triplet of u8 values representing Major, Minor, and
4454
/// Patch version.
4555
pub fn api_level() -> (u8, u8, u8) {
46-
let patch: u8 = (OPENSSL_API_LEVEL & 0xff) as u8;
47-
let minor: u8 = ((OPENSSL_API_LEVEL & 0xff00) >> 8) as u8;
48-
let major: u8 = ((OPENSSL_API_LEVEL & 0xff0000) >> 16) as u8;
49-
(major, minor, patch)
56+
#[cfg(not(feature = "openssl-sys"))]
57+
{
58+
// This is the 0xMMNNPP (Major, miNor, Patch) scheme
59+
let version = OPENSSL_API_LEVEL;
60+
let patch: u8 = (version & 0xff) as u8;
61+
let minor: u8 = ((version & 0xff00) >> 8) as u8;
62+
let major: u8 = ((version & 0xff0000) >> 16) as u8;
63+
(major, minor, patch)
64+
}
65+
66+
#[cfg(feature = "openssl-sys")]
67+
{
68+
// This is the 0xMNN00PP0 (Major, miNor, Patch) scheme
69+
let v = env!("DEP_OPENSSL_VERSION_NUMBER");
70+
let version = u64::from_str_radix(&v, 16).unwrap();
71+
72+
let patch: u8 = ((version & 0xff0) >> 4) as u8;
73+
let minor: u8 = ((version & 0xff00000) >> 20) as u8;
74+
let major: u8 = ((version & 0xff0000000) >> 28) as u8;
75+
(major, minor, patch)
76+
}
5077
}
5178

5279
/// Securely zeroizes a memory slice using `OPENSSL_cleanse`.
@@ -496,13 +523,13 @@ pub struct OsslParam<'a>(OsslParamBuilder<'a>);
496523
impl Drop for OsslParamBuilder<'_> {
497524
fn drop(&mut self) {
498525
if self.freeptr {
499-
#[cfg(param_clear_free)]
526+
#[cfg(ossl_v400)]
500527
unsafe {
501528
OSSL_PARAM_clear_free(
502529
self.p.as_ref().as_ptr() as *mut OSSL_PARAM
503530
);
504531
}
505-
#[cfg(not(param_clear_free))]
532+
#[cfg(not(ossl_v400))]
506533
unsafe {
507534
OSSL_PARAM_free(self.p.as_ref().as_ptr() as *mut OSSL_PARAM);
508535
}

0 commit comments

Comments
 (0)