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