mirror of https://github.com/OISF/suricata
nfs3: support NFS over UDP
parent
d9f87cec3d
commit
c7e10c73f9
@ -0,0 +1,395 @@
|
|||||||
|
/* Copyright (C) 2015 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 Victor Julien <victor@inliniac.net>
|
||||||
|
*
|
||||||
|
* NFS3 application layer detector and parser for learning and
|
||||||
|
* nfs3 pruposes.
|
||||||
|
*
|
||||||
|
* This nfs3 implements a simple application layer for something
|
||||||
|
* like the NFS3 protocol running on port 2049.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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-nfs3-udp.h"
|
||||||
|
|
||||||
|
#ifndef HAVE_RUST
|
||||||
|
void RegisterNFS3UDPParsers(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include "rust.h"
|
||||||
|
#include "rust-nfs-nfs3-gen.h"
|
||||||
|
|
||||||
|
/* The default port to probe for echo traffic if not provided in the
|
||||||
|
* configuration file. */
|
||||||
|
#define NFS3_DEFAULT_PORT "2049"
|
||||||
|
|
||||||
|
/* The minimum size for a RFC message. For some protocols this might
|
||||||
|
* be the size of a header. TODO actual min size is likely larger */
|
||||||
|
#define NFS3_MIN_FRAME_LEN 32
|
||||||
|
|
||||||
|
/* 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 nfs3 any any -> any any (msg:"SURICATA NFS3 empty message"; \
|
||||||
|
* app-layer-event:nfs3.empty_message; sid:X; rev:Y;)
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
NFS3_DECODER_EVENT_EMPTY_MESSAGE,
|
||||||
|
};
|
||||||
|
|
||||||
|
SCEnumCharMap nfs3_udp_decoder_event_table[] = {
|
||||||
|
{"EMPTY_MESSAGE", NFS3_DECODER_EVENT_EMPTY_MESSAGE},
|
||||||
|
{ NULL, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static void *NFS3StateAlloc(void)
|
||||||
|
{
|
||||||
|
return rs_nfs3_state_new();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void NFS3StateFree(void *state)
|
||||||
|
{
|
||||||
|
rs_nfs3_state_free(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Callback from the application layer to have a transaction freed.
|
||||||
|
*
|
||||||
|
* \param state a void pointer to the NFS3State object.
|
||||||
|
* \param tx_id the transaction ID to free.
|
||||||
|
*/
|
||||||
|
static void NFS3StateTxFree(void *state, uint64_t tx_id)
|
||||||
|
{
|
||||||
|
rs_nfs3_state_tx_free(state, tx_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static int NFS3StateGetEventInfo(const char *event_name, int *event_id,
|
||||||
|
AppLayerEventType *event_type)
|
||||||
|
{
|
||||||
|
*event_id = SCMapEnumNameToValue(event_name, nfs3_decoder_event_table);
|
||||||
|
if (*event_id == -1) {
|
||||||
|
SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
|
||||||
|
"nfs3 enum map table.", event_name);
|
||||||
|
/* This should be treated as fatal. */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AppLayerDecoderEvents *NFS3GetEvents(void *state, uint64_t tx_id)
|
||||||
|
{
|
||||||
|
NFS3State *nfs3_state = state;
|
||||||
|
NFS3Transaction *tx;
|
||||||
|
|
||||||
|
TAILQ_FOREACH(tx, &nfs3_state->tx_list, next) {
|
||||||
|
if (tx->tx_id == tx_id) {
|
||||||
|
return tx->decoder_events;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int NFS3HasEvents(void *state)
|
||||||
|
{
|
||||||
|
NFS3State *echo = state;
|
||||||
|
return echo->events;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Probe the input to see if it looks like echo.
|
||||||
|
*
|
||||||
|
* \retval ALPROTO_NFS3 if it looks like echo, otherwise
|
||||||
|
* ALPROTO_UNKNOWN.
|
||||||
|
*/
|
||||||
|
static AppProto NFS3ProbingParserTS(uint8_t *input, uint32_t input_len,
|
||||||
|
uint32_t *offset)
|
||||||
|
{
|
||||||
|
SCLogNotice("probing");
|
||||||
|
if (input_len < NFS3_MIN_FRAME_LEN) {
|
||||||
|
SCLogNotice("unknown");
|
||||||
|
return ALPROTO_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t r = rs_nfs_probe_udp_ts(input, input_len);
|
||||||
|
if (r == 1) {
|
||||||
|
SCLogNotice("nfs3");
|
||||||
|
return ALPROTO_NFS3;
|
||||||
|
} else if (r == -1) {
|
||||||
|
SCLogNotice("failed");
|
||||||
|
return ALPROTO_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
SCLogNotice("Protocol not detected as ALPROTO_NFS3.");
|
||||||
|
return ALPROTO_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AppProto NFS3ProbingParserTC(uint8_t *input, uint32_t input_len,
|
||||||
|
uint32_t *offset)
|
||||||
|
{
|
||||||
|
SCLogNotice("probing");
|
||||||
|
if (input_len < NFS3_MIN_FRAME_LEN) {
|
||||||
|
SCLogNotice("unknown");
|
||||||
|
return ALPROTO_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t r = rs_nfs_probe_tc(input, input_len);
|
||||||
|
if (r == 1) {
|
||||||
|
SCLogNotice("nfs3");
|
||||||
|
return ALPROTO_NFS3;
|
||||||
|
} else if (r == -1) {
|
||||||
|
SCLogNotice("failed");
|
||||||
|
return ALPROTO_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
SCLogNotice("Protocol not detected as ALPROTO_NFS3.");
|
||||||
|
return ALPROTO_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int NFS3ParseRequest(Flow *f, void *state,
|
||||||
|
AppLayerParserState *pstate, uint8_t *input, uint32_t input_len,
|
||||||
|
void *local_data)
|
||||||
|
{
|
||||||
|
uint16_t file_flags = FileFlowToFlags(f, STREAM_TOSERVER);
|
||||||
|
rs_nfs3_setfileflags(0, state, file_flags);
|
||||||
|
|
||||||
|
return rs_nfs3_parse_request_udp(f, state, pstate, input, input_len, local_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int NFS3ParseResponse(Flow *f, void *state, AppLayerParserState *pstate,
|
||||||
|
uint8_t *input, uint32_t input_len, void *local_data)
|
||||||
|
{
|
||||||
|
uint16_t file_flags = FileFlowToFlags(f, STREAM_TOCLIENT);
|
||||||
|
rs_nfs3_setfileflags(1, state, file_flags);
|
||||||
|
|
||||||
|
return rs_nfs3_parse_response_udp(f, state, pstate, input, input_len, local_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t NFS3GetTxCnt(void *state)
|
||||||
|
{
|
||||||
|
return rs_nfs3_state_get_tx_count(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *NFS3GetTx(void *state, uint64_t tx_id)
|
||||||
|
{
|
||||||
|
return rs_nfs3_state_get_tx(state, tx_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void NFS3SetTxLogged(void *state, void *vtx, uint32_t logger)
|
||||||
|
{
|
||||||
|
rs_nfs3_tx_set_logged(state, vtx, logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int NFS3GetTxLogged(void *state, void *vtx, uint32_t logger)
|
||||||
|
{
|
||||||
|
return rs_nfs3_tx_get_logged(state, vtx, logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Called by the application layer.
|
||||||
|
*
|
||||||
|
* In most cases 1 can be returned here.
|
||||||
|
*/
|
||||||
|
static int NFS3GetAlstateProgressCompletionStatus(uint8_t direction) {
|
||||||
|
return rs_nfs3_state_progress_completion_status(direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \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 NFS3GetStateProgress(void *tx, uint8_t direction)
|
||||||
|
{
|
||||||
|
return rs_nfs3_tx_get_alstate_progress(tx, direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief get stored tx detect state
|
||||||
|
*/
|
||||||
|
static DetectEngineState *NFS3GetTxDetectState(void *vtx)
|
||||||
|
{
|
||||||
|
return rs_nfs3_state_get_tx_detect_state(vtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief set store tx detect state
|
||||||
|
*/
|
||||||
|
static int NFS3SetTxDetectState(void *state, void *vtx,
|
||||||
|
DetectEngineState *s)
|
||||||
|
{
|
||||||
|
rs_nfs3_state_set_tx_detect_state(state, vtx, s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FileContainer *NFS3GetFiles(void *state, uint8_t direction)
|
||||||
|
{
|
||||||
|
return rs_nfs3_getfiles(direction, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static StreamingBufferConfig sbcfg = STREAMING_BUFFER_CONFIG_INITIALIZER;
|
||||||
|
static SuricataFileContext sfc = { &sbcfg };
|
||||||
|
|
||||||
|
void RegisterNFS3UDPParsers(void)
|
||||||
|
{
|
||||||
|
const char *proto_name = "nfs3";
|
||||||
|
|
||||||
|
/* Check if NFS3 TCP detection is enabled. If it does not exist in
|
||||||
|
* the configuration file then it will be enabled by default. */
|
||||||
|
if (AppLayerProtoDetectConfProtoDetectionEnabled("udp", proto_name)) {
|
||||||
|
|
||||||
|
rs_nfs3_init(&sfc);
|
||||||
|
|
||||||
|
SCLogNotice("NFS3 UDP protocol detection enabled.");
|
||||||
|
|
||||||
|
AppLayerProtoDetectRegisterProtocol(ALPROTO_NFS3, proto_name);
|
||||||
|
|
||||||
|
if (RunmodeIsUnittests()) {
|
||||||
|
|
||||||
|
SCLogNotice("Unittest mode, registering default configuration.");
|
||||||
|
AppLayerProtoDetectPPRegister(IPPROTO_UDP, NFS3_DEFAULT_PORT,
|
||||||
|
ALPROTO_NFS3, 0, NFS3_MIN_FRAME_LEN, STREAM_TOSERVER,
|
||||||
|
NFS3ProbingParserTS, NFS3ProbingParserTC);
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
if (!AppLayerProtoDetectPPParseConfPorts("udp", IPPROTO_UDP,
|
||||||
|
proto_name, ALPROTO_NFS3, 0, NFS3_MIN_FRAME_LEN,
|
||||||
|
NFS3ProbingParserTS, NFS3ProbingParserTC)) {
|
||||||
|
SCLogNotice("No NFS3 app-layer configuration, enabling NFS3"
|
||||||
|
" detection TCP detection on port %s.",
|
||||||
|
NFS3_DEFAULT_PORT);
|
||||||
|
AppLayerProtoDetectPPRegister(IPPROTO_UDP,
|
||||||
|
NFS3_DEFAULT_PORT, ALPROTO_NFS3, 0,
|
||||||
|
NFS3_MIN_FRAME_LEN, STREAM_TOSERVER,
|
||||||
|
NFS3ProbingParserTS, NFS3ProbingParserTC);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
SCLogNotice("Protocol detecter and parser disabled for NFS3.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AppLayerParserConfParserEnabled("udp", proto_name))
|
||||||
|
{
|
||||||
|
SCLogNotice("Registering NFS3 protocol parser.");
|
||||||
|
|
||||||
|
/* Register functions for state allocation and freeing. A
|
||||||
|
* state is allocated for every new NFS3 flow. */
|
||||||
|
AppLayerParserRegisterStateFuncs(IPPROTO_UDP, ALPROTO_NFS3,
|
||||||
|
NFS3StateAlloc, NFS3StateFree);
|
||||||
|
|
||||||
|
/* Register request parser for parsing frame from server to client. */
|
||||||
|
AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_NFS3,
|
||||||
|
STREAM_TOSERVER, NFS3ParseRequest);
|
||||||
|
|
||||||
|
/* Register response parser for parsing frames from server to client. */
|
||||||
|
AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_NFS3,
|
||||||
|
STREAM_TOCLIENT, NFS3ParseResponse);
|
||||||
|
|
||||||
|
/* Register a function to be called by the application layer
|
||||||
|
* when a transaction is to be freed. */
|
||||||
|
AppLayerParserRegisterTxFreeFunc(IPPROTO_UDP, ALPROTO_NFS3,
|
||||||
|
NFS3StateTxFree);
|
||||||
|
|
||||||
|
AppLayerParserRegisterLoggerFuncs(IPPROTO_UDP, ALPROTO_NFS3,
|
||||||
|
NFS3GetTxLogged, NFS3SetTxLogged);
|
||||||
|
|
||||||
|
/* Register a function to return the current transaction count. */
|
||||||
|
AppLayerParserRegisterGetTxCnt(IPPROTO_UDP, ALPROTO_NFS3,
|
||||||
|
NFS3GetTxCnt);
|
||||||
|
|
||||||
|
/* Transaction handling. */
|
||||||
|
AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_NFS3,
|
||||||
|
NFS3GetAlstateProgressCompletionStatus);
|
||||||
|
AppLayerParserRegisterGetStateProgressFunc(IPPROTO_UDP,
|
||||||
|
ALPROTO_NFS3, NFS3GetStateProgress);
|
||||||
|
AppLayerParserRegisterGetTx(IPPROTO_UDP, ALPROTO_NFS3,
|
||||||
|
NFS3GetTx);
|
||||||
|
|
||||||
|
AppLayerParserRegisterGetFilesFunc(IPPROTO_UDP, ALPROTO_NFS3, NFS3GetFiles);
|
||||||
|
|
||||||
|
/* Application layer event handling. */
|
||||||
|
// AppLayerParserRegisterHasEventsFunc(IPPROTO_UDP, ALPROTO_NFS3,
|
||||||
|
// NFS3HasEvents);
|
||||||
|
|
||||||
|
/* What is this being registered for? */
|
||||||
|
AppLayerParserRegisterDetectStateFuncs(IPPROTO_UDP, ALPROTO_NFS3,
|
||||||
|
NULL, NFS3GetTxDetectState, NFS3SetTxDetectState);
|
||||||
|
|
||||||
|
// AppLayerParserRegisterGetEventInfo(IPPROTO_UDP, ALPROTO_NFS3,
|
||||||
|
// NFS3StateGetEventInfo);
|
||||||
|
// AppLayerParserRegisterGetEventsFunc(IPPROTO_UDP, ALPROTO_NFS3,
|
||||||
|
// NFS3GetEvents);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
SCLogNotice("NFS3 protocol parsing disabled.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef UNITTESTS
|
||||||
|
AppLayerParserRegisterProtocolUnittests(IPPROTO_UDP, ALPROTO_NFS3,
|
||||||
|
NFS3UDPParserRegisterTests);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef UNITTESTS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void NFS3UDPParserRegisterTests(void)
|
||||||
|
{
|
||||||
|
#ifdef UNITTESTS
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* HAVE_RUST */
|
@ -0,0 +1,34 @@
|
|||||||
|
/* 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 Victor Julien <victor@inliniac.net>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __APP_LAYER_NFS3_UDP_H__
|
||||||
|
#define __APP_LAYER_NFS3_UDP_H__
|
||||||
|
|
||||||
|
void RegisterNFS3UDPParsers(void);
|
||||||
|
void NFS3UDPParserRegisterTests(void);
|
||||||
|
|
||||||
|
/** Opaque Rust types. */
|
||||||
|
typedef struct NFS3tate_ NFS3State;
|
||||||
|
typedef struct NFS3Transaction_ NFS3Transaction;
|
||||||
|
|
||||||
|
#endif /* __APP_LAYER_NFS3_H__ */
|
Loading…
Reference in New Issue