diff options
Diffstat (limited to 'src')
45 files changed, 1356 insertions, 232 deletions
diff --git a/src/conftest/config.c b/src/conftest/config.c index 10b773de2..7f05e9c72 100644 --- a/src/conftest/config.c +++ b/src/conftest/config.c @@ -188,7 +188,7 @@ static child_cfg_t *load_child_config(private_config_t *this, enumerator = enumerator_create_token(token, ",", " "); while (enumerator->enumerate(enumerator, &token)) { - ts = traffic_selector_create_from_cidr(token, 0, 0); + ts = traffic_selector_create_from_cidr(token, 0, 0, 65535); if (ts) { child_cfg->add_traffic_selector(child_cfg, TRUE, ts); @@ -212,7 +212,7 @@ static child_cfg_t *load_child_config(private_config_t *this, enumerator = enumerator_create_token(token, ",", " "); while (enumerator->enumerate(enumerator, &token)) { - ts = traffic_selector_create_from_cidr(token, 0, 0); + ts = traffic_selector_create_from_cidr(token, 0, 0, 65535); if (ts) { child_cfg->add_traffic_selector(child_cfg, FALSE, ts); diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index bc25dcf21..6b8aa0f7f 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -513,6 +513,13 @@ if MONOLITHIC endif endif +if USE_SYSTIME_FIX + SUBDIRS += plugins/systime_fix +if MONOLITHIC + libcharon_la_LIBADD += plugins/systime_fix/libstrongswan-systime-fix.la +endif +endif + if USE_LED SUBDIRS += plugins/led if MONOLITHIC diff --git a/src/libcharon/encoding/payloads/traffic_selector_substructure.c b/src/libcharon/encoding/payloads/traffic_selector_substructure.c index 15f791b95..334823db9 100644 --- a/src/libcharon/encoding/payloads/traffic_selector_substructure.c +++ b/src/libcharon/encoding/payloads/traffic_selector_substructure.c @@ -114,7 +114,11 @@ METHOD(payload_t, verify, status_t, { if (this->start_port > this->end_port) { - return FAILED; + /* OPAQUE ports are the only exception */ + if (this->start_port != 0xffff && this->end_port != 0) + { + return FAILED; + } } switch (this->ts_type) { diff --git a/src/libcharon/plugins/load_tester/load_tester_config.c b/src/libcharon/plugins/load_tester/load_tester_config.c index 187946579..52afe1b0a 100644 --- a/src/libcharon/plugins/load_tester/load_tester_config.c +++ b/src/libcharon/plugins/load_tester/load_tester_config.c @@ -369,7 +369,7 @@ static void add_ts(char *string, child_cfg_t *cfg, bool local) if (string) { - ts = traffic_selector_create_from_cidr(string, 0, 0); + ts = traffic_selector_create_from_cidr(string, 0, 0, 65535); if (!ts) { DBG1(DBG_CFG, "parsing TS string '%s' failed", string); diff --git a/src/libcharon/plugins/medcli/medcli_config.c b/src/libcharon/plugins/medcli/medcli_config.c index d3bd29a1b..2bff70307 100644 --- a/src/libcharon/plugins/medcli/medcli_config.c +++ b/src/libcharon/plugins/medcli/medcli_config.c @@ -63,7 +63,7 @@ static traffic_selector_t *ts_from_string(char *str) { traffic_selector_t *ts; - ts = traffic_selector_create_from_cidr(str, 0, 0); + ts = traffic_selector_create_from_cidr(str, 0, 0, 65535); if (ts) { return ts; diff --git a/src/libcharon/plugins/stroke/stroke_config.c b/src/libcharon/plugins/stroke/stroke_config.c index 7bca94d43..5970e7cf3 100644 --- a/src/libcharon/plugins/stroke/stroke_config.c +++ b/src/libcharon/plugins/stroke/stroke_config.c @@ -448,25 +448,42 @@ static auth_cfg_t *build_auth_cfg(private_stroke_config_t *this, identity = identification_create_from_string(id); if (cert) { - certificate = this->cred->load_peer(this->cred, cert); - if (certificate) + enumerator_t *enumerator; + bool has_subject = FALSE; + certificate_t *first = NULL; + + enumerator = enumerator_create_token(cert, ",", " "); + while (enumerator->enumerate(enumerator, &cert)) { - if (local) - { - this->ca->check_for_hash_and_url(this->ca, certificate); - } - cfg->add(cfg, AUTH_RULE_SUBJECT_CERT, certificate); - if (identity->get_type(identity) == ID_ANY || - !certificate->has_subject(certificate, identity)) + certificate = this->cred->load_peer(this->cred, cert); + if (certificate) { - DBG1(DBG_CFG, " id '%Y' not confirmed by certificate, " - "defaulting to '%Y'", identity, - certificate->get_subject(certificate)); - identity->destroy(identity); - identity = certificate->get_subject(certificate); - identity = identity->clone(identity); + if (local) + { + this->ca->check_for_hash_and_url(this->ca, certificate); + } + cfg->add(cfg, AUTH_RULE_SUBJECT_CERT, certificate); + if (!first) + { + first = certificate; + } + if (identity->get_type(identity) != ID_ANY && + certificate->has_subject(certificate, identity)) + { + has_subject = TRUE; + } } } + enumerator->destroy(enumerator); + + if (first && !has_subject) + { + DBG1(DBG_CFG, " id '%Y' not confirmed by certificate, " + "defaulting to '%Y'", identity, first->get_subject(first)); + identity->destroy(identity); + identity = first->get_subject(first); + identity = identity->clone(identity); + } } if (identity->get_type(identity) != ID_ANY) { @@ -878,7 +895,7 @@ static void add_ts(private_stroke_config_t *this, if (end->tohost) { ts = traffic_selector_create_dynamic(end->protocol, - end->port ? end->port : 0, end->port ? end->port : 65535); + end->from_port, end->to_port); child_cfg->add_traffic_selector(child_cfg, local, ts); } else @@ -891,7 +908,7 @@ static void add_ts(private_stroke_config_t *this, if (net) { ts = traffic_selector_create_from_subnet(net, 0, end->protocol, - end->port); + end->from_port, end->to_port); child_cfg->add_traffic_selector(child_cfg, local, ts); } } @@ -903,8 +920,8 @@ static void add_ts(private_stroke_config_t *this, enumerator = enumerator_create_token(end->subnets, ",", " "); while (enumerator->enumerate(enumerator, &subnet)) { - ts = traffic_selector_create_from_cidr(subnet, - end->protocol, end->port); + ts = traffic_selector_create_from_cidr(subnet, end->protocol, + end->from_port, end->to_port); if (ts) { child_cfg->add_traffic_selector(child_cfg, local, ts); diff --git a/src/libcharon/plugins/systime_fix/Makefile.am b/src/libcharon/plugins/systime_fix/Makefile.am new file mode 100644 index 000000000..a1f843db7 --- /dev/null +++ b/src/libcharon/plugins/systime_fix/Makefile.am @@ -0,0 +1,15 @@ + +INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libhydra \ + -I$(top_srcdir)/src/libcharon + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-systime-fix.la +else +plugin_LTLIBRARIES = libstrongswan-systime-fix.la +endif + +libstrongswan_systime_fix_la_SOURCES = \ + systime_fix_validator.h systime_fix_validator.c \ + systime_fix_plugin.h systime_fix_plugin.c + +libstrongswan_systime_fix_la_LDFLAGS = -module -avoid-version diff --git a/src/libcharon/plugins/systime_fix/systime_fix_plugin.c b/src/libcharon/plugins/systime_fix/systime_fix_plugin.c new file mode 100644 index 000000000..7727ba03b --- /dev/null +++ b/src/libcharon/plugins/systime_fix/systime_fix_plugin.c @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "systime_fix_plugin.h" +#include "systime_fix_validator.h" + +#include <daemon.h> +#include <processing/jobs/callback_job.h> +#include <processing/jobs/delete_ike_sa_job.h> +#include <processing/jobs/rekey_ike_sa_job.h> + +#include <time.h> + +/** + * Defining _XOPEN_SOURCE is difficult with libstrongswan includes, + * declare function explicitly. + */ +char *strptime(const char *s, const char *format, struct tm *tm); + +typedef struct private_systime_fix_plugin_t private_systime_fix_plugin_t; + +/** + * Private data of systime_fix plugin + */ +struct private_systime_fix_plugin_t { + + /** + * Implements plugin interface + */ + systime_fix_plugin_t public; + + /** + * Certificate lifetime validator + */ + systime_fix_validator_t *validator; + + /** + * Interval we check for a now-valid system time, in seconds. 0 if disabled + */ + u_int interval; + + /** + * Timestamp where we start considering system time valid + */ + time_t threshold; + + /** + * Do we trigger reauth or delete when finding expired certificates? + */ + bool reauth; +}; + +METHOD(plugin_t, get_name, char*, + private_systime_fix_plugin_t *this) +{ + return "systime-fix"; +} + +METHOD(plugin_t, destroy, void, + private_systime_fix_plugin_t *this) +{ + if (this->validator) + { + lib->credmgr->remove_validator(lib->credmgr, &this->validator->validator); + this->validator->destroy(this->validator); + } + free(this); +} + +/** + * Check if all certificates associated to an IKE_SA have valid lifetimes + */ +static bool has_invalid_certs(ike_sa_t *ike_sa) +{ + enumerator_t *cfgs, *items; + certificate_t *cert; + auth_rule_t type; + auth_cfg_t *auth; + time_t not_before, not_after; + bool valid = TRUE; + + cfgs = ike_sa->create_auth_cfg_enumerator(ike_sa, FALSE); + while (valid && cfgs->enumerate(cfgs, &auth)) + { + items = auth->create_enumerator(auth); + while (valid && items->enumerate(items, &type, &cert)) + { + switch (type) + { + case AUTH_RULE_SUBJECT_CERT: + case AUTH_RULE_IM_CERT: + case AUTH_RULE_CA_CERT: + if (!cert->get_validity(cert, NULL, ¬_before, ¬_after)) + { + DBG1(DBG_CFG, "certificate '%Y' invalid " + "(valid from %T to %T)", cert->get_subject(cert), + ¬_before, FALSE, ¬_after, FALSE); + valid = FALSE; + } + break; + default: + break; + } + } + items->destroy(items); + } + cfgs->destroy(cfgs); + + if (valid) + { + DBG1(DBG_CFG, "all certificates have valid lifetimes"); + } + return !valid; +} + +/** + * Check system time, reevaluate certificates + */ +static job_requeue_t check_systime(private_systime_fix_plugin_t *this) +{ + enumerator_t *enumerator; + ike_sa_t *ike_sa; + char *action; + job_t *job; + + if (time(NULL) < this->threshold) + { + DBG2(DBG_CFG, "systime not valid, rechecking in %ds", this->interval); + lib->scheduler->schedule_job(lib->scheduler, (job_t*) + callback_job_create((callback_job_cb_t)check_systime, this, + NULL, NULL), this->interval); + return JOB_REQUEUE_NONE; + } + + DBG1(DBG_CFG, "system time got valid, rechecking certificates"); + + enumerator = charon->ike_sa_manager->create_enumerator( + charon->ike_sa_manager, TRUE); + while (enumerator->enumerate(enumerator, &ike_sa)) + { + if (has_invalid_certs(ike_sa)) + { + if (this->reauth) + { + action = "reauthenticating"; + job = &rekey_ike_sa_job_create(ike_sa->get_id(ike_sa), + TRUE)->job_interface; + } + else + { + action = "deleting"; + job = &delete_ike_sa_job_create(ike_sa->get_id(ike_sa), + TRUE)->job_interface; + } + DBG1(DBG_CFG, "%s[%d] has certificates not valid, %s IKE_SA", + ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa), + action); + lib->processor->queue_job(lib->processor, job); + } + } + enumerator->destroy(enumerator); + + return JOB_REQUEUE_NONE; +} + +/** + * Load cert lifetime validator configuration + */ +static bool load_validator(private_systime_fix_plugin_t *this) +{ + struct tm tm = { + .tm_mday = 1, + }; + char *str, *fmt; + + fmt = lib->settings->get_str(lib->settings, + "%s.plugins.%s.threshold_format", "%Y", charon->name, get_name(this)); + str = lib->settings->get_str(lib->settings, + "%s.plugins.%s.threshold", NULL, charon->name, get_name(this)); + if (!str) + { + DBG1(DBG_CFG, "no threshold configured for %s, disabled", + get_name(this)); + return FALSE; + } + if (strptime(str, fmt, &tm) == NULL) + { + DBG1(DBG_CFG, "threshold for %s invalid, disabled", get_name(this)); + return FALSE; + } + this->threshold = mktime(&tm); + if (this->threshold == -1) + { + DBG1(DBG_CFG, "converting threshold for %s failed, disabled", + get_name(this)); + return FALSE; + } + if (time(NULL) >= this->threshold) + { + DBG1(DBG_CFG, "system time looks good, disabling %s", get_name(this)); + return FALSE; + } + + DBG1(DBG_CFG, "enabling %s, threshold: %s", get_name(this), asctime(&tm)); + this->validator = systime_fix_validator_create(this->threshold); + lib->credmgr->add_validator(lib->credmgr, &this->validator->validator); + + return TRUE; +} + +/** + * Plugin constructor + */ +plugin_t *systime_fix_plugin_create() +{ + private_systime_fix_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .reload = (void*)return_false, + .destroy = _destroy, + }, + }, + .interval = lib->settings->get_int(lib->settings, + "%s.plugins.%s.interval", 0, charon->name, get_name(this)), + .reauth = lib->settings->get_bool(lib->settings, + "%s.plugins.%s.reauth", FALSE, charon->name, get_name(this)), + ); + + if (load_validator(this)) + { + if (this->interval != 0) + { + DBG1(DBG_CFG, "starting systime check, interval: %ds", + this->interval); + lib->scheduler->schedule_job(lib->scheduler, (job_t*) + callback_job_create((callback_job_cb_t)check_systime, this, + NULL, NULL), this->interval); + } + } + return &this->public.plugin; +} diff --git a/src/libcharon/plugins/systime_fix/systime_fix_plugin.h b/src/libcharon/plugins/systime_fix/systime_fix_plugin.h new file mode 100644 index 000000000..402659539 --- /dev/null +++ b/src/libcharon/plugins/systime_fix/systime_fix_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup systime_fix systime_fix + * @ingroup cplugins + * + * @defgroup systime_fix_plugin systime_fix_plugin + * @{ @ingroup systime_fix + */ + +#ifndef SYSTIME_FIX_PLUGIN_H_ +#define SYSTIME_FIX_PLUGIN_H_ + +#include <plugins/plugin.h> + +typedef struct systime_fix_plugin_t systime_fix_plugin_t; + +/** + * Plugin handling cert lifetimes gracefully if system time is out of sync. + */ +struct systime_fix_plugin_t { + + /** + * Implements plugin interface. + */ + plugin_t plugin; +}; + +#endif /** SYSTIME_FIX_PLUGIN_H_ @}*/ diff --git a/src/libcharon/plugins/systime_fix/systime_fix_validator.c b/src/libcharon/plugins/systime_fix/systime_fix_validator.c new file mode 100644 index 000000000..340e86cbc --- /dev/null +++ b/src/libcharon/plugins/systime_fix/systime_fix_validator.c @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "systime_fix_validator.h" + +#include <errno.h> +#include <time.h> + +#include <daemon.h> + +typedef struct private_systime_fix_validator_t private_systime_fix_validator_t; + +/** + * Private data of an systime_fix_validator_t object. + */ +struct private_systime_fix_validator_t { + + /** + * Public systime_fix_validator_t interface. + */ + systime_fix_validator_t public; + + /** + * Timestamp where we start to consider system time valid + */ + time_t threshold; +}; + +METHOD(cert_validator_t, check_lifetime, status_t, + private_systime_fix_validator_t *this, certificate_t *cert, + int pathlen, bool anchor, auth_cfg_t *auth) +{ + if (time(NULL) < this->threshold) + { + /* our system time seems to be invalid, accept certificate */ + if (pathlen) + { /* report only once per validated chain */ + DBG1(DBG_CFG, "system time out of sync, skipping certificate " + "lifetime check"); + } + return SUCCESS; + } + /* validate this certificate normally */ + return NEED_MORE; +} + +METHOD(systime_fix_validator_t, destroy, void, + private_systime_fix_validator_t *this) +{ + free(this); +} + +/** + * See header + */ +systime_fix_validator_t *systime_fix_validator_create(time_t threshold) +{ + private_systime_fix_validator_t *this; + + INIT(this, + .public = { + .validator = { + .check_lifetime = _check_lifetime, + }, + .destroy = _destroy, + }, + .threshold = threshold, + ); + + return &this->public; +} diff --git a/src/libcharon/plugins/systime_fix/systime_fix_validator.h b/src/libcharon/plugins/systime_fix/systime_fix_validator.h new file mode 100644 index 000000000..3e651fd91 --- /dev/null +++ b/src/libcharon/plugins/systime_fix/systime_fix_validator.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup systime_fix_validator systime_fix_validator + * @{ @ingroup systime_fix + */ + +#ifndef SYSTIME_FIX_VALIDATOR_H_ +#define SYSTIME_FIX_VALIDATOR_H_ + +#include <credentials/cert_validator.h> + +typedef struct systime_fix_validator_t systime_fix_validator_t; + +/** + * Validator that accepts cert lifetimes if system time is out of sync. + */ +struct systime_fix_validator_t { + + /** + * Implements cert_validator_t interface. + */ + cert_validator_t validator; + + /** + * Destroy a systime_fix_validator_t. + */ + void (*destroy)(systime_fix_validator_t *this); +}; + +/** + * Create a systime_fix_validator instance. + */ +systime_fix_validator_t *systime_fix_validator_create(); + +#endif /** SYSTIME_FIX_VALIDATOR_H_ @}*/ diff --git a/src/libcharon/plugins/uci/uci_config.c b/src/libcharon/plugins/uci/uci_config.c index 4d317b6cc..b58d120c1 100644 --- a/src/libcharon/plugins/uci/uci_config.c +++ b/src/libcharon/plugins/uci/uci_config.c @@ -89,7 +89,7 @@ static traffic_selector_t *create_ts(char *string) { traffic_selector_t *ts; - ts = traffic_selector_create_from_cidr(string, 0, 0); + ts = traffic_selector_create_from_cidr(string, 0, 0, 65535); if (ts) { return ts; diff --git a/src/libcharon/plugins/unity/unity_handler.c b/src/libcharon/plugins/unity/unity_handler.c index 31d13add2..9d145b93f 100644 --- a/src/libcharon/plugins/unity/unity_handler.c +++ b/src/libcharon/plugins/unity/unity_handler.c @@ -174,7 +174,6 @@ static job_requeue_t add_exclude_async(entry_t *entry) ike_sa_t *ike_sa; char name[128]; host_t *host; - bool has_vip = FALSE; ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager, entry->sa, FALSE); @@ -187,23 +186,21 @@ static job_requeue_t add_exclude_async(entry_t *entry) FALSE, 0, 0, NULL, NULL, FALSE); child_cfg->add_traffic_selector(child_cfg, FALSE, entry->ts->clone(entry->ts)); + host = ike_sa->get_my_host(ike_sa); + child_cfg->add_traffic_selector(child_cfg, TRUE, + traffic_selector_create_from_subnet(host->clone(host), + 32, 0, 0, 65535)); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE); while (enumerator->enumerate(enumerator, &host)) { - has_vip = TRUE; child_cfg->add_traffic_selector(child_cfg, TRUE, - traffic_selector_create_from_subnet(host->clone(host), 32, 0, 0)); + traffic_selector_create_from_subnet(host->clone(host), + 32, 0, 0, 65535)); } enumerator->destroy(enumerator); - if (!has_vip) - { - host = ike_sa->get_my_host(ike_sa); - child_cfg->add_traffic_selector(child_cfg, TRUE, - traffic_selector_create_from_subnet(host->clone(host), 32, 0, 0)); - } - charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); - charon->shunts->install(charon->shunts, child_cfg); child_cfg->destroy(child_cfg); diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 258e562d4..0157599c1 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -900,7 +900,7 @@ METHOD(ike_sa_t, update_hosts, void, else { /* update our address in any case */ - if (!me->equals(me, this->my_host)) + if (force && !me->equals(me, this->my_host)) { set_my_host(this, me->clone(me)); update = TRUE; @@ -909,7 +909,8 @@ METHOD(ike_sa_t, update_hosts, void, if (!other->equals(other, this->other_host)) { /* update others address if we are NOT NATed */ - if (force || !has_condition(this, COND_NAT_HERE)) + if ((has_condition(this, COND_NAT_THERE) && + !has_condition(this, COND_NAT_HERE)) || force ) { set_other_host(this, other->clone(other)); update = TRUE; @@ -1250,24 +1251,6 @@ METHOD(ike_sa_t, process_message, status_t, { /* do not handle messages in passive state */ return FAILED; } - switch (message->get_exchange_type(message)) - { - case ID_PROT: - case AGGRESSIVE: - case IKE_SA_INIT: - case IKE_AUTH: - if (this->state != IKE_CREATED && - this->state != IKE_CONNECTING && - message->get_first_payload_type(message) != FRAGMENT_V1) - { - DBG1(DBG_IKE, "ignoring %N in established IKE_SA state", - exchange_type_names, message->get_exchange_type(message)); - return FAILED; - } - break; - default: - break; - } if (message->get_major_version(message) != this->version) { DBG1(DBG_IKE, "ignoring %N IKEv%u exchange on %N SA", diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c index 2ac8c3123..4fbc4da8e 100644 --- a/src/libcharon/sa/ike_sa_manager.c +++ b/src/libcharon/sa/ike_sa_manager.c @@ -108,9 +108,9 @@ struct entry_t { identification_t *other_id; /** - * message ID currently processing, if any + * message ID or hash of currently processing message, -1 if none */ - u_int32_t message_id; + u_int32_t processing; }; /** @@ -135,23 +135,12 @@ static status_t entry_destroy(entry_t *this) */ static entry_t *entry_create() { - entry_t *this = malloc_thing(entry_t); - - this->waiting_threads = 0; - this->condvar = condvar_create(CONDVAR_TYPE_DEFAULT); - - /* we set checkout flag when we really give it out */ - this->checked_out = FALSE; - this->driveout_new_threads = FALSE; - this->driveout_waiting_threads = FALSE; - this->message_id = -1; - this->init_hash = chunk_empty; - this->other = NULL; - this->half_open = FALSE; - this->my_id = NULL; - this->other_id = NULL; - this->ike_sa_id = NULL; - this->ike_sa = NULL; + entry_t *this; + + INIT(this, + .condvar = condvar_create(CONDVAR_TYPE_DEFAULT), + .processing = -1, + ); return this; } @@ -1171,6 +1160,20 @@ METHOD(ike_sa_manager_t, checkout_new, ike_sa_t*, return ike_sa; } +/** + * Get the message ID or message hash to detect early retransmissions + */ +static u_int32_t get_message_id_or_hash(message_t *message) +{ + /* Use the message ID, or the message hash in IKEv1 Main/Aggressive mode */ + if (message->get_major_version(message) == IKEV1_MAJOR_VERSION && + message->get_message_id(message) == 0) + { + return chunk_hash(message->get_packet_data(message)); + } + return message->get_message_id(message); +} + METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*, private_ike_sa_manager_t* this, message_t *message) { @@ -1246,7 +1249,7 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*, entry->checked_out = TRUE; unlock_single_segment(this, segment); - entry->message_id = message->get_message_id(message); + entry->processing = get_message_id_or_hash(message); entry->init_hash = hash; DBG2(DBG_MGR, "created IKE_SA %s[%u]", @@ -1290,12 +1293,11 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*, if (get_entry_by_id(this, id, &entry, &segment) == SUCCESS) { - /* only check out in IKEv2 if we are not already processing it */ - if (message->get_request(message) && - message->get_message_id(message) == entry->message_id) + /* only check out if we are not already processing it. */ + if (entry->processing == get_message_id_or_hash(message)) { DBG1(DBG_MGR, "ignoring request with ID %u, already processing", - entry->message_id); + entry->processing); } else if (wait_for_entry(this, entry, segment)) { @@ -1305,7 +1307,7 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*, entry->checked_out = TRUE; if (message->get_first_payload_type(message) != FRAGMENT_V1) { - entry->message_id = message->get_message_id(message); + entry->processing = get_message_id_or_hash(message); } if (ike_id->get_responder_spi(ike_id) == 0) { @@ -1564,7 +1566,7 @@ METHOD(ike_sa_manager_t, checkin, void, entry->ike_sa_id->replace_values(entry->ike_sa_id, ike_sa->get_id(ike_sa)); /* signal waiting threads */ entry->checked_out = FALSE; - entry->message_id = -1; + entry->processing = -1; /* check if this SA is half-open */ if (entry->half_open && ike_sa->get_state(ike_sa) != IKE_CONNECTING) { @@ -1745,6 +1747,23 @@ METHOD(ike_sa_manager_t, create_id_enumerator, enumerator_t*, (void*)id_enumerator_cleanup, ids); } +/** + * Move all CHILD_SAs from old to new + */ +static void adopt_children(ike_sa_t *old, ike_sa_t *new) +{ + enumerator_t *enumerator; + child_sa_t *child_sa; + + enumerator = old->create_child_sa_enumerator(old); + while (enumerator->enumerate(enumerator, &child_sa)) + { + old->remove_child_sa(old, enumerator); + new->add_child_sa(new, child_sa); + } + enumerator->destroy(enumerator); +} + METHOD(ike_sa_manager_t, check_uniqueness, bool, private_ike_sa_manager_t *this, ike_sa_t *ike_sa, bool force_replace) { @@ -1782,6 +1801,7 @@ METHOD(ike_sa_manager_t, check_uniqueness, bool, { DBG1(DBG_IKE, "destroying duplicate IKE_SA for peer '%Y', " "received INITIAL_CONTACT", other); + charon->bus->ike_updown(charon->bus, duplicate, FALSE); checkin_and_destroy(this, duplicate); continue; } @@ -1796,6 +1816,10 @@ METHOD(ike_sa_manager_t, check_uniqueness, bool, { case UNIQUE_REPLACE: charon->bus->alert(charon->bus, ALERT_UNIQUE_REPLACE); + if (duplicate->get_version(duplicate) == IKEV1) + { + adopt_children(duplicate, ike_sa); + } DBG1(DBG_IKE, "deleting duplicate IKE_SA for peer " "'%Y' due to uniqueness policy", other); status = duplicate->delete(duplicate); diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 8a4761d5c..7a84d1cef 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -1471,6 +1471,21 @@ METHOD(task_manager_t, process_message, status_t, charon->bus->alert(charon->bus, ALERT_RETRANSMIT_RECEIVE, msg); return SUCCESS; } + + /* reject Main/Agressive Modes once established */ + if (msg->get_exchange_type(msg) == ID_PROT || + msg->get_exchange_type(msg) == AGGRESSIVE) + { + if (this->ike_sa->get_state(this->ike_sa) != IKE_CREATED && + this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING && + msg->get_first_payload_type(msg) != FRAGMENT_V1) + { + DBG1(DBG_IKE, "ignoring %N in established IKE_SA state", + exchange_type_names, msg->get_exchange_type(msg)); + return FAILED; + } + } + if (msg->get_exchange_type(msg) == TRANSACTION && this->active_tasks->get_count(this->active_tasks)) { /* main mode not yet complete, queue XAuth/Mode config tasks */ @@ -2030,4 +2045,3 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa) return &this->public; } - diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index 1eae6aa93..16c47630d 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -576,12 +576,12 @@ static bool get_ts(private_quick_mode_t *this, message_t *message) if (!tsi) { tsi = traffic_selector_create_from_subnet(hsi->clone(hsi), - hsi->get_family(hsi) == AF_INET ? 32 : 128, 0, 0); + hsi->get_family(hsi) == AF_INET ? 32 : 128, 0, 0, 65535); } if (!tsr) { tsr = traffic_selector_create_from_subnet(hsr->clone(hsr), - hsr->get_family(hsr) == AF_INET ? 32 : 128, 0, 0); + hsr->get_family(hsr) == AF_INET ? 32 : 128, 0, 0, 65535); } if (this->mode == MODE_TRANSPORT && this->udp && (!tsi->is_host(tsi, hsi) || !tsr->is_host(tsr, hsr))) diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index ea0117c54..29d8d830e 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -1123,6 +1123,18 @@ METHOD(task_manager_t, process_message, status_t, { if (mid == this->responding.mid) { + /* reject initial messages once established */ + if (msg->get_exchange_type(msg) == IKE_SA_INIT || + msg->get_exchange_type(msg) == IKE_AUTH) + { + if (this->ike_sa->get_state(this->ike_sa) != IKE_CREATED && + this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING) + { + DBG1(DBG_IKE, "ignoring %N in established IKE_SA state", + exchange_type_names, msg->get_exchange_type(msg)); + return FAILED; + } + } if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED || this->ike_sa->get_state(this->ike_sa) == IKE_CONNECTING || msg->get_exchange_type(msg) != IKE_SA_INIT) diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index eb3972c29..32c0e8c4a 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -18,6 +18,7 @@ #include "child_create.h" #include <daemon.h> +#include <hydra.h> #include <sa/ikev2/keymat_v2.h> #include <crypto/diffie_hellman.h> #include <credentials/certificates/x509.h> @@ -615,6 +616,7 @@ static void build_payloads(private_child_create_t *this, message_t *message) nonce_payload_t *nonce_payload; ke_payload_t *ke_payload; ts_payload_t *ts_payload; + kernel_feature_t features; /* add SA payload */ if (this->initiator) @@ -661,6 +663,13 @@ static void build_payloads(private_child_create_t *this, message_t *message) default: break; } + + features = hydra->kernel_interface->get_features(hydra->kernel_interface); + if (!(features & KERNEL_ESP_V3_TFC)) + { + message->add_notify(message, FALSE, ESP_TFC_PADDING_NOT_SUPPORTED, + chunk_empty); + } } /** diff --git a/src/libhydra/kernel/kernel_interface.c b/src/libhydra/kernel/kernel_interface.c index 8948e0561..b82fd6d3d 100644 --- a/src/libhydra/kernel/kernel_interface.c +++ b/src/libhydra/kernel/kernel_interface.c @@ -137,6 +137,22 @@ struct private_kernel_interface_t { bool ifaces_exclude; }; +METHOD(kernel_interface_t, get_features, kernel_feature_t, + private_kernel_interface_t *this) +{ + kernel_feature_t features = 0; + + if (this->ipsec && this->ipsec->get_features) + { + features |= this->ipsec->get_features(this->ipsec); + } + if (this->net && this->net->get_features) + { + features |= this->net->get_features(this->net); + } + return features; +} + METHOD(kernel_interface_t, get_spi, status_t, private_kernel_interface_t *this, host_t *src, host_t *dst, u_int8_t protocol, u_int32_t reqid, u_int32_t *spi) @@ -682,6 +698,7 @@ kernel_interface_t *kernel_interface_create() INIT(this, .public = { + .get_features = _get_features, .get_spi = _get_spi, .get_cpi = _get_cpi, .add_sa = _add_sa, @@ -757,4 +774,3 @@ kernel_interface_t *kernel_interface_create() return &this->public; } - diff --git a/src/libhydra/kernel/kernel_interface.h b/src/libhydra/kernel/kernel_interface.h index 8d8ef2e83..8d8d048d0 100644 --- a/src/libhydra/kernel/kernel_interface.h +++ b/src/libhydra/kernel/kernel_interface.h @@ -47,6 +47,7 @@ #define KERNEL_INTERFACE_H_ typedef struct kernel_interface_t kernel_interface_t; +typedef enum kernel_feature_t kernel_feature_t; #include <networking/host.h> #include <crypto/prf_plus.h> @@ -56,6 +57,17 @@ typedef struct kernel_interface_t kernel_interface_t; #include <kernel/kernel_net.h> /** + * Bitfield of optional features a kernel backend supports. + * + * This feature-set is for both, kernel_ipsec_t and kernel_net_t. Each + * backend returns a subset of these features. + */ +enum kernel_feature_t { + /** IPsec can process ESPv3 (RFC 4303) TFC padded packets */ + KERNEL_ESP_V3_TFC = (1<<0), +}; + +/** * Constructor function for ipsec kernel interface */ typedef kernel_ipsec_t* (*kernel_ipsec_constructor_t)(void); @@ -74,6 +86,13 @@ typedef kernel_net_t* (*kernel_net_constructor_t)(void); struct kernel_interface_t { /** + * Get the feature set supported by the net and ipsec kernel backends. + * + * @return ORed feature-set of backends + */ + kernel_feature_t (*get_features)(kernel_interface_t *this); + + /** * Get a SPI from the kernel. * * @param src source address of SA diff --git a/src/libhydra/kernel/kernel_ipsec.h b/src/libhydra/kernel/kernel_ipsec.h index 1da0805cb..c8afcaffd 100644 --- a/src/libhydra/kernel/kernel_ipsec.h +++ b/src/libhydra/kernel/kernel_ipsec.h @@ -30,6 +30,7 @@ typedef struct kernel_ipsec_t kernel_ipsec_t; #include <ipsec/ipsec_types.h> #include <selectors/traffic_selector.h> #include <plugins/plugin.h> +#include <kernel/kernel_interface.h> /** * Interface to the ipsec subsystem of the kernel. @@ -45,6 +46,13 @@ typedef struct kernel_ipsec_t kernel_ipsec_t; struct kernel_ipsec_t { /** + * Get the feature set supported by this kernel backend. + * + * @return ORed feature-set of backend + */ + kernel_feature_t (*get_features)(kernel_ipsec_t *this); + + /** * Get a SPI from the kernel. * * @param src source address of SA diff --git a/src/libhydra/kernel/kernel_net.h b/src/libhydra/kernel/kernel_net.h index 6a3b2cee7..0d3417f1d 100644 --- a/src/libhydra/kernel/kernel_net.h +++ b/src/libhydra/kernel/kernel_net.h @@ -28,6 +28,7 @@ typedef enum kernel_address_type_t kernel_address_type_t; #include <collections/enumerator.h> #include <networking/host.h> #include <plugins/plugin.h> +#include <kernel/kernel_interface.h> /** * Type of addresses (e.g. when enumerating them) @@ -56,6 +57,13 @@ enum kernel_address_type_t { struct kernel_net_t { /** + * Get the feature set supported by this kernel backend. + * + * @return ORed feature-set of backend + */ + kernel_feature_t (*get_features)(kernel_net_t *this); + + /** * Get our outgoing source address for a destination. * * Does a route lookup to get the source address used to reach dest. diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c index f9b2634a0..e81e55c24 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -787,7 +787,7 @@ static traffic_selector_t* selector2ts(struct xfrm_selector *sel, bool src) if (host) { return traffic_selector_create_from_subnet(host, prefixlen, - sel->proto, port); + sel->proto, port, port ?: 65535); } return NULL; } @@ -1036,6 +1036,12 @@ static job_requeue_t receive_events(private_kernel_netlink_ipsec_t *this) return JOB_REQUEUE_DIRECT; } +METHOD(kernel_ipsec_t, get_features, kernel_feature_t, + private_kernel_netlink_ipsec_t *this) +{ + return KERNEL_ESP_V3_TFC; +} + /** * Get an SPI for a specific protocol from the kernel. */ @@ -2196,14 +2202,15 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, /* install a route, if: * - this is a forward policy (to just get one for each child) - * - we are in tunnel/BEET mode + * - we are in tunnel/BEET mode or install a bypass policy * - routing is not disabled via strongswan.conf */ - if (policy->direction == POLICY_FWD && - ipsec->cfg.mode != MODE_TRANSPORT && this->install_routes) + if (policy->direction == POLICY_FWD && this->install_routes && + (mapping->type != POLICY_IPSEC || ipsec->cfg.mode != MODE_TRANSPORT)) { policy_sa_fwd_t *fwd = (policy_sa_fwd_t*)mapping; route_entry_t *route; + host_t *iface; INIT(route, .prefixlen = policy->sel.prefixlen_s, @@ -2219,9 +2226,17 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16); memcpy(route->dst_net.ptr, &policy->sel.saddr, route->dst_net.len); + /* get the interface to install the route for. If we have a local + * address, use it. Otherwise (for shunt policies) use the + * routes source address. */ + iface = ipsec->dst; + if (iface->is_anyaddr(iface)) + { + iface = route->src_ip; + } /* install route via outgoing interface */ if (!hydra->kernel_interface->get_interface(hydra->kernel_interface, - ipsec->dst, &route->if_name)) + iface, &route->if_name)) { this->mutex->unlock(this->mutex); route_entry_destroy(route); @@ -2734,6 +2749,7 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() INIT(this, .public = { .interface = { + .get_features = _get_features, .get_spi = _get_spi, .get_cpi = _get_cpi, .add_sa = _add_sa, @@ -2822,4 +2838,3 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() return &this->public; } - diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c index e47887859..b6df9879c 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c @@ -1757,6 +1757,10 @@ METHOD(kernel_net_t, add_ip, status_t, DBG2(DBG_KNL, "virtual IP %H installed on %s", virtual_ip, entry->iface->ifname); this->lock->unlock(this->lock); + /* during IKEv1 reauthentication, children get moved from + * old the new SA before the virtual IP is available. This + * kills the route for our virtual IP, reinstall. */ + queue_route_reinstall(this, entry->iface->ifname); return SUCCESS; } } diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index 71bdbbe2b..88b028447 100644 --- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -953,7 +953,8 @@ static traffic_selector_t* sadb_address2ts(struct sadb_address *address) ts = traffic_selector_create_from_subnet(host, address->sadb_address_prefixlen, address->sadb_address_proto, - host->get_port(host)); + host->get_port(host), + host->get_port(host) ?: 65535); return ts; } @@ -2654,4 +2655,3 @@ kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create() return &this->public; } - diff --git a/src/libstrongswan/credentials/auth_cfg.c b/src/libstrongswan/credentials/auth_cfg.c index a718de3dc..d2d0a7d72 100644 --- a/src/libstrongswan/credentials/auth_cfg.c +++ b/src/libstrongswan/credentials/auth_cfg.c @@ -76,7 +76,6 @@ static inline bool is_multi_value_rule(auth_rule_t type) case AUTH_RULE_AAA_IDENTITY: case AUTH_RULE_XAUTH_IDENTITY: case AUTH_RULE_XAUTH_BACKEND: - case AUTH_RULE_SUBJECT_CERT: case AUTH_HELPER_SUBJECT_CERT: case AUTH_HELPER_SUBJECT_HASH_URL: case AUTH_RULE_MAX: @@ -84,6 +83,7 @@ static inline bool is_multi_value_rule(auth_rule_t type) case AUTH_RULE_OCSP_VALIDATION: case AUTH_RULE_CRL_VALIDATION: case AUTH_RULE_GROUP: + case AUTH_RULE_SUBJECT_CERT: case AUTH_RULE_CA_CERT: case AUTH_RULE_IM_CERT: case AUTH_RULE_CERT_POLICY: @@ -503,8 +503,9 @@ METHOD(auth_cfg_t, complies, bool, private_auth_cfg_t *this, auth_cfg_t *constraints, bool log_error) { enumerator_t *e1, *e2; - bool success = TRUE, group_match = FALSE; + bool success = TRUE, group_match = FALSE, cert_match = FALSE; identification_t *require_group = NULL; + certificate_t *require_cert = NULL; signature_scheme_t scheme = SIGN_UNKNOWN; u_int strength = 0; auth_rule_t t1, t2; @@ -542,20 +543,21 @@ METHOD(auth_cfg_t, complies, bool, } case AUTH_RULE_SUBJECT_CERT: { - certificate_t *c1, *c2; + certificate_t *cert; - c1 = (certificate_t*)value; - c2 = get(this, AUTH_RULE_SUBJECT_CERT); - if (!c2 || !c1->equals(c1, c2)) + /* for certs, a match of a single cert is sufficient */ + require_cert = (certificate_t*)value; + + e2 = create_enumerator(this); + while (e2->enumerate(e2, &t2, &cert)) { - success = FALSE; - if (log_error) + if (t2 == AUTH_RULE_SUBJECT_CERT && + cert->equals(cert, require_cert)) { - DBG1(DBG_CFG, "constraint check failed: peer not " - "authenticated with peer cert '%Y'.", - c1->get_subject(c1)); + cert_match = TRUE; } } + e2->destroy(e2); break; } case AUTH_RULE_CRL_VALIDATION: @@ -828,6 +830,17 @@ METHOD(auth_cfg_t, complies, bool, } return FALSE; } + + if (require_cert && !cert_match) + { + if (log_error) + { + DBG1(DBG_CFG, "constraint check failed: peer not " + "authenticated with peer cert '%Y'.", + require_cert->get_subject(require_cert)); + } + return FALSE; + } return success; } @@ -999,14 +1012,15 @@ METHOD(auth_cfg_t, clone_, auth_cfg_t*, { enumerator_t *enumerator; auth_cfg_t *clone; - entry_t *entry; + auth_rule_t type; + void *value; clone = auth_cfg_create(); /* this enumerator skips duplicates for rules we expect only once */ - enumerator = this->entries->create_enumerator(this->entries); - while (enumerator->enumerate(enumerator, &entry)) + enumerator = create_enumerator(this); + while (enumerator->enumerate(enumerator, &type, &value)) { - switch (entry->type) + switch (type) { case AUTH_RULE_IDENTITY: case AUTH_RULE_EAP_IDENTITY: @@ -1014,8 +1028,8 @@ METHOD(auth_cfg_t, clone_, auth_cfg_t*, case AUTH_RULE_GROUP: case AUTH_RULE_XAUTH_IDENTITY: { - identification_t *id = (identification_t*)entry->value; - clone->add(clone, entry->type, id->clone(id)); + identification_t *id = (identification_t*)value; + clone->add(clone, type, id->clone(id)); break; } case AUTH_RULE_CA_CERT: @@ -1025,8 +1039,8 @@ METHOD(auth_cfg_t, clone_, auth_cfg_t*, case AUTH_HELPER_SUBJECT_CERT: case AUTH_HELPER_REVOCATION_CERT: { - certificate_t *cert = (certificate_t*)entry->value; - clone->add(clone, entry->type, cert->get_ref(cert)); + certificate_t *cert = (certificate_t*)value; + clone->add(clone, type, cert->get_ref(cert)); break; } case AUTH_RULE_XAUTH_BACKEND: @@ -1034,7 +1048,7 @@ METHOD(auth_cfg_t, clone_, auth_cfg_t*, case AUTH_HELPER_IM_HASH_URL: case AUTH_HELPER_SUBJECT_HASH_URL: { - clone->add(clone, entry->type, strdup(entry->value)); + clone->add(clone, type, strdup(value)); break; } case AUTH_RULE_IDENTITY_LOOSE: @@ -1046,7 +1060,7 @@ METHOD(auth_cfg_t, clone_, auth_cfg_t*, case AUTH_RULE_RSA_STRENGTH: case AUTH_RULE_ECDSA_STRENGTH: case AUTH_RULE_SIGNATURE_SCHEME: - clone->add(clone, entry->type, (uintptr_t)entry->value); + clone->add(clone, type, (uintptr_t)value); break; case AUTH_RULE_MAX: break; diff --git a/src/libstrongswan/credentials/cert_validator.h b/src/libstrongswan/credentials/cert_validator.h index 00e30d7a0..325fa0af3 100644 --- a/src/libstrongswan/credentials/cert_validator.h +++ b/src/libstrongswan/credentials/cert_validator.h @@ -35,6 +35,22 @@ typedef struct cert_validator_t cert_validator_t; struct cert_validator_t { /** + * Check the lifetime of a certificate. + * + * If this function returns SUCCESS or FAILED, the certificate lifetime is + * considered definitely (in-)valid, without asking other validators. + * If all registered validaters return NEED_MORE, the default + * lifetime check is performed. + * + * @param cert certificate to check lifetime + * @param pathlen the current length of the path bottom-up + * @param anchor is certificate trusted root anchor? + * @param auth container for resulting authentication info + * @return SUCCESS, FAILED or NEED_MORE to ask next validator + */ + status_t (*check_lifetime)(cert_validator_t *this, certificate_t *cert, + int pathlen, bool anchor, auth_cfg_t *auth); + /** * Validate a subject certificate in relation to its issuer. * * @param subject subject certificate to check @@ -43,6 +59,7 @@ struct cert_validator_t { * @param pathlen the current length of the path bottom-up * @param anchor is issuer trusted root anchor * @param auth container for resulting authentication info + * @return TRUE if subject certificate valid */ bool (*validate)(cert_validator_t *this, certificate_t *subject, certificate_t *issuer, bool online, u_int pathlen, diff --git a/src/libstrongswan/credentials/credential_manager.c b/src/libstrongswan/credentials/credential_manager.c index 9e40c5a10..f4cd9b9e6 100644 --- a/src/libstrongswan/credentials/credential_manager.c +++ b/src/libstrongswan/credentials/credential_manager.c @@ -515,32 +515,74 @@ static void cache_queue(private_credential_manager_t *this) } /** + * Use validators to check the lifetime of certificates + */ +static bool check_lifetime(private_credential_manager_t *this, + certificate_t *cert, char *label, + int pathlen, bool trusted, auth_cfg_t *auth) +{ + time_t not_before, not_after; + cert_validator_t *validator; + enumerator_t *enumerator; + status_t status = NEED_MORE; + + enumerator = this->validators->create_enumerator(this->validators); + while (enumerator->enumerate(enumerator, &validator)) + { + if (!validator->check_lifetime) + { + continue; + } + status = validator->check_lifetime(validator, cert, + pathlen, trusted, auth); + if (status != NEED_MORE) + { + break; + } + } + enumerator->destroy(enumerator); + + switch (status) + { + case NEED_MORE: + if (!cert->get_validity(cert, NULL, ¬_before, ¬_after)) + { + DBG1(DBG_CFG, "%s certificate invalid (valid from %T to %T)", + label, ¬_before, FALSE, ¬_after, FALSE); + return FALSE; + } + return TRUE; + case SUCCESS: + return TRUE; + case FAILED: + default: + return FALSE; + } +} + +/** * check a certificate for its lifetime */ static bool check_certificate(private_credential_manager_t *this, certificate_t *subject, certificate_t *issuer, bool online, int pathlen, bool trusted, auth_cfg_t *auth) { - time_t not_before, not_after; cert_validator_t *validator; enumerator_t *enumerator; - if (!subject->get_validity(subject, NULL, ¬_before, ¬_after)) + if (!check_lifetime(this, subject, "subject", pathlen, FALSE, auth) || + !check_lifetime(this, issuer, "issuer", pathlen + 1, trusted, auth)) { - DBG1(DBG_CFG, "subject certificate invalid (valid from %T to %T)", - ¬_before, FALSE, ¬_after, FALSE); - return FALSE; - } - if (!issuer->get_validity(issuer, NULL, ¬_before, ¬_after)) - { - DBG1(DBG_CFG, "issuer certificate invalid (valid from %T to %T)", - ¬_before, FALSE, ¬_after, FALSE); return FALSE; } enumerator = this->validators->create_enumerator(this->validators); while (enumerator->enumerate(enumerator, &validator)) { + if (!validator->validate) + { + continue; + } if (!validator->validate(validator, subject, issuer, online, pathlen, trusted, auth)) { @@ -1041,6 +1083,29 @@ static private_key_t *get_private_by_cert(private_credential_manager_t *this, return private; } +/** + * Move the actually used certificate to front, so it gets returned with get() + */ +static void prefer_cert(auth_cfg_t *auth, certificate_t *cert) +{ + enumerator_t *enumerator; + auth_rule_t rule; + certificate_t *current; + + enumerator = auth->create_enumerator(auth); + while (enumerator->enumerate(enumerator, &rule, ¤t)) + { + if (rule == AUTH_RULE_SUBJECT_CERT) + { + current->get_ref(current); + auth->replace(auth, enumerator, AUTH_RULE_SUBJECT_CERT, cert); + cert = current; + } + } + enumerator->destroy(enumerator); + auth->add(auth, AUTH_RULE_SUBJECT_CERT, cert); +} + METHOD(credential_manager_t, get_private, private_key_t*, private_credential_manager_t *this, key_type_t type, identification_t *id, auth_cfg_t *auth) @@ -1049,6 +1114,7 @@ METHOD(credential_manager_t, get_private, private_key_t*, certificate_t *cert; private_key_t *private = NULL; auth_cfg_t *trustchain; + auth_rule_t rule; /* check if this is a lookup by key ID, and do it if so */ if (id && id->get_type(id) == ID_KEY_ID) @@ -1062,7 +1128,35 @@ METHOD(credential_manager_t, get_private, private_key_t*, if (auth) { - /* if a specific certificate is preferred, check for a matching key */ + /* try to find a trustchain with one of the configured subject certs */ + enumerator = auth->create_enumerator(auth); + while (enumerator->enumerate(enumerator, &rule, &cert)) + { + if (rule == AUTH_RULE_SUBJECT_CERT) + { + private = get_private_by_cert(this, cert, type); + if (private) + { + trustchain = build_trustchain(this, cert, auth); + if (trustchain) + { + auth->merge(auth, trustchain, FALSE); + prefer_cert(auth, cert->get_ref(cert)); + trustchain->destroy(trustchain); + break; + } + private->destroy(private); + private = NULL; + } + } + } + enumerator->destroy(enumerator); + if (private) + { + return private; + } + + /* if none yielded a trustchain, enforce the first configured cert */ cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT); if (cert) { diff --git a/src/libstrongswan/crypto/crypto_tester.c b/src/libstrongswan/crypto/crypto_tester.c index 08b226468..12db0961b 100644 --- a/src/libstrongswan/crypto/crypto_tester.c +++ b/src/libstrongswan/crypto/crypto_tester.c @@ -451,7 +451,10 @@ METHOD(crypto_tester_t, test_aead, bool, failure: aead->destroy(aead); chunk_free(&cipher); - chunk_free(&plain); + if (plain.ptr != vector->plain) + { + chunk_free(&plain); + } if (failed) { DBG1(DBG_LIB, "disabled %N[%s]: %s test vector failed", diff --git a/src/libstrongswan/plugins/ccm/ccm_aead.h b/src/libstrongswan/plugins/ccm/ccm_aead.h index d5e302f94..79ab31804 100644 --- a/src/libstrongswan/plugins/ccm/ccm_aead.h +++ b/src/libstrongswan/plugins/ccm/ccm_aead.h @@ -42,8 +42,8 @@ struct ccm_aead_t { /** * Create a ccm_aead instance. * - * @param key_size key size in bytes * @param algo algorithm to implement, a CCM mode + * @param key_size key size in bytes * @return aead, NULL if not supported */ ccm_aead_t *ccm_aead_create(encryption_algorithm_t algo, size_t key_size); diff --git a/src/libstrongswan/plugins/gcm/gcm_aead.h b/src/libstrongswan/plugins/gcm/gcm_aead.h index db4be2442..846c3c76c 100644 --- a/src/libstrongswan/plugins/gcm/gcm_aead.h +++ b/src/libstrongswan/plugins/gcm/gcm_aead.h @@ -42,8 +42,8 @@ struct gcm_aead_t { /** * Create a gcm_aead instance. * - * @param key_size key size in bytes * @param algo algorithm to implement, a gcm mode + * @param key_size key size in bytes * @return aead, NULL if not supported */ gcm_aead_t *gcm_aead_create(encryption_algorithm_t algo, size_t key_size); diff --git a/src/libstrongswan/plugins/openssl/Makefile.am b/src/libstrongswan/plugins/openssl/Makefile.am index f971a5e08..e71567311 100644 --- a/src/libstrongswan/plugins/openssl/Makefile.am +++ b/src/libstrongswan/plugins/openssl/Makefile.am @@ -25,7 +25,8 @@ libstrongswan_openssl_la_SOURCES = \ openssl_crl.c openssl_crl.h \ openssl_pkcs7.c openssl_pkcs7.h \ openssl_rng.c openssl_rng.h \ - openssl_hmac.c openssl_hmac.h + openssl_hmac.c openssl_hmac.h \ + openssl_gcm.c openssl_gcm.h libstrongswan_openssl_la_LDFLAGS = -module -avoid-version libstrongswan_openssl_la_LIBADD = -lcrypto diff --git a/src/libstrongswan/plugins/openssl/openssl_gcm.c b/src/libstrongswan/plugins/openssl/openssl_gcm.c new file mode 100644 index 000000000..fde7ae71d --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_gcm.c @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "openssl_gcm.h" + +#include <openssl/evp.h> + +/** as defined in RFC 4106 */ +#define IV_LEN 8 +#define SALT_LEN 4 +#define NONCE_LEN (IV_LEN + SALT_LEN) + +typedef struct private_aead_t private_aead_t; + +/** + * Private data of aead_t + */ +struct private_aead_t { + + /** + * Public interface + */ + aead_t public; + + /** + * The encryption key + */ + chunk_t key; + + /** + * Salt value + */ + char salt[SALT_LEN]; + + /** + * Size of the integrity check value + */ + size_t icv_size; + + /** + * The cipher to use + */ + const EVP_CIPHER *cipher; +}; + +/** + * Do the actual en/decryption in an EVP context + */ +static bool crypt(private_aead_t *this, chunk_t data, chunk_t assoc, chunk_t iv, + u_char *out, int enc) +{ + EVP_CIPHER_CTX ctx; + u_char nonce[NONCE_LEN]; + bool success = FALSE; + int len; + + memcpy(nonce, this->salt, SALT_LEN); + memcpy(nonce + SALT_LEN, iv.ptr, IV_LEN); + + EVP_CIPHER_CTX_init(&ctx); + EVP_CIPHER_CTX_set_padding(&ctx, 0); + if (!EVP_CipherInit_ex(&ctx, this->cipher, NULL, NULL, NULL, enc) || + !EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_IVLEN, NONCE_LEN, NULL) || + !EVP_CipherInit_ex(&ctx, NULL, NULL, this->key.ptr, nonce, enc)) + { + goto done; + } + if (!enc && !EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_TAG, this->icv_size, + data.ptr + data.len)) + { /* set ICV for verification on decryption */ + goto done; + } + if (assoc.len && !EVP_CipherUpdate(&ctx, NULL, &len, assoc.ptr, assoc.len)) + { /* set AAD if specified */ + goto done; + } + if (!EVP_CipherUpdate(&ctx, out, &len, data.ptr, data.len) || + !EVP_CipherFinal_ex(&ctx, out + len, &len)) + { /* EVP_CipherFinal_ex fails if ICV is incorrect on decryption */ + goto done; + } + if (enc && !EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_GET_TAG, this->icv_size, + out + data.len)) + { /* copy back the ICV when encrypting */ + goto done; + } + success = TRUE; + +done: + EVP_CIPHER_CTX_cleanup(&ctx); + return success; +} + +METHOD(aead_t, encrypt, bool, + private_aead_t *this, chunk_t plain, chunk_t assoc, chunk_t iv, + chunk_t *encrypted) +{ + u_char *out; + + out = plain.ptr; + if (encrypted) + { + *encrypted = chunk_alloc(plain.len + this->icv_size); + out = encrypted->ptr; + } + return crypt(this, plain, assoc, iv, out, 1); +} + +METHOD(aead_t, decrypt, bool, + private_aead_t *this, chunk_t encrypted, chunk_t assoc, chunk_t iv, + chunk_t *plain) +{ + u_char *out; + + if (encrypted.len < this->icv_size) + { + return FALSE; + } + encrypted.len -= this->icv_size; + + out = encrypted.ptr; + if (plain) + { + *plain = chunk_alloc(encrypted.len); + out = plain->ptr; + } + return crypt(this, encrypted, assoc, iv, out, 0); +} + +METHOD(aead_t, get_block_size, size_t, + private_aead_t *this) +{ + return this->cipher->block_size; +} + +METHOD(aead_t, get_icv_size, size_t, + private_aead_t *this) +{ + return this->icv_size; +} + +METHOD(aead_t, get_iv_size, size_t, + private_aead_t *this) +{ + return IV_LEN; +} + +METHOD(aead_t, get_key_size, size_t, + private_aead_t *this) +{ + return this->key.len + SALT_LEN; +} + +METHOD(aead_t, set_key, bool, + private_aead_t *this, chunk_t key) +{ + if (key.len != get_key_size(this)) + { + return FALSE; + } + memcpy(this->salt, key.ptr + key.len - SALT_LEN, SALT_LEN); + memcpy(this->key.ptr, key.ptr, this->key.len); + return TRUE; +} + +METHOD(aead_t, destroy, void, + private_aead_t *this) +{ + chunk_clear(&this->key); + free(this); +} + +/* + * Described in header + */ +aead_t *openssl_gcm_create(encryption_algorithm_t algo, size_t key_size) +{ + private_aead_t *this; + + INIT(this, + .public = { + .encrypt = _encrypt, + .decrypt = _decrypt, + .get_block_size = _get_block_size, + .get_icv_size = _get_icv_size, + .get_iv_size = _get_iv_size, + .get_key_size = _get_key_size, + .set_key = _set_key, + .destroy = _destroy, + }, + ); + + switch (algo) + { + case ENCR_AES_GCM_ICV8: + this->icv_size = 8; + break; + case ENCR_AES_GCM_ICV12: + this->icv_size = 12; + break; + case ENCR_AES_GCM_ICV16: + this->icv_size = 16; + break; + default: + free(this); + return NULL; + } + + switch (algo) + { + case ENCR_AES_GCM_ICV8: + case ENCR_AES_GCM_ICV12: + case ENCR_AES_GCM_ICV16: + switch (key_size) + { + case 0: + key_size = 16; + /* FALL */ + case 16: + this->cipher = EVP_get_cipherbyname("aes-128-gcm"); + break; + case 24: + this->cipher = EVP_get_cipherbyname("aes-192-gcm"); + break; + case 32: + this->cipher = EVP_get_cipherbyname("aes-256-gcm"); + break; + default: + free(this); + return NULL; + } + break; + default: + free(this); + return NULL; + } + + if (!this->cipher) + { + free(this); + return NULL; + } + + this->key = chunk_alloc(key_size); + + return &this->public; +} diff --git a/src/libstrongswan/plugins/openssl/openssl_gcm.h b/src/libstrongswan/plugins/openssl/openssl_gcm.h new file mode 100644 index 000000000..12d2e8ab6 --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_gcm.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * Implements the aead_t interface using OpenSSL in GCM mode. + * + * @defgroup openssl_gcm openssl_gcm + * @{ @ingroup openssl_p + */ + +#ifndef OPENSSL_GCM_H_ +#define OPENSSL_GCM_H_ + +#include <crypto/aead.h> + +/** + * Constructor to create aead_t implementation. + * + * @param algo algorithm to implement + * @param key_size key size in bytes + * @return aead_t object, NULL if not supported + */ +aead_t *openssl_gcm_create(encryption_algorithm_t algo, size_t key_size); + +#endif /** OPENSSL_GCM_H_ @}*/ diff --git a/src/libstrongswan/plugins/openssl/openssl_plugin.c b/src/libstrongswan/plugins/openssl/openssl_plugin.c index 0f5b07137..282fe2b1b 100644 --- a/src/libstrongswan/plugins/openssl/openssl_plugin.c +++ b/src/libstrongswan/plugins/openssl/openssl_plugin.c @@ -43,6 +43,7 @@ #include "openssl_pkcs7.h" #include "openssl_rng.h" #include "openssl_hmac.h" +#include "openssl_gcm.h" typedef struct private_openssl_plugin_t private_openssl_plugin_t; @@ -304,6 +305,19 @@ METHOD(plugin_t, get_features, int, PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_512_256), #endif #endif /* OPENSSL_NO_HMAC */ +#ifndef OPENSSL_NO_AES + /* AES GCM */ + PLUGIN_REGISTER(AEAD, openssl_gcm_create), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV8, 16), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV8, 24), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV8, 32), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV12, 16), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV12, 24), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV12, 32), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV16, 16), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV16, 24), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV16, 32), +#endif /* OPENSSL_NO_AES */ #ifndef OPENSSL_NO_DH /* MODP DH groups */ PLUGIN_REGISTER(DH, openssl_diffie_hellman_create), diff --git a/src/libstrongswan/selectors/traffic_selector.c b/src/libstrongswan/selectors/traffic_selector.c index 87e57095c..ff8285f8c 100644 --- a/src/libstrongswan/selectors/traffic_selector.c +++ b/src/libstrongswan/selectors/traffic_selector.c @@ -174,7 +174,24 @@ static u_int8_t calc_netbits(private_traffic_selector_t *this) /** * internal generic constructor */ -static private_traffic_selector_t *traffic_selector_create(u_int8_t protocol, ts_type_t type, u_int16_t from_port, u_int16_t to_port); +static private_traffic_selector_t *traffic_selector_create(u_int8_t protocol, + ts_type_t type, u_int16_t from_port, u_int16_t to_port); + +/** + * Check if TS contains "opaque" ports + */ +static bool is_opaque(private_traffic_selector_t *this) +{ + return this->from_port == 0xffff && this->to_port == 0; +} + +/** + * Check if TS contains "any" ports + */ +static bool is_any(private_traffic_selector_t *this) +{ + return this->from_port == 0 && this->to_port == 0xffff; +} /** * Described in header. @@ -248,7 +265,7 @@ int traffic_selector_printf_hook(printf_hook_data_t *data, /* check if we have protocol and/or port selectors */ has_proto = this->protocol != 0; - has_ports = !(this->from_port == 0 && this->to_port == 0xFFFF); + has_ports = !is_any(this); if (!has_proto && !has_ports) { @@ -283,8 +300,9 @@ int traffic_selector_printf_hook(printf_hook_data_t *data, { if (this->from_port == this->to_port) { - struct servent *serv = getservbyport(htons(this->from_port), serv_proto); + struct servent *serv; + serv = getservbyport(htons(this->from_port), serv_proto); if (serv) { written += print_in_hook(data, "%s", serv->s_name); @@ -294,9 +312,14 @@ int traffic_selector_printf_hook(printf_hook_data_t *data, written += print_in_hook(data, "%d", this->from_port); } } + else if (is_opaque(this)) + { + written += print_in_hook(data, "OPAQUE"); + } else { - written += print_in_hook(data, "%d-%d", this->from_port, this->to_port); + written += print_in_hook(data, "%d-%d", + this->from_port, this->to_port); } } @@ -305,24 +328,55 @@ int traffic_selector_printf_hook(printf_hook_data_t *data, return written; } -/** - * Implements traffic_selector_t.get_subset - */ -static traffic_selector_t *get_subset(private_traffic_selector_t *this, private_traffic_selector_t *other) +METHOD(traffic_selector_t, get_subset, traffic_selector_t*, + private_traffic_selector_t *this, traffic_selector_t *other_public) { + private_traffic_selector_t *other, *subset; + u_int16_t from_port, to_port; + u_char *from, *to; + u_int8_t protocol; + size_t size; + + other = (private_traffic_selector_t*)other_public; + if (this->dynamic || other->dynamic) { /* no set_address() applied, TS has no subset */ return NULL; } - if (this->type == other->type && (this->protocol == other->protocol || - this->protocol == 0 || other->protocol == 0)) + + if (this->type != other->type) + { + return NULL; + } + switch (this->type) + { + case TS_IPV4_ADDR_RANGE: + size = sizeof(this->from4); + break; + case TS_IPV6_ADDR_RANGE: + size = sizeof(this->from6); + break; + default: + return NULL; + } + + if (this->protocol != other->protocol && + this->protocol != 0 && other->protocol != 0) { - u_int16_t from_port, to_port; - u_char *from, *to; - u_int8_t protocol; - size_t size; - private_traffic_selector_t *new_ts; + return NULL; + } + /* select protocol, which is not zero */ + protocol = max(this->protocol, other->protocol); + if ((is_opaque(this) && is_opaque(other)) || + (is_opaque(this) && is_any(other)) || + (is_opaque(other) && is_any(this))) + { + from_port = 0xffff; + to_port = 0; + } + else + { /* calculate the maximum port range allowed for both */ from_port = max(this->from_port, other->from_port); to_port = min(this->to_port, other->to_port); @@ -330,60 +384,46 @@ static traffic_selector_t *get_subset(private_traffic_selector_t *this, private_ { return NULL; } - /* select protocol, which is not zero */ - protocol = max(this->protocol, other->protocol); - - switch (this->type) - { - case TS_IPV4_ADDR_RANGE: - size = sizeof(this->from4); - break; - case TS_IPV6_ADDR_RANGE: - size = sizeof(this->from6); - break; - default: - return NULL; - } + } + /* get higher from-address */ + if (memcmp(this->from, other->from, size) > 0) + { + from = this->from; + } + else + { + from = other->from; + } + /* get lower to-address */ + if (memcmp(this->to, other->to, size) > 0) + { + to = other->to; + } + else + { + to = this->to; + } + /* if "from" > "to", we don't have a match */ + if (memcmp(from, to, size) > 0) + { + return NULL; + } - /* get higher from-address */ - if (memcmp(this->from, other->from, size) > 0) - { - from = this->from; - } - else - { - from = other->from; - } - /* get lower to-address */ - if (memcmp(this->to, other->to, size) > 0) - { - to = other->to; - } - else - { - to = this->to; - } - /* if "from" > "to", we don't have a match */ - if (memcmp(from, to, size) > 0) - { - return NULL; - } + /* we have a match in protocol, port, and address: return it... */ + subset = traffic_selector_create(protocol, this->type, from_port, to_port); + memcpy(subset->from, from, size); + memcpy(subset->to, to, size); + calc_netbits(subset); - /* we have a match in protocol, port, and address: return it... */ - new_ts = traffic_selector_create(protocol, this->type, from_port, to_port); - memcpy(new_ts->from, from, size); - memcpy(new_ts->to, to, size); - calc_netbits(new_ts); - return &new_ts->public; - } - return NULL; + return &subset->public; } -/** - * Implements traffic_selector_t.equals - */ -static bool equals(private_traffic_selector_t *this, private_traffic_selector_t *other) +METHOD(traffic_selector_t, equals, bool, + private_traffic_selector_t *this, traffic_selector_t *other_public) { + private_traffic_selector_t *other; + + other = (private_traffic_selector_t*)other_public; if (this->type != other->type) { return FALSE; @@ -535,11 +575,8 @@ METHOD(traffic_selector_t, set_address, void, } } -/** - * Implements traffic_selector_t.is_contained_in. - */ -static bool is_contained_in(private_traffic_selector_t *this, - private_traffic_selector_t *other) +METHOD(traffic_selector_t, is_contained_in, bool, + private_traffic_selector_t *this, traffic_selector_t *other) { private_traffic_selector_t *subset; bool contained_in = FALSE; @@ -548,7 +585,7 @@ static bool is_contained_in(private_traffic_selector_t *this, if (subset) { - if (equals(subset, this)) + if (equals(subset, &this->public)) { contained_in = TRUE; } @@ -739,12 +776,13 @@ traffic_selector_t *traffic_selector_create_from_rfc3779_format(ts_type_t type, * see header */ traffic_selector_t *traffic_selector_create_from_subnet(host_t *net, - u_int8_t netbits, u_int8_t protocol, u_int16_t port) + u_int8_t netbits, u_int8_t protocol, + u_int16_t from_port, u_int16_t to_port) { private_traffic_selector_t *this; chunk_t from; - this = traffic_selector_create(protocol, 0, 0, 65535); + this = traffic_selector_create(protocol, 0, from_port, to_port); switch (net->get_family(net)) { @@ -763,11 +801,6 @@ traffic_selector_t *traffic_selector_create_from_subnet(host_t *net, memcpy(this->from, from.ptr, from.len); netbits = min(netbits, this->type == TS_IPV4_ADDR_RANGE ? 32 : 128); calc_range(this, netbits); - if (port) - { - this->from_port = port; - this->to_port = port; - } net->destroy(net); return &this->public; @@ -818,8 +851,9 @@ traffic_selector_t *traffic_selector_create_from_string( /* * see header */ -traffic_selector_t *traffic_selector_create_from_cidr(char *string, - u_int8_t protocol, u_int16_t port) +traffic_selector_t *traffic_selector_create_from_cidr( + char *string, u_int8_t protocol, + u_int16_t from_port, u_int16_t to_port) { host_t *net; int bits; @@ -827,7 +861,8 @@ traffic_selector_t *traffic_selector_create_from_cidr(char *string, net = host_create_from_subnet(string, &bits); if (net) { - return traffic_selector_create_from_subnet(net, bits, protocol, port); + return traffic_selector_create_from_subnet(net, bits, protocol, + from_port, to_port); } return NULL; } @@ -859,8 +894,8 @@ static private_traffic_selector_t *traffic_selector_create(u_int8_t protocol, INIT(this, .public = { - .get_subset = (traffic_selector_t*(*)(traffic_selector_t*,traffic_selector_t*))get_subset, - .equals = (bool(*)(traffic_selector_t*,traffic_selector_t*))equals, + .get_subset = _get_subset, + .equals = _equals, .get_from_address = _get_from_address, .get_to_address = _get_to_address, .get_from_port = _get_from_port, @@ -869,7 +904,7 @@ static private_traffic_selector_t *traffic_selector_create(u_int8_t protocol, .get_protocol = _get_protocol, .is_host = _is_host, .is_dynamic = _is_dynamic, - .is_contained_in = (bool(*)(traffic_selector_t*,traffic_selector_t*))is_contained_in, + .is_contained_in = _is_contained_in, .includes = _includes, .set_address = _set_address, .to_subnet = _to_subnet, @@ -884,4 +919,3 @@ static private_traffic_selector_t *traffic_selector_create(u_int8_t protocol, return this; } - diff --git a/src/libstrongswan/selectors/traffic_selector.h b/src/libstrongswan/selectors/traffic_selector.h index b6da391aa..0de358b99 100644 --- a/src/libstrongswan/selectors/traffic_selector.h +++ b/src/libstrongswan/selectors/traffic_selector.h @@ -238,11 +238,13 @@ traffic_selector_t *traffic_selector_create_from_string( * * @param string CIDR string, such as 10.1.0.0/16 * @param protocol protocol for this ts, such as TCP or UDP - * @param port single port for this TS, 0 for any port + * @param from_port start of allowed port range + * @param to_port end of port range * @return traffic selector, NULL if string invalid */ -traffic_selector_t *traffic_selector_create_from_cidr(char *string, - u_int8_t protocol, u_int16_t port); +traffic_selector_t *traffic_selector_create_from_cidr( + char *string, u_int8_t protocol, + u_int16_t from_port, u_int16_t to_port); /** * Create a new traffic selector using data read from the net. @@ -288,14 +290,15 @@ traffic_selector_t *traffic_selector_create_from_rfc3779_format(ts_type_t type, * @param net subnet to use * @param netbits size of the subnet, as used in e.g. 192.168.0.0/24 notation * @param protocol protocol for this ts, such as TCP or UDP - * @param port port number, host order + * @param from_port start of allowed port range + * @param to_port end of port range * @return * - traffic_selector_t object * - NULL if address family of net not supported */ traffic_selector_t *traffic_selector_create_from_subnet( - host_t *net, u_int8_t netbits, - u_int8_t protocol, u_int16_t port); + host_t *net, u_int8_t netbits, u_int8_t protocol, + u_int16_t from_port, u_int16_t to_port); /** * Create a traffic selector for host-to-host cases. diff --git a/src/libstrongswan/utils/capabilities.c b/src/libstrongswan/utils/capabilities.c index c36a76efe..44a14496c 100644 --- a/src/libstrongswan/utils/capabilities.c +++ b/src/libstrongswan/utils/capabilities.c @@ -29,7 +29,9 @@ #include <utils/debug.h> -#if !defined(HAVE_GETPWNAM_R) || !defined(HAVE_GETGRNAM_R) +#if !defined(HAVE_GETPWNAM_R) || \ + !defined(HAVE_GETGRNAM_R) || \ + !defined(HAVE_GETPWUID_R) # include <threading/mutex.h> # define EMULATE_R_FUNCS #endif @@ -188,6 +190,34 @@ METHOD(capabilities_t, resolve_gid, bool, return FALSE; } +/** + * Initialize supplementary groups for unprivileged user + */ +static bool init_supplementary_groups(private_capabilities_t *this) +{ + struct passwd *pwp; + int res = -1; + +#ifdef HAVE_GETPWUID_R + struct passwd pwd; + char buf[1024]; + + if (getpwuid_r(this->uid, &pwd, buf, sizeof(buf), &pwp) == 0 && pwp) + { + res = initgroups(pwp->pw_name, this->gid); + } +#else /* HAVE_GETPWUID_R */ + this->mutex->lock(this->mutex); + pwp = getpwuid(this->uid); + if (pwp) + { + res = initgroups(pwp->pw_name, this->gid); + } + this->mutex->unlock(this->mutex); +#endif /* HAVE_GETPWUID_R */ + return res == 0; +} + METHOD(capabilities_t, drop, bool, private_capabilities_t *this) { @@ -195,6 +225,12 @@ METHOD(capabilities_t, drop, bool, prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); #endif + if (!init_supplementary_groups(this)) + { + DBG1(DBG_LIB, "initializing supplementary groups for %u failed", + this->uid); + return FALSE; + } if (this->gid && setgid(this->gid) != 0) { DBG1(DBG_LIB, "change to unprivileged group %u failed: %s", diff --git a/src/starter/cmp.c b/src/starter/cmp.c index aaba7b11d..cea864a4a 100644 --- a/src/starter/cmp.c +++ b/src/starter/cmp.c @@ -27,7 +27,8 @@ static bool starter_cmp_end(starter_end_t *c1, starter_end_t *c2) return FALSE; VARCMP(modecfg); - VARCMP(port); + VARCMP(from_port); + VARCMP(to_port); VARCMP(protocol); return cmp_args(KW_END_FIRST, KW_END_LAST, (char *)c1, (char *)c2); @@ -63,4 +64,3 @@ bool starter_cmp_ca(starter_ca_t *c1, starter_ca_t *c2) return cmp_args(KW_CA_NAME, KW_CA_LAST, (char *)c1, (char *)c2); } - diff --git a/src/starter/confread.c b/src/starter/confread.c index fecb998df..883534aad 100644 --- a/src/starter/confread.c +++ b/src/starter/confread.c @@ -142,6 +142,9 @@ static void default_values(starter_config_t *cfg) cfg->conn_default.left.ikeport = 500; cfg->conn_default.right.ikeport = 500; + cfg->conn_default.left.to_port = 0xffff; + cfg->conn_default.right.to_port = 0xffff; + cfg->ca_default.seen = SEEN_NONE; } @@ -292,24 +295,46 @@ static void kw_end(starter_conn_t *conn, starter_end_t *end, kw_token_t token, } if (streq(port, "%any")) { - end->port = 0; + end->from_port = 0; + end->to_port = 0xffff; } - else + else if (streq(port, "%opaque")) + { + end->from_port = 0xffff; + end->to_port = 0; + } + else if (*port) { svc = getservbyname(port, NULL); if (svc) { - end->port = ntohs(svc->s_port); + end->from_port = end->to_port = ntohs(svc->s_port); } else { p = strtol(port, &endptr, 0); - if ((*port && *endptr) || p < 0 || p > 0xffff) + if (p < 0 || p > 0xffff) + { + DBG1(DBG_APP, "# bad port: %s=%s", name, port); + goto err; + } + end->from_port = p; + if (*endptr == '-') + { + port = endptr + 1; + p = strtol(port, &endptr, 0); + if (p < 0 || p > 0xffff) + { + DBG1(DBG_APP, "# bad port: %s=%s", name, port); + goto err; + } + } + end->to_port = p; + if (*endptr) { - DBG1(DBG_APP, "# bad port: %s=%s", name, value); + DBG1(DBG_APP, "# bad port: %s=%s", name, port); goto err; } - end->port = (u_int16_t)p; } } if (sep) diff --git a/src/starter/confread.h b/src/starter/confread.h index 8dd56080d..0690bed4e 100644 --- a/src/starter/confread.h +++ b/src/starter/confread.h @@ -115,7 +115,8 @@ struct starter_end { bool hostaccess; bool allow_any; char *updown; - u_int16_t port; + u_int16_t from_port; + u_int16_t to_port; u_int8_t protocol; char *sourceip; char *dns; diff --git a/src/starter/starterstroke.c b/src/starter/starterstroke.c index 9d096453a..cc447c41f 100644 --- a/src/starter/starterstroke.c +++ b/src/starter/starterstroke.c @@ -146,7 +146,8 @@ static void starter_stroke_add_end(stroke_msg_t *msg, stroke_end_t *msg_end, sta msg_end->tohost = !conn_end->subnet; msg_end->allow_any = conn_end->allow_any; msg_end->protocol = conn_end->protocol; - msg_end->port = conn_end->port; + msg_end->from_port = conn_end->from_port; + msg_end->to_port = conn_end->to_port; } int starter_stroke_add_conn(starter_config_t *cfg, starter_conn_t *conn) diff --git a/src/stroke/stroke.c b/src/stroke/stroke.c index e289296c1..c2a505141 100644 --- a/src/stroke/stroke.c +++ b/src/stroke/stroke.c @@ -151,12 +151,14 @@ static int add_connection(char *name, msg.add_conn.me.ikeport = 500; msg.add_conn.me.subnets = push_string(&msg, my_nets); msg.add_conn.me.sendcert = 1; + msg.add_conn.me.to_port = 65535; msg.add_conn.other.id = push_string(&msg, other_id); msg.add_conn.other.address = push_string(&msg, other_addr); msg.add_conn.other.ikeport = 500; msg.add_conn.other.subnets = push_string(&msg, other_nets); msg.add_conn.other.sendcert = 1; + msg.add_conn.other.to_port = 65535; return send_stroke_msg(&msg); } diff --git a/src/stroke/stroke_msg.h b/src/stroke/stroke_msg.h index b2df887ae..a9c6f2369 100644 --- a/src/stroke/stroke_msg.h +++ b/src/stroke/stroke_msg.h @@ -167,7 +167,8 @@ struct stroke_end_t { int tohost; int allow_any; u_int8_t protocol; - u_int16_t port; + u_int16_t from_port; + u_int16_t to_port; }; typedef struct stroke_msg_t stroke_msg_t; |