suricata
app-layer-nfs-tcp.c
Go to the documentation of this file.
1 /* Copyright (C) 2015 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 #include "rust-nfs-nfs-gen.h"
42 
43 /* The default port to probe for echo traffic if not provided in the
44  * configuration file. */
45 #define NFSTCP_DEFAULT_PORT "2049"
46 
47 /* The minimum size for a RFC message. For some protocols this might
48  * be the size of a header. TODO actual min size is likely larger */
49 #define NFSTCP_MIN_FRAME_LEN 32
50 
51 /* Enum of app-layer events for an echo protocol. Normally you might
52  * have events for errors in parsing data, like unexpected data being
53  * received. For echo we'll make something up, and log an app-layer
54  * level alert if an empty message is received.
55  *
56  * Example rule:
57  *
58  * alert nfs any any -> any any (msg:"SURICATA NFS empty message"; \
59  * app-layer-event:nfs.empty_message; sid:X; rev:Y;)
60  */
61 enum {
63 };
64 
66  {"EMPTY_MESSAGE", NFSTCP_DECODER_EVENT_EMPTY_MESSAGE},
67  { NULL, 0 }
68 };
69 
70 static void *NFSTCPStateAlloc(void)
71 {
72  return rs_nfs_state_new();
73 }
74 
75 static void NFSTCPStateFree(void *state)
76 {
77  rs_nfs_state_free(state);
78 }
79 
80 /**
81  * \brief Callback from the application layer to have a transaction freed.
82  *
83  * \param state a void pointer to the NFSTCPState object.
84  * \param tx_id the transaction ID to free.
85  */
86 static void NFSTCPStateTxFree(void *state, uint64_t tx_id)
87 {
88  rs_nfs_state_tx_free(state, tx_id);
89 }
90 
91 static int NFSTCPStateGetEventInfo(const char *event_name, int *event_id,
93 {
94  return rs_nfs_state_get_event_info(event_name, event_id, event_type);
95 }
96 
97 static int NFSTCPStateGetEventInfoById(int event_id, const char **event_name,
99 {
100  return rs_nfs_state_get_event_info_by_id(event_id, event_name, event_type);
101 }
102 
103 static AppLayerDecoderEvents *NFSTCPGetEvents(void *tx)
104 {
105  return rs_nfs_state_get_events(tx);
106 }
107 
108 /**
109  * \brief Probe the input to see if it looks like echo.
110  *
111  * \retval ALPROTO_NFS if it looks like echo, otherwise
112  * ALPROTO_UNKNOWN.
113  */
114 static AppProto NFSTCPProbingParserMidstream(Flow *f,
115  uint8_t direction,
116  const uint8_t *input, uint32_t input_len,
117  uint8_t *rdir)
118 {
119  if (input_len < NFSTCP_MIN_FRAME_LEN) {
120  return ALPROTO_UNKNOWN;
121  }
122 
123  int8_t r = rs_nfs_probe_ms(direction, input, input_len, rdir);
124  if (r == 1) {
125  return ALPROTO_NFS;
126  } else if (r == -1) {
127  return ALPROTO_FAILED;
128  }
129 
130  SCLogDebug("Protocol not detected as ALPROTO_NFS.");
131  return ALPROTO_UNKNOWN;
132 }
133 
134 /**
135  * \brief Probe the input to see if it looks like echo.
136  *
137  * \retval ALPROTO_NFS if it looks like echo, otherwise
138  * ALPROTO_UNKNOWN.
139  */
140 static AppProto NFSTCPProbingParser(Flow *f,
141  uint8_t direction,
142  const uint8_t *input, uint32_t input_len,
143  uint8_t *rdir)
144 {
145  if (input_len < NFSTCP_MIN_FRAME_LEN) {
146  return ALPROTO_UNKNOWN;
147  }
148 
149  int8_t r = rs_nfs_probe(direction, input, input_len);
150  if (r == 1) {
151  return ALPROTO_NFS;
152  } else if (r == -1) {
153  return ALPROTO_FAILED;
154  }
155 
156  SCLogDebug("Protocol not detected as ALPROTO_NFS.");
157  return ALPROTO_UNKNOWN;
158 }
159 
160 static int NFSTCPParseRequest(Flow *f, void *state,
161  AppLayerParserState *pstate, const uint8_t *input, uint32_t input_len,
162  void *local_data, const uint8_t flags)
163 {
164  uint16_t file_flags = FileFlowToFlags(f, STREAM_TOSERVER);
165  rs_nfs_setfileflags(0, state, file_flags);
166 
167  int res;
168  if (input == NULL && input_len > 0) {
169  res = rs_nfs_parse_request_tcp_gap(state, input_len);
170  } else {
171  res = rs_nfs_parse_request(f, state, pstate, input, input_len, local_data);
172  }
173  return res;
174 }
175 
176 static int 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  int res;
184  if (input == NULL && input_len > 0) {
185  res = rs_nfs_parse_response_tcp_gap(state, input_len);
186  } else {
187  res = rs_nfs_parse_response(f, state, pstate, input, input_len, local_data);
188  }
189  return res;
190 }
191 
192 static uint64_t NFSTCPGetTxCnt(void *state)
193 {
194  return rs_nfs_state_get_tx_count(state);
195 }
196 
197 static void *NFSTCPGetTx(void *state, uint64_t tx_id)
198 {
199  return rs_nfs_state_get_tx(state, tx_id);
200 }
201 
202 static AppLayerGetTxIterTuple RustNFSTCPGetTxIterator(
203  const uint8_t ipproto, const AppProto alproto,
204  void *alstate, uint64_t min_tx_id, uint64_t max_tx_id,
205  AppLayerGetTxIterState *istate)
206 {
207  return rs_nfs_state_get_tx_iterator(alstate, min_tx_id, (uint64_t *)istate);
208 }
209 
210 static void NFSTCPSetTxLogged(void *state, void *vtx, LoggerId logged)
211 {
212  rs_nfs_tx_set_logged(state, vtx, logged);
213 }
214 
215 static LoggerId NFSTCPGetTxLogged(void *state, void *vtx)
216 {
217  return rs_nfs_tx_get_logged(state, vtx);
218 }
219 
220 /**
221  * \brief Called by the application layer.
222  *
223  * In most cases 1 can be returned here.
224  */
225 static int NFSTCPGetAlstateProgressCompletionStatus(uint8_t direction) {
226  return rs_nfs_state_progress_completion_status(direction);
227 }
228 
229 /**
230  * \brief Return the state of a transaction in a given direction.
231  *
232  * In the case of the echo protocol, the existence of a transaction
233  * means that the request is done. However, some protocols that may
234  * need multiple chunks of data to complete the request may need more
235  * than just the existence of a transaction for the request to be
236  * considered complete.
237  *
238  * For the response to be considered done, the response for a request
239  * needs to be seen. The response_done flag is set on response for
240  * checking here.
241  */
242 static int NFSTCPGetStateProgress(void *tx, uint8_t direction)
243 {
244  return rs_nfs_tx_get_alstate_progress(tx, direction);
245 }
246 
247 /**
248  * \brief get stored tx detect state
249  */
250 static DetectEngineState *NFSTCPGetTxDetectState(void *vtx)
251 {
252  return rs_nfs_state_get_tx_detect_state(vtx);
253 }
254 
255 /**
256  * \brief set store tx detect state
257  */
258 static int NFSTCPSetTxDetectState(void *vtx, DetectEngineState *s)
259 {
260  rs_nfs_state_set_tx_detect_state(vtx, s);
261  return 0;
262 }
263 
264 static FileContainer *NFSTCPGetFiles(void *state, uint8_t direction)
265 {
266  return rs_nfs_getfiles(direction, state);
267 }
268 
269 static void NFSTCPSetDetectFlags(void *tx, uint8_t dir, uint64_t flags)
270 {
271  rs_nfs_tx_set_detect_flags(tx, dir, flags);
272 }
273 
274 static uint64_t NFSTCPGetDetectFlags(void *tx, uint8_t dir)
275 {
276  return rs_nfs_tx_get_detect_flags(tx, dir);
277 }
278 
280 static SuricataFileContext sfc = { &sbcfg };
281 
283 {
284  const char *proto_name = "nfs";
285 
286  /* Check if NFSTCP TCP detection is enabled. If it does not exist in
287  * the configuration file then it will be enabled by default. */
288  if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
289 
290  rs_nfs_init(&sfc);
291 
292  SCLogDebug("NFSTCP TCP protocol detection enabled.");
293 
295 
296  if (RunmodeIsUnittests()) {
297 
298  SCLogDebug("Unittest mode, registering default configuration.");
301  NFSTCPProbingParser, NFSTCPProbingParser);
302 
303  }
304  else {
305  int midstream = 0;
306  ConfGetBool("stream.midstream", &midstream);
307  ProbingParserFPtr FuncPtr = NFSTCPProbingParser;
308  if (midstream)
309  FuncPtr = NFSTCPProbingParserMidstream;
310 
311  if (!AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP,
312  proto_name, ALPROTO_NFS, 0, NFSTCP_MIN_FRAME_LEN,
313  FuncPtr, FuncPtr)) {
314  SCLogDebug("No NFSTCP app-layer configuration, enabling NFSTCP"
315  " detection TCP detection on port %s.",
317  /* register 'midstream' probing parsers if midstream is enabled. */
318  AppLayerProtoDetectPPRegister(IPPROTO_TCP,
321  FuncPtr, FuncPtr);
322  }
323 
324  }
325 
326  }
327 
328  else {
329  SCLogDebug("Protocol detecter and parser disabled for NFSTCP.");
330  return;
331  }
332 
333  if (AppLayerParserConfParserEnabled("tcp", proto_name))
334  {
335  SCLogDebug("Registering NFSTCP protocol parser.");
336 
337  /* Register functions for state allocation and freeing. A
338  * state is allocated for every new NFSTCP flow. */
340  NFSTCPStateAlloc, NFSTCPStateFree);
341 
342  /* Register request parser for parsing frame from server to client. */
344  STREAM_TOSERVER, NFSTCPParseRequest);
345 
346  /* Register response parser for parsing frames from server to client. */
348  STREAM_TOCLIENT, NFSTCPParseResponse);
349 
350  /* Register a function to be called by the application layer
351  * when a transaction is to be freed. */
353  NFSTCPStateTxFree);
354 
356  NFSTCPGetTxLogged, NFSTCPSetTxLogged);
357 
358  /* Register a function to return the current transaction count. */
360  NFSTCPGetTxCnt);
361 
362  /* Transaction handling. */
364  NFSTCPGetAlstateProgressCompletionStatus);
366  ALPROTO_NFS, NFSTCPGetStateProgress);
368  NFSTCPGetTx);
370  RustNFSTCPGetTxIterator);
371 
372  AppLayerParserRegisterGetFilesFunc(IPPROTO_TCP, ALPROTO_NFS, NFSTCPGetFiles);
373 
374  /* What is this being registered for? */
376  NFSTCPGetTxDetectState, NFSTCPSetTxDetectState);
377 
379  NFSTCPStateGetEventInfo);
380 
382  NFSTCPStateGetEventInfoById);
383 
385  NFSTCPGetEvents);
386 
388  NFSTCPGetDetectFlags, NFSTCPSetDetectFlags);
389 
390  /* This parser accepts gaps. */
393  }
394  else {
395  SCLogDebug("NFSTCP protocol parsing disabled.");
396  }
397 
398 #ifdef UNITTESTS
401 #endif
402 }
403 
404 #ifdef UNITTESTS
405 #endif
406 
408 {
409 #ifdef UNITTESTS
410 #endif
411 }
#define STREAMING_BUFFER_CONFIG_INITIALIZER
enum AppLayerEventType_ AppLayerEventType
uint16_t flags
#define SCLogDebug(...)
Definition: util-debug.h:335
void AppLayerParserRegisterOptionFlags(uint8_t ipproto, AppProto alproto, uint32_t flags)
#define NFSTCP_DEFAULT_PORT
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
void RegisterNFSTCPParsers(void)
void AppLayerParserRegisterGetStateProgressFunc(uint8_t ipproto, AppProto alproto, int(*StateGetProgress)(void *alstate, uint8_t direction))
uint32_t event_type
LoggerId
int logged
int ConfGetBool(const char *name, int *val)
Retrieve a configuration value as an boolen.
Definition: conf.c:517
int AppLayerProtoDetectConfProtoDetectionEnabled(const char *ipproto, const char *alproto)
Given a protocol name, checks if proto detection is enabled in the conf file.
void AppLayerParserRegisterDetectFlagsFuncs(uint8_t ipproto, AppProto alproto, uint64_t(*GetTxDetectFlags)(void *tx, uint8_t dir), void(*SetTxDetectFlags)(void *tx, uint8_t dir, uint64_t))
void AppLayerParserRegisterDetectStateFuncs(uint8_t ipproto, AppProto alproto, DetectEngineState *(*GetTxDetectState)(void *tx), int(*SetTxDetectState)(void *tx, DetectEngineState *))
void AppLayerParserRegisterGetEventsFunc(uint8_t ipproto, AppProto alproto, AppLayerDecoderEvents *(*StateGetEvents)(void *))
void AppLayerParserRegisterLoggerFuncs(uint8_t ipproto, AppProto alproto, LoggerId(*StateGetTxLogged)(void *, void *), void(*StateSetTxLogged)(void *, void *, LoggerId))
uint16_t AppProto
AppProto(* ProbingParserFPtr)(Flow *f, uint8_t dir, const uint8_t *input, uint32_t input_len, uint8_t *rdir)
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 and when...
Data structure to store app layer decoder events.
int AppLayerParserRegisterParser(uint8_t ipproto, AppProto alproto, uint8_t direction, AppLayerParserFPtr Parser)
Register app layer parser for the protocol.
void AppLayerParserRegisterGetTx(uint8_t ipproto, AppProto alproto, void *(StateGetTx)(void *alstate, uint64_t tx_id))
#define NFSTCP_MIN_FRAME_LEN
void AppLayerParserRegisterGetFilesFunc(uint8_t ipproto, AppProto alproto, FileContainer *(*StateGetFiles)(void *, uint8_t))
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)
void AppLayerParserRegisterGetEventInfo(uint8_t ipproto, AppProto alproto, int(*StateGetEventInfo)(const char *event_name, int *event_id, AppLayerEventType *event_type))
void NFSTCPParserRegisterTests(void)
#define STREAM_TOCLIENT
Definition: stream.h:32
void AppLayerParserRegisterGetStateProgressCompletionStatus(AppProto alproto, int(*StateGetProgressCompletionStatus)(uint8_t direction))
int RunmodeIsUnittests(void)
Definition: suricata.c:267
void AppLayerParserRegisterGetEventInfoById(uint8_t ipproto, AppProto alproto, int(*StateGetEventInfoById)(int event_id, const char **event_name, AppLayerEventType *event_type))
void AppLayerProtoDetectRegisterProtocol(AppProto alproto, const char *alproto_name)
Registers a protocol for protocol detection phase.
PoolThreadReserved res
uint16_t tx_id
void AppLayerParserRegisterProtocolUnittests(uint8_t ipproto, AppProto alproto, void(*RegisterUnittests)(void))
SCEnumCharMap nfs_decoder_event_table[]
#define STREAM_TOSERVER
Definition: stream.h:31
#define APP_LAYER_PARSER_OPT_ACCEPT_GAPS
void AppLayerParserRegisterGetTxIterator(uint8_t ipproto, AppProto alproto, AppLayerGetTxIteratorFunc Func)
uint16_t FileFlowToFlags(const Flow *flow, uint8_t direction)
Definition: util-file.c:219
void AppLayerParserRegisterGetTxCnt(uint8_t ipproto, AppProto alproto, uint64_t(*StateGetTxCnt)(void *alstate))
void AppLayerParserRegisterStateFuncs(uint8_t ipproto, AppProto alproto, void *(*StateAlloc)(void), void(*StateFree)(void *))
Flow data structure.
Definition: flow.h:325
void AppLayerParserRegisterTxFreeFunc(uint8_t ipproto, AppProto alproto, void(*StateTransactionFree)(void *, uint64_t))