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