1 /* $Id$ */
2 /*
3  ** Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
4  ** Copyright (C) 2002-2013 Sourcefire, Inc.
5  ** Author: Daniel Roelker
6  **
7  ** This program is free software; you can redistribute it and/or modify
8  ** it under the terms of the GNU General Public License Version 2 as
9  ** published by the Free Software Foundation.  You may not use, modify or
10  ** distribute this program under any other version of the GNU General
11  ** Public License.
12  **
13  ** This program is distributed in the hope that it will be useful,
14  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
15  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  ** GNU General Public License for more details.
17  **
18  ** You should have received a copy of the GNU General Public License
19  ** along with this program; if not, write to the Free Software
20  ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21  */
22 
23 /**
24 **  @file        sp_asn1.c
25 **
26 **  @author      Daniel Roelker <droelker@sourcefire.com>
27 **
28 **  @brief       Decode and detect ASN.1 types, lengths, and data.
29 **
30 **  This detection plugin adds ASN.1 detection functions on a per rule
31 **  basis.  ASN.1 detection plugins can be added by editing this file and
32 **  providing an interface in the configuration code.
33 **
34 **  Detection Plugin Interface:
35 **
36 **  asn1: [detection function],[arguments],[offset type],[size]
37 **
38 **  Detection Functions:
39 **
40 **  bitstring_overflow: no arguments
41 **  double_overflow:    no arguments
42 **  oversize_length:    max size (if no max size, then just return value)
43 **
44 **  alert udp any any -> any 161 (msg:"foo"; \
45 **      asn1: oversize_length 10000, absolute_offset 0;)
46 **
47 **  alert tcp any any -> any 162 (msg:"foo2"; \
48 **      asn1: bitstring_overflow, oversize_length 500, relative_offset 7;)
49 **
50 **
51 **  Note that further general information about ASN.1 can be found in
52 **  the file doc/README.asn1.
53 */
54 
55 #ifdef HAVE_CONFIG_H
56 #include "config.h"
57 #endif
58 
59 #include <sys/types.h>
60 #include <stdlib.h>
61 #include <ctype.h>
62 #include <errno.h>
63 
64 #include "sf_types.h"
65 #include "snort_bounds.h"
66 #include "rules.h"
67 #include "treenodes.h"
68 #include "decode.h"
69 #include "plugbase.h"
70 #include "parser.h"
71 #include "snort_debug.h"
72 #include "util.h"
73 #include "plugin_enum.h"
74 #include "asn1.h"
75 #include "sp_asn1.h"
76 #include "sp_asn1_detect.h"
77 #include "sfhashfcn.h"
78 #include "detection_util.h"
79 
80 #define BITSTRING_OPT  "bitstring_overflow"
81 #define DOUBLE_OPT     "double_overflow"
82 #define LENGTH_OPT     "oversize_length"
83 #define DBL_FREE_OPT   "double_free"
84 
85 #define ABS_OFFSET_OPT "absolute_offset"
86 #define REL_OFFSET_OPT "relative_offset"
87 #define PRINT_OPT      "print"
88 
89 #define DELIMITERS " ,\t\n"
90 
91 #include "snort.h"
92 #include "profiler.h"
93 #ifdef PERF_PROFILING
94 PreprocStats asn1PerfStats;
95 extern PreprocStats ruleOTNEvalPerfStats;
96 #endif
97 
98 #include "sfhashfcn.h"
99 #include "detection_options.h"
100 
Asn1Hash(void * d)101 uint32_t Asn1Hash(void *d)
102 {
103     uint32_t a,b,c;
104     ASN1_CTXT *data = (ASN1_CTXT *)d;
105 
106     a = data->bs_overflow;
107     b = data->double_overflow;
108     c = data->print;
109 
110     mix(a,b,c);
111 
112     a += data->length;
113     b += data->max_length;
114     c += data->offset;
115 
116     mix(a,b,c);
117 
118     a += data->offset_type;
119     b += RULE_OPTION_TYPE_ASN1;
120 
121     final(a,b,c);
122 
123     return c;
124 }
125 
Asn1Compare(void * l,void * r)126 int Asn1Compare(void *l, void *r)
127 {
128     ASN1_CTXT *left = (ASN1_CTXT *)l;
129     ASN1_CTXT *right = (ASN1_CTXT *)r;
130 
131     if (!left || !right)
132         return DETECTION_OPTION_NOT_EQUAL;
133 
134     if ((left->bs_overflow == right->bs_overflow) &&
135         (left->double_overflow == right->double_overflow) &&
136         (left->print == right->print) &&
137         (left->length == right->length) &&
138         (left->max_length == right->max_length) &&
139         (left->offset == right->offset) &&
140         (left->offset_type == right->offset_type))
141     {
142         return DETECTION_OPTION_EQUAL;
143     }
144 
145     return DETECTION_OPTION_NOT_EQUAL;
146 }
147 
148 /*
149 **  NAME
150 **    Asn1RuleParse::
151 */
152 /**
153 **  Parse the detection option arguments.
154 **    - bitstring_overflow
155 **    - double_overflow
156 **    - oversize_length
157 **    - print
158 **    - abs_offset
159 **    - rel_offset
160 **
161 **  @return void
162 */
Asn1RuleParse(char * data,OptTreeNode * otn,ASN1_CTXT * asn1)163 static void Asn1RuleParse(char *data, OptTreeNode *otn, ASN1_CTXT *asn1)
164 {
165     char *pcTok;
166     char *endTok;
167 
168     if(!data)
169     {
170         FatalError("%s(%d) => No options to 'asn1' detection plugin.\n",
171                    file_name, file_line);
172     }
173 
174     pcTok = strtok(data, DELIMITERS);
175     if(!pcTok)
176     {
177         FatalError("%s(%d) => No options to 'asn1' detection plugin.\n",
178                    file_name, file_line);
179     }
180 
181     while(pcTok)
182     {
183         if(!strcasecmp(pcTok, BITSTRING_OPT))
184         {
185             asn1->bs_overflow = 1;
186         }
187         else if(!strcasecmp(pcTok, DOUBLE_OPT))
188         {
189             asn1->double_overflow = 1;
190         }
191         else if(!strcasecmp(pcTok, PRINT_OPT))
192         {
193             asn1->print = 1;
194         }
195         else if(!strcasecmp(pcTok, LENGTH_OPT))
196         {
197             long int max_length;
198             char *pcEnd;
199 
200             pcTok = strtok(NULL, DELIMITERS);
201             if(!pcTok)
202             {
203                 FatalError("%s(%d) => No option to '%s' in 'asn1' detection "
204                            "plugin\n", file_name, file_line, LENGTH_OPT);
205             }
206 
207             max_length = SnortStrtolRange(pcTok, &pcEnd, 10, 0, INT32_MAX);
208 
209             if ((pcEnd == pcTok) || (*pcEnd) || (errno == ERANGE))
210             {
211                 FatalError("%s(%d) => Negative size, underflow or overflow "
212                            "(of long int) to '%s' in 'asn1' detection plugin. "
213                            "Must be positive or zero.\n",
214                            file_name, file_line, LENGTH_OPT);
215             }
216 
217             asn1->length = 1;
218             asn1->max_length = (unsigned int)max_length;
219         }
220         else if(!strcasecmp(pcTok, ABS_OFFSET_OPT))
221         {
222             pcTok = strtok(NULL, DELIMITERS);
223             if(!pcTok)
224             {
225                 FatalError("%s(%d) => No option to '%s' in 'asn1' detection "
226                            "plugin\n", file_name, file_line, ABS_OFFSET_OPT);
227             }
228 
229             asn1->offset_type = ABS_OFFSET;
230             asn1->offset = SnortStrtol(pcTok, &endTok, 10);
231             if (endTok == pcTok)
232             {
233                 FatalError("%s(%d) => Invalid parameter to '%s' in 'asn1' "
234                            "detection plugin\n",
235                            file_name, file_line, ABS_OFFSET_OPT);
236             }
237 
238         }
239         else if(!strcasecmp(pcTok, REL_OFFSET_OPT))
240         {
241             pcTok = strtok(NULL, DELIMITERS);
242             if(!pcTok)
243             {
244                 FatalError("%s(%d) => No option to '%s' in 'asn1' detection "
245                            "plugin\n", file_name, file_line, REL_OFFSET_OPT);
246             }
247 
248             asn1->offset_type = REL_OFFSET;
249             asn1->offset = SnortStrtol(pcTok, &endTok, 10);
250             if (endTok == pcTok)
251             {
252                 FatalError("%s(%d) => Invalid parameter to '%s' in 'asn1' "
253                            "detection plugin\n",
254                            file_name, file_line, pcTok);
255             }
256         }
257         else
258         {
259             FatalError("%s(%d) => Unknown ('%s') asn1 detection option.\n",
260                        file_name, file_line, pcTok);
261         }
262 
263         pcTok = strtok(NULL, DELIMITERS);
264     }
265 
266     return;
267 }
268 
269 /*
270 **  NAME
271 **    Asn1Detect::
272 */
273 /**
274 **  The main snort detection function.  We grab the context ptr from the
275 **  otn and go forth.  We check all the offsets to make sure we're in
276 **  bounds, etc.
277 **
278 **  @return integer
279 **
280 **  @retval 0 failed
281 **  @retval 1 detected
282 */
Asn1Detect(void * context,Packet * p)283 int Asn1Detect(void *context, Packet *p)
284 {
285     ASN1_CTXT *ctxt;
286     PROFILE_VARS;
287 
288     /*
289     **  Failed if there is no data to decode.
290     */
291     if(!p->data)
292         return DETECTION_OPTION_NO_MATCH;
293 
294     PREPROC_PROFILE_START(asn1PerfStats);
295 
296     ctxt = (ASN1_CTXT *)context;
297 
298     if (Asn1DoDetect(p->data, p->dsize, ctxt, doe_ptr))
299     {
300         PREPROC_PROFILE_END(asn1PerfStats);
301         return DETECTION_OPTION_MATCH;
302     }
303 
304     PREPROC_PROFILE_END(asn1PerfStats);
305     return DETECTION_OPTION_NO_MATCH;
306 }
307 
Asn1Init(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)308 static void Asn1Init(struct _SnortConfig *sc, char *data, OptTreeNode *otn, int protocol)
309 {
310     ASN1_CTXT *asn1;
311     void *ds_ptr_dup;
312     OptFpList *ofl;
313 
314     /*
315      * allocate the data structure and attach
316      * it to the rule's data struct list
317      */
318     asn1 = (ASN1_CTXT *)SnortAlloc(sizeof(ASN1_CTXT));
319 
320     Asn1RuleParse(data, otn, asn1);
321 
322     if (add_detection_option(sc, RULE_OPTION_TYPE_ASN1, (void *)asn1, &ds_ptr_dup) == DETECTION_OPTION_EQUAL)
323     {
324         free(asn1);
325         asn1 = ds_ptr_dup;
326     }
327 
328     ofl = AddOptFuncToList(Asn1Detect, otn);
329 
330     ofl->context = (void *)asn1;
331     ofl->type = RULE_OPTION_TYPE_ASN1;
332 
333     if (asn1->offset_type == REL_OFFSET)
334         ofl->isRelative = 1;
335 
336 }
337 
SetupAsn1(void)338 void SetupAsn1(void)
339 {
340     /* map the keyword to an initialization/processing function */
341     RegisterRuleOption("asn1", Asn1Init, NULL, OPT_TYPE_DETECTION, NULL);
342 
343 #ifdef PERF_PROFILING
344     RegisterPreprocessorProfile("asn1", &asn1PerfStats, 3, &ruleOTNEvalPerfStats, NULL);
345 #endif
346 
347     DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Plugin: ASN1 Setup\n"););
348 }
349 
350