diff --git a/NEWS b/NEWS index e4f91a7036..6e11a9495b 100644 --- a/NEWS +++ b/NEWS @@ -40,6 +40,10 @@ strongswan-5.2.0 - Implemented the PT-EAP transport protocol (RFC 7171) for Trusted Network Connect. +- The ipsec.conf replay_window option defines connection specific IPsec replay + windows. Original patch courtesy of Zheng Zhong and Christophe Gouault from + 6Wind. + strongswan-5.1.3 ---------------- diff --git a/src/charon-tkm/src/tkm/tkm_kernel_ipsec.c b/src/charon-tkm/src/tkm/tkm_kernel_ipsec.c index 72c247da54..dbeea93f28 100644 --- a/src/charon-tkm/src/tkm/tkm_kernel_ipsec.c +++ b/src/charon-tkm/src/tkm/tkm_kernel_ipsec.c @@ -91,8 +91,9 @@ METHOD(kernel_ipsec_t, add_sa, status_t, private_tkm_kernel_ipsec_t *this, host_t *src, host_t *dst, u_int32_t spi, u_int8_t protocol, u_int32_t reqid, mark_t mark, u_int32_t tfc, lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key, - u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp, - u_int16_t cpi, bool _initiator, bool encap, bool esn, bool inbound, + u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, + u_int16_t ipcomp, u_int16_t cpi, u_int32_t replay_window, + bool _initiator, bool encap, bool esn, bool inbound, traffic_selector_t* src_ts, traffic_selector_t* dst_ts) { esa_info_t esa; diff --git a/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c b/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c index 48f1487704..b1daaf0525 100644 --- a/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c +++ b/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c @@ -64,8 +64,9 @@ METHOD(kernel_ipsec_t, add_sa, status_t, private_kernel_android_ipsec_t *this, host_t *src, host_t *dst, u_int32_t spi, u_int8_t protocol, u_int32_t reqid, mark_t mark, u_int32_t tfc, lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key, - u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp, - u_int16_t cpi, bool initiator, bool encap, bool esn, bool inbound, + u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, + u_int16_t ipcomp, u_int16_t cpi, u_int32_t replay_window, + bool initiator, bool encap, bool esn, bool inbound, traffic_selector_t *src_ts, traffic_selector_t *dst_ts) { return ipsec->sas->add_sa(ipsec->sas, src, dst, spi, protocol, reqid, mark, diff --git a/src/libcharon/config/child_cfg.c b/src/libcharon/config/child_cfg.c index 3f07b5860d..7e4a1433d2 100644 --- a/src/libcharon/config/child_cfg.c +++ b/src/libcharon/config/child_cfg.c @@ -27,6 +27,9 @@ ENUM(action_names, ACTION_NONE, ACTION_RESTART, "restart", ); +/** Default replay window size, if not set using charon.replay_window */ +#define DEFAULT_REPLAY_WINDOW 32 + typedef struct private_child_cfg_t private_child_cfg_t; /** @@ -138,6 +141,11 @@ struct private_child_cfg_t { * enable installation and removal of kernel IPsec policies */ bool install_policy; + + /** + * anti-replay window size + */ + u_int32_t replay_window; }; METHOD(child_cfg_t, get_name, char*, @@ -481,6 +489,18 @@ METHOD(child_cfg_t, get_tfc, u_int32_t, return this->tfc; } +METHOD(child_cfg_t, get_replay_window, u_int32_t, + private_child_cfg_t *this) +{ + return this->replay_window; +} + +METHOD(child_cfg_t, set_replay_window, void, + private_child_cfg_t *this, u_int32_t replay_window) +{ + this->replay_window = replay_window; +} + METHOD(child_cfg_t, set_mipv6_options, void, private_child_cfg_t *this, bool proxy_mode, bool install_policy) { @@ -558,6 +578,8 @@ child_cfg_t *child_cfg_create(char *name, lifetime_cfg_t *lifetime, .get_reqid = _get_reqid, .get_mark = _get_mark, .get_tfc = _get_tfc, + .get_replay_window = _get_replay_window, + .set_replay_window = _set_replay_window, .use_proxy_mode = _use_proxy_mode, .install_policy = _install_policy, .get_ref = _get_ref, @@ -580,6 +602,8 @@ child_cfg_t *child_cfg_create(char *name, lifetime_cfg_t *lifetime, .my_ts = linked_list_create(), .other_ts = linked_list_create(), .tfc = tfc, + .replay_window = lib->settings->get_int(lib->settings, + "%s.replay_window", DEFAULT_REPLAY_WINDOW, lib->ns), ); if (mark_in) diff --git a/src/libcharon/config/child_cfg.h b/src/libcharon/config/child_cfg.h index 43ad1c5c89..9f7a92b702 100644 --- a/src/libcharon/config/child_cfg.h +++ b/src/libcharon/config/child_cfg.h @@ -234,6 +234,20 @@ struct child_cfg_t { */ u_int32_t (*get_tfc)(child_cfg_t *this); + /** + * Get anti-replay window size + * + * @return anti-replay window size + */ + u_int32_t (*get_replay_window)(child_cfg_t *this); + + /** + * Set anti-replay window size + * + * @param window anti-replay window size + */ + void (*set_replay_window)(child_cfg_t *this, u_int32_t window); + /** * Sets two options needed for Mobile IPv6 interoperability. * diff --git a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c index b33580700a..6f137b5f5d 100644 --- a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c +++ b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c @@ -252,8 +252,9 @@ METHOD(kernel_ipsec_t, add_sa, status_t, private_kernel_libipsec_ipsec_t *this, host_t *src, host_t *dst, u_int32_t spi, u_int8_t protocol, u_int32_t reqid, mark_t mark, u_int32_t tfc, lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key, - u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp, - u_int16_t cpi, bool initiator, bool encap, bool esn, bool inbound, + u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, + u_int16_t ipcomp, u_int16_t cpi, u_int32_t replay_window, + bool initiator, bool encap, bool esn, bool inbound, traffic_selector_t *src_ts, traffic_selector_t *dst_ts) { return ipsec->sas->add_sa(ipsec->sas, src, dst, spi, protocol, reqid, mark, diff --git a/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c index 62b3ea8dd1..cc1cae6d48 100644 --- a/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c +++ b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c @@ -1947,8 +1947,9 @@ METHOD(kernel_ipsec_t, add_sa, status_t, private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst, u_int32_t spi, u_int8_t protocol, u_int32_t reqid, mark_t mark, u_int32_t tfc, lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key, - u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp, - u_int16_t cpi, bool initiator, bool encap, bool esn, bool inbound, + u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, + u_int16_t ipcomp, u_int16_t cpi, u_int32_t replay_window, + bool initiator, bool encap, bool esn, bool inbound, traffic_selector_t *src_ts, traffic_selector_t *dst_ts) { host_t *local, *remote; diff --git a/src/libcharon/plugins/load_tester/load_tester_ipsec.c b/src/libcharon/plugins/load_tester/load_tester_ipsec.c index 5edd3b82d6..3f256ddd04 100644 --- a/src/libcharon/plugins/load_tester/load_tester_ipsec.c +++ b/src/libcharon/plugins/load_tester/load_tester_ipsec.c @@ -53,8 +53,9 @@ METHOD(kernel_ipsec_t, add_sa, status_t, private_load_tester_ipsec_t *this, host_t *src, host_t *dst, u_int32_t spi, u_int8_t protocol, u_int32_t reqid, mark_t mark, u_int32_t tfc, lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key, - u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp, - u_int16_t cpi, bool initiator, bool encap, bool esn, bool inbound, + u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, + u_int16_t ipcomp, u_int16_t cpi, u_int32_t replay_window, + bool initiator, bool encap, bool esn, bool inbound, traffic_selector_t *src_ts, traffic_selector_t *dst_ts) { return SUCCESS; diff --git a/src/libcharon/plugins/stroke/stroke_config.c b/src/libcharon/plugins/stroke/stroke_config.c index df15a16087..62967b006b 100644 --- a/src/libcharon/plugins/stroke/stroke_config.c +++ b/src/libcharon/plugins/stroke/stroke_config.c @@ -1151,6 +1151,10 @@ static child_cfg_t *build_child_cfg(private_stroke_config_t *this, map_action(msg->add_conn.close_action), msg->add_conn.ipcomp, msg->add_conn.inactivity, msg->add_conn.reqid, &mark_in, &mark_out, msg->add_conn.tfc); + if (msg->add_conn.replay_window != -1) + { + child_cfg->set_replay_window(child_cfg, msg->add_conn.replay_window); + } child_cfg->set_mipv6_options(child_cfg, msg->add_conn.proxy_mode, msg->add_conn.install_policy); add_ts(this, &msg->add_conn.me, child_cfg, TRUE); diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index 2319bddafb..113d480842 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -40,6 +40,11 @@ */ #define LFT_DEFAULT_CHILD_REKEY (1 * 60 * 60) +/** + * Undefined replay window + */ +#define REPLAY_UNDEFINED (~(u_int32_t)0) + typedef struct private_vici_config_t private_vici_config_t; /** @@ -357,6 +362,7 @@ typedef struct { bool ipcomp; bool route; ipsec_mode_t mode; + u_int32_t replay_window; action_t dpd_action; action_t start_action; action_t close_action; @@ -389,6 +395,10 @@ static void log_child_data(child_data_t *data, char *name) DBG2(DBG_CFG, " hostaccess = %u", data->hostaccess); DBG2(DBG_CFG, " ipcomp = %u", data->ipcomp); DBG2(DBG_CFG, " mode = %N", ipsec_mode_names, data->mode); + if (data->replay_window != REPLAY_UNDEFINED) + { + DBG2(DBG_CFG, " replay_window = %u", data->replay_window); + } DBG2(DBG_CFG, " dpd_action = %N", action_names, data->dpd_action); DBG2(DBG_CFG, " start_action = %N", action_names, data->start_action); DBG2(DBG_CFG, " close_action = %N", action_names, data->close_action); @@ -1202,6 +1212,7 @@ CALLBACK(child_kv, bool, { "updown", parse_string, &child->updown }, { "hostaccess", parse_bool, &child->hostaccess }, { "mode", parse_mode, &child->mode }, + { "replay_window", parse_uint32, &child->replay_window }, { "rekey_time", parse_time, &child->lft.time.rekey }, { "life_time", parse_time, &child->lft.time.life }, { "rand_time", parse_time, &child->lft.time.jitter }, @@ -1308,6 +1319,7 @@ CALLBACK(children_sn, bool, .local_ts = linked_list_create(), .remote_ts = linked_list_create(), .mode = MODE_TUNNEL, + .replay_window = REPLAY_UNDEFINED, .dpd_action = ACTION_NONE, .start_action = ACTION_NONE, .close_action = ACTION_NONE, @@ -1399,6 +1411,10 @@ CALLBACK(children_sn, bool, child.inactivity, child.reqid, &child.mark_in, &child.mark_out, child.tfc); + if (child.replay_window != REPLAY_UNDEFINED) + { + cfg->set_replay_window(cfg, child.replay_window); + } while (child.local_ts->remove_first(child.local_ts, (void**)&ts) == SUCCESS) { diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index a7d7b7305d..bcb0ca20f0 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -639,6 +639,7 @@ METHOD(child_sa_t, install, status_t, host_t *src, *dst; status_t status; bool update = FALSE; + u_int32_t replay_window = 0; /* now we have to decide which spi to use. Use self allocated, if "in", * or the one in the proposal, if not "in" (others). Additionally, @@ -653,6 +654,9 @@ METHOD(child_sa_t, install, status_t, } this->my_spi = spi; this->my_cpi = cpi; + + /* required on inbound SA only */ + replay_window = this->config->get_replay_window(this->config); } else { @@ -722,8 +726,8 @@ METHOD(child_sa_t, install, status_t, src, dst, spi, proto_ike2ip(this->protocol), this->reqid, inbound ? this->mark_in : this->mark_out, tfc, lifetime, enc_alg, encr, int_alg, integ, this->mode, - this->ipcomp, cpi, initiator, this->encap, esn, update, - src_ts, dst_ts); + this->ipcomp, cpi, replay_window, initiator, this->encap, + esn, update, src_ts, dst_ts); free(lifetime); diff --git a/src/libhydra/kernel/kernel_interface.c b/src/libhydra/kernel/kernel_interface.c index 3e34d20a61..c9379dca9f 100644 --- a/src/libhydra/kernel/kernel_interface.c +++ b/src/libhydra/kernel/kernel_interface.c @@ -179,8 +179,9 @@ METHOD(kernel_interface_t, add_sa, status_t, private_kernel_interface_t *this, host_t *src, host_t *dst, u_int32_t spi, u_int8_t protocol, u_int32_t reqid, mark_t mark, u_int32_t tfc, lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key, - u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp, - u_int16_t cpi, bool initiator, bool encap, bool esn, bool inbound, + u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, + u_int16_t ipcomp, u_int16_t cpi, u_int32_t replay_window, + bool initiator, bool encap, bool esn, bool inbound, traffic_selector_t *src_ts, traffic_selector_t *dst_ts) { if (!this->ipsec) @@ -188,8 +189,9 @@ METHOD(kernel_interface_t, add_sa, status_t, return NOT_SUPPORTED; } return this->ipsec->add_sa(this->ipsec, src, dst, spi, protocol, reqid, - mark, tfc, lifetime, enc_alg, enc_key, int_alg, int_key, mode, - ipcomp, cpi, initiator, encap, esn, inbound, src_ts, dst_ts); + mark, tfc, lifetime, enc_alg, enc_key, int_alg, int_key, mode, + ipcomp, cpi, replay_window, initiator, encap, esn, inbound, + src_ts, dst_ts); } METHOD(kernel_interface_t, update_sa, status_t, diff --git a/src/libhydra/kernel/kernel_interface.h b/src/libhydra/kernel/kernel_interface.h index 3b1010d24a..bba6a5898c 100644 --- a/src/libhydra/kernel/kernel_interface.h +++ b/src/libhydra/kernel/kernel_interface.h @@ -147,6 +147,7 @@ struct kernel_interface_t { * @param mode mode of the SA (tunnel, transport) * @param ipcomp IPComp transform to use * @param cpi CPI for IPComp + * @param replay_window anti-replay window size * @param initiator TRUE if initiator of the exchange creating this SA * @param encap enable UDP encapsulation for NAT traversal * @param esn TRUE to use Extended Sequence Numbers @@ -162,6 +163,7 @@ struct kernel_interface_t { u_int16_t enc_alg, chunk_t enc_key, u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp, u_int16_t cpi, + u_int32_t replay_window, bool initiator, bool encap, bool esn, bool inbound, traffic_selector_t *src_ts, traffic_selector_t *dst_ts); diff --git a/src/libhydra/kernel/kernel_ipsec.h b/src/libhydra/kernel/kernel_ipsec.h index 25f5b38fdd..eec7401e97 100644 --- a/src/libhydra/kernel/kernel_ipsec.h +++ b/src/libhydra/kernel/kernel_ipsec.h @@ -101,6 +101,7 @@ struct kernel_ipsec_t { * @param mode mode of the SA (tunnel, transport) * @param ipcomp IPComp transform to use * @param cpi CPI for IPComp + * @param replay_window anti-replay window size * @param initiator TRUE if initiator of the exchange creating this SA * @param encap enable UDP encapsulation for NAT traversal * @param esn TRUE to use Extended Sequence Numbers @@ -116,6 +117,7 @@ struct kernel_ipsec_t { u_int16_t enc_alg, chunk_t enc_key, u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp, u_int16_t cpi, + u_int32_t replay_window, bool initiator, bool encap, bool esn, bool inbound, traffic_selector_t *src_ts, traffic_selector_t *dst_ts); diff --git a/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c b/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c index 0b66b4d943..baf87ae1cc 100644 --- a/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c +++ b/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c @@ -1682,8 +1682,9 @@ METHOD(kernel_ipsec_t, add_sa, status_t, u_int8_t protocol, u_int32_t reqid, mark_t mark, u_int32_t tfc, lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key, u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, - u_int16_t ipcomp, u_int16_t cpi, bool initiator, bool encap, bool esn, - bool inbound, traffic_selector_t *src_ts, traffic_selector_t *dst_ts) + u_int16_t ipcomp, u_int16_t cpi, u_int32_t replay_window, + bool initiator, bool encap, bool esn, bool inbound, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts) { unsigned char request[PFKEY_BUFFER_SIZE]; struct sadb_msg *msg, *out; diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c index c864a92f42..c015c0f321 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -73,9 +73,6 @@ /** Default priority of installed policies */ #define PRIO_BASE 512 -/** Default replay window size, if not set using charon.replay_window */ -#define DEFAULT_REPLAY_WINDOW 32 - /** Default lifetime of an acquire XFRM state (in seconds) */ #define DEFAULT_ACQUIRE_LIFETIME 165 @@ -316,16 +313,6 @@ struct private_kernel_netlink_ipsec_t { * Whether to track the history of a policy */ bool policy_history; - - /** - * Size of the replay window, in packets (= bits) - */ - u_int32_t replay_window; - - /** - * Size of the replay window bitmap, in number of __u32 blocks - */ - u_int32_t replay_bmp; }; typedef struct route_entry_t route_entry_t; @@ -630,14 +617,6 @@ static inline u_int32_t get_priority(policy_entry_t *policy, return priority; } -/** - * Return the length of the ESN bitmap - */ -static inline size_t esn_bmp_len(private_kernel_netlink_ipsec_t *this) -{ - return this->replay_bmp * sizeof(u_int32_t); -} - /** * Convert the general ipsec mode to the one defined in xfrm.h */ @@ -1194,8 +1173,9 @@ METHOD(kernel_ipsec_t, add_sa, status_t, private_kernel_netlink_ipsec_t *this, host_t *src, host_t *dst, u_int32_t spi, u_int8_t protocol, u_int32_t reqid, mark_t mark, u_int32_t tfc, lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key, - u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp, - u_int16_t cpi, bool initiator, bool encap, bool esn, bool inbound, + u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, + u_int16_t ipcomp, u_int16_t cpi, u_int32_t replay_window, + bool initiator, bool encap, bool esn, bool inbound, traffic_selector_t* src_ts, traffic_selector_t* dst_ts) { netlink_buf_t request; @@ -1213,8 +1193,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t, lifetime_cfg_t lft = {{0,0,0},{0,0,0},{0,0,0}}; add_sa(this, src, dst, htonl(ntohs(cpi)), IPPROTO_COMP, reqid, mark, tfc, &lft, ENCR_UNDEFINED, chunk_empty, AUTH_UNDEFINED, - chunk_empty, mode, ipcomp, 0, initiator, FALSE, FALSE, inbound, - src_ts, dst_ts); + chunk_empty, mode, ipcomp, 0, 0, initiator, FALSE, FALSE, + inbound, src_ts, dst_ts); ipcomp = IPCOMP_NONE; /* use transport mode ESP SA, IPComp uses tunnel mode */ mode = MODE_TRANSPORT; @@ -1480,23 +1460,24 @@ METHOD(kernel_ipsec_t, add_sa, status_t, if (protocol != IPPROTO_COMP) { - if (esn || this->replay_window > DEFAULT_REPLAY_WINDOW) + if (esn || replay_window > 32) { /* for ESN or larger replay windows we need the new * XFRMA_REPLAY_ESN_VAL attribute to configure a bitmap */ struct xfrm_replay_state_esn *replay; + u_int32_t bmp_size; + bmp_size = round_up(replay_window, sizeof(u_int32_t) * 8) / 8; replay = netlink_reserve(hdr, sizeof(request), XFRMA_REPLAY_ESN_VAL, - sizeof(*replay) + esn_bmp_len(this)); + sizeof(*replay) + bmp_size); if (!replay) { goto failed; } /* bmp_len contains number uf __u32's */ - replay->bmp_len = this->replay_bmp; - replay->replay_window = this->replay_window; - DBG2(DBG_KNL, " using replay window of %u packets", - this->replay_window); + replay->bmp_len = bmp_size / sizeof(u_int32_t); + replay->replay_window = replay_window; + DBG2(DBG_KNL, " using replay window of %u packets", replay_window); if (esn) { @@ -1506,9 +1487,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t, } else { - DBG2(DBG_KNL, " using replay window of %u packets", - this->replay_window); - sa->replay_window = this->replay_window; + DBG2(DBG_KNL, " using replay window of %u packets", replay_window); + sa->replay_window = replay_window; } } @@ -1542,6 +1522,7 @@ static void get_replay_state(private_kernel_netlink_ipsec_t *this, u_int32_t spi, u_int8_t protocol, host_t *dst, mark_t mark, struct xfrm_replay_state_esn **replay_esn, + u_int32_t *replay_esn_len, struct xfrm_replay_state **replay) { netlink_buf_t request; @@ -1618,9 +1599,10 @@ static void get_replay_state(private_kernel_netlink_ipsec_t *this, break; } if (rta->rta_type == XFRMA_REPLAY_ESN_VAL && - RTA_PAYLOAD(rta) >= sizeof(**replay_esn) + esn_bmp_len(this)) + RTA_PAYLOAD(rta) >= sizeof(**replay_esn)) { *replay_esn = malloc(RTA_PAYLOAD(rta)); + *replay_esn_len = RTA_PAYLOAD(rta); memcpy(*replay_esn, RTA_DATA(rta), RTA_PAYLOAD(rta)); break; } @@ -1804,6 +1786,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t, struct xfrm_encap_tmpl* tmpl = NULL; struct xfrm_replay_state *replay = NULL; struct xfrm_replay_state_esn *replay_esn = NULL; + u_int32_t replay_esn_len; status_t status = FAILED; /* if IPComp is used, we first update the IPComp SA */ @@ -1868,7 +1851,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t, goto failed; } - get_replay_state(this, spi, protocol, dst, mark, &replay_esn, &replay); + get_replay_state(this, spi, protocol, dst, mark, &replay_esn, &replay_esn_len, &replay); /* delete the old SA (without affecting the IPComp SA) */ if (del_sa(this, src, dst, spi, protocol, 0, mark) != SUCCESS) @@ -1936,12 +1919,12 @@ METHOD(kernel_ipsec_t, update_sa, status_t, struct xfrm_replay_state_esn *state; state = netlink_reserve(hdr, sizeof(request), XFRMA_REPLAY_ESN_VAL, - sizeof(*state) + esn_bmp_len(this)); + replay_esn_len); if (!state) { goto failed; } - memcpy(state, replay_esn, sizeof(*state) + esn_bmp_len(this)); + memcpy(state, replay_esn, replay_esn_len); } else if (replay) { @@ -2686,13 +2669,8 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() .policy_history = TRUE, .install_routes = lib->settings->get_bool(lib->settings, "%s.install_routes", TRUE, lib->ns), - .replay_window = lib->settings->get_int(lib->settings, - "%s.replay_window", DEFAULT_REPLAY_WINDOW, lib->ns), ); - this->replay_bmp = (this->replay_window + sizeof(u_int32_t) * 8 - 1) / - (sizeof(u_int32_t) * 8); - if (streq(lib->ns, "starter")) { /* starter has no threads, so we do not register for kernel events */ register_for_events = FALSE; diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index c865917e42..9bddb13a12 100644 --- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -1615,8 +1615,9 @@ METHOD(kernel_ipsec_t, add_sa, status_t, u_int8_t protocol, u_int32_t reqid, mark_t mark, u_int32_t tfc, lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key, u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, - u_int16_t ipcomp, u_int16_t cpi, bool initiator, bool encap, bool esn, - bool inbound, traffic_selector_t *src_ts, traffic_selector_t *dst_ts) + u_int16_t ipcomp, u_int16_t cpi, u_int32_t replay_window, + bool initiator, bool encap, bool esn, bool inbound, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts) { unsigned char request[PFKEY_BUFFER_SIZE]; struct sadb_msg *msg, *out; @@ -1633,7 +1634,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t, lifetime_cfg_t lft = {{0,0,0},{0,0,0},{0,0,0}}; add_sa(this, src, dst, htonl(ntohs(cpi)), IPPROTO_COMP, reqid, mark, tfc, &lft, ENCR_UNDEFINED, chunk_empty, AUTH_UNDEFINED, - chunk_empty, mode, ipcomp, 0, FALSE, FALSE, FALSE, inbound, + chunk_empty, mode, ipcomp, 0, 0, FALSE, FALSE, FALSE, inbound, NULL, NULL); ipcomp = IPCOMP_NONE; /* use transport mode ESP SA, IPComp uses tunnel mode */ @@ -1676,7 +1677,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t, } else { - sa->sadb_sa_replay = 32; + sa->sadb_sa_replay = min(replay_window, 32); sa->sadb_sa_auth = lookup_algorithm(INTEGRITY_ALGORITHM, int_alg); sa->sadb_sa_encrypt = lookup_algorithm(ENCRYPTION_ALGORITHM, enc_alg); } diff --git a/src/starter/args.c b/src/starter/args.c index f5a617eaa8..0d662f400e 100644 --- a/src/starter/args.c +++ b/src/starter/args.c @@ -173,6 +173,7 @@ static const token_info_t token_info[] = { ARG_STR, offsetof(starter_conn_t, me_mediated_by), NULL }, { ARG_STR, offsetof(starter_conn_t, me_peerid), NULL }, { ARG_UINT, offsetof(starter_conn_t, reqid), NULL }, + { ARG_UINT, offsetof(starter_conn_t, replay_window), NULL }, { ARG_MISC, 0, NULL /* KW_MARK */ }, { ARG_MISC, 0, NULL /* KW_MARK_IN */ }, { ARG_MISC, 0, NULL /* KW_MARK_OUT */ }, diff --git a/src/starter/confread.c b/src/starter/confread.c index 19178a2c39..0fac895425 100644 --- a/src/starter/confread.c +++ b/src/starter/confread.c @@ -34,6 +34,7 @@ #define SA_REPLACEMENT_MARGIN_DEFAULT 540 /* 9 minutes */ #define SA_REPLACEMENT_FUZZ_DEFAULT 100 /* 100% of margin */ #define SA_REPLACEMENT_RETRIES_DEFAULT 3 +#define SA_REPLAY_WINDOW_DEFAULT -1 /* use charon.replay_window */ static const char ike_defaults[] = "aes128-sha1-modp2048,3des-sha1-modp1536"; static const char esp_defaults[] = "aes128-sha1,3des-sha1"; @@ -132,6 +133,7 @@ static void default_values(starter_config_t *cfg) cfg->conn_default.install_policy = TRUE; cfg->conn_default.dpd_delay = 30; /* seconds */ cfg->conn_default.dpd_timeout = 150; /* seconds */ + cfg->conn_default.replay_window = SA_REPLAY_WINDOW_DEFAULT; cfg->conn_default.left.seen = SEEN_NONE; cfg->conn_default.right.seen = SEEN_NONE; diff --git a/src/starter/confread.h b/src/starter/confread.h index d55a17e631..a32f8cba40 100644 --- a/src/starter/confread.h +++ b/src/starter/confread.h @@ -162,6 +162,7 @@ struct starter_conn { u_int32_t reqid; mark_t mark_in; mark_t mark_out; + u_int32_t replay_window; u_int32_t tfc; bool install_policy; bool aggressive; diff --git a/src/starter/keywords.h b/src/starter/keywords.h index 705a7c16e9..5b6b28bf85 100644 --- a/src/starter/keywords.h +++ b/src/starter/keywords.h @@ -69,6 +69,7 @@ typedef enum { KW_MEDIATED_BY, KW_ME_PEERID, KW_REQID, + KW_REPLAY_WINDOW, KW_MARK, KW_MARK_IN, KW_MARK_OUT, diff --git a/src/starter/keywords.txt b/src/starter/keywords.txt index ad915bf2af..ee0bd31e1e 100644 --- a/src/starter/keywords.txt +++ b/src/starter/keywords.txt @@ -69,6 +69,7 @@ mediation, KW_MEDIATION mediated_by, KW_MEDIATED_BY me_peerid, KW_ME_PEERID reqid, KW_REQID +replay_window, KW_REPLAY_WINDOW mark, KW_MARK mark_in, KW_MARK_IN mark_out, KW_MARK_OUT diff --git a/src/starter/starterstroke.c b/src/starter/starterstroke.c index fca4b1e7dc..839e66e7bb 100644 --- a/src/starter/starterstroke.c +++ b/src/starter/starterstroke.c @@ -202,6 +202,7 @@ int starter_stroke_add_conn(starter_config_t *cfg, starter_conn_t *conn) msg.add_conn.ikeme.mediated_by = push_string(&msg, conn->me_mediated_by); msg.add_conn.ikeme.peerid = push_string(&msg, conn->me_peerid); msg.add_conn.reqid = conn->reqid; + msg.add_conn.replay_window = conn->replay_window; msg.add_conn.mark_in.value = conn->mark_in.value; msg.add_conn.mark_in.mask = conn->mark_in.mask; msg.add_conn.mark_out.value = conn->mark_out.value; diff --git a/src/stroke/stroke_msg.h b/src/stroke/stroke_msg.h index 5ece7248bf..60886cf7fe 100644 --- a/src/stroke/stroke_msg.h +++ b/src/stroke/stroke_msg.h @@ -304,6 +304,7 @@ struct stroke_msg_t { u_int32_t mask; } mark_in, mark_out; stroke_end_t me, other; + u_int32_t replay_window; } add_conn; /* data for STR_ADD_CA */ diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index 73907b6ce1..40265d0f96 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -629,6 +629,13 @@ connections..children..tfc_padding = 0 The default value of 0 disables TFC padding, the special value _mtu_ adds TFC padding to create a packet size equal to the Path Maximum Transfer Unit. +connections..children..replay_window = 32 + IPsec replay window to configure for this CHILD_SA. + + IPsec replay window to configure for this CHILD_SA. Larger values than the + default of 32 are supported using the Netlink backend only, a value of 0 + disables IPsec replay protection. + connections..children..start_action = none Action to perform after loading the configuration (_none_, _trap_, _start_).