suricata
detect-classtype.c
Go to the documentation of this file.
1/* Copyright (C) 2007-2020 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 Anoop Saldanha <anoopsaldanha@gmail.com>
22 * \author Victor Julien <victor@inliniac.net>
23 *
24 * Implements classtype keyword.
25 */
26
27#include "suricata-common.h"
28#include "decode.h"
29
30#include "detect.h"
31#include "detect-parse.h"
32#include "detect-engine.h"
33#include "detect-classtype.h"
35#include "util-error.h"
36#include "util-debug.h"
37#include "util-unittest.h"
38
39#define PARSE_REGEX "^\\s*([a-zA-Z][a-zA-Z0-9-_]*)\\s*$"
40
41static DetectParseRegex parse_regex;
42
43static int DetectClasstypeSetup(DetectEngineCtx *, Signature *, const char *);
44#ifdef UNITTESTS
45static void DetectClasstypeRegisterTests(void);
46#endif
47
48/**
49 * \brief Registers the handler functions for the "Classtype" keyword.
50 */
52{
54 sigmatch_table[DETECT_CLASSTYPE].desc = "information about the classification of rules and alerts";
55 sigmatch_table[DETECT_CLASSTYPE].url = "/rules/meta.html#classtype";
56 sigmatch_table[DETECT_CLASSTYPE].Setup = DetectClasstypeSetup;
57#ifdef UNITTESTS
58 sigmatch_table[DETECT_CLASSTYPE].RegisterTests = DetectClasstypeRegisterTests;
59#endif
61}
62
63/**
64 * \brief Parses the raw string supplied with the "Classtype" keyword.
65 *
66 * \param Pointer to the string to be parsed.
67 *
68 * \retval bool success or failure.
69 */
70static int DetectClasstypeParseRawString(const char *rawstr, char *out, size_t outsize)
71{
72 size_t pcre2len;
73
74 const size_t esize = CLASSTYPE_NAME_MAX_LEN + 8;
75 char e[esize];
76 pcre2_match_data *match = NULL;
77
78 int ret = DetectParsePcreExec(&parse_regex, &match, rawstr, 0, 0);
79 if (ret < 0) {
80 SCLogError("Invalid Classtype in Signature");
81 goto error;
82 }
83
84 pcre2len = esize;
85 ret = pcre2_substring_copy_bynumber(match, 1, (PCRE2_UCHAR8 *)e, &pcre2len);
86 if (ret < 0) {
87 SCLogError("pcre2_substring_copy_bynumber failed");
88 goto error;
89 }
90
91 if (strlen(e) >= CLASSTYPE_NAME_MAX_LEN) {
92 SCLogError("classtype '%s' is too big: max %d", rawstr, CLASSTYPE_NAME_MAX_LEN - 1);
93 goto error;
94 }
95 (void)strlcpy(out, e, outsize);
96
97 pcre2_match_data_free(match);
98 return 0;
99
100error:
101 if (match) {
102 pcre2_match_data_free(match);
103 }
104 return -1;
105}
106
107/**
108 * \brief The setup function that would be called when the Signature parsing
109 * module encounters the "Classtype" keyword.
110 *
111 * \param de_ctx Pointer to the Detection Engine Context.
112 * \param s Pointer the current Signature instance that is being parsed.
113 * \param rawstr Pointer to the argument supplied to the classtype keyword.
114 *
115 * \retval 0 On success
116 * \retval -1 On failure
117 */
118static int DetectClasstypeSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
119{
120 char parsed_ct_name[CLASSTYPE_NAME_MAX_LEN] = "";
121
122 if ((s->class_id > 0) || (s->class_msg != NULL)) {
124 SCLogError("duplicated 'classtype' "
125 "keyword detected.");
126 return -1;
127 } else {
128 SCLogWarning("duplicated 'classtype' "
129 "keyword detected. Using instance with highest priority");
130 }
131 }
132
133 if (DetectClasstypeParseRawString(rawstr, parsed_ct_name, sizeof(parsed_ct_name)) < 0) {
134 SCLogError("invalid value for classtype keyword: "
135 "\"%s\"",
136 rawstr);
137 return -1;
138 }
139
140 bool real_ct = true;
142 if (ct == NULL) {
144 SCLogError("unknown classtype '%s'", parsed_ct_name);
145 return -1;
146 }
147
148 if (s->id > 0) {
149 SCLogWarning("signature sid:%u uses "
150 "unknown classtype: \"%s\", using default priority %d. "
151 "This message won't be shown again for this classtype",
152 s->id, parsed_ct_name, DETECT_DEFAULT_PRIO);
153 } else if (de_ctx->rule_file != NULL) {
154 SCLogWarning("signature at %s:%u uses "
155 "unknown classtype: \"%s\", using default priority %d. "
156 "This message won't be shown again for this classtype",
158 } else {
159 SCLogWarning("unknown classtype: \"%s\", "
160 "using default priority %d. "
161 "This message won't be shown again for this classtype",
162 parsed_ct_name, DETECT_DEFAULT_PRIO);
163 }
164
165 char str[256];
166 snprintf(str, sizeof(str),
167 "config classification: %s,Unknown Classtype,%d\n",
168 parsed_ct_name, DETECT_DEFAULT_PRIO);
169
171 return -1;
172 ct = SCClassConfGetClasstype(parsed_ct_name, de_ctx);
173 if (ct == NULL)
174 return -1;
175 real_ct = false;
176 }
177
178 /* set prio only if not already explicitly set by 'priority' keyword.
179 * update classtype in sig, but only if it is 'real' (not undefined)
180 * update sigs classtype if its prio is lower (but not undefined)
181 */
182
183 bool update_ct = false;
185 /* don't touch Signature::prio */
186 update_ct = true;
187 } else if (s->prio == -1) {
188 s->prio = ct->priority;
189 update_ct = true;
190 } else {
191 if (ct->priority < s->prio) {
192 s->prio = ct->priority;
193 update_ct = true;
194 }
195 }
196
197 if (real_ct && update_ct) {
198 s->class_id = ct->classtype_id;
199 s->class_msg = ct->classtype_desc;
200 }
201 return 0;
202}
203
204#ifdef UNITTESTS
205
206/**
207 * \test undefined classtype
208 */
209static int DetectClasstypeTest01(void)
210{
213
215 FAIL_IF_NULL(fd);
217 Signature *s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
218 "(msg:\"Classtype test\"; "
219 "Classtype:not_available; sid:1;)");
220 FAIL_IF_NULL(s);
221 FAIL_IF_NOT(s->prio == 3);
222
224 PASS;
225}
226
227/**
228 * \test Check that both valid and invalid classtypes in a rule are handled
229 * properly, with rules containing invalid classtypes being rejected
230 * and the ones containing valid classtypes parsed and returned.
231 */
232static int DetectClasstypeTest02(void)
233{
236
238 FAIL_IF_NULL(fd);
240
241 Signature *sig = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
242 "(Classtype:bad-unknown; sid:1;)");
243 FAIL_IF_NULL(sig);
244
245 sig = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
246 "(Classtype:not-there; sid:2;)");
247 FAIL_IF_NULL(sig);
248
249 sig = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
250 "(Classtype:Bad-UnkNown; sid:3;)");
251 FAIL_IF_NULL(sig);
252
253 sig = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
254 "(Classtype:nothing-wrong; sid:4;)");
255 FAIL_IF_NULL(sig);
256
257 sig = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
258 "(Classtype:attempted_dos; Classtype:bad-unknown; sid:5;)");
259 FAIL_IF_NULL(sig);
260 FAIL_IF_NOT(sig->prio == 2);
261
262 /* duplicate test */
263 sig = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
264 "(Classtype:nothing-wrong; Classtype:Bad-UnkNown; sid:6;)");
265 FAIL_IF_NULL(sig);
266 FAIL_IF_NOT(sig->prio == 2);
267
269 PASS;
270}
271
272/**
273 * \test Check that the signatures are assigned priority based on classtype they
274 * are given.
275 */
276static int DetectClasstypeTest03(void)
277{
280
282 FAIL_IF_NULL(fd);
284
285 Signature *sig = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
286 "(msg:\"Classtype test\"; Classtype:bad-unknown; priority:1; sid:1;)");
287 FAIL_IF_NULL(sig);
288 FAIL_IF_NOT(sig->prio == 1);
289
290 sig = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
291 "(msg:\"Classtype test\"; Classtype:unKnoWn; "
292 "priority:3; sid:2;)");
293 FAIL_IF_NULL(sig);
294 FAIL_IF_NOT(sig->prio == 3);
295
296 sig = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"Classtype test\"; "
297 "Classtype:nothing-wrong; priority:1; sid:3;)");
298 FAIL_IF_NOT(sig->prio == 1);
299
300 sig = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
301 "(msg:\"Classtype test\"; Classtype:bad-unknown; Classtype:undefined; "
302 "priority:5; sid:4;)");
303 FAIL_IF_NULL(sig);
304 FAIL_IF_NOT(sig->prio == 5);
305
307 PASS;
308}
309
310/**
311 * \brief This function registers unit tests for Classification Config API.
312 */
313static void DetectClasstypeRegisterTests(void)
314{
315 UtRegisterTest("DetectClasstypeTest01", DetectClasstypeTest01);
316 UtRegisterTest("DetectClasstypeTest02", DetectClasstypeTest02);
317 UtRegisterTest("DetectClasstypeTest03", DetectClasstypeTest03);
318}
319#endif /* UNITTESTS */
void DetectClasstypeRegister(void)
Registers the handler functions for the "Classtype" keyword.
#define PARSE_REGEX
@ DETECT_CLASSTYPE
DetectEngineCtx * DetectEngineCtxInit(void)
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
Signature * DetectEngineAppendSig(DetectEngineCtx *, const char *)
Parse and append a Signature into the Detection Engine Context signature list.
bool SigMatchStrictEnabled(const enum DetectKeywordId id)
void DetectSetupParseRegexes(const char *parse_str, DetectParseRegex *detect_parse)
int DetectParsePcreExec(DetectParseRegex *parse_regex, pcre2_match_data **match, const char *str, int start_offset, int options)
SigTableElmt * sigmatch_table
#define DETECT_DEFAULT_PRIO
Definition detect.h:52
#define SIG_FLAG_INIT_PRIO_EXPLICIT
Definition detect.h:298
DetectEngineCtx * de_ctx
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
#define PASS
Pass the test.
main detection engine ctx
Definition detect.h:932
const char * rule_file
Definition detect.h:1024
Container for a Classtype from the Classification.config file.
const char * url
Definition detect.h:1462
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition detect.h:1441
const char * desc
Definition detect.h:1461
void(* RegisterTests)(void)
Definition detect.h:1448
const char * name
Definition detect.h:1459
uint32_t init_flags
Definition detect.h:608
Signature container.
Definition detect.h:668
SignatureInitData * init_data
Definition detect.h:747
uint16_t class_id
Definition detect.h:699
int prio
Definition detect.h:716
char * class_msg
Definition detect.h:739
uint32_t id
Definition detect.h:713
#define str(s)
size_t strlcpy(char *dst, const char *src, size_t siz)
FILE * SCClassConfGenerateValidDummyClassConfigFD01(void)
Creates a dummy classification file, with all valid Classtypes, for testing purposes.
int SCClassConfAddClasstype(DetectEngineCtx *de_ctx, char *rawstr, uint16_t index)
Parses a line from the classification file and adds it to Classtype hash table in DetectEngineCtx,...
SCClassConfClasstype * SCClassConfGetClasstype(const char *ct_name, DetectEngineCtx *de_ctx)
Gets the classtype from the corresponding hash table stored in the Detection Engine Context's class c...
bool SCClassConfLoadClassificationConfigFile(DetectEngineCtx *de_ctx, FILE *fd)
Loads the Classtype info from the classification.config file.
#define CLASSTYPE_NAME_MAX_LEN
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition util-debug.h:255
#define SCLogError(...)
Macro used to log ERROR messages.
Definition util-debug.h:267