Merge branch 'labeled-ipsec'

This adds support for labeled IPsec with SELinux (and a proprietary mode
that can be used to match child configs).  For SELinux support, compile
with --enable-selinux.

Other changes include a combined start action (trap|start), avoiding
initiating duplicate CHILD_SAs, updating reqids if dynamic traffic
selectors change, removing reqid errors on policy updates, or querying
specific CHILD_SAs with vici's list-sas command.

Closes #3075
This commit is contained in:
Tobias Brunner
2022-04-14 18:52:38 +02:00
89 changed files with 3317 additions and 827 deletions
+11
View File
@@ -321,6 +321,7 @@ ARG_ENABL_SET([python-eggs], [enable build of provided python eggs.])
ARG_ENABL_SET([python-eggs-install],[enable installation of provided python eggs.]) ARG_ENABL_SET([python-eggs-install],[enable installation of provided python eggs.])
ARG_ENABL_SET([perl-cpan], [enable build of provided perl CPAN module.]) ARG_ENABL_SET([perl-cpan], [enable build of provided perl CPAN module.])
ARG_ENABL_SET([perl-cpan-install],[enable installation of provided CPAN module.]) ARG_ENABL_SET([perl-cpan-install],[enable installation of provided CPAN module.])
ARG_ENABL_SET([selinux], [enable SELinux support for labeled IPsec.])
ARG_ENABL_SET([tss-trousers], [enable the use of the TrouSerS Trusted Software Stack]) ARG_ENABL_SET([tss-trousers], [enable the use of the TrouSerS Trusted Software Stack])
ARG_ENABL_SET([tss-tss2], [enable the use of the TSS 2.0 Trusted Software Stack]) ARG_ENABL_SET([tss-tss2], [enable the use of the TSS 2.0 Trusted Software Stack])
@@ -1248,6 +1249,13 @@ if test x$capabilities = xlibcap; then
AC_DEFINE([CAPABILITIES_LIBCAP], [], [have libpcap library]) AC_DEFINE([CAPABILITIES_LIBCAP], [], [have libpcap library])
fi fi
if test x$selinux = xtrue; then
PKG_CHECK_MODULES(selinux, [libselinux])
AC_SUBST(selinux_CFLAGS)
AC_SUBST(selinux_LIBS)
AC_DEFINE([USE_SELINUX], [], [build with support for SELinux])
fi
if test x$integrity_test = xtrue; then if test x$integrity_test = xtrue; then
AC_MSG_CHECKING([for dladdr()]) AC_MSG_CHECKING([for dladdr()])
AC_COMPILE_IFELSE( AC_COMPILE_IFELSE(
@@ -1516,6 +1524,7 @@ ADD_PLUGIN([kernel-iph], [c charon])
ADD_PLUGIN([kernel-pfkey], [c charon starter nm cmd]) ADD_PLUGIN([kernel-pfkey], [c charon starter nm cmd])
ADD_PLUGIN([kernel-pfroute], [c charon starter nm cmd]) ADD_PLUGIN([kernel-pfroute], [c charon starter nm cmd])
ADD_PLUGIN([kernel-netlink], [c charon starter nm cmd]) ADD_PLUGIN([kernel-netlink], [c charon starter nm cmd])
ADD_PLUGIN([selinux], [c charon starter nm cmd])
ADD_PLUGIN([resolve], [c charon cmd]) ADD_PLUGIN([resolve], [c charon cmd])
ADD_PLUGIN([save-keys], [c]) ADD_PLUGIN([save-keys], [c])
ADD_PLUGIN([socket-default], [c charon nm cmd]) ADD_PLUGIN([socket-default], [c charon nm cmd])
@@ -1762,6 +1771,7 @@ AM_CONDITIONAL(USE_RESOLVE, test x$resolve = xtrue)
AM_CONDITIONAL(USE_ATTR, test x$attr = xtrue) AM_CONDITIONAL(USE_ATTR, test x$attr = xtrue)
AM_CONDITIONAL(USE_ATTR_SQL, test x$attr_sql = xtrue) AM_CONDITIONAL(USE_ATTR_SQL, test x$attr_sql = xtrue)
AM_CONDITIONAL(USE_COUNTERS, test x$counters = xtrue) AM_CONDITIONAL(USE_COUNTERS, test x$counters = xtrue)
AM_CONDITIONAL(USE_SELINUX, test x$selinux = xtrue)
# other options # other options
# --------------- # ---------------
@@ -2065,6 +2075,7 @@ AC_CONFIG_FILES([
src/libcharon/plugins/resolve/Makefile src/libcharon/plugins/resolve/Makefile
src/libcharon/plugins/attr/Makefile src/libcharon/plugins/attr/Makefile
src/libcharon/plugins/attr_sql/Makefile src/libcharon/plugins/attr_sql/Makefile
src/libcharon/plugins/selinux/Makefile
src/libcharon/tests/Makefile src/libcharon/tests/Makefile
src/libtpmtss/Makefile src/libtpmtss/Makefile
src/libtpmtss/plugins/tpm/Makefile src/libtpmtss/plugins/tpm/Makefile
+2 -1
View File
@@ -171,7 +171,8 @@ all|coverage|sonarcloud)
DEPS="$DEPS libcurl4-gnutls-dev libsoup2.4-dev libunbound-dev libldns-dev DEPS="$DEPS libcurl4-gnutls-dev libsoup2.4-dev libunbound-dev libldns-dev
libmysqlclient-dev libsqlite3-dev clearsilver-dev libfcgi-dev libmysqlclient-dev libsqlite3-dev clearsilver-dev libfcgi-dev
libldap2-dev libpcsclite-dev libpam0g-dev binutils-dev libnm-dev libldap2-dev libpcsclite-dev libpam0g-dev binutils-dev libnm-dev
libgcrypt20-dev libjson-c-dev python3-pip libtspi-dev libsystemd-dev" libgcrypt20-dev libjson-c-dev python3-pip libtspi-dev libsystemd-dev
libselinux1-dev"
if [ "$ID" = "ubuntu" -a "$VERSION_ID" = "20.04" ]; then if [ "$ID" = "ubuntu" -a "$VERSION_ID" = "20.04" ]; then
DEPS="$DEPS libiptc-dev" DEPS="$DEPS libiptc-dev"
else else
+2
View File
@@ -388,6 +388,8 @@ static child_cfg_t* create_child_cfg(private_cmd_connection_t *this,
case TS_IPV6_ADDR_RANGE: case TS_IPV6_ADDR_RANGE:
has_v6 = TRUE; has_v6 = TRUE;
break; break;
default:
continue;
} }
child_cfg->add_traffic_selector(child_cfg, FALSE, ts); child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
} }
+3 -3
View File
@@ -635,8 +635,8 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
}, },
}, },
.mode = MODE_TUNNEL, .mode = MODE_TUNNEL,
.dpd_action = ACTION_RESTART, .dpd_action = ACTION_START,
.close_action = ACTION_RESTART, .close_action = ACTION_START,
}; };
/** /**
@@ -903,7 +903,7 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
* Initiate * Initiate
*/ */
child_cfg->get_ref(child_cfg); child_cfg->get_ref(child_cfg);
if (ike_sa->initiate(ike_sa, child_cfg, 0, NULL, NULL) != SUCCESS) if (ike_sa->initiate(ike_sa, child_cfg, NULL) != SUCCESS)
{ {
charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa);
+3 -1
View File
@@ -24,8 +24,10 @@
void charon_esa_acquire(result_type *res, const sp_id_type sp_id) void charon_esa_acquire(result_type *res, const sp_id_type sp_id)
{ {
kernel_acquire_data_t data = {};
DBG1(DBG_KNL, "ees: acquire received for reqid %u", sp_id); DBG1(DBG_KNL, "ees: acquire received for reqid %u", sp_id);
charon->kernel->acquire(charon->kernel, sp_id, NULL, NULL); charon->kernel->acquire(charon->kernel, sp_id, &data);
*res = TKM_OK; *res = TKM_OK;
} }
+2 -2
View File
@@ -305,12 +305,12 @@ static void process_auth_response(private_pretend_auth_t *this,
if (this->tsi) if (this->tsi)
{ {
message->add_payload(message, (payload_t*) message->add_payload(message, (payload_t*)
ts_payload_create_from_traffic_selectors(TRUE, this->tsi)); ts_payload_create_from_traffic_selectors(TRUE, this->tsi, NULL));
} }
if (this->tsr) if (this->tsr)
{ {
message->add_payload(message, (payload_t*) message->add_payload(message, (payload_t*)
ts_payload_create_from_traffic_selectors(FALSE, this->tsr)); ts_payload_create_from_traffic_selectors(FALSE, this->tsr, NULL));
} }
} }
@@ -801,8 +801,8 @@ static job_requeue_t initiate(private_android_service_t *this)
}, },
}, },
.mode = MODE_TUNNEL, .mode = MODE_TUNNEL,
.dpd_action = ACTION_RESTART, .dpd_action = ACTION_START,
.close_action = ACTION_RESTART, .close_action = ACTION_START,
}; };
char *type, *remote_id; char *type, *remote_id;
@@ -924,7 +924,7 @@ static job_requeue_t initiate(private_android_service_t *this)
/* get an additional reference because initiate consumes one */ /* get an additional reference because initiate consumes one */
child_cfg->get_ref(child_cfg); child_cfg->get_ref(child_cfg);
if (ike_sa->initiate(ike_sa, child_cfg, 0, NULL, NULL) != SUCCESS) if (ike_sa->initiate(ike_sa, child_cfg, NULL) != SUCCESS)
{ {
DBG1(DBG_CFG, "failed to initiate tunnel"); DBG1(DBG_CFG, "failed to initiate tunnel");
charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
+7
View File
@@ -726,6 +726,13 @@ if MONOLITHIC
endif endif
endif endif
if USE_SELINUX
SUBDIRS += plugins/selinux
if MONOLITHIC
libcharon_la_LIBADD += plugins/selinux/libstrongswan-selinux.la
endif
endif
if USE_ATTR_SQL if USE_ATTR_SQL
SUBDIRS += plugins/attr_sql SUBDIRS += plugins/attr_sql
if MONOLITHIC if MONOLITHIC
+115 -5
View File
@@ -22,10 +22,10 @@
#include <daemon.h> #include <daemon.h>
ENUM(action_names, ACTION_NONE, ACTION_RESTART, ENUM_FLAGS(action_names, ACTION_TRAP, ACTION_START,
"clear", "none",
"hold", "trap",
"restart", "start",
); );
/** Default replay window size, if not set using charon.replay_window */ /** Default replay window size, if not set using charon.replay_window */
@@ -143,6 +143,16 @@ struct private_child_cfg_t {
*/ */
mark_t set_mark_out; mark_t set_mark_out;
/**
* Optional security label for policies
*/
sec_label_t *label;
/**
* Optional label mode for policies
*/
sec_label_mode_t label_mode;
/** /**
* Traffic Flow Confidentiality padding, if enabled * Traffic Flow Confidentiality padding, if enabled
*/ */
@@ -522,6 +532,97 @@ METHOD(child_cfg_t, get_set_mark, mark_t,
return inbound ? this->set_mark_in : this->set_mark_out; return inbound ? this->set_mark_in : this->set_mark_out;
} }
METHOD(child_cfg_t, get_label, sec_label_t*,
private_child_cfg_t *this)
{
return this->label;
}
METHOD(child_cfg_t, get_label_mode, sec_label_mode_t,
private_child_cfg_t *this)
{
return this->label_mode;
}
METHOD(child_cfg_t, select_label, bool,
private_child_cfg_t *this, linked_list_t *labels, bool log,
sec_label_t **label, bool *exact_out)
{
enumerator_t *enumerator;
sec_label_t *current, *match = NULL;
bool exact = FALSE;
if (labels && labels->get_count(labels))
{
if (!this->label)
{
DBG2(DBG_CFG, "peer proposed a security label, but none expected");
return FALSE;
}
if (log)
{
DBG2(DBG_CFG, "selecting security label matching '%s':",
this->label->get_string(this->label));
}
enumerator = labels->create_enumerator(labels);
while (enumerator->enumerate(enumerator, &current))
{
if (this->label->equals(this->label, current))
{
if (log)
{
DBG2(DBG_CFG, " %s => matches exactly",
current->get_string(current));
}
match = current;
exact = TRUE;
break;
}
else if (this->label_mode == SEC_LABEL_MODE_SELINUX &&
this->label->matches(this->label, current))
{
if (log)
{
DBG2(DBG_CFG, " %s => matches%s",
current->get_string(current), match ? ", ignored" : "");
}
/* return the first match if we don't find an exact one */
if (!match)
{
match = current;
}
}
else if (log)
{
DBG2(DBG_CFG, " %s => no match", current->get_string(current));
}
}
enumerator->destroy(enumerator);
if (!match)
{
DBG2(DBG_CFG, "none of the proposed security labels match the "
"configured label '%s'", this->label->get_string(this->label));
return FALSE;
}
}
else if (this->label)
{
DBG2(DBG_CFG, "peer didn't propose any security labels, we expect one "
"matching '%s'", this->label->get_string(this->label));
return FALSE;
}
if (label)
{
*label = match;
}
if (exact_out)
{
*exact_out = exact;
}
return TRUE;
}
METHOD(child_cfg_t, get_tfc, uint32_t, METHOD(child_cfg_t, get_tfc, uint32_t,
private_child_cfg_t *this) private_child_cfg_t *this)
{ {
@@ -607,7 +708,9 @@ METHOD(child_cfg_t, equals, bool,
this->hw_offload == other->hw_offload && this->hw_offload == other->hw_offload &&
this->copy_dscp == other->copy_dscp && this->copy_dscp == other->copy_dscp &&
streq(this->updown, other->updown) && streq(this->updown, other->updown) &&
streq(this->interface, other->interface); streq(this->interface, other->interface) &&
sec_labels_equal(this->label, other->label) &&
this->label_mode == other->label_mode;
} }
METHOD(child_cfg_t, get_ref, child_cfg_t*, METHOD(child_cfg_t, get_ref, child_cfg_t*,
@@ -625,6 +728,7 @@ METHOD(child_cfg_t, destroy, void,
this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
this->my_ts->destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy)); this->my_ts->destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy));
this->other_ts->destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy)); this->other_ts->destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy));
DESTROY_IF(this->label);
free(this->updown); free(this->updown);
free(this->interface); free(this->interface);
free(this->name); free(this->name);
@@ -659,6 +763,9 @@ child_cfg_t *child_cfg_create(char *name, child_cfg_create_t *data)
.get_if_id = _get_if_id, .get_if_id = _get_if_id,
.get_mark = _get_mark, .get_mark = _get_mark,
.get_set_mark = _get_set_mark, .get_set_mark = _get_set_mark,
.get_label = _get_label,
.get_label_mode = _get_label_mode,
.select_label = _select_label,
.get_tfc = _get_tfc, .get_tfc = _get_tfc,
.get_manual_prio = _get_manual_prio, .get_manual_prio = _get_manual_prio,
.get_interface = _get_interface, .get_interface = _get_interface,
@@ -685,6 +792,9 @@ child_cfg_t *child_cfg_create(char *name, child_cfg_create_t *data)
.mark_out = data->mark_out, .mark_out = data->mark_out,
.set_mark_in = data->set_mark_in, .set_mark_in = data->set_mark_in,
.set_mark_out = data->set_mark_out, .set_mark_out = data->set_mark_out,
.label = data->label ? data->label->clone(data->label) : NULL,
.label_mode = data->label_mode != SEC_LABEL_MODE_SYSTEM ?
data->label_mode : sec_label_mode_default(),
.lifetime = data->lifetime, .lifetime = data->lifetime,
.inactivity = data->inactivity, .inactivity = data->inactivity,
.tfc = data->tfc, .tfc = data->tfc,
+44 -5
View File
@@ -40,11 +40,11 @@ typedef struct child_cfg_create_t child_cfg_create_t;
*/ */
enum action_t { enum action_t {
/** No action */ /** No action */
ACTION_NONE, ACTION_NONE = 0,
/** Route config to establish or reestablish on demand */ /** Install trap policy to (re-)establish on demand */
ACTION_ROUTE, ACTION_TRAP = (1<<0),
/** Start or restart config immediately */ /** Start or restart immediately */
ACTION_RESTART, ACTION_START = (1<<1),
}; };
/** /**
@@ -247,6 +247,41 @@ struct child_cfg_t {
*/ */
mark_t (*get_set_mark)(child_cfg_t *this, bool inbound); mark_t (*get_set_mark)(child_cfg_t *this, bool inbound);
/**
* Optional security label to be configured on policies.
*
* @return label or NULL
*/
sec_label_t *(*get_label)(child_cfg_t *this);
/**
* Get the mode in which the security label is used.
*
* @return label mode (never SEC_LABEL_MODE_SYSTEM)
*/
sec_label_mode_t (*get_label_mode)(child_cfg_t *this);
/**
* Select a security label from the given list that matches the configured
* label.
*
* This fails under the following conditions:
* - a label is configured but no labels are provided
* - no label is configured but at least one label is provided
* - the configured and provided labels don't match
*
* If no label is configured and none are provided, that's considered a
* success and label will be set to NULL.
*
* @param labels list of labels to match
* @param log FALSE to avoid logging details about the selection
* @param label[out] selected label or NULL if no label necessary
* @param exact[out] TRUE if there was an exact match
* @return FALSE on failure
*/
bool (*select_label)(child_cfg_t *this, linked_list_t *labels, bool log,
sec_label_t **label, bool *exact);
/** /**
* Get the TFC padding value to use for CHILD_SA. * Get the TFC padding value to use for CHILD_SA.
* *
@@ -367,6 +402,10 @@ struct child_cfg_create_t {
mark_t set_mark_in; mark_t set_mark_in;
/** Optional outbound mark the SA should apply to traffic */ /** Optional outbound mark the SA should apply to traffic */
mark_t set_mark_out; mark_t set_mark_out;
/** Optional security label configured on policies (cloned) */
sec_label_t *label;
/** Optional security label mode */
sec_label_mode_t label_mode;
/** Mode to propose for CHILD_SA */ /** Mode to propose for CHILD_SA */
ipsec_mode_t mode; ipsec_mode_t mode;
/** TFC padding size, 0 to disable, -1 to pad to PMTU */ /** TFC padding size, 0 to disable, -1 to pad to PMTU */
+31 -11
View File
@@ -386,13 +386,28 @@ METHOD(peer_cfg_t, create_child_cfg_enumerator, enumerator_t*,
/** /**
* Check how good a list of TS matches a given child config * Check how good a list of TS matches a given child config
*/ */
static int get_ts_match(child_cfg_t *cfg, bool local, static u_int get_ts_match(child_cfg_t *cfg, bool local,
linked_list_t *sup_list, linked_list_t *hosts) linked_list_t *sup_list, linked_list_t *hosts,
linked_list_t *sup_labels)
{ {
linked_list_t *cfg_list; linked_list_t *cfg_list;
enumerator_t *sup_enum, *cfg_enum; enumerator_t *sup_enum, *cfg_enum;
traffic_selector_t *sup_ts, *cfg_ts, *subset; traffic_selector_t *sup_ts, *cfg_ts, *subset;
int match = 0, round; sec_label_t *label;
u_int match = 0, round;
bool exact = FALSE;
if (cfg->select_label(cfg, sup_labels, TRUE, &label, &exact))
{
if (label)
{
match += exact ? 500 : 100;
}
}
else
{ /* label config doesn't match, no need to check TS */
return match;
}
/* fetch configured TS list, narrowing dynamic TS */ /* fetch configured TS list, narrowing dynamic TS */
cfg_list = cfg->get_traffic_selectors(cfg, local, NULL, hosts, TRUE); cfg_list = cfg->get_traffic_selectors(cfg, local, NULL, hosts, TRUE);
@@ -432,24 +447,29 @@ static int get_ts_match(child_cfg_t *cfg, bool local,
METHOD(peer_cfg_t, select_child_cfg, child_cfg_t*, METHOD(peer_cfg_t, select_child_cfg, child_cfg_t*,
private_peer_cfg_t *this, linked_list_t *my_ts, linked_list_t *other_ts, private_peer_cfg_t *this, linked_list_t *my_ts, linked_list_t *other_ts,
linked_list_t *my_hosts, linked_list_t *other_hosts) linked_list_t *my_hosts, linked_list_t *other_hosts,
linked_list_t *my_labels, linked_list_t *other_labels)
{ {
child_cfg_t *current, *found = NULL; child_cfg_t *current, *found = NULL;
enumerator_t *enumerator; enumerator_t *enumerator;
int best = 0; u_int best = 0;
DBG2(DBG_CFG, "looking for a child config for %#R === %#R", my_ts, other_ts); DBG2(DBG_CFG, "looking for a child config for %#R === %#R", my_ts, other_ts);
enumerator = create_child_cfg_enumerator(this); enumerator = create_child_cfg_enumerator(this);
while (enumerator->enumerate(enumerator, &current)) while (enumerator->enumerate(enumerator, &current))
{ {
int my_prio, other_prio; u_int my_prio, other_prio;
my_prio = get_ts_match(current, TRUE, my_ts, my_hosts); my_prio = get_ts_match(current, TRUE, my_ts, my_hosts, my_labels);
other_prio = get_ts_match(current, FALSE, other_ts, other_hosts); if (!my_prio)
if (my_prio && other_prio)
{ {
DBG2(DBG_CFG, " candidate \"%s\" with prio %d+%d", continue;
}
other_prio = get_ts_match(current, FALSE, other_ts, other_hosts,
other_labels);
if (other_prio)
{
DBG2(DBG_CFG, " candidate \"%s\" with prio %u+%u",
current->get_name(current), my_prio, other_prio); current->get_name(current), my_prio, other_prio);
if (my_prio + other_prio > best) if (my_prio + other_prio > best)
{ {
+7 -4
View File
@@ -174,17 +174,20 @@ struct peer_cfg_t {
enumerator_t* (*create_child_cfg_enumerator) (peer_cfg_t *this); enumerator_t* (*create_child_cfg_enumerator) (peer_cfg_t *this);
/** /**
* Select a CHILD config from traffic selectors. * Select a CHILD config from received traffic selectors.
* *
* @param my_ts TS for local side * @param my_ts TS for local side
* @param other_ts TS for remote side * @param other_ts TS for remote side
* @param my_hosts hosts to narrow down dynamic TS for local side * @param my_hosts hosts to narrow down dynamic TS for local side
* @param other_hosts hosts to narrow down dynamic TS for remote side * @param other_hosts hosts to narrow down dynamic TS for remote side
* @return selected CHILD config, or NULL if no match found * @param my_labels optional local security labels
* @param other_labels optional remove security labels
* @return selected CHILD config, or NULL if no match found
*/ */
child_cfg_t* (*select_child_cfg) (peer_cfg_t *this, child_cfg_t* (*select_child_cfg)(peer_cfg_t *this,
linked_list_t *my_ts, linked_list_t *other_ts, linked_list_t *my_ts, linked_list_t *other_ts,
linked_list_t *my_hosts, linked_list_t *other_hosts); linked_list_t *my_hosts, linked_list_t *other_hosts,
linked_list_t *my_labels, linked_list_t *other_labels);
/** /**
* Add an authentication config to the peer configuration. * Add an authentication config to the peer configuration.
+1 -1
View File
@@ -473,7 +473,7 @@ METHOD(job_t, initiate_execute, job_requeue_t,
} }
} }
if (ike_sa->initiate(ike_sa, listener->child_cfg, 0, NULL, NULL) == SUCCESS) if (ike_sa->initiate(ike_sa, listener->child_cfg, NULL) == SUCCESS)
{ {
if (!listener->logger.callback) if (!listener->logger.callback)
{ {
-4
View File
@@ -205,7 +205,6 @@ static void generate_u_int_type(private_generator_t *this,
case U_INT_4: case U_INT_4:
number_of_bits = 4; number_of_bits = 4;
break; break;
case TS_TYPE:
case RESERVED_BYTE: case RESERVED_BYTE:
case SPI_SIZE: case SPI_SIZE:
case U_INT_8: case U_INT_8:
@@ -281,7 +280,6 @@ static void generate_u_int_type(private_generator_t *this,
} }
break; break;
} }
case TS_TYPE:
case RESERVED_BYTE: case RESERVED_BYTE:
case SPI_SIZE: case SPI_SIZE:
case U_INT_8: case U_INT_8:
@@ -478,7 +476,6 @@ METHOD(generator_t, generate_payload, void,
case IKE_SPI: case IKE_SPI:
case RESERVED_BYTE: case RESERVED_BYTE:
case SPI_SIZE: case SPI_SIZE:
case TS_TYPE:
case ATTRIBUTE_TYPE: case ATTRIBUTE_TYPE:
case ATTRIBUTE_LENGTH: case ATTRIBUTE_LENGTH:
generate_u_int_type(this, rules[i].type, rules[i].offset); generate_u_int_type(this, rules[i].type, rules[i].offset);
@@ -491,7 +488,6 @@ METHOD(generator_t, generate_payload, void,
this->header_length_offset = get_offset(this); this->header_length_offset = get_offset(this);
generate_u_int_type(this, U_INT_32, rules[i].offset); generate_u_int_type(this, U_INT_32, rules[i].offset);
break; break;
case ADDRESS:
case SPI: case SPI:
case CHUNK_DATA: case CHUNK_DATA:
case ENCRYPTED_DATA: case ENCRYPTED_DATA:
-23
View File
@@ -368,7 +368,6 @@ METHOD(parser_t, parse_payload, status_t,
payload_t *pld; payload_t *pld;
void *output; void *output;
int payload_length = 0, spi_size = 0, attribute_length = 0, header_length; int payload_length = 0, spi_size = 0, attribute_length = 0, header_length;
uint16_t ts_type = 0;
bool attribute_format = FALSE; bool attribute_format = FALSE;
int rule_number, rule_count; int rule_number, rule_count;
encoding_rule_t *rule; encoding_rule_t *rule;
@@ -588,28 +587,6 @@ METHOD(parser_t, parse_payload, status_t,
} }
break; break;
} }
case TS_TYPE:
{
if (!parse_uint8(this, rule_number, output + rule->offset))
{
pld->destroy(pld);
return PARSE_ERROR;
}
ts_type = *(uint8_t*)(output + rule->offset);
break;
}
case ADDRESS:
{
int address_length = (ts_type == TS_IPV4_ADDR_RANGE) ? 4 : 16;
if (!parse_chunk(this, rule_number, output + rule->offset,
address_length))
{
pld->destroy(pld);
return PARSE_ERROR;
}
break;
}
default: default:
{ {
DBG1(DBG_ENC, " no rule to parse rule %d %N", DBG1(DBG_ENC, " no rule to parse rule %d %N",
@@ -34,8 +34,6 @@ ENUM(encoding_type_names, U_INT_4, ENCRYPTED_DATA,
"ATTRIBUTE_LENGTH_OR_VALUE", "ATTRIBUTE_LENGTH_OR_VALUE",
"ATTRIBUTE_LENGTH", "ATTRIBUTE_LENGTH",
"ATTRIBUTE_VALUE", "ATTRIBUTE_VALUE",
"TS_TYPE",
"ADDRESS",
"CHUNK_DATA", "CHUNK_DATA",
"IKE_SPI", "IKE_SPI",
"ENCRYPTED_DATA", "ENCRYPTED_DATA",
@@ -255,32 +255,6 @@ enum encoding_type_t {
*/ */
ATTRIBUTE_VALUE, ATTRIBUTE_VALUE,
/**
* Representing a Traffic selector type field.
*
* When generating it must be changed from host to network order.
* The value is read from the associated data struct.
* The current write position is moved 16 bit forward afterwards.
*
* When parsing it must be changed from network to host order.
* The value is written to the associated data struct.
* The current read pointer is moved 16 bit forward afterwards.
*/
TS_TYPE,
/**
* Representing an address field in a traffic selector.
*
* Depending on the last field of type TS_TYPE
* this field is either 4 or 16 byte long.
*
* When generating the content of the chunk pointing to
* is written.
*
* When parsing 4 or 16 bytes are read and written into the chunk pointing to.
*/
ADDRESS,
/** /**
* Representing a variable length byte field. * Representing a variable length byte field.
*/ */
@@ -14,11 +14,33 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details. * for more details.
*/ */
/*
* Copyright (C) 2022 Tobias Brunner, codelabs GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "traffic_selector_substructure.h" #include "traffic_selector_substructure.h"
#include <encoding/payloads/encodings.h> #include <encoding/payloads/encodings.h>
#include <collections/linked_list.h> #include <bio/bio_reader.h>
#include <bio/bio_writer.h>
typedef struct private_traffic_selector_substructure_t private_traffic_selector_substructure_t; typedef struct private_traffic_selector_substructure_t private_traffic_selector_substructure_t;
@@ -48,50 +70,32 @@ struct private_traffic_selector_substructure_t {
uint16_t payload_length; uint16_t payload_length;
/** /**
* Start port number. * Port/address range or security label.
*/ */
uint16_t start_port; chunk_t ts_data;
/**
* End port number.
*/
uint16_t end_port;
/**
* Starting address.
*/
chunk_t starting_address;
/**
* Ending address.
*/
chunk_t ending_address;
}; };
/** /**
* Encoding rules to parse or generate a TS payload * Encoding rules to parse or generate a TS payload.
*
* Due to the generic nature of security labels, the actual structure of regular
* TS is not parsed with these rules.
* *
* The defined offsets are the positions in a object of type * The defined offsets are the positions in a object of type
* private_traffic_selector_substructure_t. * private_traffic_selector_substructure_t.
*/ */
static encoding_rule_t encodings[] = { static encoding_rule_t encodings[] = {
/* 1 Byte next ts type*/ /* 1 Byte next ts type*/
{ TS_TYPE, offsetof(private_traffic_selector_substructure_t, ts_type) }, { U_INT_8, offsetof(private_traffic_selector_substructure_t, ts_type) },
/* 1 Byte IP protocol id*/ /* 1 Byte IP protocol id*/
{ U_INT_8, offsetof(private_traffic_selector_substructure_t, ip_protocol_id) }, { U_INT_8, offsetof(private_traffic_selector_substructure_t, ip_protocol_id) },
/* Length of the whole payload*/ /* Length of the whole payload*/
{ PAYLOAD_LENGTH,offsetof(private_traffic_selector_substructure_t, payload_length) }, { PAYLOAD_LENGTH,offsetof(private_traffic_selector_substructure_t, payload_length) },
/* 2 Byte start port*/ /* traffic selector data, length is defined in PAYLOAD_LENGTH */
{ U_INT_16, offsetof(private_traffic_selector_substructure_t, start_port) }, { CHUNK_DATA, offsetof(private_traffic_selector_substructure_t, ts_data) },
/* 2 Byte end port*/
{ U_INT_16, offsetof(private_traffic_selector_substructure_t, end_port) },
/* starting address is either 4 or 16 byte */
{ ADDRESS, offsetof(private_traffic_selector_substructure_t, starting_address) },
/* ending address is either 4 or 16 byte */
{ ADDRESS, offsetof(private_traffic_selector_substructure_t, ending_address) }
}; };
/* /* Regular traffic selectors for address ranges:
1 2 3 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -107,48 +111,91 @@ static encoding_rule_t encodings[] = {
~ Ending Address* ~ ~ Ending Address* ~
! ! ! !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* Security labels:
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+---------------+---------------+-------------------------------+
| TS Type | Reserved | Selector Length |
+---------------+---------------+-------------------------------+
| |
~ Security Label* ~
| |
+---------------------------------------------------------------+
*/ */
/**
* Parse the data of a regular address range traffic selector.
*/
static bool parse_ts_data(private_traffic_selector_substructure_t *this,
uint16_t *start_port, uint16_t *end_port,
chunk_t *start_addr, chunk_t *end_addr)
{
bio_reader_t *reader;
int addr_len;
switch (this->ts_type)
{
case TS_IPV4_ADDR_RANGE:
addr_len = 4;
break;
case TS_IPV6_ADDR_RANGE:
addr_len = 16;
break;
default:
return FALSE;
}
reader = bio_reader_create(this->ts_data);
if (!reader->read_uint16(reader, start_port) ||
!reader->read_uint16(reader, end_port) ||
!reader->read_data(reader, addr_len, start_addr) ||
!reader->read_data(reader, addr_len, end_addr) ||
reader->remaining(reader) > 0)
{
reader->destroy(reader);
return FALSE;
}
reader->destroy(reader);
return TRUE;
}
METHOD(payload_t, verify, status_t, METHOD(payload_t, verify, status_t,
private_traffic_selector_substructure_t *this) private_traffic_selector_substructure_t *this)
{ {
if (this->start_port > this->end_port)
{
/* OPAQUE ports are the only exception */
if (this->start_port != 0xffff && this->end_port != 0)
{
return FAILED;
}
}
switch (this->ts_type) switch (this->ts_type)
{ {
case TS_IPV4_ADDR_RANGE: case TS_IPV4_ADDR_RANGE:
{
if ((this->starting_address.len != 4) ||
(this->ending_address.len != 4))
{
/* ipv4 address must be 4 bytes long */
return FAILED;
}
break;
}
case TS_IPV6_ADDR_RANGE: case TS_IPV6_ADDR_RANGE:
{ {
if ((this->starting_address.len != 16) || uint16_t start_port, end_port;
(this->ending_address.len != 16)) chunk_t start_addr, end_addr;
if (!parse_ts_data(this, &start_port, &end_port, &start_addr,
&end_addr))
{ {
/* ipv6 address must be 16 bytes long */
return FAILED; return FAILED;
} }
if (start_port > end_port)
{
/* OPAQUE ports are the only exception */
if (start_port != 0xffff && end_port != 0)
{
return FAILED;
}
}
break; break;
} }
case TS_SECLABEL:
if (!this->ts_data.len)
{
return FAILED;
}
break;
default: default:
{ /* unsupported TS type, just ignored later */
/* not supported ts type */ break;
return FAILED;
}
} }
return SUCCESS; return SUCCESS;
} }
@@ -162,7 +209,7 @@ METHOD(payload_t, get_encoding_rules, int,
METHOD(payload_t, get_header_length, int, METHOD(payload_t, get_header_length, int,
private_traffic_selector_substructure_t *this) private_traffic_selector_substructure_t *this)
{ {
return 8; return 4;
} }
METHOD(payload_t, get_type, payload_type_t, METHOD(payload_t, get_type, payload_type_t,
@@ -191,17 +238,32 @@ METHOD(payload_t, get_length, size_t,
METHOD(traffic_selector_substructure_t, get_traffic_selector, traffic_selector_t*, METHOD(traffic_selector_substructure_t, get_traffic_selector, traffic_selector_t*,
private_traffic_selector_substructure_t *this) private_traffic_selector_substructure_t *this)
{ {
uint16_t start_port, end_port;
chunk_t start_addr, end_addr;
if (!parse_ts_data(this, &start_port, &end_port, &start_addr, &end_addr))
{
return NULL;
}
return traffic_selector_create_from_bytes( return traffic_selector_create_from_bytes(
this->ip_protocol_id, this->ts_type, this->ip_protocol_id, this->ts_type,
this->starting_address, this->start_port, start_addr, start_port, end_addr, end_port);
this->ending_address, this->end_port); }
METHOD(traffic_selector_substructure_t, get_sec_label, sec_label_t*,
private_traffic_selector_substructure_t *this)
{
if (this->ts_type != TS_SECLABEL)
{
return NULL;
}
return sec_label_from_encoding(this->ts_data);
} }
METHOD2(payload_t, traffic_selector_substructure_t, destroy, void, METHOD2(payload_t, traffic_selector_substructure_t, destroy, void,
private_traffic_selector_substructure_t *this) private_traffic_selector_substructure_t *this)
{ {
free(this->starting_address.ptr); free(this->ts_data.ptr);
free(this->ending_address.ptr);
free(this); free(this);
} }
@@ -225,6 +287,7 @@ traffic_selector_substructure_t *traffic_selector_substructure_create()
.destroy = _destroy, .destroy = _destroy,
}, },
.get_traffic_selector = _get_traffic_selector, .get_traffic_selector = _get_traffic_selector,
.get_sec_label = _get_sec_label,
.destroy = _destroy, .destroy = _destroy,
}, },
.payload_length = get_header_length(this), .payload_length = get_header_length(this),
@@ -241,16 +304,34 @@ traffic_selector_substructure_t *traffic_selector_substructure_create_from_traff
traffic_selector_t *ts) traffic_selector_t *ts)
{ {
private_traffic_selector_substructure_t *this; private_traffic_selector_substructure_t *this;
bio_writer_t *writer;
this = (private_traffic_selector_substructure_t*)traffic_selector_substructure_create(); this = (private_traffic_selector_substructure_t*)traffic_selector_substructure_create();
this->ts_type = ts->get_type(ts); this->ts_type = ts->get_type(ts);
this->ip_protocol_id = ts->get_protocol(ts); this->ip_protocol_id = ts->get_protocol(ts);
this->start_port = ts->get_from_port(ts);
this->end_port = ts->get_to_port(ts);
this->starting_address = chunk_clone(ts->get_from_address(ts));
this->ending_address = chunk_clone(ts->get_to_address(ts));
this->payload_length = get_header_length(this) +
this->ending_address.len + this->starting_address.len;
writer = bio_writer_create(this->ts_type == TS_IPV4_ADDR_RANGE ? 12 : 36);
writer->write_uint16(writer, ts->get_from_port(ts));
writer->write_uint16(writer, ts->get_to_port(ts));
writer->write_data(writer, ts->get_from_address(ts));
writer->write_data(writer, ts->get_to_address(ts));
this->ts_data = writer->extract_buf(writer);
this->payload_length += this->ts_data.len;
writer->destroy(writer);
return &this->public;
}
/*
* Described in header
*/
traffic_selector_substructure_t *traffic_selector_substructure_create_from_sec_label(
sec_label_t *label)
{
private_traffic_selector_substructure_t *this;
this = (private_traffic_selector_substructure_t*)traffic_selector_substructure_create();
this->ts_type = TS_SECLABEL;
this->ts_data = chunk_clone(label->get_encoding(label));
this->payload_length += this->ts_data.len;
return &this->public; return &this->public;
} }
@@ -13,6 +13,27 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details. * for more details.
*/ */
/*
* Copyright (C) 2022 Tobias Brunner, codelabs GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/** /**
* @defgroup traffic_selector_substructure traffic_selector_substructure * @defgroup traffic_selector_substructure traffic_selector_substructure
@@ -27,6 +48,7 @@ typedef struct traffic_selector_substructure_t traffic_selector_substructure_t;
#include <library.h> #include <library.h>
#include <networking/host.h> #include <networking/host.h>
#include <selectors/traffic_selector.h> #include <selectors/traffic_selector.h>
#include <selectors/sec_label.h>
#include <encoding/payloads/payload.h> #include <encoding/payloads/payload.h>
/** /**
@@ -35,88 +57,29 @@ typedef struct traffic_selector_substructure_t traffic_selector_substructure_t;
* The TRAFFIC SELECTOR format is described in RFC section 3.13.1. * The TRAFFIC SELECTOR format is described in RFC section 3.13.1.
*/ */
struct traffic_selector_substructure_t { struct traffic_selector_substructure_t {
/** /**
* The payload_t interface. * The payload_t interface.
*/ */
payload_t payload_interface; payload_t payload_interface;
/** /**
* Get the type of Traffic selector. * Get a traffic_selector_t from this substructure if possible.
* *
* @return type of traffic selector * @warning the returned object must be destroyed after use
* *
* @return contained traffic_selector_t (NULL if type mismatch)
*/ */
ts_type_t (*get_ts_type) (traffic_selector_substructure_t *this); traffic_selector_t *(*get_traffic_selector)(traffic_selector_substructure_t *this);
/** /**
* Set the type of Traffic selector. * Get a sec_label_t from this substructure if possible.
* *
* @param ts_type type of traffic selector * @warning the returned object must be destroyed after use
*
* @return contained sec_label_t (NULL if type mismatch)
*/ */
void (*set_ts_type) (traffic_selector_substructure_t *this, sec_label_t *(*get_sec_label)(traffic_selector_substructure_t *this);
ts_type_t ts_type);
/**
* Get the IP protocol ID of Traffic selector.
*
* @return type of traffic selector
*
*/
uint8_t (*get_protocol_id) (traffic_selector_substructure_t *this);
/**
* Set the IP protocol ID of Traffic selector
*
* @param protocol_id protocol ID of traffic selector
*/
void (*set_protocol_id) (traffic_selector_substructure_t *this,
uint8_t protocol_id);
/**
* Get the start port and address as host_t object.
*
* Returned host_t object has to get destroyed by the caller.
*
* @return start host as host_t object
*
*/
host_t *(*get_start_host) (traffic_selector_substructure_t *this);
/**
* Set the start port and address as host_t object.
*
* @param start_host start host as host_t object
*/
void (*set_start_host) (traffic_selector_substructure_t *this,
host_t *start_host);
/**
* Get the end port and address as host_t object.
*
* Returned host_t object has to get destroyed by the caller.
*
* @return end host as host_t object
*
*/
host_t *(*get_end_host) (traffic_selector_substructure_t *this);
/**
* Set the end port and address as host_t object.
*
* @param end_host end host as host_t object
*/
void (*set_end_host) (traffic_selector_substructure_t *this,
host_t *end_host);
/**
* Get a traffic_selector_t from this substructure.
*
* @warning traffic_selector_t must be destroyed after usage.
*
* @return contained traffic_selector_t
*/
traffic_selector_t *(*get_traffic_selector) (
traffic_selector_substructure_t *this);
/** /**
* Destroys an traffic_selector_substructure_t object. * Destroys an traffic_selector_substructure_t object.
@@ -134,13 +97,21 @@ struct traffic_selector_substructure_t {
traffic_selector_substructure_t *traffic_selector_substructure_create(void); traffic_selector_substructure_t *traffic_selector_substructure_create(void);
/** /**
* Creates an initialized traffic selector substructure using * Creates a traffic selector substructure based on a traffic_selector_t.
* the values from a traffic_selector_t.
* *
* @param traffic_selector traffic_selector_t to use for initialization * @param traffic_selector data to use
* @return traffic_selector_substructure_t object * @return traffic_selector_substructure_t object
*/ */
traffic_selector_substructure_t *traffic_selector_substructure_create_from_traffic_selector( traffic_selector_substructure_t *traffic_selector_substructure_create_from_traffic_selector(
traffic_selector_t *traffic_selector); traffic_selector_t *traffic_selector);
/**
* Creates a traffic selector substructure based on a sec_label_t.
*
* @param label data to use
* @return traffic_selector_substructure_t object
*/
traffic_selector_substructure_t *traffic_selector_substructure_create_from_sec_label(
sec_label_t *label);
#endif /** TRAFFIC_SELECTOR_SUBSTRUCTURE_H_ @}*/ #endif /** TRAFFIC_SELECTOR_SUBSTRUCTURE_H_ @}*/
+43 -6
View File
@@ -218,10 +218,11 @@ METHOD(ts_payload_t, set_initiator, void,
this->is_initiator = is_initiator; this->is_initiator = is_initiator;
} }
METHOD(ts_payload_t, get_traffic_selectors, linked_list_t*, /**
private_ts_payload_t *this) * Get a list of either traffic selectors or labels
*/
static linked_list_t *get_list(private_ts_payload_t *this, bool labels)
{ {
traffic_selector_t *ts;
enumerator_t *enumerator; enumerator_t *enumerator;
traffic_selector_substructure_t *subst; traffic_selector_substructure_t *subst;
linked_list_t *list; linked_list_t *list;
@@ -230,14 +231,42 @@ METHOD(ts_payload_t, get_traffic_selectors, linked_list_t*,
enumerator = this->substrs->create_enumerator(this->substrs); enumerator = this->substrs->create_enumerator(this->substrs);
while (enumerator->enumerate(enumerator, &subst)) while (enumerator->enumerate(enumerator, &subst))
{ {
ts = subst->get_traffic_selector(subst); if (labels)
list->insert_last(list, ts); {
sec_label_t *label = subst->get_sec_label(subst);
if (label)
{
list->insert_last(list, label);
}
}
else
{
traffic_selector_t *ts = subst->get_traffic_selector(subst);
if (ts)
{
list->insert_last(list, ts);
}
}
} }
enumerator->destroy(enumerator); enumerator->destroy(enumerator);
return list; return list;
} }
METHOD(ts_payload_t, get_traffic_selectors, linked_list_t*,
private_ts_payload_t *this)
{
return get_list(this, FALSE);
}
METHOD(ts_payload_t, get_sec_labels, linked_list_t*,
private_ts_payload_t *this)
{
return get_list(this, TRUE);
}
METHOD2(payload_t, ts_payload_t, destroy, void, METHOD2(payload_t, ts_payload_t, destroy, void,
private_ts_payload_t *this) private_ts_payload_t *this)
{ {
@@ -267,6 +296,7 @@ ts_payload_t *ts_payload_create(bool is_initiator)
.get_initiator = _get_initiator, .get_initiator = _get_initiator,
.set_initiator = _set_initiator, .set_initiator = _set_initiator,
.get_traffic_selectors = _get_traffic_selectors, .get_traffic_selectors = _get_traffic_selectors,
.get_sec_labels = _get_sec_labels,
.destroy = _destroy, .destroy = _destroy,
}, },
.next_payload = PL_NONE, .next_payload = PL_NONE,
@@ -281,7 +311,8 @@ ts_payload_t *ts_payload_create(bool is_initiator)
* Described in header * Described in header
*/ */
ts_payload_t *ts_payload_create_from_traffic_selectors(bool is_initiator, ts_payload_t *ts_payload_create_from_traffic_selectors(bool is_initiator,
linked_list_t *traffic_selectors) linked_list_t *traffic_selectors,
sec_label_t *label)
{ {
enumerator_t *enumerator; enumerator_t *enumerator;
traffic_selector_t *ts; traffic_selector_t *ts;
@@ -297,6 +328,12 @@ ts_payload_t *ts_payload_create_from_traffic_selectors(bool is_initiator,
this->substrs->insert_last(this->substrs, subst); this->substrs->insert_last(this->substrs, subst);
} }
enumerator->destroy(enumerator); enumerator->destroy(enumerator);
if (label)
{
subst = traffic_selector_substructure_create_from_sec_label(label);
this->substrs->insert_last(this->substrs, subst);
}
compute_length(this); compute_length(this);
return &this->public; return &this->public;
+16 -4
View File
@@ -63,11 +63,20 @@ struct ts_payload_t {
/** /**
* Get a list of nested traffic selectors as traffic_selector_t. * Get a list of nested traffic selectors as traffic_selector_t.
* *
* Resulting list and its traffic selectors must be destroyed after usage * Resulting list and its traffic selectors must be destroyed after use.
* *
* @return list of traffic selectors * @return list of traffic selectors
*/ */
linked_list_t *(*get_traffic_selectors) (ts_payload_t *this); linked_list_t *(*get_traffic_selectors)(ts_payload_t *this);
/**
* Get a list of security labels as sec_label_t.
*
* Resulting list and its security labels must be destroyed after use.
*
* @return list of security labels
*/
linked_list_t *(*get_sec_labels)(ts_payload_t *this);
/** /**
* Destroys an ts_payload_t object. * Destroys an ts_payload_t object.
@@ -84,13 +93,16 @@ struct ts_payload_t {
ts_payload_t *ts_payload_create(bool is_initiator); ts_payload_t *ts_payload_create(bool is_initiator);
/** /**
* Creates ts_payload with a list of traffic_selector_t * Creates ts_payload with a list of traffic_selector_t and an optional security
* label.
* *
* @param is_initiator TRUE for TSi, FALSE for TSr payload type * @param is_initiator TRUE for TSi, FALSE for TSr payload type
* @param traffic_selectors list of traffic selectors to include * @param traffic_selectors list of traffic selectors to include
* @param label optional security label to include
* @return ts_payload_t object * @return ts_payload_t object
*/ */
ts_payload_t *ts_payload_create_from_traffic_selectors(bool is_initiator, ts_payload_t *ts_payload_create_from_traffic_selectors(bool is_initiator,
linked_list_t *traffic_selectors); linked_list_t *traffic_selectors,
sec_label_t *label);
#endif /** TS_PAYLOAD_H_ @}*/ #endif /** TS_PAYLOAD_H_ @}*/
+13 -6
View File
@@ -53,20 +53,27 @@ static inline protocol_id_t proto_ip2ike(uint8_t protocol)
} }
METHOD(kernel_listener_t, acquire, bool, METHOD(kernel_listener_t, acquire, bool,
private_kernel_handler_t *this, uint32_t reqid, private_kernel_handler_t *this, uint32_t reqid, kernel_acquire_data_t *data)
traffic_selector_t *src_ts, traffic_selector_t *dst_ts)
{ {
if (src_ts && dst_ts) char buf[BUF_LEN] = "";
if (data->label)
{
snprintf(buf, sizeof(buf), ", label {%s}",
data->label->get_string(data->label));
}
if (data->src && data->dst)
{ {
DBG1(DBG_KNL, "creating acquire job for policy %R === %R with " DBG1(DBG_KNL, "creating acquire job for policy %R === %R with "
"reqid {%u}", src_ts, dst_ts, reqid); "reqid {%u}%s", data->src, data->dst, reqid, buf);
} }
else else
{ {
DBG1(DBG_KNL, "creating acquire job for policy with reqid {%u}", reqid); DBG1(DBG_KNL, "creating acquire job for policy with reqid {%u}%s",
reqid, buf);
} }
lib->processor->queue_job(lib->processor, lib->processor->queue_job(lib->processor,
(job_t*)acquire_job_create(reqid, src_ts, dst_ts)); (job_t*)acquire_job_create(reqid, data));
return TRUE; return TRUE;
} }
+57 -29
View File
@@ -208,6 +208,8 @@ typedef struct {
uint32_t if_id_in; uint32_t if_id_in;
/** outbound interface ID used for SA */ /** outbound interface ID used for SA */
uint32_t if_id_out; uint32_t if_id_out;
/** security label */
sec_label_t *label;
/** local traffic selectors */ /** local traffic selectors */
array_t *local; array_t *local;
/** remote traffic selectors */ /** remote traffic selectors */
@@ -221,19 +223,49 @@ static void reqid_entry_destroy(reqid_entry_t *entry)
{ {
array_destroy_offset(entry->local, offsetof(traffic_selector_t, destroy)); array_destroy_offset(entry->local, offsetof(traffic_selector_t, destroy));
array_destroy_offset(entry->remote, offsetof(traffic_selector_t, destroy)); array_destroy_offset(entry->remote, offsetof(traffic_selector_t, destroy));
DESTROY_IF(entry->label);
free(entry); free(entry);
} }
/**
* Hash the shared data of reqid entries
*/
static u_int entry_hash_shared(reqid_entry_t *entry)
{
u_int hash;
hash = chunk_hash_inc(chunk_from_thing(entry->mark_in),
chunk_hash_inc(chunk_from_thing(entry->mark_out),
chunk_hash_inc(chunk_from_thing(entry->if_id_in),
chunk_hash(chunk_from_thing(entry->if_id_out)))));
if (entry->label)
{
hash = entry->label->hash(entry->label, hash);
}
return hash;
}
/**
* Compare the shared properties of reqid entries
*/
static bool entry_equals_shared(reqid_entry_t *a, reqid_entry_t *b)
{
return a->mark_in.value == b->mark_in.value &&
a->mark_in.mask == b->mark_in.mask &&
a->mark_out.value == b->mark_out.value &&
a->mark_out.mask == b->mark_out.mask &&
a->if_id_in == b->if_id_in &&
a->if_id_out == b->if_id_out &&
sec_labels_equal(a->label, b->label);
}
/** /**
* Hashtable hash function for reqid entries using reqid as key * Hashtable hash function for reqid entries using reqid as key
*/ */
static u_int hash_reqid(reqid_entry_t *entry) static u_int hash_reqid(reqid_entry_t *entry)
{ {
return chunk_hash_inc(chunk_from_thing(entry->reqid), return chunk_hash_inc(chunk_from_thing(entry->reqid),
chunk_hash_inc(chunk_from_thing(entry->mark_in), entry_hash_shared(entry));
chunk_hash_inc(chunk_from_thing(entry->mark_out),
chunk_hash_inc(chunk_from_thing(entry->if_id_in),
chunk_hash(chunk_from_thing(entry->if_id_out))))));
} }
/** /**
@@ -241,13 +273,11 @@ static u_int hash_reqid(reqid_entry_t *entry)
*/ */
static bool equals_reqid(reqid_entry_t *a, reqid_entry_t *b) static bool equals_reqid(reqid_entry_t *a, reqid_entry_t *b)
{ {
return a->reqid == b->reqid && if (a->reqid == b->reqid)
a->mark_in.value == b->mark_in.value && {
a->mark_in.mask == b->mark_in.mask && return entry_equals_shared(a, b);
a->mark_out.value == b->mark_out.value && }
a->mark_out.mask == b->mark_out.mask && return FALSE;
a->if_id_in == b->if_id_in &&
a->if_id_out == b->if_id_out;
} }
/** /**
@@ -273,11 +303,9 @@ static u_int hash_ts_array(array_t *array, u_int hash)
*/ */
static u_int hash_reqid_by_ts(reqid_entry_t *entry) static u_int hash_reqid_by_ts(reqid_entry_t *entry)
{ {
return hash_ts_array(entry->local, hash_ts_array(entry->remote, return hash_ts_array(entry->local,
chunk_hash_inc(chunk_from_thing(entry->mark_in), hash_ts_array(entry->remote,
chunk_hash_inc(chunk_from_thing(entry->mark_out), entry_hash_shared(entry)));
chunk_hash_inc(chunk_from_thing(entry->if_id_in),
chunk_hash(chunk_from_thing(entry->if_id_out)))))));
} }
/** /**
@@ -311,14 +339,12 @@ static bool ts_array_equals(array_t *a, array_t *b)
*/ */
static bool equals_reqid_by_ts(reqid_entry_t *a, reqid_entry_t *b) static bool equals_reqid_by_ts(reqid_entry_t *a, reqid_entry_t *b)
{ {
return ts_array_equals(a->local, b->local) && if (ts_array_equals(a->local, b->local) &&
ts_array_equals(a->remote, b->remote) && ts_array_equals(a->remote, b->remote))
a->mark_in.value == b->mark_in.value && {
a->mark_in.mask == b->mark_in.mask && return entry_equals_shared(a, b);
a->mark_out.value == b->mark_out.value && }
a->mark_out.mask == b->mark_out.mask && return FALSE;
a->if_id_in == b->if_id_in &&
a->if_id_out == b->if_id_out;
} }
/** /**
@@ -346,7 +372,7 @@ METHOD(kernel_interface_t, alloc_reqid, status_t,
private_kernel_interface_t *this, private_kernel_interface_t *this,
linked_list_t *local_ts, linked_list_t *remote_ts, linked_list_t *local_ts, linked_list_t *remote_ts,
mark_t mark_in, mark_t mark_out, uint32_t if_id_in, uint32_t if_id_out, mark_t mark_in, mark_t mark_out, uint32_t if_id_in, uint32_t if_id_out,
uint32_t *reqid) sec_label_t *label, uint32_t *reqid)
{ {
static uint32_t counter = 0; static uint32_t counter = 0;
reqid_entry_t *entry = NULL, *tmpl; reqid_entry_t *entry = NULL, *tmpl;
@@ -359,6 +385,7 @@ METHOD(kernel_interface_t, alloc_reqid, status_t,
.mark_out = mark_out, .mark_out = mark_out,
.if_id_in = if_id_in, .if_id_in = if_id_in,
.if_id_out = if_id_out, .if_id_out = if_id_out,
.label = label ? label->clone(label) : NULL,
.reqid = *reqid, .reqid = *reqid,
); );
@@ -404,7 +431,8 @@ METHOD(kernel_interface_t, alloc_reqid, status_t,
METHOD(kernel_interface_t, release_reqid, status_t, METHOD(kernel_interface_t, release_reqid, status_t,
private_kernel_interface_t *this, uint32_t reqid, private_kernel_interface_t *this, uint32_t reqid,
mark_t mark_in, mark_t mark_out, uint32_t if_id_in, uint32_t if_id_out) mark_t mark_in, mark_t mark_out, uint32_t if_id_in, uint32_t if_id_out,
sec_label_t *label)
{ {
reqid_entry_t *entry, tmpl = { reqid_entry_t *entry, tmpl = {
.reqid = reqid, .reqid = reqid,
@@ -412,6 +440,7 @@ METHOD(kernel_interface_t, release_reqid, status_t,
.mark_out = mark_out, .mark_out = mark_out,
.if_id_in = if_id_in, .if_id_in = if_id_in,
.if_id_out = if_id_out, .if_id_out = if_id_out,
.label = label,
}; };
this->mutex->lock(this->mutex); this->mutex->lock(this->mutex);
@@ -827,7 +856,7 @@ METHOD(kernel_interface_t, remove_listener, void,
METHOD(kernel_interface_t, acquire, void, METHOD(kernel_interface_t, acquire, void,
private_kernel_interface_t *this, uint32_t reqid, private_kernel_interface_t *this, uint32_t reqid,
traffic_selector_t *src_ts, traffic_selector_t *dst_ts) kernel_acquire_data_t *data)
{ {
kernel_listener_t *listener; kernel_listener_t *listener;
enumerator_t *enumerator; enumerator_t *enumerator;
@@ -835,8 +864,7 @@ METHOD(kernel_interface_t, acquire, void,
enumerator = this->listeners->create_enumerator(this->listeners); enumerator = this->listeners->create_enumerator(this->listeners);
while (enumerator->enumerate(enumerator, &listener)) while (enumerator->enumerate(enumerator, &listener))
{ {
if (listener->acquire && if (listener->acquire && !listener->acquire(listener, reqid, data))
!listener->acquire(listener, reqid, src_ts, dst_ts))
{ {
this->listeners->remove_at(this->listeners, enumerator); this->listeners->remove_at(this->listeners, enumerator);
} }
+8 -5
View File
@@ -145,13 +145,15 @@ struct kernel_interface_t {
* @param mark_out outbound mark on SA * @param mark_out outbound mark on SA
* @param if_id_in inbound interface ID on SA * @param if_id_in inbound interface ID on SA
* @param if_id_out outbound interface ID on SA * @param if_id_out outbound interface ID on SA
* @param label security label (usually the one on the policy, not SA)
* @param reqid allocated reqid * @param reqid allocated reqid
* @return SUCCESS if reqid allocated * @return SUCCESS if reqid allocated
*/ */
status_t (*alloc_reqid)(kernel_interface_t *this, status_t (*alloc_reqid)(kernel_interface_t *this,
linked_list_t *local_ts, linked_list_t *remote_ts, linked_list_t *local_ts, linked_list_t *remote_ts,
mark_t mark_in, mark_t mark_out, uint32_t if_id_in, mark_t mark_in, mark_t mark_out, uint32_t if_id_in,
uint32_t if_id_out, uint32_t *reqid); uint32_t if_id_out, sec_label_t *label,
uint32_t *reqid);
/** /**
* Release a previously allocated reqid. * Release a previously allocated reqid.
@@ -161,11 +163,13 @@ struct kernel_interface_t {
* @param mark_out outbound mark on SA * @param mark_out outbound mark on SA
* @param if_id_in inbound interface ID on SA * @param if_id_in inbound interface ID on SA
* @param if_id_out outbound interface ID on SA * @param if_id_out outbound interface ID on SA
* @param label security label (usually the one on the policy, not SA)
* @return SUCCESS if reqid released * @return SUCCESS if reqid released
*/ */
status_t (*release_reqid)(kernel_interface_t *this, uint32_t reqid, status_t (*release_reqid)(kernel_interface_t *this, uint32_t reqid,
mark_t mark_in, mark_t mark_out, mark_t mark_in, mark_t mark_out,
uint32_t if_id_in, uint32_t if_id_out); uint32_t if_id_in, uint32_t if_id_out,
sec_label_t *label);
/** /**
* Add an SA to the SAD. * Add an SA to the SAD.
@@ -513,11 +517,10 @@ struct kernel_interface_t {
* Raise an acquire event. * Raise an acquire event.
* *
* @param reqid reqid of the policy to acquire * @param reqid reqid of the policy to acquire
* @param src_ts source traffic selector * @param data data from the acquire
* @param dst_ts destination traffic selector
*/ */
void (*acquire)(kernel_interface_t *this, uint32_t reqid, void (*acquire)(kernel_interface_t *this, uint32_t reqid,
traffic_selector_t *src_ts, traffic_selector_t *dst_ts); kernel_acquire_data_t *data);
/** /**
* Raise an expire event. * Raise an expire event.
+7
View File
@@ -38,6 +38,7 @@ typedef struct kernel_ipsec_query_policy_t kernel_ipsec_query_policy_t;
#include <networking/host.h> #include <networking/host.h>
#include <ipsec/ipsec_types.h> #include <ipsec/ipsec_types.h>
#include <selectors/traffic_selector.h> #include <selectors/traffic_selector.h>
#include <selectors/sec_label.h>
#include <plugins/plugin.h> #include <plugins/plugin.h>
#include <kernel/kernel_interface.h> #include <kernel/kernel_interface.h>
@@ -97,6 +98,8 @@ struct kernel_ipsec_add_sa_t {
hw_offload_t hw_offload; hw_offload_t hw_offload;
/** Mark the SA should apply to packets after processing */ /** Mark the SA should apply to packets after processing */
mark_t mark; mark_t mark;
/** Security label to match or apply */
sec_label_t *label;
/** TRUE to use Extended Sequence Numbers */ /** TRUE to use Extended Sequence Numbers */
bool esn; bool esn;
/** TRUE to copy the DF bit to the outer IPv4 header in tunnel mode */ /** TRUE to copy the DF bit to the outer IPv4 header in tunnel mode */
@@ -127,6 +130,8 @@ struct kernel_ipsec_update_sa_t {
bool encap; bool encap;
/** TRUE to enable UDP encapsulation */ /** TRUE to enable UDP encapsulation */
bool new_encap; bool new_encap;
/** New reqid, or 0 if unchanged */
uint32_t new_reqid;
}; };
/** /**
@@ -160,6 +165,8 @@ struct kernel_ipsec_policy_id_t {
uint32_t if_id; uint32_t if_id;
/** Network interface restricting policy */ /** Network interface restricting policy */
char *interface; char *interface;
/** Security label restricting policy */
sec_label_t *label;
}; };
/** /**
+15 -3
View File
@@ -22,12 +22,25 @@
#define KERNEL_LISTENER_H_ #define KERNEL_LISTENER_H_
typedef struct kernel_listener_t kernel_listener_t; typedef struct kernel_listener_t kernel_listener_t;
typedef struct kernel_acquire_data_t kernel_acquire_data_t;
#include <networking/host.h> #include <networking/host.h>
#include <networking/tun_device.h> #include <networking/tun_device.h>
#include <selectors/traffic_selector.h> #include <selectors/traffic_selector.h>
#include <kernel/kernel_ipsec.h> #include <kernel/kernel_ipsec.h>
/**
* Data received with a kernel's acquire, has to be cloned/copied by listener.
*/
struct kernel_acquire_data_t {
/** Optional source of the triggering packet */
traffic_selector_t *src;
/** Optional destination of the triggering packet */
traffic_selector_t *dst;
/** Optional security label of the triggering packet */
sec_label_t *label;
};
/** /**
* Interface for components interested in kernel events. * Interface for components interested in kernel events.
* *
@@ -39,12 +52,11 @@ struct kernel_listener_t {
* Hook called if an acquire event for a policy is received. * Hook called if an acquire event for a policy is received.
* *
* @param reqid reqid of the policy to acquire * @param reqid reqid of the policy to acquire
* @param src_ts source traffic selector * @param data data from the acquire
* @param dst_ts destination traffic selector
* @return TRUE to remain registered, FALSE to unregister * @return TRUE to remain registered, FALSE to unregister
*/ */
bool (*acquire)(kernel_listener_t *this, uint32_t reqid, bool (*acquire)(kernel_listener_t *this, uint32_t reqid,
traffic_selector_t *src_ts, traffic_selector_t *dst_ts); kernel_acquire_data_t *data);
/** /**
* Hook called if an expire event for an IPsec SA is received. * Hook called if an expire event for an IPsec SA is received.
@@ -577,6 +577,9 @@ struct policy_entry_t {
/** Optional interface ID */ /** Optional interface ID */
uint32_t if_id; uint32_t if_id;
/** Optional security label */
sec_label_t *label;
/** Associated route installed for this policy */ /** Associated route installed for this policy */
route_entry_t *route; route_entry_t *route;
@@ -609,6 +612,7 @@ static void policy_entry_destroy(private_kernel_netlink_ipsec_t *this,
policy->direction, this); policy->direction, this);
policy->used_by->destroy(policy->used_by); policy->used_by->destroy(policy->used_by);
} }
DESTROY_IF(policy->label);
free(policy); free(policy);
} }
@@ -618,8 +622,15 @@ static void policy_entry_destroy(private_kernel_netlink_ipsec_t *this,
static u_int policy_hash(policy_entry_t *key) static u_int policy_hash(policy_entry_t *key)
{ {
chunk_t chunk = chunk_from_thing(key->sel); chunk_t chunk = chunk_from_thing(key->sel);
return chunk_hash_inc(chunk, chunk_hash_inc(chunk_from_thing(key->mark), u_int hash;
hash = chunk_hash_inc(chunk, chunk_hash_inc(chunk_from_thing(key->mark),
chunk_hash(chunk_from_thing(key->if_id)))); chunk_hash(chunk_from_thing(key->if_id))));
if (key->label)
{
hash = key->label->hash(key->label, hash);
}
return hash;
} }
/** /**
@@ -630,7 +641,8 @@ static bool policy_equals(policy_entry_t *key, policy_entry_t *other_key)
return memeq(&key->sel, &other_key->sel, sizeof(struct xfrm_selector)) && return memeq(&key->sel, &other_key->sel, sizeof(struct xfrm_selector)) &&
key->mark == other_key->mark && key->mark == other_key->mark &&
key->if_id == other_key->if_id && key->if_id == other_key->if_id &&
key->direction == other_key->direction; key->direction == other_key->direction &&
sec_labels_equal(key->label, other_key->label);
} }
/** /**
@@ -897,11 +909,13 @@ static void process_acquire(private_kernel_netlink_ipsec_t *this,
struct xfrm_user_acquire *acquire; struct xfrm_user_acquire *acquire;
struct rtattr *rta; struct rtattr *rta;
size_t rtasize; size_t rtasize;
traffic_selector_t *src_ts, *dst_ts; kernel_acquire_data_t data = {};
chunk_t label = chunk_empty;
uint32_t reqid = 0; uint32_t reqid = 0;
int proto = 0; uint8_t proto;
acquire = NLMSG_DATA(hdr); acquire = NLMSG_DATA(hdr);
proto = acquire->id.proto;
rta = XFRM_RTA(hdr, struct xfrm_user_acquire); rta = XFRM_RTA(hdr, struct xfrm_user_acquire);
rtasize = XFRM_PAYLOAD(hdr, struct xfrm_user_acquire); rtasize = XFRM_PAYLOAD(hdr, struct xfrm_user_acquire);
@@ -913,11 +927,21 @@ static void process_acquire(private_kernel_netlink_ipsec_t *this,
if (rta->rta_type == XFRMA_TMPL) if (rta->rta_type == XFRMA_TMPL)
{ {
struct xfrm_user_tmpl* tmpl; struct xfrm_user_tmpl* tmpl = RTA_DATA(rta);
tmpl = (struct xfrm_user_tmpl*)RTA_DATA(rta);
reqid = tmpl->reqid; reqid = tmpl->reqid;
proto = tmpl->id.proto;
} }
#ifdef USE_SELINUX
if (rta->rta_type == XFRMA_SEC_CTX)
{
struct xfrm_user_sec_ctx *ctx = RTA_DATA(rta);
if (ctx->ctx_doi == XFRM_SC_DOI_LSM &&
ctx->ctx_alg == XFRM_SC_ALG_SELINUX)
{
label = chunk_create((void*)(ctx + 1), ctx->ctx_len);
}
}
#endif
rta = RTA_NEXT(rta, rtasize); rta = RTA_NEXT(rta, rtasize);
} }
switch (proto) switch (proto)
@@ -928,12 +952,18 @@ static void process_acquire(private_kernel_netlink_ipsec_t *this,
break; break;
default: default:
/* acquire for AH/ESP only, not for IPCOMP */ /* acquire for AH/ESP only, not for IPCOMP */
return; return;
} }
src_ts = selector2ts(&acquire->sel, TRUE); data.src = selector2ts(&acquire->sel, TRUE);
dst_ts = selector2ts(&acquire->sel, FALSE); data.dst = selector2ts(&acquire->sel, FALSE);
data.label = label.len ? sec_label_from_encoding(label) : NULL;
charon->kernel->acquire(charon->kernel, reqid, src_ts, dst_ts); charon->kernel->acquire(charon->kernel, reqid, &data);
DESTROY_IF(data.src);
DESTROY_IF(data.dst);
DESTROY_IF(data.label);
} }
/** /**
@@ -1283,6 +1313,47 @@ static bool add_mark(struct nlmsghdr *hdr, int buflen, mark_t mark)
return TRUE; return TRUE;
} }
/**
* Format the security label for debug messages
*/
static void format_label(char *buf, int buflen, sec_label_t *label)
{
if (label)
{
snprintf(buf, buflen, " (ctx %s)", label->get_string(label));
}
}
/**
* Add a security label to message if required
*/
static bool add_label(struct nlmsghdr *hdr, int buflen, sec_label_t *label)
{
if (label)
{
#ifdef USE_SELINUX
struct xfrm_user_sec_ctx *ctx;
chunk_t enc = label->get_encoding(label);
int len = sizeof(*ctx) + enc.len;
ctx = netlink_reserve(hdr, buflen, XFRMA_SEC_CTX, len);
if (!ctx)
{
return FALSE;
}
/* this attribute for some reason duplicates the generic header */
ctx->exttype = XFRMA_SEC_CTX;
ctx->len = len;
ctx->ctx_doi = XFRM_SC_DOI_LSM;
ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
ctx->ctx_len = enc.len;
memcpy((void*)(ctx + 1), enc.ptr, enc.len);
#endif
}
return TRUE;
}
/** /**
* Add a uint32 attribute to message * Add a uint32 attribute to message
*/ */
@@ -1871,6 +1942,11 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
goto failed; goto failed;
} }
if (!add_label(hdr, sizeof(request), data->label))
{
goto failed;
}
if (ipcomp == IPCOMP_NONE && (data->mark.value | data->mark.mask)) if (ipcomp == IPCOMP_NONE && (data->mark.value | data->mark.mask))
{ {
if (!add_uint32(hdr, sizeof(request), XFRMA_SET_MARK, if (!add_uint32(hdr, sizeof(request), XFRMA_SET_MARK,
@@ -2268,6 +2344,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
kernel_ipsec_update_sa_t ipcomp = { kernel_ipsec_update_sa_t ipcomp = {
.new_src = data->new_src, .new_src = data->new_src,
.new_dst = data->new_dst, .new_dst = data->new_dst,
.new_reqid = data->new_reqid,
}; };
update_sa(this, &ipcomp_id, &ipcomp); update_sa(this, &ipcomp_id, &ipcomp);
} }
@@ -2356,6 +2433,10 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
sa = NLMSG_DATA(hdr); sa = NLMSG_DATA(hdr);
memcpy(sa, NLMSG_DATA(out_hdr), sizeof(struct xfrm_usersa_info)); memcpy(sa, NLMSG_DATA(out_hdr), sizeof(struct xfrm_usersa_info));
sa->family = data->new_dst->get_family(data->new_dst); sa->family = data->new_dst->get_family(data->new_dst);
if (data->new_reqid)
{
sa->reqid = data->new_reqid;
}
if (!id->src->ip_equals(id->src, data->new_src)) if (!id->src->ip_equals(id->src, data->new_src))
{ {
@@ -2744,7 +2825,9 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this,
} }
tmpl->reqid = ipsec->cfg.reqid; tmpl->reqid = ipsec->cfg.reqid;
tmpl->id.proto = protos[i].proto; tmpl->id.proto = protos[i].proto;
if (policy->direction == POLICY_OUT) /* in order to match SAs with all matching labels, we can't have the
* SPI in the template */
if (policy->direction == POLICY_OUT && !policy->label)
{ {
tmpl->id.spi = protos[i].spi; tmpl->id.spi = protos[i].spi;
} }
@@ -2778,6 +2861,11 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this,
policy_change_done(this, policy); policy_change_done(this, policy);
return FAILED; return FAILED;
} }
if (!add_label(hdr, sizeof(request), policy->label))
{
policy_change_done(this, policy);
return FAILED;
}
this->mutex->unlock(this->mutex); this->mutex->unlock(this->mutex);
status = this->socket_xfrm->send_ack(this->socket_xfrm, hdr); status = this->socket_xfrm->send_ack(this->socket_xfrm, hdr);
@@ -2823,7 +2911,7 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
policy_sa_t *assigned_sa, *current_sa; policy_sa_t *assigned_sa, *current_sa;
enumerator_t *enumerator; enumerator_t *enumerator;
bool found = FALSE, update = TRUE; bool found = FALSE, update = TRUE;
char markstr[32] = ""; char markstr[32] = "", labelstr[128] = "";
uint32_t cur_priority = 0; uint32_t cur_priority = 0;
int use_count; int use_count;
@@ -2832,31 +2920,21 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
.sel = ts2selector(id->src_ts, id->dst_ts, id->interface), .sel = ts2selector(id->src_ts, id->dst_ts, id->interface),
.mark = id->mark.value & id->mark.mask, .mark = id->mark.value & id->mark.mask,
.if_id = id->if_id, .if_id = id->if_id,
.label = id->label ? id->label->clone(id->label) : NULL,
.direction = id->dir, .direction = id->dir,
.reqid = data->sa->reqid, .reqid = data->sa->reqid,
); );
format_mark(markstr, sizeof(markstr), id->mark); format_mark(markstr, sizeof(markstr), id->mark);
format_label(labelstr, sizeof(labelstr), id->label);
/* find the policy, which matches EXACTLY */ /* find the policy, which matches EXACTLY */
this->mutex->lock(this->mutex); this->mutex->lock(this->mutex);
current = this->policies->get(this->policies, policy); current = this->policies->get(this->policies, policy);
if (current) if (current)
{ { /* use existing policy */
if (current->reqid && data->sa->reqid && DBG2(DBG_KNL, "policy %R === %R %N%s%s already exists, increasing "
current->reqid != data->sa->reqid)
{
DBG1(DBG_CFG, "unable to install policy %R === %R %N%s for reqid "
"%u, the same policy for reqid %u exists",
id->src_ts, id->dst_ts, policy_dir_names, id->dir, markstr,
data->sa->reqid, current->reqid);
policy_entry_destroy(this, policy);
this->mutex->unlock(this->mutex);
return INVALID_STATE;
}
/* use existing policy */
DBG2(DBG_KNL, "policy %R === %R %N%s already exists, increasing "
"refcount", id->src_ts, id->dst_ts, policy_dir_names, id->dir, "refcount", id->src_ts, id->dst_ts, policy_dir_names, id->dir,
markstr); markstr, labelstr);
policy_entry_destroy(this, policy); policy_entry_destroy(this, policy);
policy = current; policy = current;
found = TRUE; found = TRUE;
@@ -2920,27 +2998,34 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
{ /* we don't update the policy if the priority is lower than that of { /* we don't update the policy if the priority is lower than that of
* the currently installed one */ * the currently installed one */
policy_change_done(this, policy); policy_change_done(this, policy);
DBG2(DBG_KNL, "not updating policy %R === %R %N%s [priority %u, " DBG2(DBG_KNL, "not updating policy %R === %R %N%s%s [priority %u, "
"refcount %d]", id->src_ts, id->dst_ts, policy_dir_names, "refcount %d]", id->src_ts, id->dst_ts, policy_dir_names,
id->dir, markstr, cur_priority, use_count); id->dir, markstr, labelstr, cur_priority, use_count);
return SUCCESS; return SUCCESS;
} }
policy->reqid = assigned_sa->sa->cfg.reqid; if (policy->reqid != assigned_sa->sa->cfg.reqid)
{
DBG1(DBG_CFG, "updating reqid for policy %R === %R %N%s%s from %u "
"to %u", id->src_ts, id->dst_ts, policy_dir_names, id->dir,
markstr, labelstr, policy->reqid, assigned_sa->sa->cfg.reqid);
policy->reqid = assigned_sa->sa->cfg.reqid;
}
if (this->policy_update) if (this->policy_update)
{ {
found = TRUE; found = TRUE;
} }
DBG2(DBG_KNL, "%s policy %R === %R %N%s [priority %u, refcount %d]", DBG2(DBG_KNL, "%s policy %R === %R %N%s%s [priority %u, refcount %d]",
found ? "updating" : "adding", id->src_ts, id->dst_ts, found ? "updating" : "adding", id->src_ts, id->dst_ts,
policy_dir_names, id->dir, markstr, assigned_sa->priority, use_count); policy_dir_names, id->dir, markstr, labelstr, assigned_sa->priority,
use_count);
if (add_policy_internal(this, policy, assigned_sa, found) != SUCCESS) if (add_policy_internal(this, policy, assigned_sa, found) != SUCCESS)
{ {
DBG1(DBG_KNL, "unable to %s policy %R === %R %N%s", DBG1(DBG_KNL, "unable to %s policy %R === %R %N%s%s",
found ? "update" : "add", id->src_ts, id->dst_ts, found ? "update" : "add", id->src_ts, id->dst_ts,
policy_dir_names, id->dir, markstr); policy_dir_names, id->dir, markstr, labelstr);
return FAILED; return FAILED;
} }
return SUCCESS; return SUCCESS;
@@ -2955,13 +3040,14 @@ METHOD(kernel_ipsec_t, query_policy, status_t,
struct xfrm_userpolicy_id *policy_id; struct xfrm_userpolicy_id *policy_id;
struct xfrm_userpolicy_info *policy = NULL; struct xfrm_userpolicy_info *policy = NULL;
size_t len; size_t len;
char markstr[32] = ""; char markstr[32] = "", labelstr[128] = "";
memset(&request, 0, sizeof(request)); memset(&request, 0, sizeof(request));
format_mark(markstr, sizeof(markstr), id->mark); format_mark(markstr, sizeof(markstr), id->mark);
format_label(labelstr, sizeof(labelstr), id->label);
DBG2(DBG_KNL, "querying policy %R === %R %N%s", id->src_ts, id->dst_ts, DBG2(DBG_KNL, "querying policy %R === %R %N%s%s", id->src_ts, id->dst_ts,
policy_dir_names, id->dir, markstr); policy_dir_names, id->dir, markstr, labelstr);
hdr = &request.hdr; hdr = &request.hdr;
hdr->nlmsg_flags = NLM_F_REQUEST; hdr->nlmsg_flags = NLM_F_REQUEST;
@@ -2980,6 +3066,10 @@ METHOD(kernel_ipsec_t, query_policy, status_t,
{ {
return FAILED; return FAILED;
} }
if (!add_label(hdr, sizeof(request), id->label))
{
return FAILED;
}
if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS)
{ {
@@ -3051,20 +3141,22 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
.if_id = id->if_id, .if_id = id->if_id,
.cfg = *data->sa, .cfg = *data->sa,
}; };
char markstr[32] = ""; char markstr[32] = "", labelstr[128] = "";
int use_count; int use_count;
status_t status = SUCCESS; status_t status = SUCCESS;
format_mark(markstr, sizeof(markstr), id->mark); format_mark(markstr, sizeof(markstr), id->mark);
format_label(labelstr, sizeof(labelstr), id->label);
DBG2(DBG_KNL, "deleting policy %R === %R %N%s", id->src_ts, id->dst_ts, DBG2(DBG_KNL, "deleting policy %R === %R %N%s%s", id->src_ts, id->dst_ts,
policy_dir_names, id->dir, markstr); policy_dir_names, id->dir, markstr, labelstr);
/* create a policy */ /* create a policy */
memset(&policy, 0, sizeof(policy_entry_t)); memset(&policy, 0, sizeof(policy_entry_t));
policy.sel = ts2selector(id->src_ts, id->dst_ts, id->interface); policy.sel = ts2selector(id->src_ts, id->dst_ts, id->interface);
policy.mark = id->mark.value & id->mark.mask; policy.mark = id->mark.value & id->mark.mask;
policy.if_id = id->if_id; policy.if_id = id->if_id;
policy.label = id->label;
policy.direction = id->dir; policy.direction = id->dir;
/* find the policy */ /* find the policy */
@@ -3072,8 +3164,9 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
current = this->policies->get(this->policies, &policy); current = this->policies->get(this->policies, &policy);
if (!current) if (!current)
{ {
DBG1(DBG_KNL, "deleting policy %R === %R %N%s failed, not found", DBG1(DBG_KNL, "deleting policy %R === %R %N%s%s failed, not found",
id->src_ts, id->dst_ts, policy_dir_names, id->dir, markstr); id->src_ts, id->dst_ts, policy_dir_names, id->dir, markstr,
labelstr);
this->mutex->unlock(this->mutex); this->mutex->unlock(this->mutex);
return NOT_FOUND; return NOT_FOUND;
} }
@@ -3086,7 +3179,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
current->waiting--; current->waiting--;
/* remove mapping to SA by reqid and priority */ /* remove mapping to SA by reqid and priority */
auto_priority = get_priority(current, data->prio,id->interface); auto_priority = get_priority(current, data->prio, id->interface);
priority = this->get_priority ? this->get_priority(id, data) priority = this->get_priority ? this->get_priority(id, data)
: data->manual_prio; : data->manual_prio;
priority = priority ?: auto_priority; priority = priority ?: auto_priority;
@@ -3118,22 +3211,29 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
if (!is_installed) if (!is_installed)
{ /* no need to update as the policy was not installed for this SA */ { /* no need to update as the policy was not installed for this SA */
policy_change_done(this, current); policy_change_done(this, current);
DBG2(DBG_KNL, "not updating policy %R === %R %N%s [priority %u, " DBG2(DBG_KNL, "not updating policy %R === %R %N%s%s [priority %u, "
"refcount %d]", id->src_ts, id->dst_ts, policy_dir_names, "refcount %d]", id->src_ts, id->dst_ts, policy_dir_names,
id->dir, markstr, cur_priority, use_count); id->dir, markstr, labelstr, cur_priority, use_count);
return SUCCESS; return SUCCESS;
} }
current->used_by->get_first(current->used_by, (void**)&mapping); current->used_by->get_first(current->used_by, (void**)&mapping);
current->reqid = mapping->sa->cfg.reqid; if (current->reqid != mapping->sa->cfg.reqid)
{
DBG1(DBG_CFG, "updating reqid for policy %R === %R %N%s%s from %u "
"to %u", id->src_ts, id->dst_ts, policy_dir_names, id->dir,
markstr, labelstr, current->reqid, mapping->sa->cfg.reqid);
current->reqid = mapping->sa->cfg.reqid;
}
DBG2(DBG_KNL, "updating policy %R === %R %N%s [priority %u, " DBG2(DBG_KNL, "updating policy %R === %R %N%s%s [priority %u, "
"refcount %d]", id->src_ts, id->dst_ts, policy_dir_names, id->dir, "refcount %d]", id->src_ts, id->dst_ts, policy_dir_names, id->dir,
markstr, mapping->priority, use_count); markstr, labelstr, mapping->priority, use_count);
if (add_policy_internal(this, current, mapping, TRUE) != SUCCESS) if (add_policy_internal(this, current, mapping, TRUE) != SUCCESS)
{ {
DBG1(DBG_KNL, "unable to update policy %R === %R %N%s", DBG1(DBG_KNL, "unable to update policy %R === %R %N%s%s",
id->src_ts, id->dst_ts, policy_dir_names, id->dir, markstr); id->src_ts, id->dst_ts, policy_dir_names, id->dir, markstr,
labelstr);
return FAILED; return FAILED;
} }
return SUCCESS; return SUCCESS;
@@ -3160,6 +3260,11 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
policy_change_done(this, current); policy_change_done(this, current);
return FAILED; return FAILED;
} }
if (!add_label(hdr, sizeof(request), id->label))
{
policy_change_done(this, current);
return FAILED;
}
if (current->route) if (current->route)
{ {
@@ -3170,16 +3275,16 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
route->pass) != SUCCESS) route->pass) != SUCCESS)
{ {
DBG1(DBG_KNL, "error uninstalling route installed with policy " DBG1(DBG_KNL, "error uninstalling route installed with policy "
"%R === %R %N%s", id->src_ts, id->dst_ts, policy_dir_names, "%R === %R %N%s%s", id->src_ts, id->dst_ts, policy_dir_names,
id->dir, markstr); id->dir, markstr, labelstr);
} }
} }
this->mutex->unlock(this->mutex); this->mutex->unlock(this->mutex);
if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS) if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS)
{ {
DBG1(DBG_KNL, "unable to delete policy %R === %R %N%s", id->src_ts, DBG1(DBG_KNL, "unable to delete policy %R === %R %N%s%s", id->src_ts,
id->dst_ts, policy_dir_names, id->dir, markstr); id->dst_ts, policy_dir_names, id->dir, markstr, labelstr);
status = FAILED; status = FAILED;
} }
@@ -1318,8 +1318,8 @@ static void process_acquire(private_kernel_pfkey_ipsec_t *this,
struct sadb_msg* msg) struct sadb_msg* msg)
{ {
pfkey_msg_t response; pfkey_msg_t response;
kernel_acquire_data_t data = {};
uint32_t index, reqid = 0; uint32_t index, reqid = 0;
traffic_selector_t *src_ts, *dst_ts;
policy_entry_t *policy; policy_entry_t *policy;
policy_sa_t *sa; policy_sa_t *sa;
@@ -1363,10 +1363,16 @@ static void process_acquire(private_kernel_pfkey_ipsec_t *this,
this->mutex->unlock(this->mutex); this->mutex->unlock(this->mutex);
} }
src_ts = sadb_address2ts(response.src); if (reqid)
dst_ts = sadb_address2ts(response.dst); {
data.src = sadb_address2ts(response.src);
data.dst = sadb_address2ts(response.dst);
charon->kernel->acquire(charon->kernel, reqid, src_ts, dst_ts); charon->kernel->acquire(charon->kernel, reqid, &data);
data.src->destroy(data.src);
data.dst->destroy(data.dst);
}
} }
/** /**
@@ -1954,6 +1960,12 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
size_t len; size_t len;
status_t status = FAILED; status_t status = FAILED;
if (data->new_reqid)
{
DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x: reqid "
"change is not supported", ntohl(id->spi));
return NOT_SUPPORTED;
}
#ifndef SADB_X_EXT_NEW_ADDRESS_SRC #ifndef SADB_X_EXT_NEW_ADDRESS_SRC
/* we can't update the SA if any of the ip addresses have changed. /* we can't update the SA if any of the ip addresses have changed.
* that's because we can't use SADB_UPDATE and by deleting and readding the * that's because we can't use SADB_UPDATE and by deleting and readding the
@@ -820,6 +820,8 @@ static bool install_sps(private_kernel_wfp_ipsec_t *this,
case TS_IPV6_ADDR_RANGE: case TS_IPV6_ADDR_RANGE:
has_v6 = TRUE; has_v6 = TRUE;
break; break;
default:
continue;
} }
/* inbound policy */ /* inbound policy */
@@ -1634,6 +1636,7 @@ static u_int hash_trap(trap_t *trap)
static void acquire(private_kernel_wfp_ipsec_t *this, UINT64 filter_id, static void acquire(private_kernel_wfp_ipsec_t *this, UINT64 filter_id,
traffic_selector_t *src, traffic_selector_t *dst) traffic_selector_t *src, traffic_selector_t *dst)
{ {
kernel_acquire_data_t data = {};
uint32_t reqid = 0; uint32_t reqid = 0;
trap_t *trap, key = { trap_t *trap, key = {
.filter_id = filter_id, .filter_id = filter_id,
@@ -1649,9 +1652,13 @@ static void acquire(private_kernel_wfp_ipsec_t *this, UINT64 filter_id,
if (reqid) if (reqid)
{ {
src = src ? src->clone(src) : NULL; data.src = src ? src->clone(src) : NULL;
dst = dst ? dst->clone(dst) : NULL; data.dst = dst ? dst->clone(dst) : NULL;
charon->kernel->acquire(charon->kernel, reqid, src, dst);
charon->kernel->acquire(charon->kernel, reqid, &data);
DESTROY_IF(data.src);
DESTROY_IF(data.dst);
} }
} }
@@ -2219,8 +2226,8 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
{ {
/* inbound entry, do update */ /* inbound entry, do update */
sa_id = entry->sa_id; sa_id = entry->sa_id;
ports.localUdpEncapPort = entry->local->get_port(entry->local); ports.localUdpEncapPort = data->new_dst->get_port(data->new_dst);
ports.remoteUdpEncapPort = entry->remote->get_port(entry->remote); ports.remoteUdpEncapPort = data->new_src->get_port(data->new_src);
} }
this->mutex->unlock(this->mutex); this->mutex->unlock(this->mutex);
@@ -2273,6 +2280,10 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
key.dst = entry->osa.dst; key.dst = entry->osa.dst;
this->osas->remove(this->osas, &key); this->osas->remove(this->osas, &key);
if (data->new_reqid)
{
entry->reqid = data->new_reqid;
}
entry->local->destroy(entry->local); entry->local->destroy(entry->local);
entry->remote->destroy(entry->remote); entry->remote->destroy(entry->remote);
entry->local = data->new_dst->clone(data->new_dst); entry->local = data->new_dst->clone(data->new_dst);
+18
View File
@@ -0,0 +1,18 @@
AM_CPPFLAGS = \
-I$(top_srcdir)/src/libstrongswan \
-I$(top_srcdir)/src/libcharon
AM_CFLAGS = \
$(PLUGIN_CFLAGS)
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-selinux.la
else
plugin_LTLIBRARIES = libstrongswan-selinux.la
endif
libstrongswan_selinux_la_SOURCES = \
selinux_plugin.h selinux_plugin.c \
selinux_listener.h selinux_listener.c
libstrongswan_selinux_la_LDFLAGS = -module -avoid-version
@@ -0,0 +1,261 @@
/*
* Copyright (C) 2022 Tobias Brunner, codelabs GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "selinux_listener.h"
#include <daemon.h>
#include <collections/array.h>
#include <collections/hashtable.h>
typedef struct private_selinux_listener_t private_selinux_listener_t;
/**
* Private data.
*/
struct private_selinux_listener_t {
/**
* Public interface.
*/
selinux_listener_t public;
/**
* IKE_SAs with attached trap policies, ike_sa_id_t => entry_t.
*/
hashtable_t *sas;
};
/**
* Entry to keep track of trap policies.
*/
typedef struct {
/**
* IKE_SA ID.
*/
ike_sa_id_t *id;
/**
* Installed trap policies.
*/
array_t *traps;
} entry_t;
/**
* Destroy the given entry.
*/
static void destroy_entry(entry_t *entry)
{
entry->id->destroy(entry->id);
array_destroy(entry->traps);
free(entry);
}
/**
* Hashtable hash function
*/
static u_int hash(const void *key)
{
ike_sa_id_t *id = (ike_sa_id_t*)key;
uint64_t spi_i = id->get_initiator_spi(id),
spi_r = id->get_responder_spi(id);
return chunk_hash_inc(chunk_from_thing(spi_i),
chunk_hash(chunk_from_thing(spi_r)));
}
/**
* Hashtable equals function
*/
static bool equals(const void *a_pub, const void *b)
{
ike_sa_id_t *a = (ike_sa_id_t*)a_pub;
return a->equals(a, (ike_sa_id_t*)b);
}
/**
* Install a trap policy for the generic SELinux label.
*/
static bool install_generic_trap(ike_sa_t *ike_sa, child_sa_t *child_sa)
{
linked_list_t *local, *remote;
sec_label_t *label;
bool success;
label = child_sa->get_label(child_sa);
DBG1(DBG_IKE, "installing trap %s{%d} with generic security label '%s'",
child_sa->get_name(child_sa), child_sa->get_unique_id(child_sa),
label->get_string(label));
local = ike_sa_get_dynamic_hosts(ike_sa, TRUE);
remote = ike_sa_get_dynamic_hosts(ike_sa, FALSE);
success = charon->traps->install_external(charon->traps,
ike_sa->get_peer_cfg(ike_sa),
child_sa, local, remote);
local->destroy(local);
remote->destroy(remote);
return success;
}
METHOD(listener_t, ike_updown, bool,
private_selinux_listener_t *this, ike_sa_t *ike_sa, bool up)
{
enumerator_t *enumerator;
peer_cfg_t *peer_cfg;
child_cfg_t *child_cfg;
child_sa_t *child_sa;
entry_t *entry;
if (up)
{
child_sa_create_t child = {
.if_id_in_def = ike_sa->get_if_id(ike_sa, TRUE),
.if_id_out_def = ike_sa->get_if_id(ike_sa, FALSE),
};
INIT(entry,
.id = ike_sa->get_id(ike_sa),
);
entry->id = entry->id->clone(entry->id);
peer_cfg = ike_sa->get_peer_cfg(ike_sa);
enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
while (enumerator->enumerate(enumerator, &child_cfg))
{
if (child_cfg->get_label(child_cfg) &&
child_cfg->get_label_mode(child_cfg) == SEC_LABEL_MODE_SELINUX)
{
child_sa = child_sa_create(ike_sa->get_my_host(ike_sa),
ike_sa->get_other_host(ike_sa),
child_cfg, &child);
if (install_generic_trap(ike_sa, child_sa))
{
array_insert_create(&entry->traps, ARRAY_TAIL, child_sa);
}
else
{
child_sa->destroy(child_sa);
}
}
}
enumerator->destroy(enumerator);
if (array_count(entry->traps))
{
this->sas->put(this->sas, entry->id, entry);
}
else
{
destroy_entry(entry);
}
}
else
{
entry = this->sas->remove(this->sas, ike_sa->get_id(ike_sa));
if (entry)
{
while (array_remove(entry->traps, ARRAY_TAIL, &child_sa))
{
sec_label_t *label = child_sa->get_label(child_sa);
DBG1(DBG_IKE, "uninstalling trap %s{%d} with generic security "
"label '%s'", child_sa->get_name(child_sa),
child_sa->get_unique_id(child_sa),
label->get_string(label));
charon->traps->remove_external(charon->traps, child_sa);
child_sa->destroy(child_sa);
}
destroy_entry(entry);
}
}
return TRUE;
}
METHOD(listener_t, ike_rekey, bool,
private_selinux_listener_t *this, ike_sa_t *old, ike_sa_t *new)
{
entry_t *entry;
entry = this->sas->remove(this->sas, old->get_id(old));
if (entry)
{
entry->id->destroy(entry->id);
entry->id = new->get_id(new);
entry->id = entry->id->clone(entry->id);
this->sas->put(this->sas, entry->id, entry);
}
return TRUE;
}
METHOD(listener_t, ike_update, bool,
private_selinux_listener_t *this, ike_sa_t *ike_sa,
host_t *local, host_t *remote)
{
entry_t *entry;
child_sa_t *child_sa;
linked_list_t *vips;
int i;
entry = this->sas->get(this->sas, ike_sa->get_id(ike_sa));
if (entry)
{
vips = linked_list_create_from_enumerator(
ike_sa->create_virtual_ip_enumerator(ike_sa, local));
for (i = 0; i < array_count(entry->traps); i++)
{
array_get(entry->traps, i, &child_sa);
child_sa->update(child_sa, local, remote, vips,
ike_sa->has_condition(ike_sa, COND_NAT_ANY));
}
vips->destroy(vips);
}
return TRUE;
}
METHOD(selinux_listener_t, destroy, void,
private_selinux_listener_t *this)
{
this->sas->destroy(this->sas);
free(this);
}
/*
* Described in header
*/
selinux_listener_t *selinux_listener_create()
{
private_selinux_listener_t *this;
INIT(this,
.public = {
.listener = {
.ike_updown = _ike_updown,
.ike_rekey = _ike_rekey,
.ike_update = _ike_update,
},
.destroy = _destroy,
},
.sas = hashtable_create(hash, equals, 32),
);
return &this->public;
}
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2022 Tobias Brunner, codelabs GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* @defgroup selinux_listener selinux_listener
* @{ @ingroup selinux
*/
#ifndef SELINUX_LISTENER_H_
#define SELINUX_LISTENER_H_
#include <bus/listeners/listener.h>
typedef struct selinux_listener_t selinux_listener_t;
/**
* Listener to manage trap policies for generic SELinux labels.
*/
struct selinux_listener_t {
/**
* Implements listener_t interface.
*/
listener_t listener;
/**
* Destroy a selinux_listener_t.
*/
void (*destroy)(selinux_listener_t *this);
};
/**
* Create a listener instance.
*/
selinux_listener_t *selinux_listener_create();
#endif /** SELINUX_LISTENER_H_ @}*/
@@ -0,0 +1,106 @@
/*
* Copyright (C) 2022 Tobias Brunner, codelabs GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "selinux_plugin.h"
#include "selinux_listener.h"
#include <daemon.h>
typedef struct private_selinux_plugin_t private_selinux_plugin_t;
/**
* Private data
*/
struct private_selinux_plugin_t {
/**
* Public interface
*/
selinux_plugin_t public;
/**
* Listener
*/
selinux_listener_t *listener;
};
METHOD(plugin_t, get_name, char*,
private_selinux_plugin_t *this)
{
return "selinux";
}
/**
* Register handler
*/
static bool plugin_cb(private_selinux_plugin_t *this,
plugin_feature_t *feature, bool reg, void *cb_data)
{
if (reg)
{
charon->bus->add_listener(charon->bus, &this->listener->listener);
}
else
{
charon->bus->remove_listener(charon->bus, &this->listener->listener);
}
return TRUE;
}
METHOD(plugin_t, get_features, int,
private_selinux_plugin_t *this, plugin_feature_t *features[])
{
static plugin_feature_t f[] = {
PLUGIN_CALLBACK((plugin_feature_callback_t)plugin_cb, NULL),
PLUGIN_PROVIDE(CUSTOM, "selinux"),
};
*features = f;
return countof(f);
}
METHOD(plugin_t, destroy, void,
private_selinux_plugin_t *this)
{
this->listener->destroy(this->listener);
free(this);
}
/*
* Described in header
*/
plugin_t *selinux_plugin_create()
{
private_selinux_plugin_t *this;
INIT(this,
.public = {
.plugin = {
.get_name = _get_name,
.get_features = _get_features,
.destroy = _destroy,
},
},
.listener = selinux_listener_create(),
);
return &this->public.plugin;
}
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2022 Tobias Brunner, codelabs GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* @defgroup selinux selinux
* @ingroup cplugins
*
* @defgroup selinux_plugin selinux_plugin
* @{ @ingroup selinux
*/
#ifndef SELINUX_PLUGIN_H_
#define SELINUX_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct selinux_plugin_t selinux_plugin_t;
/**
* Plugin managing trap policies with generic SELinux labels.
*/
struct selinux_plugin_t {
/**
* Implements plugin interface
*/
plugin_t plugin;
};
#endif /** SELINUX_PLUGIN_H_ @}*/
+2 -2
View File
@@ -1042,9 +1042,9 @@ static action_t map_action(int starter_action)
switch (starter_action) switch (starter_action)
{ {
case 2: /* =hold */ case 2: /* =hold */
return ACTION_ROUTE; return ACTION_TRAP;
case 3: /* =restart */ case 3: /* =restart */
return ACTION_RESTART; return ACTION_START;
default: default:
return ACTION_NONE; return ACTION_NONE;
} }
+7 -2
View File
@@ -277,7 +277,7 @@ Terminates an SA while streaming _control-log_ events.
{ {
child = <terminate a CHILD_SA by configuration name> child = <terminate a CHILD_SA by configuration name>
ike = <terminate an IKE_SA by configuration name> ike = <terminate an IKE_SA by configuration name>
child-id = <terminate a CHILD_SA by its reqid> child-id = <terminate a CHILD_SA by its unique id>
ike-id = <terminate an IKE_SA by its unique id> ike-id = <terminate an IKE_SA by its unique id>
force = <terminate IKE_SA without waiting for proper DELETE, if timeout force = <terminate IKE_SA without waiting for proper DELETE, if timeout
is given, waits for a response until it is reached> is given, waits for a response until it is reached>
@@ -300,7 +300,7 @@ Initiate the rekeying of an SA.
{ {
child = <rekey a CHILD_SA by configuration name> child = <rekey a CHILD_SA by configuration name>
ike = <rekey an IKE_SA by configuration name> ike = <rekey an IKE_SA by configuration name>
child-id = <rekey a CHILD_SA by its reqid> child-id = <rekey a CHILD_SA by its unique id>
ike-id = <rekey an IKE_SA by its unique id> ike-id = <rekey an IKE_SA by its unique id>
reauth = <reauthenticate instead of rekey an IKEv2 SA> reauth = <reauthenticate instead of rekey an IKEv2 SA>
} => { } => {
@@ -361,6 +361,8 @@ events.
noblock = <use non-blocking mode if key is set> noblock = <use non-blocking mode if key is set>
ike = <filter listed IKE_SAs by its name> ike = <filter listed IKE_SAs by its name>
ike-id = <filter listed IKE_SA by its unique id> ike-id = <filter listed IKE_SA by its unique id>
child = <filter listed CHILD_SAs by name>
child-id = <filter listed CHILD_SAs by unique id>
} => { } => {
# completes after streaming list-sa events # completes after streaming list-sa events
} }
@@ -817,6 +819,7 @@ command.
mark-mask-out = <hex encoded outbound Netfilter mark mask> mark-mask-out = <hex encoded outbound Netfilter mark mask>
if-id-in = <hex encoded inbound XFRM interface ID> if-id-in = <hex encoded inbound XFRM interface ID>
if-id-out = <hex encoded outbound XFRM interface ID> if-id-out = <hex encoded outbound XFRM interface ID>
label = <hex encoded security label>
encr-alg = <ESP encryption algorithm name, if any> encr-alg = <ESP encryption algorithm name, if any>
encr-keysize = <ESP encryption key size, if applicable> encr-keysize = <ESP encryption key size, if applicable>
integ-alg = <ESP or AH integrity algorithm name, if any> integ-alg = <ESP or AH integrity algorithm name, if any>
@@ -854,6 +857,7 @@ _list-policies_ command.
child = <CHILD_SA configuration name> child = <CHILD_SA configuration name>
ike = <IKE_SA configuration name or namespace, if available> ike = <IKE_SA configuration name or namespace, if available>
mode = <policy mode, tunnel|transport|pass|drop> mode = <policy mode, tunnel|transport|pass|drop>
label = <hex encoded security label>
local-ts = [ local-ts = [
<list of local traffic selectors> <list of local traffic selectors>
] ]
@@ -903,6 +907,7 @@ _list-conns_ command.
children = { children = {
<CHILD_SA config name>* = { <CHILD_SA config name>* = {
mode = <IPsec mode> mode = <IPsec mode>
label = <hex encoded security label>
rekey_time = <CHILD_SA rekeying interval in seconds> rekey_time = <CHILD_SA rekeying interval in seconds>
rekey_bytes = <CHILD_SA rekeying interval in bytes> rekey_bytes = <CHILD_SA rekeying interval in bytes>
rekey_packets = <CHILD_SA rekeying interval in packets> rekey_packets = <CHILD_SA rekeying interval in packets>
+149 -98
View File
@@ -563,6 +563,9 @@ static void log_child_data(child_data_t *data, char *name)
cfg->set_mark_in.value, cfg->set_mark_in.mask); cfg->set_mark_in.value, cfg->set_mark_in.mask);
DBG2(DBG_CFG, " set_mark_out = %u/%u", DBG2(DBG_CFG, " set_mark_out = %u/%u",
cfg->set_mark_out.value, cfg->set_mark_out.mask); cfg->set_mark_out.value, cfg->set_mark_out.mask);
DBG2(DBG_CFG, " label = %s",
cfg->label ? cfg->label->get_string(cfg->label) : NULL);
DBG2(DBG_CFG, " label_mode = %N", sec_label_mode_names, cfg->label_mode);
DBG2(DBG_CFG, " inactivity = %llu", cfg->inactivity); DBG2(DBG_CFG, " inactivity = %llu", cfg->inactivity);
DBG2(DBG_CFG, " proposals = %#P", data->proposals); DBG2(DBG_CFG, " proposals = %#P", data->proposals);
DBG2(DBG_CFG, " local_ts = %#R", data->local_ts); DBG2(DBG_CFG, " local_ts = %#R", data->local_ts);
@@ -585,6 +588,7 @@ static void free_child_data(child_data_t *data)
offsetof(traffic_selector_t, destroy)); offsetof(traffic_selector_t, destroy));
data->remote_ts->destroy_offset(data->remote_ts, data->remote_ts->destroy_offset(data->remote_ts,
offsetof(traffic_selector_t, destroy)); offsetof(traffic_selector_t, destroy));
DESTROY_IF(data->cfg.label);
free(data->cfg.updown); free(data->cfg.updown);
free(data->cfg.interface); free(data->cfg.interface);
} }
@@ -1004,13 +1008,14 @@ CALLBACK(parse_action, bool,
action_t *out, chunk_t v) action_t *out, chunk_t v)
{ {
enum_map_t map[] = { enum_map_t map[] = {
{ "start", ACTION_RESTART }, { "start", ACTION_START },
{ "restart", ACTION_RESTART }, { "restart", ACTION_START },
{ "route", ACTION_ROUTE }, { "route", ACTION_TRAP },
{ "trap", ACTION_ROUTE }, { "trap", ACTION_TRAP },
{ "none", ACTION_NONE }, { "none", ACTION_NONE },
{ "clear", ACTION_NONE }, { "clear", ACTION_NONE },
}; };
char buf[BUF_LEN];
int d; int d;
if (parse_map(map, countof(map), &d, v)) if (parse_map(map, countof(map), &d, v))
@@ -1018,6 +1023,14 @@ CALLBACK(parse_action, bool,
*out = d; *out = d;
return TRUE; return TRUE;
} }
if (!vici_stringify(v, buf, sizeof(buf)))
{
return FALSE;
}
if (enum_flags_from_string(action_names, buf, out))
{
return TRUE;
}
return FALSE; return FALSE;
} }
@@ -1261,6 +1274,38 @@ CALLBACK(parse_if_id, bool,
return if_id_from_string(buf, out); return if_id_from_string(buf, out);
} }
/**
* Parse security label
*/
CALLBACK(parse_label, bool,
sec_label_t **out, chunk_t v)
{
char buf[BUF_LEN];
if (!vici_stringify(v, buf, sizeof(buf)))
{
return FALSE;
}
*out = sec_label_from_string(buf);
return *out != NULL;
}
/**
* Parse security label mode
*/
CALLBACK(parse_label_mode, bool,
sec_label_mode_t *out, chunk_t v)
{
char buf[BUF_LEN];
if (!vici_stringify(v, buf, sizeof(buf)) ||
!sec_label_mode_from_string(buf, out))
{
return FALSE;
}
return TRUE;
}
/** /**
* Parse TFC padding option * Parse TFC padding option
*/ */
@@ -1760,6 +1805,8 @@ CALLBACK(child_kv, bool,
{ "copy_dscp", parse_copy_dscp, &child->cfg.copy_dscp }, { "copy_dscp", parse_copy_dscp, &child->cfg.copy_dscp },
{ "if_id_in", parse_if_id, &child->cfg.if_id_in }, { "if_id_in", parse_if_id, &child->cfg.if_id_in },
{ "if_id_out", parse_if_id, &child->cfg.if_id_out }, { "if_id_out", parse_if_id, &child->cfg.if_id_out },
{ "label", parse_label, &child->cfg.label },
{ "label_mode", parse_label_mode, &child->cfg.label_mode },
}; };
return parse_rules(rules, countof(rules), name, value, return parse_rules(rules, countof(rules), name, value,
@@ -2143,30 +2190,33 @@ CALLBACK(peer_sn, bool,
static void run_start_action(private_vici_config_t *this, peer_cfg_t *peer_cfg, static void run_start_action(private_vici_config_t *this, peer_cfg_t *peer_cfg,
child_cfg_t *child_cfg) child_cfg_t *child_cfg)
{ {
switch (child_cfg->get_start_action(child_cfg)) action_t action;
action = child_cfg->get_start_action(child_cfg);
if (action & ACTION_TRAP)
{ {
case ACTION_RESTART: DBG1(DBG_CFG, "installing '%s'", child_cfg->get_name(child_cfg));
DBG1(DBG_CFG, "initiating '%s'", child_cfg->get_name(child_cfg)); switch (child_cfg->get_mode(child_cfg))
charon->controller->initiate(charon->controller, {
case MODE_PASS:
case MODE_DROP:
charon->shunts->install(charon->shunts,
peer_cfg->get_name(peer_cfg), child_cfg);
/* no need to check for ACTION_START */
return;
default:
charon->traps->install(charon->traps, peer_cfg, child_cfg);
break;
}
}
if (action & ACTION_START)
{
DBG1(DBG_CFG, "initiating '%s'", child_cfg->get_name(child_cfg));
charon->controller->initiate(charon->controller,
peer_cfg->get_ref(peer_cfg), child_cfg->get_ref(child_cfg), peer_cfg->get_ref(peer_cfg), child_cfg->get_ref(child_cfg),
NULL, NULL, 0, FALSE); NULL, NULL, 0, FALSE);
break;
case ACTION_ROUTE:
DBG1(DBG_CFG, "installing '%s'", child_cfg->get_name(child_cfg));
switch (child_cfg->get_mode(child_cfg))
{
case MODE_PASS:
case MODE_DROP:
charon->shunts->install(charon->shunts,
peer_cfg->get_name(peer_cfg), child_cfg);
break;
default:
charon->traps->install(charon->traps, peer_cfg, child_cfg);
break;
}
break;
default:
break;
} }
} }
@@ -2181,100 +2231,101 @@ static void clear_start_action(private_vici_config_t *this, char *peer_name,
ike_sa_t *ike_sa; ike_sa_t *ike_sa;
uint32_t id = 0, others; uint32_t id = 0, others;
array_t *ids = NULL, *ikeids = NULL; array_t *ids = NULL, *ikeids = NULL;
action_t action;
char *name; char *name;
name = child_cfg->get_name(child_cfg); name = child_cfg->get_name(child_cfg);
action = child_cfg->get_start_action(child_cfg);
switch (child_cfg->get_start_action(child_cfg)) if (action & ACTION_TRAP)
{ {
case ACTION_RESTART: DBG1(DBG_CFG, "uninstalling '%s'", name);
enumerator = charon->controller->create_ike_sa_enumerator( switch (child_cfg->get_mode(child_cfg))
{
case MODE_PASS:
case MODE_DROP:
charon->shunts->uninstall(charon->shunts, peer_name, name);
/* no need to check for ACTION_START */
return;
default:
charon->traps->uninstall(charon->traps, peer_name, name);
break;
}
}
if (action & ACTION_START)
{
enumerator = charon->controller->create_ike_sa_enumerator(
charon->controller, TRUE); charon->controller, TRUE);
while (enumerator->enumerate(enumerator, &ike_sa)) while (enumerator->enumerate(enumerator, &ike_sa))
{
if (!streq(ike_sa->get_name(ike_sa), peer_name))
{ {
if (!streq(ike_sa->get_name(ike_sa), peer_name)) continue;
}
others = id = 0;
children = ike_sa->create_child_sa_enumerator(ike_sa);
while (children->enumerate(children, &child_sa))
{
if (child_sa->get_state(child_sa) != CHILD_DELETING &&
child_sa->get_state(child_sa) != CHILD_DELETED)
{ {
continue; if (streq(name, child_sa->get_name(child_sa)))
{
id = child_sa->get_unique_id(child_sa);
}
else
{
others++;
}
} }
others = id = 0; }
children->destroy(children);
if (!ike_sa->get_child_count(ike_sa) || (id && !others))
{
/* found no children or only matching, delete IKE_SA */
id = ike_sa->get_unique_id(ike_sa);
array_insert_create_value(&ikeids, sizeof(id),
ARRAY_TAIL, &id);
}
else
{
children = ike_sa->create_child_sa_enumerator(ike_sa); children = ike_sa->create_child_sa_enumerator(ike_sa);
while (children->enumerate(children, &child_sa)) while (children->enumerate(children, &child_sa))
{ {
if (child_sa->get_state(child_sa) != CHILD_DELETING && if (streq(name, child_sa->get_name(child_sa)))
child_sa->get_state(child_sa) != CHILD_DELETED)
{ {
if (streq(name, child_sa->get_name(child_sa))) id = child_sa->get_unique_id(child_sa);
{ array_insert_create_value(&ids, sizeof(id),
id = child_sa->get_unique_id(child_sa); ARRAY_TAIL, &id);
}
else
{
others++;
}
} }
} }
children->destroy(children); children->destroy(children);
}
}
enumerator->destroy(enumerator);
if (!ike_sa->get_child_count(ike_sa) || (id && !others)) if (array_count(ids))
{ {
/* found no children or only matching, delete IKE_SA */ while (array_remove(ids, ARRAY_HEAD, &id))
id = ike_sa->get_unique_id(ike_sa);
array_insert_create_value(&ikeids, sizeof(id),
ARRAY_TAIL, &id);
}
else
{
children = ike_sa->create_child_sa_enumerator(ike_sa);
while (children->enumerate(children, &child_sa))
{
if (streq(name, child_sa->get_name(child_sa)))
{
id = child_sa->get_unique_id(child_sa);
array_insert_create_value(&ids, sizeof(id),
ARRAY_TAIL, &id);
}
}
children->destroy(children);
}
}
enumerator->destroy(enumerator);
if (array_count(ids))
{ {
while (array_remove(ids, ARRAY_HEAD, &id)) DBG1(DBG_CFG, "closing '%s' #%u", name, id);
{ charon->controller->terminate_child(charon->controller,
DBG1(DBG_CFG, "closing '%s' #%u", name, id); id, NULL, NULL, 0);
charon->controller->terminate_child(charon->controller,
id, NULL, NULL, 0);
}
array_destroy(ids);
} }
if (array_count(ikeids)) array_destroy(ids);
}
if (array_count(ikeids))
{
while (array_remove(ikeids, ARRAY_HEAD, &id))
{ {
while (array_remove(ikeids, ARRAY_HEAD, &id)) DBG1(DBG_CFG, "closing IKE_SA #%u", id);
{ charon->controller->terminate_ike(charon->controller, id,
DBG1(DBG_CFG, "closing IKE_SA #%u", id); FALSE, NULL, NULL, 0);
charon->controller->terminate_ike(charon->controller, id,
FALSE, NULL, NULL, 0);
}
array_destroy(ikeids);
} }
break; array_destroy(ikeids);
case ACTION_ROUTE: }
DBG1(DBG_CFG, "uninstalling '%s'", name);
switch (child_cfg->get_mode(child_cfg))
{
case MODE_PASS:
case MODE_DROP:
charon->shunts->uninstall(charon->shunts, peer_name, name);
break;
default:
charon->traps->uninstall(charon->traps, peer_name, name);
break;
}
break;
default:
break;
} }
} }
+40 -3
View File
@@ -150,6 +150,29 @@ static void list_mode(vici_builder_t *b, child_sa_t *child, child_cfg_t *cfg)
} }
} }
/**
* List the security label of a CHILD_SA or config
*/
static void list_label(vici_builder_t *b, child_sa_t *child, child_cfg_t *cfg)
{
sec_label_t *label = NULL;
chunk_t enc;
if (child)
{
label = child->get_label(child);
}
else if (cfg)
{
label = cfg->get_label(cfg);
}
if (label)
{
enc = label->get_encoding(label);
b->add_kv(b, "label", "%+B", &enc);
}
}
/** /**
* List IPsec-related details about a CHILD_SA * List IPsec-related details about a CHILD_SA
*/ */
@@ -275,6 +298,7 @@ static void list_child(private_vici_query_t *this, vici_builder_t *b,
state = child->get_state(child); state = child->get_state(child);
b->add_kv(b, "state", "%N", child_sa_state_names, state); b->add_kv(b, "state", "%N", child_sa_state_names, state);
list_mode(b, child, NULL); list_mode(b, child, NULL);
list_label(b, child, NULL);
switch (state) switch (state)
{ {
@@ -505,15 +529,16 @@ CALLBACK(list_sas, vici_message_t*,
ike_sa_t *ike_sa; ike_sa_t *ike_sa;
child_sa_t *child_sa; child_sa_t *child_sa;
time_t now; time_t now;
char *ike; char *ike, *child;
u_int ike_id; u_int ike_id, child_id;
bool bl; bool bl;
char buf[BUF_LEN]; char buf[BUF_LEN];
bl = request->get_str(request, NULL, "noblock") == NULL; bl = request->get_str(request, NULL, "noblock") == NULL;
ike = request->get_str(request, NULL, "ike"); ike = request->get_str(request, NULL, "ike");
ike_id = request->get_int(request, 0, "ike-id"); ike_id = request->get_int(request, 0, "ike-id");
child = request->get_str(request, NULL, "child");
child_id = request->get_int(request, 0, "child-id");
isas = charon->controller->create_ike_sa_enumerator(charon->controller, bl); isas = charon->controller->create_ike_sa_enumerator(charon->controller, bl);
while (isas->enumerate(isas, &ike_sa)) while (isas->enumerate(isas, &ike_sa))
@@ -538,6 +563,15 @@ CALLBACK(list_sas, vici_message_t*,
csas = ike_sa->create_child_sa_enumerator(ike_sa); csas = ike_sa->create_child_sa_enumerator(ike_sa);
while (csas->enumerate(csas, &child_sa)) while (csas->enumerate(csas, &child_sa))
{ {
if (child && !streq(child, child_sa->get_name(child_sa)))
{
continue;
}
if (child_id && child_sa->get_unique_id(child_sa) != child_id)
{
continue;
}
snprintf(buf, sizeof(buf), "%s-%u", child_sa->get_name(child_sa), snprintf(buf, sizeof(buf), "%s-%u", child_sa->get_name(child_sa),
child_sa->get_unique_id(child_sa)); child_sa->get_unique_id(child_sa));
b->begin_section(b, buf); b->begin_section(b, buf);
@@ -576,6 +610,7 @@ static void raise_policy(private_vici_query_t *this, u_int id, char *ike,
b->add_kv(b, "ike", "%s", ike); b->add_kv(b, "ike", "%s", ike);
list_mode(b, child, NULL); list_mode(b, child, NULL);
list_label(b, child, NULL);
b->begin_list(b, "local-ts"); b->begin_list(b, "local-ts");
enumerator = child->create_ts_enumerator(child, TRUE); enumerator = child->create_ts_enumerator(child, TRUE);
@@ -624,6 +659,7 @@ static void raise_policy_cfg(private_vici_query_t *this, u_int id, char *ike,
} }
list_mode(b, NULL, cfg); list_mode(b, NULL, cfg);
list_label(b, NULL, cfg);
b->begin_list(b, "local-ts"); b->begin_list(b, "local-ts");
list = cfg->get_traffic_selectors(cfg, TRUE, NULL, NULL, FALSE); list = cfg->get_traffic_selectors(cfg, TRUE, NULL, NULL, FALSE);
@@ -930,6 +966,7 @@ CALLBACK(list_conns, vici_message_t*,
b->begin_section(b, child_cfg->get_name(child_cfg)); b->begin_section(b, child_cfg->get_name(child_cfg));
list_mode(b, NULL, child_cfg); list_mode(b, NULL, child_cfg);
list_label(b, NULL, child_cfg);
lft = child_cfg->get_lifetime(child_cfg, FALSE); lft = child_cfg->get_lifetime(child_cfg, FALSE);
b->add_kv(b, "rekey_time", "%"PRIu64, lft->time.rekey); b->add_kv(b, "rekey_time", "%"PRIu64, lft->time.rekey);
+22 -18
View File
@@ -30,34 +30,29 @@ struct private_acquire_job_t {
acquire_job_t public; acquire_job_t public;
/** /**
* reqid of the child to rekey * reqid of the triggered policy
*/ */
uint32_t reqid; uint32_t reqid;
/** /**
* acquired source traffic selector * Data from the acquire
*/ */
traffic_selector_t *src_ts; kernel_acquire_data_t data;
/**
* acquired destination traffic selector
*/
traffic_selector_t *dst_ts;
}; };
METHOD(job_t, destroy, void, METHOD(job_t, destroy, void,
private_acquire_job_t *this) private_acquire_job_t *this)
{ {
DESTROY_IF(this->src_ts); DESTROY_IF(this->data.src);
DESTROY_IF(this->dst_ts); DESTROY_IF(this->data.dst);
DESTROY_IF(this->data.label);
free(this); free(this);
} }
METHOD(job_t, execute, job_requeue_t, METHOD(job_t, execute, job_requeue_t,
private_acquire_job_t *this) private_acquire_job_t *this)
{ {
charon->traps->acquire(charon->traps, this->reqid, charon->traps->acquire(charon->traps, this->reqid, &this->data);
this->src_ts, this->dst_ts);
return JOB_REQUEUE_NONE; return JOB_REQUEUE_NONE;
} }
@@ -70,9 +65,7 @@ METHOD(job_t, get_priority, job_priority_t,
/* /*
* Described in header * Described in header
*/ */
acquire_job_t *acquire_job_create(uint32_t reqid, acquire_job_t *acquire_job_create(uint32_t reqid, kernel_acquire_data_t *data)
traffic_selector_t *src_ts,
traffic_selector_t *dst_ts)
{ {
private_acquire_job_t *this; private_acquire_job_t *this;
@@ -85,10 +78,21 @@ acquire_job_t *acquire_job_create(uint32_t reqid,
}, },
}, },
.reqid = reqid, .reqid = reqid,
.src_ts = src_ts, .data = *data,
.dst_ts = dst_ts,
); );
if (this->data.src)
{
this->data.src = this->data.src->clone(this->data.src);
}
if (this->data.dst)
{
this->data.dst = this->data.dst->clone(this->data.dst);
}
if (this->data.label)
{
this->data.label = this->data.label->clone(this->data.label);
}
return &this->public; return &this->public;
} }
+4 -7
View File
@@ -24,7 +24,7 @@
typedef struct acquire_job_t acquire_job_t; typedef struct acquire_job_t acquire_job_t;
#include <library.h> #include <library.h>
#include <selectors/traffic_selector.h> #include <kernel/kernel_interface.h>
#include <processing/jobs/job.h> #include <processing/jobs/job.h>
/** /**
@@ -42,13 +42,10 @@ struct acquire_job_t {
/** /**
* Creates a job of type ACQUIRE. * Creates a job of type ACQUIRE.
* *
* @param reqid reqid of the trapped CHILD_SA to acquire * @param reqid reqid of the triggered policy
* @param src_ts source traffic selector * @param data data from the acquire
* @param dst_ts destination traffic selector
* @return acquire_job_t object * @return acquire_job_t object
*/ */
acquire_job_t *acquire_job_create(uint32_t reqid, acquire_job_t *acquire_job_create(uint32_t reqid, kernel_acquire_data_t *data);
traffic_selector_t *src_ts,
traffic_selector_t *dst_ts);
#endif /** REKEY_CHILD_SA_JOB_H_ @}*/ #endif /** REKEY_CHILD_SA_JOB_H_ @}*/
@@ -253,7 +253,7 @@ METHOD(job_t, execute, job_requeue_t,
task->migrate(task, ike_sa); task->migrate(task, ike_sa);
ike_sa->queue_task(ike_sa, task); ike_sa->queue_task(ike_sa, task);
} }
if (ike_sa->initiate(ike_sa, NULL, 0, NULL, NULL) == DESTROY_ME) if (ike_sa->initiate(ike_sa, NULL, NULL) == DESTROY_ME)
{ {
charon->ike_sa_manager->checkin_and_destroy( charon->ike_sa_manager->checkin_and_destroy(
charon->ike_sa_manager, ike_sa); charon->ike_sa_manager, ike_sa);
@@ -55,7 +55,7 @@ METHOD(job_t, execute, job_requeue_t,
this->ike_sa_id); this->ike_sa_id);
if (ike_sa) if (ike_sa)
{ {
if (ike_sa->initiate(ike_sa, NULL, 0, NULL, NULL) == DESTROY_ME) if (ike_sa->initiate(ike_sa, NULL, NULL) == DESTROY_ME)
{ {
charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
ike_sa); ike_sa);
@@ -42,8 +42,7 @@ METHOD(job_t, execute, job_requeue_t,
enumerator_t *enumerator, *children; enumerator_t *enumerator, *children;
peer_cfg_t *peer_cfg; peer_cfg_t *peer_cfg;
child_cfg_t *child_cfg; child_cfg_t *child_cfg;
ipsec_mode_t mode; action_t action;
char *name;
enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends, enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends,
NULL, NULL, NULL, NULL, IKE_ANY); NULL, NULL, NULL, NULL, IKE_ANY);
@@ -52,34 +51,39 @@ METHOD(job_t, execute, job_requeue_t,
children = peer_cfg->create_child_cfg_enumerator(peer_cfg); children = peer_cfg->create_child_cfg_enumerator(peer_cfg);
while (children->enumerate(children, &child_cfg)) while (children->enumerate(children, &child_cfg))
{ {
name = child_cfg->get_name(child_cfg); action = child_cfg->get_start_action(child_cfg);
if (action == ACTION_NONE)
switch (child_cfg->get_start_action(child_cfg))
{ {
case ACTION_RESTART: continue;
DBG1(DBG_JOB, "start action: initiate '%s'", name); }
charon->controller->initiate(charon->controller,
peer_cfg->get_ref(peer_cfg), DBG1(DBG_JOB, "start action: %N '%s'", action_names, action,
child_cfg->get_ref(child_cfg), child_cfg->get_name(child_cfg));
NULL, NULL, 0, FALSE);
break; if (action & ACTION_TRAP)
case ACTION_ROUTE: {
DBG1(DBG_JOB, "start action: route '%s'", name); switch (child_cfg->get_mode(child_cfg))
mode = child_cfg->get_mode(child_cfg); {
if (mode == MODE_PASS || mode == MODE_DROP) case MODE_PASS:
{ case MODE_DROP:
charon->shunts->install(charon->shunts, charon->shunts->install(charon->shunts,
peer_cfg->get_name(peer_cfg), peer_cfg->get_name(peer_cfg),
child_cfg); child_cfg);
} /* no need to check for ACTION_START */
else continue;
{ default:
charon->traps->install(charon->traps, peer_cfg, charon->traps->install(charon->traps, peer_cfg,
child_cfg); child_cfg);
} break;
break; }
case ACTION_NONE: }
break;
if (action & ACTION_START)
{
charon->controller->initiate(charon->controller,
peer_cfg->get_ref(peer_cfg),
child_cfg->get_ref(child_cfg),
NULL, NULL, 0, FALSE);
} }
} }
children->destroy(children); children->destroy(children);
+266 -65
View File
@@ -42,6 +42,7 @@ ENUM(child_sa_state_names, CHILD_CREATED, CHILD_DESTROYING,
); );
ENUM_FLAGS(child_sa_outbound_state_names, CHILD_OUTBOUND_REGISTERED, CHILD_OUTBOUND_POLICIES, ENUM_FLAGS(child_sa_outbound_state_names, CHILD_OUTBOUND_REGISTERED, CHILD_OUTBOUND_POLICIES,
"NONE",
"REGISTERED", "REGISTERED",
"SA", "SA",
"POLICIES", "POLICIES",
@@ -178,6 +179,11 @@ struct private_child_sa_t {
*/ */
mark_t mark_out; mark_t mark_out;
/**
* Security label
*/
sec_label_t *label;
/** /**
* absolute time when rekeying is scheduled * absolute time when rekeying is scheduled
*/ */
@@ -297,6 +303,46 @@ static inline mark_t mark_in_sa(private_child_sa_t *this)
return (mark_t){}; return (mark_t){};
} }
/**
* Possible uses for security labels
*/
typedef enum {
LABEL_USE_REQID,
LABEL_USE_POLICY,
LABEL_USE_SA,
} label_use_t;
/**
* Returns the security label for either policies, SAs or reqids.
*/
static inline sec_label_t *label_for(private_child_sa_t *this, label_use_t use)
{
/* For SELinux we use the configured label for policies and reqid but the
* negotiated one for the SAs. That's because the label on the policies is
* usually a generic one that matches specific labels on flows, which will
* trigger an acquire if no matching SA with that label exists yet.
* When that SA is later installed, we want to avoid having to install
* policies in the kernel that will never get used, so we use the configured
* label again.
* Note that while the labels don't have to be equal, they are both either
* NULL or defined.
*/
if (this->config->get_label_mode(this->config) == SEC_LABEL_MODE_SELINUX)
{
switch (use)
{
case LABEL_USE_REQID:
case LABEL_USE_POLICY:
return this->config->get_label(this->config);
case LABEL_USE_SA:
return this->label;
}
}
/* for the simple label mode we don't pass labels to the kernel, so we don't
* use it to acquire unique reqids either */
return NULL;
}
METHOD(child_sa_t, get_name, char*, METHOD(child_sa_t, get_name, char*,
private_child_sa_t *this) private_child_sa_t *this)
{ {
@@ -512,8 +558,12 @@ METHOD(enumerator_t, policy_destroy, void,
free(this); free(this);
} }
METHOD(child_sa_t, create_policy_enumerator, enumerator_t*, /**
private_child_sa_t *this) * Create an enumerator over two lists of traffic selectors, returning all the
* pairs of traffic selectors from the first and second list.
*/
static enumerator_t *create_policy_enumerator_internal(array_t *my_ts,
array_t *other_ts)
{ {
policy_enumerator_t *e; policy_enumerator_t *e;
@@ -523,15 +573,21 @@ METHOD(child_sa_t, create_policy_enumerator, enumerator_t*,
.venumerate = _policy_enumerate, .venumerate = _policy_enumerate,
.destroy = _policy_destroy, .destroy = _policy_destroy,
}, },
.mine = array_create_enumerator(this->my_ts), .mine = array_create_enumerator(my_ts),
.other = array_create_enumerator(this->other_ts), .other = array_create_enumerator(other_ts),
.array = this->other_ts, .array = other_ts,
.ts = NULL, .ts = NULL,
); );
return &e->public; return &e->public;
} }
METHOD(child_sa_t, create_policy_enumerator, enumerator_t*,
private_child_sa_t *this)
{
return create_policy_enumerator_internal(this->my_ts, this->other_ts);
}
/** /**
* update the cached usebytes * update the cached usebytes
* returns SUCCESS if the usebytes have changed, FAILED if not or no SPIs * returns SUCCESS if the usebytes have changed, FAILED if not or no SPIs
@@ -637,6 +693,7 @@ static bool update_usetime(private_child_sa_t *this, bool inbound)
.dst_ts = my_ts, .dst_ts = my_ts,
.mark = this->mark_in, .mark = this->mark_in,
.if_id = this->if_id_in, .if_id = this->if_id_in,
.label = label_for(this, LABEL_USE_POLICY),
}; };
kernel_ipsec_query_policy_t query = {}; kernel_ipsec_query_policy_t query = {};
@@ -664,6 +721,7 @@ static bool update_usetime(private_child_sa_t *this, bool inbound)
.mark = this->mark_out, .mark = this->mark_out,
.if_id = this->if_id_out, .if_id = this->if_id_out,
.interface = this->config->get_interface(this->config), .interface = this->config->get_interface(this->config),
.label = label_for(this, LABEL_USE_POLICY),
}; };
kernel_ipsec_query_policy_t query = {}; kernel_ipsec_query_policy_t query = {};
@@ -735,6 +793,12 @@ METHOD(child_sa_t, get_if_id, uint32_t,
return inbound ? this->if_id_in : this->if_id_out; return inbound ? this->if_id_in : this->if_id_out;
} }
METHOD(child_sa_t, get_label, sec_label_t*,
private_child_sa_t *this)
{
return this->label ?: this->config->get_label(this->config);
}
METHOD(child_sa_t, get_lifetime, time_t, METHOD(child_sa_t, get_lifetime, time_t,
private_child_sa_t *this, bool hard) private_child_sa_t *this, bool hard)
{ {
@@ -855,7 +919,8 @@ static status_t install_internal(private_child_sa_t *this, chunk_t encr,
{ {
status = charon->kernel->alloc_reqid(charon->kernel, my_ts, other_ts, status = charon->kernel->alloc_reqid(charon->kernel, my_ts, other_ts,
this->mark_in, this->mark_out, this->if_id_in, this->mark_in, this->mark_out, this->if_id_in,
this->if_id_out, &this->reqid); this->if_id_out, label_for(this, LABEL_USE_REQID),
&this->reqid);
if (status != SUCCESS) if (status != SUCCESS)
{ {
my_ts->destroy(my_ts); my_ts->destroy(my_ts);
@@ -919,6 +984,7 @@ static status_t install_internal(private_child_sa_t *this, chunk_t encr,
.copy_df = !this->config->has_option(this->config, OPT_NO_COPY_DF), .copy_df = !this->config->has_option(this->config, OPT_NO_COPY_DF),
.copy_ecn = !this->config->has_option(this->config, OPT_NO_COPY_ECN), .copy_ecn = !this->config->has_option(this->config, OPT_NO_COPY_ECN),
.copy_dscp = this->config->get_copy_dscp(this->config), .copy_dscp = this->config->get_copy_dscp(this->config),
.label = label_for(this, LABEL_USE_SA),
.initiator = initiator, .initiator = initiator,
.inbound = inbound, .inbound = inbound,
.update = update, .update = update,
@@ -1016,6 +1082,7 @@ static status_t install_policies_inbound(private_child_sa_t *this,
.dst_ts = my_ts, .dst_ts = my_ts,
.mark = this->mark_in, .mark = this->mark_in,
.if_id = this->if_id_in, .if_id = this->if_id_in,
.label = label_for(this, LABEL_USE_POLICY),
}; };
kernel_ipsec_manage_policy_t in_policy = { kernel_ipsec_manage_policy_t in_policy = {
.type = type, .type = type,
@@ -1052,6 +1119,7 @@ static status_t install_policies_outbound(private_child_sa_t *this,
.mark = this->mark_out, .mark = this->mark_out,
.if_id = this->if_id_out, .if_id = this->if_id_out,
.interface = this->config->get_interface(this->config), .interface = this->config->get_interface(this->config),
.label = label_for(this, LABEL_USE_POLICY),
}; };
kernel_ipsec_manage_policy_t out_policy = { kernel_ipsec_manage_policy_t out_policy = {
.type = type, .type = type,
@@ -1061,6 +1129,7 @@ static status_t install_policies_outbound(private_child_sa_t *this,
.dst = other_addr, .dst = other_addr,
.sa = other_sa, .sa = other_sa,
}; };
uint32_t reqid = other_sa->reqid;
status_t status = SUCCESS; status_t status = SUCCESS;
status |= charon->kernel->add_policy(charon->kernel, &out_id, &out_policy); status |= charon->kernel->add_policy(charon->kernel, &out_id, &out_policy);
@@ -1084,7 +1153,7 @@ static status_t install_policies_outbound(private_child_sa_t *this,
status |= charon->kernel->add_policy(charon->kernel, &out_id, status |= charon->kernel->add_policy(charon->kernel, &out_id,
&out_policy); &out_policy);
/* reset the reqid for any other further policies */ /* reset the reqid for any other further policies */
other_sa->reqid = this->reqid; other_sa->reqid = reqid;
} }
return status; return status;
} }
@@ -1125,6 +1194,7 @@ static void del_policies_inbound(private_child_sa_t *this,
.dst_ts = my_ts, .dst_ts = my_ts,
.mark = this->mark_in, .mark = this->mark_in,
.if_id = this->if_id_in, .if_id = this->if_id_in,
.label = label_for(this, LABEL_USE_POLICY),
}; };
kernel_ipsec_manage_policy_t in_policy = { kernel_ipsec_manage_policy_t in_policy = {
.type = type, .type = type,
@@ -1160,6 +1230,7 @@ static void del_policies_outbound(private_child_sa_t *this,
.mark = this->mark_out, .mark = this->mark_out,
.if_id = this->if_id_out, .if_id = this->if_id_out,
.interface = this->config->get_interface(this->config), .interface = this->config->get_interface(this->config),
.label = label_for(this, LABEL_USE_POLICY),
}; };
kernel_ipsec_manage_policy_t out_policy = { kernel_ipsec_manage_policy_t out_policy = {
.type = type, .type = type,
@@ -1169,6 +1240,7 @@ static void del_policies_outbound(private_child_sa_t *this,
.dst = other_addr, .dst = other_addr,
.sa = other_sa, .sa = other_sa,
}; };
uint32_t reqid = other_sa->reqid;
charon->kernel->del_policy(charon->kernel, &out_id, &out_policy); charon->kernel->del_policy(charon->kernel, &out_id, &out_policy);
@@ -1181,7 +1253,7 @@ static void del_policies_outbound(private_child_sa_t *this,
out_policy.prio = POLICY_PRIORITY_ROUTED; out_policy.prio = POLICY_PRIORITY_ROUTED;
} }
charon->kernel->del_policy(charon->kernel, &out_id, &out_policy); charon->kernel->del_policy(charon->kernel, &out_id, &out_policy);
other_sa->reqid = this->reqid; other_sa->reqid = reqid;
} }
} }
@@ -1239,27 +1311,38 @@ METHOD(child_sa_t, set_policies, void,
array_sort(this->other_ts, (void*)traffic_selector_cmp, NULL); array_sort(this->other_ts, (void*)traffic_selector_cmp, NULL);
} }
/**
* Allocate a reqid for the given local and remote traffic selectors.
*/
static status_t alloc_reqid(private_child_sa_t *this, array_t *my_ts,
array_t *other_ts, uint32_t *reqid)
{
linked_list_t *my_ts_list, *other_ts_list;
status_t status;
my_ts_list = linked_list_create_from_enumerator(array_create_enumerator(my_ts));
other_ts_list = linked_list_create_from_enumerator(array_create_enumerator(other_ts));
status = charon->kernel->alloc_reqid(
charon->kernel, my_ts_list, other_ts_list,
this->mark_in, this->mark_out, this->if_id_in,
this->if_id_out, label_for(this, LABEL_USE_REQID),
reqid);
my_ts_list->destroy(my_ts_list);
other_ts_list->destroy(other_ts_list);
return status;
}
METHOD(child_sa_t, install_policies, status_t, METHOD(child_sa_t, install_policies, status_t,
private_child_sa_t *this) private_child_sa_t *this)
{ {
enumerator_t *enumerator; enumerator_t *enumerator;
linked_list_t *my_ts_list, *other_ts_list;
traffic_selector_t *my_ts, *other_ts; traffic_selector_t *my_ts, *other_ts;
status_t status = SUCCESS; status_t status = SUCCESS;
bool install_outbound = FALSE; bool install_outbound = FALSE;
if (!this->reqid_allocated && !this->static_reqid) if (!this->reqid_allocated && !this->static_reqid)
{ {
my_ts_list = linked_list_create_from_enumerator( status = alloc_reqid(this, this->my_ts, this->other_ts, &this->reqid);
array_create_enumerator(this->my_ts));
other_ts_list = linked_list_create_from_enumerator(
array_create_enumerator(this->other_ts));
status = charon->kernel->alloc_reqid(
charon->kernel, my_ts_list, other_ts_list,
this->mark_in, this->mark_out, this->if_id_in,
this->if_id_out, &this->reqid);
my_ts_list->destroy(my_ts_list);
other_ts_list->destroy(other_ts_list);
if (status != SUCCESS) if (status != SUCCESS)
{ {
return status; return status;
@@ -1311,15 +1394,36 @@ METHOD(child_sa_t, install_policies, status_t,
return status; return status;
} }
/**
* Check if we can install the outbound SA immediately.
*
* If the kernel supports installing SPIs with policies, we can do so as it
* will only be used once we update the policies.
*
* However, if we use labels with SELinux, we can't as we don't set SPIs
* on the policy in order to match SAs with other labels that match the generic
* label that's used on the policies.
*/
static bool install_outbound_immediately(private_child_sa_t *this)
{
if (charon->kernel->get_features(charon->kernel) & KERNEL_POLICY_SPI)
{
if (this->config->get_label_mode(this->config) == SEC_LABEL_MODE_SELINUX)
{
return !this->label;
}
return TRUE;
}
return FALSE;
}
METHOD(child_sa_t, register_outbound, status_t, METHOD(child_sa_t, register_outbound, status_t,
private_child_sa_t *this, chunk_t encr, chunk_t integ, uint32_t spi, private_child_sa_t *this, chunk_t encr, chunk_t integ, uint32_t spi,
uint16_t cpi, bool tfcv3) uint16_t cpi, bool tfcv3)
{ {
status_t status; status_t status;
/* if the kernel supports installing SPIs with policies we install the if (install_outbound_immediately(this))
* SA immediately as it will only be used once we update the policies */
if (charon->kernel->get_features(charon->kernel) & KERNEL_POLICY_SPI)
{ {
status = install_internal(this, encr, integ, spi, cpi, FALSE, FALSE, status = install_internal(this, encr, integ, spi, cpi, FALSE, FALSE,
tfcv3); tfcv3);
@@ -1472,7 +1576,7 @@ CALLBACK(reinstall_vip, void,
* Update addresses and encap state of IPsec SAs in the kernel * Update addresses and encap state of IPsec SAs in the kernel
*/ */
static status_t update_sas(private_child_sa_t *this, host_t *me, host_t *other, static status_t update_sas(private_child_sa_t *this, host_t *me, host_t *other,
bool encap) bool encap, uint32_t reqid)
{ {
/* update our (initiator) SA */ /* update our (initiator) SA */
if (this->my_spi && this->inbound_installed) if (this->my_spi && this->inbound_installed)
@@ -1491,6 +1595,7 @@ static status_t update_sas(private_child_sa_t *this, host_t *me, host_t *other,
.new_dst = me, .new_dst = me,
.encap = this->encap, .encap = this->encap,
.new_encap = encap, .new_encap = encap,
.new_reqid = reqid,
}; };
if (charon->kernel->update_sa(charon->kernel, &id, if (charon->kernel->update_sa(charon->kernel, &id,
&sa) == NOT_SUPPORTED) &sa) == NOT_SUPPORTED)
@@ -1516,6 +1621,7 @@ static status_t update_sas(private_child_sa_t *this, host_t *me, host_t *other,
.new_dst = other, .new_dst = other,
.encap = this->encap, .encap = this->encap,
.new_encap = encap, .new_encap = encap,
.new_reqid = reqid,
}; };
if (charon->kernel->update_sa(charon->kernel, &id, if (charon->kernel->update_sa(charon->kernel, &id,
&sa) == NOT_SUPPORTED) &sa) == NOT_SUPPORTED)
@@ -1527,6 +1633,30 @@ static status_t update_sas(private_child_sa_t *this, host_t *me, host_t *other,
return SUCCESS; return SUCCESS;
} }
/**
* Fill the second list with copies of the given traffic selectors updating
* dynamic traffic selectors based on the given addresses.
*/
static void update_ts(host_t *old_host, host_t *new_host, array_t *old_list,
array_t *new_list)
{
enumerator_t *enumerator;
traffic_selector_t *old_ts, *new_ts;
enumerator = array_create_enumerator(old_list);
while (enumerator->enumerate(enumerator, &old_ts))
{
new_ts = old_ts->clone(old_ts);
if (new_ts->is_host(new_ts, old_host))
{
new_ts->set_address(new_ts, new_host);
}
array_insert(new_list, ARRAY_TAIL, new_ts);
}
enumerator->destroy(enumerator);
array_sort(new_list, (void*)traffic_selector_cmp, NULL);
}
METHOD(child_sa_t, update, status_t, METHOD(child_sa_t, update, status_t,
private_child_sa_t *this, host_t *me, host_t *other, linked_list_t *vips, private_child_sa_t *this, host_t *me, host_t *other, linked_list_t *vips,
bool encap) bool encap)
@@ -1554,17 +1684,31 @@ METHOD(child_sa_t, update, status_t,
ipsec_sa_cfg_t my_sa, other_sa; ipsec_sa_cfg_t my_sa, other_sa;
enumerator_t *enumerator; enumerator_t *enumerator;
traffic_selector_t *my_ts, *other_ts; traffic_selector_t *my_ts, *other_ts;
uint32_t manual_prio; array_t *new_my_ts = NULL, *new_other_ts = NULL;
policy_priority_t priority;
uint32_t manual_prio, new_reqid = 0;
status_t state; status_t state;
bool outbound; bool outbound;
prepare_sa_cfg(this, &my_sa, &other_sa); prepare_sa_cfg(this, &my_sa, &other_sa);
manual_prio = this->config->get_manual_prio(this->config); manual_prio = this->config->get_manual_prio(this->config);
outbound = (this->outbound_state & CHILD_OUTBOUND_POLICIES); priority = this->trap ? POLICY_PRIORITY_ROUTED
: POLICY_PRIORITY_DEFAULT;
outbound = (this->outbound_state & CHILD_OUTBOUND_POLICIES) || this->trap;
enumerator = create_policy_enumerator(this); enumerator = create_policy_enumerator(this);
while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
{ {
if (!new_my_ts && !me->ip_equals(me, this->my_addr) &&
my_ts->is_host(my_ts, this->my_addr))
{
new_my_ts = array_create(0, 0);
}
if (!new_other_ts && !other->ip_equals(other, this->other_addr) &&
other_ts->is_host(other_ts, this->other_addr))
{
new_other_ts = array_create(0, 0);
}
/* install drop policy to avoid traffic leaks, acquires etc. */ /* install drop policy to avoid traffic leaks, acquires etc. */
if (outbound) if (outbound)
{ {
@@ -1575,77 +1719,130 @@ METHOD(child_sa_t, update, status_t,
/* remove old policies */ /* remove old policies */
del_policies_internal(this, this->my_addr, this->other_addr, del_policies_internal(this, this->my_addr, this->other_addr,
my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC,
POLICY_PRIORITY_DEFAULT, manual_prio, outbound); priority, manual_prio, outbound);
} }
enumerator->destroy(enumerator); enumerator->destroy(enumerator);
if (new_my_ts)
{
update_ts(this->my_addr, me, this->my_ts, new_my_ts);
}
if (new_other_ts)
{
update_ts(this->other_addr, other, this->other_ts, new_other_ts);
}
if (this->reqid_allocated && (new_my_ts || new_other_ts))
{
/* if we allocated a reqid with the previous TS, we have to get a
* new one that matches the updated TS */
if (alloc_reqid(this, new_my_ts ?: this->my_ts,
new_other_ts ?: this->other_ts, &new_reqid) != SUCCESS)
{
DBG1(DBG_CHD, "allocating new reqid for updated SA failed");
}
}
/* update the IPsec SAs */ /* update the IPsec SAs */
state = update_sas(this, me, other, encap); state = update_sas(this, me, other, encap, new_reqid);
/* install new/updated policies only if we were able to update the
* SAs, otherwise we reinstall the old policies further below */
if (state != NOT_SUPPORTED)
{
/* we reinstall the virtual IP to handle interface roaming
* correctly */
if (vips)
{
vips->invoke_function(vips, reinstall_vip, me);
}
if (new_reqid)
{
my_sa.reqid = other_sa.reqid = new_reqid;
}
enumerator = create_policy_enumerator_internal(
new_my_ts ?: this->my_ts,
new_other_ts ?: this->other_ts);
while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
{
install_policies_internal(this, me, other, my_ts, other_ts,
&my_sa, &other_sa, POLICY_IPSEC,
priority, manual_prio, outbound);
}
enumerator->destroy(enumerator);
if (new_reqid)
{
my_sa.reqid = other_sa.reqid = this->reqid;
}
}
enumerator = create_policy_enumerator(this); enumerator = create_policy_enumerator(this);
while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
{ {
traffic_selector_t *old_my_ts = NULL, *old_other_ts = NULL;
/* reinstall the previous policies if we can't update the SAs */ /* reinstall the previous policies if we can't update the SAs */
if (state == NOT_SUPPORTED) if (state == NOT_SUPPORTED)
{ {
install_policies_internal(this, this->my_addr, this->other_addr, install_policies_internal(this, this->my_addr, this->other_addr,
my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC,
POLICY_PRIORITY_DEFAULT, manual_prio, outbound); priority, manual_prio, outbound);
}
else
{
/* check if we have to update a "dynamic" traffic selector */
if (!me->ip_equals(me, this->my_addr) &&
my_ts->is_host(my_ts, this->my_addr))
{
old_my_ts = my_ts->clone(my_ts);
my_ts->set_address(my_ts, me);
}
if (!other->ip_equals(other, this->other_addr) &&
other_ts->is_host(other_ts, this->other_addr))
{
old_other_ts = other_ts->clone(other_ts);
other_ts->set_address(other_ts, other);
}
/* we reinstall the virtual IP to handle interface roaming
* correctly */
if (vips)
{
vips->invoke_function(vips, reinstall_vip, me);
}
/* reinstall updated policies */
install_policies_internal(this, me, other, my_ts, other_ts,
&my_sa, &other_sa, POLICY_IPSEC,
POLICY_PRIORITY_DEFAULT, manual_prio, outbound);
} }
/* remove the drop policy */ /* remove the drop policy */
if (outbound) if (outbound)
{ {
del_policies_outbound(this, this->my_addr, this->other_addr, del_policies_outbound(this, this->my_addr, this->other_addr,
old_my_ts ?: my_ts, old_other_ts ?: other_ts, my_ts, other_ts, &my_sa, &other_sa, POLICY_DROP,
&my_sa, &other_sa, POLICY_DROP,
POLICY_PRIORITY_DEFAULT, manual_prio); POLICY_PRIORITY_DEFAULT, manual_prio);
} }
DESTROY_IF(old_my_ts);
DESTROY_IF(old_other_ts);
} }
enumerator->destroy(enumerator); enumerator->destroy(enumerator);
if (state == NOT_SUPPORTED) if (state == NOT_SUPPORTED)
{ {
if (new_reqid &&
charon->kernel->release_reqid(charon->kernel,
new_reqid, this->mark_in, this->mark_out,
this->if_id_in, this->if_id_out,
label_for(this, LABEL_USE_REQID)) != SUCCESS)
{
DBG1(DBG_CHD, "releasing reqid %u failed", new_reqid);
}
array_destroy_offset(new_my_ts,
offsetof(traffic_selector_t, destroy));
array_destroy_offset(new_other_ts,
offsetof(traffic_selector_t, destroy));
set_state(this, old); set_state(this, old);
return NOT_SUPPORTED; return NOT_SUPPORTED;
} }
if (new_reqid)
{
if (charon->kernel->release_reqid(charon->kernel,
this->reqid, this->mark_in, this->mark_out,
this->if_id_in, this->if_id_out,
label_for(this, LABEL_USE_REQID)) != SUCCESS)
{
DBG1(DBG_CHD, "releasing reqid %u failed", this->reqid);
}
DBG1(DBG_CHD, "replaced reqid %u with reqid %u for updated "
"CHILD_SA %s{%d}", this->reqid, new_reqid, get_name(this),
this->unique_id);
this->reqid = new_reqid;
}
if (new_my_ts)
{
array_destroy_offset(this->my_ts,
offsetof(traffic_selector_t, destroy));
this->my_ts = new_my_ts;
}
if (new_other_ts)
{
array_destroy_offset(this->other_ts,
offsetof(traffic_selector_t, destroy));
this->other_ts = new_other_ts;
}
} }
else if (!transport_proxy_mode) else if (!transport_proxy_mode)
{ {
if (update_sas(this, me, other, encap) == NOT_SUPPORTED) if (update_sas(this, me, other, encap, 0) == NOT_SUPPORTED)
{ {
set_state(this, old); set_state(this, old);
return NOT_SUPPORTED; return NOT_SUPPORTED;
@@ -1743,7 +1940,8 @@ METHOD(child_sa_t, destroy, void,
{ {
if (charon->kernel->release_reqid(charon->kernel, if (charon->kernel->release_reqid(charon->kernel,
this->reqid, this->mark_in, this->mark_out, this->reqid, this->mark_in, this->mark_out,
this->if_id_in, this->if_id_out) != SUCCESS) this->if_id_in, this->if_id_out,
label_for(this, LABEL_USE_REQID)) != SUCCESS)
{ {
DBG1(DBG_CHD, "releasing reqid %u failed", this->reqid); DBG1(DBG_CHD, "releasing reqid %u failed", this->reqid);
} }
@@ -1754,6 +1952,7 @@ METHOD(child_sa_t, destroy, void,
this->my_addr->destroy(this->my_addr); this->my_addr->destroy(this->my_addr);
this->other_addr->destroy(this->other_addr); this->other_addr->destroy(this->other_addr);
DESTROY_IF(this->proposal); DESTROY_IF(this->proposal);
DESTROY_IF(this->label);
this->config->destroy(this->config); this->config->destroy(this->config);
chunk_clear(&this->encr_r); chunk_clear(&this->encr_r);
chunk_clear(&this->integ_r); chunk_clear(&this->integ_r);
@@ -1826,6 +2025,7 @@ child_sa_t *child_sa_create(host_t *me, host_t *other, child_cfg_t *config,
.get_usestats = _get_usestats, .get_usestats = _get_usestats,
.get_mark = _get_mark, .get_mark = _get_mark,
.get_if_id = _get_if_id, .get_if_id = _get_if_id,
.get_label = _get_label,
.has_encap = _has_encap, .has_encap = _has_encap,
.get_ipcomp = _get_ipcomp, .get_ipcomp = _get_ipcomp,
.set_ipcomp = _set_ipcomp, .set_ipcomp = _set_ipcomp,
@@ -1863,6 +2063,7 @@ child_sa_t *child_sa_create(host_t *me, host_t *other, child_cfg_t *config,
.mark_out = config->get_mark(config, FALSE), .mark_out = config->get_mark(config, FALSE),
.if_id_in = config->get_if_id(config, TRUE) ?: data->if_id_in_def, .if_id_in = config->get_if_id(config, TRUE) ?: data->if_id_in_def,
.if_id_out = config->get_if_id(config, FALSE) ?: data->if_id_out_def, .if_id_out = config->get_if_id(config, FALSE) ?: data->if_id_out_def,
.label = data->label ? data->label->clone(data->label) : NULL,
.install_time = time_monotonic(NULL), .install_time = time_monotonic(NULL),
.policies_fwd_out = config->has_option(config, OPT_FWD_OUT_POLICIES), .policies_fwd_out = config->has_option(config, OPT_FWD_OUT_POLICIES),
); );
+12 -1
View File
@@ -369,6 +369,15 @@ struct child_sa_t {
*/ */
uint32_t (*get_if_id)(child_sa_t *this, bool inbound); uint32_t (*get_if_id)(child_sa_t *this, bool inbound);
/**
* Get the security label used with this CHILD_SA.
*
* This might be different than the configured label.
*
* @return security label used with this CHILD_SA
*/
sec_label_t *(*get_label)(child_sa_t *this);
/** /**
* Create an enumerator over traffic selectors of one side. * Create an enumerator over traffic selectors of one side.
* *
@@ -533,6 +542,8 @@ struct child_sa_create_t {
/** Optional default outbound interface ID, if neither if_id_out, nor config /** Optional default outbound interface ID, if neither if_id_out, nor config
* sets one */ * sets one */
uint32_t if_id_out_def; uint32_t if_id_out_def;
/** Optional security label to apply on SAs (cloned) */
sec_label_t *label;
/** TRUE to enable UDP encapsulation (NAT traversal) */ /** TRUE to enable UDP encapsulation (NAT traversal) */
bool encap; bool encap;
}; };
@@ -546,7 +557,7 @@ struct child_sa_create_t {
* @param data data for this CHILD_SA * @param data data for this CHILD_SA
* @return child_sa_t object * @return child_sa_t object
*/ */
child_sa_t *child_sa_create(host_t *me, host_t *other, child_cfg_t *config, child_sa_t *child_sa_create(host_t *me, host_t *other, child_cfg_t *config,
child_sa_create_t *data); child_sa_create_t *data);
#endif /** CHILD_SA_H_ @}*/ #endif /** CHILD_SA_H_ @}*/
+68 -21
View File
@@ -1531,8 +1531,7 @@ static void resolve_hosts(private_ike_sa_t *this)
} }
METHOD(ike_sa_t, initiate, status_t, METHOD(ike_sa_t, initiate, status_t,
private_ike_sa_t *this, child_cfg_t *child_cfg, uint32_t reqid, private_ike_sa_t *this, child_cfg_t *child_cfg, child_init_args_t *args)
traffic_selector_t *tsi, traffic_selector_t *tsr)
{ {
bool defer_initiate = FALSE; bool defer_initiate = FALSE;
@@ -1587,8 +1586,7 @@ METHOD(ike_sa_t, initiate, status_t,
if (child_cfg) if (child_cfg)
{ {
/* normal IKE_SA with CHILD_SA */ /* normal IKE_SA with CHILD_SA */
this->task_manager->queue_child(this->task_manager, child_cfg, reqid, this->task_manager->queue_child(this->task_manager, child_cfg, args);
tsi, tsr);
#ifdef ME #ifdef ME
if (this->peer_cfg->get_mediated_by(this->peer_cfg)) if (this->peer_cfg->get_mediated_by(this->peer_cfg))
{ {
@@ -1621,7 +1619,7 @@ METHOD(ike_sa_t, retry_initiate, status_t,
if (this->retry_initiate_queued) if (this->retry_initiate_queued)
{ {
this->retry_initiate_queued = FALSE; this->retry_initiate_queued = FALSE;
return initiate(this, NULL, 0, NULL, NULL); return initiate(this, NULL, NULL);
} }
return SUCCESS; return SUCCESS;
} }
@@ -2062,7 +2060,7 @@ static status_t reestablish_children(private_ike_sa_t *this, ike_sa_t *new,
} }
if (force) if (force)
{ {
action = ACTION_RESTART; action = ACTION_START;
} }
else else
{ /* only restart CHILD_SAs that are configured accordingly */ { /* only restart CHILD_SAs that are configured accordingly */
@@ -2075,15 +2073,18 @@ static status_t reestablish_children(private_ike_sa_t *this, ike_sa_t *new,
action = child_sa->get_dpd_action(child_sa); action = child_sa->get_dpd_action(child_sa);
} }
} }
if (action == ACTION_RESTART) if (action & ACTION_START)
{ {
child_init_args_t args = {
.reqid = child_sa->get_reqid(child_sa),
.label = child_sa->get_label(child_sa),
};
child_cfg = child_sa->get_config(child_sa); child_cfg = child_sa->get_config(child_sa);
DBG1(DBG_IKE, "restarting CHILD_SA %s", DBG1(DBG_IKE, "restarting CHILD_SA %s",
child_cfg->get_name(child_cfg)); child_cfg->get_name(child_cfg));
other->task_manager->queue_child(other->task_manager, other->task_manager->queue_child(other->task_manager,
child_cfg->get_ref(child_cfg), child_cfg->get_ref(child_cfg),
child_sa->get_reqid(child_sa), &args);
NULL, NULL);
} }
} }
enumerator->destroy(enumerator); enumerator->destroy(enumerator);
@@ -2091,7 +2092,7 @@ static status_t reestablish_children(private_ike_sa_t *this, ike_sa_t *new,
/* adopt any active or queued CHILD-creating tasks */ /* adopt any active or queued CHILD-creating tasks */
new->adopt_child_tasks(new, &this->public); new->adopt_child_tasks(new, &this->public);
return new->initiate(new, NULL, 0, NULL, NULL); return new->initiate(new, NULL, NULL);
} }
METHOD(ike_sa_t, reestablish, status_t, METHOD(ike_sa_t, reestablish, status_t,
@@ -2150,17 +2151,14 @@ METHOD(ike_sa_t, reestablish, status_t,
{ {
action = child_sa->get_dpd_action(child_sa); action = child_sa->get_dpd_action(child_sa);
} }
switch (action) if (action & ACTION_TRAP)
{ {
case ACTION_RESTART: charon->traps->install(charon->traps, this->peer_cfg,
restart = TRUE; child_sa->get_config(child_sa));
break; }
case ACTION_ROUTE: if (action & ACTION_START)
charon->traps->install(charon->traps, this->peer_cfg, {
child_sa->get_config(child_sa)); restart = TRUE;
break;
default:
break;
} }
} }
enumerator->destroy(enumerator); enumerator->destroy(enumerator);
@@ -2224,7 +2222,7 @@ METHOD(ike_sa_t, reestablish, status_t,
#ifdef ME #ifdef ME
if (this->peer_cfg->is_mediation(this->peer_cfg)) if (this->peer_cfg->is_mediation(this->peer_cfg))
{ {
status = new->initiate(new, NULL, 0, NULL, NULL); status = new->initiate(new, NULL, NULL);
} }
else else
#endif /* ME */ #endif /* ME */
@@ -3264,3 +3262,52 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
} }
return &this->public; return &this->public;
} }
/**
* Check if we have a an address pool configured.
*/
static bool have_pool(private_ike_sa_t *this)
{
enumerator_t *enumerator;
bool found = FALSE;
if (this->peer_cfg)
{
enumerator = this->peer_cfg->create_pool_enumerator(this->peer_cfg);
found = enumerator->enumerate(enumerator, NULL);
enumerator->destroy(enumerator);
}
return found;
}
/*
* Described in header
*/
linked_list_t *ike_sa_get_dynamic_hosts(ike_sa_t *ike_sa, bool local)
{
private_ike_sa_t *this = (private_ike_sa_t*)ike_sa;
enumerator_t *enumerator;
linked_list_t *list;
host_t *host;
list = linked_list_create();
enumerator = create_virtual_ip_enumerator(this, local);
while (enumerator->enumerate(enumerator, &host))
{
list->insert_last(list, host);
}
enumerator->destroy(enumerator);
if (!list->get_count(list))
{ /* no virtual IPs assigned */
if (local)
{
list->insert_last(list, this->my_host);
}
else if (!have_pool(this))
{ /* use remote host only if we don't have a pool configured */
list->insert_last(list, this->other_host);
}
}
return list;
}
+29 -7
View File
@@ -29,6 +29,7 @@ typedef enum ike_condition_t ike_condition_t;
typedef enum ike_sa_state_t ike_sa_state_t; typedef enum ike_sa_state_t ike_sa_state_t;
typedef enum statistic_t statistic_t; typedef enum statistic_t statistic_t;
typedef enum update_hosts_flag_t update_hosts_flag_t; typedef enum update_hosts_flag_t update_hosts_flag_t;
typedef struct child_init_args_t child_init_args_t;
typedef struct ike_sa_t ike_sa_t; typedef struct ike_sa_t ike_sa_t;
#include <library.h> #include <library.h>
@@ -369,6 +370,20 @@ enum ike_sa_state_t {
*/ */
extern enum_name_t *ike_sa_state_names; extern enum_name_t *ike_sa_state_names;
/**
* Optional arguments passed when initiating a CHILD_SA.
*/
struct child_init_args_t {
/** Reqid to use for CHILD_SA, 0 to assign automatically */
uint32_t reqid;
/** Optional source of triggering packet */
traffic_selector_t *src;
/** Optional destination of triggering packet */
traffic_selector_t *dst;
/** Optional security label of triggering packet */
sec_label_t *label;
};
/** /**
* Class ike_sa_t representing an IKE_SA. * Class ike_sa_t representing an IKE_SA.
* *
@@ -787,16 +802,13 @@ struct ike_sa_t {
* to the CHILD_SA. * to the CHILD_SA.
* *
* @param child_cfg child config to create CHILD from * @param child_cfg child config to create CHILD from
* @param reqid reqid to use for CHILD_SA, 0 assign uniquely * @param args optional arguments for the CHILD initiation
* @param tsi source of triggering packet
* @param tsr destination of triggering packet.
* @return * @return
* - SUCCESS if initialization started * - SUCCESS if initialization started
* - DESTROY_ME if initialization failed * - DESTROY_ME if initialization failed
*/ */
status_t (*initiate) (ike_sa_t *this, child_cfg_t *child_cfg, status_t (*initiate) (ike_sa_t *this, child_cfg_t *child_cfg,
uint32_t reqid, traffic_selector_t *tsi, child_init_args_t *args);
traffic_selector_t *tsr);
/** /**
* Retry initiation of this IKE_SA after it got deferred previously. * Retry initiation of this IKE_SA after it got deferred previously.
@@ -1238,7 +1250,7 @@ struct ike_sa_t {
* @param ike_sa_id ike_sa_id_t to associate with new IKE_SA/ISAKMP_SA * @param ike_sa_id ike_sa_id_t to associate with new IKE_SA/ISAKMP_SA
* @param initiator TRUE to create this IKE_SA as initiator * @param initiator TRUE to create this IKE_SA as initiator
* @param version IKE version of this SA * @param version IKE version of this SA
* @return ike_sa_t object * @return ike_sa_t object
*/ */
ike_sa_t *ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, ike_sa_t *ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
ike_version_t version); ike_version_t version);
@@ -1247,8 +1259,18 @@ ike_sa_t *ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
* Check if the given IKE_SA can be reauthenticated actively or if config * Check if the given IKE_SA can be reauthenticated actively or if config
* parameters or the authentication method prevent it. * parameters or the authentication method prevent it.
* *
* @return TRUE if active reauthentication is possible * @param this IKE_SA to check
* @return TRUE if active reauthentication is possible
*/ */
bool ike_sa_can_reauthenticate(ike_sa_t *this); bool ike_sa_can_reauthenticate(ike_sa_t *this);
/**
* Get hosts, virtual or physical, for deriving dynamic traffic selectors.
*
* @param this IKE_SA to retrieve addresses from
* @param local TRUE to get local hosts
* @return list of hosts (internal objects)
*/
linked_list_t *ike_sa_get_dynamic_hosts(ike_sa_t *this, bool local);
#endif /** IKE_SA_H_ @}*/ #endif /** IKE_SA_H_ @}*/
+11 -6
View File
@@ -1685,7 +1685,7 @@ METHOD(task_manager_t, queue_ike_reauth, void,
enumerator->destroy(enumerator); enumerator->destroy(enumerator);
} }
if (new->initiate(new, NULL, 0, NULL, NULL) != DESTROY_ME) if (new->initiate(new, NULL, NULL) != DESTROY_ME)
{ {
charon->ike_sa_manager->checkin(charon->ike_sa_manager, new); charon->ike_sa_manager->checkin(charon->ike_sa_manager, new);
this->ike_sa->set_state(this->ike_sa, IKE_REKEYING); this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
@@ -1732,14 +1732,19 @@ METHOD(task_manager_t, queue_mobike, void,
} }
METHOD(task_manager_t, queue_child, void, METHOD(task_manager_t, queue_child, void,
private_task_manager_t *this, child_cfg_t *cfg, uint32_t reqid, private_task_manager_t *this, child_cfg_t *cfg, child_init_args_t *args)
traffic_selector_t *tsi, traffic_selector_t *tsr)
{ {
quick_mode_t *task; quick_mode_t *task;
task = quick_mode_create(this->ike_sa, cfg, tsi, tsr); if (args)
task->use_reqid(task, reqid); {
task = quick_mode_create(this->ike_sa, cfg, args->src, args->dst);
task->use_reqid(task, args->reqid);
}
else
{
task = quick_mode_create(this->ike_sa, cfg, NULL, NULL);
}
queue_task(this, &task->task); queue_task(this, &task->task);
} }
+15 -14
View File
@@ -149,23 +149,24 @@ static status_t delete_child(private_quick_delete_t *this,
if (remote_close) if (remote_close)
{ {
child_init_args_t args = {
.reqid = child_sa->get_reqid(child_sa),
};
action_t action;
action = child_sa->get_close_action(child_sa);
child_cfg = child_sa->get_config(child_sa); child_cfg = child_sa->get_config(child_sa);
child_cfg->get_ref(child_cfg); child_cfg->get_ref(child_cfg);
if (action & ACTION_TRAP)
switch (child_sa->get_close_action(child_sa))
{ {
case ACTION_RESTART: charon->traps->install(charon->traps,
child_cfg->get_ref(child_cfg); this->ike_sa->get_peer_cfg(this->ike_sa),
status = this->ike_sa->initiate(this->ike_sa, child_cfg, child_cfg);
child_sa->get_reqid(child_sa), NULL, NULL); }
break; if (action & ACTION_START)
case ACTION_ROUTE: {
charon->traps->install(charon->traps, child_cfg->get_ref(child_cfg);
this->ike_sa->get_peer_cfg(this->ike_sa), status = this->ike_sa->initiate(this->ike_sa, child_cfg, &args);
child_cfg);
break;
default:
break;
} }
child_cfg->destroy(child_cfg); child_cfg->destroy(child_cfg);
} }
+1 -2
View File
@@ -1096,7 +1096,7 @@ METHOD(task_t, process_r, status_t,
hostsi = get_dynamic_hosts(this->ike_sa, FALSE); hostsi = get_dynamic_hosts(this->ike_sa, FALSE);
hostsr = get_dynamic_hosts(this->ike_sa, TRUE); hostsr = get_dynamic_hosts(this->ike_sa, TRUE);
this->config = peer_cfg->select_child_cfg(peer_cfg, tsr, tsi, this->config = peer_cfg->select_child_cfg(peer_cfg, tsr, tsi,
hostsr, hostsi); hostsr, hostsi, NULL, NULL);
hostsi->destroy(hostsi); hostsi->destroy(hostsi);
hostsr->destroy(hostsr); hostsr->destroy(hostsr);
if (this->config) if (this->config)
@@ -1475,7 +1475,6 @@ METHOD(task_t, migrate, void,
this->dh = NULL; this->dh = NULL;
this->spi_i = 0; this->spi_i = 0;
this->spi_r = 0; this->spi_r = 0;
this->child = (child_sa_create_t){};
if (!this->initiator) if (!this->initiator)
{ {
+10 -6
View File
@@ -1983,7 +1983,7 @@ static void trigger_mbb_reauth(private_task_manager_t *this)
/* suspend online revocation checking until the SA is established */ /* suspend online revocation checking until the SA is established */
new->set_condition(new, COND_ONLINE_VALIDATION_SUSPENDED, TRUE); new->set_condition(new, COND_ONLINE_VALIDATION_SUSPENDED, TRUE);
if (new->initiate(new, NULL, 0, NULL, NULL) != DESTROY_ME) if (new->initiate(new, NULL, NULL) != DESTROY_ME)
{ {
new->queue_task(new, (task_t*)ike_verify_peer_cert_create(new)); new->queue_task(new, (task_t*)ike_verify_peer_cert_create(new));
new->queue_task(new, (task_t*)ike_reauth_complete_create(new, new->queue_task(new, (task_t*)ike_reauth_complete_create(new,
@@ -2102,15 +2102,19 @@ METHOD(task_manager_t, queue_dpd, void,
} }
METHOD(task_manager_t, queue_child, void, METHOD(task_manager_t, queue_child, void,
private_task_manager_t *this, child_cfg_t *cfg, uint32_t reqid, private_task_manager_t *this, child_cfg_t *cfg, child_init_args_t *args)
traffic_selector_t *tsi, traffic_selector_t *tsr)
{ {
child_create_t *task; child_create_t *task;
task = child_create_create(this->ike_sa, cfg, FALSE, tsi, tsr); if (args)
if (reqid)
{ {
task->use_reqid(task, reqid); task = child_create_create(this->ike_sa, cfg, FALSE, args->src, args->dst);
task->use_reqid(task, args->reqid);
task->use_label(task, args->label);
}
else
{
task = child_create_create(this->ike_sa, cfg, FALSE, NULL, NULL);
} }
queue_task(this, &task->task); queue_task(this, &task->task);
} }
+236 -73
View File
@@ -84,15 +84,25 @@ struct private_child_create_t {
proposal_t *proposal; proposal_t *proposal;
/** /**
* traffic selectors for initiators side * traffic selectors for initiator side
*/ */
linked_list_t *tsi; linked_list_t *tsi;
/** /**
* traffic selectors for responders side * traffic selectors for responder side
*/ */
linked_list_t *tsr; linked_list_t *tsr;
/**
* labels for initiator side
*/
linked_list_t *labels_i;
/**
* labels for responder side
*/
linked_list_t *labels_r;
/** /**
* source of triggering packet * source of triggering packet
*/ */
@@ -210,6 +220,7 @@ static void schedule_delayed_retry(private_child_create_t *this)
task->use_reqid(task, this->child.reqid); task->use_reqid(task, this->child.reqid);
task->use_marks(task, this->child.mark_in, this->child.mark_out); task->use_marks(task, this->child.mark_in, this->child.mark_out);
task->use_if_ids(task, this->child.if_id_in, this->child.if_id_out); task->use_if_ids(task, this->child.if_id_in, this->child.if_id_out);
task->use_label(task, this->child.label);
DBG1(DBG_IKE, "creating CHILD_SA failed, trying again in %d seconds", DBG1(DBG_IKE, "creating CHILD_SA failed, trying again in %d seconds",
retry); retry);
@@ -358,62 +369,6 @@ static void schedule_inactivity_timeout(private_child_create_t *this)
} }
} }
/**
* Check if we have a an address pool configured
*/
static bool have_pool(ike_sa_t *ike_sa)
{
enumerator_t *enumerator;
peer_cfg_t *peer_cfg;
char *pool;
bool found = FALSE;
peer_cfg = ike_sa->get_peer_cfg(ike_sa);
if (peer_cfg)
{
enumerator = peer_cfg->create_pool_enumerator(peer_cfg);
if (enumerator->enumerate(enumerator, &pool))
{
found = TRUE;
}
enumerator->destroy(enumerator);
}
return found;
}
/**
* Get hosts to use for dynamic traffic selectors
*/
static linked_list_t *get_dynamic_hosts(ike_sa_t *ike_sa, bool local)
{
enumerator_t *enumerator;
linked_list_t *list;
host_t *host;
list = linked_list_create();
enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, local);
while (enumerator->enumerate(enumerator, &host))
{
list->insert_last(list, host);
}
enumerator->destroy(enumerator);
if (list->get_count(list) == 0)
{ /* no virtual IPs assigned */
if (local)
{
host = ike_sa->get_my_host(ike_sa);
list->insert_last(list, host);
}
else if (!have_pool(ike_sa))
{ /* use host only if we don't have a pool configured */
host = ike_sa->get_other_host(ike_sa);
list->insert_last(list, host);
}
}
return list;
}
/** /**
* Substitute any host address with NATed address in traffic selector * Substitute any host address with NATed address in traffic selector
*/ */
@@ -468,7 +423,7 @@ static linked_list_t* narrow_ts(private_child_create_t *this, bool local,
ike_condition_t cond; ike_condition_t cond;
cond = local ? COND_NAT_HERE : COND_NAT_THERE; cond = local ? COND_NAT_HERE : COND_NAT_THERE;
hosts = get_dynamic_hosts(this->ike_sa, local); hosts = ike_sa_get_dynamic_hosts(this->ike_sa, local);
if (this->mode == MODE_TRANSPORT && if (this->mode == MODE_TRANSPORT &&
this->ike_sa->has_condition(this->ike_sa, cond)) this->ike_sa->has_condition(this->ike_sa, cond))
@@ -891,9 +846,11 @@ static bool build_payloads(private_child_create_t *this, message_t *message)
} }
/* add TSi/TSr payloads */ /* add TSi/TSr payloads */
ts_payload = ts_payload_create_from_traffic_selectors(TRUE, this->tsi); ts_payload = ts_payload_create_from_traffic_selectors(TRUE, this->tsi,
this->child.label);
message->add_payload(message, (payload_t*)ts_payload); message->add_payload(message, (payload_t*)ts_payload);
ts_payload = ts_payload_create_from_traffic_selectors(FALSE, this->tsr); ts_payload = ts_payload_create_from_traffic_selectors(FALSE, this->tsr,
this->child.label);
message->add_payload(message, (payload_t*)ts_payload); message->add_payload(message, (payload_t*)ts_payload);
/* add a notify if we are not in tunnel mode */ /* add a notify if we are not in tunnel mode */
@@ -1039,10 +996,12 @@ static void process_payloads(private_child_create_t *this, message_t *message)
case PLV2_TS_INITIATOR: case PLV2_TS_INITIATOR:
ts_payload = (ts_payload_t*)payload; ts_payload = (ts_payload_t*)payload;
this->tsi = ts_payload->get_traffic_selectors(ts_payload); this->tsi = ts_payload->get_traffic_selectors(ts_payload);
this->labels_i = ts_payload->get_sec_labels(ts_payload);
break; break;
case PLV2_TS_RESPONDER: case PLV2_TS_RESPONDER:
ts_payload = (ts_payload_t*)payload; ts_payload = (ts_payload_t*)payload;
this->tsr = ts_payload->get_traffic_selectors(ts_payload); this->tsr = ts_payload->get_traffic_selectors(ts_payload);
this->labels_r = ts_payload->get_sec_labels(ts_payload);
break; break;
case PLV2_NOTIFY: case PLV2_NOTIFY:
handle_notify(this, (notify_payload_t*)payload); handle_notify(this, (notify_payload_t*)payload);
@@ -1054,6 +1013,16 @@ static void process_payloads(private_child_create_t *this, message_t *message)
enumerator->destroy(enumerator); enumerator->destroy(enumerator);
} }
/**
* Check if we have only the generic label available when using SELinux and not
* a specific one from an acquire.
*/
static bool generic_label_only(private_child_create_t *this)
{
return this->config->get_label(this->config) && !this->child.label &&
this->config->get_label_mode(this->config) == SEC_LABEL_MODE_SELINUX;
}
/** /**
* Check if we should defer the creation of this CHILD_SA until after the * Check if we should defer the creation of this CHILD_SA until after the
* IKE_SA has been established childless. * IKE_SA has been established childless.
@@ -1061,17 +1030,23 @@ static void process_payloads(private_child_create_t *this, message_t *message)
static status_t defer_child_sa(private_child_create_t *this) static status_t defer_child_sa(private_child_create_t *this)
{ {
ike_cfg_t *ike_cfg; ike_cfg_t *ike_cfg;
childless_t policy;
ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
policy = ike_cfg->childless(ike_cfg);
if (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_CHILDLESS)) if (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_CHILDLESS))
{ {
if (ike_cfg->childless(ike_cfg) == CHILDLESS_FORCE) /* with SELinux, we prefer not to create a CHILD_SA when we only have
* the generic label available. if the peer does not support it,
* creating the SA will most likely fail */
if (policy == CHILDLESS_FORCE ||
generic_label_only(this))
{ {
return NEED_MORE; return NEED_MORE;
} }
} }
else if (ike_cfg->childless(ike_cfg) == CHILDLESS_FORCE) else if (policy == CHILDLESS_FORCE)
{ {
DBG1(DBG_IKE, "peer does not support childless IKE_SA initiation"); DBG1(DBG_IKE, "peer does not support childless IKE_SA initiation");
return DESTROY_ME; return DESTROY_ME;
@@ -1079,6 +1054,88 @@ static status_t defer_child_sa(private_child_create_t *this)
return NOT_SUPPORTED; return NOT_SUPPORTED;
} }
/**
* Compare two CHILD_SA objects for equality
*/
static bool child_sa_equals(child_sa_t *a, child_sa_t *b)
{
child_cfg_t *cfg = a->get_config(a);
return cfg->equals(cfg, b->get_config(b)) &&
/* reqids are allocated based on the final TS, so we can only compare
* them if they are static (i.e. both have them) */
(!a->get_reqid(a) || !b->get_reqid(b) ||
a->get_reqid(a) == b->get_reqid(b)) &&
a->get_mark(a, TRUE).value == b->get_mark(b, TRUE).value &&
a->get_mark(a, FALSE).value == b->get_mark(b, FALSE).value &&
a->get_if_id(a, TRUE) == b->get_if_id(b, TRUE) &&
a->get_if_id(a, FALSE) == b->get_if_id(b, FALSE) &&
sec_labels_equal(a->get_label(a), b->get_label(b));
}
/**
* Check if there is a duplicate CHILD_SA already established and we can abort
* initiating this one.
*/
static bool check_for_duplicate(private_child_create_t *this)
{
enumerator_t *enumerator;
child_sa_t *child_sa, *found = NULL;
enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa);
while (enumerator->enumerate(enumerator, (void**)&child_sa))
{
if (child_sa->get_state(child_sa) == CHILD_INSTALLED &&
child_sa_equals(child_sa, this->child_sa))
{
found = child_sa;
break;
}
}
enumerator->destroy(enumerator);
if (found)
{
linked_list_t *my_ts, *other_ts;
my_ts = linked_list_create_from_enumerator(
found->create_ts_enumerator(found, TRUE));
other_ts = linked_list_create_from_enumerator(
found->create_ts_enumerator(found, FALSE));
DBG1(DBG_IKE, "not establishing CHILD_SA %s{%d} due to existing "
"duplicate {%d} with SPIs %.8x_i %.8x_o and TS %#R === %#R",
this->child_sa->get_name(this->child_sa),
this->child_sa->get_unique_id(this->child_sa),
found->get_unique_id(found),
ntohl(found->get_spi(found, TRUE)),
ntohl(found->get_spi(found, FALSE)), my_ts, other_ts);
my_ts->destroy(my_ts);
other_ts->destroy(other_ts);
}
return found;
}
/**
* Check if this is an attempt to create an SA with generic label and should
* be aborted.
*/
static bool check_for_generic_label(private_child_create_t *this)
{
if (generic_label_only(this))
{
sec_label_t *label;
label = this->config->get_label(this->config);
DBG1(DBG_IKE, "not establishing CHILD_SA %s{%d} with generic "
"label '%s'", this->child_sa->get_name(this->child_sa),
this->child_sa->get_unique_id(this->child_sa),
label->get_string(label));
return TRUE;
}
return FALSE;
}
METHOD(task_t, build_i, status_t, METHOD(task_t, build_i, status_t,
private_child_create_t *this, message_t *message) private_child_create_t *this, message_t *message)
{ {
@@ -1094,8 +1151,7 @@ METHOD(task_t, build_i, status_t,
case CREATE_CHILD_SA: case CREATE_CHILD_SA:
if (!generate_nonce(this)) if (!generate_nonce(this))
{ {
message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, message->set_exchange_type(message, EXCHANGE_TYPE_UNDEFINED);
chunk_empty);
return SUCCESS; return SUCCESS;
} }
if (!this->retry && this->dh_group == MODP_NONE) if (!this->retry && this->dh_group == MODP_NONE)
@@ -1150,12 +1206,12 @@ METHOD(task_t, build_i, status_t,
else else
{ /* no virtual IPs configured */ { /* no virtual IPs configured */
list->destroy(list); list->destroy(list);
list = get_dynamic_hosts(this->ike_sa, TRUE); list = ike_sa_get_dynamic_hosts(this->ike_sa, TRUE);
this->tsi = this->config->get_traffic_selectors(this->config, this->tsi = this->config->get_traffic_selectors(this->config,
TRUE, NULL, list, TRUE); TRUE, NULL, list, TRUE);
list->destroy(list); list->destroy(list);
} }
list = get_dynamic_hosts(this->ike_sa, FALSE); list = ike_sa_get_dynamic_hosts(this->ike_sa, FALSE);
this->tsr = this->config->get_traffic_selectors(this->config, this->tsr = this->config->get_traffic_selectors(this->config,
FALSE, NULL, list, TRUE); FALSE, NULL, list, TRUE);
list->destroy(list); list->destroy(list);
@@ -1170,6 +1226,22 @@ METHOD(task_t, build_i, status_t,
this->tsr->insert_first(this->tsr, this->tsr->insert_first(this->tsr,
this->packet_tsr->clone(this->packet_tsr)); this->packet_tsr->clone(this->packet_tsr));
} }
if (!generic_label_only(this) && !this->child.label)
{ /* in the simple label mode we propose the configured label as we
* won't have labels from acquires */
this->child.label = this->config->get_label(this->config);
if (this->child.label)
{
this->child.label = this->child.label->clone(this->child.label);
}
}
if (this->child.label)
{
DBG2(DBG_CFG, "proposing security label '%s'",
this->child.label->get_string(this->child.label));
}
this->proposals = this->config->get_proposals(this->config, this->proposals = this->config->get_proposals(this->config,
this->dh_group == MODP_NONE); this->dh_group == MODP_NONE);
this->mode = this->config->get_mode(this->config); this->mode = this->config->get_mode(this->config);
@@ -1181,6 +1253,16 @@ METHOD(task_t, build_i, status_t,
this->ike_sa->get_other_host(this->ike_sa), this->ike_sa->get_other_host(this->ike_sa),
this->config, &this->child); this->config, &this->child);
/* check this after creating the object so that its destruction is detected
* by controller and trap manager */
if (!this->rekey &&
message->get_exchange_type(message) == CREATE_CHILD_SA &&
(check_for_generic_label(this) || check_for_duplicate(this)))
{
message->set_exchange_type(message, EXCHANGE_TYPE_UNDEFINED);
return SUCCESS;
}
if (this->child.reqid) if (this->child.reqid)
{ {
DBG0(DBG_IKE, "establishing CHILD_SA %s{%d} reqid %d", DBG0(DBG_IKE, "establishing CHILD_SA %s{%d} reqid %d",
@@ -1340,11 +1422,11 @@ static child_cfg_t* select_child_cfg(private_child_create_t *this)
tsr = get_ts_if_nat_transport(this, TRUE, this->tsr); tsr = get_ts_if_nat_transport(this, TRUE, this->tsr);
tsi = get_ts_if_nat_transport(this, FALSE, this->tsi); tsi = get_ts_if_nat_transport(this, FALSE, this->tsi);
listr = get_dynamic_hosts(this->ike_sa, TRUE); listr = ike_sa_get_dynamic_hosts(this->ike_sa, TRUE);
listi = get_dynamic_hosts(this->ike_sa, FALSE); listi = ike_sa_get_dynamic_hosts(this->ike_sa, FALSE);
child_cfg = peer_cfg->select_child_cfg(peer_cfg, child_cfg = peer_cfg->select_child_cfg(peer_cfg,
tsr ?: this->tsr, tsi ?: this->tsi, tsr ?: this->tsr, tsi ?: this->tsi,
listr, listi); listr, listi, this->labels_r, this->labels_i);
if ((tsi || tsr) && child_cfg && if ((tsi || tsr) && child_cfg &&
child_cfg->get_mode(child_cfg) != MODE_TRANSPORT) child_cfg->get_mode(child_cfg) != MODE_TRANSPORT)
{ {
@@ -1356,7 +1438,8 @@ static child_cfg_t* select_child_cfg(private_child_create_t *this)
{ {
/* no match for the substituted NAT selectors, try it without */ /* no match for the substituted NAT selectors, try it without */
child_cfg = peer_cfg->select_child_cfg(peer_cfg, child_cfg = peer_cfg->select_child_cfg(peer_cfg,
this->tsr, this->tsi, listr, listi); this->tsr, this->tsi,
listr, listi, this->labels_r, this->labels_i);
} }
listr->destroy(listr); listr->destroy(listr);
listi->destroy(listi); listi->destroy(listi);
@@ -1398,6 +1481,49 @@ static status_t handle_childless(private_child_create_t *this)
return NOT_SUPPORTED; return NOT_SUPPORTED;
} }
/**
* Select a security label.
*
* We already know that the proposed labels match the selected config, just make
* sure that the proposed/returned labels are the same.
*/
static bool select_label(private_child_create_t *this)
{
sec_label_t *li, *lr;
if (!this->config->select_label(this->config, this->labels_i, FALSE, &li, NULL) ||
!this->config->select_label(this->config, this->labels_r, FALSE, &lr, NULL))
{ /* sanity check */
return FALSE;
}
if (li)
{
if (!li->equals(li, lr))
{
DBG1(DBG_CHD, "security labels in TSi and TSr don't match");
return FALSE;
}
else if (!this->child.label)
{
this->child.label = li->clone(li);
}
else if (!this->child.label->equals(this->child.label, li))
{
DBG1(DBG_CHD, "returned security label '%s' doesn't match proposed "
"'%s'", li->get_string(li),
this->child.label->get_string(this->child.label));
return FALSE;
}
}
if (this->child.label)
{
DBG1(DBG_CFG, "selected security label: %s",
this->child.label->get_string(this->child.label));
}
return TRUE;
}
METHOD(task_t, build_r, status_t, METHOD(task_t, build_r, status_t,
private_child_create_t *this, message_t *message) private_child_create_t *this, message_t *message)
{ {
@@ -1505,6 +1631,13 @@ METHOD(task_t, build_r, status_t,
} }
enumerator->destroy(enumerator); enumerator->destroy(enumerator);
if (!select_label(this))
{
message->add_notify(message, FALSE, TS_UNACCEPTABLE, chunk_empty);
handle_child_sa_failure(this, message);
return SUCCESS;
}
this->child.if_id_in_def = this->ike_sa->get_if_id(this->ike_sa, TRUE); this->child.if_id_in_def = this->ike_sa->get_if_id(this->ike_sa, TRUE);
this->child.if_id_out_def = this->ike_sa->get_if_id(this->ike_sa, FALSE); this->child.if_id_out_def = this->ike_sa->get_if_id(this->ike_sa, FALSE);
this->child.encap = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY); this->child.encap = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY);
@@ -1762,6 +1895,12 @@ METHOD(task_t, process_i, status_t,
return delete_failed_sa(this); return delete_failed_sa(this);
} }
if (!select_label(this))
{
handle_child_sa_failure(this, message);
return delete_failed_sa(this);
}
if (select_and_install(this, no_dh, ike_auth) == SUCCESS) if (select_and_install(this, no_dh, ike_auth) == SUCCESS)
{ {
if (!this->rekey) if (!this->rekey)
@@ -1797,6 +1936,13 @@ METHOD(child_create_t, use_if_ids, void,
this->child.if_id_out = out; this->child.if_id_out = out;
} }
METHOD(child_create_t, use_label, void,
private_child_create_t *this, sec_label_t *label)
{
DESTROY_IF(this->child.label);
this->child.label = label ? label->clone(label) : NULL;
}
METHOD(child_create_t, use_dh_group, void, METHOD(child_create_t, use_dh_group, void,
private_child_create_t *this, diffie_hellman_group_t dh_group) private_child_create_t *this, diffie_hellman_group_t dh_group)
{ {
@@ -1849,6 +1995,14 @@ METHOD(task_t, migrate, void,
{ {
this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
} }
if (this->labels_i)
{
this->labels_i->destroy_offset(this->labels_i, offsetof(sec_label_t, destroy));
}
if (this->labels_r)
{
this->labels_r->destroy_offset(this->labels_r, offsetof(sec_label_t, destroy));
}
DESTROY_IF(this->child_sa); DESTROY_IF(this->child_sa);
DESTROY_IF(this->proposal); DESTROY_IF(this->proposal);
DESTROY_IF(this->nonceg); DESTROY_IF(this->nonceg);
@@ -1876,7 +2030,6 @@ METHOD(task_t, migrate, void,
this->ipcomp_received = IPCOMP_NONE; this->ipcomp_received = IPCOMP_NONE;
this->other_cpi = 0; this->other_cpi = 0;
this->established = FALSE; this->established = FALSE;
this->child = (child_sa_create_t){};
} }
METHOD(task_t, destroy, void, METHOD(task_t, destroy, void,
@@ -1892,6 +2045,14 @@ METHOD(task_t, destroy, void,
{ {
this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
} }
if (this->labels_i)
{
this->labels_i->destroy_offset(this->labels_i, offsetof(sec_label_t, destroy));
}
if (this->labels_r)
{
this->labels_r->destroy_offset(this->labels_r, offsetof(sec_label_t, destroy));
}
if (!this->established) if (!this->established)
{ {
DESTROY_IF(this->child_sa); DESTROY_IF(this->child_sa);
@@ -1906,6 +2067,7 @@ METHOD(task_t, destroy, void,
} }
DESTROY_IF(this->config); DESTROY_IF(this->config);
DESTROY_IF(this->nonceg); DESTROY_IF(this->nonceg);
DESTROY_IF(this->child.label);
free(this); free(this);
} }
@@ -1926,6 +2088,7 @@ child_create_t *child_create_create(ike_sa_t *ike_sa,
.use_reqid = _use_reqid, .use_reqid = _use_reqid,
.use_marks = _use_marks, .use_marks = _use_marks,
.use_if_ids = _use_if_ids, .use_if_ids = _use_if_ids,
.use_label = _use_label,
.use_dh_group = _use_dh_group, .use_dh_group = _use_dh_group,
.task = { .task = {
.get_type = _get_type, .get_type = _get_type,
@@ -68,6 +68,13 @@ struct child_create_t {
*/ */
void (*use_if_ids)(child_create_t *this, uint32_t in, uint32_t out); void (*use_if_ids)(child_create_t *this, uint32_t in, uint32_t out);
/**
* Use specific security label, overriding configuration.
*
* @param label security label
*/
void (*use_label)(child_create_t *this, sec_label_t *label);
/** /**
* Initially propose a specific DH group to override configuration. * Initially propose a specific DH group to override configuration.
* *
+18 -15
View File
@@ -312,12 +312,13 @@ static void process_payloads(private_child_delete_t *this, message_t *message)
*/ */
static status_t destroy_and_reestablish(private_child_delete_t *this) static status_t destroy_and_reestablish(private_child_delete_t *this)
{ {
child_init_args_t args = {};
enumerator_t *enumerator; enumerator_t *enumerator;
entry_t *entry; entry_t *entry;
child_sa_t *child_sa; child_sa_t *child_sa;
child_cfg_t *child_cfg; child_cfg_t *child_cfg;
protocol_id_t protocol; protocol_id_t protocol;
uint32_t spi, reqid; uint32_t spi;
action_t action; action_t action;
status_t status = SUCCESS; status_t status = SUCCESS;
time_t now, expire; time_t now, expire;
@@ -362,32 +363,34 @@ static status_t destroy_and_reestablish(private_child_delete_t *this)
/* no delay and no lifetime, destroy it immediately */ /* no delay and no lifetime, destroy it immediately */
} }
spi = child_sa->get_spi(child_sa, TRUE); spi = child_sa->get_spi(child_sa, TRUE);
reqid = child_sa->get_reqid(child_sa);
child_cfg = child_sa->get_config(child_sa); child_cfg = child_sa->get_config(child_sa);
child_cfg->get_ref(child_cfg); child_cfg->get_ref(child_cfg);
args.reqid = child_sa->get_reqid(child_sa);
args.label = child_sa->get_label(child_sa);
if (args.label)
{
args.label = args.label->clone(args.label);
}
action = child_sa->get_close_action(child_sa); action = child_sa->get_close_action(child_sa);
this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi); this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi);
if (entry->check_delete_action) if (entry->check_delete_action)
{ /* enforce child_cfg policy if deleted passively */ { /* enforce child_cfg policy if deleted passively */
switch (action) if (action & ACTION_TRAP)
{ {
case ACTION_RESTART: charon->traps->install(charon->traps,
child_cfg->get_ref(child_cfg); this->ike_sa->get_peer_cfg(this->ike_sa),
status = this->ike_sa->initiate(this->ike_sa, child_cfg, child_cfg);
reqid, NULL, NULL); }
break; if (action & ACTION_START)
case ACTION_ROUTE: {
charon->traps->install(charon->traps, child_cfg->get_ref(child_cfg);
this->ike_sa->get_peer_cfg(this->ike_sa), status = this->ike_sa->initiate(this->ike_sa, child_cfg, &args);
child_cfg);
break;
default:
break;
} }
} }
child_cfg->destroy(child_cfg); child_cfg->destroy(child_cfg);
DESTROY_IF(args.label);
if (status != SUCCESS) if (status != SUCCESS)
{ {
break; break;
+16 -5
View File
@@ -210,6 +210,8 @@ METHOD(task_t, build_i, status_t,
this->child_create->use_if_ids(this->child_create, this->child_create->use_if_ids(this->child_create,
this->child_sa->get_if_id(this->child_sa, TRUE), this->child_sa->get_if_id(this->child_sa, TRUE),
this->child_sa->get_if_id(this->child_sa, FALSE)); this->child_sa->get_if_id(this->child_sa, FALSE));
this->child_create->use_label(this->child_create,
this->child_sa->get_label(this->child_sa));
if (this->child_create->task.build(&this->child_create->task, if (this->child_create->task.build(&this->child_create->task,
message) != NEED_MORE) message) != NEED_MORE)
@@ -272,6 +274,8 @@ METHOD(task_t, build_r, status_t,
this->child_create->use_if_ids(this->child_create, this->child_create->use_if_ids(this->child_create,
this->child_sa->get_if_id(this->child_sa, TRUE), this->child_sa->get_if_id(this->child_sa, TRUE),
this->child_sa->get_if_id(this->child_sa, FALSE)); this->child_sa->get_if_id(this->child_sa, FALSE));
this->child_create->use_label(this->child_create,
this->child_sa->get_label(this->child_sa));
config = this->child_sa->get_config(this->child_sa); config = this->child_sa->get_config(this->child_sa);
this->child_create->set_config(this->child_create, config->get_ref(config)); this->child_create->set_config(this->child_create, config->get_ref(config));
this->child_create->task.build(&this->child_create->task, message); this->child_create->task.build(&this->child_create->task, message);
@@ -391,7 +395,8 @@ METHOD(task_t, process_i, status_t,
if (message->get_notify(message, CHILD_SA_NOT_FOUND)) if (message->get_notify(message, CHILD_SA_NOT_FOUND))
{ {
child_cfg_t *child_cfg; child_cfg_t *child_cfg;
uint32_t reqid; child_init_args_t args = {};
status_t status;
if (this->collision && if (this->collision &&
this->collision->get_type(this->collision) == TASK_CHILD_DELETE) this->collision->get_type(this->collision) == TASK_CHILD_DELETE)
@@ -406,15 +411,21 @@ METHOD(task_t, process_i, status_t,
* that (we could go by name, but that might be tricky e.g. due to * that (we could go by name, but that might be tricky e.g. due to
* narrowing) */ * narrowing) */
spi = this->child_sa->get_spi(this->child_sa, TRUE); spi = this->child_sa->get_spi(this->child_sa, TRUE);
reqid = this->child_sa->get_reqid(this->child_sa);
protocol = this->child_sa->get_protocol(this->child_sa); protocol = this->child_sa->get_protocol(this->child_sa);
child_cfg = this->child_sa->get_config(this->child_sa); child_cfg = this->child_sa->get_config(this->child_sa);
child_cfg->get_ref(child_cfg); child_cfg->get_ref(child_cfg);
args.reqid = this->child_sa->get_reqid(this->child_sa);
args.label = this->child_sa->get_label(this->child_sa);
if (args.label)
{
args.label = args.label->clone(args.label);
}
charon->bus->child_updown(charon->bus, this->child_sa, FALSE); charon->bus->child_updown(charon->bus, this->child_sa, FALSE);
this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi); this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi);
return this->ike_sa->initiate(this->ike_sa, status = this->ike_sa->initiate(this->ike_sa,
child_cfg->get_ref(child_cfg), reqid, child_cfg->get_ref(child_cfg), &args);
NULL, NULL); DESTROY_IF(args.label);
return status;
} }
if (this->child_create->task.process(&this->child_create->task, if (this->child_create->task.process(&this->child_create->task,
+3 -5
View File
@@ -172,12 +172,10 @@ struct task_manager_t {
* Queue CHILD_SA establishing tasks. * Queue CHILD_SA establishing tasks.
* *
* @param cfg CHILD_SA config to establish * @param cfg CHILD_SA config to establish
* @param reqid reqid to use for CHILD_SA * @param args optional arguments for the initiation
* @param tsi initiator traffic selector, if packet-triggered
* @param tsr responder traffic selector, if packet-triggered
*/ */
void (*queue_child)(task_manager_t *this, child_cfg_t *cfg, uint32_t reqid, void (*queue_child)(task_manager_t *this, child_cfg_t *cfg,
traffic_selector_t *tsi, traffic_selector_t *tsr); child_init_args_t *args);
/** /**
* Queue CHILD_SA rekeying tasks. * Queue CHILD_SA rekeying tasks.
+148 -43
View File
@@ -106,6 +106,8 @@ typedef struct {
child_sa_t *child_sa; child_sa_t *child_sa;
/** TRUE in case of wildcard Transport Mode SA */ /** TRUE in case of wildcard Transport Mode SA */
bool wildcard; bool wildcard;
/** TRUE for CHILD_SAs that are externally managed */
bool external;
} entry_t; } entry_t;
/** /**
@@ -118,6 +120,8 @@ typedef struct {
uint32_t reqid; uint32_t reqid;
/** destination address (wildcard case) */ /** destination address (wildcard case) */
host_t *dst; host_t *dst;
/** security label, if any */
sec_label_t *label;
} acquire_t; } acquire_t;
/** /**
@@ -125,7 +129,10 @@ typedef struct {
*/ */
static void destroy_entry(entry_t *this) static void destroy_entry(entry_t *this)
{ {
this->child_sa->destroy(this->child_sa); if (!this->external)
{
this->child_sa->destroy(this->child_sa);
}
this->peer_cfg->destroy(this->peer_cfg); this->peer_cfg->destroy(this->peer_cfg);
free(this->name); free(this->name);
free(this); free(this);
@@ -137,6 +144,7 @@ static void destroy_entry(entry_t *this)
static void destroy_acquire(acquire_t *this) static void destroy_acquire(acquire_t *this)
{ {
DESTROY_IF(this->dst); DESTROY_IF(this->dst);
DESTROY_IF(this->label);
free(this); free(this);
} }
@@ -144,9 +152,10 @@ CALLBACK(acquire_by_reqid, bool,
acquire_t *this, va_list args) acquire_t *this, va_list args)
{ {
uint32_t reqid; uint32_t reqid;
sec_label_t *label;
VA_ARGS_VGET(args, reqid); VA_ARGS_VGET(args, reqid, label);
return this->reqid == reqid; return this->reqid == reqid && sec_labels_equal(this->label, label);
} }
CALLBACK(acquire_by_dst, bool, CALLBACK(acquire_by_dst, bool,
@@ -183,6 +192,41 @@ static bool dynamic_remote_ts(child_cfg_t *child)
return found; return found;
} }
/**
* Install the given trap
*/
static status_t install_trap(child_sa_t *child_sa, linked_list_t *local,
linked_list_t *remote)
{
linked_list_t *my_ts, *other_ts, *proposals;
proposal_t *proposal;
child_cfg_t *child;
protocol_id_t proto = PROTO_ESP;
child = child_sa->get_config(child_sa);
my_ts = child->get_traffic_selectors(child, TRUE, NULL, local, FALSE);
other_ts = child->get_traffic_selectors(child, FALSE, NULL, remote, FALSE);
/* we don't know the finally negotiated protocol (ESP|AH), we install
* the SA with the protocol of the first proposal */
proposals = child->get_proposals(child, TRUE);
if (proposals->get_first(proposals, (void**)&proposal) == SUCCESS)
{
proto = proposal->get_protocol(proposal);
}
proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
child_sa->set_protocol(child_sa, proto);
child_sa->set_mode(child_sa, child->get_mode(child));
child_sa->set_policies(child_sa, my_ts, other_ts);
my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy));
other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));
return child_sa->install_policies(child_sa);
}
METHOD(trap_manager_t, install, bool, METHOD(trap_manager_t, install, bool,
private_trap_manager_t *this, peer_cfg_t *peer, child_cfg_t *child) private_trap_manager_t *this, peer_cfg_t *peer, child_cfg_t *child)
{ {
@@ -190,12 +234,9 @@ METHOD(trap_manager_t, install, bool,
ike_cfg_t *ike_cfg; ike_cfg_t *ike_cfg;
child_sa_t *child_sa; child_sa_t *child_sa;
host_t *me, *other; host_t *me, *other;
linked_list_t *my_ts, *other_ts, *list; linked_list_t *local, *remote;
enumerator_t *enumerator; enumerator_t *enumerator;
status_t status; status_t status;
linked_list_t *proposals;
proposal_t *proposal;
protocol_id_t proto = PROTO_ESP;
bool result = FALSE, wildcard = FALSE; bool result = FALSE, wildcard = FALSE;
/* try to resolve addresses */ /* try to resolve addresses */
@@ -254,13 +295,14 @@ METHOD(trap_manager_t, install, bool,
enumerator = this->traps->create_enumerator(this->traps); enumerator = this->traps->create_enumerator(this->traps);
while (enumerator->enumerate(enumerator, &entry)) while (enumerator->enumerate(enumerator, &entry))
{ {
if (streq(entry->name, child->get_name(child)) && if (!entry->external &&
streq(entry->name, child->get_name(child)) &&
streq(entry->peer_cfg->get_name(entry->peer_cfg), streq(entry->peer_cfg->get_name(entry->peer_cfg),
peer->get_name(peer))) peer->get_name(peer)))
{ {
found = entry; found = entry;
if (entry->child_sa) if (entry->child_sa)
{ /* replace it with an updated version, if already installed */ { /* replace it with an updated version if already installed */
this->traps->remove_at(this->traps, enumerator); this->traps->remove_at(this->traps, enumerator);
} }
break; break;
@@ -302,28 +344,13 @@ METHOD(trap_manager_t, install, bool,
}; };
child_sa = child_sa_create(me, other, child, &child_data); child_sa = child_sa_create(me, other, child, &child_data);
list = linked_list_create_with_items(me, NULL); local = linked_list_create_with_items(me, NULL);
my_ts = child->get_traffic_selectors(child, TRUE, NULL, list, FALSE); remote = linked_list_create_with_items(other, NULL);
list->destroy_offset(list, offsetof(host_t, destroy));
list = linked_list_create_with_items(other, NULL); status = install_trap(child_sa, local, remote);
other_ts = child->get_traffic_selectors(child, FALSE, NULL, list, FALSE);
list->destroy_offset(list, offsetof(host_t, destroy));
/* We don't know the finally negotiated protocol (ESP|AH), we install local->destroy_offset(local, offsetof(host_t, destroy));
* the SA with the protocol of the first proposal */ remote->destroy_offset(remote, offsetof(host_t, destroy));
proposals = child->get_proposals(child, TRUE);
if (proposals->get_first(proposals, (void**)&proposal) == SUCCESS)
{
proto = proposal->get_protocol(proposal);
}
proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
child_sa->set_protocol(child_sa, proto);
child_sa->set_mode(child_sa, child->get_mode(child));
child_sa->set_policies(child_sa, my_ts, other_ts);
status = child_sa->install_policies(child_sa);
my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy));
other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));
if (status != SUCCESS) if (status != SUCCESS)
{ {
DBG1(DBG_CFG, "installing trap failed"); DBG1(DBG_CFG, "installing trap failed");
@@ -366,8 +393,71 @@ METHOD(trap_manager_t, uninstall, bool,
enumerator = this->traps->create_enumerator(this->traps); enumerator = this->traps->create_enumerator(this->traps);
while (enumerator->enumerate(enumerator, &entry)) while (enumerator->enumerate(enumerator, &entry))
{ {
if (streq(entry->name, child) && if (!entry->external &&
(!peer || streq(peer, entry->peer_cfg->get_name(entry->peer_cfg)))) streq(entry->name, child) &&
(!peer || streq(peer, entry->peer_cfg->get_name(entry->peer_cfg))))
{
this->traps->remove_at(this->traps, enumerator);
found = entry;
break;
}
}
enumerator->destroy(enumerator);
this->lock->unlock(this->lock);
if (!found)
{
return FALSE;
}
destroy_entry(found);
return TRUE;
}
METHOD(trap_manager_t, install_external, bool,
private_trap_manager_t *this, peer_cfg_t *peer, child_sa_t *child,
linked_list_t *local, linked_list_t *remote)
{
entry_t *entry;
this->lock->write_lock(this->lock);
if (this->installing == INSTALL_DISABLED)
{ /* flush() has been called */
this->lock->unlock(this->lock);
return FALSE;
}
INIT(entry,
.name = strdup(child->get_name(child)),
.peer_cfg = peer->get_ref(peer),
.child_sa = child,
.external = TRUE,
);
this->traps->insert_first(this->traps, entry);
this->lock->unlock(this->lock);
if (install_trap(child, local, remote) != SUCCESS)
{
DBG1(DBG_CFG, "installing trap failed");
this->lock->write_lock(this->lock);
this->traps->remove(this->traps, entry, NULL);
this->lock->unlock(this->lock);
destroy_entry(entry);
return FALSE;
}
return TRUE;
}
METHOD(trap_manager_t, remove_external, bool,
private_trap_manager_t *this, child_sa_t *child)
{
enumerator_t *enumerator;
entry_t *entry, *found = NULL;
this->lock->write_lock(this->lock);
enumerator = this->traps->create_enumerator(this->traps);
while (enumerator->enumerate(enumerator, &entry))
{
if (entry->external && entry->child_sa == child)
{ {
this->traps->remove_at(this->traps, enumerator); this->traps->remove_at(this->traps, enumerator);
found = entry; found = entry;
@@ -396,8 +486,9 @@ CALLBACK(trap_filter, bool,
while (orig->enumerate(orig, &entry)) while (orig->enumerate(orig, &entry))
{ {
if (!entry->child_sa) if (!entry->child_sa || entry->external)
{ /* skip entries that are currently being installed */ { /* skip entries that are currently being installed or are managed
* externally */
continue; continue;
} }
if (peer_cfg) if (peer_cfg)
@@ -423,8 +514,7 @@ METHOD(trap_manager_t, create_enumerator, enumerator_t*,
} }
METHOD(trap_manager_t, acquire, void, METHOD(trap_manager_t, acquire, void,
private_trap_manager_t *this, uint32_t reqid, private_trap_manager_t *this, uint32_t reqid, kernel_acquire_data_t *data)
traffic_selector_t *src, traffic_selector_t *dst)
{ {
enumerator_t *enumerator; enumerator_t *enumerator;
entry_t *entry, *found = NULL; entry_t *entry, *found = NULL;
@@ -454,7 +544,6 @@ METHOD(trap_manager_t, acquire, void,
this->lock->unlock(this->lock); this->lock->unlock(this->lock);
return; return;
} }
reqid = found->child_sa->get_reqid(found->child_sa);
wildcard = found->wildcard; wildcard = found->wildcard;
this->mutex->lock(this->mutex); this->mutex->lock(this->mutex);
@@ -463,7 +552,7 @@ METHOD(trap_manager_t, acquire, void,
* with the same peer */ * with the same peer */
uint8_t mask; uint8_t mask;
dst->to_subnet(dst, &host, &mask); data->dst->to_subnet(data->dst, &host, &mask);
if (this->acquires->find_first(this->acquires, acquire_by_dst, if (this->acquires->find_first(this->acquires, acquire_by_dst,
(void**)&acquire, host)) (void**)&acquire, host))
{ {
@@ -482,7 +571,7 @@ METHOD(trap_manager_t, acquire, void,
else else
{ {
if (this->acquires->find_first(this->acquires, acquire_by_reqid, if (this->acquires->find_first(this->acquires, acquire_by_reqid,
(void**)&acquire, reqid)) (void**)&acquire, reqid, data->label))
{ {
ignore = TRUE; ignore = TRUE;
} }
@@ -490,6 +579,7 @@ METHOD(trap_manager_t, acquire, void,
{ {
INIT(acquire, INIT(acquire,
.reqid = reqid, .reqid = reqid,
.label = data->label ? data->label->clone(data->label) : NULL,
); );
this->acquires->insert_last(this->acquires, acquire); this->acquires->insert_last(this->acquires, acquire);
} }
@@ -497,7 +587,8 @@ METHOD(trap_manager_t, acquire, void,
this->mutex->unlock(this->mutex); this->mutex->unlock(this->mutex);
if (ignore) if (ignore)
{ {
DBG1(DBG_CFG, "ignoring acquire, connection attempt pending"); DBG1(DBG_CFG, "ignoring acquire for reqid %u, connection attempt "
"pending", reqid);
this->lock->unlock(this->lock); this->lock->unlock(this->lock);
return; return;
} }
@@ -521,12 +612,12 @@ METHOD(trap_manager_t, acquire, void,
ike_cfg = ike_sa->get_ike_cfg(ike_sa); ike_cfg = ike_sa->get_ike_cfg(ike_sa);
port = ike_cfg->get_other_port(ike_cfg); port = ike_cfg->get_other_port(ike_cfg);
dst->to_subnet(dst, &host, &mask); data->dst->to_subnet(data->dst, &host, &mask);
host->set_port(host, port); host->set_port(host, port);
ike_sa->set_other_host(ike_sa, host); ike_sa->set_other_host(ike_sa, host);
port = ike_cfg->get_my_port(ike_cfg); port = ike_cfg->get_my_port(ike_cfg);
src->to_subnet(src, &host, &mask); data->src->to_subnet(data->src, &host, &mask);
host->set_port(host, port); host->set_port(host, port);
ike_sa->set_my_host(ike_sa, host); ike_sa->set_my_host(ike_sa, host);
@@ -542,17 +633,24 @@ METHOD(trap_manager_t, acquire, void,
if (ike_sa) if (ike_sa)
{ {
child_init_args_t args = {
.reqid = reqid,
.src = data->src,
.dst = data->dst,
.label = data->label,
};
if (this->ignore_acquire_ts || ike_sa->get_version(ike_sa) == IKEV1) if (this->ignore_acquire_ts || ike_sa->get_version(ike_sa) == IKEV1)
{ /* in IKEv1, don't prepend the acquiring packet TS, as we only { /* in IKEv1, don't prepend the acquiring packet TS, as we only
* have a single TS that we can establish in a Quick Mode. */ * have a single TS that we can establish in a Quick Mode. */
src = dst = NULL; args.src = args.dst = NULL;
} }
this->mutex->lock(this->mutex); this->mutex->lock(this->mutex);
acquire->ike_sa = ike_sa; acquire->ike_sa = ike_sa;
this->mutex->unlock(this->mutex); this->mutex->unlock(this->mutex);
if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME) if (ike_sa->initiate(ike_sa, child, &args) != DESTROY_ME)
{ {
charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
} }
@@ -600,6 +698,11 @@ static void complete(private_trap_manager_t *this, ike_sa_t *ike_sa,
{ {
continue; continue;
} }
else if (!sec_labels_equal(acquire->label,
child_sa->get_label(child_sa)))
{
continue;
}
} }
this->acquires->remove_at(this->acquires, enumerator); this->acquires->remove_at(this->acquires, enumerator);
destroy_acquire(acquire); destroy_acquire(acquire);
@@ -673,6 +776,8 @@ trap_manager_t *trap_manager_create(void)
.public = { .public = {
.install = _install, .install = _install,
.uninstall = _uninstall, .uninstall = _uninstall,
.install_external = _install_external,
.remove_external = _remove_external,
.create_enumerator = _create_enumerator, .create_enumerator = _create_enumerator,
.acquire = _acquire, .acquire = _acquire,
.flush = _flush, .flush = _flush,
+29 -5
View File
@@ -25,6 +25,7 @@
#include <library.h> #include <library.h>
#include <collections/enumerator.h> #include <collections/enumerator.h>
#include <config/peer_cfg.h> #include <config/peer_cfg.h>
#include <sa/child_sa.h>
typedef struct trap_manager_t trap_manager_t; typedef struct trap_manager_t trap_manager_t;
@@ -55,7 +56,31 @@ struct trap_manager_t {
bool (*uninstall)(trap_manager_t *this, char *peer, char *child); bool (*uninstall)(trap_manager_t *this, char *peer, char *child);
/** /**
* Create an enumerator over all installed traps. * Install and register an externally managed trap policy using the two
* lists of local and remote addresses when deriving traffic selectors.
*
* @param peer peer configuration to register
* @param child CHILD_SA to install and register
* @param local list of local addresses (virtual or physical)
* @param remote list of remote addresses (virtual or physical)
* @return TRUE if successfully installed and registered
*/
bool (*install_external)(trap_manager_t *this, peer_cfg_t *peer,
child_sa_t *child, linked_list_t *local,
linked_list_t *remote);
/**
* Remove and uninstall a previously registered externally managed trap
* policy.
*
* @param child CHILD_SA to remove
* @return TRUE if successfully removed
*/
bool (*remove_external)(trap_manager_t *this, child_sa_t *child);
/**
* Create an enumerator over all installed traps (does not include
* externally managed trap policies).
* *
* @return enumerator over (peer_cfg_t, child_sa_t) * @return enumerator over (peer_cfg_t, child_sa_t)
*/ */
@@ -64,12 +89,11 @@ struct trap_manager_t {
/** /**
* Acquire an SA triggered by an installed trap. * Acquire an SA triggered by an installed trap.
* *
* @param reqid reqid of the triggering CHILD_SA * @param reqid reqid of the triggered policy
* @param src source of the triggering packet * @param data data from the acquire
* @param dst destination of the triggering packet
*/ */
void (*acquire)(trap_manager_t *this, uint32_t reqid, void (*acquire)(trap_manager_t *this, uint32_t reqid,
traffic_selector_t *src, traffic_selector_t *dst); kernel_acquire_data_t *data);
/** /**
* Clear any installed trap. * Clear any installed trap.
+49 -2
View File
@@ -21,6 +21,47 @@
#include <tests/utils/job_asserts.h> #include <tests/utils/job_asserts.h>
#include <tests/utils/sa_asserts.h> #include <tests/utils/sa_asserts.h>
/**
* The peers try to create a new CHILD_SA that looks exactly the same
* as the existing one, so it won't get initiated.
*/
START_TEST(test_duplicate)
{
child_cfg_t *child_cfg;
child_cfg_create_t child = {
.mode = MODE_TUNNEL,
};
ike_sa_t *a, *b;
exchange_test_helper->establish_sa(exchange_test_helper,
&a, &b, NULL);
assert_no_jobs_scheduled();
assert_hook_not_called(child_updown);
assert_hook_not_called(message);
child_cfg = child_cfg_create("child", &child);
child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
child_cfg->add_traffic_selector(child_cfg, TRUE,
traffic_selector_create_dynamic(0, 0, 65535));
child_cfg->add_traffic_selector(child_cfg, FALSE,
traffic_selector_create_dynamic(0, 0, 65535));
child_cfg->get_ref(child_cfg);
call_ikesa(a, initiate, child_cfg, NULL);
assert_child_sa_count(a, 1);
assert_sa_idle(a);
call_ikesa(b, initiate, child_cfg, NULL);
assert_child_sa_count(b, 1);
assert_sa_idle(b);
assert_hook();
assert_hook();
assert_scheduler();
call_ikesa(a, destroy);
call_ikesa(b, destroy);
}
END_TEST
/** /**
* One of the peers tries to create a new CHILD_SA while the other concurrently * One of the peers tries to create a new CHILD_SA while the other concurrently
* started to rekey the IKE_SA. TEMPORARY_FAILURE should be returned on both * started to rekey the IKE_SA. TEMPORARY_FAILURE should be returned on both
@@ -31,6 +72,8 @@ START_TEST(test_collision_ike_rekey)
child_cfg_t *child_cfg; child_cfg_t *child_cfg;
child_cfg_create_t child = { child_cfg_create_t child = {
.mode = MODE_TUNNEL, .mode = MODE_TUNNEL,
/* make sure this is not a duplicate of the initial CHILD_SA */
.mark_out = { .value = 42, .mask = 0xffffffff },
}; };
ike_sa_t *a, *b; ike_sa_t *a, *b;
@@ -44,7 +87,7 @@ START_TEST(test_collision_ike_rekey)
traffic_selector_create_dynamic(0, 0, 65535)); traffic_selector_create_dynamic(0, 0, 65535));
child_cfg->add_traffic_selector(child_cfg, FALSE, child_cfg->add_traffic_selector(child_cfg, FALSE,
traffic_selector_create_dynamic(0, 0, 65535)); traffic_selector_create_dynamic(0, 0, 65535));
call_ikesa(a, initiate, child_cfg, 0, NULL, NULL); call_ikesa(a, initiate, child_cfg, NULL);
assert_child_sa_count(a, 1); assert_child_sa_count(a, 1);
assert_hook(); assert_hook();
@@ -81,7 +124,7 @@ START_TEST(test_collision_ike_rekey)
ck_assert(!exchange_test_helper->sender->dequeue(exchange_test_helper->sender)); ck_assert(!exchange_test_helper->sender->dequeue(exchange_test_helper->sender));
assert_num_tasks(a, 0, TASK_QUEUE_ACTIVE); assert_num_tasks(a, 0, TASK_QUEUE_ACTIVE);
assert_num_tasks(a, 1, TASK_QUEUE_QUEUED); assert_num_tasks(a, 1, TASK_QUEUE_QUEUED);
call_ikesa(a, initiate, NULL, 0, NULL, NULL); call_ikesa(a, initiate, NULL, NULL);
assert_num_tasks(a, 0, TASK_QUEUE_ACTIVE); assert_num_tasks(a, 0, TASK_QUEUE_ACTIVE);
assert_sa_idle(b); assert_sa_idle(b);
@@ -98,6 +141,10 @@ Suite *child_create_suite_create()
s = suite_create("child create"); s = suite_create("child create");
tc = tcase_create("initiate duplicate");
tcase_add_test(tc, test_duplicate);
suite_add_tcase(s, tc);
tc = tcase_create("collisions ike rekey"); tc = tcase_create("collisions ike rekey");
tcase_add_test(tc, test_collision_ike_rekey); tcase_add_test(tc, test_collision_ike_rekey);
suite_add_tcase(s, tc); suite_add_tcase(s, tc);
+5 -5
View File
@@ -44,7 +44,7 @@ START_TEST(test_regular)
id_a = a->get_id(a); id_a = a->get_id(a);
id_b = b->get_id(b); id_b = b->get_id(b);
call_ikesa(a, initiate, child_cfg, 0, NULL, NULL); call_ikesa(a, initiate, child_cfg, NULL);
/* IKE_SA_INIT --> */ /* IKE_SA_INIT --> */
id_b->set_initiator_spi(id_b, id_a->get_initiator_spi(id_a)); id_b->set_initiator_spi(id_b, id_a->get_initiator_spi(id_a));
@@ -115,7 +115,7 @@ START_TEST(test_regular_manual)
id_a = a->get_id(a); id_a = a->get_id(a);
id_b = b->get_id(b); id_b = b->get_id(b);
call_ikesa(a, initiate, NULL, 0, NULL, NULL); call_ikesa(a, initiate, NULL, NULL);
/* IKE_SA_INIT --> */ /* IKE_SA_INIT --> */
id_b->set_initiator_spi(id_b, id_a->get_initiator_spi(id_a)); id_b->set_initiator_spi(id_b, id_a->get_initiator_spi(id_a));
@@ -144,7 +144,7 @@ START_TEST(test_regular_manual)
assert_sa_idle(a); assert_sa_idle(a);
assert_sa_idle(b); assert_sa_idle(b);
call_ikesa(a, initiate, child_cfg, 0, NULL, NULL); call_ikesa(a, initiate, child_cfg, NULL);
/* CREATE_CHILD_SA { SA, Ni, KEi, TSi, TSr } --> */ /* CREATE_CHILD_SA { SA, Ni, KEi, TSi, TSr } --> */
assert_hook_called(child_updown); assert_hook_called(child_updown);
@@ -192,7 +192,7 @@ START_TEST(test_failure_init)
id_a = a->get_id(a); id_a = a->get_id(a);
id_b = b->get_id(b); id_b = b->get_id(b);
call_ikesa(a, initiate, child_cfg, 0, NULL, NULL); call_ikesa(a, initiate, child_cfg, NULL);
/* IKE_SA_INIT --> */ /* IKE_SA_INIT --> */
id_b->set_initiator_spi(id_b, id_a->get_initiator_spi(id_a)); id_b->set_initiator_spi(id_b, id_a->get_initiator_spi(id_a));
@@ -233,7 +233,7 @@ START_TEST(test_failure_resp)
id_a = a->get_id(a); id_a = a->get_id(a);
id_b = b->get_id(b); id_b = b->get_id(b);
call_ikesa(a, initiate, child_cfg, 0, NULL, NULL); call_ikesa(a, initiate, child_cfg, NULL);
/* IKE_SA_INIT --> */ /* IKE_SA_INIT --> */
id_b->set_initiator_spi(id_b, id_a->get_initiator_spi(id_a)); id_b->set_initiator_spi(id_b, id_a->get_initiator_spi(id_a));
@@ -493,7 +493,7 @@ START_TEST(test_active)
charon->bus->remove_listener(charon->bus, &mid.listener); charon->bus->remove_listener(charon->bus, &mid.listener);
/* the active task was queued again */ /* the active task was queued again */
call_ikesa(a, initiate, NULL, 0, NULL, NULL); call_ikesa(a, initiate, NULL, NULL);
exchange_test_helper->process_message(exchange_test_helper, b, NULL); exchange_test_helper->process_message(exchange_test_helper, b, NULL);
exchange_test_helper->process_message(exchange_test_helper, a, NULL); exchange_test_helper->process_message(exchange_test_helper, a, NULL);
send_dpd(b, a); send_dpd(b, a);
@@ -273,7 +273,7 @@ METHOD(exchange_test_helper_t, establish_sa, void,
id_i = sa_i->get_id(sa_i); id_i = sa_i->get_id(sa_i);
id_r = sa_r->get_id(sa_r); id_r = sa_r->get_id(sa_r);
call_ikesa(sa_i, initiate, child_i, 0, NULL, NULL); call_ikesa(sa_i, initiate, child_i, NULL);
/* IKE_SA_INIT --> */ /* IKE_SA_INIT --> */
id_r->set_initiator_spi(id_r, id_i->get_initiator_spi(id_i)); id_r->set_initiator_spi(id_r, id_i->get_initiator_spi(id_i));
+2 -1
View File
@@ -41,7 +41,8 @@ networking/streams/stream_tcp.c networking/streams/stream_service_tcp.c \
pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \ pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \
processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \ processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \
processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \ processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \
selectors/traffic_selector.c settings/settings.c settings/settings_types.c \ selectors/sec_label.c selectors/traffic_selector.c \
settings/settings.c settings/settings_types.c \
settings/settings_parser.c settings/settings_lexer.c utils/cpu_feature.c \ settings/settings_parser.c settings/settings_lexer.c utils/cpu_feature.c \
utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \ utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \
utils/lexparser.c utils/optionsfrom.c utils/capabilities.c utils/backtrace.c \ utils/lexparser.c utils/optionsfrom.c utils/capabilities.c utils/backtrace.c \
+7 -3
View File
@@ -39,7 +39,8 @@ networking/streams/stream_tcp.c networking/streams/stream_service_tcp.c \
pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \ pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \
processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \ processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \
processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \ processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \
selectors/traffic_selector.c settings/settings.c settings/settings_types.c \ selectors/sec_label.c selectors/traffic_selector.c \
settings/settings.c settings/settings_types.c \
settings/settings_parser.y settings/settings_lexer.l utils/cpu_feature.c \ settings/settings_parser.y settings/settings_lexer.l utils/cpu_feature.c \
utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \ utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \
utils/lexparser.c utils/optionsfrom.c utils/capabilities.c utils/backtrace.c \ utils/lexparser.c utils/optionsfrom.c utils/capabilities.c utils/backtrace.c \
@@ -112,7 +113,8 @@ resolver/resolver.h resolver/resolver_response.h resolver/rr_set.h \
resolver/rr.h resolver/resolver_manager.h \ resolver/rr.h resolver/resolver_manager.h \
plugins/plugin_loader.h plugins/plugin.h plugins/plugin_feature.h \ plugins/plugin_loader.h plugins/plugin.h plugins/plugin_feature.h \
processing/jobs/job.h processing/jobs/callback_job.h processing/processor.h \ processing/jobs/job.h processing/jobs/callback_job.h processing/processor.h \
processing/scheduler.h processing/watcher.h selectors/traffic_selector.h \ processing/scheduler.h processing/watcher.h \
selectors/sec_label.h selectors/traffic_selector.h \
settings/settings.h settings/settings_parser.h threading/thread_value.h \ settings/settings.h settings/settings_parser.h threading/thread_value.h \
threading/thread.h threading/windows/thread.h \ threading/thread.h threading/windows/thread.h \
threading/mutex.h threading/condvar.h threading/spinlock.h threading/semaphore.h \ threading/mutex.h threading/condvar.h threading/spinlock.h threading/semaphore.h \
@@ -132,7 +134,8 @@ library.lo : $(top_builddir)/config.status
libstrongswan_la_LIBADD = \ libstrongswan_la_LIBADD = \
$(DLLIB) $(ATOMICLIB) $(BTLIB) $(SOCKLIB) \ $(DLLIB) $(ATOMICLIB) $(BTLIB) $(SOCKLIB) \
$(RTLIB) $(BFDLIB) $(UNWINDLIB) $(RTLIB) $(BFDLIB) $(UNWINDLIB) \
$(selinux_LIBS)
AM_CPPFLAGS = \ AM_CPPFLAGS = \
-I$(top_srcdir)/src/libstrongswan \ -I$(top_srcdir)/src/libstrongswan \
@@ -142,6 +145,7 @@ AM_CPPFLAGS = \
-DSTRONGSWAN_CONF=\"${strongswan_conf}\" -DSTRONGSWAN_CONF=\"${strongswan_conf}\"
AM_CFLAGS = \ AM_CFLAGS = \
$(selinux_CFLAGS) \
@COVERAGE_CFLAGS@ @COVERAGE_CFLAGS@
AM_LDFLAGS = \ AM_LDFLAGS = \
+228
View File
@@ -0,0 +1,228 @@
/*
* Copyright (C) 2021 Tobias Brunner, codelabs GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#define _GNU_SOURCE
#include <stdio.h>
#ifdef USE_SELINUX
#include <selinux/selinux.h>
#endif
#include "sec_label.h"
ENUM(sec_label_mode_names, SEC_LABEL_MODE_SYSTEM, SEC_LABEL_MODE_SELINUX,
"system",
"simple",
"selinux",
);
typedef struct private_sec_label_t private_sec_label_t;
/**
* Private data.
*/
struct private_sec_label_t {
/**
* Public interface
*/
sec_label_t public;
/**
* Encoded label value
*/
chunk_t encoding;
/**
* String representation of the label
*/
char *str;
};
static sec_label_t *create_sec_label(chunk_t encoding, char *str);
METHOD(sec_label_t, get_encoding, chunk_t,
private_sec_label_t *this)
{
return this->encoding;
}
METHOD(sec_label_t, get_string, char*,
private_sec_label_t *this)
{
return this->str;
}
METHOD(sec_label_t, clone_, sec_label_t*,
private_sec_label_t *this)
{
return create_sec_label(chunk_clone(this->encoding), strdup(this->str));
}
METHOD(sec_label_t, equals, bool,
private_sec_label_t *this, sec_label_t *other_pub)
{
private_sec_label_t *other = (private_sec_label_t*)other_pub;
if (!other_pub)
{
return FALSE;
}
return chunk_equals_const(this->encoding, other->encoding);
}
METHOD(sec_label_t, matches, bool,
private_sec_label_t *this, sec_label_t *other_pub)
{
if (!other_pub)
{
return FALSE;
}
#ifdef USE_SELINUX
if (is_selinux_enabled())
{ /* if disabled, the following matches anything against anything */
private_sec_label_t *other = (private_sec_label_t*)other_pub;
return selinux_check_access(other->str, this->str, "association",
"polmatch", NULL) == 0;
}
#endif
return equals(this, other_pub);
}
METHOD(sec_label_t, hash, u_int,
private_sec_label_t *this, u_int inc)
{
return chunk_hash_inc(this->encoding, inc);
}
METHOD(sec_label_t, destroy, void,
private_sec_label_t *this)
{
chunk_free(&this->encoding);
free(this->str);
free(this);
}
/**
* Internal constructor, data is adopted
*/
static sec_label_t *create_sec_label(chunk_t encoding, char *str)
{
private_sec_label_t *this;
INIT(this,
.public = {
.get_encoding = _get_encoding,
.get_string = _get_string,
.clone = _clone_,
.matches = _matches,
.equals = _equals,
.hash = _hash,
.destroy = _destroy,
},
.encoding = encoding,
.str = str,
);
return &this->public;
}
/*
* Described in header
*/
sec_label_t *sec_label_from_encoding(const chunk_t value)
{
chunk_t cloned, sanitized = chunk_empty;
char *str;
if (!value.len || (value.len == 1 && !value.ptr[0]))
{
DBG1(DBG_LIB, "invalid empty security label");
return NULL;
}
else if (value.ptr[value.len-1])
{
DBG1(DBG_LIB, "adding null-terminator to security label");
cloned = chunk_cat("cc", value, chunk_from_chars(0x00));
}
else
{
cloned = chunk_clone(value);
}
/* create a sanitized version while ignoring the null-terminator */
if (!chunk_printable(chunk_create(cloned.ptr, cloned.len-1), &sanitized, '?'))
{
#ifdef USE_SELINUX
/* don't accept labels with non-printable characters if we use SELinux */
DBG1(DBG_LIB, "invalid security label with non-printable characters %B",
&value);
chunk_free(&sanitized);
chunk_free(&cloned);
return NULL;
#endif
}
if (asprintf(&str, "%.*s", (int)sanitized.len, sanitized.ptr) <= 0)
{
chunk_free(&sanitized);
chunk_free(&cloned);
return NULL;
}
chunk_free(&sanitized);
return create_sec_label(cloned, str);
}
/*
* Described in header
*/
sec_label_t *sec_label_from_string(const char *value)
{
if (!value)
{
return NULL;
}
return sec_label_from_encoding(chunk_create((char*)value, strlen(value)+1));
}
/*
* Described in header
*/
bool sec_label_mode_from_string(const char *value, sec_label_mode_t *mode)
{
sec_label_mode_t def = sec_label_mode_default();
return enum_from_name(sec_label_mode_names, value, mode) &&
(def == SEC_LABEL_MODE_SELINUX || *mode != SEC_LABEL_MODE_SELINUX);
}
/*
* Described in header
*/
sec_label_mode_t sec_label_mode_default()
{
#ifdef USE_SELINUX
if (is_selinux_enabled())
{
return SEC_LABEL_MODE_SELINUX;
}
#endif
return SEC_LABEL_MODE_SIMPLE;
}
+172
View File
@@ -0,0 +1,172 @@
/*
* Copyright (C) 2021 Tobias Brunner, codelabs GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* @defgroup sec_label sec_label
* @{ @ingroup ipsec
*/
#ifndef SEC_LABEL_H_
#define SEC_LABEL_H_
typedef enum sec_label_mode_t sec_label_mode_t;
typedef struct sec_label_t sec_label_t;
#include <library.h>
/**
* Mode in which security labels are used.
*/
enum sec_label_mode_t {
/**
* System default. Simple mode if SELinux is not supported or disabled
* on the system.
*/
SEC_LABEL_MODE_SYSTEM,
/**
* Simple mode that does establish regular CHILD_SAs, matches labels exactly
* and does not install them in the kernel.
*/
SEC_LABEL_MODE_SIMPLE,
/**
* SELinux mode where configured labels are installed on (trap) policies,
* labels from acquires/peer on SAs, child-less IKE_SAs are initiated
* if there is no acquire, labels are also matched via polmatch.
*/
SEC_LABEL_MODE_SELINUX,
};
/**
* Names for security label modes.
*/
extern enum_name_t *sec_label_mode_names;
/**
* Representation of a security label used on policies/SAs.
*
* For example, with SELinux this could be a value like
* system_u:object_r:ipsec_spd_t:s0.
*/
struct sec_label_t {
/**
* Return a binary encoding of the security label as used for IKE.
*
* @return binary encoding (internal data)
*/
chunk_t (*get_encoding)(sec_label_t *this);
/**
* Return a string representation of this security label.
*
* @return string representation (internal data)
*/
char *(*get_string)(sec_label_t *this);
/**
* Clone this security label.
*
* @return clone of it
*/
sec_label_t *(*clone)(sec_label_t *this);
/**
* Match two security labels.
*
* For SELinux this checks if this security label permits other in terms
* of association { polmatch }.
*
* @param other security label to match against this
* @return TRUE if matching, FALSE otherwise
*/
bool (*matches)(sec_label_t *this, sec_label_t *other);
/**
* Compare two security labels for equality.
*
* @param other security label to compare with this
* @return TRUE if equal, FALSE otherwise
*/
bool (*equals)(sec_label_t *this, sec_label_t *other);
/**
* Create a hash value for the security label.
*
* @param inc optional value for incremental hashing
* @return calculated hash value for the security label
*/
u_int (*hash)(sec_label_t *this, u_int inc);
/**
* Destroys the object.
*/
void (*destroy)(sec_label_t *this);
};
/**
* Try to parse a sec_label_t from the given binary encoding.
*
* @param value encoding to parse
* @return security label instance, NULL if invalid
*/
sec_label_t *sec_label_from_encoding(const chunk_t value);
/**
* Try to parse a sec_label_t from the given string.
*
* @param value string to parse
* @return security label instance, NULL if invalid
*/
sec_label_t *sec_label_from_string(const char *value);
/**
* Compare two security labels for equality, accept if both are NULL.
*
* @param a first label
* @param b second label
* @return TRUE if labels are equal or both NULL
*/
static inline bool sec_labels_equal(sec_label_t *a, sec_label_t *b)
{
return (!a && !b) || (a && a->equals(a, b));
}
/**
* Try to parse a security label mode from the given string.
*
* @param value string to parse
* @param mode parsed mode
* @return TRUE if mode is valid (and usable on system)
*/
bool sec_label_mode_from_string(const char *value, sec_label_mode_t *mode);
/**
* Get the system default security label mode.
*
* @return default mode
*/
sec_label_mode_t sec_label_mode_default();
#endif /** SEC_LABEL_H_ @}*/
@@ -31,10 +31,12 @@
#define NON_SUBNET_ADDRESS_RANGE 255 #define NON_SUBNET_ADDRESS_RANGE 255
ENUM(ts_type_name, TS_IPV4_ADDR_RANGE, TS_IPV6_ADDR_RANGE, ENUM_BEGIN(ts_type_name, TS_IPV4_ADDR_RANGE, TS_IPV6_ADDR_RANGE,
"TS_IPV4_ADDR_RANGE", "TS_IPV4_ADDR_RANGE",
"TS_IPV6_ADDR_RANGE", "TS_IPV6_ADDR_RANGE");
); ENUM_NEXT(ts_type_name, TS_SECLABEL, TS_SECLABEL, TS_IPV6_ADDR_RANGE,
"TS_SECLABEL");
ENUM_END(ts_type_name, TS_SECLABEL);
typedef struct private_traffic_selector_t private_traffic_selector_t; typedef struct private_traffic_selector_t private_traffic_selector_t;
@@ -50,7 +50,12 @@ enum ts_type_t {
* (inclusive). All addresses falling between the two specified * (inclusive). All addresses falling between the two specified
* addresses are considered to be within the list. * addresses are considered to be within the list.
*/ */
TS_IPV6_ADDR_RANGE = 8 TS_IPV6_ADDR_RANGE = 8,
/**
* A security label.
*/
TS_SECLABEL = 10,
}; };
/** /**
+71 -10
View File
@@ -20,13 +20,13 @@
/******************************************************************************* /*******************************************************************************
* continuous enum * continuous enum
*/ */
enum { enum test_enum_cont {
CONT1, CONT1,
CONT2, CONT2,
CONT3, CONT3,
CONT4, CONT4,
CONT5, CONT5,
} test_enum_cont; };
ENUM_BEGIN(test_enum_cont_names, CONT1, CONT5, ENUM_BEGIN(test_enum_cont_names, CONT1, CONT5,
"CONT1", "CONT2", "CONT3", "CONT4", "CONT5"); "CONT1", "CONT2", "CONT3", "CONT4", "CONT5");
@@ -35,13 +35,13 @@ ENUM_END(test_enum_cont_names, CONT5);
/******************************************************************************* /*******************************************************************************
* split enum * split enum
*/ */
enum { enum test_enum_split {
SPLIT1 = 1, SPLIT1 = 1,
SPLIT2, SPLIT2,
SPLIT3 = 5, SPLIT3 = 5,
SPLIT4, SPLIT4,
SPLIT5 = 255, SPLIT5 = 255,
} test_enum_split; };
ENUM_BEGIN(test_enum_split_names, SPLIT1, SPLIT2, ENUM_BEGIN(test_enum_split_names, SPLIT1, SPLIT2,
"SPLIT1", "SPLIT2"); "SPLIT1", "SPLIT2");
@@ -54,7 +54,7 @@ ENUM_END(test_enum_split_names, SPLIT5);
/******************************************************************************* /*******************************************************************************
* enum flags * enum flags
*/ */
enum { enum test_enum_flags {
FLAG1 = (1 << 0), FLAG1 = (1 << 0),
FLAG2 = (1 << 1), FLAG2 = (1 << 1),
FLAG3 = (1 << 2), FLAG3 = (1 << 2),
@@ -67,18 +67,18 @@ enum {
FLAG10 = (1 << 9), FLAG10 = (1 << 9),
FLAG11 = (1 << 10), FLAG11 = (1 << 10),
FLAG12 = (1 << 11), FLAG12 = (1 << 11),
} test_enum_flags; };
ENUM_FLAGS(test_enum_flags_names, FLAG1, FLAG5, ENUM_FLAGS(test_enum_flags_names, FLAG1, FLAG5,
"FLAG1", "FLAG2", "FLAG3", "FLAG4", "FLAG5"); "(unset)", "FLAG1", "FLAG2", "FLAG3", "FLAG4", "FLAG5");
ENUM_FLAGS(test_enum_flags_incomplete_names, FLAG3, FLAG4, ENUM_FLAGS(test_enum_flags_incomplete_names, FLAG3, FLAG4,
"FLAG3", "FLAG4"); "(unset)", "FLAG3", "FLAG4");
ENUM_FLAGS(test_enum_flags_null_names, FLAG1, FLAG4, ENUM_FLAGS(test_enum_flags_null_names, FLAG1, FLAG4,
"FLAG1", NULL, "FLAG3", NULL); "(unset)", "FLAG1", NULL, "FLAG3", NULL);
ENUM_FLAGS(test_enum_flags_overflow_names, FLAG1, FLAG12, ENUM_FLAGS(test_enum_flags_overflow_names, FLAG1, FLAG12, "(unset)",
"OVERFLOWFLAGLONGNAME1", "OVERFLOWFLAGLONGNAME2", "OVERFLOWFLAGLONGNAME3", "OVERFLOWFLAGLONGNAME1", "OVERFLOWFLAGLONGNAME2", "OVERFLOWFLAGLONGNAME3",
"OVERFLOWFLAGLONGNAME4", "OVERFLOWFLAGLONGNAME5", "OVERFLOWFLAGLONGNAME6", "OVERFLOWFLAGLONGNAME4", "OVERFLOWFLAGLONGNAME5", "OVERFLOWFLAGLONGNAME6",
"OVERFLOWFLAGLONGNAME7", "OVERFLOWFLAGLONGNAME8", "OVERFLOWFLAGLONGNAME9", "OVERFLOWFLAGLONGNAME7", "OVERFLOWFLAGLONGNAME8", "OVERFLOWFLAGLONGNAME9",
@@ -263,6 +263,30 @@ static struct {
}, enum_flags_to_string_tests[] = { }, enum_flags_to_string_tests[] = {
{-1, NULL}, {-1, NULL},
{6435, NULL}, {6435, NULL},
}, enum_flags_from_string_tests[] = {
{0, NULL},
{0, ""},
{0, "(unset)"},
{FLAG1, "FLAG1"},
{FLAG2, "flag2"},
{FLAG3, "fLaG3"},
{FLAG4, "FLAG4"},
{FLAG5, "FLAG5"},
{FLAG1 | FLAG3, "FLAG1 | FLAG3"},
{FLAG1 | FLAG3, "flag3|flag1"},
{FLAG1 | FLAG3, "flag1|flag3 | (unset)"},
{FLAG1 | FLAG2 | FLAG3 | FLAG4 | FLAG5, "flag1|flag2|flag3|flag4|flag5"},
{FLAG1 | FLAG2 | FLAG3 | FLAG4 | FLAG5, "flag3|flag4|flag5|flag2|flag1"},
{FLAG5, "(unset)|flag5"},
{FLAG1, "FLAG1 | flag1 | flAg1"},
{-1, "FLAG6"},
{-1, "flag1 | asdf"},
}, enum_flags_from_string_noflagenum_tests[] = {
{0, NULL},
{0, ""},
{CONT2, "CONT2"},
{CONT5, "CONT5"},
{-1, "asdf"},
}; };
START_TEST(test_enum_printf_hook_cont) START_TEST(test_enum_printf_hook_cont)
@@ -371,6 +395,38 @@ START_TEST(test_enum_flags_to_string_noflagenum)
} }
END_TEST END_TEST
START_TEST(test_enum_flags_from_string)
{
enum test_enum_flags val;
if (enum_flags_from_string(test_enum_flags_names,
enum_flags_from_string_tests[_i].str, &val))
{
ck_assert_int_eq(enum_flags_from_string_tests[_i].val, val);
}
else
{
ck_assert_int_eq(enum_flags_from_string_tests[_i].val, -1);
}
}
END_TEST
START_TEST(test_enum_flags_from_string_noflagenum)
{
enum test_enum_cont val;
if (enum_flags_from_string(test_enum_cont_names,
enum_flags_from_string_noflagenum_tests[_i].str, &val))
{
ck_assert_int_eq(enum_flags_from_string_noflagenum_tests[_i].val, val);
}
else
{
ck_assert_int_eq(enum_flags_from_string_noflagenum_tests[_i].val, -1);
}
}
END_TEST
START_TEST(test_enum_printf_hook_width) START_TEST(test_enum_printf_hook_width)
{ {
char buf[128]; char buf[128];
@@ -406,6 +462,11 @@ Suite *enum_suite_create()
tcase_add_loop_test(tc, test_enum_flags_to_string_noflagenum, 0, countof(printf_tests_flags_noflagenum)); tcase_add_loop_test(tc, test_enum_flags_to_string_noflagenum, 0, countof(printf_tests_flags_noflagenum));
suite_add_tcase(s, tc); suite_add_tcase(s, tc);
tc = tcase_create("enum_flags_from_string");
tcase_add_loop_test(tc, test_enum_flags_from_string, 0, countof(enum_flags_from_string_tests));
tcase_add_loop_test(tc, test_enum_flags_from_string_noflagenum, 0, countof(enum_flags_from_string_noflagenum_tests));
suite_add_tcase(s, tc);
tc = tcase_create("enum_printf_hook"); tc = tcase_create("enum_printf_hook");
tcase_add_loop_test(tc, test_enum_printf_hook_cont, 0, countof(printf_tests_cont)); tcase_add_loop_test(tc, test_enum_printf_hook_cont, 0, countof(printf_tests_cont));
tcase_add_loop_test(tc, test_enum_printf_hook_split, 0, countof(printf_tests_split)); tcase_add_loop_test(tc, test_enum_printf_hook_split, 0, countof(printf_tests_split));
+55 -5
View File
@@ -17,6 +17,7 @@
#include <stdio.h> #include <stdio.h>
#include <library.h> #include <library.h>
#include <collections/enumerator.h>
#include <utils/utils.h> #include <utils/utils.h>
#include "enum.h" #include "enum.h"
@@ -66,16 +67,17 @@ bool enum_from_name_as_int(enum_name_t *e, const char *name, int *val)
/** /**
* Get the position of a flag name using offset calculation * Get the position of a flag name using offset calculation
*/ */
static int find_flag_pos(u_int val, u_int first) static int find_flag_pos(u_int first, u_int val)
{ {
int offset = 0; int offset = 0;
while (val != 0x01) while (first != 0x01)
{ {
val = val >> 1; first = first >> 1;
offset++; offset++;
} }
return first - offset; /* skip the first name as that's used if no flag is set */
return 1 + val - offset;
} }
/** /**
@@ -95,7 +97,7 @@ char *enum_flags_to_string(enum_name_t *e, u_int val, char *buf, size_t len)
return buf; return buf;
} }
if (snprintf(buf, len, "(unset)") >= len) if (snprintf(buf, len, e->names[0]) >= len)
{ {
return NULL; return NULL;
} }
@@ -134,6 +136,54 @@ char *enum_flags_to_string(enum_name_t *e, u_int val, char *buf, size_t len)
return buf; return buf;
} }
/*
* Described in header
*/
bool enum_flags_from_string_as_int(enum_name_t *e, const char *str, u_int *val)
{
enumerator_t *enumerator;
char *name;
*val = 0;
if (!str || !*str)
{
return TRUE;
}
else if (e->next != ENUM_FLAG_MAGIC)
{
return enum_from_name_as_int(e, str, val);
}
enumerator = enumerator_create_token(str, "|", " ");
while (enumerator->enumerate(enumerator, &name))
{
u_int flag, i;
bool found = FALSE;
if (strcaseeq(name, e->names[0]))
{ /* accept name used if no flags are set */
continue;
}
for (i = 1, flag = e->first; flag <= e->last; i++, flag <<= 1)
{
if (e->names[i] && strcaseeq(name, e->names[i]))
{
*val |= flag;
found = TRUE;
break;
}
}
if (!found)
{
enumerator->destroy(enumerator);
return FALSE;
}
}
enumerator->destroy(enumerator);
return TRUE;
}
/** /**
* See header. * See header.
*/ */
+36 -5
View File
@@ -130,13 +130,14 @@ struct enum_name_t {
* @param name name of the enum_name list * @param name name of the enum_name list
* @param first enum value of the first enum string * @param first enum value of the first enum string
* @param last enum value of the last enum string * @param last enum value of the last enum string
* @param unset name used if no flags are set
* @param ... a list of strings * @param ... a list of strings
*/ */
#define ENUM_FLAGS(name, first, last, ...) \ #define ENUM_FLAGS(name, first, last, unset, ...) \
static enum_name_t name##last = {first, last + \ static enum_name_t name##last = {first, last + \
BUILD_ASSERT((__builtin_ffs(last)-__builtin_ffs(first)+1) == \ BUILD_ASSERT((__builtin_ffs(last)-__builtin_ffs(first)+1) == \
countof(((char*[]){__VA_ARGS__}))), \ countof(((char*[]){__VA_ARGS__}))), \
ENUM_FLAG_MAGIC, { __VA_ARGS__ }}; ENUM_END(name, last) ENUM_FLAG_MAGIC, { unset, __VA_ARGS__ }}; ENUM_END(name, last)
/** /**
* Convert a enum value to its string representation. * Convert a enum value to its string representation.
@@ -157,7 +158,7 @@ char *enum_to_name(enum_name_t *e, int val);
*/ */
#define enum_from_name(e, name, valp) ({ \ #define enum_from_name(e, name, valp) ({ \
int _val; \ int _val; \
int _found = enum_from_name_as_int(e, name, &_val); \ bool _found = enum_from_name_as_int(e, name, &_val); \
if (_found) \ if (_found) \
{ \ { \
*(valp) = _val; \ *(valp) = _val; \
@@ -167,13 +168,13 @@ char *enum_to_name(enum_name_t *e, int val);
/** /**
* Convert a enum string back to its enum value, integer pointer variant. * Convert a enum string back to its enum value, integer pointer variant.
* *
* This variant takes integer pointer only, use enum_from_name() to pass * This variant takes an integer pointer, use enum_from_name() to pass
* enum type pointers for the result. * enum type pointers for the result.
* *
* @param e enum names for this enum value * @param e enum names for this enum value
* @param name name to get enum value for * @param name name to get enum value for
* @param val integer pointer receiving value * @param val integer pointer receiving value
* @return TRUE if enum name found, FALSE otherwise * @return TRUE if all names found, FALSE otherwise
*/ */
bool enum_from_name_as_int(enum_name_t *e, const char *name, int *val); bool enum_from_name_as_int(enum_name_t *e, const char *name, int *val);
@@ -188,6 +189,36 @@ bool enum_from_name_as_int(enum_name_t *e, const char *name, int *val);
*/ */
char *enum_flags_to_string(enum_name_t *e, u_int val, char *buf, size_t len); char *enum_flags_to_string(enum_name_t *e, u_int val, char *buf, size_t len);
/**
* Convert a string of flags separated by | to their combined value
*
* @param e enum names for this enum value
* @param str string to get enum value for
* @param valp variable sized pointer receiving value
* @return TRUE if all names found, FALSE otherwise
*/
#define enum_flags_from_string(e, str, valp) ({ \
u_int _val; \
bool _found = enum_flags_from_string_as_int(e, str, &_val); \
if (_found) \
{ \
*(valp) = _val; \
} \
_found; })
/**
* Convert a string of flags separated by | to their combined value.
*
* This variant takes an unsigned integer pointer, use enum_flags_from_names()
* to pass enum type pointers for the result.
*
* @param e enum names for this enum value
* @param str string to get enum value for
* @param val integer pointer receiving value
* @return TRUE if enum name found, FALSE otherwise
*/
bool enum_flags_from_string_as_int(enum_name_t *e, const char *str, u_int *val);
/** /**
* printf hook function for enum_names_t. * printf hook function for enum_names_t.
* *
+1
View File
@@ -539,6 +539,7 @@ static char *whitelist[] = {
"initgroups", "initgroups",
"tzset", "tzset",
"_IO_file_doallocate", "_IO_file_doallocate",
"selinux_check_access",
/* ignore dlopen, as we do not dlclose to get proper leak reports */ /* ignore dlopen, as we do not dlclose to get proper leak reports */
"dlopen", "dlopen",
"dlerror", "dlerror",
+2
View File
@@ -20,6 +20,7 @@
#include <stdio.h> #include <stdio.h>
#include <errno.h> #include <errno.h>
#include "swanctl.h"
#include "command.h" #include "command.h"
#include <collections/hashtable.h> #include <collections/hashtable.h>
@@ -135,6 +136,7 @@ CALLBACK(children_sn, int,
} }
printf("\n"); printf("\n");
print_label(" label: ", child->get(child, "label"));
printf(" local: %s\n", child->get(child, "local-ts")); printf(" local: %s\n", child->get(child, "local-ts"));
printf(" remote: %s\n", child->get(child, "remote-ts")); printf(" remote: %s\n", child->get(child, "remote-ts"));
+2
View File
@@ -18,6 +18,7 @@
#include <errno.h> #include <errno.h>
#include "command.h" #include "command.h"
#include "swanctl.h"
#include <collections/hashtable.h> #include <collections/hashtable.h>
@@ -85,6 +86,7 @@ CALLBACK(policies, int,
ret = vici_parse_cb(res, NULL, policy_values, policy_list, pol); ret = vici_parse_cb(res, NULL, policy_values, policy_list, pol);
printf("%s, %s\n", name, pol->get(pol, "mode")); printf("%s, %s\n", name, pol->get(pol, "mode"));
print_label(" label: ", pol->get(pol, "label"));
printf(" local: %s\n", pol->get(pol, "local-ts")); printf(" local: %s\n", pol->get(pol, "local-ts"));
printf(" remote: %s\n", pol->get(pol, "remote-ts")); printf(" remote: %s\n", pol->get(pol, "remote-ts"));
+22 -4
View File
@@ -43,6 +43,7 @@
#include <errno.h> #include <errno.h>
#include "command.h" #include "command.h"
#include "swanctl.h"
#include <collections/hashtable.h> #include <collections/hashtable.h>
@@ -81,7 +82,6 @@ CALLBACK(sa_values, int,
return 0; return 0;
} }
CALLBACK(sa_list, int, CALLBACK(sa_list, int,
hashtable_t *sa, vici_res_t *res, char *name, void *value, int len) hashtable_t *sa, vici_res_t *res, char *name, void *value, int len)
{ {
@@ -227,6 +227,7 @@ CALLBACK(child_sas, int,
} }
printf("\n"); printf("\n");
print_label(" label ", child->get(child, "label"));
printf(" local %s\n", child->get(child, "local-ts")); printf(" local %s\n", child->get(child, "local-ts"));
printf(" remote %s\n", child->get(child, "remote-ts")); printf(" remote %s\n", child->get(child, "remote-ts"));
} }
@@ -372,8 +373,8 @@ static int list_sas(vici_conn_t *conn)
vici_res_t *res; vici_res_t *res;
bool noblock = FALSE; bool noblock = FALSE;
command_format_options_t format = COMMAND_FORMAT_NONE; command_format_options_t format = COMMAND_FORMAT_NONE;
char *arg, *ike = NULL; char *arg, *ike = NULL, *child = NULL;
int ike_id = 0, ret; int ike_id = 0, child_id = 0, ret;
while (TRUE) while (TRUE)
{ {
@@ -387,6 +388,12 @@ static int list_sas(vici_conn_t *conn)
case 'I': case 'I':
ike_id = atoi(arg); ike_id = atoi(arg);
continue; continue;
case 'c':
child = arg;
continue;
case 'C':
child_id = atoi(arg);
continue;
case 'n': case 'n':
noblock = TRUE; noblock = TRUE;
continue; continue;
@@ -418,6 +425,14 @@ static int list_sas(vici_conn_t *conn)
{ {
vici_add_key_valuef(req, "ike-id", "%d", ike_id); vici_add_key_valuef(req, "ike-id", "%d", ike_id);
} }
if (child)
{
vici_add_key_valuef(req, "child", "%s", child);
}
if (child_id)
{
vici_add_key_valuef(req, "child-id", "%d", child_id);
}
if (noblock) if (noblock)
{ {
vici_add_key_valuef(req, "noblock", "yes"); vici_add_key_valuef(req, "noblock", "yes");
@@ -489,11 +504,14 @@ static void __attribute__ ((constructor))reg()
{ {
command_register((command_t) { command_register((command_t) {
list_sas, 'l', "list-sas", "list currently active IKE_SAs", list_sas, 'l', "list-sas", "list currently active IKE_SAs",
{"[--raw|--pretty]"}, {"[--ike <name>|--ike-id <id>] [--child <name>|--child-id <id>]",
"[--raw|--pretty]"},
{ {
{"help", 'h', 0, "show usage information"}, {"help", 'h', 0, "show usage information"},
{"ike", 'i', 1, "filter IKE_SAs by name"}, {"ike", 'i', 1, "filter IKE_SAs by name"},
{"ike-id", 'I', 1, "filter IKE_SAs by unique identifier"}, {"ike-id", 'I', 1, "filter IKE_SAs by unique identifier"},
{"child", 'c', 1, "filter CHILD_SAs by name"},
{"child-id", 'C', 1, "filter CHILD_SAs by unique identifier"},
{"noblock", 'n', 0, "don't wait for IKE_SAs in use"}, {"noblock", 'n', 0, "don't wait for IKE_SAs in use"},
{"raw", 'r', 0, "dump raw response message"}, {"raw", 'r', 0, "dump raw response message"},
{"pretty", 'P', 0, "dump raw response message in pretty print"}, {"pretty", 'P', 0, "dump raw response message in pretty print"},
+1 -1
View File
@@ -156,7 +156,7 @@ static void __attribute__ ((constructor))reg()
{"help", 'h', 0, "show usage information"}, {"help", 'h', 0, "show usage information"},
{"child", 'c', 1, "terminate by CHILD_SA name"}, {"child", 'c', 1, "terminate by CHILD_SA name"},
{"ike", 'i', 1, "terminate by IKE_SA name"}, {"ike", 'i', 1, "terminate by IKE_SA name"},
{"child-id", 'C', 1, "terminate by CHILD_SA reqid"}, {"child-id", 'C', 1, "terminate by CHILD_SA unique identifier"},
{"ike-id", 'I', 1, "terminate by IKE_SA unique identifier"}, {"ike-id", 'I', 1, "terminate by IKE_SA unique identifier"},
{"force", 'f', 0, "terminate IKE_SA without waiting, unless timeout is set"}, {"force", 'f', 0, "terminate IKE_SA without waiting, unless timeout is set"},
{"timeout", 't', 1, "timeout in seconds before detaching"}, {"timeout", 't', 1, "timeout in seconds before detaching"},
+22
View File
@@ -22,6 +22,7 @@
#include <unistd.h> #include <unistd.h>
#include <library.h> #include <library.h>
#include <selectors/sec_label.h>
/* /*
* Described in header * Described in header
@@ -59,6 +60,27 @@ settings_t *load_swanctl_conf(char *file)
return cfg; return cfg;
} }
/*
* Described in header
*/
void print_label(const char *prefix, const char *value)
{
sec_label_t *label;
chunk_t encoding;
if (value)
{
encoding = chunk_from_hex(chunk_from_str((char*)value), NULL);
label = sec_label_from_encoding(encoding);
if (label)
{
printf("%s%s\n", prefix, label->get_string(label));
label->destroy(label);
}
chunk_free(&encoding);
}
}
/** /**
* Cleanup library atexit() * Cleanup library atexit()
*/ */
+9
View File
@@ -111,4 +111,13 @@ extern char *swanctl_dir;
*/ */
settings_t *load_swanctl_conf(char *file); settings_t *load_swanctl_conf(char *file);
/**
* Print the given security label (hex-encoded) on a separate line with the
* given prefix.
*
* @param prefix prefix to print before the string-version of the label
* @param value hex-encoded security label
*/
void print_label(const char *prefix, const char *value);
#endif /** SWANCTL_H_ @}*/ #endif /** SWANCTL_H_ @}*/
+36 -4
View File
@@ -885,7 +885,8 @@ connections.<conn>.children.<child>.dpd_action = clear
Action to perform for this CHILD_SA on DPD timeout. The default _clear_ Action to perform for this CHILD_SA on DPD timeout. The default _clear_
closes the CHILD_SA and does not take further action. _trap_ installs closes the CHILD_SA and does not take further action. _trap_ installs
a trap policy, which will catch matching traffic and tries to re-negotiate a trap policy, which will catch matching traffic and tries to re-negotiate
the tunnel on-demand. _restart_ immediately tries to re-negotiate the the tunnel on-demand (note that this is redundant if **start_action**
includes _trap_). _restart_ immediately tries to re-negotiate the
CHILD_SA under a fresh IKE_SA. CHILD_SA under a fresh IKE_SA.
connections.<conn>.children.<child>.ipcomp = no connections.<conn>.children.<child>.ipcomp = no
@@ -1010,6 +1011,34 @@ connections.<conn>.children.<child>.if_id_out = 0
The daemon will not install routes for CHILD_SAs that have this option set. The daemon will not install routes for CHILD_SAs that have this option set.
connections.<conn>.children.<child>.label
Optional security label (e.g. SELinux context), IKEv2 only. Refer to
**label_mode** for details on how labels are processed.
connections.<conn>.children.<child>.label_mode = system
Security label mode (_system_, _simple_ or _selinux_), IKEv2 only.
Defines the mode in which the configured security label is used. The default
value of _system_ selects _selinux_ if strongSwan was built with SELinux
support and SELinux is enabled by the kernel, otherwise, _simple_ will be
selected.
If set to _simple_, the label will be used as is as an additional
identifier/selector on the IKEv2 level when negotiating CHILD_SAs and
selecting configs, labels are not installed in the kernel and received
labels have to match exactly.
If set to _selinux_, which is only allowed if SELinux is usable on the
system, the configured label is expected to be a generic context (e.g.
_system_u:object_r:ipsec_spd_t:s0_) for which flows, whose context match it
via association:polmatch, will trigger an acquire if no SA exists yet for
the flow's specific context. The configured label is installed on (trap)
policies, so this should generally be combined with _trap_ in
**start_action**. However, if the connection is initiated directly,
without acquire, a childless IKE_SA is established and appropriate trap
policies are installed on both ends. Labels received from peers are accepted
if they match the configured label via association:polmatch.
connections.<conn>.children.<child>.tfc_padding = 0 connections.<conn>.children.<child>.tfc_padding = 0
Traffic Flow Confidentiality padding. Traffic Flow Confidentiality padding.
@@ -1075,19 +1104,22 @@ connections.<conn>.children.<child>.start_action = none
The value _trap_ installs a trap policy, which triggers the tunnel as soon The value _trap_ installs a trap policy, which triggers the tunnel as soon
as matching traffic has been detected. The value _start_ initiates as matching traffic has been detected. The value _start_ initiates
the connection actively. the connection actively. These two modes can be combined with _trap|start_,
to immediately initiate a connection for which trap policies have been
installed.
When unloading or replacing a CHILD_SA configuration having a When unloading or replacing a CHILD_SA configuration having a
**start_action** different from _none_, the inverse action is performed. **start_action** different from _none_, the inverse action is performed.
Configurations with _start_ get closed, while such with _trap_ get Configurations with _start_ get closed, while such with _trap_ get
uninstalled. uninstalled (both happens for connections with _trap|start_).
connections.<conn>.children.<child>.close_action = none connections.<conn>.children.<child>.close_action = none
Action to perform after a CHILD_SA gets closed (_none_, _trap_, _start_). Action to perform after a CHILD_SA gets closed (_none_, _trap_, _start_).
Action to perform after a CHILD_SA gets closed by the peer. The default of Action to perform after a CHILD_SA gets closed by the peer. The default of
_none_ does not take any action, _trap_ installs a trap policy for the _none_ does not take any action, _trap_ installs a trap policy for the
CHILD_SA. _start_ tries to re-create the CHILD_SA. CHILD_SA (note that this is redundant if **start_action** includes _trap_).
_start_ tries to immediately re-create the CHILD_SA.
**close_action** does not provide any guarantee that the CHILD_SA is kept **close_action** does not provide any guarantee that the CHILD_SA is kept
alive. It acts on explicit close messages only, but not on negotiation alive. It acts on explicit close messages only, but not on negotiation
+26 -13
View File
@@ -406,12 +406,16 @@ do
echo -e "\nPRE-TEST\n" >> $CONSOLE_LOG 2>&1 echo -e "\nPRE-TEST\n" >> $CONSOLE_LOG 2>&1
eval `awk -F "::" '{ eval `awk -F "::" '{
if ($1 !~ /^#.*/ && $2 != "") if ($0 ~ /^#.*/)
{ {
printf("echo \"$(print_time)%s# %s\"; ", $1, $2) printf("echo \"%s\"; ", $0);
printf("ssh \044SSHCONF root@\044ipv4_%s \"%s\"; ", $1, $2) }
printf("echo;\n") else if ($2 != "")
} {
printf("echo \"$(print_time)%s# %s\"; ", $1, $2)
printf("ssh \044SSHCONF root@\044ipv4_%s \"%s\"; ", $1, $2)
printf("echo;\n")
}
}' $TESTDIR/pretest.dat` >> $CONSOLE_LOG 2>&1 }' $TESTDIR/pretest.dat` >> $CONSOLE_LOG 2>&1
@@ -443,7 +447,12 @@ do
command=$2 command=$2
pattern=$3 pattern=$3
hit=$4 hit=$4
if (host ~ /^#.*/ || command == "") if ($0 ~ /^#.*/)
{
printf("echo \"%s\"; ", $0);
next
}
else if (command == "")
{ {
next next
} }
@@ -766,12 +775,16 @@ do
echo -e "\nPOST-TEST\n" >> $CONSOLE_LOG 2>&1 echo -e "\nPOST-TEST\n" >> $CONSOLE_LOG 2>&1
eval `awk -F "::" '{ eval `awk -F "::" '{
if ($1 !~ /^#.*/ && $2 != "") if ($0 ~ /^#.*/)
{ {
printf("echo \"$(print_time)%s# %s\"; ", $1, $2) printf("echo \"%s\"; ", $0);
printf("ssh \044SSHCONF root@\044ipv4_%s \"%s\"; ", $1, $2) }
printf("echo;\n") else if ($2 != "")
} {
printf("echo \"$(print_time)%s# %s\"; ", $1, $2)
printf("ssh \044SSHCONF root@\044ipv4_%s \"%s\"; ", $1, $2)
printf("echo;\n")
}
}' $TESTDIR/posttest.dat` >> $CONSOLE_LOG 2>&1 }' $TESTDIR/posttest.dat` >> $CONSOLE_LOG 2>&1
########################################################################## ##########################################################################
@@ -0,0 +1,11 @@
In order to support <b>Differentiated Services</b> (DiffServ), two parallel IPsec
connections between the subnets behind the gateways <b>moon</b> and <b>sun</b> are
set up. Using <b>XFRM marks</b> one IPsec SA is designated for <b>Best Effort</b> (BE)
traffic and the second SA for <b>Expedited Forwarding</b> (EF) traffic. To guarantee
that the CHILD_SA with the correct mark is selected on the responder side, labels
are used and negotiated via IKEv2. The authentication is based on <b>X.509 certificates</b>.
<p/>
Upon the successful establishment of the IPsec tunnel, the updown script automatically
inserts iptables-based firewall rules that let pass the tunneled traffic
In order to test both tunnel and firewall, client <b>alice</b> behind gateway <b>moon</b>
pings client <b>bob</b> located behind gateway <b>sun</b>.
@@ -0,0 +1,10 @@
moon::swanctl --list-sas --raw 2> /dev/null::dscp.*version=2 state=ESTABLISHED local-host=PH_IP_MOON local-port=500 local-id=moon.strongswan.org remote-host=PH_IP_SUN remote-port=500 remote-id=sun.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net-be.*state=INSTALLED mode=TUNNEL label=647363702d626500.*ESP.*mark-in=0000000a mark-out=0000000a encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[10.2.0.0/16]::YES
moon::swanctl --list-sas --raw 2> /dev/null::dscp.*version=2 state=ESTABLISHED local-host=PH_IP_MOON local-port=500 local-id=moon.strongswan.org remote-host=PH_IP_SUN remote-port=500 remote-id=sun.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net-ef.*state=INSTALLED mode=TUNNEL label=647363702d656600.*ESP.*mark-in=00000014 mark-out=00000014 encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[10.2.0.0/16]::YES
sun:: swanctl --list-sas --raw 2> /dev/null::dscp.*version=2 state=ESTABLISHED local-host=PH_IP_SUN local-port=500 local-id=sun.strongswan.org remote-host=PH_IP_MOON remote-port=500 remote-id=moon.strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net-be.*state=INSTALLED mode=TUNNEL label=647363702d626500.*ESP.*mark-in=0000000a mark-out=0000000a encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.2.0.0/16] remote-ts=\[10.1.0.0/16]::YES
sun:: swanctl --list-sas --raw 2> /dev/null::dscp.*version=2 state=ESTABLISHED local-host=PH_IP_SUN local-port=500 local-id=sun.strongswan.org remote-host=PH_IP_MOON remote-port=500 remote-id=moon.strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net-ef.*state=INSTALLED mode=TUNNEL label=647363702d656600.*ESP.*mark-in=00000014 mark-out=00000014 encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.2.0.0/16] remote-ts=\[10.1.0.0/16]::YES
alice::ping -c 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_.eq=1::YES
venus::ping -c 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_.eq=1::YES
moon::swanctl --list-sas --child net-be --raw 2> /dev/null::child-sas.*net-be.*packets-in=1.*packets-out=1::YES
moon::swanctl --list-sas --child net-ef --raw 2> /dev/null::child-sas.*net-ef.*packets-in=1.*packets-out=1::YES
sun::tcpdump::IP moon.strongswan.org > sun.strongswan.org: ESP::YES
sun::tcpdump::IP sun.strongswan.org > moon.strongswan.org: ESP::YES
@@ -0,0 +1,14 @@
# /etc/strongswan.conf - strongSwan configuration file
swanctl {
load = pem pkcs1 x509 revocation constraints pubkey openssl random
}
charon-systemd {
load = random nonce aes sha1 sha2 hmac pem pkcs1 x509 revocation curve25519 gmp curl kernel-netlink socket-default updown vici
syslog {
daemon {
knl = 2
}
}
}
@@ -0,0 +1,38 @@
connections {
dscp {
local_addrs = PH_IP_MOON
remote_addrs = PH_IP_SUN
local {
auth = pubkey
certs = moonCert.pem
id = moon.strongswan.org
}
remote {
auth = pubkey
id = sun.strongswan.org
}
children {
net-be {
local_ts = 10.1.0.0/16
remote_ts = 10.2.0.0/16
mark_in = 10
mark_out = 10
label = dscp-be
updown = /usr/local/libexec/ipsec/_updown iptables
esp_proposals = aes128gcm128-x25519
}
net-ef : connections.dscp.children.net-be {
mark_in = 20
mark_out = 20
label = dscp-ef
}
}
version = 2
mobike = no
proposals = aes128-sha256-x25519
}
}
@@ -0,0 +1,15 @@
# /etc/strongswan.conf - strongSwan configuration file
swanctl {
load = pem pkcs1 x509 revocation constraints pubkey openssl random
}
charon-systemd {
load = random nonce aes sha1 sha2 hmac pem pkcs1 x509 revocation curve25519 gmp curl kernel-netlink socket-default updown vici
multiple_authentication = no
syslog {
daemon {
knl = 2
}
}
}
@@ -0,0 +1,38 @@
connections {
dscp {
local_addrs = PH_IP_SUN
remote_addrs = PH_IP_MOON
local {
auth = pubkey
certs = sunCert.pem
id = sun.strongswan.org
}
remote {
auth = pubkey
id = moon.strongswan.org
}
children {
net-be {
local_ts = 10.2.0.0/16
remote_ts = 10.1.0.0/16
mark_in = 10
mark_out = 10
label = dscp-be
updown = /usr/local/libexec/ipsec/_updown iptables
esp_proposals = aes128gcm128-x25519
}
net-ef : connections.dscp.children.net-be {
mark_in = 20
mark_out = 20
label = dscp-ef
}
}
version = 2
mobike = no
proposals = aes128-sha256-x25519
}
}
@@ -0,0 +1,8 @@
moon::systemctl stop strongswan
sun::systemctl stop strongswan
moon::iptables-restore < /etc/iptables.flush
sun::iptables-restore < /etc/iptables.flush
alice::iptables -t mangle -F OUTPUT
venus::iptables -t mangle -F OUTPUT
bob::iptables -t mangle -F OUTPUT
@@ -0,0 +1,16 @@
moon::iptables-restore < /etc/iptables.rules
sun::iptables-restore < /etc/iptables.rules
alice::iptables -t mangle -A OUTPUT -p icmp -j DSCP --set-dscp-class BE
venus::iptables -t mangle -A OUTPUT -p icmp -j DSCP --set-dscp-class EF
moon::iptables -t mangle -A PREROUTING -m dscp --dscp-class BE -j MARK --set-mark 10
moon::iptables -t mangle -A PREROUTING -m dscp --dscp-class EF -j MARK --set-mark 20
bob::iptables -t mangle -A OUTPUT -d PH_IP_ALICE -p icmp -j DSCP --set-dscp-class BE
bob::iptables -t mangle -A OUTPUT -d PH_IP_VENUS -p icmp -j DSCP --set-dscp-class EF
sun::iptables -t mangle -A PREROUTING -m dscp --dscp-class BE -j MARK --set-mark 10
sun::iptables -t mangle -A PREROUTING -m dscp --dscp-class EF -j MARK --set-mark 20
sun::systemctl start strongswan
moon::systemctl start strongswan
sun::expect-connection dscp
moon::expect-connection dscp
moon::swanctl --initiate --child net-be
moon::swanctl --initiate --child net-ef
@@ -0,0 +1,25 @@
#!/bin/bash
#
# This configuration file provides information on the
# guest instances used for this test
# All guest instances that are required for this test
#
VIRTHOSTS="alice venus moon winnetou sun bob"
# Corresponding block diagram
#
DIAGRAM="a-v-m-w-s-b.png"
# Guest instances on which tcpdump is to be started
#
TCPDUMPHOSTS="sun"
# Guest instances on which IPsec is started
# Used for IPsec logging purposes
#
IPSECHOSTS="moon sun"
# charon controlled by swanctl
#
SWANCTL=1