"Fossies" - the Fresh Open Source Software Archive 
Member "snort-2.9.17/src/parser.c" (16 Oct 2020, 372548 Bytes) of package /linux/misc/snort-2.9.17.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "parser.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
2.9.16.1_vs_2.9.17.
1 /* $Id$ */
2 /*
3 ** Copyright (C) 2014-2020 Cisco and/or its affiliates. All rights reserved.
4 ** Copyright (C) 2002-2013 Sourcefire, Inc.
5 ** Copyright (C) 1998-2002 Martin Roesch <roesch@sourcefire.com>
6 ** Copyright (C) 2000,2001 Andrew R. Baker <andrewb@uab.edu>
7 **
8 ** This program is free software; you can redistribute it and/or modify
9 ** it under the terms of the GNU General Public License Version 2 as
10 ** published by the Free Software Foundation. You may not use, modify or
11 ** distribute this program under any other version of the GNU General
12 ** Public License.
13 **
14 ** This program is distributed in the hope that it will be useful,
15 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ** GNU General Public License for more details.
18 **
19 ** You should have received a copy of the GNU General Public License
20 ** along with this program; if not, write to the Free Software
21 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <assert.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <unistd.h>
36 #include <stdarg.h>
37 #include <pcap.h>
38 #ifdef HAVE_DUMBNET_H
39 #include <dumbnet.h>
40 #else
41 #include <dnet.h>
42 #endif
43
44 #ifdef HAVE_STRINGS_H
45 # include <strings.h>
46 #endif
47
48 #ifndef WIN32
49 # include <netdb.h>
50 # include <sys/socket.h>
51 # include <netinet/in.h>
52 # include <arpa/inet.h>
53 # include <grp.h>
54 # include <pwd.h>
55 # include <fnmatch.h>
56 #endif /* !WIN32 */
57
58 #include "encode.h"
59 #include "snort_bounds.h"
60 #include "rules.h"
61 #include "treenodes.h"
62 #include "parser.h"
63 #include "plugbase.h"
64 #include "plugin_enum.h"
65 #include "snort_debug.h"
66 #include "util.h"
67 #include "mstring.h"
68 #include "detect.h"
69 #include "fpcreate.h"
70 #include "log.h"
71 #include "generators.h"
72 #include "tag.h"
73 #include "signature.h"
74 #include "strlcatu.h"
75 #include "strlcpyu.h"
76 #include "sfthreshold.h"
77 #include "sfutil/sfthd.h"
78 #include "snort.h"
79 #include "event_queue.h"
80 #include "asn1.h"
81 #include "sfutil/sfghash.h"
82 #include "sp_preprocopt.h"
83 #include "detection-plugins/sp_icmp_type_check.h"
84 #include "detection-plugins/sp_ip_proto.h"
85 #include "detection-plugins/sp_pattern_match.h"
86 #include "detection-plugins/sp_flowbits.h"
87 #include "sf_vartable.h"
88 #include "ipv6_port.h"
89 #include "sfutil/sf_ip.h"
90 #include "sflsq.h"
91 #include "ppm.h"
92 #include "rate_filter.h"
93 #include "detection_filter.h"
94 #include "sfPolicy.h"
95 #include "sfutil/mpse.h"
96 #include "sfutil/sfrim.h"
97 #include "sfutil/sfportobject.h"
98 #include "sfutil/strvec.h"
99 #include "active.h"
100 #include "file_config.h"
101 #include "file_service_config.h"
102 #include "dynamic-plugins/sp_dynamic.h"
103 #include "dynamic-output/plugins/output.h"
104 #include "stream_common.h"
105
106 #ifdef SIDE_CHANNEL
107 # include "sidechannel.h"
108 #endif
109
110 #ifdef TARGET_BASED
111 # include "sftarget_reader.h"
112 #endif
113
114
115 /* Macros *********************************************************************/
116 #define ENABLE_ALL_RULES 1
117 #define ENABLE_RULE 1
118 #define ENABLE_ONE_RULE 0
119 #define MAX_RULE_OPTIONS 256
120 #define MAX_LINE_LENGTH 32768
121 #define MAX_IPLIST_ENTRIES 4096
122 #define DEFAULT_LARGE_RULE_GROUP 9
123 #define SF_IPPROTO_UNKNOWN -1
124 #define MAX_RULE_COUNT (65535 * 2)
125
126 /* Rule list order keywords
127 * This is separate from keywords because activation was used
128 * instead of activate */
129 #define RULE_LIST_TYPE__ALERT "alert"
130 #define RULE_LIST_TYPE__DROP "drop"
131 #define RULE_LIST_TYPE__LOG "log"
132 #define RULE_LIST_TYPE__PASS "pass"
133 #define RULE_LIST_TYPE__REJECT "reject"
134 #define RULE_LIST_TYPE__SDROP "sdrop"
135
136 #define RULE_PROTO_OPT__IP "ip"
137 #define RULE_PROTO_OPT__TCP "tcp"
138 #define RULE_PROTO_OPT__UDP "udp"
139 #define RULE_PROTO_OPT__ICMP "icmp"
140
141 #define RULE_DIR_OPT__DIRECTIONAL "->"
142 #define RULE_DIR_OPT__BIDIRECTIONAL "<>"
143
144 /* For user defined rule type */
145 #define RULE_TYPE_OPT__TYPE "type"
146
147 /* Rule options
148 * Only the basic ones are here. The detection options and preprocessor
149 * detection options define their own */
150 #define RULE_OPT__CLASSTYPE "classtype"
151 #define RULE_OPT__DETECTION_FILTER "detection_filter"
152 #define RULE_OPT__GID "gid"
153 #define RULE_OPT__MSG "msg"
154 #define RULE_OPT__METADATA "metadata"
155 #define RULE_OPT__LOGTO "logto"
156 #define RULE_OPT__PRIORITY "priority"
157 #define RULE_OPT__REFERENCE "reference"
158 #define RULE_OPT__REVISION "rev"
159 #define RULE_OPT__SID "sid"
160 #define RULE_OPT__TAG "tag"
161 #define RULE_OPT__THRESHOLD "threshold"
162
163 /* Metadata rule option keys */
164 #define METADATA_KEY__ENGINE "engine"
165 #define METADATA_KEY__OS "os"
166 #define METADATA_KEY__RULE_FLUSHING "rule-flushing"
167 #define METADATA_KEY__RULE_TYPE "rule-type"
168 #define METADATA_KEY__SOID "soid"
169 #define METADATA_KEY__SERVICE "service"
170
171 /* Metadata rule option values */
172 #define METADATA_VALUE__DECODE "decode"
173 #define METADATA_VALUE__DETECT "detect"
174 #define METADATA_VALUE__DISABLED "disabled"
175 #define METADATA_VALUE__ENABLED "enabled"
176 #define METADATA_VALUE__OFF "off"
177 #define METADATA_VALUE__ON "on"
178 #define METADATA_VALUE__PREPROC "preproc"
179 #define METADATA_VALUE__SHARED "shared"
180
181 /* MPLS payload types */
182 #ifdef MPLS
183 # define MPLS_PAYLOAD_OPT__IPV4 "ipv4"
184 # define MPLS_PAYLOAD_OPT__IPV6 "ipv6"
185 # define MPLS_PAYLOAD_OPT__ETHERNET "ethernet"
186 #endif
187
188 /* Tag options */
189 #define TAG_OPT__BYTES "bytes"
190 #define TAG_OPT__DST "dst"
191 #define TAG_OPT__HOST "host"
192 #define TAG_OPT__PACKETS "packets"
193 #define TAG_OPT__SECONDS "seconds"
194 #define TAG_OPT__SESSION "session"
195 #define TAG_OPT__SRC "src"
196 #define TAG_OPT__EXCLUSIVE "exclusive"
197
198 /* Dynamic library specifier option values */
199 #define DYNAMIC_LIB_OPT__FILE "file"
200 #define DYNAMIC_LIB_OPT__DIRECTORY "directory"
201
202 /* Threshold options */
203 #define THRESHOLD_OPT__COUNT "count"
204 #define THRESHOLD_OPT__GID "gen_id"
205 #define THRESHOLD_OPT__IP "ip"
206 #define THRESHOLD_OPT__SECONDS "seconds"
207 #define THRESHOLD_OPT__SID "sig_id"
208 #define THRESHOLD_OPT__TRACK "track"
209 #define THRESHOLD_OPT__TYPE "type"
210
211 #define THRESHOLD_TYPE__BOTH "both"
212 #define THRESHOLD_TYPE__LIMIT "limit"
213 #define THRESHOLD_TYPE__THRESHOLD "threshold"
214
215 #define THRESHOLD_TRACK__BY_DST "by_dst"
216 #define THRESHOLD_TRACK__BY_SRC "by_src"
217
218 #define RULE_STATE_OPT__DISABLED "disabled"
219 #define RULE_STATE_OPT__ENABLED "enabled"
220
221 #define DETECTION_OPT__BLEEDOVER_PORT_LIMIT "bleedover-port-limit"
222 #define DETECTION_OPT__BLEEDOVER_WARNINGS_ENABLED "bleedover-warnings-enabled"
223 #define DETECTION_OPT__DEBUG "debug"
224 #define DETECTION_OPT__DEBUG_PRINT_NOCONTENT_RULE_TESTS "debug-print-nocontent-rule-tests"
225 #define DETECTION_OPT__DEBUG_PRINT_RULE_GROUP_BUILD_DETAILS "debug-print-rule-group-build-details"
226 #define DETECTION_OPT__DEBUG_PRINT_RULE_GROUPS_UNCOMPILED "debug-print-rule-groups-uncompiled"
227 #define DETECTION_OPT__DEBUG_PRINT_RULE_GROUPS_COMPILED "debug-print-rule-groups-compiled"
228 #define DETECTION_OPT__ENABLE_SINGLE_RULE_GROUP "enable-single-rule-group"
229 #define DETECTION_OPT__MAX_QUEUE_EVENTS "max_queue_events"
230 #define DETECTION_OPT__NO_STREAM_INSERTS "no_stream_inserts"
231 #define DETECTION_OPT__SEARCH_METHOD "search-method"
232 #define DETECTION_OPT__SEARCH_OPTIMIZE "search-optimize"
233 #define DETECTION_OPT__SPLIT_ANY_ANY "split-any-any"
234 #define DETECTION_OPT__MAX_PATTERN_LEN "max-pattern-len"
235 #define DETECTION_OPT__DEBUG_PRINT_FAST_PATTERN "debug-print-fast-pattern"
236
237 /* Protected Content options - there should be a better way instead of declaring the type strings here */
238 #define PROTECTED_CONTENT_OPT__HASH_TYPE "hash"
239 #define EVENT_QUEUE_OPT__LOG "log"
240 #define EVENT_QUEUE_OPT__MAX_QUEUE "max_queue"
241 #define EVENT_QUEUE_OPT__ORDER_EVENTS "order_events"
242 #define EVENT_QUEUE_OPT__PROCESS_ALL_EVENTS "process_all_events"
243
244 #define EVENT_TRACE_OPT__FILE "file"
245 #define EVENT_TRACE_OPT__MAX_DATA "max_data"
246 #define EVENT_TRACE_OPT__FILE_DEFAULT "event_trace.txt"
247 #define EVENT_TRACE_OPT__MAX_DATA_DEFAULT 64
248
249 #define ORDER_EVENTS_OPT__CONTENT_LENGTH "content_length"
250 #define ORDER_EVENTS_OPT__PRIORITY "priority"
251
252 #define THRESHOLD_OPT__MEMCAP "memcap"
253
254 #define CHECKSUM_MODE_OPT__ALL "all"
255 #define CHECKSUM_MODE_OPT__NONE "none"
256 #define CHECKSUM_MODE_OPT__IP "ip"
257 #define CHECKSUM_MODE_OPT__NO_IP "noip"
258 #define CHECKSUM_MODE_OPT__TCP "tcp"
259 #define CHECKSUM_MODE_OPT__NO_TCP "notcp"
260 #define CHECKSUM_MODE_OPT__UDP "udp"
261 #define CHECKSUM_MODE_OPT__NO_UDP "noudp"
262 #define CHECKSUM_MODE_OPT__ICMP "icmp"
263 #define CHECKSUM_MODE_OPT__NO_ICMP "noicmp"
264
265 #define RULE_STATE_OPT__DISABLED "disabled"
266 #define RULE_STATE_OPT__ENABLED "enabled"
267
268 #define POLICY_MODE_PASSIVE "tap"
269 #define POLICY_MODE_INLINE "inline"
270 #define POLICY_MODE_INLINE_TEST "inline_test"
271
272 #ifdef PERF_PROFILING
273 # define PROFILE_OPT__FILENAME "filename"
274 # define PROFILE_OPT__PRINT "print"
275 # define PROFILE_OPT__ALL "all"
276 # define PROFILE_OPT__SORT "sort"
277 # define PROFILE_OPT__CHECKS "checks"
278 # define PROFILE_OPT__AVG_TICKS "avg_ticks"
279 # define PROFILE_OPT__TOTAL_TICKS "total_ticks"
280 # define PROFILE_OPT__MATCHES "matches"
281 # define PROFILE_OPT__NO_MATCHES "nomatches"
282 # define PROFILE_OPT__AVG_TICKS_PER_MATCH "avg_ticks_per_match"
283 # define PROFILE_OPT__AVG_TICKS_PER_NO_MATCH "avg_ticks_per_nomatch"
284 # define PROFILE_OPT__APPEND "append"
285 #endif
286
287 #ifdef PPM_MGR
288 # define PPM_OPT__MAX_PKT_TIME "max-pkt-time"
289 # define PPM_OPT__MAX_RULE_TIME "max-rule-time"
290 # define PPM_OPT__SUSPEND_TIMEOUT "suspend-timeout"
291 # define PPM_OPT__SUSPEND_EXP_RULES "suspend-expensive-rules"
292 # define PPM_OPT__THRESHOLD "threshold"
293 # define PPM_OPT__FAST_PATH_EXP_PKTS "fastpath-expensive-packets"
294 # define PPM_OPT__PKT_LOG "pkt-log"
295 # define PPM_OPT__RULE_LOG "rule-log"
296 # define PPM_OPT__ALERT "alert"
297 # define PPM_OPT__LOG "log"
298 # define PPM_OPT__DEBUG_PKTS "debug-pkts"
299 #endif
300
301 #ifdef ACTIVE_RESPONSE
302 #define RESPONSE_OPT__ATTEMPTS "attempts"
303 #define RESPONSE_OPT__DEVICE "device"
304 #define RESPONSE_OPT__DST_MAC "dst_mac"
305 #endif
306
307 #define ERR_PAIR_COUNT \
308 "%s has incorrect argument count; should be %d pairs.", ERR_KEY
309 #define ERR_NOT_PAIRED \
310 "%s is missing an option or argument to go with: %s.", ERR_KEY, pairs[0]
311 #define ERR_EXTRA_OPTION \
312 "%s has extra option of type: %s.", ERR_KEY, pairs[0]
313 #define ERR_BAD_OPTION \
314 "%s has unknown option: %s.", ERR_KEY, pairs[0]
315 #define ERR_BAD_VALUE \
316 "%s has unknown %s: %s.", ERR_KEY, pairs[0], pairs[1]
317 #define ERR_BAD_ARG_COUNT \
318 "%s has incorrect argument count.", ERR_KEY
319 #define ERR_CREATE \
320 "%s could not be created.", ERR_KEY
321 #define ERR_CREATE_EX \
322 "%s could not be created: %s.", ERR_KEY
323
324 // arbitrary conf file name used to allow initialization w/o conf
325 // (required for packet logging mode)
326 #define NULL_CONF "null"
327
328 /* Data types *****************************************************************/
329 typedef void (*ParseFunc)(SnortConfig *, SnortPolicy *, char *);
330 typedef void (*ParseConfigFunc)(SnortConfig *, char *);
331 typedef void (*ParseRuleOptFunc)(SnortConfig *, RuleTreeNode *, OptTreeNode *, RuleType, char *);
332
333 /* Used to determine whether or not to parse the keyword line based on
334 * whether or not we're parsing rules */
335 typedef enum _KeywordType
336 {
337 KEYWORD_TYPE__MAIN,
338 KEYWORD_TYPE__RULE,
339 KEYWORD_TYPE__ALL
340
341 } KeywordType;
342
343 typedef enum _VarType
344 {
345 VAR_TYPE__DEFAULT,
346 VAR_TYPE__PORTVAR,
347 VAR_TYPE__IPVAR
348
349 } VarType;
350
351 typedef struct _KeywordFunc
352 {
353 char *name;
354 KeywordType type;
355 int expand_vars;
356 int default_policy_only;
357 ParseFunc parse_func;
358
359 } KeywordFunc;
360
361 typedef struct _RuleOptFunc
362 {
363 char *name;
364 int args_required;
365 int only_once;
366 int detection;
367 ParseRuleOptFunc parse_func;
368
369 } RuleOptFunc;
370
371 typedef struct _ConfigFunc
372 {
373 char *name;
374 int args_required;
375 int only_once;
376 int default_policy_only;
377 ParseConfigFunc parse_func;
378
379 } ConfigFunc;
380
381 /* Tracking the port_list_t structure for printing and debugging at
382 * this point...temporarily... */
383 typedef struct
384 {
385 int rule_type;
386 int proto;
387 int icmp_type;
388 int ip_proto;
389 char *protocol;
390 char *src_port;
391 char *dst_port;
392 unsigned int gid;
393 unsigned int sid;
394 int dir;
395 char content;
396 char uricontent;
397
398 } port_entry_t;
399
400 typedef struct
401 {
402 int pl_max;
403 int pl_cnt;
404 port_entry_t pl_array[MAX_RULE_COUNT];
405
406 } port_list_t;
407
408 /* rule counts for port lists */
409 typedef struct
410 {
411 int src;
412 int dst;
413 int aa; /* any-any */
414 int sd; /* src+dst ports specified */
415 int nc; /* no content */
416
417 } rule_count_t;
418
419
420 /* Externs ********************************************************************/
421 extern VarNode *cmd_line_var_list;
422 extern char *snort_conf_file;
423 extern char *snort_conf_dir;
424 extern RuleOptConfigFuncNode *rule_opt_config_funcs;
425
426 /* Globals ********************************************************************/
427
428 char *file_name = NULL; /* current config file being processed */
429 int file_line = 0; /* current line being processed in the file */
430
431 /* Main parsing function uses this to indicate whether or not
432 * rules are to be parsed. */
433 static int parse_rules = 0;
434
435 /* Used when parsing rule options and a threshold is used. Need to add
436 * sid and gid to threshold object and they might not have been
437 * parsed yet. */
438 static THDX_STRUCT *thdx_tmp = NULL;
439
440 int rule_count = 0; /* number of rules generated */
441 int detect_rule_count = 0; /* number of detection rules generated */
442 int decode_rule_count = 0; /* number of decoder rules generated */
443 int preproc_rule_count = 0; /* number of preprocessor rules generated */
444 int head_count = 0; /* number of header blocks (chain heads?) */
445 int otn_count = 0; /* number of chains */
446
447 static port_list_t port_list;
448
449 static rule_count_t tcpCnt;
450 static rule_count_t udpCnt;
451 static rule_count_t icmpCnt;
452 static rule_count_t ipCnt;
453
454 rule_index_map_t *ruleIndexMap = NULL; /* rule index -> sid:gid map */
455
456 static void ParseAlert(SnortConfig *, SnortPolicy *, char *);
457 static void ParseDrop(SnortConfig *, SnortPolicy *, char *);
458 static void ParseLog(SnortConfig *, SnortPolicy *, char *);
459 static void ParsePass(SnortConfig *, SnortPolicy *, char *);
460 static void ParseReject(SnortConfig *, SnortPolicy *, char *);
461 static void ParseSdrop(SnortConfig *, SnortPolicy *, char *);
462
463 #ifdef TARGET_BASED
464 static void ParseAttributeTable(SnortConfig *, SnortPolicy *, char *);
465 #endif /* TARGET_BASED */
466 static void ParseConfig(SnortConfig *, SnortPolicy *, char *);
467 static void ParseDynamicDetectionInfo(SnortConfig *, SnortPolicy *, char *);
468 static void ParseDynamicEngineInfo(SnortConfig *, SnortPolicy *, char *);
469 static void ParseDynamicPreprocessorInfo(SnortConfig *, SnortPolicy *, char *);
470 # ifdef SIDE_CHANNEL
471 static void ParseDynamicSideChannelInfo(SnortConfig *, SnortPolicy *, char *);
472 # endif /* SIDE_CHANNEL */
473 static void ParseDynamicOutputInfo(SnortConfig *, SnortPolicy *, char *);
474 static void ParseEventFilter(SnortConfig *, SnortPolicy *, char *);
475 static void ParseInclude(SnortConfig *, SnortPolicy *, char *);
476 static void ParseIpVar(SnortConfig *, SnortPolicy *, char *);
477 static void ParsePortVar(SnortConfig *, SnortPolicy *, char *);
478 static void ParsePreprocessor(SnortConfig *, SnortPolicy *, char *);
479 static void ParseRateFilter(SnortConfig *, SnortPolicy *, char *);
480 static void ParseRuleState(SnortConfig *, SnortPolicy *, char *);
481 static void ParseRuleTypeDeclaration(SnortConfig *, SnortPolicy *, char *);
482 #ifdef SIDE_CHANNEL
483 static void ParseSideChannelModule(SnortConfig *, SnortPolicy *, char *);
484 static void ConfigSideChannel(SnortConfig *, char *);
485 #endif /* SIDE_CHANNEL */
486 static void ParseSuppress(SnortConfig *, SnortPolicy *, char *);
487 static void ParseThreshold(SnortConfig *, SnortPolicy *, char *);
488 static void ParseVar(SnortConfig *, SnortPolicy *, char *);
489 static void ParseFile(SnortConfig *, SnortPolicy *, char *);
490 static void AddVarToTable(SnortConfig *, char *, char *);
491
492 int ParseBool(char *arg);
493
494 static const KeywordFunc snort_conf_keywords[] =
495 {
496 /* Rule keywords */
497 { SNORT_CONF_KEYWORD__ALERT, KEYWORD_TYPE__RULE, 0, 0, ParseAlert },
498 { SNORT_CONF_KEYWORD__DROP, KEYWORD_TYPE__RULE, 0, 0, ParseDrop },
499 { SNORT_CONF_KEYWORD__BLOCK, KEYWORD_TYPE__RULE, 0, 0, ParseDrop },
500 { SNORT_CONF_KEYWORD__LOG, KEYWORD_TYPE__RULE, 0, 0, ParseLog },
501 { SNORT_CONF_KEYWORD__PASS, KEYWORD_TYPE__RULE, 0, 0, ParsePass },
502 { SNORT_CONF_KEYWORD__REJECT, KEYWORD_TYPE__RULE, 0, 0, ParseReject },
503 { SNORT_CONF_KEYWORD__SDROP, KEYWORD_TYPE__RULE, 0, 0, ParseSdrop },
504 { SNORT_CONF_KEYWORD__SBLOCK, KEYWORD_TYPE__RULE, 0, 0, ParseSdrop },
505
506 /* Non-rule keywords */
507 #ifdef TARGET_BASED
508 /* Need to fatal error if attribute_table is not configured in default
509 * policy. Since we're just skipping configuring non-default
510 * configurations with default only configuration types, set this to
511 * be configured for non-default policies and fatal in function */
512 { SNORT_CONF_KEYWORD__ATTRIBUTE_TABLE, KEYWORD_TYPE__MAIN, 1, 0, ParseAttributeTable },
513 #endif
514 { SNORT_CONF_KEYWORD__CONFIG, KEYWORD_TYPE__MAIN, 1, 0, ParseConfig },
515 { SNORT_CONF_KEYWORD__DYNAMIC_OUTPUT , KEYWORD_TYPE__MAIN, 1, 1, ParseDynamicOutputInfo },
516 { SNORT_CONF_KEYWORD__DYNAMIC_DETECTION, KEYWORD_TYPE__MAIN, 1, 1, ParseDynamicDetectionInfo },
517 { SNORT_CONF_KEYWORD__DYNAMIC_ENGINE, KEYWORD_TYPE__MAIN, 1, 1, ParseDynamicEngineInfo },
518 { SNORT_CONF_KEYWORD__DYNAMIC_PREPROC, KEYWORD_TYPE__MAIN, 1, 1, ParseDynamicPreprocessorInfo },
519 #ifdef SIDE_CHANNEL
520 { SNORT_CONF_KEYWORD__DYNAMIC_SIDE_CHAN, KEYWORD_TYPE__MAIN, 1, 1, ParseDynamicSideChannelInfo },
521 #endif /* SIDE_CHANNEL */
522 { SNORT_CONF_KEYWORD__EVENT_FILTER, KEYWORD_TYPE__MAIN, 1, 0, ParseEventFilter },
523 { SNORT_CONF_KEYWORD__INCLUDE, KEYWORD_TYPE__ALL, 1, 0, ParseInclude },
524 { SNORT_CONF_KEYWORD__IPVAR, KEYWORD_TYPE__MAIN, 0, 0, ParseIpVar },
525 { SNORT_CONF_KEYWORD__OUTPUT, KEYWORD_TYPE__MAIN, 1, 1, ParseOutput },
526 { SNORT_CONF_KEYWORD__PORTVAR, KEYWORD_TYPE__MAIN, 0, 0, ParsePortVar },
527 { SNORT_CONF_KEYWORD__PREPROCESSOR, KEYWORD_TYPE__MAIN, 1, 0, ParsePreprocessor },
528 { SNORT_CONF_KEYWORD__RATE_FILTER, KEYWORD_TYPE__MAIN, 0, 0, ParseRateFilter },
529 { SNORT_CONF_KEYWORD__RULE_STATE, KEYWORD_TYPE__MAIN, 1, 0, ParseRuleState },
530 { SNORT_CONF_KEYWORD__RULE_TYPE, KEYWORD_TYPE__ALL, 1, 0, ParseRuleTypeDeclaration },
531 #ifdef SIDE_CHANNEL
532 { SNORT_CONF_KEYWORD__SIDE_CHANNEL, KEYWORD_TYPE__MAIN, 1, 1, ParseSideChannelModule },
533 #endif /* SIDE_CHANNEL */
534 { SNORT_CONF_KEYWORD__SUPPRESS, KEYWORD_TYPE__MAIN, 0, 0, ParseSuppress },
535 { SNORT_CONF_KEYWORD__THRESHOLD, KEYWORD_TYPE__MAIN, 1, 0, ParseThreshold },
536 { SNORT_CONF_KEYWORD__VAR, KEYWORD_TYPE__MAIN, 0, 0, ParseVar },
537 { SNORT_CONF_KEYWORD__FILE, KEYWORD_TYPE__MAIN, 0, 1, ParseFile },
538 { NULL, KEYWORD_TYPE__ALL, 0, 0, NULL } /* Marks end of array */
539 };
540
541 static void ParseOtnClassType(SnortConfig *, RuleTreeNode *,
542 OptTreeNode *, RuleType, char *);
543 static void ParseOtnDetectionFilter(SnortConfig *, RuleTreeNode *,
544 OptTreeNode *, RuleType, char *);
545 static void ParseOtnGid(SnortConfig *, RuleTreeNode *,
546 OptTreeNode *, RuleType, char *);
547 static void ParseOtnLogTo(SnortConfig *, RuleTreeNode *,
548 OptTreeNode *, RuleType, char *);
549 static void ParseOtnMessage(SnortConfig *, RuleTreeNode *,
550 OptTreeNode *, RuleType, char *);
551 static void ParseOtnMetadata(SnortConfig *, RuleTreeNode *,
552 OptTreeNode *, RuleType, char *);
553 static void ParseOtnPriority(SnortConfig *, RuleTreeNode *,
554 OptTreeNode *, RuleType, char *);
555 static void ParseOtnReference(SnortConfig *, RuleTreeNode *,
556 OptTreeNode *, RuleType, char *);
557 static void ParseOtnRevision(SnortConfig *, RuleTreeNode *,
558 OptTreeNode *, RuleType, char *);
559 static void ParseOtnSid(SnortConfig *, RuleTreeNode *,
560 OptTreeNode *, RuleType, char *);
561 static void ParseOtnTag(SnortConfig *, RuleTreeNode *,
562 OptTreeNode *, RuleType, char *);
563 static void ParseOtnThreshold(SnortConfig *, RuleTreeNode *,
564 OptTreeNode *, RuleType, char *);
565
566 static const RuleOptFunc rule_options[] =
567 {
568 { RULE_OPT__CLASSTYPE, 1, 1, 0, ParseOtnClassType },
569 { RULE_OPT__DETECTION_FILTER, 1, 1, 1, ParseOtnDetectionFilter },
570 { RULE_OPT__GID, 1, 1, 0, ParseOtnGid },
571 { RULE_OPT__LOGTO, 1, 1, 0, ParseOtnLogTo },
572 { RULE_OPT__METADATA, 1, 0, 0, ParseOtnMetadata },
573 { RULE_OPT__MSG, 1, 1, 0, ParseOtnMessage },
574 { RULE_OPT__PRIORITY, 1, 1, 0, ParseOtnPriority },
575 { RULE_OPT__REFERENCE, 1, 0, 0, ParseOtnReference },
576 { RULE_OPT__REVISION, 1, 1, 0, ParseOtnRevision },
577 { RULE_OPT__SID, 1, 1, 0, ParseOtnSid },
578 { RULE_OPT__TAG, 1, 1, 0, ParseOtnTag },
579 { RULE_OPT__THRESHOLD, 1, 1, 1, ParseOtnThreshold },
580
581 { NULL, 0, 0, 0, NULL } /* Marks end of array */
582 };
583
584 #ifdef PERF_PROFILING
585 /* Internal prototypes used in lists below */
586 static void _ConfigProfilePreprocs(SnortConfig *, char *);
587 static void _ConfigProfileRules(SnortConfig *, char *);
588 #endif
589
590 static const ConfigFunc config_opts[] =
591 {
592 { CONFIG_OPT__ALERT_FILE, 1, 1, 1, ConfigAlertFile },
593 { CONFIG_OPT__ALERT_WITH_IFACE_NAME, 0, 1, 1, ConfigAlertWithInterfaceName },
594 { CONFIG_OPT__AUTOGEN_PREPROC_DECODER_RULES, 0, 1, 0, ConfigAutogenPreprocDecoderRules },
595 { CONFIG_OPT__ASN1, 1, 1, 1, ConfigAsn1 },
596 { CONFIG_OPT__BINDING, 1, 0, 1, ConfigBinding },
597 { CONFIG_OPT__BPF_FILE, 1, 1, 1, ConfigBpfFile },
598 { CONFIG_OPT__CHECKSUM_DROP, 0, 0, 0, ConfigChecksumDrop },
599 { CONFIG_OPT__CHECKSUM_MODE, 0, 0, 0, ConfigChecksumMode },
600 { CONFIG_OPT__CHROOT_DIR, 1, 1, 1, ConfigChrootDir },
601 { CONFIG_OPT__CLASSIFICATION, 1, 0, 0, ConfigClassification },
602 { CONFIG_OPT__DAEMON, 0, 1, 1, ConfigDaemon },
603 { CONFIG_OPT__DECODE_DATA_LINK, 0, 1, 1, ConfigDecodeDataLink },
604 { CONFIG_OPT__DECODE_ESP, 0, 1, 1, ConfigEnableEspDecoding },
605 { CONFIG_OPT__DEFAULT_RULE_STATE, 0, 1, 1, ConfigDefaultRuleState },
606 { CONFIG_OPT__DETECTION, 1, 0, 1, ConfigDetection }, /* This is reconfigurable */
607 { CONFIG_OPT__DETECTION_FILTER, 1, 1, 1, ConfigDetectionFilter },
608 { CONFIG_OPT__DISABLE_DECODE_ALERTS, 0, 1, 0, ConfigDisableDecodeAlerts },
609 { CONFIG_OPT__DISABLE_DECODE_DROPS, 0, 1, 0, ConfigDisableDecodeDrops },
610 #ifdef INLINE_FAILOPEN
611 { CONFIG_OPT__DISABLE_INLINE_FAILOPEN, 0, 1, 1, ConfigDisableInlineFailopen },
612 #endif
613 { CONFIG_OPT__DISABLE_IP_OPT_ALERTS, 0, 1, 0, ConfigDisableIpOptAlerts },
614 { CONFIG_OPT__DISABLE_IP_OPT_DROPS, 0, 1, 0, ConfigDisableIpOptDrops },
615 { CONFIG_OPT__DISABLE_TCP_OPT_ALERTS, 0, 1, 0, ConfigDisableTcpOptAlerts },
616 { CONFIG_OPT__DISABLE_TCP_OPT_DROPS, 0, 1, 0, ConfigDisableTcpOptDrops },
617 { CONFIG_OPT__DISABLE_TCP_OPT_EXP_ALERTS, 0, 1, 0, ConfigDisableTcpOptExperimentalAlerts },
618 { CONFIG_OPT__DISABLE_TCP_OPT_EXP_DROPS, 0, 1, 0, ConfigDisableTcpOptExperimentalDrops },
619 { CONFIG_OPT__DISABLE_TCP_OPT_OBS_ALERTS, 0, 1, 0, ConfigDisableTcpOptObsoleteAlerts },
620 { CONFIG_OPT__DISABLE_TCP_OPT_OBS_DROPS, 0, 1, 0, ConfigDisableTcpOptObsoleteDrops },
621 { CONFIG_OPT__DISABLE_TTCP_ALERTS, 0, 1, 0, ConfigDisableTTcpAlerts },
622 { CONFIG_OPT__DISABLE_TCP_OPT_TTCP_ALERTS, 0, 1, 0, ConfigDisableTTcpAlerts },
623 { CONFIG_OPT__DISABLE_TTCP_DROPS, 0, 1, 0, ConfigDisableTTcpDrops },
624 { CONFIG_OPT__DUMP_CHARS_ONLY, 0, 1, 1, ConfigDumpCharsOnly },
625 { CONFIG_OPT__DUMP_PAYLOAD, 0, 1, 1, ConfigDumpPayload },
626 { CONFIG_OPT__DUMP_PAYLOAD_VERBOSE, 0, 1, 1, ConfigDumpPayloadVerbose },
627 { CONFIG_OPT__ENABLE_DECODE_DROPS, 0, 1, 0, ConfigEnableDecodeDrops },
628 { CONFIG_OPT__ENABLE_DECODE_OVERSIZED_ALERTS, 0, 1, 0, ConfigEnableDecodeOversizedAlerts },
629 { CONFIG_OPT__ENABLE_DECODE_OVERSIZED_DROPS, 0, 1, 0, ConfigEnableDecodeOversizedDrops },
630 { CONFIG_OPT__ENABLE_DEEP_TEREDO_INSPECTION, 0, 1, 1, ConfigEnableDeepTeredoInspection },
631 { CONFIG_OPT__ENABLE_GTP_DECODING, 0, 1, 1, ConfigEnableGTPDecoding },
632 { CONFIG_OPT__ENABLE_IP_OPT_DROPS, 0, 1, 1, ConfigEnableIpOptDrops },
633 #ifdef MPLS
634 { CONFIG_OPT__ENABLE_MPLS_MULTICAST, 0, 1, 1, ConfigEnableMplsMulticast },
635 { CONFIG_OPT__ENABLE_MPLS_OVERLAPPING_IP, 0, 1, 1, ConfigEnableMplsOverlappingIp },
636 #endif
637 { CONFIG_OPT__ENABLE_TCP_OPT_DROPS, 0, 1, 0, ConfigEnableTcpOptDrops },
638 { CONFIG_OPT__ENABLE_TCP_OPT_EXP_DROPS, 0, 1, 0, ConfigEnableTcpOptExperimentalDrops },
639 { CONFIG_OPT__ENABLE_TCP_OPT_OBS_DROPS, 0, 1, 0, ConfigEnableTcpOptObsoleteDrops },
640 { CONFIG_OPT__ENABLE_TTCP_DROPS, 0, 1, 0, ConfigEnableTTcpDrops },
641 { CONFIG_OPT__ENABLE_TCP_OPT_TTCP_DROPS, 0, 1, 0, ConfigEnableTTcpDrops },
642 { CONFIG_OPT__EVENT_FILTER, 1, 1, 1, ConfigEventFilter },
643 { CONFIG_OPT__EVENT_QUEUE, 1, 1, 1, ConfigEventQueue },
644 { CONFIG_OPT__EVENT_TRACE, 0, 1, 1, ConfigEventTrace },
645 { CONFIG_OPT__REACT, 1, 1, 1, ConfigReact },
646 #ifdef ENABLE_RESPONSE3
647 { CONFIG_OPT__FLEXRESP2_INTERFACE, 1, 1, 1, ConfigFlexresp2Interface },
648 { CONFIG_OPT__FLEXRESP2_ATTEMPTS, 1, 1, 1, ConfigFlexresp2Attempts },
649 { CONFIG_OPT__FLEXRESP2_MEMCAP, 1, 1, 1, ConfigFlexresp2Memcap },
650 { CONFIG_OPT__FLEXRESP2_ROWS, 1, 1, 1, ConfigFlexresp2Rows },
651 #endif
652 #ifdef ACTIVE_RESPONSE
653 { CONFIG_OPT__RESPONSE, 1, 1, 1, ConfigResponse },
654 #endif
655 { CONFIG_OPT__FLOWBITS_SIZE, 1, 1, 1, ConfigFlowbitsSize },
656 { CONFIG_OPT__IGNORE_PORTS, 1, 0, 1, ConfigIgnorePorts },
657 { CONFIG_OPT__ALERT_VLAN, 0, 1, 1, ConfigIncludeVlanInAlert },
658 { CONFIG_OPT__INTERFACE, 1, 1, 1, ConfigInterface },
659 { CONFIG_OPT__IPV6_FRAG, 1, 1, 1, ConfigIpv6Frag },
660 { CONFIG_OPT__LAYER2RESETS, 1, 1, 1, ConfigLayer2Resets },
661 { CONFIG_OPT__LOG_DIR, 1, 1, 1, ConfigLogDir },
662 { CONFIG_OPT__DAQ_TYPE, 1, 1, 1, ConfigDaqType },
663 { CONFIG_OPT__DAQ_MODE, 1, 1, 1, ConfigDaqMode },
664 { CONFIG_OPT__DAQ_VAR, 1, 0, 1, ConfigDaqVar },
665 { CONFIG_OPT__DAQ_DIR, 1, 0, 1, ConfigDaqDir },
666 { CONFIG_OPT__DIRTY_PIG, 0, 1, 1, ConfigDirtyPig },
667 #ifdef TARGET_BASED
668 { CONFIG_OPT__MAX_ATTRIBUTE_HOSTS, 1, 1, 1, ConfigMaxAttributeHosts },
669 { CONFIG_OPT__MAX_ATTRIBUTE_SERVICES_PER_HOST, 1, 1, 1, ConfigMaxAttributeServicesPerHost },
670 { CONFIG_OPT__MAX_METADATA_SERVICES, 1, 1, 1, ConfigMaxMetadataServices },
671 { CONFIG_OPT__DISABLE_ATTRIBUTE_RELOAD, 0, 1, 1, ConfigDisableAttributeReload },
672 #endif
673 #ifdef MPLS
674 { CONFIG_OPT__MAX_MPLS_LABELCHAIN_LEN, 0, 1, 1, ConfigMaxMplsLabelChain },
675 { CONFIG_OPT__MPLS_PAYLOAD_TYPE, 0, 1, 1, ConfigMplsPayloadType },
676 #endif
677 { CONFIG_OPT__MIN_TTL, 1, 1, 0, ConfigMinTTL },
678 #ifdef NORMALIZER
679 { CONFIG_OPT__NEW_TTL, 1, 1, 0, ConfigNewTTL },
680 #endif
681 { CONFIG_OPT__NO_LOG, 0, 1, 1, ConfigNoLog },
682 { CONFIG_OPT__NO_PCRE, 0, 1, 1, ConfigNoPcre },
683 { CONFIG_OPT__NO_PROMISCUOUS, 0, 1, 1, ConfigNoPromiscuous },
684 { CONFIG_OPT__OBFUSCATE, 0, 1, 1, ConfigObfuscate },
685 { CONFIG_OPT__ORDER, 1, 1, 1, ConfigRuleListOrder },
686 { CONFIG_OPT__PAF_MAX, 1, 1, 0, ConfigPafMax },
687 { CONFIG_OPT__PKT_COUNT, 1, 1, 1, ConfigPacketCount },
688 { CONFIG_OPT__PKT_SNAPLEN, 1, 1, 1, ConfigPacketSnaplen },
689 { CONFIG_OPT__PCRE_MATCH_LIMIT, 1, 1, 1, ConfigPcreMatchLimit },
690 { CONFIG_OPT__PCRE_MATCH_LIMIT_RECURSION, 1, 1, 1, ConfigPcreMatchLimitRecursion },
691 /* XXX We can configure this on the command line - why not in config file ??? */
692 #ifdef NOT_UNTIL_WE_DAEMONIZE_AFTER_READING_CONFFILE
693 { CONFIG_OPT__PID_PATH, 1, 1, 1, ConfigPidPath },
694 #endif
695 { CONFIG_OPT__POLICY, 1, 1, 0, ConfigPolicy },
696 { CONFIG_OPT__IPS_POLICY_MODE, 1, 1, 0, ConfigIpsPolicyMode },
697 { CONFIG_OPT__NAP_POLICY_MODE, 1, 1, 0, ConfigNapPolicyMode },
698 { CONFIG_OPT__POLICY_VERSION , 1, 0, 0, ConfigPolicyVersion },
699 { CONFIG_OPT__PROTECTED_CONTENT , 1, 0, 0, ConfigProtectedContent },
700 #ifdef PPM_MGR
701 { CONFIG_OPT__PPM, 1, 0, 1, ConfigPPM },
702 #endif
703 #ifdef PERF_PROFILING
704 { CONFIG_OPT__PROFILE_PREPROCS, 0, 1, 1, _ConfigProfilePreprocs },
705 { CONFIG_OPT__PROFILE_RULES, 0, 1, 1, _ConfigProfileRules },
706 #endif
707 { CONFIG_OPT__QUIET, 0, 1, 1, ConfigQuiet },
708 { CONFIG_OPT__RATE_FILTER, 1, 1, 1, ConfigRateFilter },
709 { CONFIG_OPT__REFERENCE, 1, 0, 1, ConfigReference },
710 { CONFIG_OPT__REFERENCE_NET, 1, 1, 1, ConfigReferenceNet },
711 { CONFIG_OPT__SET_GID, 1, 1, 1, ConfigSetGid },
712 { CONFIG_OPT__SET_UID, 1, 1, 1, ConfigSetUid },
713 { CONFIG_OPT__SHOW_YEAR, 0, 1, 1, ConfigShowYear },
714 { CONFIG_OPT__SO_RULE_MEMCAP, 1, 1, 1, ConfigSoRuleMemcap },
715 { CONFIG_OPT__STATEFUL, 0, 1, 1, ConfigStateful },
716 { CONFIG_OPT__TAGGED_PACKET_LIMIT, 1, 1, 1, ConfigTaggedPacketLimit },
717 { CONFIG_OPT__THRESHOLD, 1, 1, 1, ConfigThreshold },
718 { CONFIG_OPT__UMASK, 1, 1, 1, ConfigUmask },
719 { CONFIG_OPT__UTC, 0, 1, 1, ConfigUtc },
720 { CONFIG_OPT__VERBOSE, 0, 1, 1, ConfigVerbose },
721 { CONFIG_OPT__VLAN_AGNOSTIC, 0, 1, 1, ConfigVlanAgnostic },
722 { CONFIG_OPT__ADDRESSSPACE_AGNOSTIC, 0, 1, 1, ConfigAddressSpaceAgnostic },
723 { CONFIG_OPT__LOG_IPV6_EXTRA, 0, 1, 1, ConfigLogIPv6Extra },
724 { CONFIG_OPT__DUMP_DYNAMIC_RULES_PATH, 1, 1, 1, ConfigDumpDynamicRulesPath },
725 { CONFIG_OPT__CONTROL_SOCKET_DIR, 1, 1, 1, ConfigControlSocketDirectory },
726 { CONFIG_OPT__FILE, 1, 1, 1, ConfigFile },
727 { CONFIG_OPT__TUNNEL_BYPASS, 1, 1, 1, ConfigTunnelVerdicts },
728 #ifdef SIDE_CHANNEL
729 { CONFIG_OPT__SIDE_CHANNEL, 0, 1, 1, ConfigSideChannel },
730 #endif
731 { CONFIG_OPT__MAX_IP6_EXTENSIONS, 1, 1, 1, ConfigMaxIP6Extensions },
732 { CONFIG_OPT__DISABLE_REPLACE, 0, 1, 0, ConfigDisableReplace },
733 #ifdef DUMP_BUFFER
734 { CONFIG_OPT__BUFFER_DUMP, 1, 1, 1, ConfigBufferDump },
735 { CONFIG_OPT__BUFFER_DUMP_ALERT, 1, 1, 1, ConfigBufferDump },
736 #endif
737 { NULL, 0, 0, 0, NULL } /* Marks end of array */
738 };
739
740 /* Used to determine if a config option has already been configured
741 * Gets zeroed when initially parsing a configuration file, then each
742 * index gets set to 1 as an option is configured. Maps to config_opts */
743 static uint8_t config_opt_configured[sizeof(config_opts) / sizeof(ConfigFunc)];
744
745
746 /* Private function prototypes ************************************************/
747 static void InitVarTables(SnortPolicy *);
748 static void InitPolicyMode(SnortPolicy *);
749 static void InitParser(void);
750 static int VarIsIpAddr(vartable_t *, char *);
751 static RuleType GetRuleType(char *);
752 static void CreateDefaultRules(SnortConfig *);
753 static void ParseConfigFile(SnortConfig *, SnortPolicy *, char *);
754 static int ValidateUserDefinedRuleType(SnortConfig *, char *);
755 static void IntegrityCheckRules(SnortConfig *);
756 static void ParseDynamicLibInfo(DynamicLibInfo *, char *);
757 static int GetRuleProtocol(char *);
758 static RuleTreeNode * ProcessHeadNode(SnortConfig *, RuleTreeNode *, ListHead *);
759 static int ContinuationCheck(char *);
760 static ListHead * CreateRuleType(SnortConfig *, char *, RuleType, int, ListHead *);
761 static int GetChecksumFlags(char *);
762 static int GetPolicyMode(char *,int );
763 static void OtnInit(SnortConfig *);
764 static void _ParseRuleTypeDeclaration(SnortConfig *, FILE *, char *, int);
765 static void printRuleListOrder(RuleListNode *);
766 static RuleListNode * addNodeToOrderedList(RuleListNode *, RuleListNode *, int);
767 static VarEntry * VarDefine(SnortConfig *, char *, char *);
768 static char * VarSearch(SnortConfig *, char *);
769 static char * ExpandVars(SnortConfig *, char *);
770 static VarEntry * VarAlloc(void);
771 static int ParsePort(SnortConfig *, char *, uint16_t *, uint16_t *, char *, int *);
772 static uint16_t ConvPort(char *, char *);
773 static char * ReadLine(FILE *);
774 static void DeleteVars(VarEntry *);
775 static int ValidateIPList(IpAddrSet *, char *);
776 static int PortVarDefine(SnortConfig *, char *, char *);
777 static void port_entry_free(port_entry_t *);
778 static int port_list_add_entry(port_list_t *, port_entry_t *);
779 #if 0
780 static port_entry_t * port_list_get(port_list_t *, int);
781 static void port_list_print(port_list_t *);
782 #endif
783 static void port_list_free(port_list_t *);
784 static void finish_portlist_table(FastPatternConfig *, char *, PortTable *);
785 static void PortTablesFinish(rule_port_tables_t *, FastPatternConfig *);
786 static rule_port_tables_t * PortTablesNew(void);
787 static int FinishPortListRule(rule_port_tables_t *, RuleTreeNode *, OptTreeNode *,
788 int, port_entry_t *, FastPatternConfig *);
789 static PortObject * ParsePortListTcpUdpPort(PortVarTable *, PortTable *, char *);
790 static int ParsePortList(RuleTreeNode *, PortVarTable *, PortTable *, char *, int, int);
791 static void ParseRule(SnortConfig *, SnortPolicy *, char *, RuleType, ListHead *);
792 static void TransferOutputConfigs(OutputConfig *, OutputConfig **);
793 static OutputConfig * DupOutputConfig(OutputConfig *);
794 static void RemoveOutputConfigs(OutputConfig **, int);
795 static void XferHeader(RuleTreeNode *, RuleTreeNode *);
796 static OptTreeNode * ParseRuleOptions(SnortConfig *, RuleTreeNode **,
797 char *, RuleType, int);
798 #ifndef SOURCEFIRE
799 static void DefineAllIfaceVars(SnortConfig *);
800 static void DefineIfaceVar(SnortConfig *, char *, uint8_t *, uint8_t *);
801 #endif
802
803 #ifdef DEBUG_MSGS
804 #if 0
805 static void DumpList(IpAddrNode *, int);
806 #endif
807 static void DumpChain(char *, int, int);
808 static void DumpRuleChains(RuleListNode *);
809 #endif
810 static int ProcessIP(SnortConfig *, char *, RuleTreeNode *, int, int);
811 static int TestHeader(RuleTreeNode *, RuleTreeNode *);
812 static void FreeRuleTreeNode(RuleTreeNode *rtn);
813 static void DestroyRuleTreeNode(RuleTreeNode *rtn);
814 static void AddrToFunc(RuleTreeNode *, int);
815 static void PortToFunc(RuleTreeNode *, int, int, int);
816 static void SetupRTNFuncList(RuleTreeNode *);
817 static void DisallowCrossTableDuplicateVars(SnortConfig *, char *, VarType);
818 static int mergeDuplicateOtn(SnortConfig *, OptTreeNode *, OptTreeNode *, RuleTreeNode *);
819 #if 0
820 #ifdef DEBUG_MSGS
821 static void PrintRtnPorts(RuleTreeNode *);
822 #endif
823 #endif
824
825 static void FreeRuleTreeNodes(SnortConfig *);
826 static void FreeOutputLists(ListHead *list);
827
828 static int ParseNetworkBindingLine(tSfPolicyConfig *, int, char **, char *);
829 static int ParseVlanBindingLine(tSfPolicyConfig *, int, char **, char *);
830 static int ParsePolicyIdBindingLine(tSfPolicyConfig *, int, char **, char *);
831
832 static RuleTreeNode * findHeadNode(SnortConfig *, RuleTreeNode *, tSfPolicyId);
833 static inline RuleTreeNode * createDynamicRuleTypeRtn(SnortConfig *, RuleTreeNode *);
834
835 // only keep drop rules
836 // if we are inline (and can actually drop),
837 // or we are going to just alert instead of drop,
838 // or we are going to ignore session data instead of drop.
839 // the alert case is tested for separately with ScTreatDropAsAlert().
840 static inline int ScKeepDropRules (SnortConfig * sc)
841 {
842 return ( ScIpsInlineModeNewConf(sc) || ScAdapterInlineModeNewConf(sc) || ScTreatDropAsIgnoreNewConf(sc) );
843 }
844
845 static inline int ScLoadAsDropRules (SnortConfig * sc)
846 {
847 return ( ScIpsInlineTestModeNewConf(sc) || ScAdapterInlineTestModeNewConf(sc) );
848 }
849
850 /****************************************************************************
851 * Function: ParseSnortConf()
852 *
853 * Read the rules file a line at a time and send each rule to the rule parser
854 * This is the first pass of the configuration file. It parses everything
855 * except the rules.
856 *
857 * Arguments: None
858 *
859 * Returns:
860 * SnortConfig *
861 * An initialized and configured snort configuration struct.
862 * This struct should be passed on the second pass of the
863 * configuration file to parse the rules.
864 *
865 ***************************************************************************/
866 SnortConfig * ParseSnortConf(void)
867 {
868 SnortConfig *sc = SnortConfNew();
869 VarNode *tmp = cmd_line_var_list;
870 tSfPolicyId policy_id;
871
872 file_line = 0;
873 file_name = snort_conf_file ? snort_conf_file : NULL_CONF;
874
875 InitParser();
876
877 /* Setup the default rule action anchor points
878 * Need to do this now in case we get a user defined rule type */
879 CreateDefaultRules(sc);
880
881 sc->port_tables = PortTablesNew();
882
883 mpseInitSummary();
884 OtnInit(sc);
885
886 /* Used to store "config" configurations */
887 sc->config_table = sfghash_new(20, 0, 0, free);
888 if (sc->config_table == NULL)
889 {
890 ParseError("%s(%d) No memory to create config table.\n",
891 __FILE__, __LINE__);
892 }
893
894 sc->fast_pattern_config = FastPatternConfigNew();
895 sc->event_queue_config = EventQueueConfigNew();
896 sc->threshold_config = ThresholdConfigNew();
897 sc->rate_filter_config = RateFilter_ConfigNew();
898 sc->detection_filter_config = DetectionFilterConfigNew();
899 sc->ip_proto_only_lists = (SF_LIST **)SnortAlloc(NUM_IP_PROTOS * sizeof(SF_LIST *));
900
901 /* We're not going to parse rules on the first pass */
902 parse_rules = 0;
903
904 sc->policy_config = sfPolicyInit();
905 if (sc->policy_config == NULL)
906 {
907 ParseError("No memory to create policy configuration.\n");
908 }
909
910 /* Add the default policy */
911 policy_id = sfPolicyAdd(sc->policy_config, file_name);
912 sfSetDefaultPolicy(sc->policy_config, policy_id);
913 sfDynArrayCheckBounds((void **)&sc->targeted_policies, policy_id, &sc->num_policies_allocated);
914 sc->targeted_policies[policy_id] = SnortPolicyNew();
915 InitVarTables(sc->targeted_policies[policy_id]);
916 InitPolicyMode(sc->targeted_policies[policy_id]);
917 setParserPolicy(sc, policy_id);
918
919 #ifndef SOURCEFIRE
920 /* If snort is not run with root privileges, no interfaces will be defined,
921 * so user beware if an iface_ADDRESS variable is used in snort.conf and
922 * snort is not run as root (even if just in read mode) */
923 DefineAllIfaceVars(sc);
924 #endif
925
926 /* Add command line defined variables - duplicates will already
927 * have been resolved */
928 while (tmp != NULL)
929 {
930 AddVarToTable(sc, tmp->name, tmp->value);
931 tmp = tmp->next;
932 }
933
934 /* Initialize storage space for preprocessor defined rule options */
935 sc->preproc_rule_options = PreprocessorRuleOptionsNew();
936 if (sc->preproc_rule_options == NULL)
937 {
938 ParseError("Could not allocate storage for preprocessor rule options.\n");
939 }
940
941 if ( strcmp(file_name, NULL_CONF) )
942 ParseConfigFile(sc, sc->targeted_policies[policy_id], file_name);
943
944 /* We've picked up any targeted policy configs at this point so
945 * it's probably okay to parse them here */
946 for (policy_id = 0;
947 policy_id < sfPolicyNumAllocated(sc->policy_config);
948 policy_id++)
949 {
950 char *fname = sfPolicyGet(sc->policy_config, policy_id);
951
952 /* Already configured default policy */
953 if (policy_id == sfGetDefaultPolicy(sc->policy_config))
954 continue;
955
956 if (fname != NULL)
957 {
958 sfDynArrayCheckBounds(
959 (void **)&sc->targeted_policies, policy_id, &sc->num_policies_allocated);
960 sc->targeted_policies[policy_id] = SnortPolicyNew();
961
962 InitVarTables(sc->targeted_policies[policy_id]);
963 InitPolicyMode(sc->targeted_policies[policy_id]);
964 setParserPolicy(sc, policy_id);
965
966 /* Need to reset this for each targeted policy */
967 memset(config_opt_configured, 0, sizeof(config_opt_configured));
968
969 /* Add command line defined variables - duplicates will already
970 * have been resolved */
971 tmp = cmd_line_var_list;
972 while (tmp != NULL)
973 {
974 AddVarToTable(sc, tmp->name, tmp->value);
975 tmp = tmp->next;
976 }
977
978 /* Parse as include file so if the file is specified relative to
979 * the snort conf directory we'll pick it up */
980 ParseInclude(sc, sc->targeted_policies[policy_id], fname);
981 }
982 }
983
984 /* This can be initialized now since we've picked up any user
985 * defined rules */
986 sc->omd = OtnXMatchDataNew(sc->num_rule_types);
987
988 /* Reset these. The only issue in not reseting would be if we were
989 * parsing a command line again, but do it anyway */
990 file_name = NULL;
991 file_line = 0;
992
993 return sc;
994 }
995
996 static int ParsePolicyIdBindingLine(
997 tSfPolicyConfig *config,
998 int num_toks,
999 char **toks,
1000 char *fileName
1001 )
1002 {
1003 int i;
1004 int parsedPolicyId;
1005
1006 for (i = 0; i < num_toks; i++)
1007 {
1008 char *endp;
1009 if ( toks[i] )
1010 {
1011 errno = 0;
1012 parsedPolicyId = SnortStrtolRange(toks[i], &endp, 10, 0, USHRT_MAX);
1013 if ((errno == ERANGE) || (*endp != '\0'))
1014 return -1;
1015
1016 if ( sfPolicyIdAddBinding(config, parsedPolicyId, fileName) != 0)
1017 {
1018 return -1;
1019 //FatalError("Unable to add policy: policyId %d, file %s\n", parsedPolicyId, fileName);
1020 }
1021 }
1022 else
1023 {
1024 return -1;
1025 //FatalError("formating error in binding file: %s\n", aLine);
1026 }
1027 }
1028
1029 return 0;
1030 }
1031
1032 static int ParseVlanBindingLine(
1033 tSfPolicyConfig *config,
1034 int num_toks,
1035 char **toks,
1036 char *fileName
1037 )
1038 {
1039 int i;
1040 int vlanId1=0, vlanId2=0;
1041
1042
1043 for (i = 0; i < num_toks; i++)
1044 {
1045 int num_tok2;
1046 char **toks2;
1047 int vlanId;
1048 unsigned pos;
1049 char *findStr1, *findStr2;
1050 char *endp;
1051 findStr1 = strchr(toks[i], '-');
1052 if ( findStr1 )
1053 {
1054 pos = findStr1 - toks[i];
1055 if ( pos == 0 || pos == (strlen(toks[i]) - 1) )
1056 {
1057 return -1;
1058 }
1059 findStr2 = strchr(findStr1+1, '-');
1060 if ( findStr2 )
1061 {
1062 return -1;
1063 }
1064 toks2 = mSplit(toks[i], "-", 2, &num_tok2, 0);
1065 if (num_tok2 == 2)
1066 {
1067 /* vlanId1 must be < SF_VLAN_BINDING_MAX -1
1068 to allow for an actual range */
1069 vlanId1 = SnortStrtolRange(toks2[0], &endp, 10, 0, SF_VLAN_BINDING_MAX-1);
1070 if( *endp )
1071 {
1072 mSplitFree(&toks2, num_tok2);
1073 return -1;
1074 }
1075 /* vlanId2 must be > vlanId1 */
1076 vlanId2 = SnortStrtolRange(toks2[1], &endp, 10, vlanId1+1, SF_VLAN_BINDING_MAX);
1077 if ( *endp )
1078 {
1079 mSplitFree(&toks2, num_tok2);
1080 return -1;
1081 }
1082 if ( (vlanId1 < 0)
1083 || (vlanId2 >= SF_VLAN_BINDING_MAX)
1084 || (vlanId1 > vlanId2) )
1085 {
1086 mSplitFree(&toks2, num_tok2);
1087 return -1;
1088 //FatalError("Invalid range: %d:%d\n", vlanId1, vlanId2);
1089 }
1090 for (vlanId = vlanId1; vlanId <= vlanId2; vlanId++)
1091 {
1092 if ( sfVlanAddBinding(config, vlanId, fileName) != 0)
1093 {
1094 mSplitFree(&toks2, num_tok2);
1095 return -1;
1096 //FatalError("Unable to add policy: vlan %d, file %s\n", vlanId, fileName);
1097 }
1098 }
1099 }
1100 else
1101 {
1102 mSplitFree(&toks2, num_tok2);
1103 return -1;
1104 }
1105 mSplitFree(&toks2, num_tok2);
1106 }
1107
1108 else if ( toks[i] )
1109 {
1110 vlanId = SnortStrtolRange(toks[i], &endp, 10, 0, SF_VLAN_BINDING_MAX-1);
1111 if( *endp )
1112 return -1;
1113 if ( (vlanId >= SF_VLAN_BINDING_MAX) || sfVlanAddBinding(config, vlanId, fileName) != 0)
1114 {
1115 return -1;
1116 //FatalError("Unable to add policy: vlan %d, file %s\n", vlanId, fileName);
1117 }
1118 }
1119 else
1120 {
1121 return -1;
1122 //FatalError("formating error in binding file: %s\n", aLine);
1123 }
1124 }
1125
1126 return 0;
1127 }
1128
1129 static int ParseNetworkBindingLine(
1130 tSfPolicyConfig *config,
1131 int num_toks,
1132 char **toks,
1133 char *fileName
1134 )
1135 {
1136 int i;
1137
1138 for (i = 0; i < num_toks; i++)
1139 {
1140 SFIP_RET status;
1141 sfcidr_t *sfip;
1142
1143 if( (sfip = sfip_alloc(toks[i], &status)) == NULL )
1144 {
1145 return -1;
1146 }
1147 if (sfNetworkAddBinding(config, sfip, fileName) != 0)
1148 {
1149 sfip_free(sfip);
1150 return -1;
1151 // FatalError("Unable to add policy: vlan %d, file %s\n", vlanId, fileName);
1152 }
1153
1154 sfip_free(sfip);
1155 }
1156
1157 return 0;
1158 }
1159
1160 #ifdef DEBUG_MSGS
1161 static void DumpRuleChains(RuleListNode *rule_lists)
1162 {
1163 RuleListNode *rule = rule_lists;
1164
1165 if (rule_lists == NULL)
1166 return;
1167
1168 while (rule != NULL)
1169 {
1170 DumpChain(rule->name, rule->mode, ETHERNET_TYPE_IP);
1171 DumpChain(rule->name, rule->mode, IPPROTO_TCP);
1172 DumpChain(rule->name, rule->mode, IPPROTO_UDP);
1173 DumpChain(rule->name, rule->mode, IPPROTO_ICMP);
1174
1175 rule = rule->next;
1176 }
1177 }
1178
1179 /****************************************************************************
1180 *
1181 * Function: DumpChain(RuleTreeNode *, char *, char *)
1182 *
1183 * Purpose: Iterate over RTNs calling DumpList on each
1184 *
1185 * Arguments: rtn_idx => the RTN index pointer
1186 * rulename => the name of the rule the list belongs to
1187 * listname => the name of the list being printed out
1188 *
1189 * Returns: void function
1190 *
1191 ***************************************************************************/
1192 static void DumpChain(char *name, int mode, int proto)
1193 {
1194 // XXX Not yet implemented - Rule chain dumping
1195 }
1196
1197 /****************************************************************************
1198 *
1199 * Function: DumpList(IpAddrNode*)
1200 *
1201 * Purpose: print out the chain lists by header block node group
1202 *
1203 * Arguments: node => the head node
1204 *
1205 * Returns: void function
1206 *
1207 ***************************************************************************/
1208 #if 0 // avoid warning: ‘DumpList’ defined but not used
1209 static void DumpList(IpAddrNode *idx, int negated)
1210 {
1211 DEBUG_WRAP(int i=0;);
1212 if(!idx)
1213 return;
1214
1215 while(idx != NULL)
1216 {
1217 DEBUG_WRAP(DebugMessage(DEBUG_RULES,
1218 "[%d] %s",
1219 i++, sfip_ntoa(idx->ip)););
1220
1221 if(negated)
1222 {
1223 DEBUG_WRAP(DebugMessage(DEBUG_RULES,
1224 " (EXCEPTION_FLAG Active)\n"););
1225 }
1226 else
1227 {
1228 DEBUG_WRAP(DebugMessage(DEBUG_RULES, "\n"););
1229 }
1230
1231 idx = idx->next;
1232 }
1233 }
1234 #endif /* 0 */
1235 #endif /* DEBUG */
1236
1237 /*
1238 * Finish adding the rule to the port tables
1239 *
1240 * 1) find the table this rule should belong to (src/dst/any-any tcp,udp,icmp,ip or nocontent)
1241 * 2) find an index for the sid:gid pair
1242 * 3) add all no content rules to a single no content port object, the ports are irrelevant so
1243 * make it a any-any port object.
1244 * 4) if it's an any-any rule with content, add to an any-any port object
1245 * 5) find if we have a port object with these ports defined, if so get it, otherwise create it.
1246 * a)do this for src and dst port
1247 * b)add the rule index/id to the portobject(s)
1248 * c)if the rule is bidir add the rule and port-object to both src and dst tables
1249 *
1250 */
1251 static int FinishPortListRule(rule_port_tables_t *port_tables, RuleTreeNode *rtn, OptTreeNode *otn,
1252 int proto, port_entry_t *pe, FastPatternConfig *fp)
1253 {
1254 int large_port_group = 0;
1255 int src_cnt = 0;
1256 int dst_cnt = 0;
1257 int rim_index;
1258 PortTable *dstTable;
1259 PortTable *srcTable;
1260 #ifdef TARGET_BASED
1261 PortTable *dstTable_noservice;
1262 PortTable *srcTable_noservice;
1263 #endif
1264 PortObject *aaObject;
1265 rule_count_t *prc;
1266
1267 #ifdef TARGET_BASED
1268 ServiceOverride service_override = otn->sigInfo.service_override;
1269 int num_services = otn->sigInfo.num_services;
1270
1271 if ( !IsAdaptiveConfigured() )
1272 otn->sigInfo.service_override = ServiceOverride_ElsePorts;
1273 else if ( service_override == ServiceOverride_Nil && num_services )
1274 otn->sigInfo.service_override = ServiceOverride_ElsePorts;
1275 else if ( service_override == ServiceOverride_Nil && !num_services )
1276 otn->sigInfo.service_override = ServiceOverride_OrPorts;
1277 #endif
1278 /* Select the Target PortTable for this rule, based on protocol, src/dst
1279 * dir, and if there is rule content */
1280 if (proto == IPPROTO_TCP)
1281 {
1282 dstTable = port_tables->tcp_dst;
1283 srcTable = port_tables->tcp_src;
1284 aaObject = port_tables->tcp_anyany;
1285 prc = &tcpCnt;
1286 #ifdef TARGET_BASED
1287 dstTable_noservice = port_tables->ns_tcp_dst;
1288 srcTable_noservice = port_tables->ns_tcp_src;
1289 #endif
1290 }
1291 else if (proto == IPPROTO_UDP)
1292 {
1293 dstTable = port_tables->udp_dst;
1294 srcTable = port_tables->udp_src;
1295 aaObject = port_tables->udp_anyany;
1296 prc = &udpCnt;
1297 #ifdef TARGET_BASED
1298 dstTable_noservice = port_tables->ns_udp_dst;
1299 srcTable_noservice = port_tables->ns_udp_src;
1300 #endif
1301 }
1302 else if (proto == IPPROTO_ICMP)
1303 {
1304 dstTable = port_tables->icmp_dst;
1305 srcTable = port_tables->icmp_src;
1306 aaObject = port_tables->icmp_anyany;
1307 prc = &icmpCnt;
1308 #ifdef TARGET_BASED
1309 dstTable_noservice = port_tables->ns_icmp_dst;
1310 srcTable_noservice = port_tables->ns_icmp_src;
1311 #endif
1312 }
1313 else if (proto == ETHERNET_TYPE_IP)
1314 {
1315 dstTable = port_tables->ip_dst;
1316 srcTable = port_tables->ip_src;
1317 aaObject = port_tables->ip_anyany;
1318 prc = &ipCnt;
1319 #ifdef TARGET_BASED
1320 dstTable_noservice = port_tables->ns_ip_dst;
1321 srcTable_noservice = port_tables->ns_ip_src;
1322 #endif
1323 }
1324 else
1325 {
1326 return -1;
1327 }
1328
1329 /* Count rules with both src and dst specific ports */
1330 if (!(rtn->flags & ANY_DST_PORT) && !(rtn->flags & ANY_SRC_PORT))
1331 {
1332 DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,
1333 "***\n***Info: src & dst ports are both specific"
1334 " >> gid=%u sid=%u src=%s dst=%s\n***\n",
1335 otn->sigInfo.generator, otn->sigInfo.id,
1336 pe->src_port, pe->dst_port););
1337
1338 prc->sd++;
1339 }
1340
1341 /* Create/find an index to store this rules sid and gid at,
1342 * and use as reference in Port Objects */
1343 rim_index = otn->ruleIndex;
1344
1345 /* Add up the nocontent rules */
1346 if (!pe->content && !pe->uricontent)
1347 prc->nc++;
1348
1349 /* If not an any-any rule test for port bleedover, if we are using a
1350 * single rule group, don't bother */
1351 if (!fpDetectGetSingleRuleGroup(fp) &&
1352 (rtn->flags & (ANY_DST_PORT|ANY_SRC_PORT)) != (ANY_DST_PORT|ANY_SRC_PORT))
1353 {
1354 if (!(rtn->flags & ANY_SRC_PORT))
1355 {
1356 src_cnt = PortObjectPortCount(rtn->src_portobject);
1357 if (src_cnt >= fpDetectGetBleedOverPortLimit(fp))
1358 large_port_group = 1;
1359 }
1360
1361 if (!(rtn->flags & ANY_DST_PORT))
1362 {
1363 dst_cnt = PortObjectPortCount(rtn->dst_portobject);
1364 if (dst_cnt >= fpDetectGetBleedOverPortLimit(fp))
1365 large_port_group = 1;
1366 }
1367
1368 if (large_port_group && fpDetectGetBleedOverWarnings(fp))
1369 {
1370
1371 LogMessage("***Bleedover Port Limit(%d) Exceeded for rule %u:%u "
1372 "(%d)ports: ", fpDetectGetBleedOverPortLimit(fp),
1373 otn->sigInfo.generator, otn->sigInfo.id,
1374 (src_cnt > dst_cnt) ? src_cnt : dst_cnt);
1375
1376 /* If logging to syslog, this will be all multiline */
1377 fflush(stdout); fflush(stderr);
1378 PortObjectPrintPortsRaw(rtn->src_portobject);
1379 LogMessage(" -> ");
1380 PortObjectPrintPortsRaw(rtn->dst_portobject);
1381 LogMessage(" adding to any-any group\n");
1382 fflush(stdout);fflush(stderr);
1383 }
1384 }
1385
1386 /* If an any-any rule add rule index to any-any port object
1387 * both content and no-content type rules go here if they are
1388 * any-any port rules...
1389 * If we have an any-any rule or a large port group or
1390 * were using a single rule group we make it an any-any rule. */
1391 if (((rtn->flags & (ANY_DST_PORT|ANY_SRC_PORT)) == (ANY_DST_PORT|ANY_SRC_PORT)) ||
1392 large_port_group || fpDetectGetSingleRuleGroup(fp))
1393 {
1394 if (proto == ETHERNET_TYPE_IP)
1395 {
1396 /* Add the IP rules to the higher level app protocol groups, if they apply
1397 * to those protocols. All IP rules should have any-any port descriptors
1398 * and fall into this test. IP rules that are not tcp/udp/icmp go only into the
1399 * IP table */
1400 DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,
1401 "Finishing IP any-any rule %u:%u\n",
1402 otn->sigInfo.generator,otn->sigInfo.id););
1403
1404 switch (GetOtnIpProto(otn))
1405 {
1406 case IPPROTO_TCP:
1407 PortObjectAddRule(port_tables->tcp_anyany, rim_index);
1408 tcpCnt.aa++;
1409 break;
1410
1411 case IPPROTO_UDP:
1412 PortObjectAddRule(port_tables->udp_anyany, rim_index);
1413 udpCnt.aa++;
1414 break;
1415
1416 case IPPROTO_ICMP:
1417 PortObjectAddRule(port_tables->icmp_anyany, rim_index);
1418 icmpCnt.aa++;
1419 break;
1420
1421 case -1: /* Add to all ip proto anyany port tables */
1422 PortObjectAddRule(port_tables->tcp_anyany, rim_index);
1423 tcpCnt.aa++;
1424
1425 PortObjectAddRule(port_tables->udp_anyany, rim_index);
1426 udpCnt.aa++;
1427
1428 PortObjectAddRule(port_tables->icmp_anyany, rim_index);
1429 icmpCnt.aa++;
1430
1431 break;
1432
1433 default:
1434 break;
1435 }
1436
1437 /* Add to the IP ANY ANY */
1438 PortObjectAddRule(aaObject, rim_index);
1439 prc->aa++;
1440 }
1441 else
1442 {
1443 /* For other protocols-tcp/udp/icmp add to the any any group */
1444 PortObjectAddRule(aaObject, rim_index);
1445 prc->aa++;
1446 }
1447
1448 return 0; /* done */
1449 }
1450
1451 /* add rule index to dst table if we have a specific dst port or port list */
1452 if (!(rtn->flags & ANY_DST_PORT))
1453 {
1454 PortObject *pox;
1455
1456 prc->dst++;
1457
1458 DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,
1459 "Finishing rule: dst port rule\n"););
1460
1461 /* find the proper port object */
1462 pox = PortTableFindInputPortObjectPorts(dstTable, rtn->dst_portobject);
1463 if (pox == NULL)
1464 {
1465 /* Create a permanent port object */
1466 pox = PortObjectDupPorts(rtn->dst_portobject);
1467 if (pox == NULL)
1468 {
1469 ParseError("Could not dup a port object - out of memory!\n");
1470 }
1471
1472 /* Add the port object to the table, and add the rule to the port object */
1473 PortTableAddObject(dstTable, pox);
1474 }
1475
1476 PortObjectAddRule(pox, rim_index);
1477
1478 /* if bidir, add this rule and port group to the src table */
1479 if (rtn->flags & BIDIRECTIONAL)
1480 {
1481 pox = PortTableFindInputPortObjectPorts(srcTable, rtn->dst_portobject);
1482 if (pox == NULL)
1483 {
1484 pox = PortObjectDupPorts(rtn->dst_portobject);
1485 if (pox == NULL)
1486 {
1487 ParseError("Could not dup a bidir-port object - out of memory!\n");
1488 }
1489
1490 PortTableAddObject(srcTable, pox);
1491 }
1492
1493 PortObjectAddRule(pox, rim_index);
1494 }
1495 #ifdef TARGET_BASED
1496 // Add to the NOSERVICE group
1497 if (IsAdaptiveConfigured()
1498 && (otn->sigInfo.num_services == 0 || otn->sigInfo.service_override == ServiceOverride_OrPorts))
1499 {
1500
1501 if ( otn->sigInfo.service_override == ServiceOverride_AndPorts )
1502 ParseError("Service override \"and-ports\" specified on a port only rule.");
1503
1504 pox = PortTableFindInputPortObjectPorts(dstTable_noservice, rtn->dst_portobject);
1505 if (pox == NULL)
1506 {
1507 pox = PortObjectDupPorts(rtn->dst_portobject);
1508
1509 if (pox == NULL)
1510 ParseError("Could not dup a port object - out of memory!");
1511
1512 PortTableAddObject(dstTable_noservice, pox);
1513 }
1514
1515 PortObjectAddRule(pox, rim_index);
1516 }
1517 #endif // TARGET_BASED
1518 }
1519
1520 /* add rule index to src table if we have a specific src port or port list */
1521 if (!(rtn->flags & ANY_SRC_PORT))
1522 {
1523 PortObject *pox;
1524
1525 prc->src++;
1526
1527 pox = PortTableFindInputPortObjectPorts(srcTable, rtn->src_portobject);
1528 if (pox == NULL)
1529 {
1530 pox = PortObjectDupPorts(rtn->src_portobject);
1531 if (pox == NULL)
1532 {
1533 ParseError("Could not dup a port object - out of memory!\n");
1534 }
1535
1536 PortTableAddObject(srcTable, pox);
1537 }
1538
1539 PortObjectAddRule(pox, rim_index);
1540
1541 /* if bidir, add this rule and port group to the dst table */
1542 if (rtn->flags & BIDIRECTIONAL)
1543 {
1544 pox = PortTableFindInputPortObjectPorts(dstTable, rtn->src_portobject);
1545 if (pox == NULL)
1546 {
1547 pox = PortObjectDupPorts(rtn->src_portobject);
1548 if (pox == NULL)
1549 {
1550 ParseError("Could not dup a bidir-port object - out "
1551 "of memory!\n");
1552 }
1553
1554 PortTableAddObject(dstTable, pox);
1555 }
1556
1557 PortObjectAddRule(pox, rim_index);
1558 }
1559
1560 #ifdef TARGET_BASED
1561 // Add to the NOSERVICE group
1562 if (IsAdaptiveConfigured()
1563 && (otn->sigInfo.num_services == 0 || otn->sigInfo.service_override == ServiceOverride_OrPorts))
1564 {
1565 if ( otn->sigInfo.service_override == ServiceOverride_AndPorts )
1566 ParseError("Service override \"and-ports\" specified on a port only rule.");
1567
1568 /* find the proper port object */
1569 pox = PortTableFindInputPortObjectPorts(srcTable_noservice, rtn->src_portobject);
1570 if (pox == NULL)
1571 {
1572 pox = PortObjectDupPorts(rtn->src_portobject);
1573
1574 if (pox == NULL)
1575 ParseError("Could not dup a port object - out of memory!\n");
1576
1577 PortTableAddObject(srcTable_noservice, pox);
1578 }
1579
1580 PortObjectAddRule(pox, rim_index);
1581 }
1582 #endif // TARGET_BASED
1583 }
1584
1585 return 0;
1586 }
1587 /*
1588 * Parse a port string as a port var, and create or find a port object for it,
1589 * and add it to the port var table. These are used by the rtn's
1590 * as src and dst port lists for final rtn/otn processing.
1591 *
1592 * These should not be confused with the port objects used to merge ports and rules
1593 * to build PORT_GROUP objects. Those are generated after the otn processing.
1594 *
1595 */
1596 static PortObject * ParsePortListTcpUdpPort(PortVarTable *pvt,
1597 PortTable *noname, char *port_str)
1598 {
1599 PortObject * portobject;
1600 //PortObject * pox;
1601 char * errstr=0;
1602 POParser poparser;
1603
1604 if ((pvt == NULL) || (noname == NULL) || (port_str == NULL))
1605 return NULL;
1606
1607 /* 1st - check if we have an any port */
1608 if( strcasecmp(port_str,"any")== 0 )
1609 {
1610 portobject = PortVarTableFind(pvt, "any");
1611 if (portobject == NULL)
1612 ParseError("PortVarTable missing an 'any' variable.");
1613
1614 return portobject;
1615 }
1616
1617 /* 2nd - check if we have a PortVar */
1618 else if( port_str[0]=='$' )
1619 {
1620 /*||isalpha(port_str[0])*/ /*TODO: interferes with protocol names for ports*/
1621 char * name = port_str + 1;
1622
1623 DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"PortVarTableFind: finding '%s'\n", port_str););
1624
1625 /* look it up in the port var table */
1626 portobject = PortVarTableFind(pvt, name);
1627 if (portobject == NULL)
1628 ParseError("***PortVar Lookup failed on '%s'.", port_str);
1629
1630 DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"PortVarTableFind: '%s' found!\n", port_str););
1631 }
1632
1633 /* 3rd - and finally process a raw port list */
1634 else
1635 {
1636 /* port list = [p,p,p:p,p,...] or p or p:p , no embedded spaces due to tokenizer */
1637 PortObject * pox;
1638
1639 DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,
1640 "parser.c->PortObjectParseString: parsing '%s'\n",port_str););
1641
1642 portobject = PortObjectParseString(pvt, &poparser, 0, port_str, 0);
1643
1644 DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,
1645 "parser.c->PortObjectParseString: '%s' done.\n",port_str););
1646
1647 if( !portobject )
1648 {
1649 errstr = PortObjectParseError( &poparser );
1650 ParseError("***Rule--PortVar Parse error: (pos=%d,error=%s)\n>>%s\n>>%*s\n",
1651 poparser.pos,errstr,port_str,poparser.pos,"^");
1652 }
1653
1654 /* check if we already have this port object in the un-named port var table ... */
1655 pox = PortTableFindInputPortObjectPorts(noname, portobject);
1656 if( pox )
1657 {
1658 DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,
1659 "parser.c: already have '%s' as a PortObject - "
1660 "calling PortObjectFree(portbject) line=%d\n",port_str,__LINE__ ););
1661 PortObjectFree( portobject );
1662 portobject = pox;
1663 }
1664 else
1665 {
1666 DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,
1667 "parser.c: adding '%s' as a PortObject line=%d\n",port_str,__LINE__ ););
1668 /* Add to the un-named port var table */
1669 if (PortTableAddObject(noname, portobject))
1670 {
1671 ParseError("Unable to add raw port object to unnamed "
1672 "port var table, out of memory!\n");
1673 }
1674 }
1675 }
1676
1677 return portobject;
1678 }
1679 #ifdef XXXXX
1680 /*
1681 * Extract the Icmp Type field to determine the PortGroup.
1682 */
1683 PortObject * GetPortListIcmpPortObject( OptTreeNode * otn, PortTable * rulesPortTable, PortObject * anyAnyPortObject )
1684 {
1685 PortObject * portobject=0;
1686 int type;
1687 IcmpTypeCheckData * IcmpType;
1688
1689 IcmpType = (IcmpTypeCheckData *)otn->ds_list[PLUGIN_ICMP_TYPE];
1690
1691 if( IcmpType && (IcmpType->operator == ICMP_TYPE_TEST_EQ) )
1692 {
1693 type = IcmpType->icmp_type;
1694 }
1695 else
1696 {
1697 return anyAnyPortObject;
1698 }
1699
1700 /* TODO: optimize */
1701 return anyAnyPortObject;
1702 }
1703 /*
1704 * Extract the IP Protocol field to determine the PortGroup.
1705 */
1706 PortObject * GetPortListIPPortObject( OptTreeNode * otn,PortTable * rulesPortTable, PortObject * anyAnyPortObject )
1707 {
1708 if (GetOtnIpProto(otn) == -1)
1709 return anyAnyPortObject;
1710
1711 /* TODO: optimize */
1712 return anyAnyPortObject;
1713 }
1714
1715 #if 0
1716 Not currently used
1717 /*
1718 * Extract the Icmp Type field to determine the PortGroup.
1719 */
1720 static
1721 int GetOtnIcmpType(OptTreeNode * otn )
1722 {
1723 int type;
1724 IcmpTypeCheckData * IcmpType;
1725
1726 IcmpType = (IcmpTypeCheckData *)otn->ds_list[PLUGIN_ICMP_TYPE];
1727
1728 if( IcmpType && (IcmpType->operator == ICMP_TYPE_TEST_EQ) )
1729 {
1730 type = IcmpType->icmp_type;
1731 }
1732 else
1733 {
1734 return -1;
1735 }
1736
1737 return -1;
1738 }
1739 #endif
1740
1741 #endif /* XXXX - PORTLISTS */
1742 /*
1743 * Process the rule, add it to the appropriate PortObject
1744 * and add the PortObject to the rtn.
1745 *
1746 * TCP/UDP rules use ports/portlists, icmp uses the icmp type field and ip uses the protocol
1747 * field as a dst port for the purposes of looking up a rule group as packets are being
1748 * processed.
1749 *
1750 * TCP/UDP- use src/dst ports
1751 * ICMP - use icmp type as dst port,src=-1
1752 * IP - use protocol as dst port,src=-1
1753 *
1754 * rtn - proto_node
1755 * port_str - port list string or port var name
1756 * proto - protocol
1757 * dst_flag - dst or src port flag, true = dst, false = src
1758 *
1759 */
1760 static int ParsePortList(RuleTreeNode *rtn, PortVarTable *pvt, PortTable *noname,
1761 char *port_str, int proto, int dst_flag)
1762 {
1763 PortObject *portobject = NULL; /* src or dst */
1764
1765 /* Get the protocol specific port object */
1766 if( proto == IPPROTO_TCP || proto == IPPROTO_UDP )
1767 {
1768 portobject = ParsePortListTcpUdpPort(pvt, noname, port_str);
1769 }
1770 else /* ICMP, IP - no real ports just Type and Protocol */
1771 {
1772 portobject = PortVarTableFind(pvt, "any");
1773 if (portobject == NULL)
1774 {
1775 ParseError("PortVarTable missing an 'any' variable\n");
1776 }
1777 }
1778
1779 DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"Rule-PortVar Parsed: %s \n",port_str););
1780
1781 /* !ports - port lists can be mixed 80:90,!82,
1782 * so the old NOT flag is depracated for port lists
1783 */
1784
1785 /* set up any any flags */
1786 if( PortObjectHasAny(portobject) )
1787 {
1788 if( dst_flag )
1789 rtn->flags |= ANY_DST_PORT;
1790 else
1791 rtn->flags |= ANY_SRC_PORT;
1792 }
1793
1794 /* check for a pure not rule - fatal if we find one */
1795 if( PortObjectIsPureNot( portobject ) )
1796 {
1797 ParseError("Pure NOT ports are not allowed!");
1798 /*
1799 if( dst_flag )
1800 rtn->flags |= EXCEPT_DST_PORT;
1801 else
1802 rtn->flags |= EXCEPT_SRC_PORT;
1803 */
1804 }
1805
1806 /*
1807 * set to the port object for this rules src/dst port,
1808 * these are used during rtn/otn port verification of the rule.
1809 */
1810
1811 if (dst_flag)
1812 rtn->dst_portobject = portobject;
1813 else
1814 rtn->src_portobject = portobject;
1815
1816 return 0;
1817 }
1818
1819 /*
1820 * ParseIpsPortList() will create portlist for portocol specific and only for IPS policy at the snort
1821 * process bringup, it will be used to enable/disable detection on packet.
1822 */
1823 IpsPortFilter** ParseIpsPortList (SnortConfig *sc, IpProto protocol)
1824 {
1825 tSfPolicyId policyId;
1826 IpsPortFilter *ips_portfilter;
1827 bool ignore_any = false;
1828
1829 // Allocate memory for each policy to hold port filter list
1830 IpsPortFilter **ips_port_filter_list = ( IpsPortFilter** ) SnortAlloc( sizeof(IpsPortFilter*) * sfPolicyNumAllocated(sc->policy_config) );
1831
1832 if ( !ips_port_filter_list )
1833 {
1834 ParseError("IPS portlist memory allocation failed\n");
1835 return NULL;
1836 }
1837
1838 ignore_any = getStreamIgnoreAnyConfig(sc, protocol);
1839 for (policyId = 0; policyId < sfPolicyNumAllocated(sc->policy_config); policyId++)
1840 {
1841 ips_portfilter = NULL;
1842 // Create port filter list for default and IPS policy
1843 if ( (policyId == 0) || (!getStreamPolicyConfig(policyId, 0)) )
1844 {
1845 ips_portfilter = ( IpsPortFilter* ) SnortAlloc( sizeof(IpsPortFilter) );
1846 if ( ips_portfilter )
1847 {
1848 ips_portfilter->parserPolicyId = policyId;
1849 setPortFilterList(sc, ips_portfilter->port_filter, IPPROTO_UDP, ignore_any, policyId);
1850 ips_port_filter_list[ policyId ] = ips_portfilter;
1851 } else
1852 {
1853 ParseError("Failed to allocate memory for port filter list policy id :%d \n",policyId);
1854 return NULL;
1855 }
1856 } else
1857 {
1858 // NAP policy port filter list is created in pre-processor check
1859 ips_port_filter_list[ policyId ] = NULL;
1860 }
1861 }
1862 return ips_port_filter_list;
1863 }
1864
1865 /****************************************************************************
1866 *
1867 * Function: CheckForIPListConflicts
1868 *
1869 * Purpose: Checks For IP List Conflicts in a RuleTreeNode. Such as
1870 * negations that are overlapping and more general are not allowed.
1871 *
1872 * For example, the following is not allowed:
1873 *
1874 * [1.1.0.0/16,!1.0.0.0/8]
1875 *
1876 * The following is allowed though (not overlapping):
1877 *
1878 * [1.1.0.0/16,!2.0.0.0/8]
1879 *
1880 * Arguments: addrset -- IpAddrSet pointer.
1881 *
1882 * Returns: -1 if IP is empty, 1 if a conflict exists and 0 otherwise.
1883 *
1884 ***************************************************************************/
1885 int CheckForIPListConflicts(IpAddrSet *addrset)
1886 {
1887 /* Conflict checking takes place inside the SFIP library */
1888 return 0;
1889 }
1890
1891 /****************************************************************************
1892 *
1893 * Function: AddRuleFuncToList(int (*func)(), RuleTreeNode *)
1894 *
1895 * Purpose: Adds RuleTreeNode associated detection functions to the
1896 * current rule's function list
1897 *
1898 * Arguments: *func => function pointer to the detection function
1899 * rtn => pointer to the current rule
1900 *
1901 * Returns: void function
1902 *
1903 ***************************************************************************/
1904 void AddRuleFuncToList(int (*rfunc) (Packet *, struct _RuleTreeNode *, struct _RuleFpList *, int), RuleTreeNode * rtn)
1905 {
1906 RuleFpList *idx;
1907
1908 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"Adding new rule to list\n"););
1909
1910 idx = rtn->rule_func;
1911 if(idx == NULL)
1912 {
1913 rtn->rule_func = (RuleFpList *)SnortAlloc(sizeof(RuleFpList));
1914
1915 rtn->rule_func->RuleHeadFunc = rfunc;
1916 }
1917 else
1918 {
1919 while(idx->next != NULL)
1920 idx = idx->next;
1921
1922 idx->next = (RuleFpList *)SnortAlloc(sizeof(RuleFpList));
1923 idx = idx->next;
1924 idx->RuleHeadFunc = rfunc;
1925 }
1926 }
1927
1928
1929 /****************************************************************************
1930 *
1931 * Function: SetupRTNFuncList(RuleTreeNode *)
1932 *
1933 * Purpose: Configures the function list for the rule header detection
1934 * functions (addrs and ports)
1935 *
1936 * Arguments: rtn => the pointer to the current rules list entry to attach to
1937 *
1938 * Returns: void function
1939 *
1940 ***************************************************************************/
1941 static void SetupRTNFuncList(RuleTreeNode * rtn)
1942 {
1943 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"Initializing RTN function list!\n"););
1944 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"Functions: "););
1945
1946 if(rtn->flags & BIDIRECTIONAL)
1947 {
1948 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"CheckBidirectional->\n"););
1949 AddRuleFuncToList(CheckBidirectional, rtn);
1950 }
1951 else
1952 {
1953 /* Attach the proper port checking function to the function list */
1954 /*
1955 * the in-line "if's" check to see if the "any" or "not" flags have
1956 * been set so the PortToFunc call can determine which port testing
1957 * function to attach to the list
1958 */
1959 PortToFunc(rtn, (rtn->flags & ANY_DST_PORT ? 1 : 0),
1960 (rtn->flags & EXCEPT_DST_PORT ? 1 : 0), DST);
1961
1962 /* as above */
1963 PortToFunc(rtn, (rtn->flags & ANY_SRC_PORT ? 1 : 0),
1964 (rtn->flags & EXCEPT_SRC_PORT ? 1 : 0), SRC);
1965
1966 /* link in the proper IP address detection function */
1967 AddrToFunc(rtn, SRC);
1968
1969 /* last verse, same as the first (but for dest IP) ;) */
1970 AddrToFunc(rtn, DST);
1971 }
1972
1973 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"RuleListEnd\n"););
1974
1975 /* tack the end (success) function to the list */
1976 AddRuleFuncToList(RuleListEnd, rtn);
1977 }
1978
1979 /****************************************************************************
1980 *
1981 * Function: AddrToFunc(RuleTreeNode *, u_long, u_long, int, int)
1982 *
1983 * Purpose: Links the proper IP address testing function to the current RTN
1984 * based on the address, netmask, and addr flags
1985 *
1986 * Arguments: rtn => the pointer to the current rules list entry to attach to
1987 * ip => IP address of the current rule
1988 * mask => netmask of the current rule
1989 * exception_flag => indicates that a "!" has been set for this
1990 * address
1991 * mode => indicates whether this is a rule for the source
1992 * or destination IP for the rule
1993 *
1994 * Returns: void function
1995 *
1996 ***************************************************************************/
1997 static void AddrToFunc(RuleTreeNode * rtn, int mode)
1998 {
1999 /*
2000 * if IP and mask are both 0, this is a "any" IP and we don't need to
2001 * check it
2002 */
2003 switch(mode)
2004 {
2005 case SRC:
2006 if((rtn->flags & ANY_SRC_IP) == 0)
2007 {
2008 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"CheckSrcIP -> "););
2009 AddRuleFuncToList(CheckSrcIP, rtn);
2010 }
2011
2012 break;
2013
2014 case DST:
2015 if((rtn->flags & ANY_DST_IP) == 0)
2016 {
2017 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"CheckDstIP -> "););
2018 AddRuleFuncToList(CheckDstIP, rtn);
2019 }
2020
2021 break;
2022 }
2023 }
2024
2025 /****************************************************************************
2026 *
2027 * Function: PortToFunc(RuleTreeNode *, int, int, int)
2028 *
2029 * Purpose: Links in the port analysis function for the current rule
2030 *
2031 * Arguments: rtn => the pointer to the current rules list entry to attach to
2032 * any_flag => accept any port if set
2033 * except_flag => indicates negation (logical NOT) of the test
2034 * mode => indicates whether this is a rule for the source
2035 * or destination port for the rule
2036 *
2037 * Returns: void function
2038 *
2039 ***************************************************************************/
2040 static void PortToFunc(RuleTreeNode * rtn, int any_flag, int except_flag, int mode)
2041 {
2042 /*
2043 * if the any flag is set we don't need to perform any test to match on
2044 * this port
2045 */
2046 if(any_flag)
2047 return;
2048
2049 /* if the except_flag is up, test with the "NotEq" funcs */
2050 if(except_flag)
2051 {
2052 switch(mode)
2053 {
2054 case SRC:
2055 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"CheckSrcPortNotEq -> "););
2056 AddRuleFuncToList(CheckSrcPortNotEq, rtn);
2057 break;
2058
2059
2060 case DST:
2061 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"CheckDstPortNotEq -> "););
2062 AddRuleFuncToList(CheckDstPortNotEq, rtn);
2063 break;
2064 }
2065
2066 return;
2067 }
2068 /* default to setting the straight test function */
2069 switch(mode)
2070 {
2071 case SRC:
2072 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"CheckSrcPortEqual -> "););
2073 AddRuleFuncToList(CheckSrcPortEqual, rtn);
2074 break;
2075
2076 case DST:
2077 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"CheckDstPortEqual -> "););
2078 AddRuleFuncToList(CheckDstPortEqual, rtn);
2079 break;
2080 }
2081
2082 return;
2083 }
2084
2085 /****************************************************************************
2086 * Function: ParsePreprocessor()
2087 *
2088 * Saves the preprocessor configuration for loading later after dynamic
2089 * preprocessor keywords and configuration functions have been registered.
2090 * Configuration is also used later for configuration reload to check if
2091 * configuration has changed.
2092 *
2093 * Arguments:
2094 * SnortConfig *
2095 * Snort configuration to attach preprocessor configuration to.
2096 * char *
2097 * The preprocessor arguments.
2098 *
2099 * Returns: void function
2100 *
2101 ***************************************************************************/
2102 static void ParsePreprocessor(SnortConfig *sc, SnortPolicy *p, char *args)
2103 {
2104 char **toks;
2105 int num_toks;
2106 char *keyword;
2107 char *opts = NULL;
2108 PreprocConfig *config;
2109
2110 if ((sc == NULL) || (p == NULL) || (args == NULL))
2111 return;
2112
2113 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES, "Preprocessor\n"););
2114
2115 /* break out the arguments from the keywords */
2116 toks = mSplit(args, ":", 2, &num_toks, '\\');
2117 keyword = toks[0];
2118
2119 if (num_toks > 1)
2120 opts = toks[1];
2121
2122 /* Save the configuration and load later */
2123 config = (PreprocConfig *)SnortAlloc(sizeof(PreprocConfig));
2124
2125 if (p->preproc_configs == NULL)
2126 {
2127 p->preproc_configs = config;
2128 }
2129 else
2130 {
2131 PreprocConfig *tmp = p->preproc_configs;
2132
2133 while (tmp->next != NULL)
2134 tmp = tmp->next;
2135
2136 tmp->next = config;
2137 }
2138
2139 config->keyword = SnortStrdup(keyword);
2140 config->file_name = SnortStrdup(file_name);
2141 config->file_line = file_line;
2142
2143 if (opts != NULL)
2144 config->opts = SnortStrdup(opts);
2145 else
2146 config->opts = NULL;
2147
2148 mSplitFree(&toks, num_toks);
2149 }
2150
2151 void ConfigurePreprocessors(SnortConfig *sc, int configure_dynamic)
2152 {
2153 char *stored_file_name = file_name;
2154 int stored_file_line = file_line;
2155 tSfPolicyId i;
2156
2157 if (sc == NULL)
2158 return;
2159
2160 for (i = 0; i < sc->num_policies_allocated; i++)
2161 {
2162 PreprocConfig *config;
2163
2164 setParserPolicy(sc, i);
2165
2166 if (sc->targeted_policies[i] == NULL)
2167 continue;
2168
2169 config = sc->targeted_policies[i]->preproc_configs;
2170
2171 for (; config != NULL; config = config->next)
2172 {
2173 PreprocConfigFuncNode *node;
2174
2175 if (config->configured)
2176 continue;
2177
2178 file_name = config->file_name;
2179 file_line = config->file_line;
2180
2181 node = GetPreprocConfig(config->keyword);
2182 if ((node == NULL) && configure_dynamic)
2183 ParseError("Unknown preprocessor: \"%s\".", config->keyword);
2184
2185 if (node != NULL)
2186 {
2187 #ifdef SNORT_RELOAD
2188 if (node->initialized)
2189 {
2190 if (node->reload_func != NULL)
2191 {
2192 PreprocessorSwapData *swapData;
2193 PreprocessorSwapData **swapDataNode;
2194
2195 for (swapData = sc->preprocSwapData;
2196 swapData && swapData->preprocNode != node;
2197 swapData = swapData->next);
2198 if (!swapData)
2199 {
2200 swapData = (PreprocessorSwapData *)SnortAlloc(sizeof(*swapData));
2201 swapData->preprocNode = node;
2202 for (swapDataNode = &sc->preprocSwapData; *swapDataNode; swapDataNode = &(*swapDataNode)->next);
2203 *swapDataNode = swapData;
2204 }
2205 node->reload_func(sc, config->opts, &swapData->data);
2206 if (!sc->streamReloadConfig && node->keyword && strcmp(node->keyword, "stream5_global") == 0)
2207 sc->streamReloadConfig = swapData->data;
2208 }
2209 }
2210 else
2211 #endif
2212 {
2213 if (node->config_func != NULL)
2214 node->config_func(sc, config->opts);
2215 }
2216
2217 config->configured = 1;
2218 }
2219 }
2220 }
2221
2222 #ifdef SNORT_RELOAD
2223 for (i = 0; i < sc->num_policies_allocated; i++)
2224 {
2225 PreprocConfig *config;
2226
2227 setParserPolicy(sc, i);
2228
2229 if (sc->targeted_policies[i] == NULL)
2230 continue;
2231
2232 setParserPolicy(sc, i);
2233
2234 config = sc->targeted_policies[i]->preproc_configs;
2235
2236 /* Set all configured preprocessors to intialized */
2237 for (; config != NULL; config = config->next)
2238 {
2239 if (config->configured)
2240 {
2241 PreprocConfigFuncNode *node = GetPreprocConfig(config->keyword);
2242
2243 if (node != NULL)
2244 node->initialized = 1;
2245 }
2246 }
2247 }
2248 #endif
2249
2250 /* Reset these since we're done with configuring dynamic preprocessors */
2251 file_name = stored_file_name;
2252 file_line = stored_file_line;
2253 }
2254
2255 #ifdef SIDE_CHANNEL
2256
2257 static void ParseSideChannelModule(SnortConfig *sc, SnortPolicy *p, char *args)
2258 {
2259 char **toks;
2260 int num_toks;
2261 char *opts = NULL;
2262 SideChannelModuleConfig *config;
2263
2264 toks = mSplit(args, ":", 2, &num_toks, '\\');
2265
2266 if (num_toks > 1)
2267 opts = toks[1];
2268
2269 config = (SideChannelModuleConfig *) SnortAlloc(sizeof(SideChannelModuleConfig));
2270
2271 if (sc->side_channel_config.module_configs == NULL)
2272 {
2273 sc->side_channel_config.module_configs = config;
2274 }
2275 else
2276 {
2277 SideChannelModuleConfig *tmp = sc->side_channel_config.module_configs;
2278
2279 while (tmp->next != NULL)
2280 tmp = tmp->next;
2281
2282 tmp->next = config;
2283 }
2284
2285 config->keyword = SnortStrdup(toks[0]);
2286 if (opts != NULL)
2287 config->opts = SnortStrdup(opts);
2288
2289 /* This could come from parsing the command line (No, actually, I don't think that it could...) */
2290 if (file_name != NULL)
2291 {
2292 config->file_name = SnortStrdup(file_name);
2293 config->file_line = file_line;
2294 }
2295
2296 mSplitFree(&toks, num_toks);
2297 }
2298
2299 void ConfigureSideChannelModules(SnortConfig *sc)
2300 {
2301 SideChannelModuleConfig *config;
2302 char *stored_file_name = file_name;
2303 int stored_file_line = file_line;
2304 int rval;
2305
2306 for (config = sc->side_channel_config.module_configs; config != NULL; config = config->next)
2307 {
2308 file_name = config->file_name;
2309 file_line = config->file_line;
2310
2311 rval = ConfigureSideChannelModule(config->keyword, config->opts);
2312 if (rval == -ENOENT)
2313 ParseError("Unknown side channel plugin: \"%s\"", config->keyword);
2314 }
2315
2316 /* Reset these since we're done with configuring side channels */
2317 file_name = stored_file_name;
2318 file_line = stored_file_line;
2319 }
2320
2321 #endif /* SIDE_CHANNEL */
2322
2323 /* Parses standalone rate_filter configuration.
2324 *
2325 * Parses rate_filter configuration in the following format and populates internal
2326 * structures:
2327 * @code
2328 * rate_filter gid <gen-id>, sid <sig-id>,
2329 * track <by_src|by_dst|by_rule>,
2330 * count <c> , seconds <s>,
2331 * new_action <alert|drop|pass|drop|reject|sdrop>,
2332 * timeout <t> [, apply_to <cidr>];
2333 * @endcode
2334 * And then adds it into pContext.
2335 *
2336 * @param rule - string containing rate_filter configuration from snort.conf file.
2337 *
2338 * @returns void
2339 */
2340 static void ParseRateFilter(SnortConfig *sc, SnortPolicy *p, char *args)
2341 {
2342 char **toks;
2343 int num_toks;
2344 int count_flag = 0;
2345 int new_action_flag = 0;
2346 int timeout_flag = 0;
2347 int seconds_flag = 0;
2348 int tracking_flag = 0;
2349 int genid_flag = 0;
2350 int sigid_flag = 0;
2351 int apply_flag = 0;
2352 int i;
2353 const char* ERR_KEY = "rate_filter";
2354
2355 tSFRFConfigNode thdx;
2356
2357 memset( &thdx, 0, sizeof(thdx) );
2358
2359 /* Potential IP list might be present so we can't split on commas
2360 * Change commas to semi-colons */
2361 args = FixSeparators(args, ';', "rate_filter");
2362 toks = mSplit(args, ";", 0, &num_toks, 0); /* get rule option pairs */
2363
2364 for (i = 0; i < num_toks; i++)
2365 {
2366 char **pairs;
2367 int num_pairs;
2368
2369 pairs = mSplit(toks[i], " \t", 2, &num_pairs, 0); /* get rule option pairs */
2370
2371 if (num_pairs != 2)
2372 {
2373 ParseError(ERR_NOT_PAIRED);
2374 }
2375
2376 if (!strcasecmp(pairs[0], "gen_id"))
2377 {
2378 if ( genid_flag++ )
2379 {
2380 ParseError(ERR_EXTRA_OPTION);
2381 }
2382
2383 thdx.gid = xatou(pairs[1], "rate_filter: gen_id");
2384 }
2385 else if (!strcasecmp(pairs[0], "sig_id"))
2386 {
2387 if ( sigid_flag++ )
2388 {
2389 ParseError(ERR_EXTRA_OPTION);
2390 }
2391
2392 thdx.sid = xatou(pairs[1], "rate_filter: sig_id");
2393 }
2394 else if (!strcasecmp(pairs[0], "track"))
2395 {
2396 if ( tracking_flag++ )
2397 {
2398 ParseError(ERR_EXTRA_OPTION);
2399 }
2400
2401 if (!strcasecmp(pairs[1], "by_src"))
2402 {
2403 thdx.tracking = SFRF_TRACK_BY_SRC;
2404 }
2405 else if (!strcasecmp(pairs[1], "by_dst"))
2406 {
2407 thdx.tracking = SFRF_TRACK_BY_DST;
2408 }
2409 else if (!strcasecmp(pairs[1], "by_rule"))
2410 {
2411 thdx.tracking = SFRF_TRACK_BY_RULE;
2412 }
2413 else
2414 {
2415 ParseError(ERR_BAD_VALUE);
2416 }
2417 }
2418 else if (!strcasecmp(pairs[0], "count"))
2419 {
2420 if ( count_flag++ )
2421 {
2422 ParseError(ERR_EXTRA_OPTION);
2423 }
2424
2425 thdx.count = xatoup(pairs[1], "rate_filter: count");
2426 }
2427 else if (!strcasecmp(pairs[0], "seconds"))
2428 {
2429 if ( seconds_flag++ )
2430 {
2431 ParseError(ERR_EXTRA_OPTION);
2432 }
2433
2434 thdx.seconds = xatou(pairs[1], "rate_filter: seconds");
2435 }
2436 else if (!strcasecmp(pairs[0], "new_action"))
2437 {
2438 if ( new_action_flag++ )
2439 {
2440 ParseError(ERR_EXTRA_OPTION);
2441 }
2442
2443 if (!strcasecmp(pairs[1], "alert"))
2444 {
2445 thdx.newAction = RULE_TYPE__ALERT;
2446 }
2447 else if (!strcasecmp(pairs[1], "drop"))
2448 {
2449 thdx.newAction = RULE_TYPE__DROP;
2450 }
2451 else if (!strcasecmp(pairs[1], "pass"))
2452 {
2453 thdx.newAction = RULE_TYPE__PASS;
2454 }
2455 else if (!strcasecmp(pairs[1], "log"))
2456 {
2457 thdx.newAction = RULE_TYPE__LOG;
2458 }
2459 else if (!strcasecmp(pairs[1], "reject"))
2460 {
2461 thdx.newAction = RULE_TYPE__REJECT;
2462 }
2463 else if (!strcasecmp(pairs[1], "sdrop"))
2464 {
2465 thdx.newAction = RULE_TYPE__SDROP;
2466 }
2467 else
2468 {
2469 ParseError(ERR_BAD_VALUE);
2470 }
2471 }
2472 else if (!strcasecmp(pairs[0], "timeout"))
2473 {
2474 if ( timeout_flag++ )
2475 {
2476 ParseError(ERR_EXTRA_OPTION);
2477 }
2478
2479 thdx.timeout = xatou(pairs[1],"rate_filter: timeout");
2480 }
2481 else if (!strcasecmp(pairs[0], "apply_to"))
2482 {
2483 char *ip_list = pairs[1];
2484
2485 if ( apply_flag++ )
2486 {
2487 ParseError(ERR_EXTRA_OPTION);
2488 }
2489
2490 thdx.applyTo = IpAddrSetParse(sc, ip_list);
2491 }
2492 else
2493 {
2494 ParseError(ERR_BAD_OPTION);
2495 }
2496
2497 mSplitFree(&pairs, num_pairs);
2498 }
2499
2500 if ( (genid_flag != 1) || (sigid_flag != 1) || (tracking_flag != 1)
2501 || (count_flag != 1) || (seconds_flag != 1) || (new_action_flag != 1)
2502 || (timeout_flag != 1) || (apply_flag > 1) )
2503 {
2504 ParseError(ERR_BAD_ARG_COUNT);
2505 }
2506 if ( !thdx.seconds
2507 && (thdx.gid != GENERATOR_INTERNAL
2508 || thdx.sid != INTERNAL_EVENT_SESSION_ADD) )
2509 {
2510 ParseError("rate_filter: seconds must be > 0");
2511 }
2512
2513 if (RateFilter_Create(sc, sc->rate_filter_config, &thdx))
2514 {
2515 ParseError(ERR_CREATE);
2516 }
2517
2518 mSplitFree(&toks, num_toks);
2519 }
2520
2521 static void ParseRuleTypeOutput(SnortConfig *sc, char *args, ListHead *list)
2522 {
2523 char **toks;
2524 int num_toks;
2525 char *opts = NULL;
2526 OutputConfig *config;
2527
2528 toks = mSplit(args, ":", 2, &num_toks, '\\');
2529
2530 if (num_toks > 1)
2531 opts = toks[1];
2532
2533 config = (OutputConfig *)SnortAlloc(sizeof(OutputConfig));
2534
2535 if (sc->rule_type_output_configs == NULL)
2536 {
2537 sc->rule_type_output_configs = config;
2538 }
2539 else
2540 {
2541 OutputConfig *tmp = sc->rule_type_output_configs;
2542
2543 while (tmp->next != NULL)
2544 tmp = tmp->next;
2545
2546 tmp->next = config;
2547 }
2548
2549 config->keyword = SnortStrdup(toks[0]);
2550 if (opts != NULL)
2551 config->opts = SnortStrdup(opts);
2552 config->rule_list = list;
2553
2554 if (file_name != NULL)
2555 {
2556 config->file_name = SnortStrdup(file_name);
2557 config->file_line = file_line;
2558 }
2559
2560 mSplitFree(&toks, num_toks);
2561 }
2562
2563 void ParseOutput(SnortConfig *sc, SnortPolicy *p, char *args)
2564 {
2565 char **toks;
2566 int num_toks;
2567 char *opts = NULL;
2568 OutputConfig *config;
2569
2570 toks = mSplit(args, ":", 2, &num_toks, '\\');
2571
2572 if (num_toks > 1)
2573 opts = toks[1];
2574
2575 config = (OutputConfig *)SnortAlloc(sizeof(OutputConfig));
2576
2577 if (sc->output_configs == NULL)
2578 {
2579 sc->output_configs = config;
2580 }
2581 else
2582 {
2583 OutputConfig *tmp = sc->output_configs;
2584
2585 while (tmp->next != NULL)
2586 tmp = tmp->next;
2587
2588 tmp->next = config;
2589 }
2590
2591 config->keyword = SnortStrdup(toks[0]);
2592 if (opts != NULL)
2593 config->opts = SnortStrdup(opts);
2594
2595 /* This could come from parsing the command line */
2596 if (file_name != NULL)
2597 {
2598 config->file_name = SnortStrdup(file_name);
2599 config->file_line = file_line;
2600 }
2601
2602 mSplitFree(&toks, num_toks);
2603 }
2604
2605 static void TransferOutputConfigs(OutputConfig *from_list, OutputConfig **to_list)
2606 {
2607 if ((from_list == NULL) || (to_list == NULL))
2608 return;
2609
2610 for (; from_list != NULL; from_list = from_list->next)
2611 {
2612 if (*to_list == NULL)
2613 {
2614 *to_list = DupOutputConfig(from_list);
2615 }
2616 else
2617 {
2618 OutputConfig *tmp = DupOutputConfig(from_list);
2619
2620 if (tmp != NULL)
2621 {
2622 tmp->next = *to_list;
2623 *to_list = tmp;
2624 }
2625 }
2626 }
2627 }
2628
2629 static OutputConfig * DupOutputConfig(OutputConfig *dupme)
2630 {
2631 OutputConfig *medup;
2632
2633 if (dupme == NULL)
2634 return NULL;
2635
2636 medup = (OutputConfig *)SnortAlloc(sizeof(OutputConfig));
2637
2638 if (dupme->keyword != NULL)
2639 medup->keyword = SnortStrdup(dupme->keyword);
2640
2641 if (dupme->opts != NULL)
2642 medup->opts = SnortStrdup(dupme->opts);
2643
2644 if (dupme->file_name != NULL)
2645 medup->file_name = SnortStrdup(dupme->file_name);
2646
2647 medup->file_line = dupme->file_line;
2648 medup->rule_list = dupme->rule_list;
2649
2650 return medup;
2651 }
2652
2653 static void RemoveOutputConfigs(OutputConfig **head, int remove_flags)
2654 {
2655 OutputConfig *config;
2656 OutputConfig *last = NULL;
2657
2658 if (head == NULL)
2659 return;
2660
2661 config = *head;
2662
2663 while (config != NULL)
2664 {
2665 int type_flags = GetOutputTypeFlags(config->keyword);
2666
2667 if (type_flags & remove_flags)
2668 {
2669 OutputConfig *tmp = config;
2670
2671 config = config->next;
2672
2673 if (last == NULL)
2674 *head = config;
2675 else
2676 last->next = config;
2677
2678 free(tmp->keyword);
2679
2680 if (tmp->opts != NULL)
2681 free(tmp->opts);
2682
2683 if (tmp->file_name != NULL)
2684 free(tmp->file_name);
2685
2686 free(tmp);
2687 }
2688 else
2689 {
2690 last = config;
2691 config = config->next;
2692 }
2693 }
2694 }
2695
2696 void ResolveOutputPlugins(SnortConfig *cmd_line, SnortConfig *config_file)
2697 {
2698 int cmd_line_type_flags = 0;
2699
2700 if (cmd_line == NULL)
2701 return;
2702
2703 if (cmd_line->no_log)
2704 {
2705 /* Free any log output plugins in both lists */
2706 RemoveOutputConfigs(&cmd_line->output_configs, OUTPUT_TYPE__LOG);
2707
2708 if (config_file != NULL)
2709 {
2710 RemoveOutputConfigs(&config_file->output_configs, OUTPUT_TYPE__LOG);
2711 RemoveOutputConfigs(&config_file->rule_type_output_configs, OUTPUT_TYPE__LOG);
2712 }
2713 }
2714 else if ((config_file != NULL) && config_file->no_log)
2715 {
2716 /* Free any log output plugins in config list */
2717 RemoveOutputConfigs(&config_file->output_configs, OUTPUT_TYPE__LOG);
2718 RemoveOutputConfigs(&config_file->rule_type_output_configs, OUTPUT_TYPE__LOG);
2719 }
2720
2721 if (cmd_line->no_alert)
2722 {
2723 /* Free any alert output plugins in both lists */
2724 RemoveOutputConfigs(&cmd_line->output_configs, OUTPUT_TYPE__ALERT);
2725
2726 if (config_file != NULL)
2727 {
2728 RemoveOutputConfigs(&config_file->output_configs, OUTPUT_TYPE__ALERT);
2729 RemoveOutputConfigs(&config_file->rule_type_output_configs, OUTPUT_TYPE__ALERT);
2730 }
2731 }
2732 else if ((config_file != NULL) && config_file->no_alert)
2733 {
2734 /* Free any alert output plugins in config list */
2735 RemoveOutputConfigs(&config_file->output_configs, OUTPUT_TYPE__ALERT);
2736 RemoveOutputConfigs(&config_file->rule_type_output_configs, OUTPUT_TYPE__ALERT);
2737 }
2738
2739 /* Command line overrides configuration file output */
2740 if (cmd_line->output_configs != NULL)
2741 {
2742 OutputConfig *config = cmd_line->output_configs;
2743
2744 for (; config != NULL; config = config->next)
2745 {
2746 int type_flags = GetOutputTypeFlags(config->keyword);
2747
2748 cmd_line_type_flags |= type_flags;
2749
2750 if (config_file != NULL)
2751 {
2752 RemoveOutputConfigs(&config_file->output_configs, type_flags);
2753 RemoveOutputConfigs(&config_file->rule_type_output_configs, type_flags);
2754 }
2755 }
2756
2757 /* Put what's in the command line output into the config file output */
2758 if (config_file != NULL)
2759 TransferOutputConfigs(cmd_line->output_configs, &config_file->output_configs);
2760 }
2761
2762 if (config_file != NULL)
2763 {
2764 if (cmd_line->no_log)
2765 config_file->no_log = cmd_line->no_log;
2766
2767 if (cmd_line->no_alert)
2768 config_file->no_alert = cmd_line->no_alert;
2769 }
2770
2771 /* Don't try to configure defaults if running in test mode */
2772 if (!ScTestModeNewConf(cmd_line))
2773 {
2774 if (config_file == NULL)
2775 {
2776 if (!cmd_line->no_log && !(cmd_line_type_flags & OUTPUT_TYPE__LOG))
2777 ParseOutput(cmd_line, NULL, "log_tcpdump");
2778
2779 if (!cmd_line->no_alert && !(cmd_line_type_flags & OUTPUT_TYPE__ALERT))
2780 ParseOutput(cmd_line, NULL, "alert_full");
2781 }
2782 else
2783 {
2784 int config_file_type_flags = 0;
2785 OutputConfig *config = config_file->output_configs;
2786
2787 for (; config != NULL; config = config->next)
2788 config_file_type_flags |= GetOutputTypeFlags(config->keyword);
2789
2790 if (!config_file->no_log && !(config_file_type_flags & OUTPUT_TYPE__LOG))
2791 ParseOutput(config_file, NULL, "log_tcpdump");
2792
2793 if (!config_file->no_alert && !(config_file_type_flags & OUTPUT_TYPE__ALERT))
2794 ParseOutput(config_file, NULL, "alert_full");
2795 }
2796 }
2797 }
2798
2799 void ConfigureOutputPlugins(SnortConfig *sc)
2800 {
2801 OutputConfig *config;
2802 char *stored_file_name = file_name;
2803 int stored_file_line = file_line;
2804
2805 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"Output Plugin\n"););
2806
2807 for (config = sc->output_configs; config != NULL; config = config->next)
2808 {
2809 OutputConfigFunc oc_func;
2810
2811 file_name = config->file_name;
2812 file_line = config->file_line;
2813
2814 oc_func = GetOutputConfigFunc(config->keyword);
2815 if (oc_func == NULL)
2816 ParseError("Unknown output plugin: \"%s\"", config->keyword);
2817
2818 oc_func(sc, config->opts);
2819 }
2820
2821 /* Configure output plugins for user defined rule types */
2822 for (config = sc->rule_type_output_configs; config != NULL; config = config->next)
2823 {
2824 OutputConfigFunc oc_func;
2825
2826 file_name = config->file_name;
2827 file_line = config->file_line;
2828
2829 oc_func = GetOutputConfigFunc(config->keyword);
2830 if (oc_func == NULL)
2831 ParseError("Unknown output plugin \"%s\"", config->keyword);
2832
2833 /* Each user defined rule type has it's own rule list and output plugin is
2834 * attached to it's Alert and/or Log lists */
2835 sc->head_tmp = config->rule_list;
2836 oc_func(sc, config->opts);
2837 sc->head_tmp = NULL;
2838 }
2839
2840 /* Reset these since we're done with configuring dynamic preprocessors */
2841 file_name = stored_file_name;
2842 file_line = stored_file_line;
2843 }
2844
2845 static void FreeRuleTreeNode(RuleTreeNode *rtn)
2846 {
2847 RuleFpList *idx, *tmp;
2848 if (!rtn)
2849 return;
2850
2851 if (rtn->sip)
2852 {
2853 sfvar_free(rtn->sip);
2854 }
2855
2856 if (rtn->dip)
2857 {
2858 sfvar_free(rtn->dip);
2859 }
2860
2861 idx = rtn->rule_func;
2862 while (idx)
2863 {
2864 tmp = idx;
2865 idx = idx->next;
2866 free(tmp);
2867 }
2868 }
2869
2870 static void DestroyRuleTreeNode(RuleTreeNode *rtn)
2871 {
2872 if (!rtn)
2873 return;
2874
2875 rtn->otnRefCount--;
2876 if (rtn->otnRefCount != 0)
2877 return;
2878
2879 FreeRuleTreeNode(rtn);
2880
2881 free(rtn);
2882 }
2883
2884 /****************************************************************************
2885 *
2886 * Function: mergeDuplicateOtn()
2887 *
2888 * Purpose: Conditionally removes duplicate SID/GIDs. Keeps duplicate with
2889 * higher revision. If revision is the same, keeps newest rule.
2890 *
2891 * Arguments: otn_dup => The existing duplicate
2892 * rtn => the RTN chain to check
2893 * char => String describing the rule
2894 * rule_type => enumerated rule type (alert, pass, log)
2895 *
2896 * Returns: 0 if original rule stays, 1 if new rule stays
2897 *
2898 ***************************************************************************/
2899 static int mergeDuplicateOtn(SnortConfig *sc, OptTreeNode *otn_dup,
2900 OptTreeNode *otn_new, RuleTreeNode *rtn_new)
2901 {
2902 RuleTreeNode *rtn_dup = NULL;
2903 RuleTreeNode *rtnTmp2 = NULL;
2904 unsigned i;
2905
2906 if (otn_dup->proto != otn_new->proto)
2907 {
2908 ParseError("GID %d SID %d in rule duplicates previous rule, with "
2909 "different protocol.",
2910 otn_new->sigInfo.generator, otn_new->sigInfo.id);
2911 }
2912
2913 rtn_dup = getParserRtnFromOtn(sc, otn_dup);
2914
2915 if((rtn_dup != NULL) && (rtn_dup->type != rtn_new->type))
2916 {
2917 ParseError("GID %d SID %d in rule duplicates previous rule, with "
2918 "different type.",
2919 otn_new->sigInfo.generator, otn_new->sigInfo.id);
2920 }
2921
2922 if((otn_new->sigInfo.shared < otn_dup->sigInfo.shared)
2923 || ((otn_new->sigInfo.shared == otn_dup->sigInfo.shared)
2924 && (otn_new->sigInfo.rev < otn_dup->sigInfo.rev)))
2925 {
2926 //existing OTN is newer version. Keep existing and discard the new one.
2927 //OTN is for new policy group, salvage RTN
2928 deleteRtnFromOtn(sc, otn_new, getParserPolicy(sc));
2929
2930 ParseMessage("GID %d SID %d duplicates previous rule. Using %s.",
2931 otn_new->sigInfo.generator, otn_new->sigInfo.id,
2932 otn_dup->sigInfo.shared ? "SO rule.":"higher revision");
2933
2934 /* delete the data for each rule option in this OTN */
2935 OtnDeleteData(otn_new);
2936
2937 /* Now free the OTN itself -- this function is also used
2938 * by the hash-table calls out of OtnRemove, so it cannot
2939 * be modified to delete data for rule options */
2940 OtnFree(otn_new);
2941
2942 //Add rtn to existing otn for the first rule instance in a policy,
2943 //otherwise ignore it
2944 if (rtn_dup == NULL)
2945 {
2946 addRtnToOtn(sc, otn_dup, getParserPolicy(sc), rtn_new);
2947 }
2948 else
2949 {
2950 DestroyRuleTreeNode(rtn_new);
2951 }
2952
2953 return 0;
2954 }
2955
2956 //delete existing rule instance and keep the new one
2957
2958 for (i = 0; i < otn_dup->proto_node_num; i++)
2959 {
2960 rtnTmp2 = deleteRtnFromOtn(sc, otn_dup, i);
2961
2962 if ((rtnTmp2 && (i != getParserPolicy(sc))))
2963 {
2964 addRtnToOtn(sc, otn_new, i, rtnTmp2);
2965 }
2966 }
2967
2968 if (rtn_dup)
2969 {
2970 if (ScConfErrorOutNewConf(sc))
2971 {
2972 ParseError("GID %d SID %d in rule duplicates previous rule.",
2973 otn_new->sigInfo.generator, otn_new->sigInfo.id);
2974 }
2975 else
2976 {
2977 ParseWarning("GID %d SID %d in rule duplicates previous "
2978 "rule. Ignoring old rule.\n",
2979 otn_new->sigInfo.generator, otn_new->sigInfo.id);
2980 }
2981
2982 switch (otn_new->sigInfo.rule_type)
2983 {
2984 case SI_RULE_TYPE_DETECT:
2985 detect_rule_count--;
2986 break;
2987 case SI_RULE_TYPE_DECODE:
2988 decode_rule_count--;
2989 break;
2990 case SI_RULE_TYPE_PREPROC:
2991 preproc_rule_count--;
2992 break;
2993 default:
2994 break;
2995 }
2996 }
2997
2998 otn_count--;
2999
3000 OtnRemove(sc->otn_map, sc->so_rule_otn_map, otn_dup);
3001 DestroyRuleTreeNode(rtn_dup);
3002
3003 return 1;
3004 }
3005
3006 /* createDynamicRuleTypeRtn
3007 *
3008 * This api is only getting called whenever will see those
3009 * GID/SID pairs whose rule action type is getting modulated
3010 * from code depending upon few runtime parameters.
3011 *
3012 * In present scenario for
3013 * GID - GENERATOR_FILE_SIGNATURE/GENERATOR_FILE_TYPE, rule action
3014 * type is getting modified depending upon file verdict.
3015 *
3016 * I/P - SnortConfig -
3017 * old_rtn - This is the old RTN which has been created
3018 * irrespective of above mentioned scenario.
3019 * As a new RTN will be created with type NONE,
3020 * will destroy this old RTN.
3021 *
3022 * O/P - rtn - This is RTN which has been created with
3023 * RULE_TYPE_NONE specify that rule type will
3024 * be decided at the run time.
3025 */
3026 static inline RuleTreeNode * createDynamicRuleTypeRtn(SnortConfig *sc, RuleTreeNode *old_rtn)
3027 {
3028 RuleTreeNode test_rtn;
3029 RuleTreeNode *rtn;
3030 ListHead *list = old_rtn->listhead;
3031
3032 memset(&test_rtn, 0, sizeof(RuleTreeNode));
3033
3034 /* Making own key by changing the type to NONE */
3035 test_rtn.flags |= ANY_DST_PORT;
3036 test_rtn.flags |= ANY_SRC_PORT;
3037 test_rtn.flags |= ANY_DST_IP;
3038 test_rtn.flags |= ANY_SRC_IP;
3039 test_rtn.flags |= BIDIRECTIONAL;
3040
3041 /*
3042 * Initialising the rule type as NONE since type value will be
3043 * dynamic for GENERATOR_FILE_SIGNATURE/GENERATOR_FILE_TYPE
3044 */
3045 test_rtn.type = RULE_TYPE__NONE;
3046 test_rtn.listhead = list;
3047
3048 DestroyRuleTreeNode(old_rtn);
3049
3050 /* Creating the RTN node for File policy internal IPS rules(147:1/146)*/
3051 rtn = ProcessHeadNode(sc, &test_rtn, list);
3052 return rtn;
3053 }
3054
3055 /****************************************************************************
3056 *
3057 * Function: ParseRuleOptions(char *, int)
3058 *
3059 * Purpose: Process an individual rule's options and add it to the
3060 * appropriate rule chain
3061 *
3062 * Arguments: rule => rule string
3063 * rule_type => enumerated rule type (alert, pass, log)
3064 * *conflicts => Identifies whether there was a conflict due to duplicate
3065 * rule and whether existing otn was newer or not.
3066 * 0 - no conflict
3067 * 1 - existing otn is newer.
3068 * -1 - existing otn is older.
3069 *
3070 * Returns:
3071 * OptTreeNode *
3072 * The new OptTreeNode on success or NULL on error.
3073 *
3074 ***************************************************************************/
3075 OptTreeNode * ParseRuleOptions(SnortConfig *sc, RuleTreeNode **rtn_addr,
3076 char *rule_opts, RuleType rule_type, int protocol)
3077 {
3078 OptTreeNode *otn;
3079 RuleOptOtnHandler otn_handler = NULL;
3080 int num_detection_opts = 0;
3081 char *dopt_keyword = NULL;
3082 OptFpList *fpl = NULL;
3083 int got_sid = 0;
3084 RuleTreeNode *rtn = *rtn_addr;
3085
3086 otn = (OptTreeNode *)SnortAlloc(sizeof(OptTreeNode));
3087
3088 otn->chain_node_number = otn_count;
3089 otn->proto = protocol;
3090 otn->event_data.sig_generator = GENERATOR_SNORT_ENGINE;
3091 otn->sigInfo.generator = GENERATOR_SNORT_ENGINE;
3092 otn->sigInfo.rule_type = SI_RULE_TYPE_DETECT; /* standard rule */
3093 otn->sigInfo.rule_flushing = SI_RULE_FLUSHING_ON; /* usually just standard rules cause a flush*/
3094 #ifdef TARGET_BASED
3095 otn->sigInfo.service_override = ServiceOverride_Nil;
3096 #endif
3097
3098 /* Set the default rule state */
3099 otn->rule_state = ScDefaultRuleStateNewConf(sc);
3100
3101 if (rule_opts == NULL)
3102 {
3103 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES, "No rule options.\n"););
3104
3105 if (ScRequireRuleSidNewConf(sc))
3106 ParseError("Each rule must contain a Rule-sid.");
3107
3108 addRtnToOtn(sc, otn, getParserPolicy(sc), rtn);
3109
3110 otn->ruleIndex = RuleIndexMapAdd(ruleIndexMap,
3111 otn->sigInfo.generator,
3112 otn->sigInfo.id);
3113 }
3114 else
3115 {
3116 char **toks;
3117 int num_toks;
3118 char configured[sizeof(rule_options) / sizeof(RuleOptFunc)];
3119 int i;
3120 OptTreeNode *otn_dup;
3121
3122 if ((rule_opts[0] != '(') || (rule_opts[strlen(rule_opts) - 1] != ')'))
3123 {
3124 ParseError("Rule options must be enclosed in '(' and ')'.");
3125 }
3126
3127 /* Move past '(' and zero out ')' */
3128 rule_opts++;
3129 rule_opts[strlen(rule_opts) - 1] = '\0';
3130
3131 /* Used to determine if a rule option has already been configured
3132 * in the rule. Some can only be configured once */
3133 memset(configured, 0, sizeof(configured));
3134
3135 toks = mSplit(rule_opts, ";", 0, &num_toks, '\\');
3136
3137 for (i = 0; i < num_toks; i++)
3138 {
3139 char **opts;
3140 int num_opts;
3141 char *option_args = NULL;
3142 int j;
3143
3144 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES," option: %s\n", toks[i]););
3145
3146 /* break out the option name from its data */
3147 opts = mSplit(toks[i], ":", 2, &num_opts, '\\');
3148
3149 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES," option name: %s\n", opts[0]););
3150
3151 if (num_opts == 2)
3152 {
3153 option_args = opts[1];
3154 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES," option args: %s\n", option_args););
3155 }
3156
3157 for (j = 0; rule_options[j].name != NULL; j++)
3158 {
3159 if (strcasecmp(opts[0], rule_options[j].name) == 0)
3160 {
3161 if (configured[j] && rule_options[j].only_once)
3162 {
3163 ParseError("Only one '%s' rule option per rule.",
3164 opts[0]);
3165 }
3166
3167 if ((option_args == NULL) && rule_options[j].args_required)
3168 {
3169 ParseError("No argument passed to keyword \"%s\". "
3170 "Make sure you didn't forget a ':' or the "
3171 "argument to this keyword.\n", opts[0]);
3172 }
3173
3174 rule_options[j].parse_func(sc, rtn, otn, rule_type, option_args);
3175 configured[j] = 1;
3176
3177 if ( !dopt_keyword && rule_options[j].detection )
3178 dopt_keyword = SnortStrdup(opts[0]);
3179 break;
3180 }
3181 }
3182
3183 /* Because we actually allow an sid of 0 */
3184 if ((rule_options[j].name != NULL) &&
3185 (strcasecmp(rule_options[j].name, RULE_OPT__SID) == 0))
3186 {
3187 got_sid = 1;
3188 }
3189
3190 /* It's possibly a detection option plugin */
3191 if (rule_options[j].name == NULL)
3192 {
3193 RuleOptConfigFuncNode *dopt = rule_opt_config_funcs;
3194
3195 for (; dopt != NULL; dopt = dopt->next)
3196 {
3197 if (strcasecmp(opts[0], dopt->keyword) == 0)
3198 {
3199 dopt->func(sc, option_args, otn, protocol);
3200
3201 /* If this option contains an OTN handler, save it for
3202 use after the rule is done parsing. */
3203 if (dopt->otn_handler != NULL)
3204 otn_handler = dopt->otn_handler;
3205
3206 /* This is done so if we have a preprocessor/decoder
3207 * rule, we can tell the user that detection options
3208 * are not supported with those types of rules, and
3209 * what the detection option is */
3210 if ((dopt_keyword == NULL) &&
3211 (dopt->type == OPT_TYPE_DETECTION))
3212 {
3213 dopt_keyword = SnortStrdup(opts[0]);
3214 }
3215
3216 break;
3217 }
3218 }
3219
3220 if (dopt == NULL)
3221 {
3222 /* Maybe it's a preprocessor rule option */
3223 PreprocOptionInit initFunc = NULL;
3224 PreprocOptionEval evalFunc = NULL;
3225 PreprocOptionFastPatternFunc fpFunc = NULL;
3226 PreprocOptionOtnHandler preprocOtnHandler = NULL;
3227 PreprocOptionCleanup cleanupFunc = NULL;
3228 void *opt_data = NULL;
3229
3230 int ret = GetPreprocessorRuleOptionFuncs
3231 (sc, opts[0], &initFunc, &evalFunc,
3232 &preprocOtnHandler, &fpFunc, &cleanupFunc);
3233
3234 if (ret && (initFunc != NULL))
3235 {
3236 initFunc(sc, opts[0], option_args, &opt_data);
3237 AddPreprocessorRuleOption(sc, opts[0], otn, opt_data, evalFunc);
3238 if (preprocOtnHandler != NULL)
3239 otn_handler = (RuleOptOtnHandler)preprocOtnHandler;
3240
3241 DEBUG_WRAP(DebugMessage(DEBUG_INIT, "%s->", opts[0]););
3242 }
3243 else
3244 {
3245 /* Unrecognized rule option */
3246 ParseError("Unknown rule option: '%s'.", opts[0]);
3247 }
3248 }
3249
3250 if (dopt_keyword == NULL)
3251 dopt_keyword = SnortStrdup(opts[0]);
3252
3253 num_detection_opts++;
3254 }
3255
3256 mSplitFree(&opts, num_opts);
3257 }
3258
3259 if ((dopt_keyword != NULL) &&
3260 (otn->sigInfo.rule_type != SI_RULE_TYPE_DETECT))
3261 {
3262 /* Preprocessor and decoder rules can not have
3263 * detection options */
3264 ParseError("Preprocessor and decoder rules do not support "
3265 "detection options: %s.", dopt_keyword);
3266 }
3267
3268 if (dopt_keyword != NULL)
3269 free(dopt_keyword);
3270
3271 /* Each rule must have a sid irrespective of snort
3272 * in test mode or IPS/IDS mode.*/
3273 if (!got_sid)
3274 ParseError("Each rule must contain a rule sid.");
3275
3276 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"OptListEnd\n"););
3277
3278 if ((otn->sigInfo.generator == GENERATOR_FILE_SIGNATURE &&
3279 otn->sigInfo.id == FILE_SIGNATURE_SHA256) ||
3280 otn->sigInfo.generator == GENERATOR_FILE_TYPE)
3281 {
3282 rtn = createDynamicRuleTypeRtn(sc, rtn);
3283 *rtn_addr = rtn;
3284 }
3285 addRtnToOtn(sc, otn, getParserPolicy(sc), rtn);
3286
3287 /* Check for duplicate SID */
3288 otn_dup = OtnLookup(sc->otn_map, otn->sigInfo.generator, otn->sigInfo.id);
3289 if (otn_dup != NULL)
3290 {
3291 otn->ruleIndex = otn_dup->ruleIndex;
3292
3293 if (mergeDuplicateOtn(sc, otn_dup, otn, rtn) == 0)
3294 {
3295 /* We are keeping the old/dup OTN and trashing the new one
3296 * we just created - it's free'd in the remove dup function */
3297 mSplitFree(&toks, num_toks);
3298 return NULL;
3299 }
3300 }
3301 else
3302 {
3303 otn->ruleIndex = RuleIndexMapAdd(ruleIndexMap,
3304 otn->sigInfo.generator,
3305 otn->sigInfo.id);
3306 }
3307
3308 mSplitFree(&toks, num_toks);
3309 }
3310
3311 otn->num_detection_opts += num_detection_opts;
3312 otn_count++;
3313
3314 if (otn->sigInfo.rule_type == SI_RULE_TYPE_DETECT)
3315 {
3316 detect_rule_count++;
3317 }
3318 else if (otn->sigInfo.rule_type == SI_RULE_TYPE_DECODE)
3319 {
3320 //Set the bit if the decoder rule is enabled in the policies
3321 UpdateDecodeRulesArray(otn->sigInfo.id, ENABLE_RULE, ENABLE_ONE_RULE);
3322 decode_rule_count++;
3323 }
3324 else if (otn->sigInfo.rule_type == SI_RULE_TYPE_PREPROC)
3325 {
3326 preproc_rule_count++;
3327 }
3328
3329 fpl = AddOptFuncToList(OptListEnd, otn);
3330 fpl->type = RULE_OPTION_TYPE_LEAF_NODE;
3331
3332 if (otn_handler != NULL)
3333 {
3334 otn_handler(sc, otn);
3335 }
3336
3337 FinalizeContentUniqueness(sc, otn);
3338 ValidateFastPattern(otn);
3339
3340 if ((thdx_tmp != NULL) && (otn->detection_filter != NULL))
3341 {
3342 ParseError("The \"detection_filter\" rule option and the \"threshold\" "
3343 "rule option cannot be used in the same rule.\n");
3344 }
3345
3346 if (thdx_tmp != NULL)
3347 {
3348 int rstat;
3349
3350 thdx_tmp->sig_id = otn->sigInfo.id;
3351 thdx_tmp->gen_id = otn->sigInfo.generator;
3352 rstat = sfthreshold_create(sc, sc->threshold_config, thdx_tmp);
3353
3354 if (rstat)
3355 {
3356 if (rstat == THD_TOO_MANY_THDOBJ)
3357 {
3358 ParseError("threshold (in rule): could not create threshold - "
3359 "only one per sig_id=%u.", thdx_tmp->sig_id);
3360 }
3361 else
3362 {
3363 ParseError("threshold (in rule): could not add threshold "
3364 "for sig_id=%u!\n", thdx_tmp->sig_id);
3365 }
3366 }
3367
3368 thdx_tmp = NULL;
3369 }
3370
3371 /* setup gid,sid->otn mapping */
3372 SoRuleOtnLookupAdd(sc->so_rule_otn_map, otn);
3373 OtnLookupAdd(sc->otn_map, otn);
3374
3375 return otn;
3376 }
3377
3378 /****************************************************************************
3379 *
3380 * Function: GetRuleProtocol(char *)
3381 *
3382 * Purpose: Figure out which protocol the current rule is talking about
3383 *
3384 * Arguments: proto_str => the protocol string
3385 *
3386 * Returns: The integer value of the protocol
3387 *
3388 ***************************************************************************/
3389 static int GetRuleProtocol(char *proto_str)
3390 {
3391 if (strcasecmp(proto_str, RULE_PROTO_OPT__TCP) == 0)
3392 {
3393 return IPPROTO_TCP;
3394 }
3395 else if (strcasecmp(proto_str, RULE_PROTO_OPT__UDP) == 0)
3396 {
3397 return IPPROTO_UDP;
3398 }
3399 else if (strcasecmp(proto_str, RULE_PROTO_OPT__ICMP) == 0)
3400 {
3401 return IPPROTO_ICMP;
3402 }
3403 else if (strcasecmp(proto_str, RULE_PROTO_OPT__IP) == 0)
3404 {
3405 return ETHERNET_TYPE_IP;
3406 }
3407 else
3408 {
3409 /* If we've gotten here, we have a protocol string we didn't recognize
3410 * and should exit */
3411 ParseError("Bad protocol: %s.", proto_str);
3412 }
3413
3414 return -1;
3415 }
3416
3417
3418 static int ProcessIP(SnortConfig *sc, char *addr, RuleTreeNode *rtn, int mode, int neg_list)
3419 {
3420 vartable_t *ip_vartable = sc->targeted_policies[getParserPolicy(sc)]->ip_vartable;
3421
3422 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"Got address string: %s\n",
3423 addr););
3424 assert(rtn);
3425 /* If a rule has a variable in it, we want to copy that variable's
3426 * contents to the IP variable (IP list) stored with the rtn.
3427 * This code tries to look up the variable, and if found, will copy it
3428 * to the rtn->{sip,dip} */
3429 if(mode == SRC)
3430 {
3431 int ret;
3432
3433 if (rtn->sip == NULL)
3434 {
3435 sfip_var_t *tmp = sfvt_lookup_var(ip_vartable, addr);
3436 if (tmp != NULL)
3437 {
3438 rtn->sip = sfvar_create_alias(tmp, tmp->name);
3439 if (rtn->sip == NULL)
3440 ret = SFIP_FAILURE;
3441 else
3442 ret = SFIP_SUCCESS;
3443 }
3444 else
3445 {
3446 rtn->sip = (sfip_var_t *)SnortAlloc(sizeof(sfip_var_t));
3447 ret = sfvt_add_to_var(ip_vartable, rtn->sip, addr);
3448 }
3449 }
3450 else
3451 {
3452 ret = sfvt_add_to_var(ip_vartable, rtn->sip, addr);
3453 }
3454
3455 /* The function sfvt_add_to_var adds 'addr' to the variable 'rtn->sip' */
3456 if (ret != SFIP_SUCCESS)
3457 {
3458 if(ret == SFIP_LOOKUP_FAILURE)
3459 {
3460 ParseError("Undefined variable in the string: %s.", addr);
3461 }
3462 else if(ret == SFIP_CONFLICT)
3463 {
3464 ParseError("Negated IP ranges that are more general than "
3465 "non-negated ranges are not allowed. Consider "
3466 "inverting the logic: %s.", addr);
3467 }
3468 else if(ret == SFIP_NOT_ANY)
3469 {
3470 ParseError("!any is not allowed: %s.", addr);
3471 }
3472 else
3473 {
3474 ParseError("Unable to process the IP address: %s.", addr);
3475 }
3476 }
3477
3478 if(rtn->sip->head && rtn->sip->head->flags & SFIP_ANY)
3479 {
3480 rtn->flags |= ANY_SRC_IP;
3481 }
3482 }
3483 /* mode == DST */
3484 else
3485 {
3486 int ret;
3487
3488 if (rtn->dip == NULL)
3489 {
3490 sfip_var_t *tmp = sfvt_lookup_var(ip_vartable, addr);
3491 if (tmp != NULL)
3492 {
3493 rtn->dip = sfvar_create_alias(tmp, tmp->name);
3494 if (rtn->dip == NULL)
3495 ret = SFIP_FAILURE;
3496 else
3497 ret = SFIP_SUCCESS;
3498 }
3499 else
3500 {
3501 rtn->dip = (sfip_var_t *)SnortAlloc(sizeof(sfip_var_t));
3502 ret = sfvt_add_to_var(ip_vartable, rtn->dip, addr);
3503 }
3504 }
3505 else
3506 {
3507 ret = sfvt_add_to_var(ip_vartable, rtn->dip, addr);
3508 }
3509
3510 if (ret != SFIP_SUCCESS)
3511 {
3512 if(ret == SFIP_LOOKUP_FAILURE)
3513 {
3514 ParseError("Undefined variable in the string: %s.", addr);
3515 }
3516 else if(ret == SFIP_CONFLICT)
3517 {
3518 ParseError("Negated IP ranges that are more general than "
3519 "non-negated ranges are not allowed. Consider "
3520 "inverting the logic: %s.", addr);
3521 }
3522 else if(ret == SFIP_NOT_ANY)
3523 {
3524 ParseError("!any is not allowed: %s.", addr);
3525 }
3526 else
3527 {
3528 ParseError("Unable to process the IP address: %s.", addr);
3529 }
3530 }
3531
3532 if(rtn->dip->head && rtn->dip->head->flags & SFIP_ANY)
3533 {
3534 rtn->flags |= ANY_DST_IP;
3535 }
3536 }
3537
3538 /* Make sure the IP lists provided by the user are valid */
3539 if (mode == SRC)
3540 ValidateIPList(rtn->sip, addr);
3541 else
3542 ValidateIPList(rtn->dip, addr);
3543
3544 return 0;
3545 }
3546
3547
3548 /****************************************************************************
3549 *
3550 * Function: ParsePort(SnortConfig *, char *, u_short *)
3551 *
3552 * Purpose: Convert the port string over to an integer value
3553 *
3554 * Arguments: prule_port => port rule string
3555 * port => converted integer value of the port
3556 *
3557 * Returns: 0 for a normal port number, 1 for an "any" port
3558 *
3559 ***************************************************************************/
3560 int ParsePort(SnortConfig *sc, char *prule_port, uint16_t *hi_port, uint16_t *lo_port, char *proto, int *not_flag)
3561 {
3562 char **toks; /* token dbl buffer */
3563 int num_toks; /* number of tokens found by mSplit() */
3564 char *rule_port; /* port string */
3565
3566 *not_flag = 0;
3567
3568 /* check for variable */
3569 if(!strncmp(prule_port, "$", 1))
3570 {
3571 if((rule_port = VarGet(sc, prule_port + 1)) == NULL)
3572 {
3573 ParseError("Undefined variable %s.", prule_port);
3574 }
3575 }
3576 else
3577 rule_port = prule_port;
3578
3579 if(rule_port[0] == '(')
3580 {
3581 /* user forgot to put a port number in for this rule */
3582 ParseError("Bad port number: \"%s\".", rule_port);
3583 }
3584
3585
3586 /* check for wildcards */
3587 if(!strcasecmp(rule_port, "any"))
3588 {
3589 *hi_port = 0;
3590 *lo_port = 0;
3591 return 1;
3592 }
3593
3594 if(rule_port[0] == '!')
3595 {
3596 if(!strcasecmp(&rule_port[1], "any"))
3597 {
3598 ParseWarning("Negating \"any\" is invalid. Rule "
3599 "will be ignored.");
3600 return -1;
3601 }
3602
3603 *not_flag = 1;
3604 rule_port++;
3605 }
3606
3607 if(rule_port[0] == ':')
3608 {
3609 *lo_port = 0;
3610 }
3611
3612 toks = mSplit(rule_port, ":", 2, &num_toks, 0);
3613
3614 switch(num_toks)
3615 {
3616 case 1:
3617 *hi_port = (u_short)ConvPort(toks[0], proto);
3618
3619 if(rule_port[0] == ':')
3620 {
3621 *lo_port = 0;
3622 }
3623 else
3624 {
3625 *lo_port = *hi_port;
3626
3627 if(strchr(rule_port, ':') != NULL)
3628 {
3629 *hi_port = MAXPORTS-1;
3630 }
3631 }
3632
3633 break;
3634
3635 case 2:
3636 *lo_port = (u_short)ConvPort(toks[0], proto);
3637
3638 if(toks[1][0] == 0)
3639 *hi_port = MAXPORTS-1;
3640 else
3641 *hi_port = (u_short)ConvPort(toks[1], proto);
3642
3643 break;
3644
3645 default:
3646 ParseError("Port conversion failed on \"%s\".", rule_port);
3647 }
3648
3649 mSplitFree(&toks, num_toks);
3650
3651 return 0;
3652 }
3653
3654 /****************************************************************************
3655 *
3656 * Function: ConvPort(char *, char *)
3657 *
3658 * Purpose: Convert the port string over to an integer value
3659 *
3660 * Arguments: port => port string
3661 * proto => converted integer value of the port
3662 *
3663 * Returns: the port number
3664 *
3665 ***************************************************************************/
3666 uint16_t ConvPort(char *port, char *proto)
3667 {
3668 int conv; /* storage for the converted number */
3669 char *digit; /* used to check for a number */
3670 struct servent *service_info;
3671
3672 /*
3673 * convert a "word port" (http, ftp, imap, whatever) to its corresponding
3674 * numeric port value
3675 */
3676 if(isalpha((int) port[0]) != 0)
3677 {
3678 service_info = getservbyname(port, proto);
3679
3680 if(service_info != NULL)
3681 {
3682 conv = ntohs(service_info->s_port);
3683 return conv;
3684 }
3685 else
3686 {
3687 ParseError("getservbyname() failed on \"%s\".", port);
3688 }
3689 }
3690 digit = port;
3691 while (*digit) {
3692
3693 if(!isdigit((int) *digit))
3694 {
3695 ParseError("Invalid port: %s.", port);
3696 }
3697 digit++;
3698 }
3699 /* convert the value */
3700 conv = atoi(port);
3701
3702 /* make sure it's in bounds */
3703 if ((conv < 0) || (conv > MAXPORTS-1))
3704 {
3705 ParseError("Bad port number: %s.", port);
3706 }
3707
3708 return (uint16_t)conv;
3709 }
3710
3711 /****************************************************************************
3712 *
3713 * Function: XferHeader(RuleTreeNode *, RuleTreeNode *)
3714 *
3715 * Purpose: Transfer the rule block header data from point A to point B
3716 *
3717 * Arguments: rule => the place to xfer from
3718 * rtn => the place to xfer to
3719 *
3720 * Returns: void function
3721 *
3722 ***************************************************************************/
3723 static void XferHeader(RuleTreeNode *test_node, RuleTreeNode *rtn)
3724 {
3725 rtn->flags = test_node->flags;
3726 rtn->type = test_node->type;
3727 rtn->sip = test_node->sip;
3728 rtn->dip = test_node->dip;
3729
3730 rtn->proto = test_node->proto;
3731
3732 rtn->src_portobject = test_node->src_portobject;
3733 rtn->dst_portobject = test_node->dst_portobject;
3734 }
3735
3736 /****************************************************************************
3737 *
3738 * Function: CompareIPNodes(RuleTreeNode *, RuleTreeNode *). Support function
3739 * for CompareIPLists.
3740 *
3741 * Purpose: Checks if the node's contents equal.
3742 *
3743 * Returns: 1 if they match, 0 if they don't
3744 *
3745 ***************************************************************************/
3746 int CompareIPNodes(IpAddrNode *one, IpAddrNode *two)
3747 {
3748 if( (sfip_compare(&one->ip->addr, &two->ip->addr) != SFIP_EQUAL) ||
3749 (sfip_bits(one->ip) != sfip_bits(two->ip)) ||
3750 (sfvar_flags(one) != sfvar_flags(two)) )
3751 return 0;
3752 return 1;
3753 }
3754
3755
3756 /****************************************************************************
3757 *
3758 * Function: TestHeader(RuleTreeNode *, RuleTreeNode *)
3759 *
3760 * Purpose: Check to see if the two header blocks are identical
3761 *
3762 * Arguments: rule => uh
3763 * rtn => uuuuhhhhh....
3764 *
3765 * Returns: 1 if they match, 0 if they don't
3766 *
3767 ***************************************************************************/
3768 static int TestHeader(RuleTreeNode * rule, RuleTreeNode * rtn)
3769 {
3770 if ((rule == NULL) || (rtn == NULL))
3771 return 0;
3772
3773 if (rule->type != rtn->type)
3774 return 0;
3775
3776 if (rule->proto != rtn->proto)
3777 return 0;
3778
3779 /* For custom rule type declarations */
3780 if (rule->listhead != rtn->listhead)
3781 return 0;
3782
3783 if (rule->flags != rtn->flags)
3784 return 0;
3785
3786 if ((rule->sip != NULL) && (rtn->sip != NULL) &&
3787 (sfvar_compare(rule->sip, rtn->sip) != SFIP_EQUAL))
3788 {
3789 return 0;
3790 }
3791
3792 if ((rule->dip != NULL) && (rtn->dip != NULL) &&
3793 (sfvar_compare(rule->dip, rtn->dip) != SFIP_EQUAL))
3794 {
3795 return 0;
3796 }
3797
3798 /* compare the port group pointers - this prevents confusing src/dst port objects
3799 * with the same port set, and it's quicker. It does assume that we only have
3800 * one port object and pointer for each unique port set...this is handled by the
3801 * parsing and initial port object storage and lookup. This must be consistent during
3802 * the rule parsing phase. - man */
3803 if ((rule->src_portobject != rtn->src_portobject)
3804 || (rule->dst_portobject != rtn->dst_portobject))
3805 {
3806 return 0;
3807 }
3808
3809 return 1;
3810 }
3811
3812 /*
3813 * PortVarDefine
3814 *
3815 * name - portlist name, i.e. http, smtp, ...
3816 * s - port number, port range, or a list of numbers/ranges in brackets
3817 *
3818 * examples:
3819 * portvar http [80,8080,8138,8700:8800,!8711]
3820 * portvar http $http_basic
3821 */
3822 static int PortVarDefine(SnortConfig *sc, char *name, char *s)
3823 {
3824 PortObject *po;
3825 POParser pop;
3826 char *errstr="unknown";
3827 int rstat;
3828 PortVarTable *portVarTable = sc->targeted_policies[getParserPolicy(sc)]->portVarTable;
3829 char *end;
3830 bool invalidvar = true;
3831
3832 DisallowCrossTableDuplicateVars(sc, name, VAR_TYPE__PORTVAR);
3833
3834 for(end = name; *end && !isspace((int)*end) && *end != '\\'; end++)
3835 {
3836 if(isalpha((int)*end))
3837 invalidvar = false;
3838 }
3839
3840 if(invalidvar)
3841 ParseError("Can not define variable name - %s. Use different name", name);
3842
3843 if( SnortStrcasestr(s,strlen(s),"any") ) /* this allows 'any' or '[any]' */
3844 {
3845 if(strstr(s,"!"))
3846 {
3847 ParseError("Illegal use of negation and 'any': %s.", s);
3848 }
3849
3850 po = PortObjectNew();
3851 if( !po )
3852 {
3853 ParseError("PortVarTable missing an 'any' variable.\n");
3854 }
3855 PortObjectSetName( po, name );
3856 PortObjectAddPortAny( po );
3857 }
3858 else
3859 {
3860 /* Parse the Port List info into a PortObject */
3861 po = PortObjectParseString(portVarTable, &pop, name, s, 0);
3862 if(!po)
3863 {
3864 errstr = PortObjectParseError( &pop );
3865 ParseError("*** PortVar Parse error: (pos=%d,error=%s)\n>>%s\n>>%*s.",
3866 pop.pos,errstr,s,pop.pos,"^");
3867 }
3868 }
3869
3870 /* Add The PortObject to the PortList Table */
3871 rstat = PortVarTableAdd(portVarTable, po);
3872 if( rstat < 0 )
3873 {
3874 ParseError("***PortVarTableAdd failed with '%s', exiting.", po->name);
3875 }
3876 else if( rstat > 0 )
3877 {
3878 ParseMessage("PortVar '%s', already defined.", po->name);
3879 }
3880
3881 /* Print the PortList - PortObjects */
3882 LogMessage("PortVar '%s' defined : ",po->name);
3883 PortObjectPrintPortsRaw(po);
3884 LogMessage("\n");
3885
3886 return 0;
3887 }
3888
3889 /****************************************************************************
3890 *
3891 * Function: VarAlloc()
3892 *
3893 * Purpose: allocates memory for a variable
3894 *
3895 * Arguments: none
3896 *
3897 * Returns: pointer to new VarEntry
3898 *
3899 ***************************************************************************/
3900 VarEntry *VarAlloc()
3901 {
3902 VarEntry *new;
3903
3904 new = (VarEntry *)SnortAlloc(sizeof(VarEntry));
3905
3906 return(new);
3907 }
3908
3909 /****************************************************************************
3910 *
3911 * Function: VarIsIpAddr(char *, char *)
3912 *
3913 * Purpose: Checks if a var is an IP address. Necessary since moving forward
3914 * we want all IP addresses handled by the IP variable table.
3915 * If a list is given, this checks each value.
3916 *
3917 * Arguments: value => the string to check
3918 *
3919 * Returns: 1 if IP address, 0 otherwise
3920 *
3921 ***************************************************************************/
3922 static int VarIsIpAddr(vartable_t *ip_vartable, char *value)
3923 {
3924 char *tmp;
3925
3926 /* empty list, consider this an IP address */
3927 if ((*value == '[') && (*(value+1) == ']'))
3928 return 1;
3929
3930 while(*value == '!' || *value == '[') value++;
3931
3932 /* Check for dotted-quad */
3933 if( isdigit((int)*value) &&
3934 ((tmp = strchr(value, (int)'.')) != NULL) &&
3935 ((tmp = strchr(tmp+1, (int)'.')) != NULL) &&
3936 (strchr(tmp+1, (int)'.') != NULL))
3937 return 1;
3938
3939 /* IPv4 with a mask, and fewer than 4 fields */
3940 else if( isdigit((int)*value) &&
3941 (strchr(value+1, (int)':') == NULL) &&
3942 ((tmp = strchr(value+1, (int)'/')) != NULL) &&
3943 isdigit((int)(*(tmp+1))) )
3944 return 1;
3945
3946 /* IPv6 */
3947 else if((tmp = strchr(value, (int)':')) != NULL)
3948 {
3949 char *tmp2;
3950
3951 if((tmp2 = strchr(tmp+1, (int)':')) == NULL)
3952 return 0;
3953
3954 for(tmp++; tmp < tmp2; tmp++)
3955 if(!isxdigit((int)*tmp))
3956 return 0;
3957
3958 return 1;
3959 }
3960
3961 /* Any */
3962 else if(!strncmp(value, "any", 3))
3963 return 1;
3964
3965 /* Check if it's a variable containing an IP */
3966 else if(sfvt_lookup_var(ip_vartable, value+1) || sfvt_lookup_var(ip_vartable, value))
3967 return 1;
3968
3969 return 0;
3970 }
3971
3972 /****************************************************************************
3973 *
3974 * Function: CheckBrackets(char *)
3975 *
3976 * Purpose: Check that the brackets match up in a string that
3977 * represents a list.
3978 *
3979 * Arguments: value => the string to check
3980 *
3981 * Returns: 1 if the brackets match correctly, 0 otherwise
3982 *
3983 ***************************************************************************/
3984 static int CheckBrackets(char *value)
3985 {
3986 int num_brackets = 0;
3987
3988 while (*value == '!')
3989 value++;
3990
3991 if ((value[0] != '[') || value[strlen(value)-1] != ']')
3992 {
3993 /* List does not begin or end with a bracket. */
3994 return 0;
3995 }
3996
3997 while ((*value != '\0') && (num_brackets >= 0))
3998 {
3999 if (*value == '[')
4000 num_brackets++;
4001 else if (*value == ']')
4002 num_brackets--;
4003 value++;
4004 }
4005 if (num_brackets != 0)
4006 {
4007 /* Mismatched brackets */
4008 return 0;
4009 }
4010
4011 return 1;
4012 }
4013
4014 /****************************************************************************
4015 *
4016 * Function: VarIsIpList(vartable_t *, char*)
4017 *
4018 * Purpose: Checks if a var is a list of IP addresses.
4019 *
4020 * Arguments: value => the string to check
4021 *
4022 * Returns: 1 if each item is an IP address, 0 otherwise
4023 *
4024 ***************************************************************************/
4025 static int VarIsIpList(vartable_t *ip_vartable, char *value)
4026 {
4027 char *copy, *item;
4028 int item_is_ip = 1;
4029
4030 copy = SnortStrdup((const char*)value);
4031
4032 /* Ensure that the brackets are correct. */
4033 if (strchr((const char*)copy, ','))
4034 {
4035 /* This is a list! */
4036 if (CheckBrackets(copy) == 0)
4037 {
4038 free(copy);
4039 return 0;
4040 }
4041 }
4042
4043 /* There's no need to worry about the list structure here.
4044 * We just strip out the IP delimiters and process each one. */
4045 item = strtok(copy, "[],!");
4046 while ((item != NULL) && item_is_ip)
4047 {
4048 item_is_ip = VarIsIpAddr(ip_vartable, item);
4049 item = strtok(NULL, "[],!");
4050 }
4051
4052 free(copy);
4053 return item_is_ip;
4054 }
4055
4056 /****************************************************************************
4057 *
4058 * Function: DisallowCrossTableDuplicateVars(char *, int)
4059 *
4060 * Purpose: FatalErrors if the a variable name is redefined across variable
4061 * types. Enforcing this mutual exclusion prevents the
4062 * catatrophe where the variable lookup fall-through (see VarSearch)
4063 * finds an unintended variable from the wrong table. Note: VarSearch
4064 * is only necessary for ExpandVars.
4065 *
4066 * Arguments: name => The name of the variable
4067 * var_type => The type of the variable that is about to be defined.
4068 * The corresponding variable table will not be searched.
4069 *
4070 * Returns: void function
4071 *
4072 ***************************************************************************/
4073 static void DisallowCrossTableDuplicateVars(SnortConfig *sc, char *name, VarType var_type)
4074 {
4075 VarEntry *var_table = sc->targeted_policies[getParserPolicy(sc)]->var_table;
4076 PortVarTable *portVarTable = sc->targeted_policies[getParserPolicy(sc)]->portVarTable;
4077 VarEntry *p = var_table;
4078 vartable_t *ip_vartable = sc->targeted_policies[getParserPolicy(sc)]->ip_vartable;
4079
4080 /* If this is a faked Portvar, treat as a portvar */
4081 if ((var_type == VAR_TYPE__DEFAULT) &&
4082 (strstr(name, "_PORT") || strstr(name, "PORT_")))
4083 {
4084 var_type = VAR_TYPE__PORTVAR;
4085 }
4086
4087 switch (var_type)
4088 {
4089 case VAR_TYPE__DEFAULT:
4090 if (PortVarTableFind(portVarTable, name)
4091 || sfvt_lookup_var(ip_vartable, name)
4092 )
4093 {
4094 ParseError("Can not redefine variable name %s to be of type "
4095 "'var'. Use a different name.", name);
4096 }
4097 break;
4098
4099 case VAR_TYPE__PORTVAR:
4100 if (var_table != NULL)
4101 {
4102 do
4103 {
4104 if(strcasecmp(p->name, name) == 0)
4105 {
4106 ParseError("Can not redefine variable name %s to be of "
4107 "type 'portvar'. Use a different name.", name);
4108 }
4109 p = p->next;
4110 } while(p != var_table);
4111 }
4112
4113 if(sfvt_lookup_var(ip_vartable, name))
4114 {
4115 ParseError("Can not redefine variable name %s to be of type "
4116 "'portvar'. Use a different name.", name);
4117 }
4118
4119 break;
4120
4121 case VAR_TYPE__IPVAR:
4122 if (var_table != NULL)
4123 {
4124 do
4125 {
4126 if(strcasecmp(p->name, name) == 0)
4127 {
4128 ParseError("Can not redefine variable name %s to be of "
4129 "type 'ipvar'. Use a different name.", name);
4130 }
4131
4132 p = p->next;
4133 } while(p != var_table);
4134 }
4135
4136 if(PortVarTableFind(portVarTable, name))
4137 {
4138 ParseError("Can not redefine variable name %s to be of type "
4139 "'ipvar'. Use a different name.", name);
4140 }
4141
4142 default:
4143 /* Invalid function usage */
4144 break;
4145 }
4146 }
4147
4148 /****************************************************************************
4149 *
4150 * Function: VarDefine(char *, char *)
4151 *
4152 * Purpose: define the contents of a variable
4153 *
4154 * Arguments: name => the name of the variable
4155 * value => the contents of the variable
4156 *
4157 * Returns: void function
4158 *
4159 ***************************************************************************/
4160 VarEntry * VarDefine(SnortConfig *sc, char *name, char *value)
4161 {
4162 VarEntry *var_table = sc->targeted_policies[getParserPolicy(sc)]->var_table;
4163 vartable_t *ip_vartable = sc->targeted_policies[getParserPolicy(sc)]->ip_vartable;
4164 VarEntry *p;
4165 uint32_t var_id = 0;
4166
4167 if(value == NULL)
4168 {
4169 ParseError("Bad value in variable definition! Make sure you don't "
4170 "have a \"$\" in the var name.");
4171 }
4172
4173 if(VarIsIpList(ip_vartable, value))
4174 {
4175 SFIP_RET ret;
4176
4177 if (ip_vartable == NULL)
4178 return NULL;
4179
4180 /* Verify a variable by this name is not already used as either a
4181 * portvar or regular var. Enforcing this mutual exclusion prevents the
4182 * catatrophe where the variable lookup fall-through (see VarSearch)
4183 * finds an unintended variable from the wrong table. Note: VarSearch
4184 * is only necessary for ExpandVars. */
4185 DisallowCrossTableDuplicateVars(sc, name, VAR_TYPE__IPVAR);
4186
4187 if((ret = sfvt_define(ip_vartable, name, value)) != SFIP_SUCCESS)
4188 {
4189 switch(ret) {
4190 case SFIP_ARG_ERR:
4191 ParseError("The following is not allowed: %s.", value);
4192 break;
4193
4194 case SFIP_DUPLICATE:
4195 ParseMessage("Var '%s' redefined.", name);
4196 break;
4197
4198 case SFIP_CONFLICT:
4199 ParseError("Negated IP ranges that are more general than "
4200 "non-negated ranges are not allowed. Consider "
4201 "inverting the logic in %s.", name);
4202 break;
4203
4204 case SFIP_NOT_ANY:
4205 ParseError("!any is not allowed in %s.", name);
4206 break;
4207
4208 default:
4209 ParseError("Failed to parse the IP address: %s.", value);
4210 }
4211 }
4212 return NULL;
4213 }
4214 /* Check if this is a variable that stores an IP */
4215 else if(*value == '$')
4216 {
4217 sfip_var_t *var;
4218 if((var = sfvt_lookup_var(ip_vartable, value)) != NULL)
4219 {
4220 sfvt_define(ip_vartable, name, value);
4221 return NULL;
4222 }
4223 }
4224
4225
4226 DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,
4227 "VarDefine: name=%s value=%s\n",name,value););
4228
4229 /* Check to see if this variable is just being aliased */
4230 if (var_table != NULL)
4231 {
4232 VarEntry *tmp = var_table;
4233
4234 do
4235 {
4236 /* value+1 to move past $ */
4237 if (strcmp(tmp->name, value+1) == 0)
4238 {
4239 var_id = tmp->id;
4240 break;
4241 }
4242
4243 tmp = tmp->next;
4244
4245 } while (tmp != var_table);
4246 }
4247
4248 value = ExpandVars(sc, value);
4249 if(!value)
4250 {
4251 ParseError("Could not expand var('%s').", name);
4252 }
4253
4254 DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,
4255 "VarDefine: name=%s value=%s (expanded)\n",name,value););
4256
4257 DisallowCrossTableDuplicateVars(sc, name, VAR_TYPE__DEFAULT);
4258
4259 if (var_table == NULL)
4260 {
4261 p = VarAlloc();
4262 p->name = SnortStrdup(name);
4263 p->value = SnortStrdup(value);
4264
4265 p->prev = p;
4266 p->next = p;
4267
4268 sc->targeted_policies[getParserPolicy(sc)]->var_table = p;
4269
4270 p->id = sc->targeted_policies[getParserPolicy(sc)]->var_id++;
4271
4272 return p;
4273 }
4274
4275 /* See if an existing variable is being redefined */
4276 p = var_table;
4277
4278 do
4279 {
4280 if (strcasecmp(p->name, name) == 0)
4281 {
4282 if (p->value != NULL)
4283 free(p->value);
4284
4285 p->value = SnortStrdup(value);
4286 ParseWarning("Var '%s' redefined\n", p->name);
4287 return p;
4288 }
4289
4290 p = p->next;
4291
4292 } while (p != var_table); /* List is circular */
4293
4294 p = VarAlloc();
4295 p->name = SnortStrdup(name);
4296 p->value = SnortStrdup(value);
4297 p->prev = var_table;
4298 p->next = var_table->next;
4299 p->next->prev = p;
4300 var_table->next = p;
4301
4302 if (!var_id)
4303 p->id = sc->targeted_policies[getParserPolicy(sc)]->var_id++;
4304 else
4305 p->id = var_id;
4306
4307 #ifdef XXXXXXX
4308 vlen = strlen(value);
4309 LogMessage("Var '%s' defined, value len = %d chars", p->name, vlen );
4310
4311 if( vlen < 64 )
4312 {
4313 LogMessage(", value = %s\n", value );
4314 }
4315 else
4316 {
4317 LogMessage("\n");
4318 n = 128;
4319 s = value;
4320 while(vlen)
4321 {
4322 if( n > vlen ) n = vlen;
4323 LogMessage(" %.*s\n", n, s );
4324 s += n;
4325 vlen -= n;
4326 }
4327 }
4328 #endif
4329
4330 return p;
4331 }
4332
4333 static void DeleteVars(VarEntry *var_table)
4334 {
4335 VarEntry *q, *p = var_table;
4336
4337 while (p)
4338 {
4339 q = p->next;
4340 if (p->name)
4341 free(p->name);
4342 if (p->value)
4343 free(p->value);
4344 if (p->addrset)
4345 {
4346 IpAddrSetDestroy(p->addrset);
4347 }
4348 free(p);
4349 p = q;
4350 if (p == var_table)
4351 break; /* Grumble, it's a friggin circular list */
4352 }
4353 }
4354
4355 /****************************************************************************
4356 *
4357 * Function: VarGet(SnortConfig *, char *)
4358 *
4359 * Purpose: get the contents of a variable
4360 *
4361 * Arguments: name => the name of the variable
4362 *
4363 * Returns: char * to contents of variable or FatalErrors on an
4364 * undefined variable name
4365 *
4366 ***************************************************************************/
4367 char *VarGet(SnortConfig *sc, char *name)
4368 {
4369 SnortPolicy *policy;
4370 VarEntry *var_table;
4371 vartable_t *ip_vartable;
4372 sfip_var_t *var;
4373
4374 if (sc == NULL)
4375 return NULL;
4376
4377 policy = sc->targeted_policies[getParserPolicy(sc)];
4378 if (policy == NULL)
4379 return NULL;
4380
4381 var_table = sc->targeted_policies[getParserPolicy(sc)]->var_table;
4382
4383 // XXX-IPv6 This function should never be used if IP6 support is enabled!
4384 // Infact it won't presently even work for IP variables since the raw ASCII
4385 // value is never stored, and is never meant to be used.
4386 ip_vartable = sc->targeted_policies[getParserPolicy(sc)]->ip_vartable;
4387
4388 if((var = sfvt_lookup_var(ip_vartable, name)) == NULL) {
4389 /* Do the old style lookup since it wasn't found in
4390 * the variable table */
4391 if(var_table != NULL)
4392 {
4393 VarEntry *p = var_table;
4394 do
4395 {
4396 if(strcasecmp(p->name, name) == 0)
4397 return p->value;
4398 p = p->next;
4399 } while(p != var_table);
4400 }
4401
4402 ParseError("Undefined variable name: %s.", name);
4403 }
4404
4405 return name;
4406
4407 }
4408
4409 /****************************************************************************
4410 *
4411 * Function: ExpandVars()
4412 *
4413 * Purpose: expand all variables in a string
4414 *
4415 * Arguments:
4416 * SnortConfig *
4417 * The snort config that has the vartables.
4418 * char *
4419 * The name of the variable.
4420 *
4421 * Returns:
4422 * char *
4423 * The expanded string. Note that the string is returned in a
4424 * static variable and most likely needs to be string dup'ed.
4425 *
4426 ***************************************************************************/
4427 static char * ExpandVars(SnortConfig *sc, char *string)
4428 {
4429 static char estring[ PARSERULE_SIZE ];
4430
4431 char rawvarname[128], varname[128], varaux[128], varbuffer[128];
4432 char varmodifier, *varcontents;
4433 int varname_completed, c, i, j, iv, jv, l_string, name_only;
4434 int quote_toggle = 0;
4435
4436 if(!string || !*string || !strchr(string, '$'))
4437 return(string);
4438
4439 memset((char *) estring, 0, PARSERULE_SIZE);
4440
4441 i = j = 0;
4442 l_string = strlen(string);
4443 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES, "ExpandVars, Before: %s\n", string););
4444
4445 while(i < l_string && j < (int)sizeof(estring) - 1)
4446 {
4447 c = string[i++];
4448
4449 if(c == '"')
4450 {
4451 /* added checks to make sure that we are inside a quoted string
4452 */
4453 quote_toggle ^= 1;
4454 }
4455
4456 if(c == '$' && !quote_toggle)
4457 {
4458 memset((char *) rawvarname, 0, sizeof(rawvarname));
4459 varname_completed = 0;
4460 name_only = 1;
4461 iv = i;
4462 jv = 0;
4463
4464 if(string[i] == '(')
4465 {
4466 name_only = 0;
4467 iv = i + 1;
4468 }
4469
4470 while(!varname_completed
4471 && iv < l_string
4472 && jv < (int)sizeof(rawvarname) - 1)
4473 {
4474 c = string[iv++];
4475
4476 if((name_only && !(isalnum(c) || c == '_'))
4477 || (!name_only && c == ')'))
4478 {
4479 varname_completed = 1;
4480
4481 if(name_only)
4482 iv--;
4483 }
4484 else
4485 {
4486 rawvarname[jv++] = (char)c;
4487 }
4488 }
4489
4490 if(varname_completed || iv == l_string)
4491 {
4492 char *p;
4493
4494 i = iv;
4495
4496 varcontents = NULL;
4497
4498 memset((char *) varname, 0, sizeof(varname));
4499 memset((char *) varaux, 0, sizeof(varaux));
4500 varmodifier = ' ';
4501
4502 p = strchr(rawvarname, ':');
4503 if (p)
4504 {
4505 SnortStrncpy(varname, rawvarname, p - rawvarname);
4506
4507 if(strlen(p) >= 2)
4508 {
4509 varmodifier = *(p + 1);
4510 SnortStrncpy(varaux, p + 2, sizeof(varaux));
4511 }
4512 }
4513 else
4514 SnortStrncpy(varname, rawvarname, sizeof(varname));
4515
4516 memset((char *) varbuffer, 0, sizeof(varbuffer));
4517
4518 varcontents = VarSearch(sc, varname);
4519
4520 switch(varmodifier)
4521 {
4522 case '-':
4523 if(!varcontents || !strlen(varcontents))
4524 varcontents = varaux;
4525 break;
4526
4527 case '?':
4528 if(!varcontents || !strlen(varcontents))
4529 {
4530 ErrorMessage("%s(%d): ", file_name, file_line);
4531
4532 if(strlen(varaux))
4533 ParseError("%s", varaux);
4534 else
4535 ParseError("Undefined variable \"%s\".", varname);
4536 }
4537 break;
4538 }
4539
4540 /* If variable not defined now, we're toast */
4541 if(!varcontents || !strlen(varcontents))
4542 ParseError("Undefined variable name: %s.", varname);
4543
4544 if(varcontents)
4545 {
4546 int l_varcontents = strlen(varcontents);
4547
4548 iv = 0;
4549
4550 while(iv < l_varcontents && j < (int)sizeof(estring) - 1)
4551 estring[j++] = varcontents[iv++];
4552 }
4553 }
4554 else
4555 {
4556 estring[j++] = '$';
4557 }
4558 }
4559 else
4560 {
4561 estring[j++] = (char)c;
4562 }
4563 }
4564
4565 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES, "ExpandVars, After: %s\n", estring););
4566
4567 return estring;
4568 }
4569
4570 char * ProcessFileOption(SnortConfig *sc, const char *filespec)
4571 {
4572 char *filename = NULL;
4573 char buffer[STD_BUF];
4574
4575 if (sc == NULL)
4576 sc = snort_conf;
4577
4578 if(filespec == NULL)
4579 {
4580 ParseError("no argument in this file option, remove extra ':' at the end of the alert option\n");
4581 }
4582
4583 /* look for ".." in the string and complain and exit if it is found */
4584 if(strstr(filespec, "..") != NULL)
4585 {
4586 ParseError("file definition contains \"..\". Do not do that!\n");
4587 }
4588
4589 if(filespec[0] == '/')
4590 {
4591 /* absolute filespecs are saved as is */
4592 filename = SnortStrdup(filespec);
4593 }
4594 else
4595 {
4596 /* relative filespec is considered relative to the log directory */
4597 /* or /var/log if the log directory has not been set */
4598 /* Make sure this function isn't called before log dir is set */
4599 if ((sc != NULL) && (sc->log_dir != NULL))
4600 {
4601 strlcpy(buffer, snort_conf->log_dir, STD_BUF);
4602 }
4603 else
4604 {
4605 strlcpy(buffer, "/var/log/snort", STD_BUF);
4606 }
4607
4608 strlcat(buffer, "/", STD_BUF - strlen(buffer));
4609 strlcat(buffer, filespec, STD_BUF - strlen(buffer));
4610 buffer[sizeof(buffer) - 1] = '\0';
4611 filename = SnortStrdup(buffer);
4612 }
4613
4614 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"ProcessFileOption: %s\n", filename););
4615
4616 return filename;
4617 }
4618
4619
4620 #ifdef TARGET_BASED
4621 static void ParseAttributeTable(SnortConfig *sc, SnortPolicy *p, char *args)
4622 {
4623 tSfPolicyId currentPolicyId = getParserPolicy(sc);
4624 tSfPolicyId defaultPolicyId = sfGetDefaultPolicy(sc->policy_config);
4625 TargetBasedConfig *defTbc = &sc->targeted_policies[defaultPolicyId]->target_based_config;
4626
4627 /* Save for configuring after configuration is parsed in case
4628 * config max_attribute_hosts is configured after this */
4629 if ((currentPolicyId != defaultPolicyId)
4630 && ((defTbc->args == NULL) || (strcmp(args, defTbc->args) != 0)))
4631 {
4632 //arguments should be same as in default policy. Ignoring the arguments
4633 ParseError("Attribute table must be configured in default policy if "
4634 "it is to be used in other policies and attribute table "
4635 "filename must be the same across policies.");
4636 }
4637
4638 if(p->target_based_config.args)
4639 free(p->target_based_config.args);
4640 p->target_based_config.args = SnortStrdup(args);
4641
4642 if (file_name != NULL)
4643 {
4644 if(p->target_based_config.file_name)
4645 free(p->target_based_config.file_name);
4646 p->target_based_config.file_name = SnortStrdup(file_name);
4647 p->target_based_config.file_line = file_line;
4648 }
4649 }
4650 #endif
4651
4652 static void ParseConfig(SnortConfig *sc, SnortPolicy *p, char *args)
4653 {
4654 char **toks;
4655 int num_toks;
4656 char *opts = NULL;
4657 int i;
4658
4659 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"Rule file config\n"););
4660
4661 toks = mSplit(args, ":", 2, &num_toks, 0);
4662
4663 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"Opt: %s\n", toks[0]););
4664
4665 if (num_toks > 1)
4666 {
4667 /* Dup the opts because we're putting into hash table */
4668 opts = SnortStrdup(toks[1]);
4669 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"Args: %s\n", opts););
4670 }
4671
4672 switch (sfghash_add(sc->config_table, toks[0], opts))
4673 {
4674 case SFGHASH_NOMEM:
4675 ParseError("%s(%d) No memory to add entry to config table.\n",
4676 __FILE__, __LINE__);
4677 break;
4678
4679 case SFGHASH_INTABLE:
4680 /* Only reference and classifications are likely dup candidates
4681 * right now and we're not too worried about keeping track of
4682 * all of them */
4683 if (opts != NULL)
4684 {
4685 free(opts);
4686 opts = toks[1];
4687 }
4688
4689 break;
4690
4691 default:
4692 break;
4693 }
4694
4695 for (i = 0; config_opts[i].name != NULL; i++)
4696 {
4697 if (strcasecmp(toks[0], config_opts[i].name) == 0)
4698 {
4699 if ((getParserPolicy(sc) != getDefaultPolicy()) &&
4700 config_opts[i].default_policy_only)
4701 {
4702 /* Config option configurable on by the default policy*/
4703 /**Dont raise parse error, ignore any config that is not allowed in non-default
4704 * policy.
4705 */
4706 DEBUG_WRAP(DebugMessage(DEBUG_INIT, "Config option \"%s\" "
4707 "configurable only by default policy. Ignoring it\n", toks[0]));
4708 break;
4709 }
4710
4711 if (config_opts[i].only_once && config_opt_configured[i])
4712 {
4713 /* Configured already and set to only configure once
4714 * This array is reset for each policy read in so this is
4715 * on a per policy basis */
4716 ParseError("Config option \"%s\" can only be "
4717 "configured once.", toks[0]);
4718 }
4719
4720 if (config_opts[i].args_required && (opts == NULL))
4721 {
4722 /* Need arguments and there are none */
4723 ParseError("Config option \"%s\" requires arguments.", toks[0]);
4724 }
4725
4726 config_opts[i].parse_func(sc, opts);
4727 config_opt_configured[i] = 1;
4728 break;
4729 }
4730 }
4731
4732 if (config_opts[i].name == NULL)
4733 {
4734 /* Didn't find a matching config option */
4735 ParseError("Unknown config directive: %s.", toks[0]);
4736 }
4737
4738 mSplitFree(&toks, num_toks);
4739 }
4740
4741 /****************************************************************************
4742 *
4743 * Purpose: Check that special rules have an OTN.
4744 * TODO: Free up memory associated with disabled rules.
4745 *
4746 * Arguments: list => Pointer for a list of rules
4747 *
4748 * Returns: void function
4749 *
4750 * Notes: man - modified to used .shared flag in otn sigInfo instead of specialGID
4751 * sas - removed specialGID
4752 *
4753 *****************************************************************************/
4754 int CheckRuleStates(SnortConfig *sc)
4755 {
4756 RuleTreeNode *rtn;
4757 OptTreeNode *otn;
4758 SFGHASH_NODE *hashNode;
4759 int oneErr = 0;
4760 tSfPolicyId policyId = 0;
4761
4762 if (sc == NULL)
4763 return 0;
4764
4765 for (hashNode = sfghash_findfirst(sc->otn_map);
4766 hashNode;
4767 hashNode = sfghash_findnext(sc->otn_map))
4768 {
4769 otn = (OptTreeNode *)hashNode->data;
4770 for (policyId = 0;
4771 policyId < otn->proto_node_num;
4772 policyId++)
4773 {
4774 rtn = otn->proto_nodes[policyId];
4775
4776 if (!rtn)
4777 {
4778 continue;
4779 }
4780
4781 if ((rtn->proto == IPPROTO_TCP) || (rtn->proto == IPPROTO_UDP) ||
4782 (rtn->proto == IPPROTO_ICMP) || (rtn->proto == ETHERNET_TYPE_IP))
4783 {
4784 //do operation
4785 if ( otn->sigInfo.shared )
4786 {
4787 if (otn->ds_list[PLUGIN_DYNAMIC] == NULL)
4788 {
4789 // Have a dynamic rule but no dynamic plugin
4790 if (otn->sigInfo.id != otn->sigInfo.otnKey.sid)
4791 {
4792 // If its a different SID, but same soid metadata as something
4793 // else, try to find it
4794 OptTreeNode *otn_original;
4795 otn_original = SoRuleOtnLookup(sc->so_rule_otn_map,
4796 otn->sigInfo.otnKey.gid, otn->sigInfo.otnKey.sid);
4797
4798 if ( otn_original && (otn != otn_original) &&
4799 !otn->sigInfo.dup_opt_func )
4800 {
4801 OptFpList *opt_func = otn->opt_func;
4802 while (opt_func != NULL)
4803 {
4804 /* Delete the option functions that came from the
4805 * parsing -- this rule will be identical to its
4806 * "cloned" brother. */
4807 OptFpList *tmp = opt_func;
4808 opt_func = opt_func->next;
4809 free(tmp);
4810 }
4811 if (otn_original->sigInfo.shared)
4812 {
4813 /* Its still a shared object -- has its own detection function. */
4814 otn->ds_list[PLUGIN_DYNAMIC] = otn_original->ds_list[PLUGIN_DYNAMIC];
4815 }
4816 else
4817 {
4818 /* It was back-converted from a shared object */
4819 int i;
4820 for (i=PLUGIN_CLIENTSERVER; i<PLUGIN_MAX; i++)
4821 {
4822 otn->ds_list[i] = otn_original->ds_list[i];
4823 }
4824 otn->sigInfo.shared = 0; /* no longer shared */
4825 }
4826 otn->opt_func = otn_original->opt_func;
4827 otn->sigInfo.dup_opt_func = 1;
4828 }
4829 }
4830 }
4831
4832 if (otn->sigInfo.shared && (otn->ds_list[PLUGIN_DYNAMIC] == NULL))
4833 {
4834 /* If still shared... */
4835 ParseWarning("Encoded Rule Plugin SID: %d, GID: %d not "
4836 "registered properly. Disabling this rule.\n",
4837 otn->sigInfo.id, otn->sigInfo.generator);
4838 oneErr = 1;
4839 otn->rule_state = RULE_STATE_DISABLED;
4840 }
4841 }
4842 }
4843 }
4844 }
4845
4846 return oneErr;
4847 }
4848
4849 /****************************************************************************
4850 *
4851 * Purpose: Adjust the information for a given rule
4852 * relative to the Rule State list
4853 *
4854 * Arguments: None
4855 *
4856 * Returns: void function
4857 *
4858 * Notes: specialGID is depracated, uses sigInfo.shared flag
4859 *
4860 *****************************************************************************/
4861 void SetRuleStates(SnortConfig *sc)
4862 {
4863 RuleState *rule_state;
4864 #if 0
4865 int oneErr = 0, err;
4866 #endif
4867
4868 if (sc == NULL)
4869 return;
4870
4871 /* First, cycle through the rule state list and update the
4872 * rule state for each one we find. */
4873 for (rule_state = sc->rule_state_list; rule_state != NULL; rule_state = rule_state->next)
4874 {
4875 /* Lookup the OTN by ruleState->sid, ruleState->gid */
4876 OptTreeNode *otn = OtnLookup(sc->otn_map, rule_state->gid, rule_state->sid);
4877
4878 if (otn == NULL)
4879 {
4880 ParseError("Rule state specified for invalid SID: %d GID: %d\n",
4881 rule_state->sid, rule_state->gid);
4882 }
4883
4884 otn->rule_state = rule_state->state;
4885 }
4886
4887 /* Check TCP/UDP/ICMP/IP in one iteration for all rulelists and for all policies*/
4888 #if 1
4889 CheckRuleStates(sc);
4890 #else
4891 err = CheckRuleStates(sc);
4892 if (err)
4893 oneErr = 1;
4894
4895 if (oneErr)
4896 {
4897 FatalError("Misconfigured or unregistered encoded rule plugins\n");
4898 }
4899 #endif
4900 }
4901
4902 /****************************************************************************
4903 *
4904 * Purpose: Parses a rule state line.
4905 * Format is sid, gid, state, action.
4906 * state should be "enabled" or "disabled"
4907 * action should be "alert", "drop", "sdrop", "log", etc.
4908 *
4909 * Arguments: args => string containing a single rule state entry
4910 *
4911 * Returns: void function
4912 *
4913 *****************************************************************************/
4914 static void ParseRuleState(SnortConfig *sc, SnortPolicy *p, char *args)
4915 {
4916 char **toks;
4917 int num_toks;
4918 RuleState *state;
4919 char *endptr;
4920
4921 if (sc == NULL)
4922 return;
4923
4924 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"RuleState\n"););
4925
4926 toks = mSplit(args, ", ", 0, &num_toks, 0);
4927
4928 if (num_toks != 4)
4929 ParseError("Config rule_state: Empty state info.");
4930
4931 state = (RuleState *)SnortAlloc(sizeof(RuleState));
4932
4933 state->sid = SnortStrtoul(toks[0], &endptr, 0);
4934 if ((errno == ERANGE) || (*endptr != '\0'))
4935 {
4936 ParseError("Invalid sid for rule state: %s. Sid must be between 0 and "
4937 "%u inclusive.", args, UINT32_MAX);
4938 }
4939
4940 state->gid = SnortStrtoul(toks[1], &endptr, 0);
4941 if ((errno == ERANGE) || (*endptr != '\0'))
4942 {
4943 ParseError("Invalid gid for rule state: %s. Gid must be between 0 and "
4944 "%u inclusive.", args, UINT32_MAX);
4945 }
4946
4947 if (strcasecmp(toks[2], RULE_STATE_OPT__DISABLED) == 0)
4948 {
4949 state->state = RULE_STATE_DISABLED;
4950 }
4951 else if (strcasecmp(toks[2], RULE_STATE_OPT__ENABLED) == 0)
4952 {
4953 state->state = RULE_STATE_ENABLED;
4954 }
4955 else
4956 {
4957 ParseError("Rule_state: Invalid state - must be either "
4958 "'enabled' or 'disabled'.");
4959 }
4960
4961 state->action = GetRuleType(toks[3]);
4962 if (state->action == RULE_TYPE__NONE)
4963 {
4964 ParseError("Rule_state: Invalid action - must be a valid "
4965 "rule type.");
4966 }
4967
4968 mSplitFree(&toks, num_toks);
4969
4970 if (sc->rule_state_list == NULL)
4971 {
4972 sc->rule_state_list = state;
4973 }
4974 else
4975 {
4976 state->next = sc->rule_state_list;
4977 sc->rule_state_list = state;
4978 }
4979 }
4980
4981 static void ParseDynamicLibInfo(DynamicLibInfo *dylib_info, char *args)
4982 {
4983 char getcwd_path[PATH_MAX];
4984 char **toks = NULL;
4985 int num_toks = 0;
4986 char *path = NULL;
4987 PathType ptype = PATH_TYPE__FILE;
4988 DynamicLibPath *dylib_path;
4989 struct stat buf;
4990
4991 if (dylib_info == NULL)
4992 return;
4993
4994 if (dylib_info->count >= MAX_DYNAMIC_LIBS)
4995 {
4996 ParseError("Maximum number of loaded libriaries of this dynamic "
4997 "library type exceeded: %d.", MAX_DYNAMIC_LIBS);
4998 }
4999
5000 if (args == NULL)
5001 {
5002 if (getcwd(getcwd_path, sizeof(getcwd_path)) == NULL)
5003 {
5004 ParseError("Dynamic library path too long. If you really "
5005 "think your path needs to be as long as it is, please "
5006 "submit a bug to bugs@snort.org.");
5007 }
5008
5009 path = getcwd_path;
5010 ptype = PATH_TYPE__DIRECTORY;
5011 }
5012 else
5013 {
5014 toks = mSplit(args, " \t", 0, &num_toks, 0);
5015
5016 if (num_toks == 1)
5017 {
5018 path = toks[0];
5019 ptype = PATH_TYPE__FILE;
5020 }
5021 else if (num_toks == 2)
5022 {
5023 if (strcasecmp(toks[0], DYNAMIC_LIB_OPT__FILE) == 0)
5024 {
5025 ptype = PATH_TYPE__FILE;
5026 }
5027 else if (strcasecmp(toks[0], DYNAMIC_LIB_OPT__DIRECTORY) == 0)
5028 {
5029 ptype = PATH_TYPE__DIRECTORY;
5030 }
5031 else
5032 {
5033 ParseError("Invalid specifier for Dynamic library specifier. "
5034 "Should be file|directory pathname.");
5035 }
5036
5037 path = toks[1];
5038 }
5039 else
5040 {
5041 ParseError("Missing/incorrect dynamic engine lib specifier.");
5042 }
5043 }
5044
5045 dylib_path = (DynamicLibPath *)SnortAlloc(sizeof(DynamicLibPath));
5046 dylib_path->ptype = ptype;
5047 dylib_path->path = SnortStrdup(path);
5048
5049 dylib_info->lib_paths[dylib_info->count] = dylib_path;
5050 dylib_info->count++;
5051
5052 if (toks != NULL)
5053 mSplitFree(&toks, num_toks);
5054
5055 if (stat(dylib_path->path, &buf) == -1)
5056 {
5057 ParseError("Could not stat dynamic module path \"%s\": %s.\n",
5058 dylib_path->path, strerror(errno));
5059 }
5060
5061 dylib_path->last_mod_time = buf.st_mtime;
5062 }
5063
5064 /****************************************************************************
5065 *
5066 * Purpose: Parses a dynamic engine line
5067 * Format is full path of dynamic engine
5068 *
5069 * Arguments: args => string containing a single dynamic engine
5070 *
5071 * Returns: void function
5072 *
5073 *****************************************************************************/
5074 static void ParseDynamicEngineInfo(SnortConfig *sc, SnortPolicy *p, char *args)
5075 {
5076 if (sc == NULL)
5077 return;
5078
5079 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"DynamicEngine\n"););
5080
5081 if (sc->dyn_engines == NULL)
5082 {
5083 sc->dyn_engines = (DynamicLibInfo *)SnortAlloc(sizeof(DynamicLibInfo));
5084 sc->dyn_engines->type = DYNAMIC_TYPE__ENGINE;
5085 }
5086
5087 ParseDynamicLibInfo(sc->dyn_engines, args);
5088 }
5089
5090 /****************************************************************************
5091 *
5092 * Purpose: Parses a dynamic detection lib line
5093 * Format is full path of dynamic engine
5094 *
5095 * Arguments: args => string containing a single dynamic engine
5096 *
5097 * Returns: void function
5098 *
5099 *****************************************************************************/
5100 static void ParseDynamicDetectionInfo(SnortConfig *sc, SnortPolicy *p, char *args)
5101 {
5102 if (sc == NULL)
5103 return;
5104
5105 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"DynamicDetection\n"););
5106
5107 if (sc->dyn_rules == NULL)
5108 {
5109 sc->dyn_rules = (DynamicLibInfo *)SnortAlloc(sizeof(DynamicLibInfo));
5110 sc->dyn_rules->type = DYNAMIC_TYPE__DETECTION;
5111 }
5112
5113 ParseDynamicLibInfo(sc->dyn_rules, args);
5114 }
5115
5116 /****************************************************************************
5117 *
5118 * Purpose: Parses a dynamic preprocessor lib line
5119 * Format is full path of dynamic engine
5120 *
5121 * Arguments: args => string containing a single dynamic engine
5122 *
5123 * Returns: void function
5124 *
5125 *****************************************************************************/
5126 static void ParseDynamicPreprocessorInfo(SnortConfig *sc, SnortPolicy *p, char *args)
5127 {
5128 if (sc == NULL)
5129 return;
5130
5131 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"DynamicPreprocessor\n"););
5132
5133 if (sc->dyn_preprocs == NULL)
5134 {
5135 sc->dyn_preprocs = (DynamicLibInfo *)SnortAlloc(sizeof(DynamicLibInfo));
5136 sc->dyn_preprocs->type = DYNAMIC_TYPE__PREPROCESSOR;
5137 }
5138
5139 ParseDynamicLibInfo(sc->dyn_preprocs, args);
5140 }
5141
5142 # ifdef SIDE_CHANNEL
5143
5144 /****************************************************************************
5145 *
5146 * Purpose: Parses a dynamic side channel lib line
5147 * Format is full path of dynamic side channel
5148 *
5149 * Arguments: args => string containing a single dynamic side channel
5150 *
5151 * Returns: void function
5152 *
5153 *****************************************************************************/
5154 static void ParseDynamicSideChannelInfo(SnortConfig *sc, SnortPolicy *p, char *args)
5155 {
5156 if (sc == NULL)
5157 return;
5158
5159 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"DynamicSideChannel\n"););
5160
5161 if (sc->dyn_side_channels == NULL)
5162 {
5163 sc->dyn_side_channels = (DynamicLibInfo *)SnortAlloc(sizeof(DynamicLibInfo));
5164 sc->dyn_side_channels->type = DYNAMIC_TYPE__SIDE_CHANNEL;
5165 }
5166
5167 ParseDynamicLibInfo(sc->dyn_side_channels, args);
5168 }
5169
5170 # endif /* SIDE_CHANNEL */
5171
5172 /****************************************************************************
5173 *
5174 * Purpose: Parses a dynamic output lib line
5175 * Format is full path of dynamic output
5176 *
5177 * Arguments: args => string containing a single dynamic output
5178 *
5179 * Returns: void function
5180 *
5181 *****************************************************************************/
5182 static void ParseDynamicOutputInfo(SnortConfig *sc, SnortPolicy *p, char *args)
5183 {
5184 char **toks = NULL;
5185 int num_toks = 0;
5186 char *path = NULL;
5187 PathType ptype = PATH_TYPE__FILE;
5188 if (sc == NULL)
5189 return;
5190
5191 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"DynamicOutput\n"););
5192
5193 if (args == NULL)
5194 {
5195 char *getcwd_path = SnortAlloc(PATH_MAX);
5196
5197 if (getcwd(getcwd_path, PATH_MAX) == NULL)
5198 {
5199 ParseError("Dynamic library path too long. If you really "
5200 "think your path needs to be as long as it is, please "
5201 "submit a bug to bugs@snort.org.");
5202 }
5203
5204 path = getcwd_path;
5205 ptype = PATH_TYPE__DIRECTORY;
5206 }
5207 else
5208 {
5209 toks = mSplit(args, " \t", 0, &num_toks, 0);
5210
5211 if (num_toks == 1)
5212 {
5213 path = SnortStrdup(toks[0]);
5214 ptype = PATH_TYPE__FILE;
5215 }
5216 else if (num_toks == 2)
5217 {
5218 if (strcasecmp(toks[0], DYNAMIC_LIB_OPT__FILE) == 0)
5219 {
5220 ptype = PATH_TYPE__FILE;
5221 }
5222 else if (strcasecmp(toks[0], DYNAMIC_LIB_OPT__DIRECTORY) == 0)
5223 {
5224 ptype = PATH_TYPE__DIRECTORY;
5225 }
5226 else
5227 {
5228 ParseError("Invalid specifier for Dynamic library specifier. "
5229 "Should be file|directory pathname.");
5230 }
5231
5232 path = SnortStrdup(toks[1]);
5233 }
5234 else
5235 {
5236 ParseError("Missing/incorrect dynamic engine lib specifier.");
5237 }
5238 mSplitFree(&toks, num_toks);
5239 }
5240 if (ptype == PATH_TYPE__DIRECTORY)
5241 output_load(path);
5242 else if (ptype == PATH_TYPE__FILE)
5243 output_load_module(path);
5244 if (path)
5245 free(path);
5246
5247 }
5248
5249 /* verify that we are not reusing some other keyword */
5250 static int ValidateUserDefinedRuleType(SnortConfig *sc, char *keyword)
5251 {
5252 RuleListNode *node;
5253
5254 if ((sc == NULL) || (sc->rule_lists == NULL))
5255 return 0;
5256
5257 node = sc->rule_lists;
5258
5259 /* This keyword cannot match any of our predefined rule types */
5260 if (GetRuleType(keyword) != RULE_TYPE__NONE)
5261 return 0;
5262
5263 /* Walk through the rule list to make sure the user didn't already
5264 * define this one */
5265 while (node != NULL)
5266 {
5267 if (strcasecmp(node->name, keyword) == 0)
5268 return 0;
5269
5270 node = node->next;
5271 }
5272
5273 return 1;
5274 }
5275
5276 /* This function does nothing. It is just a place holder in the snort conf
5277 * keyword array so the keyword can be matched. Its's a special configuration
5278 * case in that multiple non-escaped lines need to be read and the current
5279 * file pointer needs to be passed in. */
5280 static void ParseRuleTypeDeclaration(SnortConfig *sc, SnortPolicy *p, char *arg)
5281 {
5282 return;
5283 }
5284
5285 static void _ParseRuleTypeDeclaration(SnortConfig *sc, FILE *fp, char *arg, int prules)
5286 {
5287 char **toks;
5288 int num_toks;
5289 char *input;
5290 char *rule_type_name;
5291 RuleType type;
5292 int rval = 1;
5293 ListHead *listhead = NULL;
5294 int got_output = 0;
5295
5296 if ((sc == NULL) || (fp == NULL) || (arg == NULL))
5297 return;
5298
5299 /* Already parsed this or ignoring for any non-default policy, but need to move past
5300 * the rule declaration because it doesn't have continuation characters
5301 */
5302 if (prules /* parsing rules */
5303 || (getParserPolicy(sc) != getDefaultPolicy()))
5304 {
5305 while (1)
5306 {
5307 input = ReadLine(fp);
5308 if (input == NULL)
5309 ParseError("Rule type declaration syntax error: %s.", arg);
5310
5311 toks = mSplit(input, " \t", 2, &num_toks, 0);
5312
5313 /* Just continue for blank line */
5314 if (toks == NULL)
5315 {
5316 free (input);
5317 continue;
5318 }
5319
5320 /* Got end of rule type */
5321 if ((num_toks == 1) && (strcmp(toks[0], "}") == 0))
5322 {
5323 free(input);
5324 mSplitFree(&toks, num_toks);
5325 break;
5326 }
5327
5328 free(input);
5329 mSplitFree(&toks, num_toks);
5330 }
5331
5332 return;
5333 }
5334
5335
5336 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"Rule type declaration\n"););
5337
5338 toks = mSplit(arg, " \t", 2, &num_toks, 0);
5339
5340 /* Need rule type name for creating new node in rule list */
5341 rule_type_name = SnortStrdup(ExpandVars(sc, toks[0]));
5342
5343 /* Verify keyword is unique */
5344 if (!ValidateUserDefinedRuleType(sc, rule_type_name))
5345 {
5346 ParseError("Duplicate rule type declaration found: %s.", rule_type_name);
5347 }
5348
5349 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"Declaring new rule type: %s\n",
5350 rule_type_name););
5351
5352 if (num_toks == 2)
5353 {
5354 /* User put '{' on the same line, which is okay */
5355 if ((toks[1] != NULL) && (strcmp(toks[1], "{") != 0))
5356 {
5357 ParseError("Rule type declaration syntax error: %s.", arg);
5358 }
5359 }
5360 else
5361 {
5362 /* Get next line. It should only be '{' */
5363 input = ReadLine(fp);
5364 if ((input == NULL) || (strcmp(input, "{") != 0))
5365 {
5366 ParseError("Rule type declaration syntax error: %s.", arg);
5367 }
5368
5369 free(input);
5370 }
5371
5372 mSplitFree(&toks, num_toks);
5373
5374 input = ReadLine(fp);
5375 if (input == NULL)
5376 ParseError("Rule type declaration syntax error: %s.", arg);
5377
5378 toks = mSplit(input, " \t", 2, &num_toks, 0);
5379 if ((num_toks != 2) ||
5380 (strcasecmp(toks[0], RULE_TYPE_OPT__TYPE) != 0))
5381 {
5382 ParseError("Rule type declaration syntax error: %s.", arg);
5383 }
5384
5385 type = GetRuleType(toks[1]);
5386 if (type == RULE_TYPE__NONE)
5387 ParseError("Invalid type for rule type declaration: %s.", toks[1]);
5388
5389 DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"\ttype(%i): %s\n", type, toks[1]););
5390
5391 if (type == RULE_TYPE__PASS)
5392 rval = 0;
5393
5394 listhead = CreateRuleType(sc, rule_type_name, type, rval, NULL);
5395
5396 free(rule_type_name);
5397 free(input);
5398 mSplitFree(&toks, num_toks);
5399
5400 /* Get output plugin declarations
5401 * This