suricata
detect-ftpdata.c
Go to the documentation of this file.
1 /* Copyright (C) 2017 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 Eric Leblond <eric@regit.org>
22  *
23  * Match on ftp command used to trigger a ftp data transfer
24  */
25 
26 #include "suricata-common.h"
27 #include "util-unittest.h"
28 
29 #include "detect-parse.h"
30 #include "detect-engine.h"
31 #include "detect-engine-state.h"
32 
33 #include "app-layer-ftp.h"
34 
35 #include "detect-ftpdata.h"
36 
37 /**
38  * \brief Regex for parsing our keyword options
39  */
40 #define PARSE_REGEX "^\\s*(stor|retr)\\s*$"
41 static pcre *parse_regex;
42 static pcre_extra *parse_regex_study;
43 
44 /* Prototypes of functions registered in DetectFtpdataRegister below */
45 static int DetectFtpdataMatch(ThreadVars *, DetectEngineThreadCtx *,
46  Flow *, uint8_t, void *, void *,
47  const Signature *, const SigMatchCtx *);
48 static int DetectFtpdataSetup (DetectEngineCtx *, Signature *, const char *);
49 static void DetectFtpdataFree (void *);
50 static void DetectFtpdataRegisterTests (void);
51 static int DetectEngineInspectFtpdataGeneric(ThreadVars *tv,
52  DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
53  const Signature *s, const SigMatchData *smd,
54  Flow *f, uint8_t flags, void *alstate,
55  void *txv, uint64_t tx_id);
56 static int g_ftpdata_buffer_id = 0;
57 
58 /**
59  * \brief Registration function for ftpcommand: keyword
60  *
61  * This function is called once in the 'lifetime' of the engine.
62  */
64  /* keyword name: this is how the keyword is used in a rule */
65  sigmatch_table[DETECT_FTPDATA].name = "ftpdata_command";
66  /* description: listed in "suricata --list-keywords=all" */
67  sigmatch_table[DETECT_FTPDATA].desc = "match FTP command triggering a FTP data channel";
68  sigmatch_table[DETECT_FTPDATA].url = DOC_URL DOC_VERSION "/rules/ftp-keywords.html#ftpdata-command";
69  sigmatch_table[DETECT_FTPDATA].AppLayerTxMatch = DetectFtpdataMatch;
70  /* setup function is called during signature parsing, when the ftpcommand
71  * keyword is encountered in the rule */
72  sigmatch_table[DETECT_FTPDATA].Setup = DetectFtpdataSetup;
73  /* free function is called when the detect engine is freed. Normally at
74  * shutdown, but also during rule reloads. */
75  sigmatch_table[DETECT_FTPDATA].Free = DetectFtpdataFree;
76  /* registers unittests into the system */
77  sigmatch_table[DETECT_FTPDATA].RegisterTests = DetectFtpdataRegisterTests;
78 
79  DetectAppLayerInspectEngineRegister("ftpdata_command",
81  DetectEngineInspectFtpdataGeneric);
82 
83  DetectAppLayerInspectEngineRegister("ftpdata_command",
85  DetectEngineInspectFtpdataGeneric);
86  g_ftpdata_buffer_id = DetectBufferTypeGetByName("ftpdata_command");
87 
88  /* set up the PCRE for keyword parsing */
89  DetectSetupParseRegexes(PARSE_REGEX, &parse_regex, &parse_regex_study);
90 }
91 
92 static int DetectEngineInspectFtpdataGeneric(ThreadVars *tv,
93  DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
94  const Signature *s, const SigMatchData *smd,
95  Flow *f, uint8_t flags, void *alstate,
96  void *txv, uint64_t tx_id)
97 {
98  return DetectEngineInspectGenericList(tv, de_ctx, det_ctx, s, smd,
99  f, flags, alstate, txv, tx_id);
100 }
101 
102 /**
103  * \brief This function is used to check matches from the FTP App Layer Parser
104  *
105  * \param t pointer to thread vars
106  * \param det_ctx pointer to the pattern matcher thread
107  * \param p pointer to the current packet
108  * \param m pointer to the sigmatch
109  * \retval 0 no match
110  * \retval 1 match
111  */
112 static int DetectFtpdataMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
113  Flow *f, uint8_t flags,
114  void *state, void *txv,
115  const Signature *s, const SigMatchCtx *m)
116 {
117  const DetectFtpdataData *ftpcommandd = (const DetectFtpdataData *) m;
118  const FtpDataState *ftp_state = (const FtpDataState *)state;
119 
120  if (ftp_state == NULL)
121  return 0;
122 
123  if (ftpcommandd->command == ftp_state->command) {
124  /* Only match if the flow is in the good direction */
125  if ((flags & STREAM_TOSERVER) && (ftpcommandd->command == FTP_COMMAND_RETR)) {
126  return 0;
127  } else if ((flags & STREAM_TOCLIENT) && (ftpcommandd->command == FTP_COMMAND_STOR)) {
128  return 0;
129  }
130  return 1;
131  }
132 
133  return 0;
134 }
135 
136 /**
137  * \brief This function is used to parse ftpcommand options passed via ftpcommand: keyword
138  *
139  * \param ftpcommandstr Pointer to the user provided ftpcommand options
140  *
141  * \retval ftpcommandd pointer to DetectFtpdataData on success
142  * \retval NULL on failure
143  */
144 static DetectFtpdataData *DetectFtpdataParse(const char *ftpcommandstr)
145 {
146  DetectFtpdataData *ftpcommandd = NULL;
147  char arg1[5] = "";
148 #define MAX_SUBSTRINGS 30
149  int ret = 0, res = 0;
150  int ov[MAX_SUBSTRINGS];
151 
152  ret = pcre_exec(parse_regex, parse_regex_study,
153  ftpcommandstr, strlen(ftpcommandstr),
154  0, 0, ov, MAX_SUBSTRINGS);
155  if (ret != 2) {
156  SCLogError(SC_ERR_PCRE_MATCH, "parse error, ret %" PRId32 "", ret);
157  goto error;
158  }
159 
160  res = pcre_copy_substring((char *) ftpcommandstr, ov, MAX_SUBSTRINGS, 1, arg1, sizeof(arg1));
161  if (res < 0) {
162  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
163  goto error;
164  }
165  SCLogDebug("Arg1 \"%s\"", arg1);
166 
167  ftpcommandd = SCMalloc(sizeof (DetectFtpdataData));
168  if (unlikely(ftpcommandd == NULL))
169  goto error;
170  if (!strcmp(arg1, "stor")) {
171  ftpcommandd->command = FTP_COMMAND_STOR;
172  } else if (!strcmp(arg1, "retr")) {
173  ftpcommandd->command = FTP_COMMAND_RETR;
174  } else {
175  SCLogError(SC_ERR_NOT_SUPPORTED, "Invalid command value");
176  goto error;
177  }
178 
179 
180  return ftpcommandd;
181 
182 error:
183  if (ftpcommandd)
184  SCFree(ftpcommandd);
185  return NULL;
186 }
187 
188 /**
189  * \brief parse the options from the 'ftpcommand' keyword in the rule into
190  * the Signature data structure.
191  *
192  * \param de_ctx pointer to the Detection Engine Context
193  * \param s pointer to the Current Signature
194  * \param ftpcommandstr pointer to the user provided ftpcommand options
195  *
196  * \retval 0 on Success
197  * \retval -1 on Failure
198  */
199 static int DetectFtpdataSetup(DetectEngineCtx *de_ctx, Signature *s, const char *ftpcommandstr)
200 {
201  DetectFtpdataData *ftpcommandd = NULL;
202  SigMatch *sm = NULL;
203 
205  return -1;
206 
207  ftpcommandd = DetectFtpdataParse(ftpcommandstr);
208  if (ftpcommandd == NULL)
209  goto error;
210 
211  sm = SigMatchAlloc();
212  if (sm == NULL)
213  goto error;
214 
215  sm->type = DETECT_FTPDATA;
216  sm->ctx = (void *)ftpcommandd;
217 
218  SigMatchAppendSMToList(s, sm, g_ftpdata_buffer_id);
219 
220  return 0;
221 
222 error:
223  if (ftpcommandd != NULL)
224  DetectFtpdataFree(ftpcommandd);
225  if (sm != NULL)
226  SCFree(sm);
227  return -1;
228 }
229 
230 /**
231  * \brief this function will free memory associated with DetectFtpdataData
232  *
233  * \param ptr pointer to DetectFtpdataData
234  */
235 static void DetectFtpdataFree(void *ptr) {
236  DetectFtpdataData *ftpcommandd = (DetectFtpdataData *)ptr;
237 
238  /* do more specific cleanup here, if needed */
239 
240  SCFree(ftpcommandd);
241 }
242 
243 #if UNITTESTS
244 
245 static int DetectFtpdataParseTest01(void)
246 {
247  DetectFtpdataData *ftpcommandd = DetectFtpdataParse("stor");
248  FAIL_IF_NULL(ftpcommandd);
249  FAIL_IF(!(ftpcommandd->command == FTP_COMMAND_STOR));
250  DetectFtpdataFree(ftpcommandd);
251  PASS;
252 }
253 
254 static int DetectFtpdataSignatureTest01(void)
255 {
257  FAIL_IF_NULL(de_ctx);
258 
259  Signature *sig = DetectEngineAppendSig(de_ctx, "alert ip any any -> any any (ftpdata_command:stor; sid:1; rev:1;)");
260  FAIL_IF_NULL(sig);
261 
262  DetectEngineCtxFree(de_ctx);
263  PASS;
264 }
265 
266 #endif /* UNITTESTS */
267 
268 /**
269  * \brief this function registers unit tests for DetectFtpdata
270  */
271 void DetectFtpdataRegisterTests(void) {
272 #ifdef UNITTESTS
273  UtRegisterTest("DetectFtpdataParseTest01", DetectFtpdataParseTest01);
274  UtRegisterTest("DetectFtpdataSignatureTest01",
275  DetectFtpdataSignatureTest01);
276 #endif /* UNITTESTS */
277 }
Signature * DetectEngineAppendSig(DetectEngineCtx *de_ctx, const char *sigstr)
Parse and append a Signature into the Detection Engine Context signature list.
SigTableElmt sigmatch_table[DETECT_TBLSIZE]
Definition: detect.h:1403
uint16_t flags
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1146
int(* AppLayerTxMatch)(ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t flags, void *alstate, void *txv, const Signature *, const SigMatchCtx *)
Definition: detect.h:1132
#define SCLogDebug(...)
Definition: util-debug.h:335
int DetectSignatureSetAppProto(Signature *s, AppProto alproto)
void DetectFtpdataRegister(void)
Registration function for ftpcommand: keyword.
#define PASS
Pass the test.
#define unlikely(expr)
Definition: util-optimize.h:35
#define FAIL_IF(expr)
Fail a test if expression evaluates to false.
Definition: util-unittest.h:71
Data needed for Match()
Definition: detect.h:333
const char * name
Definition: detect.h:1160
int DetectEngineInspectGenericList(ThreadVars *tv, const DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchData *smd, Flow *f, const uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
Do the content inspection & validation for a signature.
Signature container.
Definition: detect.h:492
Used to start a pointer to SigMatch context Should never be dereferenced without casting to something...
Definition: detect.h:319
main detection engine ctx
Definition: detect.h:720
int DetectBufferTypeGetByName(const char *name)
#define SIG_FLAG_TOCLIENT
Definition: detect.h:244
Data structures and function prototypes for keeping state for the detection engine.
void(* Free)(void *)
Definition: detect.h:1151
FtpRequestCommand command
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:294
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
void DetectSetupParseRegexes(const char *parse_str, pcre **parse_regex, pcre_extra **parse_regex_study)
#define SIG_FLAG_TOSERVER
Definition: detect.h:243
#define STREAM_TOCLIENT
Definition: stream.h:32
#define PARSE_REGEX
Regex for parsing our keyword options.
uint8_t type
Definition: detect.h:325
const char * desc
Definition: detect.h:1162
void SigMatchAppendSMToList(Signature *s, SigMatch *new, int list)
Append a SigMatch to the list type.
Definition: detect-parse.c:282
SigMatchCtx * ctx
Definition: detect.h:327
#define SCMalloc(a)
Definition: util-mem.h:174
FtpRequestCommand command
#define SCFree(a)
Definition: util-mem.h:236
PoolThreadReserved res
uint16_t tx_id
const char * url
Definition: detect.h:1163
#define STREAM_TOSERVER
Definition: stream.h:31
SCMutex m
Definition: flow-hash.h:105
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
Definition: util-unittest.h:89
#define DOC_URL
Definition: suricata.h:86
SigMatch * SigMatchAlloc(void)
Definition: detect-parse.c:226
Per thread variable structure.
Definition: threadvars.h:57
#define DOC_VERSION
Definition: suricata.h:91
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
Flow data structure.
Definition: flow.h:327
void DetectAppLayerInspectEngineRegister(const char *name, AppProto alproto, uint32_t dir, int progress, InspectEngineFuncPtr Callback)
register inspect engine at start up time
void(* RegisterTests)(void)
Definition: detect.h:1152
a single match condition for a signature
Definition: detect.h:324
#define MAX_SUBSTRINGS
DetectEngineCtx * DetectEngineCtxInit(void)