1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
|
/* Copyright (C) 2022-2023 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file
*/
#include "suricata-common.h"
#include "suricata.h"
#include "packet.h"
#include "util-exception-policy.h"
#include "util-misc.h"
#include "stream-tcp-reassemble.h"
#include "action-globals.h"
enum ExceptionPolicy g_eps_master_switch = EXCEPTION_POLICY_NOT_SET;
/** true if exception policy was defined in config */
static bool g_eps_have_exception_policy = false;
static const char *ExceptionPolicyEnumToString(enum ExceptionPolicy policy)
{
switch (policy) {
case EXCEPTION_POLICY_NOT_SET:
return "ignore";
case EXCEPTION_POLICY_AUTO:
return "auto";
case EXCEPTION_POLICY_REJECT:
return "reject";
case EXCEPTION_POLICY_BYPASS_FLOW:
return "bypass";
case EXCEPTION_POLICY_DROP_FLOW:
return "drop-flow";
case EXCEPTION_POLICY_DROP_PACKET:
return "drop-packet";
case EXCEPTION_POLICY_PASS_PACKET:
return "pass-packet";
case EXCEPTION_POLICY_PASS_FLOW:
return "pass-flow";
}
// TODO we shouldn't reach this, but if we do, better not to leave this as simply null...
return "not set";
}
void SetMasterExceptionPolicy(void)
{
g_eps_master_switch = ExceptionPolicyParse("exception-policy", true);
}
static enum ExceptionPolicy GetMasterExceptionPolicy(const char *option)
{
return g_eps_master_switch;
}
void ExceptionPolicyApply(Packet *p, enum ExceptionPolicy policy, enum PacketDropReason drop_reason)
{
SCLogDebug("start: pcap_cnt %" PRIu64 ", policy %u", p->pcap_cnt, policy);
switch (policy) {
case EXCEPTION_POLICY_AUTO:
break;
case EXCEPTION_POLICY_NOT_SET:
break;
case EXCEPTION_POLICY_REJECT:
SCLogDebug("EXCEPTION_POLICY_REJECT");
PacketDrop(p, ACTION_REJECT, drop_reason);
if (!EngineModeIsIPS()) {
break;
}
/* fall through */
case EXCEPTION_POLICY_DROP_FLOW:
SCLogDebug("EXCEPTION_POLICY_DROP_FLOW");
if (p->flow) {
p->flow->flags |= FLOW_ACTION_DROP;
FlowSetNoPayloadInspectionFlag(p->flow);
FlowSetNoPacketInspectionFlag(p->flow);
StreamTcpDisableAppLayer(p->flow);
}
/* fall through */
case EXCEPTION_POLICY_DROP_PACKET:
SCLogDebug("EXCEPTION_POLICY_DROP_PACKET");
DecodeSetNoPayloadInspectionFlag(p);
DecodeSetNoPacketInspectionFlag(p);
PacketDrop(p, ACTION_DROP, drop_reason);
break;
case EXCEPTION_POLICY_BYPASS_FLOW:
PacketBypassCallback(p);
/* fall through */
case EXCEPTION_POLICY_PASS_FLOW:
SCLogDebug("EXCEPTION_POLICY_PASS_FLOW");
if (p->flow) {
p->flow->flags |= FLOW_ACTION_PASS;
FlowSetNoPacketInspectionFlag(p->flow); // TODO util func
}
/* fall through */
case EXCEPTION_POLICY_PASS_PACKET:
SCLogDebug("EXCEPTION_POLICY_PASS_PACKET");
DecodeSetNoPayloadInspectionFlag(p);
DecodeSetNoPacketInspectionFlag(p);
break;
}
SCLogDebug("end");
}
static enum ExceptionPolicy PickPacketAction(const char *option, enum ExceptionPolicy p)
{
switch (p) {
case EXCEPTION_POLICY_DROP_FLOW:
SCLogWarning(
"flow actions not supported for %s, defaulting to \"drop-packet\"", option);
return EXCEPTION_POLICY_DROP_PACKET;
case EXCEPTION_POLICY_PASS_FLOW:
SCLogWarning(
"flow actions not supported for %s, defaulting to \"pass-packet\"", option);
return EXCEPTION_POLICY_PASS_PACKET;
case EXCEPTION_POLICY_BYPASS_FLOW:
SCLogWarning("flow actions not supported for %s, defaulting to \"ignore\"", option);
return EXCEPTION_POLICY_NOT_SET;
/* add all cases, to make sure new cases not handle will raise
* errors */
case EXCEPTION_POLICY_DROP_PACKET:
break;
case EXCEPTION_POLICY_PASS_PACKET:
break;
case EXCEPTION_POLICY_REJECT:
break;
case EXCEPTION_POLICY_NOT_SET:
break;
case EXCEPTION_POLICY_AUTO:
break;
}
return p;
}
static enum ExceptionPolicy ExceptionPolicyConfigValueParse(
const char *option, const char *value_str)
{
enum ExceptionPolicy policy = EXCEPTION_POLICY_NOT_SET;
if (strcmp(value_str, "drop-flow") == 0) {
policy = EXCEPTION_POLICY_DROP_FLOW;
} else if (strcmp(value_str, "pass-flow") == 0) {
policy = EXCEPTION_POLICY_PASS_FLOW;
} else if (strcmp(value_str, "bypass") == 0) {
policy = EXCEPTION_POLICY_BYPASS_FLOW;
} else if (strcmp(value_str, "drop-packet") == 0) {
policy = EXCEPTION_POLICY_DROP_PACKET;
} else if (strcmp(value_str, "pass-packet") == 0) {
policy = EXCEPTION_POLICY_PASS_PACKET;
} else if (strcmp(value_str, "reject") == 0) {
policy = EXCEPTION_POLICY_REJECT;
} else if (strcmp(value_str, "ignore") == 0) { // TODO name?
policy = EXCEPTION_POLICY_NOT_SET;
} else if (strcmp(value_str, "auto") == 0) {
policy = EXCEPTION_POLICY_AUTO;
} else {
FatalErrorOnInit(
"\"%s\" is not a valid exception policy value. Valid options are drop-flow, "
"pass-flow, bypass, reject, drop-packet, pass-packet, ignore or auto.",
value_str);
}
return policy;
}
/* Select an exception policy in case the configuration value was set to 'auto' */
static enum ExceptionPolicy ExceptionPolicyPickAuto(bool midstream_enabled, bool support_flow)
{
enum ExceptionPolicy policy = EXCEPTION_POLICY_NOT_SET;
if (!midstream_enabled && EngineModeIsIPS()) {
if (support_flow) {
policy = EXCEPTION_POLICY_DROP_FLOW;
} else {
policy = EXCEPTION_POLICY_DROP_PACKET;
}
}
return policy;
}
static enum ExceptionPolicy ExceptionPolicyMasterParse(const char *value)
{
enum ExceptionPolicy policy = ExceptionPolicyConfigValueParse("exception-policy", value);
if (!EngineModeIsIPS() &&
(policy == EXCEPTION_POLICY_DROP_PACKET || policy == EXCEPTION_POLICY_DROP_FLOW)) {
policy = EXCEPTION_POLICY_NOT_SET;
}
g_eps_have_exception_policy = true;
SCLogInfo("master exception-policy set to: %s", ExceptionPolicyEnumToString(policy));
return policy;
}
static enum ExceptionPolicy ExceptionPolicyGetDefault(
const char *option, bool support_flow, bool midstream)
{
enum ExceptionPolicy p = EXCEPTION_POLICY_NOT_SET;
if (g_eps_have_exception_policy) {
p = GetMasterExceptionPolicy(option);
if (p == EXCEPTION_POLICY_AUTO) {
p = ExceptionPolicyPickAuto(midstream, support_flow);
}
if (!support_flow) {
p = PickPacketAction(option, p);
}
SCLogConfig("%s: %s (defined via 'exception-policy' master switch)", option,
ExceptionPolicyEnumToString(p));
return p;
} else if (EngineModeIsIPS() && !midstream) {
p = EXCEPTION_POLICY_DROP_FLOW;
}
SCLogConfig("%s: %s (defined via 'built-in default' for %s-mode)", option,
ExceptionPolicyEnumToString(p), EngineModeIsIPS() ? "IPS" : "IDS");
return p;
}
enum ExceptionPolicy ExceptionPolicyParse(const char *option, bool support_flow)
{
enum ExceptionPolicy policy = EXCEPTION_POLICY_NOT_SET;
const char *value_str = NULL;
if ((ConfGet(option, &value_str) == 1) && value_str != NULL) {
if (strcmp(option, "exception-policy") == 0) {
policy = ExceptionPolicyMasterParse(value_str);
} else {
policy = ExceptionPolicyConfigValueParse(option, value_str);
if (policy == EXCEPTION_POLICY_AUTO) {
policy = ExceptionPolicyPickAuto(false, support_flow);
}
if (!support_flow) {
policy = PickPacketAction(option, policy);
}
SCLogConfig("%s: %s", option, ExceptionPolicyEnumToString(policy));
}
} else {
policy = ExceptionPolicyGetDefault(option, support_flow, false);
}
return policy;
}
enum ExceptionPolicy ExceptionPolicyMidstreamParse(bool midstream_enabled)
{
enum ExceptionPolicy policy = EXCEPTION_POLICY_NOT_SET;
const char *value_str = NULL;
/* policy was set directly */
if ((ConfGet("stream.midstream-policy", &value_str)) == 1 && value_str != NULL) {
policy = ExceptionPolicyConfigValueParse("midstream-policy", value_str);
if (policy == EXCEPTION_POLICY_AUTO) {
policy = ExceptionPolicyPickAuto(midstream_enabled, true);
} else if (midstream_enabled) {
if (policy != EXCEPTION_POLICY_NOT_SET && policy != EXCEPTION_POLICY_PASS_FLOW) {
FatalErrorOnInit(
"Error parsing stream.midstream-policy from config file. \"%s\" is "
"not a valid exception policy when midstream is enabled. Valid options "
"are pass-flow and ignore.",
value_str);
}
}
if (!EngineModeIsIPS()) {
if (policy == EXCEPTION_POLICY_DROP_FLOW) {
FatalErrorOnInit(
"Error parsing stream.midstream-policy from config file. \"%s\" is "
"not a valid exception policy in IDS mode. See our documentation for a "
"list of all possible values.",
value_str);
}
}
} else {
policy = ExceptionPolicyGetDefault("stream.midstream-policy", true, midstream_enabled);
}
if (policy == EXCEPTION_POLICY_PASS_PACKET || policy == EXCEPTION_POLICY_DROP_PACKET) {
FatalErrorOnInit("Error parsing stream.midstream-policy from config file. \"%s\" is "
"not valid for this exception policy. See our documentation for a list of "
"all possible values.",
value_str);
}
return policy;
}
#ifndef DEBUG
int ExceptionSimulationCommandLineParser(const char *name, const char *arg)
{
return 0;
}
#else
/* exception policy simulation (eps) handling */
uint64_t g_eps_applayer_error_offset_ts = UINT64_MAX;
uint64_t g_eps_applayer_error_offset_tc = UINT64_MAX;
uint64_t g_eps_pcap_packet_loss = UINT64_MAX;
uint64_t g_eps_stream_ssn_memcap = UINT64_MAX;
uint64_t g_eps_stream_reassembly_memcap = UINT64_MAX;
uint64_t g_eps_flow_memcap = UINT64_MAX;
uint64_t g_eps_defrag_memcap = UINT64_MAX;
bool g_eps_is_alert_queue_fail_mode = false;
/* 1: parsed, 0: not for us, -1: error */
int ExceptionSimulationCommandLineParser(const char *name, const char *arg)
{
if (strcmp(name, "simulate-applayer-error-at-offset-ts") == 0) {
BUG_ON(arg == NULL);
uint64_t offset = 0;
if (ParseSizeStringU64(arg, &offset) < 0) {
return -1;
}
g_eps_applayer_error_offset_ts = offset;
} else if (strcmp(name, "simulate-applayer-error-at-offset-tc") == 0) {
BUG_ON(arg == NULL);
uint64_t offset = 0;
if (ParseSizeStringU64(arg, &offset) < 0) {
return TM_ECODE_FAILED;
}
g_eps_applayer_error_offset_tc = offset;
} else if (strcmp(name, "simulate-packet-loss") == 0) {
BUG_ON(arg == NULL);
uint64_t pkt_num = 0;
if (ParseSizeStringU64(arg, &pkt_num) < 0) {
return TM_ECODE_FAILED;
}
g_eps_pcap_packet_loss = pkt_num;
} else if (strcmp(name, "simulate-packet-tcp-reassembly-memcap") == 0) {
BUG_ON(arg == NULL);
uint64_t pkt_num = 0;
if (ParseSizeStringU64(arg, &pkt_num) < 0) {
return TM_ECODE_FAILED;
}
g_eps_stream_reassembly_memcap = pkt_num;
} else if (strcmp(name, "simulate-packet-tcp-ssn-memcap") == 0) {
BUG_ON(arg == NULL);
uint64_t pkt_num = 0;
if (ParseSizeStringU64(arg, &pkt_num) < 0) {
return TM_ECODE_FAILED;
}
g_eps_stream_ssn_memcap = pkt_num;
} else if (strcmp(name, "simulate-packet-flow-memcap") == 0) {
BUG_ON(arg == NULL);
uint64_t pkt_num = 0;
if (ParseSizeStringU64(arg, &pkt_num) < 0) {
return TM_ECODE_FAILED;
}
g_eps_flow_memcap = pkt_num;
} else if (strcmp(name, "simulate-packet-defrag-memcap") == 0) {
BUG_ON(arg == NULL);
uint64_t pkt_num = 0;
if (ParseSizeStringU64(arg, &pkt_num) < 0) {
return TM_ECODE_FAILED;
}
g_eps_defrag_memcap = pkt_num;
} else if (strcmp(name, "simulate-alert-queue-realloc-failure") == 0) {
g_eps_is_alert_queue_fail_mode = true;
} else {
// not for us
return 0;
}
return 1;
}
#endif
|