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