Coverage Report

Created: 2026-06-30 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/util-classification-config.c
Line
Count
Source
1
/* Copyright (C) 2007-2010 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
 *
23
 * Used for parsing a classification.config file
24
 */
25
26
#include "suricata-common.h"
27
#include "detect.h"
28
#include "detect-engine.h"
29
#include "util-hash.h"
30
31
#include "conf.h"
32
#include "util-classification-config.h"
33
#include "util-unittest.h"
34
#include "util-error.h"
35
#include "util-debug.h"
36
#include "util-fmemopen.h"
37
#include "util-byte.h"
38
39
/* Regex to parse the classtype argument from a Signature.  The first substring
40
 * holds the classtype name, the second substring holds the classtype the
41
 * classtype description, and the third argument holds the priority */
42
56.8k
#define DETECT_CLASSCONFIG_REGEX "^\\s*config\\s*classification\\s*:\\s*([a-zA-Z][a-zA-Z0-9-_]*)\\s*,\\s*(.+)\\s*,\\s*(\\d+)\\s*$"
43
44
/* Default path for the classification.config file */
45
#if defined OS_WIN32 || defined __CYGWIN__
46
#define SC_CLASS_CONF_DEF_CONF_FILEPATH CONFIG_DIR "\\\\classification.config"
47
#else
48
145k
#define SC_CLASS_CONF_DEF_CONF_FILEPATH CONFIG_DIR "/classification.config"
49
#endif
50
51
uint32_t SCClassConfClasstypeHashFunc(HashTable *ht, void *data, uint16_t datalen);
52
char SCClassConfClasstypeHashCompareFunc(void *data1, uint16_t datalen1,
53
                                         void *data2, uint16_t datalen2);
54
void SCClassConfClasstypeHashFree(void *ch);
55
static const char *SCClassConfGetConfFilename(const DetectEngineCtx *de_ctx);
56
57
static SCClassConfClasstype *SCClassConfAllocClasstype(uint16_t classtype_id,
58
        const char *classtype, const char *classtype_desc, int priority);
59
static void SCClassConfDeAllocClasstype(SCClassConfClasstype *ct);
60
61
void SCClassConfInit(DetectEngineCtx *de_ctx)
62
56.8k
{
63
56.8k
    int en;
64
56.8k
    PCRE2_SIZE eo;
65
56.8k
    int opts = 0;
66
67
56.8k
    de_ctx->class_conf_regex = pcre2_compile(
68
56.8k
            (PCRE2_SPTR8)DETECT_CLASSCONFIG_REGEX, PCRE2_ZERO_TERMINATED, opts, &en, &eo, NULL);
69
56.8k
    if (de_ctx->class_conf_regex == NULL) {
70
0
        PCRE2_UCHAR errbuffer[256];
71
0
        pcre2_get_error_message(en, errbuffer, sizeof(errbuffer));
72
0
        SCLogWarning("pcre2 compile of \"%s\" failed at "
73
0
                     "offset %d: %s",
74
0
                DETECT_CLASSCONFIG_REGEX, (int)eo, errbuffer);
75
0
        return;
76
0
    }
77
56.8k
    de_ctx->class_conf_regex_match =
78
56.8k
            pcre2_match_data_create_from_pattern(de_ctx->class_conf_regex, NULL);
79
56.8k
    return;
80
56.8k
}
81
82
void SCClassConfDeinit(DetectEngineCtx *de_ctx)
83
145k
{
84
145k
    if (de_ctx->class_conf_regex != NULL) {
85
145k
        pcre2_code_free(de_ctx->class_conf_regex);
86
145k
        de_ctx->class_conf_regex = NULL;
87
145k
    }
88
145k
    if (de_ctx->class_conf_regex_match != NULL) {
89
145k
        pcre2_match_data_free(de_ctx->class_conf_regex_match);
90
145k
        de_ctx->class_conf_regex_match = NULL;
91
145k
    }
92
145k
}
93
94
95
/**
96
 * \brief Inits the context to be used by the Classification Config parsing API.
97
 *
98
 *        This function initializes the hash table to be used by the Detection
99
 *        Engine Context to hold the data from the classification.config file,
100
 *        obtains the file desc to parse the classification.config file, and
101
 *        inits the regex used to parse the lines from classification.config
102
 *        file.
103
 *
104
 * \param de_ctx Pointer to the Detection Engine Context.
105
 * \param fd Pointer to already opened file
106
 *
107
 * \note even if the file open fails we will keep the de_ctx->class_conf_ht
108
 *       initialized.
109
 *
110
 * \retval fp NULL on error
111
 */
112
static FILE *SCClassConfInitContextAndLocalResources(DetectEngineCtx *de_ctx, FILE *fd)
113
145k
{
114
    /* init the hash table to be used by the classification config Classtypes */
115
145k
    de_ctx->class_conf_ht = HashTableInit(128, SCClassConfClasstypeHashFunc,
116
145k
                                          SCClassConfClasstypeHashCompareFunc,
117
145k
                                          SCClassConfClasstypeHashFree);
118
145k
    if (de_ctx->class_conf_ht == NULL) {
119
0
        SCLogError("Error initializing the hash "
120
0
                   "table");
121
0
        return NULL;
122
0
    }
123
124
    /* if it is not NULL, use the file descriptor.  The hack so that we can
125
     * avoid using a dummy classification file for testing purposes and
126
     * instead use an input stream against a buffer containing the
127
     * classification strings */
128
145k
    if (fd == NULL) {
129
145k
        const char *filename = SCClassConfGetConfFilename(de_ctx);
130
145k
        if ( (fd = fopen(filename, "r")) == NULL) {
131
#ifdef UNITTESTS
132
            if (RunmodeIsUnittests())
133
                return NULL; // silently fail
134
#endif
135
145k
            SCLogWarning("could not open: \"%s\": %s", filename, strerror(errno));
136
145k
            return NULL;
137
145k
        }
138
145k
    }
139
140
0
    return fd;
141
145k
}
142
143
144
/**
145
 * \brief Returns the path for the Classification Config file.  We check if we
146
 *        can retrieve the path from the yaml conf file.  If it is not present,
147
 *        return the default path for the classification file which is
148
 *        "./classification.config".
149
 *
150
 * \retval log_filename Pointer to a string containing the path for the
151
 *                      Classification Config file.
152
 */
153
static const char *SCClassConfGetConfFilename(const DetectEngineCtx *de_ctx)
154
145k
{
155
145k
    const char *log_filename = NULL;
156
157
145k
    if (de_ctx != NULL && strlen(de_ctx->config_prefix) > 0) {
158
0
        char config_value[256];
159
0
        snprintf(config_value, sizeof(config_value),
160
0
                 "%s.classification-file", de_ctx->config_prefix);
161
162
        /* try loading prefix setting, fall back to global if that
163
         * fails. */
164
0
        if (ConfGet(config_value, &log_filename) != 1) {
165
0
            if (ConfGet("classification-file", &log_filename) != 1) {
166
0
                log_filename = (char *)SC_CLASS_CONF_DEF_CONF_FILEPATH;
167
0
            }
168
0
        }
169
145k
    } else {
170
145k
        if (ConfGet("classification-file", &log_filename) != 1) {
171
145k
            log_filename = (char *)SC_CLASS_CONF_DEF_CONF_FILEPATH;
172
145k
        }
173
145k
    }
174
175
145k
    return log_filename;
176
145k
}
177
178
/**
179
 * \brief Releases resources used by the Classification Config API.
180
 */
181
static void SCClassConfDeInitLocalResources(DetectEngineCtx *de_ctx, FILE *fd)
182
0
{
183
0
    if (fd != NULL) {
184
0
        fclose(fd);
185
0
    }
186
0
}
187
188
/**
189
 * \brief Releases resources used by the Classification Config API.
190
 */
191
void SCClassConfDeInitContext(DetectEngineCtx *de_ctx)
192
56.8k
{
193
56.8k
    if (de_ctx->class_conf_ht != NULL)
194
56.8k
        HashTableFree(de_ctx->class_conf_ht);
195
196
56.8k
    de_ctx->class_conf_ht = NULL;
197
198
56.8k
    return;
199
56.8k
}
200
201
/**
202
 * \brief Converts a string to lowercase.
203
 *
204
 * \param str Pointer to the string to be converted.
205
 */
206
static char *SCClassConfStringToLowercase(const char *str)
207
17.8k
{
208
17.8k
    char *new_str = NULL;
209
17.8k
    char *temp_str = NULL;
210
211
17.8k
    if ( (new_str = SCStrdup(str)) == NULL) {
212
0
        SCLogError("Error allocating memory");
213
0
        return NULL;
214
0
    }
215
216
17.8k
    temp_str = new_str;
217
320k
    while (*temp_str != '\0') {
218
302k
        *temp_str = u8_tolower((unsigned char)*temp_str);
219
302k
        temp_str++;
220
302k
    }
221
222
17.8k
    return new_str;
223
17.8k
}
224
225
/**
226
 * \brief Parses a line from the classification file and adds it to Classtype
227
 *        hash table in DetectEngineCtx, i.e. DetectEngineCtx->class_conf_ht.
228
 *
229
 * \param rawstr Pointer to the string to be parsed.
230
 * \param index  Relative index of the string to be parsed.
231
 * \param de_ctx Pointer to the Detection Engine Context.
232
 *
233
 * \retval  0 On success.
234
 * \retval -1 On failure.
235
 */
236
int SCClassConfAddClasstype(DetectEngineCtx *de_ctx, char *rawstr, uint16_t index)
237
17.8k
{
238
17.8k
    char ct_name[CLASSTYPE_NAME_MAX_LEN];
239
17.8k
    char ct_desc[CLASSTYPE_DESC_MAX_LEN];
240
17.8k
    char ct_priority_str[16];
241
17.8k
    uint32_t ct_priority = 0;
242
17.8k
    uint16_t ct_id = index;
243
244
17.8k
    SCClassConfClasstype *ct_new = NULL;
245
17.8k
    SCClassConfClasstype *ct_lookup = NULL;
246
247
17.8k
    int ret = 0;
248
249
17.8k
    ret = pcre2_match(de_ctx->class_conf_regex, (PCRE2_SPTR8)rawstr, strlen(rawstr), 0, 0,
250
17.8k
            de_ctx->class_conf_regex_match, NULL);
251
17.8k
    if (ret < 0) {
252
0
        SCLogError("Invalid Classtype in "
253
0
                   "classification.config file %s: \"%s\"",
254
0
                SCClassConfGetConfFilename(de_ctx), rawstr);
255
0
        goto error;
256
0
    }
257
258
17.8k
    size_t copylen = sizeof(ct_name);
259
    /* retrieve the classtype name */
260
17.8k
    ret = pcre2_substring_copy_bynumber(
261
17.8k
            de_ctx->class_conf_regex_match, 1, (PCRE2_UCHAR8 *)ct_name, &copylen);
262
17.8k
    if (ret < 0) {
263
0
        SCLogInfo("pcre2_substring_copy_bynumber() failed");
264
0
        goto error;
265
0
    }
266
267
    /* retrieve the classtype description */
268
17.8k
    copylen = sizeof(ct_desc);
269
17.8k
    ret = pcre2_substring_copy_bynumber(
270
17.8k
            de_ctx->class_conf_regex_match, 2, (PCRE2_UCHAR8 *)ct_desc, &copylen);
271
17.8k
    if (ret < 0) {
272
0
        SCLogInfo("pcre2_substring_copy_bynumber() failed");
273
0
        goto error;
274
0
    }
275
276
    /* retrieve the classtype priority */
277
17.8k
    copylen = sizeof(ct_priority_str);
278
17.8k
    ret = pcre2_substring_copy_bynumber(
279
17.8k
            de_ctx->class_conf_regex_match, 3, (PCRE2_UCHAR8 *)ct_priority_str, &copylen);
280
17.8k
    if (ret < 0) {
281
0
        SCLogInfo("pcre2_substring_copy_bynumber() failed");
282
0
        goto error;
283
0
    }
284
17.8k
    if (StringParseUint32(&ct_priority, 10, 0, (const char *)ct_priority_str) < 0) {
285
0
        goto error;
286
0
    }
287
288
    /* Create a new instance of the parsed Classtype string */
289
17.8k
    ct_new = SCClassConfAllocClasstype(ct_id, ct_name, ct_desc, ct_priority);
290
17.8k
    if (ct_new == NULL)
291
0
        goto error;
292
293
    /* Check if the Classtype is present in the HashTable.  In case it's present
294
     * ignore it, as it is a duplicate.  If not present, add it to the table */
295
17.8k
    ct_lookup = HashTableLookup(de_ctx->class_conf_ht, ct_new, 0);
296
17.8k
    if (ct_lookup == NULL) {
297
17.8k
        if (HashTableAdd(de_ctx->class_conf_ht, ct_new, 0) < 0)
298
0
            SCLogDebug("HashTable Add failed");
299
17.8k
    } else {
300
0
        SCLogDebug("Duplicate classtype found inside classification.config");
301
0
        if (ct_new->classtype_desc) SCFree(ct_new->classtype_desc);
302
0
        if (ct_new->classtype) SCFree(ct_new->classtype);
303
0
        SCFree(ct_new);
304
0
    }
305
306
17.8k
    return 0;
307
308
0
 error:
309
0
    return -1;
310
17.8k
}
311
312
/**
313
 * \brief Checks if a string is a comment or a blank line.
314
 *
315
 *        Comments lines are lines of the following format -
316
 *        "# This is a comment string" or
317
 *        "   # This is a comment string".
318
 *
319
 * \param line String that has to be checked
320
 *
321
 * \retval 1 On the argument string being a comment or blank line
322
 * \retval 0 Otherwise
323
 */
324
static int SCClassConfIsLineBlankOrComment(char *line)
325
0
{
326
0
    while (*line != '\0') {
327
        /* we have a comment */
328
0
        if (*line == '#')
329
0
            return 1;
330
331
        /* this line is neither a comment line, nor a blank line */
332
0
        if (!isspace((unsigned char)*line))
333
0
            return 0;
334
335
0
        line++;
336
0
    }
337
338
    /* we have a blank line */
339
0
    return 1;
340
0
}
341
342
/**
343
 * \brief Parses the Classification Config file and updates the
344
 *        DetectionEngineCtx->class_conf_ht with the Classtype information.
345
 *
346
 * \param de_ctx Pointer to the Detection Engine Context.
347
 */
348
static bool SCClassConfParseFile(DetectEngineCtx *de_ctx, FILE *fd)
349
0
{
350
0
    char line[1024];
351
0
    uint16_t i = 1;
352
0
    int errors = 0;
353
354
0
    while (fgets(line, sizeof(line), fd) != NULL) {
355
0
        if (SCClassConfIsLineBlankOrComment(line))
356
0
            continue;
357
358
0
        if (SCClassConfAddClasstype(de_ctx, line, i) == -1) {
359
0
            errors++;
360
0
        } else {
361
0
            i++;
362
0
        }
363
0
    }
364
365
#ifdef UNITTESTS
366
    SCLogInfo("Added \"%d\" classification types from the classification file",
367
              de_ctx->class_conf_ht->count);
368
#endif
369
370
0
    return errors == 0;
371
0
}
372
373
/**
374
 * \internal
375
 * \brief Returns a new SCClassConfClasstype instance.  The classtype string
376
 *        is converted into lowercase, before being assigned to the instance.
377
 *
378
 * \param classtype      Pointer to the classification type.
379
 * \param classtype_desc Pointer to the classification type description.
380
 * \param priority       Holds the priority for the classification type.
381
 *
382
 * \retval ct Pointer to the new instance of SCClassConfClasstype on success;
383
 *            NULL on failure.
384
 */
385
static SCClassConfClasstype *SCClassConfAllocClasstype(uint16_t classtype_id,
386
                                                const char *classtype,
387
                                                const char *classtype_desc,
388
                                                int priority)
389
17.8k
{
390
17.8k
    SCClassConfClasstype *ct = NULL;
391
392
17.8k
    if (classtype == NULL)
393
0
        return NULL;
394
395
17.8k
    if ( (ct = SCMalloc(sizeof(SCClassConfClasstype))) == NULL)
396
0
        return NULL;
397
17.8k
    memset(ct, 0, sizeof(SCClassConfClasstype));
398
399
17.8k
    if ((ct->classtype = SCClassConfStringToLowercase(classtype)) == NULL) {
400
0
        SCClassConfDeAllocClasstype(ct);
401
0
        return NULL;
402
0
    }
403
404
17.8k
    if (classtype_desc != NULL &&
405
17.8k
        (ct->classtype_desc = SCStrdup(classtype_desc)) == NULL) {
406
0
        SCLogError("Error allocating memory");
407
408
0
        SCClassConfDeAllocClasstype(ct);
409
0
        return NULL;
410
0
    }
411
412
17.8k
    ct->classtype_id = classtype_id;
413
17.8k
    ct->priority = priority;
414
415
17.8k
    return ct;
416
17.8k
}
417
418
/**
419
 * \internal
420
 * \brief Frees a SCClassConfClasstype instance
421
 *
422
 * \param Pointer to the SCClassConfClasstype instance that has to be freed
423
 */
424
static void SCClassConfDeAllocClasstype(SCClassConfClasstype *ct)
425
10.5k
{
426
10.5k
    if (ct != NULL) {
427
10.5k
        if (ct->classtype != NULL)
428
10.5k
            SCFree(ct->classtype);
429
430
10.5k
        if (ct->classtype_desc != NULL)
431
10.5k
            SCFree(ct->classtype_desc);
432
433
10.5k
        SCFree(ct);
434
10.5k
    }
435
436
10.5k
    return;
437
10.5k
}
438
439
/**
440
 * \brief Hashing function to be used to hash the Classtype name.  Would be
441
 *        supplied as an argument to the HashTableInit function for
442
 *        DetectEngineCtx->class_conf_ht.
443
 *
444
 * \param ht      Pointer to the HashTable.
445
 * \param data    Pointer to the data to be hashed.  In this case, the data
446
 *                would be a pointer to a SCClassConfClasstype instance.
447
 * \param datalen Not used by this function.
448
 */
449
uint32_t SCClassConfClasstypeHashFunc(HashTable *ht, void *data, uint16_t datalen)
450
353k
{
451
353k
    SCClassConfClasstype *ct = (SCClassConfClasstype *)data;
452
353k
    uint32_t hash = 0;
453
353k
    int i = 0;
454
455
353k
    int len = strlen(ct->classtype);
456
457
7.10M
    for (i = 0; i < len; i++)
458
6.75M
        hash += u8_tolower((unsigned char)(ct->classtype)[i]);
459
460
353k
    hash = hash % ht->array_size;
461
462
353k
    return hash;
463
353k
}
464
465
/**
466
 * \brief Used to compare two Classtypes that have been stored in the HashTable.
467
 *        This function is supplied as an argument to the HashTableInit function
468
 *        for DetectionEngineCtx->class_conf_ct.
469
 *
470
 * \param data1 Pointer to the first SCClassConfClasstype to be compared.
471
 * \param len1  Not used by this function.
472
 * \param data2 Pointer to the second SCClassConfClasstype to be compared.
473
 * \param len2  Not used by this function.
474
 *
475
 * \retval 1 On data1 and data2 being equal.
476
 * \retval 0 On data1 and data2 not being equal.
477
 */
478
char SCClassConfClasstypeHashCompareFunc(void *data1, uint16_t datalen1,
479
                                         void *data2, uint16_t datalen2)
480
162k
{
481
162k
    SCClassConfClasstype *ct1 = (SCClassConfClasstype *)data1;
482
162k
    SCClassConfClasstype *ct2 = (SCClassConfClasstype *)data2;
483
162k
    int len1 = 0;
484
162k
    int len2 = 0;
485
486
162k
    if (ct1 == NULL || ct2 == NULL)
487
0
        return 0;
488
489
162k
    if (ct1->classtype == NULL || ct2->classtype == NULL)
490
0
        return 0;
491
492
162k
    len1 = strlen(ct1->classtype);
493
162k
    len2 = strlen(ct2->classtype);
494
495
162k
    if (len1 == len2 && memcmp(ct1->classtype, ct2->classtype, len1) == 0) {
496
152k
        SCLogDebug("Match found inside Classification-Config hash function");
497
152k
        return 1;
498
152k
    }
499
500
9.26k
    return 0;
501
162k
}
502
503
/**
504
 * \brief Used to free the Classification Config Hash Data that was stored in
505
 *        DetectEngineCtx->class_conf_ht Hashtable.
506
 *
507
 * \param ch Pointer to the data that has to be freed.
508
 */
509
void SCClassConfClasstypeHashFree(void *ch)
510
10.5k
{
511
10.5k
    SCClassConfDeAllocClasstype(ch);
512
513
10.5k
    return;
514
10.5k
}
515
516
/**
517
 * \brief Loads the Classtype info from the classification.config file.
518
 *
519
 *        The classification.config file contains the different classtypes,
520
 *        that can be used to label Signatures.  Each line of the file should
521
 *        have the following format -
522
 *        classtype_name, classtype_description, priority
523
 *        None of the above parameters should hold a quote inside the file.
524
 *
525
 * \param de_ctx Pointer to the Detection Engine Context that should be updated
526
 *               with Classtype information.
527
 */
528
bool SCClassConfLoadClassificationConfigFile(DetectEngineCtx *de_ctx, FILE *fd)
529
145k
{
530
145k
    fd = SCClassConfInitContextAndLocalResources(de_ctx, fd);
531
145k
    if (fd == NULL) {
532
#ifdef UNITTESTS
533
        if (RunmodeIsUnittests()) {
534
            return false;
535
        }
536
#endif
537
145k
        SCLogError("please check the \"classification-file\" "
538
145k
                   "option in your suricata.yaml file");
539
145k
        return false;
540
145k
    }
541
542
145k
    bool ret = true;
543
0
    if (!SCClassConfParseFile(de_ctx, fd)) {
544
0
        SCLogWarning("Error loading classification configuration from %s",
545
0
                SCClassConfGetConfFilename(de_ctx));
546
0
        ret = false;
547
0
    }
548
549
0
    SCClassConfDeInitLocalResources(de_ctx, fd);
550
551
0
    return ret;
552
145k
}
553
554
/**
555
 * \brief Gets the classtype from the corresponding hash table stored
556
 *        in the Detection Engine Context's class conf ht, given the
557
 *        classtype name.
558
 *
559
 * \param ct_name Pointer to the classtype name that has to be looked up.
560
 * \param de_ctx  Pointer to the Detection Engine Context.
561
 *
562
 * \retval lookup_ct_info Pointer to the SCClassConfClasstype instance from
563
 *                        the hash table on success; NULL on failure.
564
 */
565
SCClassConfClasstype *SCClassConfGetClasstype(const char *ct_name,
566
                                              DetectEngineCtx *de_ctx)
567
163k
{
568
163k
    char name[strlen(ct_name) + 1];
569
163k
    size_t s;
570
3.26M
    for (s = 0; s < strlen(ct_name); s++)
571
3.10M
        name[s] = u8_tolower((unsigned char)ct_name[s]);
572
163k
    name[s] = '\0';
573
574
    SCClassConfClasstype ct_lookup = {0, 0, name, NULL };
575
163k
    SCClassConfClasstype *lookup_ct_info = HashTableLookup(de_ctx->class_conf_ht,
576
163k
                                                           &ct_lookup, 0);
577
163k
    return lookup_ct_info;
578
163k
}
579
580
/*----------------------------------Unittests---------------------------------*/
581
582
583
#ifdef UNITTESTS
584
585
/**
586
 * \brief Creates a dummy classification file, with all valid Classtypes, for
587
 *        testing purposes.
588
 *
589
 * \file_path Pointer to the file_path for the dummy classification file.
590
 */
591
FILE *SCClassConfGenerateValidDummyClassConfigFD01(void)
592
{
593
    const char *buffer =
594
        "config classification: nothing-wrong,Nothing Wrong With Us,3\n"
595
        "config classification: unknown,Unknown are we,3\n"
596
        "config classification: bad-unknown,We think it's bad, 2\n";
597
598
    FILE *fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
599
    if (fd == NULL)
600
        SCLogDebug("Error with SCFmemopen() called by Classification Config test code");
601
602
    return fd;
603
}
604
605
/**
606
 * \brief Creates a dummy classification file, with some valid Classtypes and a
607
 *        couple of invalid Classtypes, for testing purposes.
608
 *
609
 * \file_path Pointer to the file_path for the dummy classification file.
610
 */
611
FILE *SCClassConfGenerateInvalidDummyClassConfigFD02(void)
612
{
613
    const char *buffer =
614
        "config classification: not-suspicious,Not Suspicious Traffic,3\n"
615
        "onfig classification: unknown,Unknown Traffic,3\n"
616
        "config classification: _badunknown,Potentially Bad Traffic, 2\n"
617
        "config classification: bamboola1,Unknown Traffic,3\n"
618
        "config classification: misc-activity,Misc activity,-1\n"
619
        "config classification: policy-violation,Potential Corporate "
620
        "config classification: bamboola,Unknown Traffic,3\n";
621
622
    FILE *fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
623
    if (fd == NULL)
624
        SCLogDebug("Error with SCFmemopen() called by Classification Config test code");
625
626
    return fd;
627
}
628
629
/**
630
 * \brief Creates a dummy classification file, with all invalid Classtypes, for
631
 *        testing purposes.
632
 *
633
 * \file_path Pointer to the file_path for the dummy classification file.
634
 */
635
FILE *SCClassConfGenerateInvalidDummyClassConfigFD03(void)
636
{
637
    const char *buffer =
638
        "conig classification: not-suspicious,Not Suspicious Traffic,3\n"
639
        "onfig classification: unknown,Unknown Traffic,3\n"
640
        "config classification: _badunknown,Potentially Bad Traffic, 2\n"
641
        "config classification: misc-activity,Misc activity,-1\n";
642
643
    FILE *fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
644
    if (fd == NULL)
645
        SCLogDebug("Error with SCFmemopen() called by Classification Config test code");
646
647
    return fd;
648
}
649
650
/**
651
 * \test Check that the classification file is loaded and the detection engine
652
 *       content class_conf_hash_table loaded with the classtype data.
653
 */
654
static int SCClassConfTest01(void)
655
{
656
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
657
    int result = 0;
658
659
    if (de_ctx == NULL)
660
        return result;
661
662
    FILE *fd = SCClassConfGenerateValidDummyClassConfigFD01();
663
    SCClassConfLoadClassificationConfigFile(de_ctx, fd);
664
665
    if (de_ctx->class_conf_ht == NULL)
666
        return result;
667
668
    result = (de_ctx->class_conf_ht->count == 3);
669
    if (result == 0) printf("de_ctx->class_conf_ht->count %u: ", de_ctx->class_conf_ht->count);
670
671
    DetectEngineCtxFree(de_ctx);
672
673
    return result;
674
}
675
676
/**
677
 * \test Check that invalid classtypes present in the classification config file
678
 *       aren't loaded.
679
 */
680
static int SCClassConfTest02(void)
681
{
682
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
683
    int result = 0;
684
685
    if (de_ctx == NULL)
686
        return result;
687
688
    FILE *fd = SCClassConfGenerateInvalidDummyClassConfigFD03();
689
    SCClassConfLoadClassificationConfigFile(de_ctx, fd);
690
691
    if (de_ctx->class_conf_ht == NULL)
692
        return result;
693
694
    result = (de_ctx->class_conf_ht->count == 0);
695
696
    DetectEngineCtxFree(de_ctx);
697
698
    return result;
699
}
700
701
/**
702
 * \test Check that only valid classtypes are loaded into the hash table from
703
 *       the classification.config file.
704
 */
705
static int SCClassConfTest03(void)
706
{
707
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
708
709
    FAIL_IF_NULL(de_ctx);
710
711
    FILE *fd = SCClassConfGenerateInvalidDummyClassConfigFD02();
712
    FAIL_IF(SCClassConfLoadClassificationConfigFile(de_ctx, fd));
713
714
    DetectEngineCtxFree(de_ctx);
715
716
    PASS;
717
}
718
719
/**
720
 * \test Check if the classtype info from the classification.config file have
721
 *       been loaded into the hash table.
722
 */
723
static int SCClassConfTest04(void)
724
{
725
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
726
    int result = 1;
727
728
    if (de_ctx == NULL)
729
        return 0;
730
731
    FILE *fd = SCClassConfGenerateValidDummyClassConfigFD01();
732
    SCClassConfLoadClassificationConfigFile(de_ctx, fd);
733
734
    if (de_ctx->class_conf_ht == NULL)
735
        return 0;
736
737
    result = (de_ctx->class_conf_ht->count == 3);
738
739
    result &= (SCClassConfGetClasstype("unknown", de_ctx) != NULL);
740
    result &= (SCClassConfGetClasstype("unKnoWn", de_ctx) != NULL);
741
    result &= (SCClassConfGetClasstype("bamboo", de_ctx) == NULL);
742
    result &= (SCClassConfGetClasstype("bad-unknown", de_ctx) != NULL);
743
    result &= (SCClassConfGetClasstype("BAD-UNKnOWN", de_ctx) != NULL);
744
    result &= (SCClassConfGetClasstype("bed-unknown", de_ctx) == NULL);
745
746
    DetectEngineCtxFree(de_ctx);
747
748
    return result;
749
}
750
751
/**
752
 * \test Check if the classtype info from the invalid classification.config file
753
 *       have not been loaded into the hash table, and cross verify to check
754
 *       that the hash table contains no classtype data.
755
 */
756
static int SCClassConfTest05(void)
757
{
758
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
759
    int result = 1;
760
761
    if (de_ctx == NULL)
762
        return 0;
763
764
    FILE *fd = SCClassConfGenerateInvalidDummyClassConfigFD03();
765
    SCClassConfLoadClassificationConfigFile(de_ctx, fd);
766
767
    if (de_ctx->class_conf_ht == NULL)
768
        return 0;
769
770
    result = (de_ctx->class_conf_ht->count == 0);
771
772
    result &= (SCClassConfGetClasstype("unknown", de_ctx) == NULL);
773
    result &= (SCClassConfGetClasstype("unKnoWn", de_ctx) == NULL);
774
    result &= (SCClassConfGetClasstype("bamboo", de_ctx) == NULL);
775
    result &= (SCClassConfGetClasstype("bad-unknown", de_ctx) == NULL);
776
    result &= (SCClassConfGetClasstype("BAD-UNKnOWN", de_ctx) == NULL);
777
    result &= (SCClassConfGetClasstype("bed-unknown", de_ctx) == NULL);
778
779
    DetectEngineCtxFree(de_ctx);
780
781
    return result;
782
}
783
784
/**
785
 * \brief This function registers unit tests for Classification Config API.
786
 */
787
void SCClassConfRegisterTests(void)
788
{
789
    UtRegisterTest("SCClassConfTest01", SCClassConfTest01);
790
    UtRegisterTest("SCClassConfTest02", SCClassConfTest02);
791
    UtRegisterTest("SCClassConfTest03", SCClassConfTest03);
792
    UtRegisterTest("SCClassConfTest04", SCClassConfTest04);
793
    UtRegisterTest("SCClassConfTest05", SCClassConfTest05);
794
}
795
#endif /* UNITTESTS */