1 /* Copyright (C) 2007-2021 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 #include "suricata-common.h"
19
20 #include "detect.h"
21 #include "detect-engine-alert.h"
22 #include "detect-engine-threshold.h"
23 #include "detect-engine-tag.h"
24
25 #include "decode.h"
26
27 #include "flow.h"
28 #include "flow-private.h"
29
30 #include "util-profiling.h"
31
32 /** tag signature we use for tag alerts */
33 static Signature g_tag_signature;
34 /** tag packet alert structure for tag alerts */
35 static PacketAlert g_tag_pa;
36
PacketAlertTagInit(void)37 void PacketAlertTagInit(void)
38 {
39 memset(&g_tag_signature, 0x00, sizeof(g_tag_signature));
40
41 g_tag_signature.id = TAG_SIG_ID;
42 g_tag_signature.gid = TAG_SIG_GEN;
43 g_tag_signature.num = TAG_SIG_ID;
44 g_tag_signature.rev = 1;
45 g_tag_signature.prio = 2;
46
47 memset(&g_tag_pa, 0x00, sizeof(g_tag_pa));
48
49 g_tag_pa.action = ACTION_ALERT;
50 g_tag_pa.s = &g_tag_signature;
51 }
52
PacketAlertGetTag(void)53 PacketAlert *PacketAlertGetTag(void)
54 {
55 return &g_tag_pa;
56 }
57
58 /**
59 * \brief Handle a packet and check if needs a threshold logic
60 * Also apply rule action if necessary.
61 *
62 * \param de_ctx Detection Context
63 * \param sig Signature pointer
64 * \param p Packet structure
65 *
66 * \retval 1 alert is not suppressed
67 * \retval 0 alert is suppressed
68 */
PacketAlertHandle(DetectEngineCtx * de_ctx,DetectEngineThreadCtx * det_ctx,const Signature * s,Packet * p,PacketAlert * pa)69 static int PacketAlertHandle(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
70 const Signature *s, Packet *p, PacketAlert *pa)
71 {
72 SCEnter();
73 int ret = 1;
74 const DetectThresholdData *td = NULL;
75 const SigMatchData *smd;
76
77 if (!(PKT_IS_IPV4(p) || PKT_IS_IPV6(p))) {
78 SCReturnInt(1);
79 }
80
81 /* handle suppressions first */
82 if (s->sm_arrays[DETECT_SM_LIST_SUPPRESS] != NULL) {
83 KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_SUPPRESS);
84 smd = NULL;
85 do {
86 td = SigGetThresholdTypeIter(s, &smd, DETECT_SM_LIST_SUPPRESS);
87 if (td != NULL) {
88 SCLogDebug("td %p", td);
89
90 /* PacketAlertThreshold returns 2 if the alert is suppressed but
91 * we do need to apply rule actions to the packet. */
92 KEYWORD_PROFILING_START;
93 ret = PacketAlertThreshold(de_ctx, det_ctx, td, p, s, pa);
94 if (ret == 0 || ret == 2) {
95 KEYWORD_PROFILING_END(det_ctx, DETECT_THRESHOLD, 0);
96 /* It doesn't match threshold, remove it */
97 SCReturnInt(ret);
98 }
99 KEYWORD_PROFILING_END(det_ctx, DETECT_THRESHOLD, 1);
100 }
101 } while (smd != NULL);
102 }
103
104 /* if we're still here, consider thresholding */
105 if (s->sm_arrays[DETECT_SM_LIST_THRESHOLD] != NULL) {
106 KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_THRESHOLD);
107 smd = NULL;
108 do {
109 td = SigGetThresholdTypeIter(s, &smd, DETECT_SM_LIST_THRESHOLD);
110 if (td != NULL) {
111 SCLogDebug("td %p", td);
112
113 /* PacketAlertThreshold returns 2 if the alert is suppressed but
114 * we do need to apply rule actions to the packet. */
115 KEYWORD_PROFILING_START;
116 ret = PacketAlertThreshold(de_ctx, det_ctx, td, p, s, pa);
117 if (ret == 0 || ret == 2) {
118 KEYWORD_PROFILING_END(det_ctx, DETECT_THRESHOLD ,0);
119 /* It doesn't match threshold, remove it */
120 SCReturnInt(ret);
121 }
122 KEYWORD_PROFILING_END(det_ctx, DETECT_THRESHOLD, 1);
123 }
124 } while (smd != NULL);
125 }
126 SCReturnInt(1);
127 }
128
129
130 /**
131 * \brief Check if a certain sid alerted, this is used in the test functions
132 *
133 * \param p Packet on which we want to check if the signature alerted or not
134 * \param sid Signature id of the signature that thas to be checked for a match
135 *
136 * \retval match A value > 0 on a match; 0 on no match
137 */
PacketAlertCheck(Packet * p,uint32_t sid)138 int PacketAlertCheck(Packet *p, uint32_t sid)
139 {
140 uint16_t i = 0;
141 int match = 0;
142
143 for (i = 0; i < p->alerts.cnt; i++) {
144 if (p->alerts.alerts[i].s == NULL)
145 continue;
146
147 if (p->alerts.alerts[i].s->id == sid)
148 match++;
149 }
150
151 return match;
152 }
153
154 /**
155 * \brief Remove alert from the p->alerts.alerts array at pos
156 * \param p Pointer to the Packet
157 * \param pos Position in the array
158 * \retval 0 if the number of alerts is less than pos
159 * 1 if all goes well
160 */
PacketAlertRemove(Packet * p,uint16_t pos)161 int PacketAlertRemove(Packet *p, uint16_t pos)
162 {
163 uint16_t i = 0;
164 int match = 0;
165
166 if (pos > p->alerts.cnt) {
167 SCLogDebug("removing %u failed, pos > cnt %u", pos, p->alerts.cnt);
168 return 0;
169 }
170
171 for (i = pos; i <= p->alerts.cnt - 1; i++) {
172 memcpy(&p->alerts.alerts[i], &p->alerts.alerts[i + 1], sizeof(PacketAlert));
173 }
174
175 // Update it, since we removed 1
176 p->alerts.cnt--;
177
178 return match;
179 }
180
181 /** \brief append a signature match to a packet
182 *
183 * \param det_ctx thread detection engine ctx
184 * \param s the signature that matched
185 * \param p packet
186 * \param flags alert flags
187 */
PacketAlertAppend(DetectEngineThreadCtx * det_ctx,const Signature * s,Packet * p,uint64_t tx_id,uint8_t flags)188 int PacketAlertAppend(DetectEngineThreadCtx *det_ctx, const Signature *s,
189 Packet *p, uint64_t tx_id, uint8_t flags)
190 {
191 int i = 0;
192
193 if (p->alerts.cnt == PACKET_ALERT_MAX)
194 return 0;
195
196 SCLogDebug("sid %"PRIu32"", s->id);
197
198 /* It should be usually the last, so check it before iterating */
199 if (p->alerts.cnt == 0 || (p->alerts.cnt > 0 &&
200 p->alerts.alerts[p->alerts.cnt - 1].num < s->num)) {
201 /* We just add it */
202 p->alerts.alerts[p->alerts.cnt].num = s->num;
203 p->alerts.alerts[p->alerts.cnt].action = s->action;
204 p->alerts.alerts[p->alerts.cnt].flags = flags;
205 p->alerts.alerts[p->alerts.cnt].s = s;
206 p->alerts.alerts[p->alerts.cnt].tx_id = tx_id;
207 } else {
208 /* We need to make room for this s->num
209 (a bit ugly with memcpy but we are planning changes here)*/
210 for (i = p->alerts.cnt - 1; i >= 0 && p->alerts.alerts[i].num > s->num; i--) {
211 memcpy(&p->alerts.alerts[i + 1], &p->alerts.alerts[i], sizeof(PacketAlert));
212 }
213
214 i++; /* The right place to store the alert */
215
216 p->alerts.alerts[i].num = s->num;
217 p->alerts.alerts[i].action = s->action;
218 p->alerts.alerts[i].flags = flags;
219 p->alerts.alerts[i].s = s;
220 p->alerts.alerts[i].tx_id = tx_id;
221 }
222
223 /* Update the count */
224 p->alerts.cnt++;
225
226 return 0;
227 }
228
RuleActionToFlow(const uint8_t action,Flow * f)229 static inline void RuleActionToFlow(const uint8_t action, Flow *f)
230 {
231 if (action & (ACTION_DROP | ACTION_REJECT_ANY | ACTION_PASS)) {
232 if (f->flags & (FLOW_ACTION_DROP | FLOW_ACTION_PASS)) {
233 /* drop or pass already set. First to set wins. */
234 SCLogDebug("not setting %s flow already set to %s",
235 (action & ACTION_PASS) ? "pass" : "drop",
236 (f->flags & FLOW_ACTION_DROP) ? "drop" : "pass");
237 } else {
238 if (action & (ACTION_DROP | ACTION_REJECT_ANY)) {
239 f->flags |= FLOW_ACTION_DROP;
240 SCLogDebug("setting flow action drop");
241 }
242 if (action & ACTION_PASS) {
243 f->flags |= FLOW_ACTION_PASS;
244 SCLogDebug("setting flow action pass");
245 FlowSetNoPacketInspectionFlag(f);
246 }
247 }
248 }
249 }
250
251 /** \brief Apply action(s) and Set 'drop' sig info,
252 * if applicable */
PacketApplySignatureActions(Packet * p,const Signature * s,const uint8_t alert_flags)253 static void PacketApplySignatureActions(Packet *p, const Signature *s, const uint8_t alert_flags)
254 {
255 SCLogDebug("packet %" PRIu64 " sid %u action %02x alert_flags %02x", p->pcap_cnt, s->id,
256 s->action, alert_flags);
257 PACKET_UPDATE_ACTION(p, s->action);
258
259 if (s->action & ACTION_DROP) {
260 if (p->alerts.drop.action == 0) {
261 p->alerts.drop.num = s->num;
262 p->alerts.drop.action = s->action;
263 p->alerts.drop.s = (Signature *)s;
264 }
265 if ((p->flow != NULL) && (alert_flags & PACKET_ALERT_FLAG_APPLY_ACTION_TO_FLOW)) {
266 RuleActionToFlow(s->action, p->flow);
267 }
268 } else if (s->action & ACTION_PASS) {
269 if ((p->flow != NULL) && (alert_flags & PACKET_ALERT_FLAG_APPLY_ACTION_TO_FLOW)) {
270 RuleActionToFlow(s->action, p->flow);
271 }
272 }
273 }
274
275 /**
276 * \brief Check the threshold of the sigs that match, set actions, break on pass action
277 * This function iterate the packet alerts array, removing those that didn't match
278 * the threshold, and those that match after a signature with the action "pass".
279 * The array is sorted by action priority/order
280 * \param de_ctx detection engine context
281 * \param det_ctx detection engine thread context
282 * \param p pointer to the packet
283 */
PacketAlertFinalize(DetectEngineCtx * de_ctx,DetectEngineThreadCtx * det_ctx,Packet * p)284 void PacketAlertFinalize(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
285 {
286 SCEnter();
287 int i = 0;
288
289 while (i < p->alerts.cnt) {
290 const Signature *s = de_ctx->sig_array[p->alerts.alerts[i].num];
291 SCLogDebug("Sig->num: %" PRIu32 " SID %u", p->alerts.alerts[i].num, s->id);
292
293 int res = PacketAlertHandle(de_ctx, det_ctx, s, p, &p->alerts.alerts[i]);
294 if (res > 0) {
295 /* Now, if we have an alert, we have to check if we want
296 * to tag this session or src/dst host */
297 if (s->sm_arrays[DETECT_SM_LIST_TMATCH] != NULL) {
298 KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_TMATCH);
299 SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_TMATCH];
300 while (1) {
301 /* tags are set only for alerts */
302 KEYWORD_PROFILING_START;
303 sigmatch_table[smd->type].Match(det_ctx, p, (Signature *)s, smd->ctx);
304 KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
305 if (smd->is_last)
306 break;
307 smd++;
308 }
309 }
310
311 /* For DROP and PASS sigs we need to apply the action to the flow if
312 * - sig is IP or PD only
313 * - match is in applayer
314 * - match is in stream */
315 if (s->action & (ACTION_DROP | ACTION_PASS)) {
316 if ((p->alerts.alerts[i].flags &
317 (PACKET_ALERT_FLAG_STATE_MATCH | PACKET_ALERT_FLAG_STREAM_MATCH)) ||
318 (s->flags & (SIG_FLAG_IPONLY | SIG_FLAG_PDONLY | SIG_FLAG_APPLAYER))) {
319 p->alerts.alerts[i].flags |= PACKET_ALERT_FLAG_APPLY_ACTION_TO_FLOW;
320 SCLogDebug("packet %" PRIu64 " sid %u action %02x alert_flags %02x (set "
321 "PACKET_ALERT_FLAG_APPLY_ACTION_TO_FLOW)",
322 p->pcap_cnt, s->id, s->action, p->alerts.alerts[i].flags);
323 }
324 }
325
326 /* set actions on packet */
327 PacketApplySignatureActions(p, p->alerts.alerts[i].s, p->alerts.alerts[i].flags);
328
329 if (PACKET_TEST_ACTION(p, ACTION_PASS)) {
330 /* Ok, reset the alert cnt to end in the previous of pass
331 * so we ignore the rest with less prio */
332 p->alerts.cnt = i;
333 break;
334 }
335 }
336
337 /* Thresholding removes this alert */
338 if (res == 0 || res == 2 || (s->flags & SIG_FLAG_NOALERT)) {
339 PacketAlertRemove(p, i);
340
341 if (p->alerts.cnt == 0)
342 break;
343 } else {
344 i++;
345 }
346 }
347
348 /* At this point, we should have all the new alerts. Now check the tag
349 * keyword context for sessions and hosts */
350 if (!(p->flags & PKT_PSEUDO_STREAM_END))
351 TagHandlePacket(de_ctx, det_ctx, p);
352
353 /* Set flag on flow to indicate that it has alerts */
354 if (p->flow != NULL && p->alerts.cnt > 0) {
355 FlowSetHasAlertsFlag(p->flow);
356 }
357
358 }
359
360
361