/src/suricata7/src/detect-distance.c
Line | Count | Source |
1 | | /* Copyright (C) 2007-2023 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 Victor Julien <victor@inliniac.net> |
22 | | * \author Anoop Saldanha <anoopsaldanha@gmail.com> |
23 | | * |
24 | | * Implements the distance keyword |
25 | | */ |
26 | | |
27 | | #include "suricata-common.h" |
28 | | |
29 | | #include "decode.h" |
30 | | |
31 | | #include "detect.h" |
32 | | #include "detect-parse.h" |
33 | | #include "detect-engine.h" |
34 | | #include "app-layer.h" |
35 | | |
36 | | #include "detect-content.h" |
37 | | #include "detect-uricontent.h" |
38 | | #include "detect-pcre.h" |
39 | | #include "detect-byte.h" |
40 | | #include "detect-byte-extract.h" |
41 | | #include "detect-distance.h" |
42 | | |
43 | | #include "flow-var.h" |
44 | | |
45 | | #include "util-byte.h" |
46 | | #include "util-debug.h" |
47 | | #include "util-unittest.h" |
48 | | #include "detect-bytejump.h" |
49 | | #include "util-unittest-helper.h" |
50 | | |
51 | | static int DetectDistanceSetup(DetectEngineCtx *, Signature *, const char *); |
52 | | #ifdef UNITTESTS |
53 | | static void DetectDistanceRegisterTests(void); |
54 | | #endif |
55 | | |
56 | | void DetectDistanceRegister(void) |
57 | 73 | { |
58 | 73 | sigmatch_table[DETECT_DISTANCE].name = "distance"; |
59 | 73 | sigmatch_table[DETECT_DISTANCE].desc = "indicates a relation between this content keyword and the content preceding it"; |
60 | 73 | sigmatch_table[DETECT_DISTANCE].url = "/rules/payload-keywords.html#distance"; |
61 | 73 | sigmatch_table[DETECT_DISTANCE].Match = NULL; |
62 | 73 | sigmatch_table[DETECT_DISTANCE].Setup = DetectDistanceSetup; |
63 | 73 | sigmatch_table[DETECT_DISTANCE].Free = NULL; |
64 | | #ifdef UNITTESTS |
65 | | sigmatch_table[DETECT_DISTANCE].RegisterTests = DetectDistanceRegisterTests; |
66 | | #endif |
67 | 73 | } |
68 | | |
69 | | static int DetectDistanceSetup (DetectEngineCtx *de_ctx, Signature *s, |
70 | | const char *distancestr) |
71 | 123k | { |
72 | 123k | const char *str = distancestr; |
73 | | |
74 | | /* retrieve the sm to apply the distance against */ |
75 | 123k | SigMatch *pm = DetectGetLastSMFromLists(s, DETECT_CONTENT, -1); |
76 | 123k | if (pm == NULL) { |
77 | 1.08k | SCLogError("distance needs " |
78 | 1.08k | "preceding content, uricontent option, http_client_body, " |
79 | 1.08k | "http_server_body, http_header option, http_raw_header option, " |
80 | 1.08k | "http_method option, http_cookie, http_raw_uri, " |
81 | 1.08k | "http_stat_msg, http_stat_code, http_user_agent or " |
82 | 1.08k | "file_data/dce_stub_data sticky buffer option"); |
83 | 1.08k | return -1; |
84 | 1.08k | } |
85 | | |
86 | | /* verify other conditions */ |
87 | 122k | DetectContentData *cd = (DetectContentData *)pm->ctx; |
88 | 122k | if (cd->flags & DETECT_CONTENT_DISTANCE) { |
89 | 2.71k | SCLogError("can't use multiple distances for the same content."); |
90 | 2.71k | return -1; |
91 | 2.71k | } |
92 | 119k | if ((cd->flags & DETECT_CONTENT_DEPTH) || (cd->flags & DETECT_CONTENT_OFFSET)) { |
93 | 149 | SCLogError("can't use a relative " |
94 | 149 | "keyword like within/distance with a absolute " |
95 | 149 | "relative keyword like depth/offset for the same " |
96 | 149 | "content."); |
97 | 149 | return -1; |
98 | 149 | } |
99 | 119k | if (cd->flags & DETECT_CONTENT_NEGATED && cd->flags & DETECT_CONTENT_FAST_PATTERN) { |
100 | 2 | SCLogError("can't have a relative " |
101 | 2 | "negated keyword set along with a fast_pattern"); |
102 | 2 | return -1; |
103 | 2 | } |
104 | 119k | if (cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) { |
105 | 13 | SCLogError("can't have a relative " |
106 | 13 | "keyword set along with a fast_pattern:only;"); |
107 | 13 | return -1; |
108 | 13 | } |
109 | 119k | if (str[0] != '-' && isalpha((unsigned char)str[0])) { |
110 | 9.92k | DetectByteIndexType index; |
111 | 9.92k | if (!DetectByteRetrieveSMVar(str, s, &index)) { |
112 | 2.17k | SCLogError("unknown byte_ keyword var " |
113 | 2.17k | "seen in distance - %s", |
114 | 2.17k | str); |
115 | 2.17k | return -1; |
116 | 2.17k | } |
117 | 7.75k | cd->distance = index; |
118 | 7.75k | cd->flags |= DETECT_CONTENT_DISTANCE_VAR; |
119 | 109k | } else { |
120 | 109k | if ((StringParseI32RangeCheck(&cd->distance, 0, 0, str, -DETECT_CONTENT_VALUE_MAX, |
121 | 109k | DETECT_CONTENT_VALUE_MAX) < 0)) { |
122 | 1.95k | SCLogError("invalid value for distance: %s", str); |
123 | 1.95k | return -1; |
124 | 1.95k | } |
125 | 109k | } |
126 | 115k | cd->flags |= DETECT_CONTENT_DISTANCE; |
127 | | |
128 | 115k | SigMatch *prev_pm = DetectGetLastSMByListPtr(s, pm->prev, |
129 | 115k | DETECT_CONTENT, DETECT_PCRE, -1); |
130 | 115k | if (prev_pm == NULL) { |
131 | 32.9k | return 0; |
132 | 32.9k | } |
133 | | |
134 | 82.5k | if (prev_pm->type == DETECT_CONTENT) { |
135 | 74.1k | DetectContentData *prev_cd = (DetectContentData *)prev_pm->ctx; |
136 | 74.1k | if (prev_cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) { |
137 | 2 | SCLogError("previous keyword " |
138 | 2 | "has a fast_pattern:only; set. Can't " |
139 | 2 | "have relative keywords around a fast_pattern " |
140 | 2 | "only content"); |
141 | 2 | return -1; |
142 | 2 | } |
143 | 74.1k | if ((cd->flags & DETECT_CONTENT_NEGATED) == 0) { |
144 | 72.1k | prev_cd->flags |= DETECT_CONTENT_DISTANCE_NEXT; |
145 | 72.1k | } else { |
146 | 1.96k | prev_cd->flags |= DETECT_CONTENT_RELATIVE_NEXT; |
147 | 1.96k | } |
148 | 74.1k | } else if (prev_pm->type == DETECT_PCRE) { |
149 | 8.39k | DetectPcreData *pd = (DetectPcreData *)prev_pm->ctx; |
150 | 8.39k | pd->flags |= DETECT_PCRE_RELATIVE_NEXT; |
151 | 8.39k | } |
152 | | |
153 | 82.5k | return 0; |
154 | 82.5k | } |
155 | | |
156 | | #ifdef UNITTESTS |
157 | | |
158 | | static int DetectDistanceTest01(void) |
159 | | { |
160 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
161 | | FAIL_IF_NULL(de_ctx); |
162 | | de_ctx->flags |= DE_QUIET; |
163 | | |
164 | | Signature *s = DetectEngineAppendSig(de_ctx, |
165 | | "alert tcp any any -> any any (content:\"|AA BB|\"; content:\"|CC DD EE FF 00 11 22 33 " |
166 | | "44 55 66 77 88 99 AA BB CC DD EE|\"; distance: 4; within: 19; sid:1; rev:1;)"); |
167 | | FAIL_IF_NULL(s); |
168 | | |
169 | | SigMatch *sm = de_ctx->sig_list->init_data->smlists[DETECT_SM_LIST_PMATCH]; |
170 | | FAIL_IF_NULL(sm); |
171 | | |
172 | | sm = sm->next; |
173 | | FAIL_IF_NULL(sm); |
174 | | |
175 | | DetectContentData *co = (DetectContentData *)sm->ctx; |
176 | | FAIL_IF_NULL(co); |
177 | | |
178 | | FAIL_IF_NOT(co->distance = 4); |
179 | | |
180 | | /* within needs to be 23: distance + content_len as Snort auto fixes this */ |
181 | | FAIL_IF_NOT(co->within = 19); |
182 | | |
183 | | DetectEngineCtxFree(de_ctx); |
184 | | |
185 | | PASS; |
186 | | } |
187 | | |
188 | | /** |
189 | | * \test DetectDistanceTestPacket01 is a test to check matches of |
190 | | * distance works, if the previous keyword is byte_jump and content |
191 | | * (bug 163) |
192 | | */ |
193 | | static int DetectDistanceTestPacket01 (void) |
194 | | { |
195 | | uint8_t buf[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }; |
196 | | uint16_t buflen = sizeof(buf); |
197 | | Packet *p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP); |
198 | | |
199 | | FAIL_IF_NULL(p); |
200 | | char sig[] = "alert tcp any any -> any any (msg:\"suricata test\"; " |
201 | | "byte_jump:1,2; content:\"|00|\"; " |
202 | | "within:1; distance:2; sid:98711212; rev:1;)"; |
203 | | |
204 | | p->flowflags = FLOW_PKT_ESTABLISHED | FLOW_PKT_TOCLIENT; |
205 | | FAIL_IF_NOT(UTHPacketMatchSig(p, sig)); |
206 | | |
207 | | UTHFreePacket(p); |
208 | | |
209 | | PASS; |
210 | | } |
211 | | |
212 | | static void DetectDistanceRegisterTests(void) |
213 | | { |
214 | | UtRegisterTest("DetectDistanceTest01 -- distance / within mix", |
215 | | DetectDistanceTest01); |
216 | | UtRegisterTest("DetectDistanceTestPacket01", DetectDistanceTestPacket01); |
217 | | } |
218 | | #endif /* UNITTESTS */ |