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 Sascha Steinbiss <sascha@steinbiss.name>
22  */
23 
24 #include "suricata-common.h"
25 #include "conf.h"
26 #include "detect.h"
27 #include "detect-parse.h"
28 #include "detect-engine.h"
29 #include "detect-engine-content-inspection.h"
30 #include "detect-mqtt-connack-sessionpresent.h"
31 #include "util-unittest.h"
32 
33 #include "rust-bindings.h"
34 
35 #define PARSE_REGEX "^true|false|yes|no$"
36 static DetectParseRegex parse_regex;
37 
38 static int mqtt_connack_session_present_id = 0;
39 
40 static int DetectMQTTConnackSessionPresentMatch(DetectEngineThreadCtx *det_ctx,
41                                Flow *f, uint8_t flags, void *state,
42                                void *txv, const Signature *s,
43                                const SigMatchCtx *ctx);
44 static int DetectMQTTConnackSessionPresentSetup (DetectEngineCtx *, Signature *, const char *);
45 void MQTTConnackSessionPresentRegisterTests(void);
46 void DetectMQTTConnackSessionPresentFree(DetectEngineCtx *de_ctx, void *);
47 
48 static int DetectEngineInspectMQTTConnackSessionPresentGeneric(ThreadVars *tv,
49         DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
50         const Signature *s, const SigMatchData *smd,
51         Flow *f, uint8_t flags, void *alstate,
52         void *txv, uint64_t tx_id);
53 
54 /**
55  * \brief Registration function for mqtt.connack.session_present: keyword
56  */
DetectMQTTConnackSessionPresentRegister(void)57 void DetectMQTTConnackSessionPresentRegister (void)
58 {
59     sigmatch_table[DETECT_AL_MQTT_CONNACK_SESSION_PRESENT].name = "mqtt.connack.session_present";
60     sigmatch_table[DETECT_AL_MQTT_CONNACK_SESSION_PRESENT].desc = "match MQTT CONNACK session present flag";
61     sigmatch_table[DETECT_AL_MQTT_CONNACK_SESSION_PRESENT].url = "/rules/mqtt-keywords.html#mqtt-connack-session-present";
62     sigmatch_table[DETECT_AL_MQTT_CONNACK_SESSION_PRESENT].AppLayerTxMatch = DetectMQTTConnackSessionPresentMatch;
63     sigmatch_table[DETECT_AL_MQTT_CONNACK_SESSION_PRESENT].Setup = DetectMQTTConnackSessionPresentSetup;
64     sigmatch_table[DETECT_AL_MQTT_CONNACK_SESSION_PRESENT].Free  = DetectMQTTConnackSessionPresentFree;
65 #ifdef UNITTESTS
66     sigmatch_table[DETECT_AL_MQTT_CONNACK_SESSION_PRESENT].RegisterTests = MQTTConnackSessionPresentRegisterTests;
67 #endif
68 
69     DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
70 
71     DetectAppLayerInspectEngineRegister("mqtt.connack.session_present",
72             ALPROTO_MQTT, SIG_FLAG_TOSERVER, 1,
73             DetectEngineInspectMQTTConnackSessionPresentGeneric);
74 
75     mqtt_connack_session_present_id = DetectBufferTypeGetByName("mqtt.connack.session_present");
76 }
77 
DetectEngineInspectMQTTConnackSessionPresentGeneric(ThreadVars * tv,DetectEngineCtx * de_ctx,DetectEngineThreadCtx * det_ctx,const Signature * s,const SigMatchData * smd,Flow * f,uint8_t flags,void * alstate,void * txv,uint64_t tx_id)78 static int DetectEngineInspectMQTTConnackSessionPresentGeneric(ThreadVars *tv,
79         DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
80         const Signature *s, const SigMatchData *smd,
81         Flow *f, uint8_t flags, void *alstate,
82         void *txv, uint64_t tx_id)
83 {
84     return DetectEngineInspectGenericList(tv, de_ctx, det_ctx, s, smd,
85                                           f, flags, alstate, txv, tx_id);
86 }
87 
88 /**
89  * \internal
90  * \brief Function to match session_present flag of an MQTT CONNACK message
91  *
92  * \param det_ctx Pointer to the pattern matcher thread.
93  * \param f       Pointer to the current flow.
94  * \param flags   Flags.
95  * \param state   App layer state.
96  * \param txv     Pointer to the transaction.
97  * \param s       Pointer to the Signature.
98  * \param ctx     Pointer to the sigmatch that we will cast into DetectMQTTConnackSessionPresentData.
99  *
100  * \retval 0 no match.
101  * \retval 1 match.
102  */
DetectMQTTConnackSessionPresentMatch(DetectEngineThreadCtx * det_ctx,Flow * f,uint8_t flags,void * state,void * txv,const Signature * s,const SigMatchCtx * ctx)103 static int DetectMQTTConnackSessionPresentMatch(DetectEngineThreadCtx *det_ctx,
104                                Flow *f, uint8_t flags, void *state,
105                                void *txv, const Signature *s,
106                                const SigMatchCtx *ctx)
107 {
108     const bool *de = (const bool *)ctx;
109     bool value = false;
110 
111     if (!de)
112         return 0;
113 
114     if (rs_mqtt_tx_get_connack_sessionpresent(txv, &value) ==0 ) {
115         return 0;
116     }
117     if (value != *de) {
118         return 0;
119     }
120 
121     return 1;
122 }
123 
124 /**
125  * \internal
126  * \brief This function is used to parse options passed via mqtt.connack.session_present: keyword
127  *
128  * \param rawstr Pointer to the user provided options
129  *
130  * \retval de pointer to DetectMQTTConnackSessionPresentData on success
131  * \retval NULL on failure
132  */
DetectMQTTConnackSessionPresentParse(const char * rawstr)133 static bool *DetectMQTTConnackSessionPresentParse(const char *rawstr)
134 {
135     bool *de = NULL;
136     de = SCMalloc(sizeof(bool));
137     if (unlikely(de == NULL))
138         return NULL;
139     *de = false;
140 
141     if (strcmp(rawstr, "yes") == 0) {
142         *de = true;
143     } else if (strcmp(rawstr, "true") == 0) {
144         *de = true;
145     } else if (strcmp(rawstr, "no") == 0) {
146         *de = false;
147     } else if (strcmp(rawstr, "false") == 0) {
148         *de = false;
149     } else {
150         SCLogError(SC_ERR_UNKNOWN_VALUE, "invalid session_present flag definition: %s", rawstr);
151         goto error;
152     }
153 
154     return de;
155 
156 error:
157     /* de can't be NULL here */
158     SCFree(de);
159     return NULL;
160 }
161 
162 /**
163  * \internal
164  * \brief this function is used to add the parsed type query into the current signature
165  *
166  * \param de_ctx pointer to the Detection Engine Context
167  * \param s pointer to the Current Signature
168  * \param rawstr pointer to the user provided options
169  *
170  * \retval 0 on Success
171  * \retval -1 on Failure
172  */
DetectMQTTConnackSessionPresentSetup(DetectEngineCtx * de_ctx,Signature * s,const char * rawstr)173 static int DetectMQTTConnackSessionPresentSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
174 {
175     bool *de = NULL;
176     SigMatch *sm = NULL;
177 
178     if (DetectSignatureSetAppProto(s, ALPROTO_MQTT) < 0)
179         return -1;
180 
181     de = DetectMQTTConnackSessionPresentParse(rawstr);
182     if (de == NULL)
183         goto error;
184 
185     sm = SigMatchAlloc();
186     if (sm == NULL)
187         goto error;
188 
189     sm->type = DETECT_AL_MQTT_CONNACK_SESSION_PRESENT;
190     sm->ctx = (SigMatchCtx *)de;
191 
192     SigMatchAppendSMToList(s, sm, mqtt_connack_session_present_id);
193 
194     return 0;
195 
196 error:
197     if (de != NULL)
198         SCFree(de);
199     if (sm != NULL)
200         SCFree(sm);
201     return -1;
202 }
203 
204 /**
205  * \internal
206  * \brief this function will free memory associated with DetectMQTTConnackSessionPresentData
207  *
208  * \param de pointer to DetectMQTTConnackSessionPresentData
209  */
DetectMQTTConnackSessionPresentFree(DetectEngineCtx * de_ctx,void * de_ptr)210 void DetectMQTTConnackSessionPresentFree(DetectEngineCtx *de_ctx, void *de_ptr)
211 {
212     if (de_ptr != NULL)
213         SCFree(de_ptr);
214 }
215 
216 /*
217  * ONLY TESTS BELOW THIS COMMENT
218  */
219 
220 #ifdef UNITTESTS
221 /**
222  * \test MQTTConnackSessionPresentTestParse01 is a test for a valid value
223  *
224  *  \retval 1 on success
225  *  \retval 0 on failure
226  */
MQTTConnackSessionPresentTestParse01(void)227 static int MQTTConnackSessionPresentTestParse01 (void)
228 {
229     bool *de = NULL;
230 
231     de = DetectMQTTConnackSessionPresentParse("yes");
232     FAIL_IF_NULL(de);
233     DetectMQTTConnackSessionPresentFree(NULL, de);
234 
235     de = DetectMQTTConnackSessionPresentParse("true");
236     FAIL_IF_NULL(de);
237     DetectMQTTConnackSessionPresentFree(NULL, de);
238 
239     de = DetectMQTTConnackSessionPresentParse("false");
240     FAIL_IF_NULL(de);
241     DetectMQTTConnackSessionPresentFree(NULL, de);
242 
243     de = DetectMQTTConnackSessionPresentParse("no");
244     FAIL_IF_NULL(de);
245     DetectMQTTConnackSessionPresentFree(NULL, de);
246 
247     PASS;
248 }
249 
250 /**
251  * \test MQTTConnackSessionPresentTestParse02 is a test for an invalid value
252  *
253  *  \retval 1 on success
254  *  \retval 0 on failure
255  */
MQTTConnackSessionPresentTestParse02(void)256 static int MQTTConnackSessionPresentTestParse02 (void)
257 {
258     bool *de = NULL;
259     de = DetectMQTTConnackSessionPresentParse("nix");
260     if (de) {
261         DetectMQTTConnackSessionPresentFree(NULL, de);
262         FAIL;
263     }
264 
265     PASS;
266 }
267 
268 /**
269  * \test MQTTConnackSessionPresentTestParse03 is a test for an invalid value
270  *
271  *  \retval 1 on success
272  *  \retval 0 on failure
273  */
MQTTConnackSessionPresentTestParse03(void)274 static int MQTTConnackSessionPresentTestParse03 (void)
275 {
276     bool *de = NULL;
277     de = DetectMQTTConnackSessionPresentParse("");
278     if (de) {
279         DetectMQTTConnackSessionPresentFree(NULL, de);
280         FAIL;
281     }
282 
283     PASS;
284 }
285 
286 /**
287  * \test MQTTConnackSessionPresentTestParse04 is a test for an invalid value
288  *
289  *  \retval 1 on success
290  *  \retval 0 on failure
291  */
MQTTConnackSessionPresentTestParse04(void)292 static int MQTTConnackSessionPresentTestParse04 (void)
293 {
294     bool *de = NULL;
295     de = DetectMQTTConnackSessionPresentParse(",");
296     if (de) {
297         DetectMQTTConnackSessionPresentFree(NULL, de);
298         FAIL;
299     }
300 
301     PASS;
302 }
303 
304 
305 #endif /* UNITTESTS */
306 
307 /**
308  * \brief this function registers unit tests for MQTTConnackSessionPresent
309  */
MQTTConnackSessionPresentRegisterTests(void)310 void MQTTConnackSessionPresentRegisterTests(void)
311 {
312 #ifdef UNITTESTS
313     UtRegisterTest("MQTTConnackSessionPresentTestParse01", MQTTConnackSessionPresentTestParse01);
314     UtRegisterTest("MQTTConnackSessionPresentTestParse02", MQTTConnackSessionPresentTestParse02);
315     UtRegisterTest("MQTTConnackSessionPresentTestParse03", MQTTConnackSessionPresentTestParse03);
316     UtRegisterTest("MQTTConnackSessionPresentTestParse04", MQTTConnackSessionPresentTestParse04);
317 #endif /* UNITTESTS */
318 }