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(DetectEngineCtx *, 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 = "/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(de_ctx, 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(DetectEngineCtx *de_ctx, 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(NULL, cipserviced);
259  PASS;
260 }
261 
262 /**
263  * \test Test CIP Service signature
264  */
265 static int DetectCipServiceSignatureTest01 (void)
266 {
269  Signature *sig = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (cip_service:1; sid:1; rev:1;)");
270  FAIL_IF_NULL(sig);
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(DetectEngineCtx *, 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 = "/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(de_ctx, 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(DetectEngineCtx *de_ctx, 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(NULL, enipcmdd);
433  PASS;
434 }
435 
436 /**
437  * \test ENIP Command signature test
438  */
439 static int DetectEnipCommandSignatureTest01 (void)
440 {
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 
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 }
DETECT_CIPSERVICE
@ DETECT_CIPSERVICE
Definition: detect-engine-register.h:212
util-byte.h
SigTableElmt_::url
const char * url
Definition: detect.h:1212
DetectSignatureSetAppProto
int DetectSignatureSetAppProto(Signature *s, AppProto alproto)
Definition: detect-parse.c:1468
detect-engine.h
DetectCipServiceData_::matchattribute
uint8_t matchattribute
Definition: detect-cipservice.h:43
FAIL_IF_NULL
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
Definition: util-unittest.h:89
SigTableElmt_::desc
const char * desc
Definition: detect.h:1211
SigTableElmt_::Free
void(* Free)(DetectEngineCtx *, void *)
Definition: detect.h:1200
SigTableElmt_::name
const char * name
Definition: detect.h:1209
DetectCipServiceData_::tokens
uint8_t tokens
Definition: detect-cipservice.h:44
ALPROTO_ENIP
@ ALPROTO_ENIP
Definition: app-layer-protos.h:43
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:298
MAX_CIP_CLASS
#define MAX_CIP_CLASS
Definition: app-layer-enip-common.h:63
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:766
SC_ERR_INVALID_SIGNATURE
@ SC_ERR_INVALID_SIGNATURE
Definition: util-error.h:69
DetectEngineCtxFree
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
Definition: detect-engine.c:2089
SIG_FLAG_TOCLIENT
#define SIG_FLAG_TOCLIENT
Definition: detect.h:237
SigTableElmt_::Setup
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1195
DetectEngineInspectCIP
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.
Definition: detect-engine-enip.c:220
util-unittest.h
FAIL_IF_NOT
#define FAIL_IF_NOT(expr)
Fail a test if expression to true.
Definition: util-unittest.h:82
DetectCipServiceData_::cipservice
uint8_t cipservice
Definition: detect-cipservice.h:40
DetectBufferTypeGetByName
int DetectBufferTypeGetByName(const char *name)
Definition: detect-engine.c:876
detect-engine-enip.h
MAX_CIP_ATTRIBUTE
#define MAX_CIP_ATTRIBUTE
Definition: app-layer-enip-common.h:64
DetectEnipCommandData_::enipcommand
uint16_t enipcommand
Definition: detect-cipservice.h:52
SIG_FLAG_TOSERVER
#define SIG_FLAG_TOSERVER
Definition: detect.h:236
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
MAX_CIP_SERVICE
#define MAX_CIP_SERVICE
Definition: app-layer-enip-common.h:62
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:17
SCEnter
#define SCEnter(...)
Definition: util-debug.h:300
SigMatch_::ctx
SigMatchCtx * ctx
Definition: detect.h:322
DETECT_ENIPCOMMAND
@ DETECT_ENIPCOMMAND
Definition: detect-engine-register.h:213
SCReturn
#define SCReturn
Definition: util-debug.h:302
DetectEnipCommandData_
Definition: detect-cipservice.h:51
SCReturnPtr
#define SCReturnPtr(x, type)
Definition: util-debug.h:316
SigTableElmt_::Match
int(* Match)(DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)
Definition: detect.h:1178
SigMatchAlloc
SigMatch * SigMatchAlloc(void)
Definition: detect-parse.c:235
SigMatch_::type
uint8_t type
Definition: detect.h:320
DetectEngineAppendSig
Signature * DetectEngineAppendSig(DetectEngineCtx *de_ctx, const char *sigstr)
Parse and append a Signature into the Detection Engine Context signature list.
Definition: detect-parse.c:2326
DetectEngineInspectENIP
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.
Definition: detect-engine-enip.c:260
FAIL_IF
#define FAIL_IF(expr)
Fail a test if expression evaluates to false.
Definition: util-unittest.h:71
suricata-common.h
DetectCipServiceRegister
void DetectCipServiceRegister(void)
Registration function for cip_service: keyword.
Definition: detect-cipservice.c:50
sigmatch_table
SigTableElmt sigmatch_table[DETECT_TBLSIZE]
Definition: detect-parse.c:73
SCLogError
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:257
MAX_ENIP_CMD
#define MAX_ENIP_CMD
Definition: app-layer-enip-common.h:32
SCStrdup
#define SCStrdup(s)
Definition: util-mem.h:56
DetectEnipCommandRegister
void DetectEnipCommandRegister(void)
Registration function for enip_command: keyword.
Definition: detect-cipservice.c:305
SCMalloc
#define SCMalloc(sz)
Definition: util-mem.h:47
SCFree
#define SCFree(p)
Definition: util-mem.h:61
detect-parse.h
Signature_
Signature container.
Definition: detect.h:527
SigMatch_
a single match condition for a signature
Definition: detect.h:319
DetectEngineCtxInit
DetectEngineCtx * DetectEngineCtxInit(void)
Definition: detect-engine.c:2044
DetectCipServiceData_::cipattribute
uint16_t cipattribute
Definition: detect-cipservice.h:42
DetectCipServiceData_::cipclass
uint16_t cipclass
Definition: detect-cipservice.h:41
DetectCipServiceData_
Definition: detect-cipservice.h:39
DetectAppLayerInspectEngineRegister
void DetectAppLayerInspectEngineRegister(const char *name, AppProto alproto, uint32_t dir, int progress, InspectEngineFuncPtr Callback)
register inspect engine at start up time
Definition: detect-engine.c:170
SCReturnInt
#define SCReturnInt(x)
Definition: util-debug.h:304
detect-cipservice.h
SigMatchAppendSMToList
void SigMatchAppendSMToList(Signature *s, SigMatch *new, int list)
Append a SigMatch to the list type.
Definition: detect-parse.c:349
SigTableElmt_::RegisterTests
void(* RegisterTests)(void)
Definition: detect.h:1201