1 /* $Id */
2 /*
3 ** Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
4 ** Copyright (C) 2005-2013 Sourcefire, Inc.
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU General Public License Version 2 as
8 ** published by the Free Software Foundation. You may not use, modify or
9 ** distribute this program under any other version of the GNU General
10 ** Public License.
11 **
12 ** This program is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ** GNU General Public License for more details.
16 **
17 ** You should have received a copy of the GNU General Public License
18 ** along with this program; if nto, write to the Free Software
19 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
20 ** USA
21 */
22
23 /*
24 * sp_urilen_check.c: Detection plugin to expose URI length to user rules.
25 */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include <ctype.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include "sf_types.h"
36 #include "rules.h"
37 #include "treenodes.h"
38 #include "decode.h"
39 #include "plugbase.h"
40 #include "snort_debug.h"
41 #include "parser.h"
42 #include "plugin_enum.h"
43 #include "util.h"
44 #include "sfhashfcn.h"
45 #include "mstring.h"
46
47 #include "sp_urilen_check.h"
48
49 #include "snort.h"
50 #include "profiler.h"
51 #ifdef PERF_PROFILING
52 PreprocStats urilenCheckPerfStats;
53 extern PreprocStats ruleOTNEvalPerfStats;
54 #endif
55
56 #include "sfhashfcn.h"
57 #include "detection_options.h"
58 #include "detection_util.h"
59
60 void UriLenCheckInit( struct _SnortConfig *, char*, OptTreeNode*, int );
61 void ParseUriLen( struct _SnortConfig *, char*, OptTreeNode* );
62 int CheckUriLen(void *option_data, Packet*p);
63
UriLenCheckHash(void * d)64 uint32_t UriLenCheckHash(void *d)
65 {
66 uint32_t a,b,c;
67 UriLenCheckData *data = (UriLenCheckData *)d;
68
69 a = data->urilen;
70 b = data->urilen2;
71 c = data->oper;
72
73 mix(a,b,c);
74
75 a += data->uri_buf;
76 b += RULE_OPTION_TYPE_URILEN;
77
78 final(a,b,c);
79
80 return c;
81 }
82
UriLenCheckCompare(void * l,void * r)83 int UriLenCheckCompare(void *l, void *r)
84 {
85 UriLenCheckData *left = (UriLenCheckData *)l;
86 UriLenCheckData *right = (UriLenCheckData *)r;
87
88 if (!left || !right)
89 return DETECTION_OPTION_NOT_EQUAL;
90
91 if ((left->urilen == right->urilen)
92 && (left->urilen2 == right->urilen2)
93 && (left->oper == right->oper)
94 && (left->uri_buf == right->uri_buf))
95 {
96 return DETECTION_OPTION_EQUAL;
97 }
98
99 return DETECTION_OPTION_NOT_EQUAL;
100 }
101
102
103 /* Called from plugbase to register any detection plugin keywords.
104 *
105 * PARAMETERS: None.
106 *
107 * RETURNS: Nothing.
108 */
SetupUriLenCheck(void)109 void SetupUriLenCheck(void)
110 {
111 RegisterRuleOption("urilen", UriLenCheckInit, NULL, OPT_TYPE_DETECTION, NULL);
112 #ifdef PERF_PROFILING
113 RegisterPreprocessorProfile("urilen_check", &urilenCheckPerfStats, 3, &ruleOTNEvalPerfStats, NULL);
114 #endif
115 }
116
117 /* Parses the urilen rule arguments and attaches info to
118 * the rule data structure for later use. Attaches detection
119 * function to OTN function list.
120 *
121 * PARAMETERS:
122 *
123 * argp: Rule arguments
124 * otnp: Pointer to the current rule option list node
125 * protocol: Pointer specified for the rule currently being parsed
126 *
127 * RETURNS: Nothing.
128 */
UriLenCheckInit(struct _SnortConfig * sc,char * argp,OptTreeNode * otnp,int protocol)129 void UriLenCheckInit( struct _SnortConfig *sc, char* argp, OptTreeNode* otnp, int protocol )
130 {
131 /* Sanity check(s) */
132 if ( !otnp )
133 return;
134
135 /* Check if there have been multiple urilen options specified
136 * in the same rule.
137 */
138 if ( otnp->ds_list[PLUGIN_URILEN_CHECK] )
139 {
140 FatalError("%s(%d): Multiple urilen options in rule\n",
141 file_name, file_line );
142 }
143
144 otnp->ds_list[PLUGIN_URILEN_CHECK] = SnortAlloc(sizeof(UriLenCheckData));
145
146 ParseUriLen( sc, argp, otnp );
147 }
148
149 /* Parses the urilen rule arguments and attaches the resulting
150 * parameters to the rule data structure. Based on arguments,
151 * attaches the appropriate callback/processing function
152 * to be used when the OTN is evaluated.
153 *
154 * PARAMETERS:
155 *
156 * argp: Pointer to string containing the arguments to be
157 * parsed.
158 * otnp: Pointer to the current rule option list node.
159 *
160 * RETURNS: Nothing.
161 */
ParseUriLen(struct _SnortConfig * sc,char * argp,OptTreeNode * otnp)162 void ParseUriLen( struct _SnortConfig *sc, char* argp, OptTreeNode* otnp )
163 {
164 OptFpList *fpl;
165 UriLenCheckData* datap = (UriLenCheckData*)otnp->ds_list[PLUGIN_URILEN_CHECK];
166 void *datap_dup;
167 char* curp = NULL;
168 char **toks;
169 int num_toks;
170
171 toks = mSplit(argp, ",", 2, &num_toks, '\\');
172 if (!num_toks)
173 {
174 FatalError("%s(%d): 'urilen' requires arguments.\n",
175 file_name, file_line);
176 }
177
178 curp = toks[0];
179
180 /* Parse the string */
181 if (isdigit((int)*curp) && strstr(curp, "<>"))
182 {
183 char **mtoks;
184 int num_mtoks;
185 char* endp = NULL;
186 long int val;
187
188 mtoks = mSplit(curp, "<>", 2, &num_mtoks, '\\');
189 if (num_mtoks != 2)
190 {
191 FatalError("%s(%d): Invalid 'urilen' argument.\n",
192 file_name, file_line);
193 }
194
195 val = strtol(mtoks[0], &endp, 0);
196 if ((val < 0) || *endp || (val > UINT16_MAX))
197 {
198 FatalError("%s(%d): Invalid 'urilen' argument.\n",
199 file_name, file_line);
200 }
201
202 datap->urilen = (uint16_t)val;
203
204 val = strtol(mtoks[1], &endp, 0);
205 if ((val < 0) || *endp || (val > UINT16_MAX))
206 {
207 FatalError("%s(%d): Invalid 'urilen' argument.\n",
208 file_name, file_line);
209 }
210
211 datap->urilen2 = (uint16_t)val;
212
213 if (datap->urilen2 < datap->urilen)
214 {
215 uint16_t tmp = datap->urilen;
216 datap->urilen = datap->urilen2;
217 datap->urilen2 = tmp;
218 }
219
220 datap->oper = URILEN_CHECK_RG;
221
222 mSplitFree(&mtoks, num_mtoks);
223 }
224 else
225 {
226 char* endp = NULL;
227 long int val;
228
229 if(*curp == '>')
230 {
231 curp++;
232 datap->oper = URILEN_CHECK_GT;
233 }
234 else if(*curp == '<')
235 {
236 curp++;
237 datap->oper = URILEN_CHECK_LT;
238 }
239 else
240 {
241 datap->oper = URILEN_CHECK_EQ;
242 }
243
244 while(isspace((int)*curp)) curp++;
245
246 if (!*curp)
247 {
248 FatalError("%s(%d): Invalid 'urilen' argument.\n",
249 file_name, file_line);
250 }
251
252 val = strtol(curp, &endp, 0);
253 if ((val < 0) || *endp || (val > UINT16_MAX))
254 {
255 FatalError("%s(%d): Invalid 'urilen' argument.\n",
256 file_name, file_line);
257 }
258
259 if ((datap->oper == URILEN_CHECK_LT) && (val == 0))
260 {
261 FatalError("%s(%d): Invalid 'urilen' argument.\n",
262 file_name, file_line);
263 }
264
265 datap->urilen = (uint16_t)val;
266 }
267
268 if (num_toks > 1)
269 {
270 if (!strcmp(toks[1], URI_LEN_BUF_NORM))
271 datap->uri_buf = HTTP_BUFFER_URI;
272 else if (!strcmp(toks[1], URI_LEN_BUF_RAW))
273 datap->uri_buf = HTTP_BUFFER_RAW_URI;
274 else
275 FatalError("%s(%d): Invalid 'urilen' argument.\n",
276 file_name, file_line);
277 }
278 else
279 {
280 if (strchr(argp, ','))
281 {
282 FatalError("%s(%d): Invalid 'urilen' argument.\n",
283 file_name, file_line);
284 }
285
286 datap->uri_buf = HTTP_BUFFER_RAW_URI;
287 }
288
289 mSplitFree(&toks, num_toks);
290
291 fpl = AddOptFuncToList(CheckUriLen, otnp);
292 fpl->type = RULE_OPTION_TYPE_URILEN;
293
294 if (add_detection_option(sc, RULE_OPTION_TYPE_URILEN, (void *)datap, &datap_dup) == DETECTION_OPTION_EQUAL)
295 {
296 otnp->ds_list[PLUGIN_URILEN_CHECK] = datap_dup;
297 free(datap);
298 }
299
300 fpl->context = otnp->ds_list[PLUGIN_URILEN_CHECK];
301 }
302
CheckUriLen(void * option_data,Packet * p)303 int CheckUriLen(void *option_data, Packet *p)
304 {
305 UriLenCheckData *udata = (UriLenCheckData *)option_data;
306 int rval = DETECTION_OPTION_NO_MATCH;
307 const HttpBuffer* hb = GetHttpBuffer(udata->uri_buf);
308 PROFILE_VARS;
309
310 PREPROC_PROFILE_START(urilenCheckPerfStats);
311
312 if ( !hb )
313 {
314 PREPROC_PROFILE_END(urilenCheckPerfStats);
315 return rval;
316 }
317
318 switch (udata->oper)
319 {
320 case URILEN_CHECK_EQ:
321 if (udata->urilen == hb->length)
322 rval = DETECTION_OPTION_MATCH;
323 break;
324 case URILEN_CHECK_GT:
325 if (udata->urilen < hb->length)
326 rval = DETECTION_OPTION_MATCH;
327 break;
328 case URILEN_CHECK_LT:
329 if (udata->urilen > hb->length)
330 rval = DETECTION_OPTION_MATCH;
331 break;
332 case URILEN_CHECK_RG:
333 if ((udata->urilen <= hb->length) && (udata->urilen2 >= hb->length))
334 rval = DETECTION_OPTION_MATCH;
335 break;
336 default:
337 break;
338 }
339
340 /* if the test isn't successful, return 0 */
341 PREPROC_PROFILE_END(urilenCheckPerfStats);
342 return rval;
343 }
344