Coverage Report

Created: 2025-07-23 07:29

/src/suricata/src/detect-transform-pcrexform.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 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 Jeff Lucovsky <jeff@lucovsky.org>
22
 *
23
 * Implements the pcrexform transform keyword with option support
24
 */
25
26
#include "suricata-common.h"
27
28
#include "detect.h"
29
#include "detect-engine.h"
30
#include "detect-engine-buffer.h"
31
#include "detect-transform-pcrexform.h"
32
#include "detect-pcre.h"
33
34
typedef struct DetectTransformPcrexformData {
35
    pcre2_code *regex;
36
    pcre2_match_context *context;
37
    uint8_t *id_data;
38
    uint32_t id_data_len;
39
} DetectTransformPcrexformData;
40
41
static int DetectTransformPcrexformSetup (DetectEngineCtx *, Signature *, const char *);
42
static void DetectTransformPcrexformFree(DetectEngineCtx *, void *);
43
static void DetectTransformPcrexform(
44
        DetectEngineThreadCtx *det_ctx, InspectionBuffer *buffer, void *options);
45
#ifdef UNITTESTS
46
void DetectTransformPcrexformRegisterTests (void);
47
#endif
48
49
static void DetectTransformPcrexformId(const uint8_t **data, uint32_t *length, void *context)
50
62.2k
{
51
62.2k
    if (context) {
52
62.2k
        DetectTransformPcrexformData *pxd = (DetectTransformPcrexformData *)context;
53
62.2k
        *data = (const uint8_t *)pxd->id_data;
54
62.2k
        *length = pxd->id_data_len;
55
62.2k
    }
56
62.2k
}
57
58
void DetectTransformPcrexformRegister(void)
59
73
{
60
73
    sigmatch_table[DETECT_TRANSFORM_PCREXFORM].name = "pcrexform";
61
73
    sigmatch_table[DETECT_TRANSFORM_PCREXFORM].desc =
62
73
        "modify buffer via PCRE before inspection";
63
73
    sigmatch_table[DETECT_TRANSFORM_PCREXFORM].url = "/rules/transforms.html#pcre-xform";
64
73
    sigmatch_table[DETECT_TRANSFORM_PCREXFORM].Transform =
65
73
        DetectTransformPcrexform;
66
73
    sigmatch_table[DETECT_TRANSFORM_PCREXFORM].TransformId = DetectTransformPcrexformId;
67
73
    sigmatch_table[DETECT_TRANSFORM_PCREXFORM].Free =
68
73
        DetectTransformPcrexformFree;
69
73
    sigmatch_table[DETECT_TRANSFORM_PCREXFORM].Setup =
70
73
        DetectTransformPcrexformSetup;
71
#ifdef UNITTESTS
72
    sigmatch_table[DETECT_TRANSFORM_PCREXFORM].RegisterTests = DetectTransformPcrexformRegisterTests;
73
#endif
74
73
    sigmatch_table[DETECT_TRANSFORM_PCREXFORM].flags |= SIGMATCH_QUOTES_MANDATORY;
75
73
}
76
77
static void DetectTransformPcrexformFree(DetectEngineCtx *de_ctx, void *ptr)
78
76.7k
{
79
76.7k
    if (ptr != NULL) {
80
76.7k
        DetectTransformPcrexformData *pxd = (DetectTransformPcrexformData *) ptr;
81
82
76.7k
        pcre2_match_context_free(pxd->context);
83
76.7k
        pcre2_code_free(pxd->regex);
84
85
76.7k
        SCFree(pxd->id_data);
86
76.7k
        SCFree(pxd);
87
76.7k
    }
88
76.7k
}
89
90
/**
91
 *  \internal
92
 *  \brief Apply the pcrexform keyword to the last pattern match
93
 *  \param det_ctx detection engine ctx
94
 *  \param s signature
95
 *  \param regexstr options string
96
 *  \retval 0 ok
97
 *  \retval -1 failure
98
 */
99
static int DetectTransformPcrexformSetup (DetectEngineCtx *de_ctx, Signature *s, const char *regexstr)
100
80.9k
{
101
80.9k
    SCEnter();
102
103
    // Create pxd from regexstr
104
80.9k
    DetectTransformPcrexformData *pxd = SCCalloc(1, sizeof(*pxd));
105
80.9k
    if (pxd == NULL) {
106
0
        SCLogDebug("pxd allocation failed");
107
0
        SCReturnInt(-1);
108
0
    }
109
110
80.9k
    pxd->context = pcre2_match_context_create(NULL);
111
80.9k
    if (pxd->context == NULL) {
112
0
        SCFree(pxd);
113
0
        SCReturnInt(-1);
114
0
    }
115
80.9k
    pcre2_set_match_limit(pxd->context, SC_MATCH_LIMIT_DEFAULT);
116
80.9k
    pcre2_set_recursion_limit(pxd->context, SC_MATCH_LIMIT_RECURSION_DEFAULT);
117
80.9k
    int en;
118
80.9k
    PCRE2_SIZE eo;
119
80.9k
    pxd->regex = pcre2_compile((PCRE2_SPTR8)regexstr, PCRE2_ZERO_TERMINATED, 0, &en, &eo, NULL);
120
80.9k
    if (pxd->regex == NULL) {
121
5.56k
        PCRE2_UCHAR buffer[256];
122
5.56k
        pcre2_get_error_message(en, buffer, sizeof(buffer));
123
5.56k
        SCLogError("pcre2 compile of \"%s\" failed at "
124
5.56k
                   "offset %d: %s",
125
5.56k
                regexstr, (int)eo, buffer);
126
5.56k
        pcre2_match_context_free(pxd->context);
127
5.56k
        SCFree(pxd);
128
5.56k
        SCReturnInt(-1);
129
5.56k
    }
130
131
    // check pcd->regex has exactly one capture expression
132
75.3k
    uint32_t nb;
133
75.3k
    if (pcre2_pattern_info(pxd->regex, PCRE2_INFO_CAPTURECOUNT, &nb) < 0) {
134
0
        SCLogError("pcrexform failed getting info about capturecount");
135
0
        DetectTransformPcrexformFree(de_ctx, pxd);
136
0
        SCReturnInt(-1);
137
0
    }
138
75.3k
    if (nb != 1) {
139
1.13k
        SCLogError("pcrexform needs exactly one substring capture, found %" PRIu32, nb);
140
1.13k
        DetectTransformPcrexformFree(de_ctx, pxd);
141
1.13k
        SCReturnInt(-1);
142
1.13k
    }
143
144
74.2k
    pxd->id_data = (uint8_t *)SCStrdup(regexstr);
145
74.2k
    if (pxd->id_data == NULL) {
146
0
        DetectTransformPcrexformFree(de_ctx, pxd);
147
0
        SCReturnInt(-1);
148
0
    }
149
74.2k
    pxd->id_data_len = (uint32_t)strlen(regexstr);
150
151
74.2k
    int r = SCDetectSignatureAddTransform(s, DETECT_TRANSFORM_PCREXFORM, pxd);
152
74.2k
    if (r != 0) {
153
466
        DetectTransformPcrexformFree(de_ctx, pxd);
154
466
    }
155
156
74.2k
    SCReturnInt(r);
157
74.2k
}
158
159
static void DetectTransformPcrexform(
160
        DetectEngineThreadCtx *det_ctx, InspectionBuffer *buffer, void *options)
161
3.85k
{
162
3.85k
    const char *input = (const char *)buffer->inspect;
163
3.85k
    const uint32_t input_len = buffer->inspect_len;
164
3.85k
    DetectTransformPcrexformData *pxd = options;
165
166
3.85k
    pcre2_match_data *match = pcre2_match_data_create_from_pattern(pxd->regex, NULL);
167
3.85k
    int ret = pcre2_match(pxd->regex, (PCRE2_SPTR8)input, input_len, 0, 0, match, pxd->context);
168
169
3.85k
    if (ret > 0) {
170
36
        const char *str;
171
36
        PCRE2_SIZE caplen;
172
36
        ret = pcre2_substring_get_bynumber(match, 1, (PCRE2_UCHAR8 **)&str, &caplen);
173
174
36
        if (ret >= 0) {
175
36
            InspectionBufferCopy(buffer, (uint8_t *)str, (uint32_t)caplen);
176
36
            pcre2_substring_free((PCRE2_UCHAR8 *)str);
177
36
        }
178
36
    }
179
3.85k
    pcre2_match_data_free(match);
180
3.85k
}
181
182
#ifdef UNITTESTS
183
#include "tests/detect-transform-pcrexform.c"
184
#endif