From: Jason Ish Date: Fri, 27 Jan 2023 05:03:22 +0000 (-0600) Subject: tls: fix date logging for dates before 1970 X-Git-Tag: suricata-7.0.0-rc1~23 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6344501dbab1de1dbecd97848a90f8aac4cfe55f;p=thirdparty%2Fsuricata.git tls: fix date logging for dates before 1970 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 --- diff --git a/rust/src/x509/log.rs b/rust/src/x509/log.rs new file mode 100644 index 0000000000..adb64646a7 --- /dev/null +++ b/rust/src/x509/log.rs @@ -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, ×tamp).is_ok(); + } + } + false +} diff --git a/rust/src/x509/mod.rs b/rust/src/x509/mod.rs index 03c9cc5aa0..1b00101e63 100644 --- a/rust/src/x509/mod.rs +++ b/rust/src/x509/mod.rs @@ -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 index 0000000000..2e4a28dd9b --- /dev/null +++ b/rust/src/x509/time.rs @@ -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 { + 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()); + } +} diff --git a/src/app-layer-ssl.c b/src/app-layer-ssl.c index 4496bf4fbd..59856e44fd 100644 --- a/src/app-layer-ssl.c +++ b/src/app-layer-ssl.c @@ -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, ¬_before, ¬_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; diff --git a/src/app-layer-ssl.h b/src/app-layer-ssl.h index 5bd703d15e..79933e6fca 100644 --- a/src/app-layer-ssl.h +++ b/src/app-layer-ssl.h @@ -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 */ diff --git a/src/log-tlslog.c b/src/log-tlslog.c index 38d7736876..dc32d3d814 100644 --- a/src/log-tlslog.c +++ b/src/log-tlslog.c @@ -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, diff --git a/src/output-json-tls.c b/src/output-json-tls.c index 8fc30213a8..9771f4d1cd 100644 --- a/src/output-json-tls.c +++ b/src/output-json-tls.c @@ -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); } }