From 401aedfb032d69daa876544e8e0a2973d69c50ac Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Mon, 8 Jul 2019 14:45:04 -0500 Subject: [PATCH] ipsec: drop outbound ESP when no crypto alg set Type: fix If a tunnel interface has the crypto alg set on the outbound SA to IPSEC_CRYPTO_ALG_NONE and packets are sent out that interface, the attempt to write an ESP trailer on the packet occurs at the wrong offset and the vnet buffer opaque data is corrupted, which can result in a SEGV when a subsequent node attempts to use that data. When an outbound SA is set on a tunnel interface which has no crypto alg set, add a node to the ip{4,6}-output feature arcs which drops all packets leaving that interface instead of adding the node which would try to encrypt the packets. Change-Id: Ie0ac8d8fdc8a035ab8bb83b72b6a94161bebaa48 Signed-off-by: Matthew Smith --- src/vnet/ipsec/esp_encrypt.c | 131 +++++++++++++++++++++++++++++++++++++++++++ src/vnet/ipsec/ipsec.c | 2 +- src/vnet/ipsec/ipsec.h | 7 +++ src/vnet/ipsec/ipsec_if.c | 27 +++++++-- 4 files changed, 162 insertions(+), 5 deletions(-) diff --git a/src/vnet/ipsec/esp_encrypt.c b/src/vnet/ipsec/esp_encrypt.c index c879a404b54..041b268975d 100644 --- a/src/vnet/ipsec/esp_encrypt.c +++ b/src/vnet/ipsec/esp_encrypt.c @@ -666,6 +666,137 @@ VNET_FEATURE_INIT (esp6_encrypt_tun_feat_node, static) = }; /* *INDENT-ON* */ +typedef struct +{ + u32 sa_index; +} esp_no_crypto_trace_t; + +static u8 * +format_esp_no_crypto_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + esp_no_crypto_trace_t *t = va_arg (*args, esp_no_crypto_trace_t *); + + s = format (s, "esp-no-crypto: sa-index %u", t->sa_index); + + return s; +} + +enum +{ + ESP_NO_CRYPTO_NEXT_DROP, + ESP_NO_CRYPTO_N_NEXT, +}; + +enum +{ + ESP_NO_CRYPTO_ERROR_RX_PKTS, +}; + +static char *esp_no_crypto_error_strings[] = { + "Outbound ESP packets received", +}; + +always_inline uword +esp_no_crypto_inline (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; + u16 nexts[VLIB_FRAME_SIZE], *next = nexts; + u32 *from = vlib_frame_vector_args (frame); + u32 n_left = frame->n_vectors; + + vlib_get_buffers (vm, from, b, n_left); + + while (n_left > 0) + { + u32 next0; + u32 sa_index0; + + /* packets are always going to be dropped, but get the sa_index */ + sa_index0 = *(u32 *) vnet_feature_next_with_data (&next0, b[0], + sizeof (sa_index0)); + + next[0] = ESP_NO_CRYPTO_NEXT_DROP; + + if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED)) + { + esp_no_crypto_trace_t *tr = vlib_add_trace (vm, node, b[0], + sizeof (*tr)); + tr->sa_index = sa_index0; + } + + n_left -= 1; + next += 1; + b += 1; + } + + vlib_node_increment_counter (vm, node->node_index, + ESP_NO_CRYPTO_ERROR_RX_PKTS, frame->n_vectors); + + vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors); + + return frame->n_vectors; +} + +VLIB_NODE_FN (esp4_no_crypto_tun_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return esp_no_crypto_inline (vm, node, from_frame); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (esp4_no_crypto_tun_node) = +{ + .name = "esp4-no-crypto", + .vector_size = sizeof (u32), + .format_trace = format_esp_no_crypto_trace, + .n_errors = ARRAY_LEN(esp_no_crypto_error_strings), + .error_strings = esp_no_crypto_error_strings, + .n_next_nodes = ESP_NO_CRYPTO_N_NEXT, + .next_nodes = { + [ESP_NO_CRYPTO_NEXT_DROP] = "ip4-drop", + }, +}; + +VNET_FEATURE_INIT (esp4_no_crypto_tun_feat_node, static) = +{ + .arc_name = "ip4-output", + .node_name = "esp4-no-crypto", + .runs_before = VNET_FEATURES ("adj-midchain-tx"), +}; + +VLIB_NODE_FN (esp6_no_crypto_tun_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return esp_no_crypto_inline (vm, node, from_frame); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (esp6_no_crypto_tun_node) = +{ + .name = "esp6-no-crypto", + .vector_size = sizeof (u32), + .format_trace = format_esp_no_crypto_trace, + .n_errors = ARRAY_LEN(esp_no_crypto_error_strings), + .error_strings = esp_no_crypto_error_strings, + .n_next_nodes = ESP_NO_CRYPTO_N_NEXT, + .next_nodes = { + [ESP_NO_CRYPTO_NEXT_DROP] = "ip6-drop", + }, +}; + +VNET_FEATURE_INIT (esp6_no_crypto_tun_feat_node, static) = +{ + .arc_name = "ip6-output", + .node_name = "esp6-no-crypto", + .runs_before = VNET_FEATURES ("adj-midchain-tx"), +}; +/* *INDENT-ON* */ + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/vnet/ipsec/ipsec.c b/src/vnet/ipsec/ipsec.c index 4caae4840fb..21e1d5ec43f 100644 --- a/src/vnet/ipsec/ipsec.c +++ b/src/vnet/ipsec/ipsec.c @@ -122,7 +122,7 @@ ipsec_add_node (vlib_main_t * vm, const char *node_name, *out_next_index = vlib_node_add_next (vm, prev_node->index, node->index); } -static void +void ipsec_add_feature (const char *arc_name, const char *node_name, u32 * out_feature_index) { diff --git a/src/vnet/ipsec/ipsec.h b/src/vnet/ipsec/ipsec.h index c77d0fe7dd8..ccbe7d7aa5c 100644 --- a/src/vnet/ipsec/ipsec.h +++ b/src/vnet/ipsec/ipsec.h @@ -142,6 +142,10 @@ typedef struct u32 esp4_encrypt_tun_feature_index; u32 esp6_encrypt_tun_feature_index; + /* tun nodes to drop packets when no crypto alg set on outbound SA */ + u32 esp4_no_crypto_tun_feature_index; + u32 esp6_no_crypto_tun_feature_index; + /* pool of ah backends */ ipsec_ah_backend_t *ah_backends; /* pool of esp backends */ @@ -241,6 +245,9 @@ ipsec_sa_get (u32 sa_index) return (pool_elt_at_index (ipsec_main.sad, sa_index)); } +void ipsec_add_feature (const char *arc_name, const char *node_name, + u32 * out_feature_index); + #endif /* __IPSEC_H__ */ /* diff --git a/src/vnet/ipsec/ipsec_if.c b/src/vnet/ipsec/ipsec_if.c index 5fc49e1af4e..562f40ec9ab 100644 --- a/src/vnet/ipsec/ipsec_if.c +++ b/src/vnet/ipsec/ipsec_if.c @@ -234,19 +234,31 @@ static void ipsec_tunnel_feature_set (ipsec_main_t * im, ipsec_tunnel_if_t * t, u8 enable) { u8 arc; + u32 esp4_feature_index, esp6_feature_index; + ipsec_sa_t *sa; + + sa = ipsec_sa_get (t->output_sa_index); + if (sa->crypto_alg == IPSEC_CRYPTO_ALG_NONE) + { + esp4_feature_index = im->esp4_no_crypto_tun_feature_index; + esp6_feature_index = im->esp6_no_crypto_tun_feature_index; + } + else + { + esp4_feature_index = im->esp4_encrypt_tun_feature_index; + esp6_feature_index = im->esp6_encrypt_tun_feature_index; + } arc = vnet_get_feature_arc_index ("ip4-output"); - vnet_feature_enable_disable_with_index (arc, - im->esp4_encrypt_tun_feature_index, + vnet_feature_enable_disable_with_index (arc, esp4_feature_index, t->sw_if_index, enable, &t->output_sa_index, sizeof (t->output_sa_index)); arc = vnet_get_feature_arc_index ("ip6-output"); - vnet_feature_enable_disable_with_index (arc, - im->esp6_encrypt_tun_feature_index, + vnet_feature_enable_disable_with_index (arc, esp6_feature_index, t->sw_if_index, enable, &t->output_sa_index, sizeof (t->output_sa_index)); @@ -562,6 +574,13 @@ ipsec_tunnel_if_init (vlib_main_t * vm) udp_register_dst_port (vm, UDP_DST_PORT_ipsec, ipsec4_if_input_node.index, 1); + + /* set up feature nodes to drop outbound packets with no crypto alg set */ + ipsec_add_feature ("ip4-output", "esp4-no-crypto", + &im->esp4_no_crypto_tun_feature_index); + ipsec_add_feature ("ip6-output", "esp6-no-crypto", + &im->esp6_no_crypto_tun_feature_index); + return 0; } -- 2.16.6