diff --git a/rust/gen-c-headers.py b/rust/gen-c-headers.py index c7af8f5c27..77880a7825 100755 --- a/rust/gen-c-headers.py +++ b/rust/gen-c-headers.py @@ -80,6 +80,8 @@ type_map = { "NFSTransaction": "NFSTransaction", "NTPState": "NTPState", "NTPTransaction": "NTPTransaction", + "TFTPTransaction": "TFTPTransaction", + "TFTPState": "TFTPState", "JsonT": "json_t", "DetectEngineState": "DetectEngineState", "core::DetectEngineState": "DetectEngineState", diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 1341aadf1e..b322c08b68 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -47,3 +47,4 @@ pub mod ftp; #[cfg(feature = "experimental")] pub mod ntp; +pub mod tftp; diff --git a/rust/src/tftp/log.rs b/rust/src/tftp/log.rs new file mode 100644 index 0000000000..51d7f071fb --- /dev/null +++ b/rust/src/tftp/log.rs @@ -0,0 +1,37 @@ +/* Copyright (C) 2017 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. + */ + +// written by Clément Galland + +extern crate libc; + +use json::*; +use tftp::tftp::*; + +#[no_mangle] +pub extern "C" fn rs_tftp_log_json_request(tx: &mut TFTPTransaction) -> *mut JsonT +{ + let js = Json::object(); + match tx.opcode { + 1 => js.set_string("packet", "read"), + 2 => js.set_string("packet", "write"), + _ => js.set_string("packet", "error") + }; + js.set_string("file", tx.filename.as_str()); + js.set_string("mode", tx.mode.as_str()); + js.unwrap() +} diff --git a/rust/src/tftp/mod.rs b/rust/src/tftp/mod.rs new file mode 100644 index 0000000000..7c3d292a00 --- /dev/null +++ b/rust/src/tftp/mod.rs @@ -0,0 +1,21 @@ +/* Copyright (C) 2017 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. + */ + +// written by Clément Galland + +pub mod tftp; +pub mod log; diff --git a/rust/src/tftp/tftp.rs b/rust/src/tftp/tftp.rs new file mode 100644 index 0000000000..eab70231e9 --- /dev/null +++ b/rust/src/tftp/tftp.rs @@ -0,0 +1,145 @@ +/* Copyright (C) 2017 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. + */ + +// written by Clément Galland + +extern crate libc; +extern crate nom; + +use std::str; +use std; +use std::mem::transmute; + +use applayer::LoggerFlags; + +#[derive(Debug)] +pub struct TFTPTransaction { + pub opcode : u8, + pub filename : String, + pub mode : String, + pub logged : LoggerFlags +} + +pub struct TFTPState { + pub transactions : Vec +} + +impl TFTPTransaction { + pub fn new(opcode : u8, filename : String, mode : String) -> TFTPTransaction { + TFTPTransaction { + opcode : opcode, + filename : filename, + mode : mode.to_lowercase(), + logged : LoggerFlags::new(), + } + } + pub fn is_mode_ok(&self) -> bool { + match self.mode.as_str() { + "netascii" | "mail" | "octet" => true, + _ => false + } + } +} + +#[no_mangle] +pub extern "C" fn rs_tftp_state_alloc() -> *mut libc::c_void { + let state = TFTPState { transactions : Vec::new() }; + let boxed = Box::new(state); + return unsafe{transmute(boxed)}; +} + +#[no_mangle] +pub extern "C" fn rs_tftp_state_free(state: *mut libc::c_void) { + let _state : Box = unsafe{transmute(state)}; +} + +#[no_mangle] +pub extern "C" fn rs_tftp_state_tx_free(state: &mut TFTPState, + tx_id: libc::uint32_t) { + state.transactions.remove(tx_id as usize); +} + +#[no_mangle] +pub extern "C" fn rs_tftp_get_tx(state: &mut TFTPState, + tx_id: libc::uint64_t) -> *mut libc::c_void { + if state.transactions.len() <= tx_id as usize { + return std::ptr::null_mut(); + } + return unsafe{transmute(&state.transactions[tx_id as usize])}; +} + +#[no_mangle] +pub extern "C" fn rs_tftp_get_tx_logged(_state: &mut TFTPState, + tx: &mut TFTPTransaction, + logger: libc::uint32_t) -> i8 { + if tx.logged.is_logged(logger) { + 1 + } else { + 0 + } +} + +#[no_mangle] +pub extern "C" fn rs_tftp_set_tx_logged(_state: &mut TFTPState, + tx: &mut TFTPTransaction, + logger: libc::uint32_t) { + tx.logged.set_logged(logger); +} + +#[no_mangle] +pub extern "C" fn rs_tftp_has_event(state: &mut TFTPState) -> i64 { + return state.transactions.len() as i64; +} + +#[no_mangle] +pub extern "C" fn rs_tftp_get_tx_cnt(state: &mut TFTPState) -> u64 { + return state.transactions.len() as u64; +} + +named!(getstr<&str>, map_res!( + take_while!(call!(|c| c != 0)), + str::from_utf8 + ) +); + +named!(pub tftp_request, + do_parse!( + tag!([0]) >> + opcode: take!(1) >> + filename: getstr >> + tag!([0]) >> + mode : getstr >> + ( + TFTPTransaction::new(opcode[0], String::from(filename), String::from(mode)) + ) + ) +); + + +#[no_mangle] +pub extern "C" fn rs_tftp_request(state: &mut TFTPState, + input: *const libc::uint8_t, + len: libc::uint32_t) -> i64 { + let buf = unsafe{std::slice::from_raw_parts(input, len as usize)}; + return match tftp_request(buf) { + nom::IResult::Done(_, rqst) => { + state.transactions.push(rqst); + 1 + }, + _ => 0 + } +} diff --git a/src/Makefile.am b/src/Makefile.am index ab325a63e2..1ec4a17a31 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -44,6 +44,7 @@ app-layer-nfs-tcp.c app-layer-nfs-tcp.h \ app-layer-nfs-udp.c app-layer-nfs-udp.h \ app-layer-ntp.c app-layer-ntp.h \ app-layer-register.c app-layer-register.h \ +app-layer-tftp.c app-layer-tftp.h \ app-layer-template.c app-layer-template.h \ app-layer-ssh.c app-layer-ssh.h \ app-layer-ssl.c app-layer-ssl.h \ @@ -308,6 +309,7 @@ output-json-ssh.c output-json-ssh.h \ output-json-stats.c output-json-stats.h \ output-json-tls.c output-json-tls.h \ output-json-nfs.c output-json-nfs.h \ +output-json-tftp.c output-json-tftp.h \ output-json-template.c output-json-template.h \ output-json-vars.c output-json-vars.h \ output-lua.c output-lua.h \ diff --git a/src/app-layer-detect-proto.c b/src/app-layer-detect-proto.c index 92d8014c9b..e3d6292f2e 100644 --- a/src/app-layer-detect-proto.c +++ b/src/app-layer-detect-proto.c @@ -723,6 +723,8 @@ static void AppLayerProtoDetectPrintProbingParsers(AppLayerProtoDetectProbingPar printf(" alproto: ALPROTO_NFS\n"); else if (pp_pe->alproto == ALPROTO_NTP) printf(" alproto: ALPROTO_NTP\n"); + else if (pp_pe->alproto == ALPROTO_TFTP) + printf(" alproto: ALPROTO_TFTP\n"); else if (pp_pe->alproto == ALPROTO_TEMPLATE) printf(" alproto: ALPROTO_TEMPLATE\n"); else if (pp_pe->alproto == ALPROTO_DNP3) @@ -786,6 +788,8 @@ static void AppLayerProtoDetectPrintProbingParsers(AppLayerProtoDetectProbingPar printf(" alproto: ALPROTO_NFS\n"); else if (pp_pe->alproto == ALPROTO_NTP) printf(" alproto: ALPROTO_NTP\n"); + else if (pp_pe->alproto == ALPROTO_TFTP) + printf(" alproto: ALPROTO_TFTP\n"); else if (pp_pe->alproto == ALPROTO_TEMPLATE) printf(" alproto: ALPROTO_TEMPLATE\n"); else if (pp_pe->alproto == ALPROTO_DNP3) diff --git a/src/app-layer-parser.c b/src/app-layer-parser.c index 9d86c5d250..a2ab40a17c 100644 --- a/src/app-layer-parser.c +++ b/src/app-layer-parser.c @@ -63,6 +63,7 @@ #include "app-layer-nfs-tcp.h" #include "app-layer-nfs-udp.h" #include "app-layer-ntp.h" +#include "app-layer-tftp.h" #include "app-layer-template.h" #include "conf.h" @@ -1389,6 +1390,7 @@ void AppLayerParserRegisterProtocolParsers(void) RegisterNFSTCPParsers(); RegisterNFSUDPParsers(); RegisterNTPParsers(); + RegisterTFTPParsers(); RegisterTemplateParsers(); /** IMAP */ diff --git a/src/app-layer-protos.c b/src/app-layer-protos.c index a846652559..ce80e4b661 100644 --- a/src/app-layer-protos.c +++ b/src/app-layer-protos.c @@ -89,6 +89,8 @@ const char *AppProtoToString(AppProto alproto) break; case ALPROTO_FTPDATA: proto_name = "ftp-data"; + case ALPROTO_TFTP: + proto_name = "tftp"; break; case ALPROTO_TEMPLATE: proto_name = "template"; diff --git a/src/app-layer-protos.h b/src/app-layer-protos.h index 0df2c80cc1..ddf9578675 100644 --- a/src/app-layer-protos.h +++ b/src/app-layer-protos.h @@ -47,6 +47,7 @@ enum AppProtoEnum { ALPROTO_NFS, ALPROTO_NTP, ALPROTO_FTPDATA, + ALPROTO_TFTP, ALPROTO_TEMPLATE, /* used by the probing parser when alproto detection fails diff --git a/src/app-layer-tftp.c b/src/app-layer-tftp.c new file mode 100644 index 0000000000..27f1180587 --- /dev/null +++ b/src/app-layer-tftp.c @@ -0,0 +1,331 @@ +/* Copyright (C) 2017 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. + * + */ + +/** + * \file + * + * \author Clément Galland + * + * Parser for NTP application layer running on UDP port 69. + */ + + +#include "suricata-common.h" +#include "stream.h" +#include "conf.h" + +#include "util-unittest.h" + +#include "app-layer-detect-proto.h" +#include "app-layer-parser.h" + +#include "app-layer-tftp.h" + +#ifndef HAVE_RUST + +void RegisterTFTPParsers(void) +{ +} + +#else + +#include "rust-tftp-tftp-gen.h" + +/* The default port to probe if not provided in the configuration file. */ +#define TFTP_DEFAULT_PORT "69" + +/* The minimum size for an message. For some protocols this might + * be the size of a header. */ +#define TFTP_MIN_FRAME_LEN 4 + +/* Enum of app-layer events for an echo protocol. Normally you might + * have events for errors in parsing data, like unexpected data being + * received. For echo we'll make something up, and log an app-layer + * level alert if an empty message is received. + * + * Example rule: + * + * alert tftp any any -> any any (msg:"SURICATA TFTP empty message"; \ + * app-layer-event:tftp.empty_message; sid:X; rev:Y;) + */ +enum { + TFTP_DECODER_EVENT_EMPTY_MESSAGE, +}; + +SCEnumCharMap tftp_decoder_event_table[] = { + {"EMPTY_MESSAGE", TFTP_DECODER_EVENT_EMPTY_MESSAGE}, + + // event table must be NULL-terminated + { NULL, -1 }, +}; + +static void *TFTPStateAlloc(void) +{ + return rs_tftp_state_alloc(); +} + +static void TFTPStateFree(void *state) +{ + rs_tftp_state_free(state); +} + +/** + * \brief Callback from the application layer to have a transaction freed. + * + * \param state a void pointer to the TFTPState object. + * \param tx_id the transaction ID to free. + */ +static void TFTPStateTxFree(void *state, uint64_t tx_id) +{ + rs_tftp_state_tx_free(state, tx_id); +} + +static int TFTPStateGetEventInfo(const char *event_name, int *event_id, + AppLayerEventType *event_type) +{ + return -1; +} + +static AppLayerDecoderEvents *TFTPGetEvents(void *state, uint64_t tx_id) +{ + return NULL; +} + +static int TFTPHasEvents(void *state) +{ + return rs_tftp_has_event(state); +} + +/** + * \brief Probe the input to see if it looks like echo. + * + * \retval ALPROTO_TFTP if it looks like echo, otherwise + * ALPROTO_UNKNOWN. + */ +static AppProto TFTPProbingParser(Flow *f, uint8_t *input, uint32_t input_len, + uint32_t *offset) +{ + /* Very simple test - if there is input, this is echo. + * Also check if it's starting by a zero */ + if (input_len >= TFTP_MIN_FRAME_LEN && *input == 0) { + SCLogDebug("Detected as ALPROTO_TFTP."); + return ALPROTO_TFTP; + } + + SCLogDebug("Protocol not detected as ALPROTO_TFTP."); + return ALPROTO_UNKNOWN; +} + +static int TFTPParseRequest(Flow *f, void *state, + AppLayerParserState *pstate, uint8_t *input, uint32_t input_len, + void *local_data) +{ + SCLogDebug("Parsing echo request: len=%"PRIu32, input_len); + + /* Likely connection closed, we can just return here. */ + if ((input == NULL || input_len == 0) && + AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) { + return 0; + } + + /* Probably don't want to create a transaction in this case + * either. */ + if (input == NULL || input_len == 0) { + return 0; + } + + return rs_tftp_request(state, input, input_len); +} + +/** + * \brief Response parsing is not implemented + */ +static int TFTPParseResponse(Flow *f, void *state, AppLayerParserState *pstate, + uint8_t *input, uint32_t input_len, void *local_data) +{ + return 0; +} + +static uint64_t TFTPGetTxCnt(void *state) +{ + return rs_tftp_get_tx_cnt(state); +} + +static void *TFTPGetTx(void *state, uint64_t tx_id) +{ + return rs_tftp_get_tx(state, tx_id); +} + +static void TFTPSetTxLogged(void *state, void *vtx, uint32_t logger) +{ + rs_tftp_set_tx_logged(state, vtx, logger); +} + +static int TFTPGetTxLogged(void *state, void *vtx, uint32_t logger) +{ + return rs_tftp_get_tx_logged(state, vtx, logger); +} + +/** + * \brief Called by the application layer. + * + * In most cases 1 can be returned here. + */ +static int TFTPGetAlstateProgressCompletionStatus(uint8_t direction) { + return 1; +} + +/** + * \brief Return the state of a transaction in a given direction. + * + * In the case of the echo protocol, the existence of a transaction + * means that the request is done. However, some protocols that may + * need multiple chunks of data to complete the request may need more + * than just the existence of a transaction for the request to be + * considered complete. + * + * For the response to be considered done, the response for a request + * needs to be seen. The response_done flag is set on response for + * checking here. + */ +static int TFTPGetStateProgress(void *tx, uint8_t direction) +{ + return 1; +} + +static DetectEngineState *TFTPGetTxDetectState(void *vtx) +{ + return NULL; +} + +static int TFTPSetTxDetectState(void *state, void *vtx, + DetectEngineState *s) +{ + return 0; +} + +void RegisterTFTPParsers(void) +{ + const char *proto_name = "tftp"; + + /* Check if TFTP UDP detection is enabled. If it does not exist in + * the configuration file then it will be enabled by default. */ + if (AppLayerProtoDetectConfProtoDetectionEnabled("udp", proto_name)) { + + SCLogDebug("TFTP UDP protocol detection enabled."); + + AppLayerProtoDetectRegisterProtocol(ALPROTO_TFTP, proto_name); + + if (RunmodeIsUnittests()) { + SCLogDebug("Unittest mode, registeringd default configuration."); + AppLayerProtoDetectPPRegister(IPPROTO_UDP, TFTP_DEFAULT_PORT, + ALPROTO_TFTP, 0, TFTP_MIN_FRAME_LEN, + STREAM_TOSERVER, TFTPProbingParser, + NULL); + } else { + if (!AppLayerProtoDetectPPParseConfPorts("udp", IPPROTO_UDP, + proto_name, ALPROTO_TFTP, + 0, TFTP_MIN_FRAME_LEN, + TFTPProbingParser, NULL)) { + SCLogDebug("No echo app-layer configuration, enabling echo" + " detection UDP detection on port %s.", + TFTP_DEFAULT_PORT); + AppLayerProtoDetectPPRegister(IPPROTO_UDP, + TFTP_DEFAULT_PORT, ALPROTO_TFTP, + 0, TFTP_MIN_FRAME_LEN, + STREAM_TOSERVER,TFTPProbingParser, + NULL); + } + } + } else { + SCLogDebug("Protocol detecter and parser disabled for TFTP."); + return; + } + + if (AppLayerParserConfParserEnabled("udp", proto_name)) { + + SCLogDebug("Registering TFTP protocol parser."); + + /* Register functions for state allocation and freeing. A + * state is allocated for every new TFTP flow. */ + AppLayerParserRegisterStateFuncs(IPPROTO_UDP, ALPROTO_TFTP, + TFTPStateAlloc, TFTPStateFree); + + /* Register request parser for parsing frame from server to client. */ + AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_TFTP, + STREAM_TOSERVER, TFTPParseRequest); + + /* Register response parser for parsing frames from server to client. */ + AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_TFTP, + STREAM_TOCLIENT, TFTPParseResponse); + + /* Register a function to be called by the application layer + * when a transaction is to be freed. */ + AppLayerParserRegisterTxFreeFunc(IPPROTO_UDP, ALPROTO_TFTP, + TFTPStateTxFree); + + AppLayerParserRegisterLoggerFuncs(IPPROTO_UDP, ALPROTO_TFTP, + TFTPGetTxLogged, TFTPSetTxLogged); + + /* Register a function to return the current transaction count. */ + AppLayerParserRegisterGetTxCnt(IPPROTO_UDP, ALPROTO_TFTP, + TFTPGetTxCnt); + + /* Transaction handling. */ + AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_TFTP, + TFTPGetAlstateProgressCompletionStatus); + AppLayerParserRegisterGetStateProgressFunc(IPPROTO_UDP, + ALPROTO_TFTP, + TFTPGetStateProgress); + AppLayerParserRegisterGetTx(IPPROTO_UDP, ALPROTO_TFTP, + TFTPGetTx); + + /* Application layer event handling. */ + AppLayerParserRegisterHasEventsFunc(IPPROTO_UDP, ALPROTO_TFTP, + TFTPHasEvents); + + /* What is this being registered for? */ + AppLayerParserRegisterDetectStateFuncs(IPPROTO_UDP, ALPROTO_TFTP, + NULL, TFTPGetTxDetectState, + TFTPSetTxDetectState); + + AppLayerParserRegisterGetEventInfo(IPPROTO_UDP, ALPROTO_TFTP, + TFTPStateGetEventInfo); + AppLayerParserRegisterGetEventsFunc(IPPROTO_UDP, ALPROTO_TFTP, + TFTPGetEvents); + } + else { + SCLogDebug("TFTP protocol parsing disabled."); + } + +#ifdef UNITTESTS + AppLayerParserRegisterProtocolUnittests(IPPROTO_UDP, ALPROTO_TFTP, + TFTPParserRegisterTests); +#endif +} + +#ifdef UNITTESTS +#endif + +void TFTPParserRegisterTests(void) +{ +#ifdef UNITTESTS +#endif +} + +#endif /* HAVE_RUST */ diff --git a/src/app-layer-tftp.h b/src/app-layer-tftp.h new file mode 100644 index 0000000000..241fa778a6 --- /dev/null +++ b/src/app-layer-tftp.h @@ -0,0 +1,38 @@ +/* Copyright (C) 2017 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. + */ + +/** + * \file + * + * \author Clément Galland + */ + +#ifndef __APP_LAYER_TFTP_H__ +#define __APP_LAYER_TFTP_H__ + +#include "detect-engine-state.h" + +#include "queue.h" + +void RegisterTFTPParsers(void); +void TFTPParserRegisterTests(void); + +/** Opaque Rust types. */ +typedef struct TFTPState_ TFTPState; +typedef struct TFTPTransaction_ TFTPTransaction; + +#endif /* __APP_LAYER_TFTP_H__ */ diff --git a/src/output-json-tftp.c b/src/output-json-tftp.c new file mode 100644 index 0000000000..ded1d12bde --- /dev/null +++ b/src/output-json-tftp.c @@ -0,0 +1,197 @@ +/* Copyright (C) 2017 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. + */ + +/** + * \file + * + * \author Clément Galland + * + * Implement JSON/eve logging app-layer TFTP. + */ + +//#ifdef HAVE_RUST + +#include "suricata-common.h" +#include "debug.h" +#include "detect.h" +#include "pkt-var.h" +#include "conf.h" + +#include "threads.h" +#include "threadvars.h" +#include "tm-threads.h" + +#include "util-unittest.h" +#include "util-buffer.h" +#include "util-debug.h" +#include "util-byte.h" + +#include "output.h" +#include "output-json.h" + +#include "app-layer.h" +#include "app-layer-parser.h" + +#include "app-layer-tftp.h" +#include "output-json-tftp.h" + +#ifdef HAVE_RUST + +#include "rust.h" +#include "rust-tftp-log-gen.h" + +#ifdef HAVE_LIBJANSSON + +typedef struct LogTFTPFileCtx_ { + LogFileCtx *file_ctx; + uint32_t flags; +} LogTFTPFileCtx; + +typedef struct LogTFTPLogThread_ { + LogTFTPFileCtx *tftplog_ctx; + uint32_t count; + MemBuffer *buffer; +} LogTFTPLogThread; + +static int JsonTFTPLogger(ThreadVars *tv, void *thread_data, + const Packet *p, Flow *f, void *state, void *tx, uint64_t tx_id) +{ + LogTFTPLogThread *thread = thread_data; + json_t *js, *tftpjs; + + js = CreateJSONHeader((Packet *)p, 0, "tftp"); + if (unlikely(js == NULL)) { + return TM_ECODE_FAILED; + } + + tftpjs = rs_tftp_log_json_request(tx); + if (unlikely(tftpjs == NULL)) { + goto error; + } + + json_object_set_new(js, "tftp", tftpjs); + + MemBufferReset(thread->buffer); + OutputJSONBuffer(js, thread->tftplog_ctx->file_ctx, &thread->buffer); + + json_decref(js); + return TM_ECODE_OK; + +error: + json_decref(js); + return TM_ECODE_FAILED; +} + +static void OutputTFTPLogDeInitCtxSub(OutputCtx *output_ctx) +{ + LogTFTPFileCtx *tftplog_ctx = (LogTFTPFileCtx *)output_ctx->data; + SCFree(tftplog_ctx); + SCFree(output_ctx); +} + +static OutputCtx *OutputTFTPLogInitSub(ConfNode *conf, + OutputCtx *parent_ctx) +{ + OutputJsonCtx *ajt = parent_ctx->data; + + LogTFTPFileCtx *tftplog_ctx = SCCalloc(1, sizeof(*tftplog_ctx)); + if (unlikely(tftplog_ctx == NULL)) { + return NULL; + } + tftplog_ctx->file_ctx = ajt->file_ctx; + + OutputCtx *output_ctx = SCCalloc(1, sizeof(*output_ctx)); + if (unlikely(output_ctx == NULL)) { + SCFree(tftplog_ctx); + return NULL; + } + output_ctx->data = tftplog_ctx; + output_ctx->DeInit = OutputTFTPLogDeInitCtxSub; + + SCLogDebug("TFTP log sub-module initialized."); + + AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_TFTP); + + return output_ctx; +} + +#define OUTPUT_BUFFER_SIZE 65535 + +static TmEcode JsonTFTPLogThreadInit(ThreadVars *t, const void *initdata, void **data) +{ + LogTFTPLogThread *thread = SCCalloc(1, sizeof(*thread)); + if (unlikely(thread == NULL)) { + return TM_ECODE_FAILED; + } + + if (initdata == NULL) { + SCLogDebug("Error getting context for EveLogTFTP. \"initdata\" is NULL."); + SCFree(thread); + return TM_ECODE_FAILED; + } + + thread->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE); + if (unlikely(thread->buffer == NULL)) { + SCFree(thread); + return TM_ECODE_FAILED; + } + + thread->tftplog_ctx = ((OutputCtx *)initdata)->data; + *data = (void *)thread; + + return TM_ECODE_OK; +} + +static TmEcode JsonTFTPLogThreadDeinit(ThreadVars *t, void *data) +{ + LogTFTPLogThread *thread = (LogTFTPLogThread *)data; + if (thread == NULL) { + return TM_ECODE_OK; + } + if (thread->buffer != NULL) { + MemBufferFree(thread->buffer); + } + SCFree(thread); + return TM_ECODE_OK; +} + +void JsonTFTPLogRegister(void) +{ + /* Register as an eve sub-module. */ + OutputRegisterTxSubModule(LOGGER_JSON_TFTP, "eve-log", "JsonTFTPLog", + "eve-log.tftp", OutputTFTPLogInitSub, + ALPROTO_TFTP, JsonTFTPLogger, + JsonTFTPLogThreadInit, JsonTFTPLogThreadDeinit, + NULL); + + SCLogDebug("TFTP JSON logger registered."); +} +#else /* HAVE_RUST */ + +void JsonTFTPLogRegister(void) +{ +} + +#endif /* HAVE_RUST */ + +#else /* HAVE_LIBJANSSON */ + +void JsonTFTPLogRegister(void) +{ +} + +#endif /* HAVE_LIBJANSSON */ diff --git a/src/output-json-tftp.h b/src/output-json-tftp.h new file mode 100644 index 0000000000..3db4ba06cd --- /dev/null +++ b/src/output-json-tftp.h @@ -0,0 +1,29 @@ +/* Copyright (C) 2017 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. + */ + +/** + * \file + * + * \author Clément Galland + */ + +#ifndef __OUTPUT_JSON_TFTP_H__ +#define __OUTPUT_JSON_TFTP_H__ + +void JsonTFTPLogRegister(void); + +#endif /* __OUTPUT_JSON_TFTP_H__ */ diff --git a/src/output.c b/src/output.c index 3e8f7ded05..36d584f084 100644 --- a/src/output.c +++ b/src/output.c @@ -69,6 +69,7 @@ #include "log-stats.h" #include "output-json.h" #include "output-json-nfs.h" +#include "output-json-tftp.h" #include "output-json-template.h" #include "output-lua.h" #include "output-json-dnp3.h" @@ -1089,6 +1090,8 @@ void OutputRegisterLoggers(void) /* NFS JSON logger. */ JsonNFSLogRegister(); + /* TFTP JSON logger. */ + JsonTFTPLogRegister(); /* Template JSON logger. */ JsonTemplateLogRegister(); } diff --git a/src/suricata-common.h b/src/suricata-common.h index 2873c46c0f..1e71d0a6a4 100644 --- a/src/suricata-common.h +++ b/src/suricata-common.h @@ -408,6 +408,7 @@ typedef enum { LOGGER_JSON_SMTP, LOGGER_JSON_TLS, LOGGER_JSON_NFS, + LOGGER_JSON_TFTP, LOGGER_JSON_DNP3_TS, LOGGER_JSON_DNP3_TC, LOGGER_JSON_SSH,