suricata
detect-cipservice.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 Kevin Wong <kwong@solananetworks.com>
22  *
23  * Set up ENIP Commnad and CIP Service rule parsing and entry point for matching
24  */
25 
26 #include "suricata-common.h"
27 #include "util-unittest.h"
28 #include "detect-parse.h"
29 #include "detect-engine.h"
30 #include "util-byte.h"
31 
32 #include "detect-cipservice.h"
33 #include "detect-engine-enip.h"
34 
35 /*
36  * CIP SERVICE CODE
37  */
38 
39 /**
40  * \brief CIP Service Detect Prototypes
41  */
42 static int DetectCipServiceSetup(DetectEngineCtx *, Signature *, const char *);
43 static void DetectCipServiceFree(void *);
44 static void DetectCipServiceRegisterTests(void);
45 static int g_cip_buffer_id = 0;
46 
47 /**
48  * \brief Registration function for cip_service: keyword
49  */
51 {
52  SCEnter();
53  sigmatch_table[DETECT_CIPSERVICE].name = "cip_service"; //rule keyword
54  sigmatch_table[DETECT_CIPSERVICE].desc = "match on CIP Service";
55  sigmatch_table[DETECT_CIPSERVICE].url = DOC_URL DOC_VERSION "/rules/enip-keyword.html#enip-cip-keywords";
57  sigmatch_table[DETECT_CIPSERVICE].Setup = DetectCipServiceSetup;
58  sigmatch_table[DETECT_CIPSERVICE].Free = DetectCipServiceFree;
60  = DetectCipServiceRegisterTests;
61 
68 
69  g_cip_buffer_id = DetectBufferTypeGetByName("cip");
70 
71  SCReturn;
72 }
73 
74 /**
75  * \brief This function is used to parse cip_service options passed via cip_service: keyword
76  *
77  * \param rulestr Pointer to the user provided rulestr options
78  * Takes comma seperated string with numeric tokens. Only first 3 are used
79  *
80  * \retval cipserviced pointer to DetectCipServiceData on success
81  * \retval NULL on failure
82  */
83 static DetectCipServiceData *DetectCipServiceParse(const char *rulestrc)
84 {
85  const char delims[] = ",";
86  DetectCipServiceData *cipserviced = NULL;
87 
88  //SCLogDebug("DetectCipServiceParse - rule string %s", rulestr);
89 
90  /* strtok_r modifies the string so work with a copy */
91  char *rulestr = SCStrdup(rulestrc);
92  if (unlikely(rulestr == NULL))
93  goto error;
94 
95  cipserviced = SCMalloc(sizeof(DetectCipServiceData));
96  if (unlikely(cipserviced == NULL))
97  goto error;
98 
99  cipserviced->cipservice = 0;
100  cipserviced->cipclass = 0;
101  cipserviced->matchattribute = 1;
102  cipserviced->cipattribute = 0;
103 
104  char* token;
105  char *save;
106  int var;
107  int input[3] = { 0, 0, 0 };
108  int i = 0;
109 
110  token = strtok_r(rulestr, delims, &save);
111  while (token != NULL)
112  {
113  if (i > 2) //for now only need 3 parameters
114  {
115  SCLogError(SC_ERR_INVALID_SIGNATURE, "too many parameters");
116  goto error;
117  }
118 
119  if (i < 2) //if on service or class
120  {
121  if (!isdigit((int) *token))
122  {
123  SCLogError(SC_ERR_INVALID_SIGNATURE, "parameter error %s", token);
124  goto error;
125  }
126  } else //if on attribute
127  {
128 
129  if (token[0] == '!')
130  {
131  cipserviced->matchattribute = 0;
132  token++;
133  }
134 
135  if (!isdigit((int) *token))
136  {
137  SCLogError(SC_ERR_INVALID_SIGNATURE, "attribute error %s", token);
138  goto error;
139  }
140 
141  }
142 
143  unsigned long num = atol(token);
144  if ((num > MAX_CIP_SERVICE) && (i == 0))//if service greater than 7 bit
145  {
146  SCLogError(SC_ERR_INVALID_SIGNATURE, "invalid CIP service %lu", num);
147  goto error;
148  } else if ((num > MAX_CIP_CLASS) && (i == 1))//if service greater than 16 bit
149  {
150  SCLogError(SC_ERR_INVALID_SIGNATURE, "invalid CIP class %lu", num);
151  goto error;
152  } else if ((num > MAX_CIP_ATTRIBUTE) && (i == 2))//if service greater than 16 bit
153  {
154  SCLogError(SC_ERR_INVALID_SIGNATURE, "invalid CIP attribute %lu", num);
155  goto error;
156  }
157 
158  sscanf(token, "%d", &var);
159  input[i++] = var;
160 
161  token = strtok_r(NULL, delims, &save);
162  }
163 
164  if (i == 0) {
165  SCLogError(SC_ERR_INVALID_SIGNATURE, "no tokens found");
166  goto error;
167  }
168 
169  cipserviced->cipservice = input[0];
170  cipserviced->cipclass = input[1];
171  cipserviced->cipattribute = input[2];
172  cipserviced->tokens = i;
173 
174  SCLogDebug("DetectCipServiceParse - tokens %d", cipserviced->tokens);
175  SCLogDebug("DetectCipServiceParse - service %d", cipserviced->cipservice);
176  SCLogDebug("DetectCipServiceParse - class %d", cipserviced->cipclass);
177  SCLogDebug("DetectCipServiceParse - match attribute %d",
178  cipserviced->matchattribute);
179  SCLogDebug("DetectCipServiceParse - attribute %d",
180  cipserviced->cipattribute);
181 
182  SCFree(rulestr);
183  SCReturnPtr(cipserviced, "DetectENIPFunction");
184 
185 error:
186  if (cipserviced)
187  SCFree(cipserviced);
188  if (rulestr)
189  SCFree(rulestr);
190  SCReturnPtr(NULL, "DetectENIP");
191 }
192 
193 /**
194  * \brief this function is used to a cipserviced the parsed cip_service data into the current signature
195  *
196  * \param de_ctx pointer to the Detection Engine Context
197  * \param s pointer to the Current Signature
198  * \param rulestr pointer to the user provided cip_service options
199  *
200  * \retval 0 on Success
201  * \retval -1 on Failure
202  */
203 static int DetectCipServiceSetup(DetectEngineCtx *de_ctx, Signature *s,
204  const char *rulestr)
205 {
206  SCEnter();
207 
208  DetectCipServiceData *cipserviced = NULL;
209  SigMatch *sm = NULL;
210 
212  return -1;
213 
214  cipserviced = DetectCipServiceParse(rulestr);
215  if (cipserviced == NULL)
216  goto error;
217 
218  sm = SigMatchAlloc();
219  if (sm == NULL)
220  goto error;
221 
222  sm->type = DETECT_CIPSERVICE;
223  sm->ctx = (void *) cipserviced;
224 
225  SigMatchAppendSMToList(s, sm, g_cip_buffer_id);
226  SCReturnInt(0);
227 
228 error:
229  if (cipserviced != NULL)
230  DetectCipServiceFree(cipserviced);
231  if (sm != NULL)
232  SCFree(sm);
233  SCReturnInt(-1);
234 }
235 
236 /**
237  * \brief this function will free memory associated with DetectCipServiceData
238  *
239  * \param ptr pointer to DetectCipServiceData
240  */
241 static void DetectCipServiceFree(void *ptr)
242 {
243  DetectCipServiceData *cipserviced = (DetectCipServiceData *) ptr;
244  SCFree(cipserviced);
245 }
246 
247 #ifdef UNITTESTS
248 
249 /**
250  * \test Test CIP Command parameter parsing
251  */
252 static int DetectCipServiceParseTest01 (void)
253 {
254  DetectCipServiceData *cipserviced = NULL;
255  cipserviced = DetectCipServiceParse("7");
256  FAIL_IF_NULL(cipserviced);
257  FAIL_IF(cipserviced->cipservice != 7);
258  DetectCipServiceFree(cipserviced);
259  PASS;
260 }
261 
262 /**
263  * \test Test CIP Service signature
264  */
265 static int DetectCipServiceSignatureTest01 (void)
266 {
268  FAIL_IF_NULL(de_ctx);
269  Signature *sig = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (cip_service:1; sid:1; rev:1;)");
270  FAIL_IF_NULL(sig);
271  DetectEngineCtxFree(de_ctx);
272  PASS;
273 }
274 
275 #endif /* UNITTESTS */
276 
277 /**
278  * \brief this function registers unit tests for DetectCipService
279  */
280 static void DetectCipServiceRegisterTests(void)
281 {
282 #ifdef UNITTESTS
283  UtRegisterTest("DetectCipServiceParseTest01",
284  DetectCipServiceParseTest01);
285  UtRegisterTest("DetectCipServiceSignatureTest01",
286  DetectCipServiceSignatureTest01);
287 #endif /* UNITTESTS */
288 }
289 
290 /*
291  * ENIP COMMAND CODE
292  */
293 
294 /**
295  * \brief ENIP Commond Detect Prototypes
296  */
297 static int DetectEnipCommandSetup(DetectEngineCtx *, Signature *, const char *);
298 static void DetectEnipCommandFree(void *);
299 static void DetectEnipCommandRegisterTests(void);
300 static int g_enip_buffer_id = 0;
301 
302 /**
303  * \brief Registration function for enip_command: keyword
304  */
306 {
307  sigmatch_table[DETECT_ENIPCOMMAND].name = "enip_command"; //rule keyword
309  = "rules for detecting EtherNet/IP command";
310  sigmatch_table[DETECT_ENIPCOMMAND].url = DOC_URL DOC_VERSION "/rules/enip-keyword.html#enip-cip-keywords";
312  sigmatch_table[DETECT_ENIPCOMMAND].Setup = DetectEnipCommandSetup;
313  sigmatch_table[DETECT_ENIPCOMMAND].Free = DetectEnipCommandFree;
315  = DetectEnipCommandRegisterTests;
316 
323 
324  g_enip_buffer_id = DetectBufferTypeGetByName("enip");
325 }
326 
327 /**
328  * \brief This function is used to parse cip_service options passed via enip_command: keyword
329  *
330  * \param rulestr Pointer to the user provided rulestr options
331  * Takes single single numeric value
332  *
333  * \retval enipcmdd pointer to DetectCipServiceData on success
334  * \retval NULL on failure
335  */
336 static DetectEnipCommandData *DetectEnipCommandParse(const char *rulestr)
337 {
338  DetectEnipCommandData *enipcmdd = NULL;
339 
340  enipcmdd = SCMalloc(sizeof(DetectEnipCommandData));
341  if (unlikely(enipcmdd == NULL))
342  goto error;
343 
344  if (!(isdigit((int) *rulestr))) {
345  SCLogError(SC_ERR_INVALID_SIGNATURE, "invalid ENIP command %s", rulestr);
346  goto error;
347  }
348 
349  unsigned long cmd = atol(rulestr);
350  if (cmd > MAX_ENIP_CMD) //if command greater than 16 bit
351  {
352  SCLogError(SC_ERR_INVALID_SIGNATURE, "invalid ENIP command %lu", cmd);
353  goto error;
354  }
355 
356  enipcmdd->enipcommand = (uint16_t) atoi(rulestr);
357 
358  return enipcmdd;
359 
360 error:
361  if (enipcmdd)
362  SCFree(enipcmdd);
363  return NULL;
364 }
365 
366 /**
367  * \brief this function is used by enipcmdd to parse enip_command data into the current signature
368  *
369  * \param de_ctx pointer to the Detection Engine Context
370  * \param s pointer to the Current Signature
371  * \param rulestr pointer to the user provided enip command options
372  *
373  * \retval 0 on Success
374  * \retval -1 on Failure
375  */
376 static int DetectEnipCommandSetup(DetectEngineCtx *de_ctx, Signature *s,
377  const char *rulestr)
378 {
379  DetectEnipCommandData *enipcmdd = NULL;
380  SigMatch *sm = NULL;
381 
383  return -1;
384 
385  enipcmdd = DetectEnipCommandParse(rulestr);
386  if (enipcmdd == NULL)
387  goto error;
388 
389  sm = SigMatchAlloc();
390  if (sm == NULL)
391  goto error;
392 
393  sm->type = DETECT_ENIPCOMMAND;
394  sm->ctx = (void *) enipcmdd;
395 
396  SigMatchAppendSMToList(s, sm, g_enip_buffer_id);
397  SCReturnInt(0);
398 
399 error:
400  if (enipcmdd != NULL)
401  DetectEnipCommandFree(enipcmdd);
402  if (sm != NULL)
403  SCFree(sm);
404  SCReturnInt(-1);
405 }
406 
407 /**
408  * \brief this function will free memory associated with DetectEnipCommandData
409  *
410  * \param ptr pointer to DetectEnipCommandData
411  */
412 static void DetectEnipCommandFree(void *ptr)
413 {
414  DetectEnipCommandData *enipcmdd = (DetectEnipCommandData *) ptr;
415  SCFree(enipcmdd);
416 }
417 
418 #ifdef UNITTESTS
419 
420 /**
421  * \test ENIP parameter test
422  */
423 
424 static int DetectEnipCommandParseTest01 (void)
425 {
426  DetectEnipCommandData *enipcmdd = NULL;
427 
428  enipcmdd = DetectEnipCommandParse("1");
429  FAIL_IF_NULL(enipcmdd);
430  FAIL_IF_NOT(enipcmdd->enipcommand == 1);
431 
432  DetectEnipCommandFree(enipcmdd);
433  PASS;
434 }
435 
436 /**
437  * \test ENIP Command signature test
438  */
439 static int DetectEnipCommandSignatureTest01 (void)
440 {
442  FAIL_IF_NULL(de_ctx);
443 
444  Signature *sig = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (enip_command:1; sid:1; rev:1;)");
445  FAIL_IF_NULL(sig);
446 
447  DetectEngineCtxFree(de_ctx);
448  PASS;
449 }
450 
451 #endif /* UNITTESTS */
452 
453 /**
454  * \brief this function registers unit tests for DetectEnipCommand
455  */
456 static void DetectEnipCommandRegisterTests(void)
457 {
458 #ifdef UNITTESTS
459  UtRegisterTest("DetectEnipCommandParseTest01",
460  DetectEnipCommandParseTest01);
461  UtRegisterTest("DetectEnipCommandSignatureTest01",
462  DetectEnipCommandSignatureTest01);
463 #endif /* UNITTESTS */
464 }
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:1448
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1186
#define SCLogDebug(...)
Definition: util-debug.h:335
int DetectSignatureSetAppProto(Signature *s, AppProto alproto)
#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
const char * name
Definition: detect.h:1200
Signature container.
Definition: detect.h:522
int DetectEngineInspectENIP(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchData *smd, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
Do the content inspection & validation for a signature.
main detection engine ctx
Definition: detect.h:761
int DetectBufferTypeGetByName(const char *name)
#define MAX_CIP_SERVICE
#define SIG_FLAG_TOCLIENT
Definition: detect.h:237
void(* Free)(void *)
Definition: detect.h:1191
#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.
#define SIG_FLAG_TOSERVER
Definition: detect.h:236
#define SCEnter(...)
Definition: util-debug.h:337
int(* Match)(DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)
Definition: detect.h:1170
#define MAX_CIP_ATTRIBUTE
uint8_t type
Definition: detect.h:319
#define SCReturnInt(x)
Definition: util-debug.h:341
#define MAX_ENIP_CMD
const char * desc
Definition: detect.h:1202
void SigMatchAppendSMToList(Signature *s, SigMatch *new, int list)
Append a SigMatch to the list type.
Definition: detect-parse.c:346
#define MAX_CIP_CLASS
SigMatchCtx * ctx
Definition: detect.h:321
#define SCMalloc(a)
Definition: util-mem.h:222
#define SCFree(a)
Definition: util-mem.h:322
void DetectEnipCommandRegister(void)
Registration function for enip_command: keyword.
const char * url
Definition: detect.h:1203
#define SCReturnPtr(x, type)
Definition: util-debug.h:353
#define SCStrdup(a)
Definition: util-mem.h:268
#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
int DetectEngineInspectCIP(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchData *smd, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
Do the content inspection & validation for a signature.
SigMatch * SigMatchAlloc(void)
Definition: detect-parse.c:232
#define SCReturn
Definition: util-debug.h:339
#define DOC_VERSION
Definition: suricata.h:91
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
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:1192
a single match condition for a signature
Definition: detect.h:318
#define FAIL_IF_NOT(expr)
Fail a test if expression to true.
Definition: util-unittest.h:82
void DetectCipServiceRegister(void)
Registration function for cip_service: keyword.
DetectEngineCtx * DetectEngineCtxInit(void)