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