]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
rust/tftp: add tftp parsing and logging
authorClement Galland <clement.galland@epita.fr>
Fri, 20 Oct 2017 07:42:37 +0000 (07:42 +0000)
committerVictor Julien <victor@inliniac.net>
Mon, 29 Jan 2018 10:44:01 +0000 (11:44 +0100)
TFTP parsing and logging written in Rust.
Log on eve.json the type of request (read or write), the name of the file and
the mode.

Example of output:
    "tftp":{"packet":"read","file":"rfc1350.txt","mode":"octet"}

16 files changed:
rust/gen-c-headers.py
rust/src/lib.rs
rust/src/tftp/log.rs [new file with mode: 0644]
rust/src/tftp/mod.rs [new file with mode: 0644]
rust/src/tftp/tftp.rs [new file with mode: 0644]
src/Makefile.am
src/app-layer-detect-proto.c
src/app-layer-parser.c
src/app-layer-protos.c
src/app-layer-protos.h
src/app-layer-tftp.c [new file with mode: 0644]
src/app-layer-tftp.h [new file with mode: 0644]
src/output-json-tftp.c [new file with mode: 0644]
src/output-json-tftp.h [new file with mode: 0644]
src/output.c
src/suricata-common.h

index c7af8f5c27960787173604e18209289035862811..77880a78251ddef481218f4401fd94133612e547 100755 (executable)
@@ -80,6 +80,8 @@ type_map = {
     "NFSTransaction": "NFSTransaction",
     "NTPState": "NTPState",
     "NTPTransaction": "NTPTransaction",
+    "TFTPTransaction": "TFTPTransaction",
+    "TFTPState": "TFTPState",
     "JsonT": "json_t",
     "DetectEngineState": "DetectEngineState",
     "core::DetectEngineState": "DetectEngineState",
index 1341aadf1e604a86e5f3dcc3730b88605bc5378c..b322c08b685debbb08b67bb33fc719de3044c183 100644 (file)
@@ -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 (file)
index 0000000..51d7f07
--- /dev/null
@@ -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 <clement.galland@epita.fr>
+
+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 (file)
index 0000000..7c3d292
--- /dev/null
@@ -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 <clement.galland@epita.fr>
+
+pub mod tftp;
+pub mod log;
diff --git a/rust/src/tftp/tftp.rs b/rust/src/tftp/tftp.rs
new file mode 100644 (file)
index 0000000..eab7023
--- /dev/null
@@ -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 <clement.galland@epita.fr>
+
+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<TFTPTransaction>
+}
+
+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<TFTPState> = 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<TFTPTransaction>,
+       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
+    }
+}
index ab325a63e213a1d45341b9f354981747c2d79b16..1ec4a17a318e0b5dff7dd742b3714b6c5a8b8e0b 100644 (file)
@@ -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 \
index 92d8014c9be9025680b318398011f631836f98dc..e3d6292f2ef050ce50761e3e2cc86dc3f09dfece 100644 (file)
@@ -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)
index 9d86c5d250ba59b90f4b80330ce3c7a8919572c5..a2ab40a17cd0fc68946e5999d1883c1303a5462f 100644 (file)
@@ -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 */
index a8466525594cf44a107e93a28fa4adf76a0f4d0f..ce80e4b6619f0a90d3281498c75a630e828fc8dc 100644 (file)
@@ -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";
index 0df2c80cc168027e88d5b0557b96ae6df61973d8..ddf95786754ef125814e89d40ef7d411cb3a4d9f 100644 (file)
@@ -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 (file)
index 0000000..27f1180
--- /dev/null
@@ -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 <clement.galland@epita.fr>
+ *
+ * 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 (file)
index 0000000..241fa77
--- /dev/null
@@ -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 <clement.galland@epita.fr>
+ */
+
+#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 (file)
index 0000000..ded1d12
--- /dev/null
@@ -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 <clement.galland@epita.fr>
+ *
+ * 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 (file)
index 0000000..3db4ba0
--- /dev/null
@@ -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 <clement.galland@epita.fr>
+ */
+
+#ifndef __OUTPUT_JSON_TFTP_H__
+#define __OUTPUT_JSON_TFTP_H__
+
+void JsonTFTPLogRegister(void);
+
+#endif /* __OUTPUT_JSON_TFTP_H__ */
index 3e8f7ded05029c7c9307d4dcf44edab52cb62f34..36d584f0849fa94f5b0aea25c9eb9340e7cc0561 100644 (file)
@@ -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();
 }
index 2873c46c0fdc06b4e3b02dde3962335119c4d2a5..1e71d0a6a431c0972d0ef951e7dcee37b557e364 100644 (file)
@@ -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,