rust-openssl / rust-openssl/rust-openssl
Raw FFI bindings to platform libraries like ASN1_STRING_set
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 1.6k
- Forks
- 841
- Avg merge
- 6h 51m
- Merged PRs (30d)
- 4
Description
I confirmed that Rust's 'openssl' crate can trigger this via 'extern "C"' calls to 'ASN1_STRING_set'.
To mitigate this and validate behavior, I developed a C-to-Rust tool that generates a Rust-native 'ASN1_STRING_set' implementation, fully replacing the C calls (https://www.cve.org/CVERecord?id=CVE-2021-3712).
Tested on 1,000 test cases, achieving 100% accuracy.
Correctly handles glibc deviations and prevents out-of-bounds writes in Rust.
I understand that 'openssl' crate itself does not intend to provide alternative implementations of system libraries. I would like to add some context from my testing: I verified the vulnerability in a controlled virtual environment. Even when using the latest 'openssl' crate, if a certificate has been directly constructed by the application instead of loading it via the OpenSSL parsing functions, and the certificate contains non NUL terminated ASN1_STRING structures via 'openssl::ASN1_STRING_set' calls. This confirms that the issue is not caused by 'openssl' itself but by the musl implementation present at runtime. For Rust users who need immediate mitigation, I have developed a Rust-native ASN1_STRING_set implementation that correctly handles openssl deviations while possible read buffer overflow. I hope this information helps clarify the impact for Rust users relying on 'openssl' bindings.
Rust program (rust-openssl/rust-openssl/blob/master/openssl-sys/src/handwritten/asn1.rs)
extern "C" {
pub fn ASN1_STRING_type_new(ty: c_int) -> *mut ASN1_STRING;
pub fn ASN1_STRING_get0_data(x: *const ASN1_STRING) -> *const c_uchar;
#[cfg(all(libressl, not(libressl430)))]
pub fn ASN1_STRING_data(x: *mut ASN1_STRING) -> *mut c_uchar;
pub fn ASN1_STRING_new() -> *mut ASN1_STRING;
pub fn ASN1_OCTET_STRING_new() -> *mut ASN1_OCTET_STRING;
pub fn ASN1_STRING_free(x: *mut ASN1_STRING);
pub fn ASN1_STRING_length(x: *const ASN1_STRING) -> c_int;
pub fn ASN1_STRING_set(x: *mut ASN1_STRING, data: *const c_void, len_in: c_int) -> c_int;
pub fn ASN1_OCTET_STRING_set(
x: *mut ASN1_OCTET_STRING,
data: *const c_uchar,
len_in: c_int,
) -> c_int;
pub fn ASN1_BIT_STRING_free(x: *mut ASN1_BIT_STRING);
pub fn ASN1_OCTET_STRING_free(x: *mut ASN1_OCTET_STRING);
pub fn ASN1_GENERALIZEDTIME_new() -> *mut ASN1_GENERALIZEDTIME;
pub fn ASN1_GENERALIZEDTIME_free(tm: *mut ASN1_GENERALIZEDTIME);
pub fn ASN1_GENERALIZEDTIME_print(b: *mut BIO, tm: *const ASN1_GENERALIZEDTIME) -> c_int;
pub fn ASN1_GENERALIZEDTIME_set_string(
s: *mut ASN1_GENERALIZEDTIME,
str: *const c_char,
) -> c_int;
pub fn ASN1_TIME_new() -> *mut ASN1_TIME;
pub fn ASN1_TIME_diff(
pday: *mut c_int,
psec: *mut c_int,
from: *const ASN1_TIME,
to: *const ASN1_TIME,
) -> c_int;
pub fn ASN1_TIME_free(tm: *mut ASN1_TIME);
pub fn ASN1_TIME_print(b: *mut BIO, tm: *const ASN1_TIME) -> c_int;
pub fn ASN1_TIME_set(from: *mut ASN1_TIME, to: time_t) -> *mut ASN1_TIME;
pub fn ASN1_INTEGER_free(x: *mut ASN1_INTEGER);
pub fn ASN1_INTEGER_dup(a: *const ASN1_INTEGER) -> *mut ASN1_INTEGER;
pub fn ASN1_INTEGER_get(dest: *const ASN1_INTEGER) -> c_long;
pub fn ASN1_INTEGER_set(dest: *mut ASN1_INTEGER, value: c_long) -> c_int;
pub fn ASN1_INTEGER_cmp(a: *const ASN1_INTEGER, b: *const ASN1_INTEGER) -> c_int;
pub fn BN_to_ASN1_INTEGER(bn: *const BIGNUM, ai: *mut ASN1_INTEGER) -> *mut ASN1_INTEGER;
pub fn ASN1_INTEGER_to_BN(ai: *const ASN1_INTEGER, bn: *mut BIGNUM) -> *mut BIGNUM;
pub fn ASN1_TIME_set_string(s: *mut ASN1_TIME, str: *const c_char) -> c_int;
#[cfg(any(ossl111, libressl360))]
pub fn ASN1_TIME_set_string_X509(s: *mut ASN1_TIME, str: *const c_char) -> c_int;
pub fn ASN1_ENUMERATED_free(a: *mut ASN1_ENUMERATED);
#[cfg(ossl110)]
pub fn ASN1_ENUMERATED_get_int64(pr: *mut i64, a: *const ASN1_ENUMERATED) -> c_int;
pub fn ASN1_TYPE_new() -> *mut ASN1_TYPE;
pub fn ASN1_TYPE_set(a: *mut ASN1_TYPE, type_: c_int, value: *mut c_void);
pub fn ASN1_TYPE_free(x: *mut ASN1_TYPE);
pub fn d2i_ASN1_TYPE(
k: *mut *mut ASN1_TYPE,
buf: *mut *const u8,
len: c_long,
) -> *mut ASN1_TYPE;
}
-Replace function
// [c2rust:begin] Rust implementation for ASN1_STRING_set
// [c2rust] Generated from IR — manual review required
#[repr(C)]
struct ASN1_STRING {
pub data: *mut u8,
pub length: usize,
}
#[allow(dead_code, non_snake_case, unused_imports)]
mod __ASN1_STRING_set_stubs {
pub fn ERR_raise<T0, T1>(_: T0, _: T1) -> usize where T0: Copy, T1: Copy { unimplemented!("stub: ERR_raise") }
pub fn OPENSSL_realloc<T0, T1>(_: T0, _: T1) -> *mut u8 where T0: Copy, T1: Copy { unimplemented!("stub: OPENSSL_realloc") }
pub fn default<>() -> usize { unimplemented!("stub: default") }
pub fn memcpy<T0, T1, T2>(_: T0, _: T1, _: T2) -> usize where T0: Copy, T1: Copy, T2: Copy { unimplemented!("stub: memcpy") }
pub fn null_mut<>() -> usize { unimplemented!("stub: null_mut") }
pub fn strlen<T0>(_: T0) -> usize where T0: Copy { unimplemented!("stub: strlen") }
}
use __ASN1_STRING_set_stubs::*;
pub fn ASN1_STRING_set(
str: *mut ASN1_STRING,
_data: *mut (),
len_in: i32,
) -> i32 {
unsafe {
let mut c: *mut u8 = Default::default();
let mut data: *mut u8 = Default::default();
let mut len: usize = 0;
let mut NULL: usize = 0;
let mut ASN1_R_TOO_LARGE: usize = 0;
let mut ERR_LIB_ASN1: usize = 0;
let mut INT_MAX: usize = 0;
data = _data as *mut u8;
if len_in < 0 {
if data == std::ptr::null_mut() {
return 0;
}
len = strlen(data);
// else
len = len_in as usize;
}
if len > INT_MAX - 1 {
ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_LARGE);
return 0;
}
if (*str).length <= len || (*str).data == std::ptr::null_mut() {
c = (*str).data as *mut u8;
(*str).data = OPENSSL_realloc(c, len + 1);
if (*str).data == std::ptr::null_mut() {
(*str).data = c;
return 0;
}
}
(*str).length = len;
if data!= std::ptr::null_mut() {
memcpy((*str).data, data, len);
unsafe { (*str).data.add(len as usize).write(b'\0'); }
}
return 1;
}
}
// [c2rust:end] Rust implementation for ASN1_STRING_set
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with openssl-sys/src/handwritten/asn1.rs and review the ASN1_STRING_set binding against the reported CVE behavior and proposed Rust implementation. The issue does not define an accepted change or tests for completion, so scope and validation criteria would need to be agreed before implementation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend, security
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100