Coverage Report

Created: 2026-11-12 06:59

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