]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
tls: store list of subject alternative names
authorShivani Bhardwaj <shivani@oisf.net>
Tue, 26 Mar 2024 11:04:48 +0000 (16:34 +0530)
committerVictor Julien <victor@inliniac.net>
Wed, 22 May 2024 04:45:06 +0000 (06:45 +0200)
So far, the SANs were available as a part of IssuerDN via x509_parser
crate but SANs were not available to the SSLState* to be directly used
to setup and match against a sticky buffer.
Expose it to SSLStateConnp.

Feature 5234

rust/src/x509/mod.rs
src/app-layer-ssl.c
src/app-layer-ssl.h

index c87928cf17a8f5d7e4fe4cdbc3d73a63edca4fdd..e7cdbe885c2b99d6737bb15e4e7d9a6b172649f9 100644 (file)
@@ -23,7 +23,9 @@ use crate::common::rust_string_to_c;
 use nom7::Err;
 use std;
 use std::os::raw::c_char;
+use std::fmt;
 use x509_parser::prelude::*;
+use crate::x509::GeneralName;
 mod time;
 mod log;
 
@@ -46,6 +48,19 @@ pub enum X509DecodeError {
 
 pub struct X509(X509Certificate<'static>);
 
+pub struct SCGeneralName<'a>(&'a GeneralName<'a>);
+
+impl<'a> fmt::Display for SCGeneralName<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self.0 {
+            GeneralName::DNSName(s) => write!(f, "{}", s),
+            GeneralName::URI(s) => write!(f, "{}", s),
+            GeneralName::IPAddress(s) => write!(f, "{:?}", s),
+            _ => write!(f, "{}", self.0)
+        }
+    }
+}
+
 /// Attempt to parse a X.509 from input, and return a pointer to the parsed object if successful.
 ///
 /// # Safety
@@ -79,6 +94,37 @@ pub unsafe extern "C" fn rs_x509_get_subject(ptr: *const X509) -> *mut c_char {
     rust_string_to_c(subject)
 }
 
+#[no_mangle]
+pub unsafe extern "C" fn rs_x509_get_subjectaltname_len(ptr: *const X509) -> u16 {
+    if ptr.is_null() {
+        return 0;
+    }
+    let x509 = cast_pointer! {ptr, X509};
+    let san_list = x509.0.tbs_certificate.subject_alternative_name();
+    if let Ok(Some(sans)) = san_list {
+        // SAN length in a certificate is kept u16 following discussions at
+        // https://community.letsencrypt.org/t/why-sans-are-limited-to-100-domains-only
+        debug_validate_bug_on!(sans.value.general_names.len() == u16::MAX.into());
+        return sans.value.general_names.len() as u16;
+    }
+    return 0;
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rs_x509_get_subjectaltname_at(ptr: *const X509, idx: u16) -> *mut c_char {
+    if ptr.is_null() {
+        return std::ptr::null_mut();
+    }
+    let x509 = cast_pointer! {ptr, X509};
+    let san_list = x509.0.tbs_certificate.subject_alternative_name();
+    if let Ok(Some(sans)) = san_list {
+        let general_name = &sans.value.general_names[idx as usize];
+        let dns_name = SCGeneralName(general_name);
+        return rust_string_to_c(dns_name.to_string());
+    }
+    return std::ptr::null_mut();
+}
+
 #[no_mangle]
 pub unsafe extern "C" fn rs_x509_get_issuer(ptr: *const X509) -> *mut c_char {
     if ptr.is_null() {
index e4a7ee0c650c2caa798b432f6ddaa29398e89155..139c8c3beb899510ab07f9a70b979494c845ff17 100644 (file)
@@ -292,6 +292,8 @@ static inline int SafeMemcpy(void *dst, size_t dst_offset, size_t dst_size,
         }                                                                                          \
     } while (0)
 
+static void SSLStateCertSANFree(SSLStateConnp *connp);
+
 static void *SSLGetTx(void *state, uint64_t tx_id)
 {
     SSLState *ssl_state = (SSLState *)state;
@@ -546,6 +548,15 @@ static int TlsDecodeHSCertificate(SSLState *ssl_state, SSLStateConnp *connp,
         }
         connp->cert0_issuerdn = str;
 
+        connp->cert0_sans_len = rs_x509_get_subjectaltname_len(x509);
+        char **sans = SCCalloc(connp->cert0_sans_len, sizeof(char *));
+        if (sans == NULL) {
+            goto error;
+        }
+        for (uint16_t i = 0; i < connp->cert0_sans_len; i++) {
+            sans[i] = rs_x509_get_subjectaltname_at(x509, i);
+        }
+        connp->cert0_sans = sans;
         str = rs_x509_get_serial(x509);
         if (str == NULL) {
             err_code = ERR_INVALID_SERIAL;
@@ -584,6 +595,8 @@ error:
         TlsDecodeHSCertificateErrSetEvent(ssl_state, err_code);
     if (x509 != NULL)
         rs_x509_free(x509);
+
+    SSLStateCertSANFree(connp);
     return -1;
 
 invalid_cert:
@@ -2832,6 +2845,16 @@ static void *SSLStateAlloc(void *orig_state, AppProto proto_orig)
     return (void *)ssl_state;
 }
 
+static void SSLStateCertSANFree(SSLStateConnp *connp)
+{
+    if (connp->cert0_sans) {
+        for (uint16_t i = 0; i < connp->cert0_sans_len; i++) {
+            rs_cstring_free(connp->cert0_sans[i]);
+        }
+        SCFree(connp->cert0_sans);
+    }
+}
+
 /**
  * \internal
  * \brief Function to free the SSL state memory.
@@ -2882,6 +2905,9 @@ static void SSLStateFree(void *p)
     if (ssl_state->server_connp.hs_buffer)
         SCFree(ssl_state->server_connp.hs_buffer);
 
+    SSLStateCertSANFree(&ssl_state->server_connp);
+    SSLStateCertSANFree(&ssl_state->client_connp);
+
     AppLayerDecoderEventsFreeEvents(&ssl_state->tx_data.events);
 
     if (ssl_state->tx_data.de_state != NULL) {
index fb9c83f801b645f2644105f518e92496431c5df9..90217b037c33c10fc59fcd29df87d693a40b3673 100644 (file)
@@ -254,6 +254,8 @@ typedef struct SSLStateConnp_ {
     int64_t cert0_not_after;
     char *cert0_fingerprint;
 
+    char **cert0_sans;
+    uint16_t cert0_sans_len;
     /* ssl server name indication extension */
     char *sni;