Merge branch 'labeled-ipsec'

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

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

Closes #3075
This commit is contained in:
Tobias Brunner
2022-04-14 18:52:38 +02:00
89 changed files with 3317 additions and 827 deletions
+11
View File
@@ -321,6 +321,7 @@ ARG_ENABL_SET([python-eggs], [enable build of provided python eggs.])
ARG_ENABL_SET([python-eggs-install],[enable installation of provided python eggs.])
ARG_ENABL_SET([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
View File
@@ -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
+2
View File
@@ -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);
}
+3 -3
View File
@@ -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);
+3 -1
View File
@@ -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;
}
+2 -2
View File
@@ -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,
+7
View File
@@ -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
+115 -5
View File
@@ -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, &current))
{
if (this->label->equals(this->label, current))
{
if (log)
{
DBG2(DBG_CFG, " %s => matches exactly",
current->get_string(current));
}
match = current;
exact = TRUE;
break;
}
else if (this->label_mode == SEC_LABEL_MODE_SELINUX &&
this->label->matches(this->label, current))
{
if (log)
{
DBG2(DBG_CFG, " %s => matches%s",
current->get_string(current), match ? ", ignored" : "");
}
/* return the first match if we don't find an exact one */
if (!match)
{
match = current;
}
}
else if (log)
{
DBG2(DBG_CFG, " %s => no match", current->get_string(current));
}
}
enumerator->destroy(enumerator);
if (!match)
{
DBG2(DBG_CFG, "none of the proposed security labels match the "
"configured label '%s'", this->label->get_string(this->label));
return FALSE;
}
}
else if (this->label)
{
DBG2(DBG_CFG, "peer didn't propose any security labels, we expect one "
"matching '%s'", this->label->get_string(this->label));
return FALSE;
}
if (label)
{
*label = match;
}
if (exact_out)
{
*exact_out = exact;
}
return TRUE;
}
METHOD(child_cfg_t, get_tfc, uint32_t,
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,
+44 -5
View File
@@ -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 */
+31 -11
View File
@@ -386,13 +386,28 @@ METHOD(peer_cfg_t, create_child_cfg_enumerator, enumerator_t*,
/**
* Check how good a list of TS matches a given child config
*/
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, &current))
{
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)
{
+7 -4
View File
@@ -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.
+1 -1
View File
@@ -473,7 +473,7 @@ METHOD(job_t, initiate_execute, job_requeue_t,
}
}
if (ike_sa->initiate(ike_sa, listener->child_cfg, 0, NULL, NULL) == SUCCESS)
if (ike_sa->initiate(ike_sa, listener->child_cfg, NULL) == SUCCESS)
{
if (!listener->logger.callback)
{
-4
View File
@@ -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:
-23
View File
@@ -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_ @}*/
+43 -6
View File
@@ -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;
+16 -4
View File
@@ -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_ @}*/
+13 -6
View File
@@ -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;
}
+57 -29
View File
@@ -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);
}
+8 -5
View File
@@ -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.
+7
View File
@@ -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;
};
/**
+15 -3
View File
@@ -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);
+18
View File
@@ -0,0 +1,18 @@
AM_CPPFLAGS = \
-I$(top_srcdir)/src/libstrongswan \
-I$(top_srcdir)/src/libcharon
AM_CFLAGS = \
$(PLUGIN_CFLAGS)
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-selinux.la
else
plugin_LTLIBRARIES = libstrongswan-selinux.la
endif
libstrongswan_selinux_la_SOURCES = \
selinux_plugin.h selinux_plugin.c \
selinux_listener.h selinux_listener.c
libstrongswan_selinux_la_LDFLAGS = -module -avoid-version
@@ -0,0 +1,261 @@
/*
* Copyright (C) 2022 Tobias Brunner, codelabs GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "selinux_listener.h"
#include <daemon.h>
#include <collections/array.h>
#include <collections/hashtable.h>
typedef struct private_selinux_listener_t private_selinux_listener_t;
/**
* Private data.
*/
struct private_selinux_listener_t {
/**
* Public interface.
*/
selinux_listener_t public;
/**
* IKE_SAs with attached trap policies, ike_sa_id_t => entry_t.
*/
hashtable_t *sas;
};
/**
* Entry to keep track of trap policies.
*/
typedef struct {
/**
* IKE_SA ID.
*/
ike_sa_id_t *id;
/**
* Installed trap policies.
*/
array_t *traps;
} entry_t;
/**
* Destroy the given entry.
*/
static void destroy_entry(entry_t *entry)
{
entry->id->destroy(entry->id);
array_destroy(entry->traps);
free(entry);
}
/**
* Hashtable hash function
*/
static u_int hash(const void *key)
{
ike_sa_id_t *id = (ike_sa_id_t*)key;
uint64_t spi_i = id->get_initiator_spi(id),
spi_r = id->get_responder_spi(id);
return chunk_hash_inc(chunk_from_thing(spi_i),
chunk_hash(chunk_from_thing(spi_r)));
}
/**
* Hashtable equals function
*/
static bool equals(const void *a_pub, const void *b)
{
ike_sa_id_t *a = (ike_sa_id_t*)a_pub;
return a->equals(a, (ike_sa_id_t*)b);
}
/**
* Install a trap policy for the generic SELinux label.
*/
static bool install_generic_trap(ike_sa_t *ike_sa, child_sa_t *child_sa)
{
linked_list_t *local, *remote;
sec_label_t *label;
bool success;
label = child_sa->get_label(child_sa);
DBG1(DBG_IKE, "installing trap %s{%d} with generic security label '%s'",
child_sa->get_name(child_sa), child_sa->get_unique_id(child_sa),
label->get_string(label));
local = ike_sa_get_dynamic_hosts(ike_sa, TRUE);
remote = ike_sa_get_dynamic_hosts(ike_sa, FALSE);
success = charon->traps->install_external(charon->traps,
ike_sa->get_peer_cfg(ike_sa),
child_sa, local, remote);
local->destroy(local);
remote->destroy(remote);
return success;
}
METHOD(listener_t, ike_updown, bool,
private_selinux_listener_t *this, ike_sa_t *ike_sa, bool up)
{
enumerator_t *enumerator;
peer_cfg_t *peer_cfg;
child_cfg_t *child_cfg;
child_sa_t *child_sa;
entry_t *entry;
if (up)
{
child_sa_create_t child = {
.if_id_in_def = ike_sa->get_if_id(ike_sa, TRUE),
.if_id_out_def = ike_sa->get_if_id(ike_sa, FALSE),
};
INIT(entry,
.id = ike_sa->get_id(ike_sa),
);
entry->id = entry->id->clone(entry->id);
peer_cfg = ike_sa->get_peer_cfg(ike_sa);
enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
while (enumerator->enumerate(enumerator, &child_cfg))
{
if (child_cfg->get_label(child_cfg) &&
child_cfg->get_label_mode(child_cfg) == SEC_LABEL_MODE_SELINUX)
{
child_sa = child_sa_create(ike_sa->get_my_host(ike_sa),
ike_sa->get_other_host(ike_sa),
child_cfg, &child);
if (install_generic_trap(ike_sa, child_sa))
{
array_insert_create(&entry->traps, ARRAY_TAIL, child_sa);
}
else
{
child_sa->destroy(child_sa);
}
}
}
enumerator->destroy(enumerator);
if (array_count(entry->traps))
{
this->sas->put(this->sas, entry->id, entry);
}
else
{
destroy_entry(entry);
}
}
else
{
entry = this->sas->remove(this->sas, ike_sa->get_id(ike_sa));
if (entry)
{
while (array_remove(entry->traps, ARRAY_TAIL, &child_sa))
{
sec_label_t *label = child_sa->get_label(child_sa);
DBG1(DBG_IKE, "uninstalling trap %s{%d} with generic security "
"label '%s'", child_sa->get_name(child_sa),
child_sa->get_unique_id(child_sa),
label->get_string(label));
charon->traps->remove_external(charon->traps, child_sa);
child_sa->destroy(child_sa);
}
destroy_entry(entry);
}
}
return TRUE;
}
METHOD(listener_t, ike_rekey, bool,
private_selinux_listener_t *this, ike_sa_t *old, ike_sa_t *new)
{
entry_t *entry;
entry = this->sas->remove(this->sas, old->get_id(old));
if (entry)
{
entry->id->destroy(entry->id);
entry->id = new->get_id(new);
entry->id = entry->id->clone(entry->id);
this->sas->put(this->sas, entry->id, entry);
}
return TRUE;
}
METHOD(listener_t, ike_update, bool,
private_selinux_listener_t *this, ike_sa_t *ike_sa,
host_t *local, host_t *remote)
{
entry_t *entry;
child_sa_t *child_sa;
linked_list_t *vips;
int i;
entry = this->sas->get(this->sas, ike_sa->get_id(ike_sa));
if (entry)
{
vips = linked_list_create_from_enumerator(
ike_sa->create_virtual_ip_enumerator(ike_sa, local));
for (i = 0; i < array_count(entry->traps); i++)
{
array_get(entry->traps, i, &child_sa);
child_sa->update(child_sa, local, remote, vips,
ike_sa->has_condition(ike_sa, COND_NAT_ANY));
}
vips->destroy(vips);
}
return TRUE;
}
METHOD(selinux_listener_t, destroy, void,
private_selinux_listener_t *this)
{
this->sas->destroy(this->sas);
free(this);
}
/*
* Described in header
*/
selinux_listener_t *selinux_listener_create()
{
private_selinux_listener_t *this;
INIT(this,
.public = {
.listener = {
.ike_updown = _ike_updown,
.ike_rekey = _ike_rekey,
.ike_update = _ike_update,
},
.destroy = _destroy,
},
.sas = hashtable_create(hash, equals, 32),
);
return &this->public;
}
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2022 Tobias Brunner, codelabs GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* @defgroup selinux_listener selinux_listener
* @{ @ingroup selinux
*/
#ifndef SELINUX_LISTENER_H_
#define SELINUX_LISTENER_H_
#include <bus/listeners/listener.h>
typedef struct selinux_listener_t selinux_listener_t;
/**
* Listener to manage trap policies for generic SELinux labels.
*/
struct selinux_listener_t {
/**
* Implements listener_t interface.
*/
listener_t listener;
/**
* Destroy a selinux_listener_t.
*/
void (*destroy)(selinux_listener_t *this);
};
/**
* Create a listener instance.
*/
selinux_listener_t *selinux_listener_create();
#endif /** SELINUX_LISTENER_H_ @}*/
@@ -0,0 +1,106 @@
/*
* Copyright (C) 2022 Tobias Brunner, codelabs GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "selinux_plugin.h"
#include "selinux_listener.h"
#include <daemon.h>
typedef struct private_selinux_plugin_t private_selinux_plugin_t;
/**
* Private data
*/
struct private_selinux_plugin_t {
/**
* Public interface
*/
selinux_plugin_t public;
/**
* Listener
*/
selinux_listener_t *listener;
};
METHOD(plugin_t, get_name, char*,
private_selinux_plugin_t *this)
{
return "selinux";
}
/**
* Register handler
*/
static bool plugin_cb(private_selinux_plugin_t *this,
plugin_feature_t *feature, bool reg, void *cb_data)
{
if (reg)
{
charon->bus->add_listener(charon->bus, &this->listener->listener);
}
else
{
charon->bus->remove_listener(charon->bus, &this->listener->listener);
}
return TRUE;
}
METHOD(plugin_t, get_features, int,
private_selinux_plugin_t *this, plugin_feature_t *features[])
{
static plugin_feature_t f[] = {
PLUGIN_CALLBACK((plugin_feature_callback_t)plugin_cb, NULL),
PLUGIN_PROVIDE(CUSTOM, "selinux"),
};
*features = f;
return countof(f);
}
METHOD(plugin_t, destroy, void,
private_selinux_plugin_t *this)
{
this->listener->destroy(this->listener);
free(this);
}
/*
* Described in header
*/
plugin_t *selinux_plugin_create()
{
private_selinux_plugin_t *this;
INIT(this,
.public = {
.plugin = {
.get_name = _get_name,
.get_features = _get_features,
.destroy = _destroy,
},
},
.listener = selinux_listener_create(),
);
return &this->public.plugin;
}
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2022 Tobias Brunner, codelabs GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* @defgroup selinux selinux
* @ingroup cplugins
*
* @defgroup selinux_plugin selinux_plugin
* @{ @ingroup selinux
*/
#ifndef SELINUX_PLUGIN_H_
#define SELINUX_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct selinux_plugin_t selinux_plugin_t;
/**
* Plugin managing trap policies with generic SELinux labels.
*/
struct selinux_plugin_t {
/**
* Implements plugin interface
*/
plugin_t plugin;
};
#endif /** SELINUX_PLUGIN_H_ @}*/
+2 -2
View File
@@ -1042,9 +1042,9 @@ static action_t map_action(int starter_action)
switch (starter_action)
{
case 2: /* =hold */
return ACTION_ROUTE;
return ACTION_TRAP;
case 3: /* =restart */
return ACTION_RESTART;
return ACTION_START;
default:
return ACTION_NONE;
}
+7 -2
View File
@@ -277,7 +277,7 @@ Terminates an SA while streaming _control-log_ events.
{
child = <terminate a CHILD_SA by configuration name>
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>
+149 -98
View File
@@ -563,6 +563,9 @@ static void log_child_data(child_data_t *data, char *name)
cfg->set_mark_in.value, cfg->set_mark_in.mask);
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);
}
}
}
+40 -3
View File
@@ -150,6 +150,29 @@ static void list_mode(vici_builder_t *b, child_sa_t *child, child_cfg_t *cfg)
}
}
/**
* List the security label of a CHILD_SA or config
*/
static void list_label(vici_builder_t *b, child_sa_t *child, child_cfg_t *cfg)
{
sec_label_t *label = NULL;
chunk_t enc;
if (child)
{
label = child->get_label(child);
}
else if (cfg)
{
label = cfg->get_label(cfg);
}
if (label)
{
enc = label->get_encoding(label);
b->add_kv(b, "label", "%+B", &enc);
}
}
/**
* List IPsec-related details about a CHILD_SA
*/
@@ -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);
+22 -18
View File
@@ -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;
}
+4 -7
View File
@@ -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
View File
@@ -42,6 +42,7 @@ ENUM(child_sa_state_names, CHILD_CREATED, CHILD_DESTROYING,
);
ENUM_FLAGS(child_sa_outbound_state_names, CHILD_OUTBOUND_REGISTERED, CHILD_OUTBOUND_POLICIES,
"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),
);
+12 -1
View File
@@ -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
View File
@@ -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 -7
View File
@@ -29,6 +29,7 @@ typedef enum ike_condition_t ike_condition_t;
typedef enum ike_sa_state_t ike_sa_state_t;
typedef enum 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_ @}*/
+11 -6
View File
@@ -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);
}
+15 -14
View File
@@ -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);
}
+1 -2
View File
@@ -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)
{
+10 -6
View File
@@ -1983,7 +1983,7 @@ static void trigger_mbb_reauth(private_task_manager_t *this)
/* suspend online revocation checking until the SA is established */
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);
}
+236 -73
View File
@@ -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.
*
+18 -15
View File
@@ -312,12 +312,13 @@ static void process_payloads(private_child_delete_t *this, message_t *message)
*/
static status_t destroy_and_reestablish(private_child_delete_t *this)
{
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;
+16 -5
View File
@@ -210,6 +210,8 @@ METHOD(task_t, build_i, status_t,
this->child_create->use_if_ids(this->child_create,
this->child_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,
+3 -5
View File
@@ -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
View File
@@ -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,
+29 -5
View File
@@ -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.
+49 -2
View File
@@ -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);
+5 -5
View File
@@ -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));
+2 -1
View File
@@ -41,7 +41,8 @@ networking/streams/stream_tcp.c networking/streams/stream_service_tcp.c \
pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \
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 \
+7 -3
View File
@@ -39,7 +39,8 @@ networking/streams/stream_tcp.c networking/streams/stream_service_tcp.c \
pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \
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 = \
+228
View File
@@ -0,0 +1,228 @@
/*
* Copyright (C) 2021 Tobias Brunner, codelabs GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#define _GNU_SOURCE
#include <stdio.h>
#ifdef USE_SELINUX
#include <selinux/selinux.h>
#endif
#include "sec_label.h"
ENUM(sec_label_mode_names, SEC_LABEL_MODE_SYSTEM, SEC_LABEL_MODE_SELINUX,
"system",
"simple",
"selinux",
);
typedef struct private_sec_label_t private_sec_label_t;
/**
* Private data.
*/
struct private_sec_label_t {
/**
* Public interface
*/
sec_label_t public;
/**
* Encoded label value
*/
chunk_t encoding;
/**
* String representation of the label
*/
char *str;
};
static sec_label_t *create_sec_label(chunk_t encoding, char *str);
METHOD(sec_label_t, get_encoding, chunk_t,
private_sec_label_t *this)
{
return this->encoding;
}
METHOD(sec_label_t, get_string, char*,
private_sec_label_t *this)
{
return this->str;
}
METHOD(sec_label_t, clone_, sec_label_t*,
private_sec_label_t *this)
{
return create_sec_label(chunk_clone(this->encoding), strdup(this->str));
}
METHOD(sec_label_t, equals, bool,
private_sec_label_t *this, sec_label_t *other_pub)
{
private_sec_label_t *other = (private_sec_label_t*)other_pub;
if (!other_pub)
{
return FALSE;
}
return chunk_equals_const(this->encoding, other->encoding);
}
METHOD(sec_label_t, matches, bool,
private_sec_label_t *this, sec_label_t *other_pub)
{
if (!other_pub)
{
return FALSE;
}
#ifdef USE_SELINUX
if (is_selinux_enabled())
{ /* if disabled, the following matches anything against anything */
private_sec_label_t *other = (private_sec_label_t*)other_pub;
return selinux_check_access(other->str, this->str, "association",
"polmatch", NULL) == 0;
}
#endif
return equals(this, other_pub);
}
METHOD(sec_label_t, hash, u_int,
private_sec_label_t *this, u_int inc)
{
return chunk_hash_inc(this->encoding, inc);
}
METHOD(sec_label_t, destroy, void,
private_sec_label_t *this)
{
chunk_free(&this->encoding);
free(this->str);
free(this);
}
/**
* Internal constructor, data is adopted
*/
static sec_label_t *create_sec_label(chunk_t encoding, char *str)
{
private_sec_label_t *this;
INIT(this,
.public = {
.get_encoding = _get_encoding,
.get_string = _get_string,
.clone = _clone_,
.matches = _matches,
.equals = _equals,
.hash = _hash,
.destroy = _destroy,
},
.encoding = encoding,
.str = str,
);
return &this->public;
}
/*
* Described in header
*/
sec_label_t *sec_label_from_encoding(const chunk_t value)
{
chunk_t cloned, sanitized = chunk_empty;
char *str;
if (!value.len || (value.len == 1 && !value.ptr[0]))
{
DBG1(DBG_LIB, "invalid empty security label");
return NULL;
}
else if (value.ptr[value.len-1])
{
DBG1(DBG_LIB, "adding null-terminator to security label");
cloned = chunk_cat("cc", value, chunk_from_chars(0x00));
}
else
{
cloned = chunk_clone(value);
}
/* create a sanitized version while ignoring the null-terminator */
if (!chunk_printable(chunk_create(cloned.ptr, cloned.len-1), &sanitized, '?'))
{
#ifdef USE_SELINUX
/* don't accept labels with non-printable characters if we use SELinux */
DBG1(DBG_LIB, "invalid security label with non-printable characters %B",
&value);
chunk_free(&sanitized);
chunk_free(&cloned);
return NULL;
#endif
}
if (asprintf(&str, "%.*s", (int)sanitized.len, sanitized.ptr) <= 0)
{
chunk_free(&sanitized);
chunk_free(&cloned);
return NULL;
}
chunk_free(&sanitized);
return create_sec_label(cloned, str);
}
/*
* Described in header
*/
sec_label_t *sec_label_from_string(const char *value)
{
if (!value)
{
return NULL;
}
return sec_label_from_encoding(chunk_create((char*)value, strlen(value)+1));
}
/*
* Described in header
*/
bool sec_label_mode_from_string(const char *value, sec_label_mode_t *mode)
{
sec_label_mode_t def = sec_label_mode_default();
return enum_from_name(sec_label_mode_names, value, mode) &&
(def == SEC_LABEL_MODE_SELINUX || *mode != SEC_LABEL_MODE_SELINUX);
}
/*
* Described in header
*/
sec_label_mode_t sec_label_mode_default()
{
#ifdef USE_SELINUX
if (is_selinux_enabled())
{
return SEC_LABEL_MODE_SELINUX;
}
#endif
return SEC_LABEL_MODE_SIMPLE;
}
+172
View File
@@ -0,0 +1,172 @@
/*
* Copyright (C) 2021 Tobias Brunner, codelabs GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* @defgroup sec_label sec_label
* @{ @ingroup ipsec
*/
#ifndef SEC_LABEL_H_
#define SEC_LABEL_H_
typedef enum sec_label_mode_t sec_label_mode_t;
typedef struct sec_label_t sec_label_t;
#include <library.h>
/**
* Mode in which security labels are used.
*/
enum sec_label_mode_t {
/**
* System default. Simple mode if SELinux is not supported or disabled
* on the system.
*/
SEC_LABEL_MODE_SYSTEM,
/**
* Simple mode that does establish regular CHILD_SAs, matches labels exactly
* and does not install them in the kernel.
*/
SEC_LABEL_MODE_SIMPLE,
/**
* SELinux mode where configured labels are installed on (trap) policies,
* labels from acquires/peer on SAs, child-less IKE_SAs are initiated
* if there is no acquire, labels are also matched via polmatch.
*/
SEC_LABEL_MODE_SELINUX,
};
/**
* Names for security label modes.
*/
extern enum_name_t *sec_label_mode_names;
/**
* Representation of a security label used on policies/SAs.
*
* For example, with SELinux this could be a value like
* system_u:object_r:ipsec_spd_t:s0.
*/
struct sec_label_t {
/**
* Return a binary encoding of the security label as used for IKE.
*
* @return binary encoding (internal data)
*/
chunk_t (*get_encoding)(sec_label_t *this);
/**
* Return a string representation of this security label.
*
* @return string representation (internal data)
*/
char *(*get_string)(sec_label_t *this);
/**
* Clone this security label.
*
* @return clone of it
*/
sec_label_t *(*clone)(sec_label_t *this);
/**
* Match two security labels.
*
* For SELinux this checks if this security label permits other in terms
* of association { polmatch }.
*
* @param other security label to match against this
* @return TRUE if matching, FALSE otherwise
*/
bool (*matches)(sec_label_t *this, sec_label_t *other);
/**
* Compare two security labels for equality.
*
* @param other security label to compare with this
* @return TRUE if equal, FALSE otherwise
*/
bool (*equals)(sec_label_t *this, sec_label_t *other);
/**
* Create a hash value for the security label.
*
* @param inc optional value for incremental hashing
* @return calculated hash value for the security label
*/
u_int (*hash)(sec_label_t *this, u_int inc);
/**
* Destroys the object.
*/
void (*destroy)(sec_label_t *this);
};
/**
* Try to parse a sec_label_t from the given binary encoding.
*
* @param value encoding to parse
* @return security label instance, NULL if invalid
*/
sec_label_t *sec_label_from_encoding(const chunk_t value);
/**
* Try to parse a sec_label_t from the given string.
*
* @param value string to parse
* @return security label instance, NULL if invalid
*/
sec_label_t *sec_label_from_string(const char *value);
/**
* Compare two security labels for equality, accept if both are NULL.
*
* @param a first label
* @param b second label
* @return TRUE if labels are equal or both NULL
*/
static inline bool sec_labels_equal(sec_label_t *a, sec_label_t *b)
{
return (!a && !b) || (a && a->equals(a, b));
}
/**
* Try to parse a security label mode from the given string.
*
* @param value string to parse
* @param mode parsed mode
* @return TRUE if mode is valid (and usable on system)
*/
bool sec_label_mode_from_string(const char *value, sec_label_mode_t *mode);
/**
* Get the system default security label mode.
*
* @return default mode
*/
sec_label_mode_t sec_label_mode_default();
#endif /** SEC_LABEL_H_ @}*/
@@ -31,10 +31,12 @@
#define NON_SUBNET_ADDRESS_RANGE 255
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,
};
/**
+71 -10
View File
@@ -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));
+55 -5
View File
@@ -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.
*/
+36 -5
View File
@@ -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.
*
+1
View File
@@ -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",
+2
View File
@@ -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"));
+2
View File
@@ -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"));
+22 -4
View File
@@ -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"},
+1 -1
View File
@@ -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
View File
@@ -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()
*/
+9
View File
@@ -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
View File
@@ -885,7 +885,8 @@ connections.<conn>.children.<child>.dpd_action = clear
Action to perform for this CHILD_SA on DPD timeout. The default _clear_
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
View File
@@ -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