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