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 
213  return -1;
214 
215  cipserviced = DetectCipServiceParse(rulestr);
216  if (cipserviced == NULL)
217  goto error;
218 
220  g_cip_buffer_id) == NULL) {
221  goto error;
222  }
223  SCReturnInt(0);
224 
225 error:
226  if (cipserviced != NULL)
227  DetectCipServiceFree(de_ctx, cipserviced);
228  SCReturnInt(-1);
229 }
230 
231 /**
232  * \brief this function will free memory associated with DetectCipServiceData
233  *
234  * \param ptr pointer to DetectCipServiceData
235  */
236 static void DetectCipServiceFree(DetectEngineCtx *de_ctx, void *ptr)
237 {
238  DetectCipServiceData *cipserviced = (DetectCipServiceData *) ptr;
239  SCFree(cipserviced);
240 }
241 
242 #ifdef UNITTESTS
243 
244 /**
245  * \test Test CIP Command parameter parsing
246  */
247 static int DetectCipServiceParseTest01 (void)
248 {
249  DetectCipServiceData *cipserviced = NULL;
250  cipserviced = DetectCipServiceParse("7");
251  FAIL_IF_NULL(cipserviced);
252  FAIL_IF(cipserviced->cipservice != 7);
253  DetectCipServiceFree(NULL, cipserviced);
254  PASS;
255 }
256 
257 /**
258  * \test Test CIP Service signature
259  */
260 static int DetectCipServiceSignatureTest01 (void)
261 {
264  Signature *sig = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (cip_service:1; sid:1; rev:1;)");
265  FAIL_IF_NULL(sig);
267  PASS;
268 }
269 
270 /**
271  * \brief this function registers unit tests for DetectCipService
272  */
273 static void DetectCipServiceRegisterTests(void)
274 {
275  UtRegisterTest("DetectCipServiceParseTest01",
276  DetectCipServiceParseTest01);
277  UtRegisterTest("DetectCipServiceSignatureTest01",
278  DetectCipServiceSignatureTest01);
279 }
280 #endif /* UNITTESTS */
281 
282 /*
283  * ENIP COMMAND CODE
284  */
285 
286 /**
287  * \brief ENIP Command Detect Prototypes
288  */
289 static int DetectEnipCommandSetup(DetectEngineCtx *, Signature *, const char *);
290 static void DetectEnipCommandFree(DetectEngineCtx *, void *);
291 #ifdef UNITTESTS
292 static void DetectEnipCommandRegisterTests(void);
293 #endif
294 static int g_enip_buffer_id = 0;
295 
296 /**
297  * \brief Registration function for enip_command: keyword
298  */
300 {
301  sigmatch_table[DETECT_ENIPCOMMAND].name = "enip_command"; //rule keyword
303  = "rules for detecting EtherNet/IP command";
304  sigmatch_table[DETECT_ENIPCOMMAND].url = "/rules/enip-keyword.html#enip-cip-keywords";
306  sigmatch_table[DETECT_ENIPCOMMAND].Setup = DetectEnipCommandSetup;
307  sigmatch_table[DETECT_ENIPCOMMAND].Free = DetectEnipCommandFree;
308 #ifdef UNITTESTS
310  = DetectEnipCommandRegisterTests;
311 #endif
316 
317  g_enip_buffer_id = DetectBufferTypeGetByName("enip");
318 }
319 
320 /**
321  * \brief This function is used to parse cip_service options passed via enip_command: keyword
322  *
323  * \param rulestr Pointer to the user provided rulestr options
324  * Takes single numeric value
325  *
326  * \retval enipcmdd pointer to DetectCipServiceData on success
327  * \retval NULL on failure
328  */
329 static DetectEnipCommandData *DetectEnipCommandParse(const char *rulestr)
330 {
331  DetectEnipCommandData *enipcmdd = NULL;
332 
333  enipcmdd = SCMalloc(sizeof(DetectEnipCommandData));
334  if (unlikely(enipcmdd == NULL))
335  goto error;
336 
337  if (!(isdigit((int) *rulestr))) {
338  SCLogError("invalid ENIP command %s", rulestr);
339  goto error;
340  }
341 
342  uint16_t cmd;
343  if (StringParseUint16(&cmd, 10, 0, rulestr) < 0) {
344  SCLogError("invalid ENIP command"
345  ": \"%s\"",
346  rulestr);
347  goto error;
348  }
349 
350  enipcmdd->enipcommand = cmd;
351 
352  return enipcmdd;
353 
354 error:
355  if (enipcmdd)
356  SCFree(enipcmdd);
357  return NULL;
358 }
359 
360 /**
361  * \brief this function is used by enipcmdd to parse enip_command data into the current signature
362  *
363  * \param de_ctx pointer to the Detection Engine Context
364  * \param s pointer to the Current Signature
365  * \param rulestr pointer to the user provided enip command options
366  *
367  * \retval 0 on Success
368  * \retval -1 on Failure
369  */
370 static int DetectEnipCommandSetup(DetectEngineCtx *de_ctx, Signature *s,
371  const char *rulestr)
372 {
373  DetectEnipCommandData *enipcmdd = NULL;
374 
376  return -1;
377 
378  enipcmdd = DetectEnipCommandParse(rulestr);
379  if (enipcmdd == NULL)
380  goto error;
381 
383  de_ctx, s, DETECT_ENIPCOMMAND, (SigMatchCtx *)enipcmdd, g_enip_buffer_id) == NULL) {
384  goto error;
385  }
386  SCReturnInt(0);
387 
388 error:
389  if (enipcmdd != NULL)
390  DetectEnipCommandFree(de_ctx, enipcmdd);
391  SCReturnInt(-1);
392 }
393 
394 /**
395  * \brief this function will free memory associated with DetectEnipCommandData
396  *
397  * \param ptr pointer to DetectEnipCommandData
398  */
399 static void DetectEnipCommandFree(DetectEngineCtx *de_ctx, void *ptr)
400 {
401  DetectEnipCommandData *enipcmdd = (DetectEnipCommandData *) ptr;
402  SCFree(enipcmdd);
403 }
404 
405 #ifdef UNITTESTS
406 
407 /**
408  * \test ENIP parameter test
409  */
410 
411 static int DetectEnipCommandParseTest01 (void)
412 {
413  DetectEnipCommandData *enipcmdd = NULL;
414 
415  enipcmdd = DetectEnipCommandParse("1");
416  FAIL_IF_NULL(enipcmdd);
417  FAIL_IF_NOT(enipcmdd->enipcommand == 1);
418 
419  DetectEnipCommandFree(NULL, enipcmdd);
420  PASS;
421 }
422 
423 /**
424  * \test ENIP Command signature test
425  */
426 static int DetectEnipCommandSignatureTest01 (void)
427 {
430 
431  Signature *sig = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (enip_command:1; sid:1; rev:1;)");
432  FAIL_IF_NULL(sig);
433 
435  PASS;
436 }
437 
438 /**
439  * \brief this function registers unit tests for DetectEnipCommand
440  */
441 static void DetectEnipCommandRegisterTests(void)
442 {
443  UtRegisterTest("DetectEnipCommandParseTest01",
444  DetectEnipCommandParseTest01);
445  UtRegisterTest("DetectEnipCommandSignatureTest01",
446  DetectEnipCommandSignatureTest01);
447 }
448 #endif /* UNITTESTS */
DETECT_CIPSERVICE
@ DETECT_CIPSERVICE
Definition: detect-engine-register.h:253
util-byte.h
SigTableElmt_::url
const char * url
Definition: detect.h:1299
DetectSignatureSetAppProto
int DetectSignatureSetAppProto(Signature *s, AppProto alproto)
Definition: detect-parse.c:1753
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
SigTableElmt_::desc
const char * desc
Definition: detect.h:1298
SigTableElmt_::Free
void(* Free)(DetectEngineCtx *, void *)
Definition: detect.h:1286
SigTableElmt_::name
const char * name
Definition: detect.h:1296
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:839
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:2533
DetectEngineAppendSig
Signature * DetectEngineAppendSig(DetectEngineCtx *, const char *)
Parse and append a Signature into the Detection Engine Context signature list.
Definition: detect-parse.c:2620
SIG_FLAG_TOCLIENT
#define SIG_FLAG_TOCLIENT
Definition: detect.h:267
SigTableElmt_::Setup
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1281
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:1072
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:266
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
DETECT_ENIPCOMMAND
@ DETECT_ENIPCOMMAND
Definition: detect-engine-register.h:254
SCReturn
#define SCReturn
Definition: util-debug.h:273
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:1264
SigMatchCtx_
Used to start a pointer to SigMatch context Should never be dereferenced without casting to something...
Definition: detect.h:345
FAIL_IF
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
Definition: util-unittest.h:71
suricata-common.h
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:127
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:299
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:596
app-layer-enip-common.h
DetectEngineCtxInit
DetectEngineCtx * DetectEngineCtxInit(void)
Definition: detect-engine.c:2494
DetectCipServiceData_::cipattribute
uint16_t cipattribute
Definition: detect-cipservice.h:34
DetectAppLayerInspectEngineRegister
void DetectAppLayerInspectEngineRegister(const char *name, AppProto alproto, uint32_t dir, int progress, InspectEngineFuncPtr Callback, InspectionBufferGetDataPtr GetData)
register inspect engine at start up time
Definition: detect-engine.c:169
SigMatchAppendSMToList
SigMatch * SigMatchAppendSMToList(DetectEngineCtx *de_ctx, Signature *s, uint16_t type, SigMatchCtx *ctx, const int list)
Append a SigMatch to the list type.
Definition: detect-parse.c:447
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:1288