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:
@@ -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([perl-cpan], [enable build of provided perl 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-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])
|
||||
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
|
||||
AC_MSG_CHECKING([for dladdr()])
|
||||
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-pfroute], [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([save-keys], [c])
|
||||
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_SQL, test x$attr_sql = xtrue)
|
||||
AM_CONDITIONAL(USE_COUNTERS, test x$counters = xtrue)
|
||||
AM_CONDITIONAL(USE_SELINUX, test x$selinux = xtrue)
|
||||
|
||||
# other options
|
||||
# ---------------
|
||||
@@ -2065,6 +2075,7 @@ AC_CONFIG_FILES([
|
||||
src/libcharon/plugins/resolve/Makefile
|
||||
src/libcharon/plugins/attr/Makefile
|
||||
src/libcharon/plugins/attr_sql/Makefile
|
||||
src/libcharon/plugins/selinux/Makefile
|
||||
src/libcharon/tests/Makefile
|
||||
src/libtpmtss/Makefile
|
||||
src/libtpmtss/plugins/tpm/Makefile
|
||||
|
||||
+2
-1
@@ -171,7 +171,8 @@ all|coverage|sonarcloud)
|
||||
DEPS="$DEPS libcurl4-gnutls-dev libsoup2.4-dev libunbound-dev libldns-dev
|
||||
libmysqlclient-dev libsqlite3-dev clearsilver-dev libfcgi-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
|
||||
DEPS="$DEPS libiptc-dev"
|
||||
else
|
||||
|
||||
@@ -388,6 +388,8 @@ static child_cfg_t* create_child_cfg(private_cmd_connection_t *this,
|
||||
case TS_IPV6_ADDR_RANGE:
|
||||
has_v6 = TRUE;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
|
||||
}
|
||||
|
||||
@@ -635,8 +635,8 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
|
||||
},
|
||||
},
|
||||
.mode = MODE_TUNNEL,
|
||||
.dpd_action = ACTION_RESTART,
|
||||
.close_action = ACTION_RESTART,
|
||||
.dpd_action = ACTION_START,
|
||||
.close_action = ACTION_START,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -903,7 +903,7 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
|
||||
* Initiate
|
||||
*/
|
||||
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);
|
||||
|
||||
|
||||
@@ -24,8 +24,10 @@
|
||||
|
||||
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);
|
||||
charon->kernel->acquire(charon->kernel, sp_id, NULL, NULL);
|
||||
charon->kernel->acquire(charon->kernel, sp_id, &data);
|
||||
*res = TKM_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -305,12 +305,12 @@ static void process_auth_response(private_pretend_auth_t *this,
|
||||
if (this->tsi)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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,
|
||||
.dpd_action = ACTION_RESTART,
|
||||
.close_action = ACTION_RESTART,
|
||||
.dpd_action = ACTION_START,
|
||||
.close_action = ACTION_START,
|
||||
};
|
||||
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 */
|
||||
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");
|
||||
charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
|
||||
|
||||
@@ -726,6 +726,13 @@ if MONOLITHIC
|
||||
endif
|
||||
endif
|
||||
|
||||
if USE_SELINUX
|
||||
SUBDIRS += plugins/selinux
|
||||
if MONOLITHIC
|
||||
libcharon_la_LIBADD += plugins/selinux/libstrongswan-selinux.la
|
||||
endif
|
||||
endif
|
||||
|
||||
if USE_ATTR_SQL
|
||||
SUBDIRS += plugins/attr_sql
|
||||
if MONOLITHIC
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
|
||||
#include <daemon.h>
|
||||
|
||||
ENUM(action_names, ACTION_NONE, ACTION_RESTART,
|
||||
"clear",
|
||||
"hold",
|
||||
"restart",
|
||||
ENUM_FLAGS(action_names, ACTION_TRAP, ACTION_START,
|
||||
"none",
|
||||
"trap",
|
||||
"start",
|
||||
);
|
||||
|
||||
/** 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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@@ -522,6 +532,97 @@ METHOD(child_cfg_t, get_set_mark, mark_t,
|
||||
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, ¤t))
|
||||
{
|
||||
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,
|
||||
private_child_cfg_t *this)
|
||||
{
|
||||
@@ -607,7 +708,9 @@ METHOD(child_cfg_t, equals, bool,
|
||||
this->hw_offload == other->hw_offload &&
|
||||
this->copy_dscp == other->copy_dscp &&
|
||||
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*,
|
||||
@@ -625,6 +728,7 @@ METHOD(child_cfg_t, destroy, void,
|
||||
this->proposals->destroy_offset(this->proposals, offsetof(proposal_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));
|
||||
DESTROY_IF(this->label);
|
||||
free(this->updown);
|
||||
free(this->interface);
|
||||
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_mark = _get_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_manual_prio = _get_manual_prio,
|
||||
.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,
|
||||
.set_mark_in = data->set_mark_in,
|
||||
.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,
|
||||
.inactivity = data->inactivity,
|
||||
.tfc = data->tfc,
|
||||
|
||||
@@ -40,11 +40,11 @@ typedef struct child_cfg_create_t child_cfg_create_t;
|
||||
*/
|
||||
enum action_t {
|
||||
/** No action */
|
||||
ACTION_NONE,
|
||||
/** Route config to establish or reestablish on demand */
|
||||
ACTION_ROUTE,
|
||||
/** Start or restart config immediately */
|
||||
ACTION_RESTART,
|
||||
ACTION_NONE = 0,
|
||||
/** Install trap policy to (re-)establish on demand */
|
||||
ACTION_TRAP = (1<<0),
|
||||
/** Start or restart immediately */
|
||||
ACTION_START = (1<<1),
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -247,6 +247,41 @@ struct child_cfg_t {
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@@ -367,6 +402,10 @@ struct child_cfg_create_t {
|
||||
mark_t set_mark_in;
|
||||
/** Optional outbound mark the SA should apply to traffic */
|
||||
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 */
|
||||
ipsec_mode_t mode;
|
||||
/** TFC padding size, 0 to disable, -1 to pad to PMTU */
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
static int get_ts_match(child_cfg_t *cfg, bool local,
|
||||
linked_list_t *sup_list, linked_list_t *hosts)
|
||||
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_labels)
|
||||
{
|
||||
linked_list_t *cfg_list;
|
||||
enumerator_t *sup_enum, *cfg_enum;
|
||||
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 */
|
||||
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*,
|
||||
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;
|
||||
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);
|
||||
enumerator = create_child_cfg_enumerator(this);
|
||||
while (enumerator->enumerate(enumerator, ¤t))
|
||||
{
|
||||
int my_prio, other_prio;
|
||||
u_int my_prio, other_prio;
|
||||
|
||||
my_prio = get_ts_match(current, TRUE, my_ts, my_hosts);
|
||||
other_prio = get_ts_match(current, FALSE, other_ts, other_hosts);
|
||||
|
||||
if (my_prio && other_prio)
|
||||
my_prio = get_ts_match(current, TRUE, my_ts, my_hosts, my_labels);
|
||||
if (!my_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);
|
||||
if (my_prio + other_prio > best)
|
||||
{
|
||||
|
||||
@@ -174,17 +174,20 @@ struct peer_cfg_t {
|
||||
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 other_ts TS for remote 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
|
||||
* @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_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.
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -205,7 +205,6 @@ static void generate_u_int_type(private_generator_t *this,
|
||||
case U_INT_4:
|
||||
number_of_bits = 4;
|
||||
break;
|
||||
case TS_TYPE:
|
||||
case RESERVED_BYTE:
|
||||
case SPI_SIZE:
|
||||
case U_INT_8:
|
||||
@@ -281,7 +280,6 @@ static void generate_u_int_type(private_generator_t *this,
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TS_TYPE:
|
||||
case RESERVED_BYTE:
|
||||
case SPI_SIZE:
|
||||
case U_INT_8:
|
||||
@@ -478,7 +476,6 @@ METHOD(generator_t, generate_payload, void,
|
||||
case IKE_SPI:
|
||||
case RESERVED_BYTE:
|
||||
case SPI_SIZE:
|
||||
case TS_TYPE:
|
||||
case ATTRIBUTE_TYPE:
|
||||
case ATTRIBUTE_LENGTH:
|
||||
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);
|
||||
generate_u_int_type(this, U_INT_32, rules[i].offset);
|
||||
break;
|
||||
case ADDRESS:
|
||||
case SPI:
|
||||
case CHUNK_DATA:
|
||||
case ENCRYPTED_DATA:
|
||||
|
||||
@@ -368,7 +368,6 @@ METHOD(parser_t, parse_payload, status_t,
|
||||
payload_t *pld;
|
||||
void *output;
|
||||
int payload_length = 0, spi_size = 0, attribute_length = 0, header_length;
|
||||
uint16_t ts_type = 0;
|
||||
bool attribute_format = FALSE;
|
||||
int rule_number, rule_count;
|
||||
encoding_rule_t *rule;
|
||||
@@ -588,28 +587,6 @@ METHOD(parser_t, parse_payload, status_t,
|
||||
}
|
||||
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:
|
||||
{
|
||||
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",
|
||||
"ATTRIBUTE_VALUE",
|
||||
"TS_TYPE",
|
||||
"ADDRESS",
|
||||
"CHUNK_DATA",
|
||||
"IKE_SPI",
|
||||
"ENCRYPTED_DATA",
|
||||
|
||||
@@ -255,32 +255,6 @@ enum encoding_type_t {
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -14,11 +14,33 @@
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* 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 <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;
|
||||
|
||||
@@ -48,50 +70,32 @@ struct private_traffic_selector_substructure_t {
|
||||
uint16_t payload_length;
|
||||
|
||||
/**
|
||||
* Start port number.
|
||||
* Port/address range or security label.
|
||||
*/
|
||||
uint16_t start_port;
|
||||
|
||||
/**
|
||||
* End port number.
|
||||
*/
|
||||
uint16_t end_port;
|
||||
|
||||
/**
|
||||
* Starting address.
|
||||
*/
|
||||
chunk_t starting_address;
|
||||
|
||||
/**
|
||||
* Ending address.
|
||||
*/
|
||||
chunk_t ending_address;
|
||||
chunk_t ts_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* private_traffic_selector_substructure_t.
|
||||
*/
|
||||
static encoding_rule_t encodings[] = {
|
||||
/* 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*/
|
||||
{ U_INT_8, offsetof(private_traffic_selector_substructure_t, ip_protocol_id) },
|
||||
/* Length of the whole payload*/
|
||||
{ PAYLOAD_LENGTH,offsetof(private_traffic_selector_substructure_t, payload_length) },
|
||||
/* 2 Byte start port*/
|
||||
{ U_INT_16, offsetof(private_traffic_selector_substructure_t, start_port) },
|
||||
/* 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) }
|
||||
/* traffic selector data, length is defined in PAYLOAD_LENGTH */
|
||||
{ CHUNK_DATA, offsetof(private_traffic_selector_substructure_t, ts_data) },
|
||||
};
|
||||
|
||||
/*
|
||||
/* Regular traffic selectors for address ranges:
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
@@ -107,48 +111,91 @@ static encoding_rule_t encodings[] = {
|
||||
~ 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,
|
||||
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)
|
||||
{
|
||||
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:
|
||||
{
|
||||
if ((this->starting_address.len != 16) ||
|
||||
(this->ending_address.len != 16))
|
||||
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))
|
||||
{
|
||||
/* ipv6 address must be 16 bytes long */
|
||||
return FAILED;
|
||||
}
|
||||
if (start_port > end_port)
|
||||
{
|
||||
/* OPAQUE ports are the only exception */
|
||||
if (start_port != 0xffff && end_port != 0)
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TS_SECLABEL:
|
||||
if (!this->ts_data.len)
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
/* not supported ts type */
|
||||
return FAILED;
|
||||
}
|
||||
/* unsupported TS type, just ignored later */
|
||||
break;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@@ -162,7 +209,7 @@ METHOD(payload_t, get_encoding_rules, int,
|
||||
METHOD(payload_t, get_header_length, int,
|
||||
private_traffic_selector_substructure_t *this)
|
||||
{
|
||||
return 8;
|
||||
return 4;
|
||||
}
|
||||
|
||||
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*,
|
||||
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(
|
||||
this->ip_protocol_id, this->ts_type,
|
||||
this->starting_address, this->start_port,
|
||||
this->ending_address, this->end_port);
|
||||
start_addr, start_port, end_addr, 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,
|
||||
private_traffic_selector_substructure_t *this)
|
||||
{
|
||||
free(this->starting_address.ptr);
|
||||
free(this->ending_address.ptr);
|
||||
free(this->ts_data.ptr);
|
||||
free(this);
|
||||
}
|
||||
|
||||
@@ -225,6 +287,7 @@ traffic_selector_substructure_t *traffic_selector_substructure_create()
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.get_traffic_selector = _get_traffic_selector,
|
||||
.get_sec_label = _get_sec_label,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.payload_length = get_header_length(this),
|
||||
@@ -241,16 +304,34 @@ traffic_selector_substructure_t *traffic_selector_substructure_create_from_traff
|
||||
traffic_selector_t *ts)
|
||||
{
|
||||
private_traffic_selector_substructure_t *this;
|
||||
bio_writer_t *writer;
|
||||
|
||||
this = (private_traffic_selector_substructure_t*)traffic_selector_substructure_create();
|
||||
this->ts_type = ts->get_type(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;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,27 @@
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* 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
|
||||
@@ -27,6 +48,7 @@ typedef struct traffic_selector_substructure_t traffic_selector_substructure_t;
|
||||
#include <library.h>
|
||||
#include <networking/host.h>
|
||||
#include <selectors/traffic_selector.h>
|
||||
#include <selectors/sec_label.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.
|
||||
*/
|
||||
struct traffic_selector_substructure_t {
|
||||
|
||||
/**
|
||||
* The payload_t 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,
|
||||
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);
|
||||
sec_label_t *(*get_sec_label)(traffic_selector_substructure_t *this);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Creates an initialized traffic selector substructure using
|
||||
* the values from a traffic_selector_t.
|
||||
* Creates a traffic selector substructure based on 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
|
||||
*/
|
||||
traffic_selector_substructure_t *traffic_selector_substructure_create_from_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_ @}*/
|
||||
|
||||
@@ -218,10 +218,11 @@ METHOD(ts_payload_t, set_initiator, void,
|
||||
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;
|
||||
traffic_selector_substructure_t *subst;
|
||||
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);
|
||||
while (enumerator->enumerate(enumerator, &subst))
|
||||
{
|
||||
ts = subst->get_traffic_selector(subst);
|
||||
list->insert_last(list, ts);
|
||||
if (labels)
|
||||
{
|
||||
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);
|
||||
|
||||
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,
|
||||
private_ts_payload_t *this)
|
||||
{
|
||||
@@ -267,6 +296,7 @@ ts_payload_t *ts_payload_create(bool is_initiator)
|
||||
.get_initiator = _get_initiator,
|
||||
.set_initiator = _set_initiator,
|
||||
.get_traffic_selectors = _get_traffic_selectors,
|
||||
.get_sec_labels = _get_sec_labels,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.next_payload = PL_NONE,
|
||||
@@ -281,7 +311,8 @@ ts_payload_t *ts_payload_create(bool is_initiator)
|
||||
* Described in header
|
||||
*/
|
||||
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;
|
||||
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);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
if (label)
|
||||
{
|
||||
subst = traffic_selector_substructure_create_from_sec_label(label);
|
||||
this->substrs->insert_last(this->substrs, subst);
|
||||
}
|
||||
compute_length(this);
|
||||
|
||||
return &this->public;
|
||||
|
||||
@@ -63,11 +63,20 @@ struct ts_payload_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
|
||||
*/
|
||||
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.
|
||||
@@ -84,13 +93,16 @@ struct ts_payload_t {
|
||||
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 traffic_selectors list of traffic selectors to include
|
||||
* @param label optional security label to include
|
||||
* @return ts_payload_t object
|
||||
*/
|
||||
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_ @}*/
|
||||
|
||||
@@ -53,20 +53,27 @@ static inline protocol_id_t proto_ip2ike(uint8_t protocol)
|
||||
}
|
||||
|
||||
METHOD(kernel_listener_t, acquire, bool,
|
||||
private_kernel_handler_t *this, uint32_t reqid,
|
||||
traffic_selector_t *src_ts, traffic_selector_t *dst_ts)
|
||||
private_kernel_handler_t *this, uint32_t reqid, kernel_acquire_data_t *data)
|
||||
{
|
||||
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 "
|
||||
"reqid {%u}", src_ts, dst_ts, reqid);
|
||||
"reqid {%u}%s", data->src, data->dst, reqid, buf);
|
||||
}
|
||||
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,
|
||||
(job_t*)acquire_job_create(reqid, src_ts, dst_ts));
|
||||
(job_t*)acquire_job_create(reqid, data));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
@@ -208,6 +208,8 @@ typedef struct {
|
||||
uint32_t if_id_in;
|
||||
/** outbound interface ID used for SA */
|
||||
uint32_t if_id_out;
|
||||
/** security label */
|
||||
sec_label_t *label;
|
||||
/** local traffic selectors */
|
||||
array_t *local;
|
||||
/** 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->remote, offsetof(traffic_selector_t, destroy));
|
||||
DESTROY_IF(entry->label);
|
||||
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
|
||||
*/
|
||||
static u_int hash_reqid(reqid_entry_t *entry)
|
||||
{
|
||||
return chunk_hash_inc(chunk_from_thing(entry->reqid),
|
||||
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))))));
|
||||
entry_hash_shared(entry));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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)
|
||||
{
|
||||
return a->reqid == b->reqid &&
|
||||
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;
|
||||
if (a->reqid == b->reqid)
|
||||
{
|
||||
return entry_equals_shared(a, b);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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)
|
||||
{
|
||||
return hash_ts_array(entry->local, hash_ts_array(entry->remote,
|
||||
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)))))));
|
||||
return hash_ts_array(entry->local,
|
||||
hash_ts_array(entry->remote,
|
||||
entry_hash_shared(entry)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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)
|
||||
{
|
||||
return ts_array_equals(a->local, b->local) &&
|
||||
ts_array_equals(a->remote, b->remote) &&
|
||||
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;
|
||||
if (ts_array_equals(a->local, b->local) &&
|
||||
ts_array_equals(a->remote, b->remote))
|
||||
{
|
||||
return entry_equals_shared(a, b);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -346,7 +372,7 @@ METHOD(kernel_interface_t, alloc_reqid, status_t,
|
||||
private_kernel_interface_t *this,
|
||||
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,
|
||||
uint32_t *reqid)
|
||||
sec_label_t *label, uint32_t *reqid)
|
||||
{
|
||||
static uint32_t counter = 0;
|
||||
reqid_entry_t *entry = NULL, *tmpl;
|
||||
@@ -359,6 +385,7 @@ METHOD(kernel_interface_t, alloc_reqid, status_t,
|
||||
.mark_out = mark_out,
|
||||
.if_id_in = if_id_in,
|
||||
.if_id_out = if_id_out,
|
||||
.label = label ? label->clone(label) : NULL,
|
||||
.reqid = *reqid,
|
||||
);
|
||||
|
||||
@@ -404,7 +431,8 @@ METHOD(kernel_interface_t, alloc_reqid, status_t,
|
||||
|
||||
METHOD(kernel_interface_t, release_reqid, status_t,
|
||||
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 = reqid,
|
||||
@@ -412,6 +440,7 @@ METHOD(kernel_interface_t, release_reqid, status_t,
|
||||
.mark_out = mark_out,
|
||||
.if_id_in = if_id_in,
|
||||
.if_id_out = if_id_out,
|
||||
.label = label,
|
||||
};
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
@@ -827,7 +856,7 @@ METHOD(kernel_interface_t, remove_listener, void,
|
||||
|
||||
METHOD(kernel_interface_t, acquire, void,
|
||||
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;
|
||||
enumerator_t *enumerator;
|
||||
@@ -835,8 +864,7 @@ METHOD(kernel_interface_t, acquire, void,
|
||||
enumerator = this->listeners->create_enumerator(this->listeners);
|
||||
while (enumerator->enumerate(enumerator, &listener))
|
||||
{
|
||||
if (listener->acquire &&
|
||||
!listener->acquire(listener, reqid, src_ts, dst_ts))
|
||||
if (listener->acquire && !listener->acquire(listener, reqid, data))
|
||||
{
|
||||
this->listeners->remove_at(this->listeners, enumerator);
|
||||
}
|
||||
|
||||
@@ -145,13 +145,15 @@ struct kernel_interface_t {
|
||||
* @param mark_out outbound mark on SA
|
||||
* @param if_id_in inbound 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
|
||||
* @return SUCCESS if reqid allocated
|
||||
*/
|
||||
status_t (*alloc_reqid)(kernel_interface_t *this,
|
||||
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, uint32_t *reqid);
|
||||
uint32_t if_id_out, sec_label_t *label,
|
||||
uint32_t *reqid);
|
||||
|
||||
/**
|
||||
* Release a previously allocated reqid.
|
||||
@@ -161,11 +163,13 @@ struct kernel_interface_t {
|
||||
* @param mark_out outbound mark on SA
|
||||
* @param if_id_in inbound 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
|
||||
*/
|
||||
status_t (*release_reqid)(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);
|
||||
uint32_t if_id_in, uint32_t if_id_out,
|
||||
sec_label_t *label);
|
||||
|
||||
/**
|
||||
* Add an SA to the SAD.
|
||||
@@ -513,11 +517,10 @@ struct kernel_interface_t {
|
||||
* Raise an acquire event.
|
||||
*
|
||||
* @param reqid reqid of the policy to acquire
|
||||
* @param src_ts source traffic selector
|
||||
* @param dst_ts destination traffic selector
|
||||
* @param data data from the acquire
|
||||
*/
|
||||
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.
|
||||
|
||||
@@ -38,6 +38,7 @@ typedef struct kernel_ipsec_query_policy_t kernel_ipsec_query_policy_t;
|
||||
#include <networking/host.h>
|
||||
#include <ipsec/ipsec_types.h>
|
||||
#include <selectors/traffic_selector.h>
|
||||
#include <selectors/sec_label.h>
|
||||
#include <plugins/plugin.h>
|
||||
#include <kernel/kernel_interface.h>
|
||||
|
||||
@@ -97,6 +98,8 @@ struct kernel_ipsec_add_sa_t {
|
||||
hw_offload_t hw_offload;
|
||||
/** Mark the SA should apply to packets after processing */
|
||||
mark_t mark;
|
||||
/** Security label to match or apply */
|
||||
sec_label_t *label;
|
||||
/** TRUE to use Extended Sequence Numbers */
|
||||
bool esn;
|
||||
/** 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;
|
||||
/** TRUE to enable UDP encapsulation */
|
||||
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;
|
||||
/** Network interface restricting policy */
|
||||
char *interface;
|
||||
/** Security label restricting policy */
|
||||
sec_label_t *label;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,12 +22,25 @@
|
||||
#define KERNEL_LISTENER_H_
|
||||
|
||||
typedef struct kernel_listener_t kernel_listener_t;
|
||||
typedef struct kernel_acquire_data_t kernel_acquire_data_t;
|
||||
|
||||
#include <networking/host.h>
|
||||
#include <networking/tun_device.h>
|
||||
#include <selectors/traffic_selector.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.
|
||||
*
|
||||
@@ -39,12 +52,11 @@ struct kernel_listener_t {
|
||||
* Hook called if an acquire event for a policy is received.
|
||||
*
|
||||
* @param reqid reqid of the policy to acquire
|
||||
* @param src_ts source traffic selector
|
||||
* @param dst_ts destination traffic selector
|
||||
* @param data data from the acquire
|
||||
* @return TRUE to remain registered, FALSE to unregister
|
||||
*/
|
||||
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.
|
||||
|
||||
@@ -577,6 +577,9 @@ struct policy_entry_t {
|
||||
/** Optional interface ID */
|
||||
uint32_t if_id;
|
||||
|
||||
/** Optional security label */
|
||||
sec_label_t *label;
|
||||
|
||||
/** Associated route installed for this policy */
|
||||
route_entry_t *route;
|
||||
|
||||
@@ -609,6 +612,7 @@ static void policy_entry_destroy(private_kernel_netlink_ipsec_t *this,
|
||||
policy->direction, this);
|
||||
policy->used_by->destroy(policy->used_by);
|
||||
}
|
||||
DESTROY_IF(policy->label);
|
||||
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)
|
||||
{
|
||||
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))));
|
||||
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)) &&
|
||||
key->mark == other_key->mark &&
|
||||
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 rtattr *rta;
|
||||
size_t rtasize;
|
||||
traffic_selector_t *src_ts, *dst_ts;
|
||||
kernel_acquire_data_t data = {};
|
||||
chunk_t label = chunk_empty;
|
||||
uint32_t reqid = 0;
|
||||
int proto = 0;
|
||||
uint8_t proto;
|
||||
|
||||
acquire = NLMSG_DATA(hdr);
|
||||
proto = acquire->id.proto;
|
||||
rta = XFRM_RTA(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)
|
||||
{
|
||||
struct xfrm_user_tmpl* tmpl;
|
||||
tmpl = (struct xfrm_user_tmpl*)RTA_DATA(rta);
|
||||
struct xfrm_user_tmpl* tmpl = RTA_DATA(rta);
|
||||
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);
|
||||
}
|
||||
switch (proto)
|
||||
@@ -928,12 +952,18 @@ static void process_acquire(private_kernel_netlink_ipsec_t *this,
|
||||
break;
|
||||
default:
|
||||
/* acquire for AH/ESP only, not for IPCOMP */
|
||||
|
||||
return;
|
||||
}
|
||||
src_ts = selector2ts(&acquire->sel, TRUE);
|
||||
dst_ts = selector2ts(&acquire->sel, FALSE);
|
||||
data.src = selector2ts(&acquire->sel, TRUE);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@@ -1871,6 +1942,11 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (!add_label(hdr, sizeof(request), data->label))
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (ipcomp == IPCOMP_NONE && (data->mark.value | data->mark.mask))
|
||||
{
|
||||
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 = {
|
||||
.new_src = data->new_src,
|
||||
.new_dst = data->new_dst,
|
||||
.new_reqid = data->new_reqid,
|
||||
};
|
||||
update_sa(this, &ipcomp_id, &ipcomp);
|
||||
}
|
||||
@@ -2356,6 +2433,10 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
|
||||
sa = NLMSG_DATA(hdr);
|
||||
memcpy(sa, NLMSG_DATA(out_hdr), sizeof(struct xfrm_usersa_info));
|
||||
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))
|
||||
{
|
||||
@@ -2744,7 +2825,9 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this,
|
||||
}
|
||||
tmpl->reqid = ipsec->cfg.reqid;
|
||||
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;
|
||||
}
|
||||
@@ -2778,6 +2861,11 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this,
|
||||
policy_change_done(this, policy);
|
||||
return FAILED;
|
||||
}
|
||||
if (!add_label(hdr, sizeof(request), policy->label))
|
||||
{
|
||||
policy_change_done(this, policy);
|
||||
return FAILED;
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
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;
|
||||
enumerator_t *enumerator;
|
||||
bool found = FALSE, update = TRUE;
|
||||
char markstr[32] = "";
|
||||
char markstr[32] = "", labelstr[128] = "";
|
||||
uint32_t cur_priority = 0;
|
||||
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),
|
||||
.mark = id->mark.value & id->mark.mask,
|
||||
.if_id = id->if_id,
|
||||
.label = id->label ? id->label->clone(id->label) : NULL,
|
||||
.direction = id->dir,
|
||||
.reqid = data->sa->reqid,
|
||||
);
|
||||
format_mark(markstr, sizeof(markstr), id->mark);
|
||||
format_label(labelstr, sizeof(labelstr), id->label);
|
||||
|
||||
/* find the policy, which matches EXACTLY */
|
||||
this->mutex->lock(this->mutex);
|
||||
current = this->policies->get(this->policies, policy);
|
||||
if (current)
|
||||
{
|
||||
if (current->reqid && data->sa->reqid &&
|
||||
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 "
|
||||
{ /* use existing policy */
|
||||
DBG2(DBG_KNL, "policy %R === %R %N%s%s already exists, increasing "
|
||||
"refcount", id->src_ts, id->dst_ts, policy_dir_names, id->dir,
|
||||
markstr);
|
||||
markstr, labelstr);
|
||||
policy_entry_destroy(this, policy);
|
||||
policy = current;
|
||||
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
|
||||
* the currently installed one */
|
||||
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,
|
||||
id->dir, markstr, cur_priority, use_count);
|
||||
id->dir, markstr, labelstr, cur_priority, use_count);
|
||||
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)
|
||||
{
|
||||
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,
|
||||
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)
|
||||
{
|
||||
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,
|
||||
policy_dir_names, id->dir, markstr);
|
||||
policy_dir_names, id->dir, markstr, labelstr);
|
||||
return FAILED;
|
||||
}
|
||||
return SUCCESS;
|
||||
@@ -2955,13 +3040,14 @@ METHOD(kernel_ipsec_t, query_policy, status_t,
|
||||
struct xfrm_userpolicy_id *policy_id;
|
||||
struct xfrm_userpolicy_info *policy = NULL;
|
||||
size_t len;
|
||||
char markstr[32] = "";
|
||||
char markstr[32] = "", labelstr[128] = "";
|
||||
|
||||
memset(&request, 0, sizeof(request));
|
||||
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,
|
||||
policy_dir_names, id->dir, markstr);
|
||||
DBG2(DBG_KNL, "querying policy %R === %R %N%s%s", id->src_ts, id->dst_ts,
|
||||
policy_dir_names, id->dir, markstr, labelstr);
|
||||
|
||||
hdr = &request.hdr;
|
||||
hdr->nlmsg_flags = NLM_F_REQUEST;
|
||||
@@ -2980,6 +3066,10 @@ METHOD(kernel_ipsec_t, query_policy, status_t,
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
if (!add_label(hdr, sizeof(request), id->label))
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
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,
|
||||
.cfg = *data->sa,
|
||||
};
|
||||
char markstr[32] = "";
|
||||
char markstr[32] = "", labelstr[128] = "";
|
||||
int use_count;
|
||||
status_t status = SUCCESS;
|
||||
|
||||
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,
|
||||
policy_dir_names, id->dir, markstr);
|
||||
DBG2(DBG_KNL, "deleting policy %R === %R %N%s%s", id->src_ts, id->dst_ts,
|
||||
policy_dir_names, id->dir, markstr, labelstr);
|
||||
|
||||
/* create a policy */
|
||||
memset(&policy, 0, sizeof(policy_entry_t));
|
||||
policy.sel = ts2selector(id->src_ts, id->dst_ts, id->interface);
|
||||
policy.mark = id->mark.value & id->mark.mask;
|
||||
policy.if_id = id->if_id;
|
||||
policy.label = id->label;
|
||||
policy.direction = id->dir;
|
||||
|
||||
/* find the policy */
|
||||
@@ -3072,8 +3164,9 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
|
||||
current = this->policies->get(this->policies, &policy);
|
||||
if (!current)
|
||||
{
|
||||
DBG1(DBG_KNL, "deleting policy %R === %R %N%s failed, not found",
|
||||
id->src_ts, id->dst_ts, policy_dir_names, id->dir, markstr);
|
||||
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,
|
||||
labelstr);
|
||||
this->mutex->unlock(this->mutex);
|
||||
return NOT_FOUND;
|
||||
}
|
||||
@@ -3086,7 +3179,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
|
||||
current->waiting--;
|
||||
|
||||
/* 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)
|
||||
: data->manual_prio;
|
||||
priority = priority ?: auto_priority;
|
||||
@@ -3118,22 +3211,29 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
|
||||
if (!is_installed)
|
||||
{ /* no need to update as the policy was not installed for this SA */
|
||||
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,
|
||||
id->dir, markstr, cur_priority, use_count);
|
||||
id->dir, markstr, labelstr, cur_priority, use_count);
|
||||
return SUCCESS;
|
||||
}
|
||||
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,
|
||||
markstr, mapping->priority, use_count);
|
||||
markstr, labelstr, mapping->priority, use_count);
|
||||
|
||||
if (add_policy_internal(this, current, mapping, TRUE) != SUCCESS)
|
||||
{
|
||||
DBG1(DBG_KNL, "unable to update policy %R === %R %N%s",
|
||||
id->src_ts, id->dst_ts, policy_dir_names, id->dir, markstr);
|
||||
DBG1(DBG_KNL, "unable to update policy %R === %R %N%s%s",
|
||||
id->src_ts, id->dst_ts, policy_dir_names, id->dir, markstr,
|
||||
labelstr);
|
||||
return FAILED;
|
||||
}
|
||||
return SUCCESS;
|
||||
@@ -3160,6 +3260,11 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
|
||||
policy_change_done(this, current);
|
||||
return FAILED;
|
||||
}
|
||||
if (!add_label(hdr, sizeof(request), id->label))
|
||||
{
|
||||
policy_change_done(this, current);
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
if (current->route)
|
||||
{
|
||||
@@ -3170,16 +3275,16 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
|
||||
route->pass) != SUCCESS)
|
||||
{
|
||||
DBG1(DBG_KNL, "error uninstalling route installed with policy "
|
||||
"%R === %R %N%s", id->src_ts, id->dst_ts, policy_dir_names,
|
||||
id->dir, markstr);
|
||||
"%R === %R %N%s%s", id->src_ts, id->dst_ts, policy_dir_names,
|
||||
id->dir, markstr, labelstr);
|
||||
}
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
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,
|
||||
id->dst_ts, policy_dir_names, id->dir, markstr);
|
||||
DBG1(DBG_KNL, "unable to delete policy %R === %R %N%s%s", id->src_ts,
|
||||
id->dst_ts, policy_dir_names, id->dir, markstr, labelstr);
|
||||
status = FAILED;
|
||||
}
|
||||
|
||||
|
||||
@@ -1318,8 +1318,8 @@ static void process_acquire(private_kernel_pfkey_ipsec_t *this,
|
||||
struct sadb_msg* msg)
|
||||
{
|
||||
pfkey_msg_t response;
|
||||
kernel_acquire_data_t data = {};
|
||||
uint32_t index, reqid = 0;
|
||||
traffic_selector_t *src_ts, *dst_ts;
|
||||
policy_entry_t *policy;
|
||||
policy_sa_t *sa;
|
||||
|
||||
@@ -1363,10 +1363,16 @@ static void process_acquire(private_kernel_pfkey_ipsec_t *this,
|
||||
this->mutex->unlock(this->mutex);
|
||||
}
|
||||
|
||||
src_ts = sadb_address2ts(response.src);
|
||||
dst_ts = sadb_address2ts(response.dst);
|
||||
if (reqid)
|
||||
{
|
||||
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;
|
||||
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
|
||||
/* 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
|
||||
|
||||
@@ -820,6 +820,8 @@ static bool install_sps(private_kernel_wfp_ipsec_t *this,
|
||||
case TS_IPV6_ADDR_RANGE:
|
||||
has_v6 = TRUE;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 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,
|
||||
traffic_selector_t *src, traffic_selector_t *dst)
|
||||
{
|
||||
kernel_acquire_data_t data = {};
|
||||
uint32_t reqid = 0;
|
||||
trap_t *trap, key = {
|
||||
.filter_id = filter_id,
|
||||
@@ -1649,9 +1652,13 @@ static void acquire(private_kernel_wfp_ipsec_t *this, UINT64 filter_id,
|
||||
|
||||
if (reqid)
|
||||
{
|
||||
src = src ? src->clone(src) : NULL;
|
||||
dst = dst ? dst->clone(dst) : NULL;
|
||||
charon->kernel->acquire(charon->kernel, reqid, src, dst);
|
||||
data.src = src ? src->clone(src) : NULL;
|
||||
data.dst = dst ? dst->clone(dst) : NULL;
|
||||
|
||||
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 */
|
||||
sa_id = entry->sa_id;
|
||||
ports.localUdpEncapPort = entry->local->get_port(entry->local);
|
||||
ports.remoteUdpEncapPort = entry->remote->get_port(entry->remote);
|
||||
ports.localUdpEncapPort = data->new_dst->get_port(data->new_dst);
|
||||
ports.remoteUdpEncapPort = data->new_src->get_port(data->new_src);
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
@@ -2273,6 +2280,10 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
|
||||
key.dst = entry->osa.dst;
|
||||
this->osas->remove(this->osas, &key);
|
||||
|
||||
if (data->new_reqid)
|
||||
{
|
||||
entry->reqid = data->new_reqid;
|
||||
}
|
||||
entry->local->destroy(entry->local);
|
||||
entry->remote->destroy(entry->remote);
|
||||
entry->local = data->new_dst->clone(data->new_dst);
|
||||
|
||||
@@ -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_ @}*/
|
||||
@@ -1042,9 +1042,9 @@ static action_t map_action(int starter_action)
|
||||
switch (starter_action)
|
||||
{
|
||||
case 2: /* =hold */
|
||||
return ACTION_ROUTE;
|
||||
return ACTION_TRAP;
|
||||
case 3: /* =restart */
|
||||
return ACTION_RESTART;
|
||||
return ACTION_START;
|
||||
default:
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
@@ -277,7 +277,7 @@ Terminates an SA while streaming _control-log_ events.
|
||||
{
|
||||
child = <terminate a CHILD_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>
|
||||
force = <terminate IKE_SA without waiting for proper DELETE, if timeout
|
||||
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>
|
||||
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>
|
||||
reauth = <reauthenticate instead of rekey an IKEv2 SA>
|
||||
} => {
|
||||
@@ -361,6 +361,8 @@ events.
|
||||
noblock = <use non-blocking mode if key is set>
|
||||
ike = <filter listed IKE_SAs by its name>
|
||||
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
|
||||
}
|
||||
@@ -817,6 +819,7 @@ command.
|
||||
mark-mask-out = <hex encoded outbound Netfilter mark mask>
|
||||
if-id-in = <hex encoded inbound 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-keysize = <ESP encryption key size, if applicable>
|
||||
integ-alg = <ESP or AH integrity algorithm name, if any>
|
||||
@@ -854,6 +857,7 @@ _list-policies_ command.
|
||||
child = <CHILD_SA configuration name>
|
||||
ike = <IKE_SA configuration name or namespace, if available>
|
||||
mode = <policy mode, tunnel|transport|pass|drop>
|
||||
label = <hex encoded security label>
|
||||
local-ts = [
|
||||
<list of local traffic selectors>
|
||||
]
|
||||
@@ -903,6 +907,7 @@ _list-conns_ command.
|
||||
children = {
|
||||
<CHILD_SA config name>* = {
|
||||
mode = <IPsec mode>
|
||||
label = <hex encoded security label>
|
||||
rekey_time = <CHILD_SA rekeying interval in seconds>
|
||||
rekey_bytes = <CHILD_SA rekeying interval in bytes>
|
||||
rekey_packets = <CHILD_SA rekeying interval in packets>
|
||||
|
||||
@@ -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);
|
||||
DBG2(DBG_CFG, " set_mark_out = %u/%u",
|
||||
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, " proposals = %#P", data->proposals);
|
||||
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));
|
||||
data->remote_ts->destroy_offset(data->remote_ts,
|
||||
offsetof(traffic_selector_t, destroy));
|
||||
DESTROY_IF(data->cfg.label);
|
||||
free(data->cfg.updown);
|
||||
free(data->cfg.interface);
|
||||
}
|
||||
@@ -1004,13 +1008,14 @@ CALLBACK(parse_action, bool,
|
||||
action_t *out, chunk_t v)
|
||||
{
|
||||
enum_map_t map[] = {
|
||||
{ "start", ACTION_RESTART },
|
||||
{ "restart", ACTION_RESTART },
|
||||
{ "route", ACTION_ROUTE },
|
||||
{ "trap", ACTION_ROUTE },
|
||||
{ "start", ACTION_START },
|
||||
{ "restart", ACTION_START },
|
||||
{ "route", ACTION_TRAP },
|
||||
{ "trap", ACTION_TRAP },
|
||||
{ "none", ACTION_NONE },
|
||||
{ "clear", ACTION_NONE },
|
||||
};
|
||||
char buf[BUF_LEN];
|
||||
int d;
|
||||
|
||||
if (parse_map(map, countof(map), &d, v))
|
||||
@@ -1018,6 +1023,14 @@ CALLBACK(parse_action, bool,
|
||||
*out = d;
|
||||
return TRUE;
|
||||
}
|
||||
if (!vici_stringify(v, buf, sizeof(buf)))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (enum_flags_from_string(action_names, buf, out))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -1261,6 +1274,38 @@ CALLBACK(parse_if_id, bool,
|
||||
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
|
||||
*/
|
||||
@@ -1760,6 +1805,8 @@ CALLBACK(child_kv, bool,
|
||||
{ "copy_dscp", parse_copy_dscp, &child->cfg.copy_dscp },
|
||||
{ "if_id_in", parse_if_id, &child->cfg.if_id_in },
|
||||
{ "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,
|
||||
@@ -2143,30 +2190,33 @@ CALLBACK(peer_sn, bool,
|
||||
static void run_start_action(private_vici_config_t *this, peer_cfg_t *peer_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, "initiating '%s'", child_cfg->get_name(child_cfg));
|
||||
charon->controller->initiate(charon->controller,
|
||||
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);
|
||||
/* 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),
|
||||
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;
|
||||
uint32_t id = 0, others;
|
||||
array_t *ids = NULL, *ikeids = NULL;
|
||||
action_t action;
|
||||
char *name;
|
||||
|
||||
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:
|
||||
enumerator = charon->controller->create_ike_sa_enumerator(
|
||||
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);
|
||||
/* 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);
|
||||
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);
|
||||
while (children->enumerate(children, &child_sa))
|
||||
{
|
||||
if (child_sa->get_state(child_sa) != CHILD_DELETING &&
|
||||
child_sa->get_state(child_sa) != CHILD_DELETED)
|
||||
if (streq(name, child_sa->get_name(child_sa)))
|
||||
{
|
||||
if (streq(name, child_sa->get_name(child_sa)))
|
||||
{
|
||||
id = child_sa->get_unique_id(child_sa);
|
||||
}
|
||||
else
|
||||
{
|
||||
others++;
|
||||
}
|
||||
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 (!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);
|
||||
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))
|
||||
if (array_count(ids))
|
||||
{
|
||||
while (array_remove(ids, ARRAY_HEAD, &id))
|
||||
{
|
||||
while (array_remove(ids, ARRAY_HEAD, &id))
|
||||
{
|
||||
DBG1(DBG_CFG, "closing '%s' #%u", name, id);
|
||||
charon->controller->terminate_child(charon->controller,
|
||||
id, NULL, NULL, 0);
|
||||
}
|
||||
array_destroy(ids);
|
||||
DBG1(DBG_CFG, "closing '%s' #%u", name, id);
|
||||
charon->controller->terminate_child(charon->controller,
|
||||
id, NULL, NULL, 0);
|
||||
}
|
||||
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,
|
||||
FALSE, NULL, NULL, 0);
|
||||
}
|
||||
array_destroy(ikeids);
|
||||
DBG1(DBG_CFG, "closing IKE_SA #%u", id);
|
||||
charon->controller->terminate_ike(charon->controller, id,
|
||||
FALSE, NULL, NULL, 0);
|
||||
}
|
||||
break;
|
||||
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;
|
||||
array_destroy(ikeids);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -275,6 +298,7 @@ static void list_child(private_vici_query_t *this, vici_builder_t *b,
|
||||
state = child->get_state(child);
|
||||
b->add_kv(b, "state", "%N", child_sa_state_names, state);
|
||||
list_mode(b, child, NULL);
|
||||
list_label(b, child, NULL);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
@@ -505,15 +529,16 @@ CALLBACK(list_sas, vici_message_t*,
|
||||
ike_sa_t *ike_sa;
|
||||
child_sa_t *child_sa;
|
||||
time_t now;
|
||||
char *ike;
|
||||
u_int ike_id;
|
||||
char *ike, *child;
|
||||
u_int ike_id, child_id;
|
||||
bool bl;
|
||||
char buf[BUF_LEN];
|
||||
|
||||
|
||||
bl = request->get_str(request, NULL, "noblock") == NULL;
|
||||
ike = request->get_str(request, NULL, "ike");
|
||||
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);
|
||||
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);
|
||||
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),
|
||||
child_sa->get_unique_id(child_sa));
|
||||
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);
|
||||
|
||||
list_mode(b, child, NULL);
|
||||
list_label(b, child, NULL);
|
||||
|
||||
b->begin_list(b, "local-ts");
|
||||
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_label(b, NULL, cfg);
|
||||
|
||||
b->begin_list(b, "local-ts");
|
||||
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));
|
||||
|
||||
list_mode(b, NULL, child_cfg);
|
||||
list_label(b, NULL, child_cfg);
|
||||
|
||||
lft = child_cfg->get_lifetime(child_cfg, FALSE);
|
||||
b->add_kv(b, "rekey_time", "%"PRIu64, lft->time.rekey);
|
||||
|
||||
@@ -30,34 +30,29 @@ struct private_acquire_job_t {
|
||||
acquire_job_t public;
|
||||
|
||||
/**
|
||||
* reqid of the child to rekey
|
||||
* reqid of the triggered policy
|
||||
*/
|
||||
uint32_t reqid;
|
||||
|
||||
/**
|
||||
* acquired source traffic selector
|
||||
* Data from the acquire
|
||||
*/
|
||||
traffic_selector_t *src_ts;
|
||||
|
||||
/**
|
||||
* acquired destination traffic selector
|
||||
*/
|
||||
traffic_selector_t *dst_ts;
|
||||
kernel_acquire_data_t data;
|
||||
};
|
||||
|
||||
METHOD(job_t, destroy, void,
|
||||
private_acquire_job_t *this)
|
||||
{
|
||||
DESTROY_IF(this->src_ts);
|
||||
DESTROY_IF(this->dst_ts);
|
||||
DESTROY_IF(this->data.src);
|
||||
DESTROY_IF(this->data.dst);
|
||||
DESTROY_IF(this->data.label);
|
||||
free(this);
|
||||
}
|
||||
|
||||
METHOD(job_t, execute, job_requeue_t,
|
||||
private_acquire_job_t *this)
|
||||
{
|
||||
charon->traps->acquire(charon->traps, this->reqid,
|
||||
this->src_ts, this->dst_ts);
|
||||
charon->traps->acquire(charon->traps, this->reqid, &this->data);
|
||||
return JOB_REQUEUE_NONE;
|
||||
}
|
||||
|
||||
@@ -70,9 +65,7 @@ METHOD(job_t, get_priority, job_priority_t,
|
||||
/*
|
||||
* Described in header
|
||||
*/
|
||||
acquire_job_t *acquire_job_create(uint32_t reqid,
|
||||
traffic_selector_t *src_ts,
|
||||
traffic_selector_t *dst_ts)
|
||||
acquire_job_t *acquire_job_create(uint32_t reqid, kernel_acquire_data_t *data)
|
||||
{
|
||||
private_acquire_job_t *this;
|
||||
|
||||
@@ -85,10 +78,21 @@ acquire_job_t *acquire_job_create(uint32_t reqid,
|
||||
},
|
||||
},
|
||||
.reqid = reqid,
|
||||
.src_ts = src_ts,
|
||||
.dst_ts = dst_ts,
|
||||
.data = *data,
|
||||
);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
typedef struct acquire_job_t acquire_job_t;
|
||||
|
||||
#include <library.h>
|
||||
#include <selectors/traffic_selector.h>
|
||||
#include <kernel/kernel_interface.h>
|
||||
#include <processing/jobs/job.h>
|
||||
|
||||
/**
|
||||
@@ -42,13 +42,10 @@ struct acquire_job_t {
|
||||
/**
|
||||
* Creates a job of type ACQUIRE.
|
||||
*
|
||||
* @param reqid reqid of the trapped CHILD_SA to acquire
|
||||
* @param src_ts source traffic selector
|
||||
* @param dst_ts destination traffic selector
|
||||
* @param reqid reqid of the triggered policy
|
||||
* @param data data from the acquire
|
||||
* @return acquire_job_t object
|
||||
*/
|
||||
acquire_job_t *acquire_job_create(uint32_t reqid,
|
||||
traffic_selector_t *src_ts,
|
||||
traffic_selector_t *dst_ts);
|
||||
acquire_job_t *acquire_job_create(uint32_t reqid, kernel_acquire_data_t *data);
|
||||
|
||||
#endif /** REKEY_CHILD_SA_JOB_H_ @}*/
|
||||
|
||||
@@ -253,7 +253,7 @@ METHOD(job_t, execute, job_requeue_t,
|
||||
task->migrate(task, ike_sa);
|
||||
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, ike_sa);
|
||||
|
||||
@@ -55,7 +55,7 @@ METHOD(job_t, execute, job_requeue_t,
|
||||
this->ike_sa_id);
|
||||
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,
|
||||
ike_sa);
|
||||
|
||||
@@ -42,8 +42,7 @@ METHOD(job_t, execute, job_requeue_t,
|
||||
enumerator_t *enumerator, *children;
|
||||
peer_cfg_t *peer_cfg;
|
||||
child_cfg_t *child_cfg;
|
||||
ipsec_mode_t mode;
|
||||
char *name;
|
||||
action_t action;
|
||||
|
||||
enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends,
|
||||
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);
|
||||
while (children->enumerate(children, &child_cfg))
|
||||
{
|
||||
name = child_cfg->get_name(child_cfg);
|
||||
|
||||
switch (child_cfg->get_start_action(child_cfg))
|
||||
action = child_cfg->get_start_action(child_cfg);
|
||||
if (action == ACTION_NONE)
|
||||
{
|
||||
case ACTION_RESTART:
|
||||
DBG1(DBG_JOB, "start action: initiate '%s'", name);
|
||||
charon->controller->initiate(charon->controller,
|
||||
peer_cfg->get_ref(peer_cfg),
|
||||
child_cfg->get_ref(child_cfg),
|
||||
NULL, NULL, 0, FALSE);
|
||||
break;
|
||||
case ACTION_ROUTE:
|
||||
DBG1(DBG_JOB, "start action: route '%s'", name);
|
||||
mode = child_cfg->get_mode(child_cfg);
|
||||
if (mode == MODE_PASS || mode == MODE_DROP)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DBG1(DBG_JOB, "start action: %N '%s'", action_names, action,
|
||||
child_cfg->get_name(child_cfg));
|
||||
|
||||
if (action & ACTION_TRAP)
|
||||
{
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no need to check for ACTION_START */
|
||||
continue;
|
||||
default:
|
||||
charon->traps->install(charon->traps, peer_cfg,
|
||||
child_cfg);
|
||||
}
|
||||
break;
|
||||
case ACTION_NONE:
|
||||
break;
|
||||
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);
|
||||
|
||||
+266
-65
@@ -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,
|
||||
"NONE",
|
||||
"REGISTERED",
|
||||
"SA",
|
||||
"POLICIES",
|
||||
@@ -178,6 +179,11 @@ struct private_child_sa_t {
|
||||
*/
|
||||
mark_t mark_out;
|
||||
|
||||
/**
|
||||
* Security label
|
||||
*/
|
||||
sec_label_t *label;
|
||||
|
||||
/**
|
||||
* 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){};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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*,
|
||||
private_child_sa_t *this)
|
||||
{
|
||||
@@ -512,8 +558,12 @@ METHOD(enumerator_t, policy_destroy, void,
|
||||
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;
|
||||
|
||||
@@ -523,15 +573,21 @@ METHOD(child_sa_t, create_policy_enumerator, enumerator_t*,
|
||||
.venumerate = _policy_enumerate,
|
||||
.destroy = _policy_destroy,
|
||||
},
|
||||
.mine = array_create_enumerator(this->my_ts),
|
||||
.other = array_create_enumerator(this->other_ts),
|
||||
.array = this->other_ts,
|
||||
.mine = array_create_enumerator(my_ts),
|
||||
.other = array_create_enumerator(other_ts),
|
||||
.array = other_ts,
|
||||
.ts = NULL,
|
||||
);
|
||||
|
||||
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
|
||||
* 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,
|
||||
.mark = this->mark_in,
|
||||
.if_id = this->if_id_in,
|
||||
.label = label_for(this, LABEL_USE_POLICY),
|
||||
};
|
||||
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,
|
||||
.if_id = this->if_id_out,
|
||||
.interface = this->config->get_interface(this->config),
|
||||
.label = label_for(this, LABEL_USE_POLICY),
|
||||
};
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
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,
|
||||
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)
|
||||
{
|
||||
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_ecn = !this->config->has_option(this->config, OPT_NO_COPY_ECN),
|
||||
.copy_dscp = this->config->get_copy_dscp(this->config),
|
||||
.label = label_for(this, LABEL_USE_SA),
|
||||
.initiator = initiator,
|
||||
.inbound = inbound,
|
||||
.update = update,
|
||||
@@ -1016,6 +1082,7 @@ static status_t install_policies_inbound(private_child_sa_t *this,
|
||||
.dst_ts = my_ts,
|
||||
.mark = this->mark_in,
|
||||
.if_id = this->if_id_in,
|
||||
.label = label_for(this, LABEL_USE_POLICY),
|
||||
};
|
||||
kernel_ipsec_manage_policy_t in_policy = {
|
||||
.type = type,
|
||||
@@ -1052,6 +1119,7 @@ static status_t install_policies_outbound(private_child_sa_t *this,
|
||||
.mark = this->mark_out,
|
||||
.if_id = this->if_id_out,
|
||||
.interface = this->config->get_interface(this->config),
|
||||
.label = label_for(this, LABEL_USE_POLICY),
|
||||
};
|
||||
kernel_ipsec_manage_policy_t out_policy = {
|
||||
.type = type,
|
||||
@@ -1061,6 +1129,7 @@ static status_t install_policies_outbound(private_child_sa_t *this,
|
||||
.dst = other_addr,
|
||||
.sa = other_sa,
|
||||
};
|
||||
uint32_t reqid = other_sa->reqid;
|
||||
status_t status = SUCCESS;
|
||||
|
||||
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,
|
||||
&out_policy);
|
||||
/* reset the reqid for any other further policies */
|
||||
other_sa->reqid = this->reqid;
|
||||
other_sa->reqid = reqid;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -1125,6 +1194,7 @@ static void del_policies_inbound(private_child_sa_t *this,
|
||||
.dst_ts = my_ts,
|
||||
.mark = this->mark_in,
|
||||
.if_id = this->if_id_in,
|
||||
.label = label_for(this, LABEL_USE_POLICY),
|
||||
};
|
||||
kernel_ipsec_manage_policy_t in_policy = {
|
||||
.type = type,
|
||||
@@ -1160,6 +1230,7 @@ static void del_policies_outbound(private_child_sa_t *this,
|
||||
.mark = this->mark_out,
|
||||
.if_id = this->if_id_out,
|
||||
.interface = this->config->get_interface(this->config),
|
||||
.label = label_for(this, LABEL_USE_POLICY),
|
||||
};
|
||||
kernel_ipsec_manage_policy_t out_policy = {
|
||||
.type = type,
|
||||
@@ -1169,6 +1240,7 @@ static void del_policies_outbound(private_child_sa_t *this,
|
||||
.dst = other_addr,
|
||||
.sa = other_sa,
|
||||
};
|
||||
uint32_t reqid = other_sa->reqid;
|
||||
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
private_child_sa_t *this)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
linked_list_t *my_ts_list, *other_ts_list;
|
||||
traffic_selector_t *my_ts, *other_ts;
|
||||
status_t status = SUCCESS;
|
||||
bool install_outbound = FALSE;
|
||||
|
||||
if (!this->reqid_allocated && !this->static_reqid)
|
||||
{
|
||||
my_ts_list = linked_list_create_from_enumerator(
|
||||
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);
|
||||
status = alloc_reqid(this, this->my_ts, this->other_ts, &this->reqid);
|
||||
if (status != SUCCESS)
|
||||
{
|
||||
return status;
|
||||
@@ -1311,15 +1394,36 @@ METHOD(child_sa_t, install_policies, status_t,
|
||||
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,
|
||||
private_child_sa_t *this, chunk_t encr, chunk_t integ, uint32_t spi,
|
||||
uint16_t cpi, bool tfcv3)
|
||||
{
|
||||
status_t status;
|
||||
|
||||
/* if the kernel supports installing SPIs with policies we install the
|
||||
* SA immediately as it will only be used once we update the policies */
|
||||
if (charon->kernel->get_features(charon->kernel) & KERNEL_POLICY_SPI)
|
||||
if (install_outbound_immediately(this))
|
||||
{
|
||||
status = install_internal(this, encr, integ, spi, cpi, FALSE, FALSE,
|
||||
tfcv3);
|
||||
@@ -1472,7 +1576,7 @@ CALLBACK(reinstall_vip, void,
|
||||
* 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,
|
||||
bool encap)
|
||||
bool encap, uint32_t reqid)
|
||||
{
|
||||
/* update our (initiator) SA */
|
||||
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,
|
||||
.encap = this->encap,
|
||||
.new_encap = encap,
|
||||
.new_reqid = reqid,
|
||||
};
|
||||
if (charon->kernel->update_sa(charon->kernel, &id,
|
||||
&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,
|
||||
.encap = this->encap,
|
||||
.new_encap = encap,
|
||||
.new_reqid = reqid,
|
||||
};
|
||||
if (charon->kernel->update_sa(charon->kernel, &id,
|
||||
&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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
private_child_sa_t *this, host_t *me, host_t *other, linked_list_t *vips,
|
||||
bool encap)
|
||||
@@ -1554,17 +1684,31 @@ METHOD(child_sa_t, update, status_t,
|
||||
ipsec_sa_cfg_t my_sa, other_sa;
|
||||
enumerator_t *enumerator;
|
||||
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;
|
||||
bool outbound;
|
||||
|
||||
prepare_sa_cfg(this, &my_sa, &other_sa);
|
||||
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);
|
||||
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. */
|
||||
if (outbound)
|
||||
{
|
||||
@@ -1575,77 +1719,130 @@ METHOD(child_sa_t, update, status_t,
|
||||
/* remove old policies */
|
||||
del_policies_internal(this, this->my_addr, this->other_addr,
|
||||
my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC,
|
||||
POLICY_PRIORITY_DEFAULT, manual_prio, outbound);
|
||||
priority, manual_prio, outbound);
|
||||
}
|
||||
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 */
|
||||
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);
|
||||
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 */
|
||||
if (state == NOT_SUPPORTED)
|
||||
{
|
||||
install_policies_internal(this, this->my_addr, this->other_addr,
|
||||
my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC,
|
||||
POLICY_PRIORITY_DEFAULT, 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);
|
||||
priority, manual_prio, outbound);
|
||||
}
|
||||
/* remove the drop policy */
|
||||
if (outbound)
|
||||
{
|
||||
del_policies_outbound(this, this->my_addr, this->other_addr,
|
||||
old_my_ts ?: my_ts, old_other_ts ?: other_ts,
|
||||
&my_sa, &other_sa, POLICY_DROP,
|
||||
my_ts, other_ts, &my_sa, &other_sa, POLICY_DROP,
|
||||
POLICY_PRIORITY_DEFAULT, manual_prio);
|
||||
}
|
||||
|
||||
DESTROY_IF(old_my_ts);
|
||||
DESTROY_IF(old_other_ts);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
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);
|
||||
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)
|
||||
{
|
||||
if (update_sas(this, me, other, encap) == NOT_SUPPORTED)
|
||||
if (update_sas(this, me, other, encap, 0) == NOT_SUPPORTED)
|
||||
{
|
||||
set_state(this, old);
|
||||
return NOT_SUPPORTED;
|
||||
@@ -1743,7 +1940,8 @@ METHOD(child_sa_t, destroy, void,
|
||||
{
|
||||
if (charon->kernel->release_reqid(charon->kernel,
|
||||
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);
|
||||
}
|
||||
@@ -1754,6 +1952,7 @@ METHOD(child_sa_t, destroy, void,
|
||||
this->my_addr->destroy(this->my_addr);
|
||||
this->other_addr->destroy(this->other_addr);
|
||||
DESTROY_IF(this->proposal);
|
||||
DESTROY_IF(this->label);
|
||||
this->config->destroy(this->config);
|
||||
chunk_clear(&this->encr_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_mark = _get_mark,
|
||||
.get_if_id = _get_if_id,
|
||||
.get_label = _get_label,
|
||||
.has_encap = _has_encap,
|
||||
.get_ipcomp = _get_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),
|
||||
.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,
|
||||
.label = data->label ? data->label->clone(data->label) : NULL,
|
||||
.install_time = time_monotonic(NULL),
|
||||
.policies_fwd_out = config->has_option(config, OPT_FWD_OUT_POLICIES),
|
||||
);
|
||||
|
||||
@@ -369,6 +369,15 @@ struct child_sa_t {
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@@ -533,6 +542,8 @@ struct child_sa_create_t {
|
||||
/** Optional default outbound interface ID, if neither if_id_out, nor config
|
||||
* sets one */
|
||||
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) */
|
||||
bool encap;
|
||||
};
|
||||
@@ -546,7 +557,7 @@ struct child_sa_create_t {
|
||||
* @param data data for this CHILD_SA
|
||||
* @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);
|
||||
|
||||
#endif /** CHILD_SA_H_ @}*/
|
||||
|
||||
+68
-21
@@ -1531,8 +1531,7 @@ static void resolve_hosts(private_ike_sa_t *this)
|
||||
}
|
||||
|
||||
METHOD(ike_sa_t, initiate, status_t,
|
||||
private_ike_sa_t *this, child_cfg_t *child_cfg, uint32_t reqid,
|
||||
traffic_selector_t *tsi, traffic_selector_t *tsr)
|
||||
private_ike_sa_t *this, child_cfg_t *child_cfg, child_init_args_t *args)
|
||||
{
|
||||
bool defer_initiate = FALSE;
|
||||
|
||||
@@ -1587,8 +1586,7 @@ METHOD(ike_sa_t, initiate, status_t,
|
||||
if (child_cfg)
|
||||
{
|
||||
/* normal IKE_SA with CHILD_SA */
|
||||
this->task_manager->queue_child(this->task_manager, child_cfg, reqid,
|
||||
tsi, tsr);
|
||||
this->task_manager->queue_child(this->task_manager, child_cfg, args);
|
||||
#ifdef ME
|
||||
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)
|
||||
{
|
||||
this->retry_initiate_queued = FALSE;
|
||||
return initiate(this, NULL, 0, NULL, NULL);
|
||||
return initiate(this, NULL, NULL);
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -2062,7 +2060,7 @@ static status_t reestablish_children(private_ike_sa_t *this, ike_sa_t *new,
|
||||
}
|
||||
if (force)
|
||||
{
|
||||
action = ACTION_RESTART;
|
||||
action = ACTION_START;
|
||||
}
|
||||
else
|
||||
{ /* 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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
DBG1(DBG_IKE, "restarting CHILD_SA %s",
|
||||
child_cfg->get_name(child_cfg));
|
||||
other->task_manager->queue_child(other->task_manager,
|
||||
child_cfg->get_ref(child_cfg),
|
||||
child_sa->get_reqid(child_sa),
|
||||
NULL, NULL);
|
||||
&args);
|
||||
}
|
||||
}
|
||||
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 */
|
||||
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,
|
||||
@@ -2150,17 +2151,14 @@ METHOD(ike_sa_t, reestablish, status_t,
|
||||
{
|
||||
action = child_sa->get_dpd_action(child_sa);
|
||||
}
|
||||
switch (action)
|
||||
if (action & ACTION_TRAP)
|
||||
{
|
||||
case ACTION_RESTART:
|
||||
restart = TRUE;
|
||||
break;
|
||||
case ACTION_ROUTE:
|
||||
charon->traps->install(charon->traps, this->peer_cfg,
|
||||
child_sa->get_config(child_sa));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
charon->traps->install(charon->traps, this->peer_cfg,
|
||||
child_sa->get_config(child_sa));
|
||||
}
|
||||
if (action & ACTION_START)
|
||||
{
|
||||
restart = TRUE;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
@@ -2224,7 +2222,7 @@ METHOD(ike_sa_t, reestablish, status_t,
|
||||
#ifdef ME
|
||||
if (this->peer_cfg->is_mediation(this->peer_cfg))
|
||||
{
|
||||
status = new->initiate(new, NULL, 0, NULL, NULL);
|
||||
status = new->initiate(new, NULL, NULL);
|
||||
}
|
||||
else
|
||||
#endif /* ME */
|
||||
@@ -3264,3 +3262,52 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
|
||||
}
|
||||
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,6 +29,7 @@ typedef enum ike_condition_t ike_condition_t;
|
||||
typedef enum ike_sa_state_t ike_sa_state_t;
|
||||
typedef enum statistic_t statistic_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;
|
||||
|
||||
#include <library.h>
|
||||
@@ -369,6 +370,20 @@ enum ike_sa_state_t {
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@@ -787,16 +802,13 @@ struct ike_sa_t {
|
||||
* to the CHILD_SA.
|
||||
*
|
||||
* @param child_cfg child config to create CHILD from
|
||||
* @param reqid reqid to use for CHILD_SA, 0 assign uniquely
|
||||
* @param tsi source of triggering packet
|
||||
* @param tsr destination of triggering packet.
|
||||
* @param args optional arguments for the CHILD initiation
|
||||
* @return
|
||||
* - SUCCESS if initialization started
|
||||
* - DESTROY_ME if initialization failed
|
||||
*/
|
||||
status_t (*initiate) (ike_sa_t *this, child_cfg_t *child_cfg,
|
||||
uint32_t reqid, traffic_selector_t *tsi,
|
||||
traffic_selector_t *tsr);
|
||||
child_init_args_t *args);
|
||||
|
||||
/**
|
||||
* 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 initiator TRUE to create this IKE_SA as initiator
|
||||
* @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_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
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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_ @}*/
|
||||
|
||||
@@ -1685,7 +1685,7 @@ METHOD(task_manager_t, queue_ike_reauth, void,
|
||||
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);
|
||||
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,
|
||||
private_task_manager_t *this, child_cfg_t *cfg, uint32_t reqid,
|
||||
traffic_selector_t *tsi, traffic_selector_t *tsr)
|
||||
private_task_manager_t *this, child_cfg_t *cfg, child_init_args_t *args)
|
||||
{
|
||||
quick_mode_t *task;
|
||||
|
||||
task = quick_mode_create(this->ike_sa, cfg, tsi, tsr);
|
||||
task->use_reqid(task, reqid);
|
||||
|
||||
if (args)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -149,23 +149,24 @@ static status_t delete_child(private_quick_delete_t *this,
|
||||
|
||||
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->get_ref(child_cfg);
|
||||
|
||||
switch (child_sa->get_close_action(child_sa))
|
||||
if (action & ACTION_TRAP)
|
||||
{
|
||||
case ACTION_RESTART:
|
||||
child_cfg->get_ref(child_cfg);
|
||||
status = this->ike_sa->initiate(this->ike_sa, child_cfg,
|
||||
child_sa->get_reqid(child_sa), NULL, NULL);
|
||||
break;
|
||||
case ACTION_ROUTE:
|
||||
charon->traps->install(charon->traps,
|
||||
this->ike_sa->get_peer_cfg(this->ike_sa),
|
||||
child_cfg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
charon->traps->install(charon->traps,
|
||||
this->ike_sa->get_peer_cfg(this->ike_sa),
|
||||
child_cfg);
|
||||
}
|
||||
if (action & ACTION_START)
|
||||
{
|
||||
child_cfg->get_ref(child_cfg);
|
||||
status = this->ike_sa->initiate(this->ike_sa, child_cfg, &args);
|
||||
}
|
||||
child_cfg->destroy(child_cfg);
|
||||
}
|
||||
|
||||
@@ -1096,7 +1096,7 @@ METHOD(task_t, process_r, status_t,
|
||||
hostsi = get_dynamic_hosts(this->ike_sa, FALSE);
|
||||
hostsr = get_dynamic_hosts(this->ike_sa, TRUE);
|
||||
this->config = peer_cfg->select_child_cfg(peer_cfg, tsr, tsi,
|
||||
hostsr, hostsi);
|
||||
hostsr, hostsi, NULL, NULL);
|
||||
hostsi->destroy(hostsi);
|
||||
hostsr->destroy(hostsr);
|
||||
if (this->config)
|
||||
@@ -1475,7 +1475,6 @@ METHOD(task_t, migrate, void,
|
||||
this->dh = NULL;
|
||||
this->spi_i = 0;
|
||||
this->spi_r = 0;
|
||||
this->child = (child_sa_create_t){};
|
||||
|
||||
if (!this->initiator)
|
||||
{
|
||||
|
||||
@@ -1983,7 +1983,7 @@ static void trigger_mbb_reauth(private_task_manager_t *this)
|
||||
/* suspend online revocation checking until the SA is established */
|
||||
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_reauth_complete_create(new,
|
||||
@@ -2102,15 +2102,19 @@ METHOD(task_manager_t, queue_dpd, void,
|
||||
}
|
||||
|
||||
METHOD(task_manager_t, queue_child, void,
|
||||
private_task_manager_t *this, child_cfg_t *cfg, uint32_t reqid,
|
||||
traffic_selector_t *tsi, traffic_selector_t *tsr)
|
||||
private_task_manager_t *this, child_cfg_t *cfg, child_init_args_t *args)
|
||||
{
|
||||
child_create_t *task;
|
||||
|
||||
task = child_create_create(this->ike_sa, cfg, FALSE, tsi, tsr);
|
||||
if (reqid)
|
||||
if (args)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -84,15 +84,25 @@ struct private_child_create_t {
|
||||
proposal_t *proposal;
|
||||
|
||||
/**
|
||||
* traffic selectors for initiators side
|
||||
* traffic selectors for initiator side
|
||||
*/
|
||||
linked_list_t *tsi;
|
||||
|
||||
/**
|
||||
* traffic selectors for responders side
|
||||
* traffic selectors for responder side
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@@ -210,6 +220,7 @@ static void schedule_delayed_retry(private_child_create_t *this)
|
||||
task->use_reqid(task, this->child.reqid);
|
||||
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_label(task, this->child.label);
|
||||
|
||||
DBG1(DBG_IKE, "creating CHILD_SA failed, trying again in %d seconds",
|
||||
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
|
||||
*/
|
||||
@@ -468,7 +423,7 @@ static linked_list_t* narrow_ts(private_child_create_t *this, bool local,
|
||||
ike_condition_t cond;
|
||||
|
||||
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 &&
|
||||
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 */
|
||||
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);
|
||||
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);
|
||||
|
||||
/* 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:
|
||||
ts_payload = (ts_payload_t*)payload;
|
||||
this->tsi = ts_payload->get_traffic_selectors(ts_payload);
|
||||
this->labels_i = ts_payload->get_sec_labels(ts_payload);
|
||||
break;
|
||||
case PLV2_TS_RESPONDER:
|
||||
ts_payload = (ts_payload_t*)payload;
|
||||
this->tsr = ts_payload->get_traffic_selectors(ts_payload);
|
||||
this->labels_r = ts_payload->get_sec_labels(ts_payload);
|
||||
break;
|
||||
case PLV2_NOTIFY:
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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)
|
||||
{
|
||||
ike_cfg_t *ike_cfg;
|
||||
childless_t policy;
|
||||
|
||||
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 (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;
|
||||
}
|
||||
}
|
||||
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");
|
||||
return DESTROY_ME;
|
||||
@@ -1079,6 +1054,88 @@ static status_t defer_child_sa(private_child_create_t *this)
|
||||
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,
|
||||
private_child_create_t *this, message_t *message)
|
||||
{
|
||||
@@ -1094,8 +1151,7 @@ METHOD(task_t, build_i, status_t,
|
||||
case CREATE_CHILD_SA:
|
||||
if (!generate_nonce(this))
|
||||
{
|
||||
message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN,
|
||||
chunk_empty);
|
||||
message->set_exchange_type(message, EXCHANGE_TYPE_UNDEFINED);
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!this->retry && this->dh_group == MODP_NONE)
|
||||
@@ -1150,12 +1206,12 @@ METHOD(task_t, build_i, status_t,
|
||||
else
|
||||
{ /* no virtual IPs configured */
|
||||
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,
|
||||
TRUE, NULL, list, TRUE);
|
||||
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,
|
||||
FALSE, NULL, list, TRUE);
|
||||
list->destroy(list);
|
||||
@@ -1170,6 +1226,22 @@ METHOD(task_t, build_i, status_t,
|
||||
this->tsr->insert_first(this->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->dh_group == MODP_NONE);
|
||||
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->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)
|
||||
{
|
||||
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);
|
||||
tsi = get_ts_if_nat_transport(this, FALSE, this->tsi);
|
||||
|
||||
listr = get_dynamic_hosts(this->ike_sa, TRUE);
|
||||
listi = get_dynamic_hosts(this->ike_sa, FALSE);
|
||||
listr = ike_sa_get_dynamic_hosts(this->ike_sa, TRUE);
|
||||
listi = ike_sa_get_dynamic_hosts(this->ike_sa, FALSE);
|
||||
child_cfg = peer_cfg->select_child_cfg(peer_cfg,
|
||||
tsr ?: this->tsr, tsi ?: this->tsi,
|
||||
listr, listi);
|
||||
tsr ?: this->tsr, tsi ?: this->tsi,
|
||||
listr, listi, this->labels_r, this->labels_i);
|
||||
if ((tsi || tsr) && child_cfg &&
|
||||
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 */
|
||||
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);
|
||||
listi->destroy(listi);
|
||||
@@ -1398,6 +1481,49 @@ static status_t handle_childless(private_child_create_t *this)
|
||||
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,
|
||||
private_child_create_t *this, message_t *message)
|
||||
{
|
||||
@@ -1505,6 +1631,13 @@ METHOD(task_t, build_r, status_t,
|
||||
}
|
||||
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_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);
|
||||
@@ -1762,6 +1895,12 @@ METHOD(task_t, process_i, status_t,
|
||||
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 (!this->rekey)
|
||||
@@ -1797,6 +1936,13 @@ METHOD(child_create_t, use_if_ids, void,
|
||||
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,
|
||||
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));
|
||||
}
|
||||
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->proposal);
|
||||
DESTROY_IF(this->nonceg);
|
||||
@@ -1876,7 +2030,6 @@ METHOD(task_t, migrate, void,
|
||||
this->ipcomp_received = IPCOMP_NONE;
|
||||
this->other_cpi = 0;
|
||||
this->established = FALSE;
|
||||
this->child = (child_sa_create_t){};
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
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)
|
||||
{
|
||||
DESTROY_IF(this->child_sa);
|
||||
@@ -1906,6 +2067,7 @@ METHOD(task_t, destroy, void,
|
||||
}
|
||||
DESTROY_IF(this->config);
|
||||
DESTROY_IF(this->nonceg);
|
||||
DESTROY_IF(this->child.label);
|
||||
free(this);
|
||||
}
|
||||
|
||||
@@ -1926,6 +2088,7 @@ child_create_t *child_create_create(ike_sa_t *ike_sa,
|
||||
.use_reqid = _use_reqid,
|
||||
.use_marks = _use_marks,
|
||||
.use_if_ids = _use_if_ids,
|
||||
.use_label = _use_label,
|
||||
.use_dh_group = _use_dh_group,
|
||||
.task = {
|
||||
.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);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
child_init_args_t args = {};
|
||||
enumerator_t *enumerator;
|
||||
entry_t *entry;
|
||||
child_sa_t *child_sa;
|
||||
child_cfg_t *child_cfg;
|
||||
protocol_id_t protocol;
|
||||
uint32_t spi, reqid;
|
||||
uint32_t spi;
|
||||
action_t action;
|
||||
status_t status = SUCCESS;
|
||||
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 */
|
||||
}
|
||||
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->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);
|
||||
|
||||
this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi);
|
||||
|
||||
if (entry->check_delete_action)
|
||||
{ /* enforce child_cfg policy if deleted passively */
|
||||
switch (action)
|
||||
if (action & ACTION_TRAP)
|
||||
{
|
||||
case ACTION_RESTART:
|
||||
child_cfg->get_ref(child_cfg);
|
||||
status = this->ike_sa->initiate(this->ike_sa, child_cfg,
|
||||
reqid, NULL, NULL);
|
||||
break;
|
||||
case ACTION_ROUTE:
|
||||
charon->traps->install(charon->traps,
|
||||
this->ike_sa->get_peer_cfg(this->ike_sa),
|
||||
child_cfg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
charon->traps->install(charon->traps,
|
||||
this->ike_sa->get_peer_cfg(this->ike_sa),
|
||||
child_cfg);
|
||||
}
|
||||
if (action & ACTION_START)
|
||||
{
|
||||
child_cfg->get_ref(child_cfg);
|
||||
status = this->ike_sa->initiate(this->ike_sa, child_cfg, &args);
|
||||
}
|
||||
}
|
||||
child_cfg->destroy(child_cfg);
|
||||
DESTROY_IF(args.label);
|
||||
if (status != SUCCESS)
|
||||
{
|
||||
break;
|
||||
|
||||
@@ -210,6 +210,8 @@ METHOD(task_t, build_i, status_t,
|
||||
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, 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,
|
||||
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_sa->get_if_id(this->child_sa, TRUE),
|
||||
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);
|
||||
this->child_create->set_config(this->child_create, config->get_ref(config));
|
||||
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))
|
||||
{
|
||||
child_cfg_t *child_cfg;
|
||||
uint32_t reqid;
|
||||
child_init_args_t args = {};
|
||||
status_t status;
|
||||
|
||||
if (this->collision &&
|
||||
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
|
||||
* narrowing) */
|
||||
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);
|
||||
child_cfg = this->child_sa->get_config(this->child_sa);
|
||||
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);
|
||||
this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi);
|
||||
return this->ike_sa->initiate(this->ike_sa,
|
||||
child_cfg->get_ref(child_cfg), reqid,
|
||||
NULL, NULL);
|
||||
status = this->ike_sa->initiate(this->ike_sa,
|
||||
child_cfg->get_ref(child_cfg), &args);
|
||||
DESTROY_IF(args.label);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (this->child_create->task.process(&this->child_create->task,
|
||||
|
||||
@@ -172,12 +172,10 @@ struct task_manager_t {
|
||||
* Queue CHILD_SA establishing tasks.
|
||||
*
|
||||
* @param cfg CHILD_SA config to establish
|
||||
* @param reqid reqid to use for CHILD_SA
|
||||
* @param tsi initiator traffic selector, if packet-triggered
|
||||
* @param tsr responder traffic selector, if packet-triggered
|
||||
* @param args optional arguments for the initiation
|
||||
*/
|
||||
void (*queue_child)(task_manager_t *this, child_cfg_t *cfg, uint32_t reqid,
|
||||
traffic_selector_t *tsi, traffic_selector_t *tsr);
|
||||
void (*queue_child)(task_manager_t *this, child_cfg_t *cfg,
|
||||
child_init_args_t *args);
|
||||
|
||||
/**
|
||||
* Queue CHILD_SA rekeying tasks.
|
||||
|
||||
+148
-43
@@ -106,6 +106,8 @@ typedef struct {
|
||||
child_sa_t *child_sa;
|
||||
/** TRUE in case of wildcard Transport Mode SA */
|
||||
bool wildcard;
|
||||
/** TRUE for CHILD_SAs that are externally managed */
|
||||
bool external;
|
||||
} entry_t;
|
||||
|
||||
/**
|
||||
@@ -118,6 +120,8 @@ typedef struct {
|
||||
uint32_t reqid;
|
||||
/** destination address (wildcard case) */
|
||||
host_t *dst;
|
||||
/** security label, if any */
|
||||
sec_label_t *label;
|
||||
} acquire_t;
|
||||
|
||||
/**
|
||||
@@ -125,7 +129,10 @@ typedef struct {
|
||||
*/
|
||||
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);
|
||||
free(this->name);
|
||||
free(this);
|
||||
@@ -137,6 +144,7 @@ static void destroy_entry(entry_t *this)
|
||||
static void destroy_acquire(acquire_t *this)
|
||||
{
|
||||
DESTROY_IF(this->dst);
|
||||
DESTROY_IF(this->label);
|
||||
free(this);
|
||||
}
|
||||
|
||||
@@ -144,9 +152,10 @@ CALLBACK(acquire_by_reqid, bool,
|
||||
acquire_t *this, va_list args)
|
||||
{
|
||||
uint32_t reqid;
|
||||
sec_label_t *label;
|
||||
|
||||
VA_ARGS_VGET(args, reqid);
|
||||
return this->reqid == reqid;
|
||||
VA_ARGS_VGET(args, reqid, label);
|
||||
return this->reqid == reqid && sec_labels_equal(this->label, label);
|
||||
}
|
||||
|
||||
CALLBACK(acquire_by_dst, bool,
|
||||
@@ -183,6 +192,41 @@ static bool dynamic_remote_ts(child_cfg_t *child)
|
||||
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,
|
||||
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;
|
||||
child_sa_t *child_sa;
|
||||
host_t *me, *other;
|
||||
linked_list_t *my_ts, *other_ts, *list;
|
||||
linked_list_t *local, *remote;
|
||||
enumerator_t *enumerator;
|
||||
status_t status;
|
||||
linked_list_t *proposals;
|
||||
proposal_t *proposal;
|
||||
protocol_id_t proto = PROTO_ESP;
|
||||
bool result = FALSE, wildcard = FALSE;
|
||||
|
||||
/* try to resolve addresses */
|
||||
@@ -254,13 +295,14 @@ METHOD(trap_manager_t, install, bool,
|
||||
enumerator = this->traps->create_enumerator(this->traps);
|
||||
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),
|
||||
peer->get_name(peer)))
|
||||
{
|
||||
found = entry;
|
||||
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);
|
||||
}
|
||||
break;
|
||||
@@ -302,28 +344,13 @@ METHOD(trap_manager_t, install, bool,
|
||||
};
|
||||
child_sa = child_sa_create(me, other, child, &child_data);
|
||||
|
||||
list = linked_list_create_with_items(me, NULL);
|
||||
my_ts = child->get_traffic_selectors(child, TRUE, NULL, list, FALSE);
|
||||
list->destroy_offset(list, offsetof(host_t, destroy));
|
||||
local = linked_list_create_with_items(me, NULL);
|
||||
remote = linked_list_create_with_items(other, NULL);
|
||||
|
||||
list = linked_list_create_with_items(other, NULL);
|
||||
other_ts = child->get_traffic_selectors(child, FALSE, NULL, list, FALSE);
|
||||
list->destroy_offset(list, offsetof(host_t, destroy));
|
||||
status = install_trap(child_sa, local, remote);
|
||||
|
||||
/* 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);
|
||||
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));
|
||||
local->destroy_offset(local, offsetof(host_t, destroy));
|
||||
remote->destroy_offset(remote, offsetof(host_t, destroy));
|
||||
if (status != SUCCESS)
|
||||
{
|
||||
DBG1(DBG_CFG, "installing trap failed");
|
||||
@@ -366,8 +393,71 @@ METHOD(trap_manager_t, uninstall, bool,
|
||||
enumerator = this->traps->create_enumerator(this->traps);
|
||||
while (enumerator->enumerate(enumerator, &entry))
|
||||
{
|
||||
if (streq(entry->name, child) &&
|
||||
(!peer || streq(peer, entry->peer_cfg->get_name(entry->peer_cfg))))
|
||||
if (!entry->external &&
|
||||
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);
|
||||
found = entry;
|
||||
@@ -396,8 +486,9 @@ CALLBACK(trap_filter, bool,
|
||||
|
||||
while (orig->enumerate(orig, &entry))
|
||||
{
|
||||
if (!entry->child_sa)
|
||||
{ /* skip entries that are currently being installed */
|
||||
if (!entry->child_sa || entry->external)
|
||||
{ /* skip entries that are currently being installed or are managed
|
||||
* externally */
|
||||
continue;
|
||||
}
|
||||
if (peer_cfg)
|
||||
@@ -423,8 +514,7 @@ METHOD(trap_manager_t, create_enumerator, enumerator_t*,
|
||||
}
|
||||
|
||||
METHOD(trap_manager_t, acquire, void,
|
||||
private_trap_manager_t *this, uint32_t reqid,
|
||||
traffic_selector_t *src, traffic_selector_t *dst)
|
||||
private_trap_manager_t *this, uint32_t reqid, kernel_acquire_data_t *data)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
entry_t *entry, *found = NULL;
|
||||
@@ -454,7 +544,6 @@ METHOD(trap_manager_t, acquire, void,
|
||||
this->lock->unlock(this->lock);
|
||||
return;
|
||||
}
|
||||
reqid = found->child_sa->get_reqid(found->child_sa);
|
||||
wildcard = found->wildcard;
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
@@ -463,7 +552,7 @@ METHOD(trap_manager_t, acquire, void,
|
||||
* with the same peer */
|
||||
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,
|
||||
(void**)&acquire, host))
|
||||
{
|
||||
@@ -482,7 +571,7 @@ METHOD(trap_manager_t, acquire, void,
|
||||
else
|
||||
{
|
||||
if (this->acquires->find_first(this->acquires, acquire_by_reqid,
|
||||
(void**)&acquire, reqid))
|
||||
(void**)&acquire, reqid, data->label))
|
||||
{
|
||||
ignore = TRUE;
|
||||
}
|
||||
@@ -490,6 +579,7 @@ METHOD(trap_manager_t, acquire, void,
|
||||
{
|
||||
INIT(acquire,
|
||||
.reqid = reqid,
|
||||
.label = data->label ? data->label->clone(data->label) : NULL,
|
||||
);
|
||||
this->acquires->insert_last(this->acquires, acquire);
|
||||
}
|
||||
@@ -497,7 +587,8 @@ METHOD(trap_manager_t, acquire, void,
|
||||
this->mutex->unlock(this->mutex);
|
||||
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);
|
||||
return;
|
||||
}
|
||||
@@ -521,12 +612,12 @@ METHOD(trap_manager_t, acquire, void,
|
||||
ike_cfg = ike_sa->get_ike_cfg(ike_sa);
|
||||
|
||||
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);
|
||||
ike_sa->set_other_host(ike_sa, host);
|
||||
|
||||
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);
|
||||
ike_sa->set_my_host(ike_sa, host);
|
||||
|
||||
@@ -542,17 +633,24 @@ METHOD(trap_manager_t, acquire, void,
|
||||
|
||||
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)
|
||||
{ /* in IKEv1, don't prepend the acquiring packet TS, as we only
|
||||
* 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);
|
||||
acquire->ike_sa = ike_sa;
|
||||
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);
|
||||
}
|
||||
@@ -600,6 +698,11 @@ static void complete(private_trap_manager_t *this, ike_sa_t *ike_sa,
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (!sec_labels_equal(acquire->label,
|
||||
child_sa->get_label(child_sa)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
this->acquires->remove_at(this->acquires, enumerator);
|
||||
destroy_acquire(acquire);
|
||||
@@ -673,6 +776,8 @@ trap_manager_t *trap_manager_create(void)
|
||||
.public = {
|
||||
.install = _install,
|
||||
.uninstall = _uninstall,
|
||||
.install_external = _install_external,
|
||||
.remove_external = _remove_external,
|
||||
.create_enumerator = _create_enumerator,
|
||||
.acquire = _acquire,
|
||||
.flush = _flush,
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <library.h>
|
||||
#include <collections/enumerator.h>
|
||||
#include <config/peer_cfg.h>
|
||||
#include <sa/child_sa.h>
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
@@ -64,12 +89,11 @@ struct trap_manager_t {
|
||||
/**
|
||||
* Acquire an SA triggered by an installed trap.
|
||||
*
|
||||
* @param reqid reqid of the triggering CHILD_SA
|
||||
* @param src source of the triggering packet
|
||||
* @param dst destination of the triggering packet
|
||||
* @param reqid reqid of the triggered policy
|
||||
* @param data data from the acquire
|
||||
*/
|
||||
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.
|
||||
|
||||
@@ -21,6 +21,47 @@
|
||||
#include <tests/utils/job_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
|
||||
* 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_create_t child = {
|
||||
.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;
|
||||
|
||||
@@ -44,7 +87,7 @@ START_TEST(test_collision_ike_rekey)
|
||||
traffic_selector_create_dynamic(0, 0, 65535));
|
||||
child_cfg->add_traffic_selector(child_cfg, FALSE,
|
||||
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_hook();
|
||||
|
||||
@@ -81,7 +124,7 @@ START_TEST(test_collision_ike_rekey)
|
||||
ck_assert(!exchange_test_helper->sender->dequeue(exchange_test_helper->sender));
|
||||
assert_num_tasks(a, 0, TASK_QUEUE_ACTIVE);
|
||||
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_sa_idle(b);
|
||||
@@ -98,6 +141,10 @@ Suite *child_create_suite_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");
|
||||
tcase_add_test(tc, test_collision_ike_rekey);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
@@ -44,7 +44,7 @@ START_TEST(test_regular)
|
||||
id_a = a->get_id(a);
|
||||
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 --> */
|
||||
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_b = b->get_id(b);
|
||||
|
||||
call_ikesa(a, initiate, NULL, 0, NULL, NULL);
|
||||
call_ikesa(a, initiate, NULL, NULL);
|
||||
|
||||
/* IKE_SA_INIT --> */
|
||||
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(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 } --> */
|
||||
assert_hook_called(child_updown);
|
||||
@@ -192,7 +192,7 @@ START_TEST(test_failure_init)
|
||||
id_a = a->get_id(a);
|
||||
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 --> */
|
||||
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_b = b->get_id(b);
|
||||
|
||||
call_ikesa(a, initiate, child_cfg, 0, NULL, NULL);
|
||||
call_ikesa(a, initiate, child_cfg, NULL);
|
||||
|
||||
/* IKE_SA_INIT --> */
|
||||
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);
|
||||
|
||||
/* 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, a, NULL);
|
||||
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_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 --> */
|
||||
id_r->set_initiator_spi(id_r, id_i->get_initiator_spi(id_i));
|
||||
|
||||
@@ -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 \
|
||||
processing/jobs/callback_job.c processing/processor.c processing/scheduler.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 \
|
||||
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 \
|
||||
|
||||
@@ -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 \
|
||||
processing/jobs/callback_job.c processing/processor.c processing/scheduler.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 \
|
||||
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 \
|
||||
@@ -112,7 +113,8 @@ resolver/resolver.h resolver/resolver_response.h resolver/rr_set.h \
|
||||
resolver/rr.h resolver/resolver_manager.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/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 \
|
||||
threading/thread.h threading/windows/thread.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 = \
|
||||
$(DLLIB) $(ATOMICLIB) $(BTLIB) $(SOCKLIB) \
|
||||
$(RTLIB) $(BFDLIB) $(UNWINDLIB)
|
||||
$(RTLIB) $(BFDLIB) $(UNWINDLIB) \
|
||||
$(selinux_LIBS)
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src/libstrongswan \
|
||||
@@ -142,6 +145,7 @@ AM_CPPFLAGS = \
|
||||
-DSTRONGSWAN_CONF=\"${strongswan_conf}\"
|
||||
|
||||
AM_CFLAGS = \
|
||||
$(selinux_CFLAGS) \
|
||||
@COVERAGE_CFLAGS@
|
||||
|
||||
AM_LDFLAGS = \
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
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_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;
|
||||
|
||||
|
||||
@@ -50,7 +50,12 @@ enum ts_type_t {
|
||||
* (inclusive). All addresses falling between the two specified
|
||||
* addresses are considered to be within the list.
|
||||
*/
|
||||
TS_IPV6_ADDR_RANGE = 8
|
||||
TS_IPV6_ADDR_RANGE = 8,
|
||||
|
||||
/**
|
||||
* A security label.
|
||||
*/
|
||||
TS_SECLABEL = 10,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,13 +20,13 @@
|
||||
/*******************************************************************************
|
||||
* continuous enum
|
||||
*/
|
||||
enum {
|
||||
enum test_enum_cont {
|
||||
CONT1,
|
||||
CONT2,
|
||||
CONT3,
|
||||
CONT4,
|
||||
CONT5,
|
||||
} test_enum_cont;
|
||||
};
|
||||
|
||||
ENUM_BEGIN(test_enum_cont_names, CONT1, CONT5,
|
||||
"CONT1", "CONT2", "CONT3", "CONT4", "CONT5");
|
||||
@@ -35,13 +35,13 @@ ENUM_END(test_enum_cont_names, CONT5);
|
||||
/*******************************************************************************
|
||||
* split enum
|
||||
*/
|
||||
enum {
|
||||
enum test_enum_split {
|
||||
SPLIT1 = 1,
|
||||
SPLIT2,
|
||||
SPLIT3 = 5,
|
||||
SPLIT4,
|
||||
SPLIT5 = 255,
|
||||
} test_enum_split;
|
||||
};
|
||||
|
||||
ENUM_BEGIN(test_enum_split_names, SPLIT1, SPLIT2,
|
||||
"SPLIT1", "SPLIT2");
|
||||
@@ -54,7 +54,7 @@ ENUM_END(test_enum_split_names, SPLIT5);
|
||||
/*******************************************************************************
|
||||
* enum flags
|
||||
*/
|
||||
enum {
|
||||
enum test_enum_flags {
|
||||
FLAG1 = (1 << 0),
|
||||
FLAG2 = (1 << 1),
|
||||
FLAG3 = (1 << 2),
|
||||
@@ -67,18 +67,18 @@ enum {
|
||||
FLAG10 = (1 << 9),
|
||||
FLAG11 = (1 << 10),
|
||||
FLAG12 = (1 << 11),
|
||||
} test_enum_flags;
|
||||
};
|
||||
|
||||
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,
|
||||
"FLAG3", "FLAG4");
|
||||
"(unset)", "FLAG3", "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",
|
||||
"OVERFLOWFLAGLONGNAME4", "OVERFLOWFLAGLONGNAME5", "OVERFLOWFLAGLONGNAME6",
|
||||
"OVERFLOWFLAGLONGNAME7", "OVERFLOWFLAGLONGNAME8", "OVERFLOWFLAGLONGNAME9",
|
||||
@@ -263,6 +263,30 @@ static struct {
|
||||
}, enum_flags_to_string_tests[] = {
|
||||
{-1, 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)
|
||||
@@ -371,6 +395,38 @@ START_TEST(test_enum_flags_to_string_noflagenum)
|
||||
}
|
||||
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)
|
||||
{
|
||||
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));
|
||||
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");
|
||||
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));
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include <library.h>
|
||||
#include <collections/enumerator.h>
|
||||
#include <utils/utils.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
|
||||
*/
|
||||
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;
|
||||
|
||||
while (val != 0x01)
|
||||
while (first != 0x01)
|
||||
{
|
||||
val = val >> 1;
|
||||
first = first >> 1;
|
||||
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;
|
||||
}
|
||||
|
||||
if (snprintf(buf, len, "(unset)") >= len)
|
||||
if (snprintf(buf, len, e->names[0]) >= len)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@@ -130,13 +130,14 @@ struct enum_name_t {
|
||||
* @param name name of the enum_name list
|
||||
* @param first enum value of the first 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
|
||||
*/
|
||||
#define ENUM_FLAGS(name, first, last, ...) \
|
||||
#define ENUM_FLAGS(name, first, last, unset, ...) \
|
||||
static enum_name_t name##last = {first, last + \
|
||||
BUILD_ASSERT((__builtin_ffs(last)-__builtin_ffs(first)+1) == \
|
||||
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.
|
||||
@@ -157,7 +158,7 @@ char *enum_to_name(enum_name_t *e, int val);
|
||||
*/
|
||||
#define enum_from_name(e, name, valp) ({ \
|
||||
int _val; \
|
||||
int _found = enum_from_name_as_int(e, name, &_val); \
|
||||
bool _found = enum_from_name_as_int(e, name, &_val); \
|
||||
if (_found) \
|
||||
{ \
|
||||
*(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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @param e enum names for this enum value
|
||||
* @param name name to get enum value for
|
||||
* @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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
||||
@@ -539,6 +539,7 @@ static char *whitelist[] = {
|
||||
"initgroups",
|
||||
"tzset",
|
||||
"_IO_file_doallocate",
|
||||
"selinux_check_access",
|
||||
/* ignore dlopen, as we do not dlclose to get proper leak reports */
|
||||
"dlopen",
|
||||
"dlerror",
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "swanctl.h"
|
||||
#include "command.h"
|
||||
|
||||
#include <collections/hashtable.h>
|
||||
@@ -135,6 +136,7 @@ CALLBACK(children_sn, int,
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
print_label(" label: ", child->get(child, "label"));
|
||||
printf(" local: %s\n", child->get(child, "local-ts"));
|
||||
printf(" remote: %s\n", child->get(child, "remote-ts"));
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <errno.h>
|
||||
|
||||
#include "command.h"
|
||||
#include "swanctl.h"
|
||||
|
||||
#include <collections/hashtable.h>
|
||||
|
||||
@@ -85,6 +86,7 @@ CALLBACK(policies, int,
|
||||
ret = vici_parse_cb(res, NULL, policy_values, policy_list, pol);
|
||||
|
||||
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(" remote: %s\n", pol->get(pol, "remote-ts"));
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include <errno.h>
|
||||
|
||||
#include "command.h"
|
||||
#include "swanctl.h"
|
||||
|
||||
#include <collections/hashtable.h>
|
||||
|
||||
@@ -81,7 +82,6 @@ CALLBACK(sa_values, int,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
CALLBACK(sa_list, int,
|
||||
hashtable_t *sa, vici_res_t *res, char *name, void *value, int len)
|
||||
{
|
||||
@@ -227,6 +227,7 @@ CALLBACK(child_sas, int,
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
print_label(" label ", child->get(child, "label"));
|
||||
printf(" local %s\n", child->get(child, "local-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;
|
||||
bool noblock = FALSE;
|
||||
command_format_options_t format = COMMAND_FORMAT_NONE;
|
||||
char *arg, *ike = NULL;
|
||||
int ike_id = 0, ret;
|
||||
char *arg, *ike = NULL, *child = NULL;
|
||||
int ike_id = 0, child_id = 0, ret;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
@@ -387,6 +388,12 @@ static int list_sas(vici_conn_t *conn)
|
||||
case 'I':
|
||||
ike_id = atoi(arg);
|
||||
continue;
|
||||
case 'c':
|
||||
child = arg;
|
||||
continue;
|
||||
case 'C':
|
||||
child_id = atoi(arg);
|
||||
continue;
|
||||
case 'n':
|
||||
noblock = TRUE;
|
||||
continue;
|
||||
@@ -418,6 +425,14 @@ static int list_sas(vici_conn_t *conn)
|
||||
{
|
||||
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)
|
||||
{
|
||||
vici_add_key_valuef(req, "noblock", "yes");
|
||||
@@ -489,11 +504,14 @@ static void __attribute__ ((constructor))reg()
|
||||
{
|
||||
command_register((command_t) {
|
||||
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"},
|
||||
{"ike", 'i', 1, "filter IKE_SAs by name"},
|
||||
{"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"},
|
||||
{"raw", 'r', 0, "dump raw response message"},
|
||||
{"pretty", 'P', 0, "dump raw response message in pretty print"},
|
||||
|
||||
@@ -156,7 +156,7 @@ static void __attribute__ ((constructor))reg()
|
||||
{"help", 'h', 0, "show usage information"},
|
||||
{"child", 'c', 1, "terminate by CHILD_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"},
|
||||
{"force", 'f', 0, "terminate IKE_SA without waiting, unless timeout is set"},
|
||||
{"timeout", 't', 1, "timeout in seconds before detaching"},
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include <library.h>
|
||||
#include <selectors/sec_label.h>
|
||||
|
||||
/*
|
||||
* Described in header
|
||||
@@ -59,6 +60,27 @@ settings_t *load_swanctl_conf(char *file)
|
||||
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()
|
||||
*/
|
||||
|
||||
@@ -111,4 +111,13 @@ extern char *swanctl_dir;
|
||||
*/
|
||||
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_ @}*/
|
||||
|
||||
+36
-4
@@ -885,7 +885,8 @@ connections.<conn>.children.<child>.dpd_action = 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
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
**start_action** different from _none_, the inverse action is performed.
|
||||
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
|
||||
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
|
||||
_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
|
||||
alive. It acts on explicit close messages only, but not on negotiation
|
||||
|
||||
+26
-13
@@ -406,12 +406,16 @@ do
|
||||
echo -e "\nPRE-TEST\n" >> $CONSOLE_LOG 2>&1
|
||||
|
||||
eval `awk -F "::" '{
|
||||
if ($1 !~ /^#.*/ && $2 != "")
|
||||
{
|
||||
printf("echo \"$(print_time)%s# %s\"; ", $1, $2)
|
||||
printf("ssh \044SSHCONF root@\044ipv4_%s \"%s\"; ", $1, $2)
|
||||
printf("echo;\n")
|
||||
}
|
||||
if ($0 ~ /^#.*/)
|
||||
{
|
||||
printf("echo \"%s\"; ", $0);
|
||||
}
|
||||
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
|
||||
|
||||
|
||||
@@ -443,7 +447,12 @@ do
|
||||
command=$2
|
||||
pattern=$3
|
||||
hit=$4
|
||||
if (host ~ /^#.*/ || command == "")
|
||||
if ($0 ~ /^#.*/)
|
||||
{
|
||||
printf("echo \"%s\"; ", $0);
|
||||
next
|
||||
}
|
||||
else if (command == "")
|
||||
{
|
||||
next
|
||||
}
|
||||
@@ -766,12 +775,16 @@ do
|
||||
echo -e "\nPOST-TEST\n" >> $CONSOLE_LOG 2>&1
|
||||
|
||||
eval `awk -F "::" '{
|
||||
if ($1 !~ /^#.*/ && $2 != "")
|
||||
{
|
||||
printf("echo \"$(print_time)%s# %s\"; ", $1, $2)
|
||||
printf("ssh \044SSHCONF root@\044ipv4_%s \"%s\"; ", $1, $2)
|
||||
printf("echo;\n")
|
||||
}
|
||||
if ($0 ~ /^#.*/)
|
||||
{
|
||||
printf("echo \"%s\"; ", $0);
|
||||
}
|
||||
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
|
||||
|
||||
##########################################################################
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user