/src/suricata7/src/detect-mqtt-subscribe-topic.c
Line | Count | Source |
1 | | /* Copyright (C) 2020-2022 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 Sascha Steinbiss <sascha@steinbiss.name> |
22 | | */ |
23 | | |
24 | | #include "suricata-common.h" |
25 | | |
26 | | #include "app-layer.h" |
27 | | #include "app-layer-parser.h" |
28 | | |
29 | | #include "conf.h" |
30 | | #include "decode.h" |
31 | | #include "detect.h" |
32 | | #include "detect-content.h" |
33 | | #include "detect-parse.h" |
34 | | #include "detect-pcre.h" |
35 | | #include "detect-engine.h" |
36 | | #include "detect-engine-content-inspection.h" |
37 | | #include "detect-engine-mpm.h" |
38 | | #include "detect-engine-prefilter.h" |
39 | | #include "detect-mqtt-subscribe-topic.h" |
40 | | #include "util-unittest.h" |
41 | | #include "util-unittest-helper.h" |
42 | | |
43 | | #include "rust-bindings.h" |
44 | | |
45 | | #include "threads.h" |
46 | | |
47 | | #include "flow.h" |
48 | | #include "flow-util.h" |
49 | | #include "flow-var.h" |
50 | | |
51 | | #include "util-debug.h" |
52 | | #include "util-spm.h" |
53 | | #include "util-print.h" |
54 | | #include "util-profiling.h" |
55 | | |
56 | | static int DetectMQTTSubscribeTopicSetup(DetectEngineCtx *, Signature *, const char *); |
57 | | |
58 | | static int g_mqtt_subscribe_topic_buffer_id = 0; |
59 | | |
60 | | static uint32_t subscribe_topic_match_limit = 100; |
61 | | |
62 | | struct MQTTSubscribeTopicGetDataArgs { |
63 | | uint32_t local_id; |
64 | | void *txv; |
65 | | }; |
66 | | |
67 | | static InspectionBuffer *MQTTSubscribeTopicGetData(DetectEngineThreadCtx *det_ctx, |
68 | | const DetectEngineTransforms *transforms, Flow *f, |
69 | | struct MQTTSubscribeTopicGetDataArgs *cbdata, int list_id) |
70 | 11 | { |
71 | 11 | SCEnter(); |
72 | | |
73 | 11 | InspectionBuffer *buffer = |
74 | 11 | InspectionBufferMultipleForListGet(det_ctx, list_id, cbdata->local_id); |
75 | 11 | if (buffer == NULL) |
76 | 0 | return NULL; |
77 | 11 | if (buffer->initialized) |
78 | 0 | return buffer; |
79 | | |
80 | 11 | const uint8_t *data; |
81 | 11 | uint32_t data_len; |
82 | 11 | if (rs_mqtt_tx_get_subscribe_topic(cbdata->txv, cbdata->local_id, &data, &data_len) == 0) { |
83 | 11 | InspectionBufferSetupMultiEmpty(buffer); |
84 | 11 | return NULL; |
85 | 11 | } |
86 | | |
87 | 0 | InspectionBufferSetupMulti(buffer, transforms, data, data_len); |
88 | |
|
89 | 0 | SCReturnPtr(buffer, "InspectionBuffer"); |
90 | 11 | } |
91 | | |
92 | | static uint8_t DetectEngineInspectMQTTSubscribeTopic(DetectEngineCtx *de_ctx, |
93 | | DetectEngineThreadCtx *det_ctx, const DetectEngineAppInspectionEngine *engine, |
94 | | const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id) |
95 | 0 | { |
96 | 0 | uint32_t local_id = 0; |
97 | |
|
98 | 0 | const DetectEngineTransforms *transforms = NULL; |
99 | 0 | if (!engine->mpm) { |
100 | 0 | transforms = engine->v2.transforms; |
101 | 0 | } |
102 | |
|
103 | 0 | while ((subscribe_topic_match_limit == 0) || local_id < subscribe_topic_match_limit) { |
104 | 0 | struct MQTTSubscribeTopicGetDataArgs cbdata = { local_id, txv, }; |
105 | 0 | InspectionBuffer *buffer = |
106 | 0 | MQTTSubscribeTopicGetData(det_ctx, transforms, f, &cbdata, engine->sm_list); |
107 | 0 | if (buffer == NULL || buffer->inspect == NULL) |
108 | 0 | break; |
109 | | |
110 | 0 | det_ctx->buffer_offset = 0; |
111 | 0 | det_ctx->discontinue_matching = 0; |
112 | 0 | det_ctx->inspection_recursion_counter = 0; |
113 | |
|
114 | 0 | const int match = DetectEngineContentInspection(de_ctx, det_ctx, s, engine->smd, |
115 | 0 | NULL, f, |
116 | 0 | (uint8_t *)buffer->inspect, |
117 | 0 | buffer->inspect_len, |
118 | 0 | buffer->inspect_offset, DETECT_CI_FLAGS_SINGLE, |
119 | 0 | DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE); |
120 | 0 | if (match == 1) { |
121 | 0 | return DETECT_ENGINE_INSPECT_SIG_MATCH; |
122 | 0 | } |
123 | 0 | local_id++; |
124 | 0 | } |
125 | 0 | return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; |
126 | 0 | } |
127 | | |
128 | | typedef struct PrefilterMpmMQTTSubscribeTopic { |
129 | | int list_id; |
130 | | const MpmCtx *mpm_ctx; |
131 | | const DetectEngineTransforms *transforms; |
132 | | } PrefilterMpmMQTTSubscribeTopic; |
133 | | |
134 | | /** \brief MQTTSubscribeTopic MQTTSubscribeTopic Mpm prefilter callback |
135 | | * |
136 | | * \param det_ctx detection engine thread ctx |
137 | | * \param p packet to inspect |
138 | | * \param f flow to inspect |
139 | | * \param txv tx to inspect |
140 | | * \param pectx inspection context |
141 | | */ |
142 | | static void PrefilterTxMQTTSubscribeTopic(DetectEngineThreadCtx *det_ctx, const void *pectx, |
143 | | Packet *p, Flow *f, void *txv, const uint64_t idx, const AppLayerTxData *_txd, |
144 | | const uint8_t flags) |
145 | 11 | { |
146 | 11 | SCEnter(); |
147 | | |
148 | 11 | const PrefilterMpmMQTTSubscribeTopic *ctx = (const PrefilterMpmMQTTSubscribeTopic *)pectx; |
149 | 11 | const MpmCtx *mpm_ctx = ctx->mpm_ctx; |
150 | 11 | const int list_id = ctx->list_id; |
151 | | |
152 | 11 | uint32_t local_id = 0; |
153 | 11 | while ((subscribe_topic_match_limit == 0) || local_id < subscribe_topic_match_limit) { |
154 | 11 | struct MQTTSubscribeTopicGetDataArgs cbdata = { local_id, txv }; |
155 | 11 | InspectionBuffer *buffer = |
156 | 11 | MQTTSubscribeTopicGetData(det_ctx, ctx->transforms, f, &cbdata, list_id); |
157 | 11 | if (buffer == NULL) |
158 | 11 | break; |
159 | | |
160 | 0 | if (buffer->inspect_len >= mpm_ctx->minlen) { |
161 | 0 | (void)mpm_table[mpm_ctx->mpm_type].Search(mpm_ctx, |
162 | 0 | &det_ctx->mtcu, &det_ctx->pmq, |
163 | 0 | buffer->inspect, buffer->inspect_len); |
164 | 0 | PREFILTER_PROFILING_ADD_BYTES(det_ctx, buffer->inspect_len); |
165 | 0 | } |
166 | 0 | local_id++; |
167 | 0 | } |
168 | 11 | } |
169 | | |
170 | | static void PrefilterMpmMQTTSubscribeTopicFree(void *ptr) |
171 | 65 | { |
172 | 65 | if (ptr != NULL) |
173 | 65 | SCFree(ptr); |
174 | 65 | } |
175 | | |
176 | | static int PrefilterMpmMQTTSubscribeTopicRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, |
177 | | MpmCtx *mpm_ctx, const DetectBufferMpmRegistry *mpm_reg, int list_id) |
178 | 65 | { |
179 | 65 | PrefilterMpmMQTTSubscribeTopic *pectx = SCCalloc(1, sizeof(*pectx)); |
180 | 65 | if (pectx == NULL) |
181 | 0 | return -1; |
182 | 65 | pectx->list_id = list_id; |
183 | 65 | pectx->mpm_ctx = mpm_ctx; |
184 | 65 | pectx->transforms = &mpm_reg->transforms; |
185 | | |
186 | 65 | return PrefilterAppendTxEngine(de_ctx, sgh, PrefilterTxMQTTSubscribeTopic, |
187 | 65 | mpm_reg->app_v2.alproto, mpm_reg->app_v2.tx_min_progress, |
188 | 65 | pectx, PrefilterMpmMQTTSubscribeTopicFree, mpm_reg->pname); |
189 | 65 | } |
190 | | |
191 | | /** |
192 | | * \brief Registration function for keyword: mqtt.subscribe.topic |
193 | | */ |
194 | | void DetectMQTTSubscribeTopicRegister (void) |
195 | 34 | { |
196 | 34 | sigmatch_table[DETECT_AL_MQTT_SUBSCRIBE_TOPIC].name = "mqtt.subscribe.topic"; |
197 | 34 | sigmatch_table[DETECT_AL_MQTT_SUBSCRIBE_TOPIC].desc = "sticky buffer to match MQTT SUBSCRIBE topic"; |
198 | 34 | sigmatch_table[DETECT_AL_MQTT_SUBSCRIBE_TOPIC].url = "/rules/mqtt-keywords.html#mqtt-subscribe-topic"; |
199 | 34 | sigmatch_table[DETECT_AL_MQTT_SUBSCRIBE_TOPIC].Setup = DetectMQTTSubscribeTopicSetup; |
200 | 34 | sigmatch_table[DETECT_AL_MQTT_SUBSCRIBE_TOPIC].flags |= SIGMATCH_NOOPT; |
201 | 34 | sigmatch_table[DETECT_AL_MQTT_SUBSCRIBE_TOPIC].flags |= SIGMATCH_INFO_STICKY_BUFFER; |
202 | | |
203 | 34 | intmax_t val = 0; |
204 | 34 | if (ConfGetInt("app-layer.protocols.mqtt.subscribe-topic-match-limit", &val)) { |
205 | 0 | subscribe_topic_match_limit = val; |
206 | 0 | } |
207 | 34 | if (subscribe_topic_match_limit <= 0) { |
208 | 0 | SCLogDebug("Using unrestricted MQTT SUBSCRIBE topic matching"); |
209 | 34 | } else { |
210 | 34 | SCLogDebug("Using MQTT SUBSCRIBE topic match-limit setting of: %u", |
211 | 34 | subscribe_topic_match_limit); |
212 | 34 | } |
213 | | |
214 | 34 | DetectAppLayerMpmRegister2("mqtt.subscribe.topic", SIG_FLAG_TOSERVER, 1, |
215 | 34 | PrefilterMpmMQTTSubscribeTopicRegister, NULL, |
216 | 34 | ALPROTO_MQTT, 1); |
217 | 34 | DetectAppLayerMpmRegister2("mqtt.subscribe.topic", SIG_FLAG_TOCLIENT, 1, |
218 | 34 | PrefilterMpmMQTTSubscribeTopicRegister, NULL, ALPROTO_MQTT, 1); |
219 | | |
220 | 34 | DetectAppLayerInspectEngineRegister2("mqtt.subscribe.topic", |
221 | 34 | ALPROTO_MQTT, SIG_FLAG_TOSERVER, 1, |
222 | 34 | DetectEngineInspectMQTTSubscribeTopic, NULL); |
223 | 34 | DetectAppLayerInspectEngineRegister2("mqtt.subscribe.topic", ALPROTO_MQTT, SIG_FLAG_TOCLIENT, 1, |
224 | 34 | DetectEngineInspectMQTTSubscribeTopic, NULL); |
225 | | |
226 | 34 | DetectBufferTypeSetDescriptionByName("mqtt.subscribe.topic", |
227 | 34 | "subscribe topic query"); |
228 | | |
229 | 34 | g_mqtt_subscribe_topic_buffer_id = DetectBufferTypeGetByName("mqtt.subscribe.topic"); |
230 | | |
231 | 34 | DetectBufferTypeSupportsMultiInstance("mqtt.subscribe.topic"); |
232 | 34 | } |
233 | | |
234 | | /** |
235 | | * \brief setup the sticky buffer keyword used in the rule |
236 | | * |
237 | | * \param de_ctx Pointer to the Detection Engine Context |
238 | | * \param s Pointer to the Signature to which the current keyword belongs |
239 | | * \param str Should hold an empty string always |
240 | | * |
241 | | * \retval 0 On success |
242 | | * \retval -1 On failure |
243 | | */ |
244 | | |
245 | | static int DetectMQTTSubscribeTopicSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str) |
246 | 66 | { |
247 | 66 | if (DetectBufferSetActiveList(de_ctx, s, g_mqtt_subscribe_topic_buffer_id) < 0) |
248 | 2 | return -1; |
249 | 64 | if (DetectSignatureSetAppProto(s, ALPROTO_MQTT) < 0) |
250 | 17 | return -1; |
251 | 47 | return 0; |
252 | 64 | } |