suricata
app-layer-nfs-tcp.c
Go to the documentation of this file.
1 /* Copyright (C) 2015-2020 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  *
21  * \author Victor Julien <victor@inliniac.net>
22  *
23  * NFS application layer detector and parser.
24  *
25  * This implements a application layer for the NFS protocol
26  * running on port 2049.
27  */
28 
29 #include "suricata-common.h"
30 #include "stream.h"
31 #include "conf.h"
32 
33 #include "util-unittest.h"
34 
35 #include "app-layer-detect-proto.h"
36 #include "app-layer-parser.h"
37 
38 #include "app-layer-nfs-tcp.h"
39 
40 #include "rust.h"
41 
42 /* The default port to probe for echo traffic if not provided in the
43  * configuration file. */
44 #define NFSTCP_DEFAULT_PORT "2049"
45 
46 /* The minimum size for a RFC message. For some protocols this might
47  * be the size of a header. TODO actual min size is likely larger */
48 #define NFSTCP_MIN_FRAME_LEN 32
49 
50 /* Enum of app-layer events for an echo protocol. Normally you might
51  * have events for errors in parsing data, like unexpected data being
52  * received. For echo we'll make something up, and log an app-layer
53  * level alert if an empty message is received.
54  *
55  * Example rule:
56  *
57  * alert nfs any any -> any any (msg:"SURICATA NFS empty message"; \
58  * app-layer-event:nfs.empty_message; sid:X; rev:Y;)
59  */
60 enum {
62 };
63 
65  {"EMPTY_MESSAGE", NFSTCP_DECODER_EVENT_EMPTY_MESSAGE},
66  { NULL, 0 }
67 };
68 
69 static void *NFSTCPStateAlloc(void *orig_state, AppProto proto_orig)
70 {
71  return rs_nfs_state_new(orig_state, proto_orig);
72 }
73 
74 static void NFSTCPStateFree(void *state)
75 {
76  rs_nfs_state_free(state);
77 }
78 
79 /**
80  * \brief Callback from the application layer to have a transaction freed.
81  *
82  * \param state a void pointer to the NFSTCPState object.
83  * \param tx_id the transaction ID to free.
84  */
85 static void NFSTCPStateTxFree(void *state, uint64_t tx_id)
86 {
87  rs_nfs_state_tx_free(state, tx_id);
88 }
89 
90 static int NFSTCPStateGetEventInfo(const char *event_name, int *event_id,
91  AppLayerEventType *event_type)
92 {
93  return rs_nfs_state_get_event_info(event_name, event_id, event_type);
94 }
95 
96 static int NFSTCPStateGetEventInfoById(int event_id, const char **event_name,
97  AppLayerEventType *event_type)
98 {
99  return rs_nfs_state_get_event_info_by_id(event_id, event_name, event_type);
100 }
101 
102 static AppLayerDecoderEvents *NFSTCPGetEvents(void *tx)
103 {
104  return rs_nfs_state_get_events(tx);
105 }
106 
107 /**
108  * \brief Probe the input to see if it looks like echo.
109  *
110  * \retval ALPROTO_NFS if it looks like echo, otherwise
111  * ALPROTO_UNKNOWN.
112  */
113 static AppProto NFSTCPProbingParserMidstream(Flow *f,
114  uint8_t direction,
115  const uint8_t *input, uint32_t input_len,
116  uint8_t *rdir)
117 {
118  if (input_len < NFSTCP_MIN_FRAME_LEN) {
119  return ALPROTO_UNKNOWN;
120  }
121 
122  int8_t r = rs_nfs_probe_ms(direction, input, input_len, rdir);
123  if (r == 1) {
124  return ALPROTO_NFS;
125  } else if (r == -1) {
126  return ALPROTO_FAILED;
127  }
128 
129  SCLogDebug("Protocol not detected as ALPROTO_NFS.");
130  return ALPROTO_UNKNOWN;
131 }
132 
133 /**
134  * \brief Probe the input to see if it looks like echo.
135  *
136  * \retval ALPROTO_NFS if it looks like echo, otherwise
137  * ALPROTO_UNKNOWN.
138  */
139 static AppProto NFSTCPProbingParser(Flow *f,
140  uint8_t direction,
141  const uint8_t *input, uint32_t input_len,
142  uint8_t *rdir)
143 {
144  if (input_len < NFSTCP_MIN_FRAME_LEN) {
145  return ALPROTO_UNKNOWN;
146  }
147 
148  int8_t r = rs_nfs_probe(direction, input, input_len);
149  if (r == 1) {
150  return ALPROTO_NFS;
151  } else if (r == -1) {
152  return ALPROTO_FAILED;
153  }
154 
155  SCLogDebug("Protocol not detected as ALPROTO_NFS.");
156  return ALPROTO_UNKNOWN;
157 }
158 
159 static AppLayerResult NFSTCPParseRequest(Flow *f, void *state,
160  AppLayerParserState *pstate, const uint8_t *input, uint32_t input_len,
161  void *local_data, const uint8_t flags)
162 {
163  uint16_t file_flags = FileFlowToFlags(f, STREAM_TOSERVER);
164  rs_nfs_setfileflags(0, state, file_flags);
165 
166  if (input == NULL && input_len > 0) {
167  AppLayerResult res = rs_nfs_parse_request_tcp_gap(state, input_len);
169  } else {
170  AppLayerResult res = rs_nfs_parse_request(f, state, pstate,
171  input, input_len, local_data);
173  }
174 }
175 
176 static AppLayerResult NFSTCPParseResponse(Flow *f, void *state, AppLayerParserState *pstate,
177  const uint8_t *input, uint32_t input_len, void *local_data,
178  const uint8_t flags)
179 {
180  uint16_t file_flags = FileFlowToFlags(f, STREAM_TOCLIENT);
181  rs_nfs_setfileflags(1, state, file_flags);
182 
183  if (input == NULL && input_len > 0) {
184  AppLayerResult res = rs_nfs_parse_response_tcp_gap(state, input_len);
186  } else {
187  AppLayerResult res = rs_nfs_parse_response(f, state, pstate,
188  input, input_len, local_data);
190  }
191 }
192 
193 static uint64_t NFSTCPGetTxCnt(void *state)
194 {
195  return rs_nfs_state_get_tx_count(state);
196 }
197 
198 static void *NFSTCPGetTx(void *state, uint64_t tx_id)
199 {
200  return rs_nfs_state_get_tx(state, tx_id);
201 }
202 
203 static AppLayerGetTxIterTuple RustNFSTCPGetTxIterator(
204  const uint8_t ipproto, const AppProto alproto,
205  void *alstate, uint64_t min_tx_id, uint64_t max_tx_id,
206  AppLayerGetTxIterState *istate)
207 {
208  return rs_nfs_state_get_tx_iterator(alstate, min_tx_id, (uint64_t *)istate);
209 }
210 
211 /**
212  * \brief Called by the application layer.
213  *
214  * In most cases 1 can be returned here.
215  */
216 static int NFSTCPGetAlstateProgressCompletionStatus(uint8_t direction) {
217  return rs_nfs_state_progress_completion_status(direction);
218 }
219 
220 /**
221  * \brief Return the state of a transaction in a given direction.
222  *
223  * In the case of the echo protocol, the existence of a transaction
224  * means that the request is done. However, some protocols that may
225  * need multiple chunks of data to complete the request may need more
226  * than just the existence of a transaction for the request to be
227  * considered complete.
228  *
229  * For the response to be considered done, the response for a request
230  * needs to be seen. The response_done flag is set on response for
231  * checking here.
232  */
233 static int NFSTCPGetStateProgress(void *tx, uint8_t direction)
234 {
235  return rs_nfs_tx_get_alstate_progress(tx, direction);
236 }
237 
238 /**
239  * \brief get stored tx detect state
240  */
241 static DetectEngineState *NFSTCPGetTxDetectState(void *vtx)
242 {
243  return rs_nfs_state_get_tx_detect_state(vtx);
244 }
245 
246 /**
247  * \brief set store tx detect state
248  */
249 static int NFSTCPSetTxDetectState(void *vtx, DetectEngineState *s)
250 {
251  rs_nfs_state_set_tx_detect_state(vtx, s);
252  return 0;
253 }
254 
255 static FileContainer *NFSTCPGetFiles(void *state, uint8_t direction)
256 {
257  return rs_nfs_getfiles(direction, state);
258 }
259 
261 static SuricataFileContext sfc = { &sbcfg };
262 
264 {
265  const char *proto_name = "nfs";
266 
267  /* Check if NFSTCP TCP detection is enabled. If it does not exist in
268  * the configuration file then it will be enabled by default. */
269  if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
270 
271  rs_nfs_init(&sfc);
272 
273  SCLogDebug("NFSTCP TCP protocol detection enabled.");
274 
276 
277  if (RunmodeIsUnittests()) {
278 
279  SCLogDebug("Unittest mode, registering default configuration.");
282  NFSTCPProbingParser, NFSTCPProbingParser);
283 
284  }
285  else {
286  int midstream = 0;
287  ConfGetBool("stream.midstream", &midstream);
288  ProbingParserFPtr FuncPtr = NFSTCPProbingParser;
289  if (midstream)
290  FuncPtr = NFSTCPProbingParserMidstream;
291 
292  if (!AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP,
293  proto_name, ALPROTO_NFS, 0, NFSTCP_MIN_FRAME_LEN,
294  FuncPtr, FuncPtr)) {
295  SCLogDebug("No NFSTCP app-layer configuration, enabling NFSTCP"
296  " detection TCP detection on port %s.",
298  /* register 'midstream' probing parsers if midstream is enabled. */
299  AppLayerProtoDetectPPRegister(IPPROTO_TCP,
302  FuncPtr, FuncPtr);
303  }
304 
305  }
306 
307  }
308 
309  else {
310  SCLogDebug("Protocol detecter and parser disabled for NFSTCP.");
311  return;
312  }
313 
314  if (AppLayerParserConfParserEnabled("tcp", proto_name))
315  {
316  SCLogDebug("Registering NFSTCP protocol parser.");
317 
318  /* Register functions for state allocation and freeing. A
319  * state is allocated for every new NFSTCP flow. */
321  NFSTCPStateAlloc, NFSTCPStateFree);
322 
323  /* Register request parser for parsing frame from server to client. */
325  STREAM_TOSERVER, NFSTCPParseRequest);
326 
327  /* Register response parser for parsing frames from server to client. */
329  STREAM_TOCLIENT, NFSTCPParseResponse);
330 
331  /* Register a function to be called by the application layer
332  * when a transaction is to be freed. */
334  NFSTCPStateTxFree);
335 
336  /* Register a function to return the current transaction count. */
338  NFSTCPGetTxCnt);
339 
340  /* Transaction handling. */
342  NFSTCPGetAlstateProgressCompletionStatus);
344  ALPROTO_NFS, NFSTCPGetStateProgress);
346  NFSTCPGetTx);
348  RustNFSTCPGetTxIterator);
349 
350  AppLayerParserRegisterGetFilesFunc(IPPROTO_TCP, ALPROTO_NFS, NFSTCPGetFiles);
351 
352  /* What is this being registered for? */
354  NFSTCPGetTxDetectState, NFSTCPSetTxDetectState);
355 
357  NFSTCPStateGetEventInfo);
358 
360  NFSTCPStateGetEventInfoById);
361 
363  NFSTCPGetEvents);
364 
366  rs_nfs_get_tx_data);
367 
368  /* This parser accepts gaps. */
371  }
372  else {
373  SCLogDebug("NFSTCP protocol parsing disabled.");
374  }
375 
376 #ifdef UNITTESTS
379 #endif
380 }
381 
382 #ifdef UNITTESTS
383 #endif
384 
386 {
387 #ifdef UNITTESTS
388 #endif
389 }
AppLayerParserRegisterGetStateProgressFunc
void AppLayerParserRegisterGetStateProgressFunc(uint8_t ipproto, AppProto alproto, int(*StateGetProgress)(void *alstate, uint8_t direction))
Definition: app-layer-parser.c:475
AppLayerProtoDetectPPParseConfPorts
int AppLayerProtoDetectPPParseConfPorts(const char *ipproto_name, uint8_t ipproto, const char *alproto_name, AppProto alproto, uint16_t min_depth, uint16_t max_depth, ProbingParserFPtr ProbingParserTs, ProbingParserFPtr ProbingParserTc)
Definition: app-layer-detect-proto.c:1649
FileContainer_
Definition: util-file.h:100
AppLayerParserRegisterOptionFlags
void AppLayerParserRegisterOptionFlags(uint8_t ipproto, AppProto alproto, uint32_t flags)
Definition: app-layer-parser.c:382
DetectEngineState_
Definition: detect-engine-state.h:92
ConfGetBool
int ConfGetBool(const char *name, int *val)
Retrieve a configuration value as an boolen.
Definition: conf.c:516
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:298
AppProto
uint16_t AppProto
Definition: app-layer-protos.h:73
AppLayerParserConfParserEnabled
int AppLayerParserConfParserEnabled(const char *ipproto, const char *alproto_name)
check if a parser is enabled in the config Returns enabled always if: were running unittests
Definition: app-layer-parser.c:301
STREAMING_BUFFER_CONFIG_INITIALIZER
#define STREAMING_BUFFER_CONFIG_INITIALIZER
Definition: util-streaming-buffer.h:77
Flow_
Flow data structure.
Definition: flow.h:347
AppLayerEventType
enum AppLayerEventType_ AppLayerEventType
AppLayerParserRegisterGetEventsFunc
void AppLayerParserRegisterGetEventsFunc(uint8_t ipproto, AppProto alproto, AppLayerDecoderEvents *(*StateGetEvents)(void *))
Definition: app-layer-parser.c:436
AppLayerParserRegisterTxFreeFunc
void AppLayerParserRegisterTxFreeFunc(uint8_t ipproto, AppProto alproto, void(*StateTransactionFree)(void *, uint64_t))
Definition: app-layer-parser.c:486
rust.h
AppLayerParserRegisterDetectStateFuncs
void AppLayerParserRegisterDetectStateFuncs(uint8_t ipproto, AppProto alproto, DetectEngineState *(*GetTxDetectState)(void *tx), int(*SetTxDetectState)(void *tx, DetectEngineState *))
Definition: app-layer-parser.c:562
AppLayerDecoderEvents_
Data structure to store app layer decoder events.
Definition: app-layer-events.h:34
util-unittest.h
AppLayerProtoDetectPPRegister
void AppLayerProtoDetectPPRegister(uint8_t ipproto, const char *portstr, AppProto alproto, uint16_t min_depth, uint16_t max_depth, uint8_t direction, ProbingParserFPtr ProbingParser1, ProbingParserFPtr ProbingParser2)
register parser at a port
Definition: app-layer-detect-proto.c:1615
NFSTCP_DEFAULT_PORT
#define NFSTCP_DEFAULT_PORT
Definition: app-layer-nfs-tcp.c:44
app-layer-detect-proto.h
AppLayerParserState_
Definition: app-layer-parser.c:154
FileFlowToFlags
uint16_t FileFlowToFlags(const Flow *flow, uint8_t direction)
Definition: util-file.c:231
STREAM_TOSERVER
#define STREAM_TOSERVER
Definition: stream.h:31
res
PoolThreadReserved res
Definition: stream-tcp-private.h:0
AppLayerParserRegisterGetFilesFunc
void AppLayerParserRegisterGetFilesFunc(uint8_t ipproto, AppProto alproto, FileContainer *(*StateGetFiles)(void *, uint8_t))
Definition: app-layer-parser.c:425
NFSTCP_DECODER_EVENT_EMPTY_MESSAGE
@ NFSTCP_DECODER_EVENT_EMPTY_MESSAGE
Definition: app-layer-nfs-tcp.c:61
AppLayerParserRegisterStateFuncs
void AppLayerParserRegisterStateFuncs(uint8_t ipproto, AppProto alproto, void *(*StateAlloc)(void *, AppProto), void(*StateFree)(void *))
Definition: app-layer-parser.c:398
app-layer-parser.h
RegisterNFSTCPParsers
void RegisterNFSTCPParsers(void)
Definition: app-layer-nfs-tcp.c:263
stream.h
AppLayerParserRegisterProtocolUnittests
void AppLayerParserRegisterProtocolUnittests(uint8_t ipproto, AppProto alproto, void(*RegisterUnittests)(void))
Definition: app-layer-parser.c:1721
AppLayerGetTxIterState
Definition: app-layer-parser.h:118
conf.h
AppLayerParserRegisterGetEventInfo
void AppLayerParserRegisterGetEventInfo(uint8_t ipproto, AppProto alproto, int(*StateGetEventInfo)(const char *event_name, int *event_id, AppLayerEventType *event_type))
Definition: app-layer-parser.c:550
AppLayerProtoDetectRegisterProtocol
void AppLayerProtoDetectRegisterProtocol(AppProto alproto, const char *alproto_name)
Registers a protocol for protocol detection phase.
Definition: app-layer-detect-proto.c:1825
ProbingParserFPtr
AppProto(* ProbingParserFPtr)(Flow *f, uint8_t dir, const uint8_t *input, uint32_t input_len, uint8_t *rdir)
Definition: app-layer-detect-proto.h:30
RunmodeIsUnittests
int RunmodeIsUnittests(void)
Definition: suricata.c:264
AppLayerParserRegisterParser
int AppLayerParserRegisterParser(uint8_t ipproto, AppProto alproto, uint8_t direction, AppLayerParserFPtr Parser)
Register app layer parser for the protocol.
Definition: app-layer-parser.c:359
AppLayerParserRegisterGetStateProgressCompletionStatus
void AppLayerParserRegisterGetStateProgressCompletionStatus(AppProto alproto, int(*StateGetProgressCompletionStatus)(uint8_t direction))
Definition: app-layer-parser.c:527
AppLayerParserRegisterGetTx
void AppLayerParserRegisterGetTx(uint8_t ipproto, AppProto alproto, void *(StateGetTx)(void *alstate, uint64_t tx_id))
Definition: app-layer-parser.c:508
SCReturnStruct
#define SCReturnStruct(x)
Definition: util-debug.h:320
flags
uint8_t flags
Definition: decode-gre.h:0
suricata-common.h
SCEnumCharMap_
Definition: util-enum.h:27
STREAM_TOCLIENT
#define STREAM_TOCLIENT
Definition: stream.h:32
AppLayerParserRegisterTxDataFunc
void AppLayerParserRegisterTxDataFunc(uint8_t ipproto, AppProto alproto, AppLayerTxData *(*GetTxData)(void *tx))
Definition: app-layer-parser.c:574
NFSTCP_MIN_FRAME_LEN
#define NFSTCP_MIN_FRAME_LEN
Definition: app-layer-nfs-tcp.c:48
StreamingBufferConfig_
Definition: util-streaming-buffer.h:67
APP_LAYER_PARSER_OPT_ACCEPT_GAPS
#define APP_LAYER_PARSER_OPT_ACCEPT_GAPS
Definition: app-layer-parser.h:45
AppLayerParserRegisterGetTxIterator
void AppLayerParserRegisterGetTxIterator(uint8_t ipproto, AppProto alproto, AppLayerGetTxIteratorFunc Func)
Definition: app-layer-parser.c:519
SuricataFileContext_
Definition: rust-context.h:53
AppLayerParserRegisterGetEventInfoById
void AppLayerParserRegisterGetEventInfoById(uint8_t ipproto, AppProto alproto, int(*StateGetEventInfoById)(int event_id, const char **event_name, AppLayerEventType *event_type))
Definition: app-layer-parser.c:538
ALPROTO_UNKNOWN
@ ALPROTO_UNKNOWN
Definition: app-layer-protos.h:29
ALPROTO_FAILED
@ ALPROTO_FAILED
Definition: app-layer-protos.h:63
AppLayerParserRegisterGetTxCnt
void AppLayerParserRegisterGetTxCnt(uint8_t ipproto, AppProto alproto, uint64_t(*StateGetTxCnt)(void *alstate))
Definition: app-layer-parser.c:497
NFSTCPParserRegisterTests
void NFSTCPParserRegisterTests(void)
Definition: app-layer-nfs-tcp.c:385
nfs_decoder_event_table
SCEnumCharMap nfs_decoder_event_table[]
Definition: app-layer-nfs-tcp.c:64
AppLayerProtoDetectConfProtoDetectionEnabled
int AppLayerProtoDetectConfProtoDetectionEnabled(const char *ipproto, const char *alproto)
Given a protocol name, checks if proto detection is enabled in the conf file.
Definition: app-layer-detect-proto.c:1888
ALPROTO_NFS
@ ALPROTO_NFS
Definition: app-layer-protos.h:45
app-layer-nfs-tcp.h