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