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