Local templates have cport 0 and IP_VS_CONN_F_NO_CPORT clear, so
ip_vs_conn_in_get() cannot match them. A template received over the
unauthenticated sync protocol with a nonzero cport can reach the
protocol state machine. Before template state validation, this
could cause out-of-bounds reads, crashes and kernel data leaks.
Ordinary records with cport 0 and IP_VS_CONN_F_NO_CPORT clear, or
with a nonzero cport and IP_VS_CONN_F_NO_CPORT set, create connections
that incoming lookups cannot match. The latter also keeps the
cport-zero fallback in ip_vs_conn_in_get() enabled.
Reject all three cases. Continue accepting ordinary connections
with cport 0 and IP_VS_CONN_F_NO_CPORT. The companion patch
"ipvs: filter some flags received in the backup server" rejects
template records carrying IP_VS_CONN_F_NO_CPORT.
Fixes: 87375ab47cd0 ("[IPVS]: ip_vs_ftp breaks connections using persistence")
Cc: stable@vger.kernel.org
Suggested-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Axel Mierczuk <redacted>
---
net/netfilter/ipvs/ip_vs_sync.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c
index 5383aeafb0ae..3998e9a0a391 100644
--- a/net/netfilter/ipvs/ip_vs_sync.c
+++ b/net/netfilter/ipvs/ip_vs_sync.c
@@ -848,6 +848,25 @@ static void ip_vs_proc_conn(struct netns_ipvs *ipvs, struct ip_vs_conn_param *pa
struct ip_vs_dest *dest;
struct ip_vs_conn *cp;
+ /* Templates require cport 0. Ordinary connections require
+ * NO_CPORT exactly when cport is 0.
+ */
+ if (flags & IP_VS_CONN_F_TEMPLATE) {
+ if (param->cport) {
+ IP_VS_DBG(2, "BACKUP, template with cport dropped\n");
+ kfree(param->pe_data);
+ return;
+ }
+ } else if (!param->cport && !(flags & IP_VS_CONN_F_NO_CPORT)) {
+ IP_VS_DBG(2, "BACKUP, conn without cport dropped\n");
+ kfree(param->pe_data);
+ return;
+ } else if (param->cport && (flags & IP_VS_CONN_F_NO_CPORT)) {
+ IP_VS_DBG(2, "BACKUP, conn with cport and NO_CPORT dropped\n");
+ kfree(param->pe_data);
+ return;
+ }
+
if (!(flags & IP_VS_CONN_F_TEMPLATE)) {
cp = ip_vs_conn_in_get(param);
if (cp && ((cp->dport != dport) ||--
2.43.0