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
pull/8485/head
Jason Ish 3 years ago committed by Victor Julien
parent ef48c5064f
commit 6344501dba

@ -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
}

@ -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 {

@ -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());
}
}

@ -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;

@ -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 */

@ -290,13 +290,13 @@ 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));
if (sc_x509_format_timestamp(*date, timebuf, sizeof(timebuf))) {
MemBufferWriteString(buffer, "%s='%s'", title, timebuf);
}
}
static void LogTlsLogString(MemBuffer *buffer, const char *title,
const char *value)

@ -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);
}
}

Loading…
Cancel
Save