]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
tls: fix date logging for dates before 1970
authorJason Ish <jason.ish@oisf.net>
Fri, 27 Jan 2023 05:03:22 +0000 (23:03 -0600)
committerVictor Julien <vjulien@oisf.net>
Sun, 29 Jan 2023 08:30:19 +0000 (09:30 +0100)
The Rust time crate used by the x509-parser crate represents dates
before 1970 as negative numbers which do not survive the conversion to
SCTime_t and formatting with the current time formatting functions.

Instead of fixing our formatting functions to handle such dates,
create a Rust function for logging TLS dates directly to JSON using
the time crate that handles such dates properly.

Also add a FFI function for formatting to a provided C buffer for the
legacy tls-log.

Issue: 5817

rust/src/x509/log.rs [new file with mode: 0644]
rust/src/x509/mod.rs
rust/src/x509/time.rs [new file with mode: 0644]
src/app-layer-ssl.c
src/app-layer-ssl.h
src/log-tlslog.c
src/output-json-tls.c

diff --git a/rust/src/x509/log.rs b/rust/src/x509/log.rs
new file mode 100644 (file)
index 0000000..adb6464
--- /dev/null
@@ -0,0 +1,40 @@
+/* Copyright (C) 2023 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+use crate::jsonbuilder::JsonBuilder;
+use crate::x509::time::format_timestamp;
+use std::ffi::CStr;
+use std::os::raw::c_char;
+
+/// Helper function to log a TLS timestamp from C to JSON with the
+/// provided key. The format of the timestamp is ISO 8601 timestamp
+/// with no sub-second or offset information as UTC is assumed.
+///
+/// # Safety
+///
+/// FFI function that dereferences pointers from C.
+#[no_mangle]
+pub unsafe extern "C" fn sc_x509_log_timestamp(
+    jb: &mut JsonBuilder, key: *const c_char, timestamp: i64,
+) -> bool {
+    if let Ok(key) = CStr::from_ptr(key).to_str() {
+        if let Ok(timestamp) = format_timestamp(timestamp) {
+            return jb.set_string(key, &timestamp).is_ok();
+        }
+    }
+    false
+}
index 03c9cc5aa0b32c34f8650c28395c5cf217670eaa..1b00101e63a41d8571d9d32c5d17feec125e73c2 100644 (file)
@@ -22,6 +22,8 @@ use nom7::Err;
 use std;
 use std::os::raw::c_char;
 use x509_parser::prelude::*;
+mod time;
+mod log;
 
 #[repr(u32)]
 pub enum X509DecodeError {
diff --git a/rust/src/x509/time.rs b/rust/src/x509/time.rs
new file mode 100644 (file)
index 0000000..2e4a28d
--- /dev/null
@@ -0,0 +1,72 @@
+/* Copyright (C) 2023 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+use std::ffi::CString;
+use std::os::raw::c_char;
+use time::macros::format_description;
+
+/// Format a timestamp in an ISO format suitable for TLS logging.
+///
+/// Negative timestamp values are used for dates prior to 1970.
+pub fn format_timestamp(timestamp: i64) -> Result<String, time::error::Error> {
+    let format = format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]");
+    let ts = time::OffsetDateTime::from_unix_timestamp(timestamp)?;
+    let formatted = ts.format(&format)?;
+    Ok(formatted)
+}
+
+/// Format a x509 ISO timestamp into the provided C buffer.
+///
+/// Returns false if an error occurs, otherwise true is returned if
+/// the timestamp is properly formatted into the provided buffer.
+///
+/// # Safety
+///
+/// Access buffers from C that are expected to be valid.
+#[no_mangle]
+pub unsafe extern "C" fn sc_x509_format_timestamp(
+    timestamp: i64, buf: *mut c_char, size: usize,
+) -> bool {
+    let ctimestamp = match format_timestamp(timestamp) {
+        Ok(ts) => match CString::new(ts) {
+            Ok(ts) => ts,
+            Err(_) => return false,
+        },
+        Err(_) => return false,
+    };
+    let bytes = ctimestamp.as_bytes_with_nul();
+
+    if size < bytes.len() {
+        false
+    } else {
+        // Convert buf into a slice we can copy into.
+        let buf = std::slice::from_raw_parts_mut(buf as *mut u8, size);
+        buf[0..bytes.len()].copy_from_slice(bytes);
+        true
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn test_format_timestamp() {
+        assert_eq!("1969-12-31T00:00:00", format_timestamp(-86400).unwrap());
+        assert_eq!("2038-12-31T00:10:03", format_timestamp(2177367003).unwrap());
+    }
+}
index 4496bf4fbd50b18854ad84c4d7b994f4d5164985..59856e44fdcbd3f12308fbc08ce8562c0d9b6637 100644 (file)
@@ -521,8 +521,6 @@ static int TlsDecodeHSCertificate(SSLState *ssl_state, SSLStateConnp *connp,
     /* only store fields from the first certificate in the chain */
     if (certn == 0 && connp->cert0_subject == NULL && connp->cert0_issuerdn == NULL &&
             connp->cert0_serial == NULL) {
-        int64_t not_before, not_after;
-
         x509 = rs_x509_decode(input, cert_len, &err_code);
         if (x509 == NULL) {
             TlsDecodeHSCertificateErrSetEvent(ssl_state, err_code);
@@ -550,13 +548,11 @@ static int TlsDecodeHSCertificate(SSLState *ssl_state, SSLStateConnp *connp,
         }
         connp->cert0_serial = str;
 
-        rc = rs_x509_get_validity(x509, &not_before, &not_after);
+        rc = rs_x509_get_validity(x509, &connp->cert0_not_before, &connp->cert0_not_after);
         if (rc != 0) {
             err_code = ERR_EXTRACT_VALIDITY;
             goto error;
         }
-        connp->cert0_not_before = (time_t)not_before;
-        connp->cert0_not_after = (time_t)not_after;
 
         rs_x509_free(x509);
         x509 = NULL;
index 5bd703d15e10b660ab9387bcc3bf6b64c9bcc544..79933e6fcae98a60d6966c3b0fd7d39aa8efd88d 100644 (file)
@@ -252,8 +252,8 @@ typedef struct SSLStateConnp_ {
     char *cert0_subject;
     char *cert0_issuerdn;
     char *cert0_serial;
-    time_t cert0_not_before;
-    time_t cert0_not_after;
+    int64_t cert0_not_before;
+    int64_t cert0_not_after;
     char *cert0_fingerprint;
 
     /* ssl server name indication extension */
index 38d77368763c19c8604ae9d68397bed48467980a..dc32d3d814d60a2e96dfec052915baf6d0265028 100644 (file)
@@ -290,12 +290,12 @@ static void LogTlsLogVersion(MemBuffer *buffer, uint16_t version)
     MemBufferWriteString(buffer, "VERSION='%s'", ssl_version);
 }
 
-static void LogTlsLogDate(MemBuffer *buffer, const char *title, time_t *date)
+static void LogTlsLogDate(MemBuffer *buffer, const char *title, int64_t *date)
 {
     char timebuf[64] = {0};
-    const SCTime_t ts = SCTIME_FROM_SECS(*date);
-    CreateUtcIsoTimeString(ts, timebuf, sizeof(timebuf));
-    MemBufferWriteString(buffer, "%s='%s'", title, timebuf);
+    if (sc_x509_format_timestamp(*date, timebuf, sizeof(timebuf))) {
+        MemBufferWriteString(buffer, "%s='%s'", title, timebuf);
+    }
 }
 
 static void LogTlsLogString(MemBuffer *buffer, const char *title,
index 8fc30213a8fe0b720256744b39d14033f0ec83fd..9771f4d1cd7c0824e08dbc99e3c8e4f47d17248e 100644 (file)
@@ -168,20 +168,14 @@ static void JsonTlsLogVersion(JsonBuilder *js, SSLState *ssl_state)
 static void JsonTlsLogNotBefore(JsonBuilder *js, SSLState *ssl_state)
 {
     if (ssl_state->server_connp.cert0_not_before != 0) {
-        char timebuf[64];
-        SCTime_t ts = SCTIME_FROM_SECS(ssl_state->server_connp.cert0_not_before);
-        CreateUtcIsoTimeString(ts, timebuf, sizeof(timebuf));
-        jb_set_string(js, "notbefore", timebuf);
+        sc_x509_log_timestamp(js, "notbefore", ssl_state->server_connp.cert0_not_before);
     }
 }
 
 static void JsonTlsLogNotAfter(JsonBuilder *js, SSLState *ssl_state)
 {
     if (ssl_state->server_connp.cert0_not_after != 0) {
-        char timebuf[64];
-        SCTime_t ts = SCTIME_FROM_SECS(ssl_state->server_connp.cert0_not_after);
-        CreateUtcIsoTimeString(ts, timebuf, sizeof(timebuf));
-        jb_set_string(js, "notafter", timebuf);
+        sc_x509_log_timestamp(js, "notafter", ssl_state->server_connp.cert0_not_after);
     }
 }