Skip to content

Commit

Permalink
exceptions: add master switch config option
Browse files Browse the repository at this point in the history
This allows all traffic Exception Policies to be set from one
configuration point. All exception policy options are available in IPS
mode. Bypass, pass and auto (disabled) are also available in iDS mode

Exception Policies set up individually will overwrite this setup for the
given traffic exception.

Task OISF#5219
  • Loading branch information
jufajardini authored and victorjulien committed Jan 24, 2023
1 parent fab3f36 commit 0d92890
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 49 deletions.
39 changes: 39 additions & 0 deletions doc/userguide/configuration/exception-policies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,45 @@ simulate failures or errors and understand Suricata behavior under such conditio
Exception Policies
------------------

.. _master-switch:

Master Switch
~~~~~~~~~~~~~

It is possible to set all configuration policies via what we call "master
switch". This offers a quick way to define what the engine should do in case of
traffic exceptions, while still allowing for the flexibility of indicating a
different behavior for specific exception policies your setup/environment may
have the need to.

::

# In IPS mode, the default is drop-packet/drop-flow. To fallback to old
# behavior (setting each of them individually, or ignoring all), set this
# to ignore.
# All values available for exception policies can be used, and there is one
# extra option: auto - which means drop-packet/drop-flow in IPS mode and
# ignore in IDS mode).
# Exception policy values are: drop-packet, drop-flow, reject, bypass,
# pass-packet, pass-flow, ignore (disable).
exception-policy: auto

This value will be overwritten by specific exception policies whose settings are
also defined in the yaml file.

Auto
''''

**In IPS mode**, the default behavior for all exception policies is to drop
packets and/or flows. It is possible to disable this default, by setting the
exception policies "master switch" yaml config option to ``ignore``.

**In IDS mode**, setting auto mode actually means disabling the
``master-swtich``, or ignoring the exception policies.

Specific settings
~~~~~~~~~~~~~~~~~

Exception policies are implemented for:

.. list-table:: Exception Policy configuration variables
Expand Down
5 changes: 5 additions & 0 deletions doc/userguide/upgrade.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ Upgrading 6.0 to 7.0
Major changes
~~~~~~~~~~~~~
- Upgrade of PCRE1 to PCRE2. See :ref:`pcre-update-v1-to-v2` for more details.
- Introducing the :ref:`Exception Policy's Master Switch <master-switch>`. This
allows to setup a single policy for all traffic exceptions. This is a breaking
change for the default behavior in the Exception Policies: in IPS mode, if an
exception policy is not set, it will fall back to the the master switch now,
instead of being ignored. Prevent this by disabling the master switch.

Security changes
~~~~~~~~~~~~~~~~
Expand Down
2 changes: 1 addition & 1 deletion src/app-layer-parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ struct AppLayerParserState_ {
FramesContainer *frames;
};

enum ExceptionPolicy g_applayerparser_error_policy = EXCEPTION_POLICY_IGNORE;
enum ExceptionPolicy g_applayerparser_error_policy = EXCEPTION_POLICY_NOT_SET;

static void AppLayerConfg(void)
{
Expand Down
4 changes: 2 additions & 2 deletions src/stream-tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -945,7 +945,7 @@ static int StreamTcpPacketStateNone(ThreadVars *tv, Packet *p,
SCLogDebug("Midstream not enabled, so won't pick up a session");
return 0;
}
if (!(stream_config.midstream_policy == EXCEPTION_POLICY_IGNORE ||
if (!(stream_config.midstream_policy == EXCEPTION_POLICY_NOT_SET ||
stream_config.midstream_policy == EXCEPTION_POLICY_PASS_FLOW ||
stream_config.midstream_policy == EXCEPTION_POLICY_PASS_PACKET)) {
SCLogDebug("Midstream policy not permissive, so won't pick up a session");
Expand Down Expand Up @@ -1118,7 +1118,7 @@ static int StreamTcpPacketStateNone(ThreadVars *tv, Packet *p,
SCLogDebug("Midstream not enabled, so won't pick up a session");
return 0;
}
if (!(stream_config.midstream_policy == EXCEPTION_POLICY_IGNORE ||
if (!(stream_config.midstream_policy == EXCEPTION_POLICY_NOT_SET ||
stream_config.midstream_policy == EXCEPTION_POLICY_PASS_FLOW ||
stream_config.midstream_policy == EXCEPTION_POLICY_PASS_PACKET)) {
SCLogDebug("Midstream policy not permissive, so won't pick up a session");
Expand Down
3 changes: 3 additions & 0 deletions src/suricata.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@
#include "util-device.h"
#include "util-dpdk.h"
#include "util-ebpf.h"
#include "util-exception-policy.h"
#include "util-host-os-info.h"
#include "util-ioctl.h"
#include "util-landlock.h"
Expand Down Expand Up @@ -2640,6 +2641,8 @@ int PostConfLoadedSetup(SCInstance *suri)

MacSetRegisterFlowStorage();

SetMasterExceptionPolicy();

LiveDeviceFinalize(); // must be after EBPF extension registration

RunModeEngineIsIPS(
Expand Down
152 changes: 107 additions & 45 deletions src/util-exception-policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,59 +27,101 @@
#include "stream-tcp-reassemble.h"
#include "action-globals.h"

enum ExceptionPolicy g_eps_master_switch = EXCEPTION_POLICY_NOT_SET;

static const char *ExceptionPolicyEnumToString(enum ExceptionPolicy policy)
{
switch (policy) {
case EXCEPTION_POLICY_NOT_SET:
return "ignore";
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);
if (EngineModeIsIPS()) {
switch (policy) {
case EXCEPTION_POLICY_IGNORE:
break;
case EXCEPTION_POLICY_REJECT:
SCLogDebug("EXCEPTION_POLICY_REJECT");
PacketDrop(p, ACTION_REJECT, drop_reason);
/* 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;
}
switch (policy) {
case EXCEPTION_POLICY_NOT_SET:
break;
case EXCEPTION_POLICY_REJECT:
SCLogDebug("EXCEPTION_POLICY_REJECT");
PacketDrop(p, ACTION_REJECT, drop_reason);
/* 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 SetIPSOption(
const char *option, const char *value_str, enum ExceptionPolicy p)
{
if (!EngineModeIsIPS()) {
SCLogConfig("%s: %s not a valid config in IDS mode. Ignoring it.)", option, value_str);
return EXCEPTION_POLICY_NOT_SET;
}
return p;
}

enum ExceptionPolicy ExceptionPolicyParse(const char *option, const bool support_flow)
{
enum ExceptionPolicy policy = EXCEPTION_POLICY_IGNORE;
enum ExceptionPolicy policy = EXCEPTION_POLICY_NOT_SET;
const char *value_str = NULL;
if ((ConfGet(option, &value_str)) == 1 && value_str != NULL) {
if (strcmp(value_str, "drop-flow") == 0) {
policy = EXCEPTION_POLICY_DROP_FLOW;
policy = SetIPSOption(option, value_str, EXCEPTION_POLICY_DROP_FLOW);
SCLogConfig("%s: %s", option, value_str);
} else if (strcmp(value_str, "pass-flow") == 0) {
policy = EXCEPTION_POLICY_PASS_FLOW;
Expand All @@ -88,7 +130,7 @@ enum ExceptionPolicy ExceptionPolicyParse(const char *option, const bool support
policy = EXCEPTION_POLICY_BYPASS_FLOW;
SCLogConfig("%s: %s", option, value_str);
} else if (strcmp(value_str, "drop-packet") == 0) {
policy = EXCEPTION_POLICY_DROP_PACKET;
policy = SetIPSOption(option, value_str, EXCEPTION_POLICY_DROP_PACKET);
SCLogConfig("%s: %s", option, value_str);
} else if (strcmp(value_str, "pass-packet") == 0) {
policy = EXCEPTION_POLICY_PASS_PACKET;
Expand All @@ -97,7 +139,10 @@ enum ExceptionPolicy ExceptionPolicyParse(const char *option, const bool support
policy = EXCEPTION_POLICY_REJECT;
SCLogConfig("%s: %s", option, value_str);
} else if (strcmp(value_str, "ignore") == 0) { // TODO name?
policy = EXCEPTION_POLICY_IGNORE;
policy = EXCEPTION_POLICY_NOT_SET;
SCLogConfig("%s: %s", option, value_str);
} else if (strcmp(value_str, "auto") == 0) {
policy = SetIPSOption(option, value_str, EXCEPTION_POLICY_DROP_FLOW);
SCLogConfig("%s: %s", option, value_str);
} else {
FatalErrorOnInit(
Expand All @@ -110,12 +155,29 @@ enum ExceptionPolicy ExceptionPolicyParse(const char *option, const bool support
if (policy == EXCEPTION_POLICY_DROP_FLOW || policy == EXCEPTION_POLICY_PASS_FLOW ||
policy == EXCEPTION_POLICY_BYPASS_FLOW) {
SCLogWarning("flow actions not supported for %s, defaulting to \"ignore\"", option);
policy = EXCEPTION_POLICY_IGNORE;
policy = EXCEPTION_POLICY_NOT_SET;
}
}

} else if (strcmp(option, "exception-policy") == 0) {
/* not enabled, we won't change the master exception policy,
for now */
SCLogWarning("'exception-policy' master switch not set, so ignoring it."
" This behavior will change in Suricata 8, so please update your"
" config. See ticket #5219 for more details.");
g_eps_master_switch = EXCEPTION_POLICY_NOT_SET;
} else {
SCLogConfig("%s: ignore", option);
/* Exception Policy was not defined individually */
enum ExceptionPolicy master_policy = GetMasterExceptionPolicy(option);
if (master_policy == EXCEPTION_POLICY_NOT_SET) {
SCLogConfig("%s: ignore", option);
} else {
/* If the master switch was set and the Exception Policy option was not
individually set, use the defined master Exception Policy */
const char *value = ExceptionPolicyEnumToString(master_policy);
SCLogConfig("%s: %s (defined via 'exception-policy' master switch", option, value);
policy = master_policy;
}
}
return policy;
}
Expand Down
4 changes: 3 additions & 1 deletion src/util-exception-policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
#include "decode.h"

enum ExceptionPolicy {
EXCEPTION_POLICY_IGNORE = 0,
EXCEPTION_POLICY_NOT_SET = 0,
EXCEPTION_POLICY_PASS_PACKET,
EXCEPTION_POLICY_PASS_FLOW,
EXCEPTION_POLICY_BYPASS_FLOW,
Expand All @@ -34,10 +34,12 @@ enum ExceptionPolicy {
EXCEPTION_POLICY_REJECT,
};

void SetMasterExceptionPolicy(void);
void ExceptionPolicyApply(
Packet *p, enum ExceptionPolicy policy, enum PacketDropReason drop_reason);
enum ExceptionPolicy ExceptionPolicyParse(const char *option, const bool support_flow);

extern enum ExceptionPolicy g_eps_master_switch;
#ifdef DEBUG
extern uint64_t g_eps_applayer_error_offset_ts;
extern uint64_t g_eps_applayer_error_offset_tc;
Expand Down
13 changes: 13 additions & 0 deletions suricata.yaml.in
Original file line number Diff line number Diff line change
Expand Up @@ -1278,6 +1278,19 @@ legacy:
# packet. Default is 15
#packet-alert-max: 15

# Exception Policies
#
# Define a common behavior for all exception policies.
# In IPS mode, the default is drop-packet/drop-flow. To fallback to old
# behavior (setting each of them individually, or ignoring all), set this
# to ignore.
# All values available for exception policies can be used, and there is one
# extra option: auto - which means drop-packet/drop-flow in IPS mode and
# ignore in IDS mode).
# Exception policy values are: drop-packet, drop-flow, reject, bypass,
# pass-packet, pass-flow, ignore (disable).
exception-policy: auto

# IP Reputation
#reputation-categories-file: @e_sysconfdir@iprep/categories.txt
#default-reputation-path: @e_sysconfdir@iprep
Expand Down

0 comments on commit 0d92890

Please sign in to comment.