Coverage Report

Created: 2025-12-31 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/rust/src/x509/mod.rs
Line
Count
Source
1
/* Copyright (C) 2019-2020 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
//! Module for SSL/TLS X.509 certificates parser and decoder.
19
20
// written by Pierre Chifflier  <chifflier@wzdftpd.net>
21
22
use crate::common::rust_string_to_c;
23
use nom7::Err;
24
use std;
25
use std::os::raw::c_char;
26
use x509_parser::prelude::*;
27
mod time;
28
mod log;
29
30
#[repr(u32)]
31
pub enum X509DecodeError {
32
    _Success = 0,
33
    /// Generic decoding error
34
    InvalidCert,
35
    /// Some length does not match, or certificate is incomplete
36
    InvalidLength,
37
    InvalidVersion,
38
    InvalidSerial,
39
    InvalidAlgorithmIdentifier,
40
    InvalidX509Name,
41
    InvalidDate,
42
    InvalidExtensions,
43
    /// DER structure is invalid
44
    InvalidDER,
45
}
46
47
pub struct X509(X509Certificate<'static>);
48
49
/// Attempt to parse a X.509 from input, and return a pointer to the parsed object if successful.
50
///
51
/// # Safety
52
///
53
/// input must be a valid buffer of at least input_len bytes
54
#[no_mangle]
55
57.3k
pub unsafe extern "C" fn rs_x509_decode(
56
57.3k
    input: *const u8,
57
57.3k
    input_len: u32,
58
57.3k
    err_code: *mut u32,
59
57.3k
) -> *mut X509 {
60
57.3k
    let slice = std::slice::from_raw_parts(input, input_len as usize);
61
57.3k
    let res = X509Certificate::from_der(slice);
62
57.3k
    match res {
63
1.76k
        Ok((_rem, cert)) => Box::into_raw(Box::new(X509(cert))),
64
55.5k
        Err(e) => {
65
55.5k
            let error = x509_parse_error_to_errcode(&e);
66
55.5k
            *err_code = error as u32;
67
55.5k
            std::ptr::null_mut()
68
        }
69
    }
70
57.3k
}
71
72
#[no_mangle]
73
1.76k
pub unsafe extern "C" fn rs_x509_get_subject(ptr: *const X509) -> *mut c_char {
74
1.76k
    if ptr.is_null() {
75
0
        return std::ptr::null_mut();
76
1.76k
    }
77
1.76k
    let x509 = cast_pointer! {ptr, X509};
78
1.76k
    let subject = x509.0.tbs_certificate.subject.to_string();
79
1.76k
    rust_string_to_c(subject)
80
1.76k
}
81
82
#[no_mangle]
83
1.75k
pub unsafe extern "C" fn rs_x509_get_issuer(ptr: *const X509) -> *mut c_char {
84
1.75k
    if ptr.is_null() {
85
0
        return std::ptr::null_mut();
86
1.75k
    }
87
1.75k
    let x509 = cast_pointer! {ptr, X509};
88
1.75k
    let issuer = x509.0.tbs_certificate.issuer.to_string();
89
1.75k
    rust_string_to_c(issuer)
90
1.75k
}
91
92
#[no_mangle]
93
1.73k
pub unsafe extern "C" fn rs_x509_get_serial(ptr: *const X509) -> *mut c_char {
94
1.73k
    if ptr.is_null() {
95
0
        return std::ptr::null_mut();
96
1.73k
    }
97
1.73k
    let x509 = cast_pointer! {ptr, X509};
98
1.73k
    let raw_serial = x509.0.tbs_certificate.raw_serial();
99
24.8k
    let v: Vec<_> = raw_serial.iter().map(|x| format!("{:02X}", x)).collect();
100
1.73k
    let serial = v.join(":");
101
1.73k
    rust_string_to_c(serial)
102
1.73k
}
103
104
/// Extract validity from input X.509 object
105
///
106
/// # Safety
107
///
108
/// ptr must be a valid object obtained using `rs_x509_decode`
109
#[no_mangle]
110
1.73k
pub unsafe extern "C" fn rs_x509_get_validity(
111
1.73k
    ptr: *const X509,
112
1.73k
    not_before: *mut i64,
113
1.73k
    not_after: *mut i64,
114
1.73k
) -> i32 {
115
1.73k
    if ptr.is_null() {
116
0
        return -1;
117
1.73k
    }
118
1.73k
    let x509 = &*ptr;
119
1.73k
    let n_b = x509.0.validity().not_before.timestamp();
120
1.73k
    let n_a = x509.0.validity().not_after.timestamp();
121
1.73k
    *not_before = n_b;
122
1.73k
    *not_after = n_a;
123
1.73k
    0
124
1.73k
}
125
126
/// Free a X.509 object allocated by Rust
127
///
128
/// # Safety
129
///
130
/// ptr must be a valid object obtained using `rs_x509_decode`
131
#[no_mangle]
132
1.76k
pub unsafe extern "C" fn rs_x509_free(ptr: *mut X509) {
133
1.76k
    if ptr.is_null() {
134
0
        return;
135
1.76k
    }
136
1.76k
    drop(Box::from_raw(ptr));
137
1.76k
}
138
139
55.5k
fn x509_parse_error_to_errcode(e: &Err<X509Error>) -> X509DecodeError {
140
55.5k
    match e {
141
35.7k
        Err::Incomplete(_) => X509DecodeError::InvalidLength,
142
19.8k
        Err::Error(e) | Err::Failure(e) => match e {
143
1.19k
            X509Error::InvalidVersion => X509DecodeError::InvalidVersion,
144
1.98k
            X509Error::InvalidSerial => X509DecodeError::InvalidSerial,
145
129
            X509Error::InvalidAlgorithmIdentifier => X509DecodeError::InvalidAlgorithmIdentifier,
146
0
            X509Error::InvalidX509Name => X509DecodeError::InvalidX509Name,
147
3.74k
            X509Error::InvalidDate => X509DecodeError::InvalidDate,
148
50
            X509Error::InvalidExtensions => X509DecodeError::InvalidExtensions,
149
8.84k
            X509Error::Der(_) => X509DecodeError::InvalidDER,
150
3.87k
            _ => X509DecodeError::InvalidCert,
151
        },
152
    }
153
55.5k
}