From patchwork Mon Sep 20 20:44:38 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cole Dishington X-Patchwork-Id: 12506725 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id DE8C4C43219 for ; Tue, 21 Sep 2021 01:56:32 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CA38961175 for ; Tue, 21 Sep 2021 01:56:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238448AbhIUB56 (ORCPT ); Mon, 20 Sep 2021 21:57:58 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58826 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230268AbhIUBkg (ORCPT ); Mon, 20 Sep 2021 21:40:36 -0400 Received: from gate2.alliedtelesis.co.nz (gate2.alliedtelesis.co.nz [IPv6:2001:df5:b000:5::4]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 256B0C0604CD for ; Mon, 20 Sep 2021 13:44:47 -0700 (PDT) Received: from svr-chch-seg1.atlnz.lc (mmarshal3.atlnz.lc [10.32.18.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by gate2.alliedtelesis.co.nz (Postfix) with ESMTPS id 3FCCB806AC; Tue, 21 Sep 2021 08:44:43 +1200 (NZST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=alliedtelesis.co.nz; s=mail181024; t=1632170683; bh=Cn+19qSMvfgrOTGheCboaarnn1DHBPEuHJWDY1D7Op8=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=Y4FtHIIpJl3KwVIy+BopoqYloLYg+M7m7ZIerKet2iatALeBCuE4P6oBX/dq5YJiu JsV5/MvOvO+yuHeVZMylnlgYFYTgNASHe7vYQHe2MoLzqxaCSDF3AwfL55QXuoUOx8 kXpvL7bZBAjefbsuQYeOSTrKeQH93nnZYYPZfjLKijOjUiUfXeiZTBHaJAvm+S3u3+ yY76eKmtm0VA0Q6i416hTrDO3dL7DdUz7FPBGJF9R8kDA2w20e2SyQrvGq5xWPghYR E7r96aRDUL4Y2tGo+spUgbqiJkNWozzGuQ3jVv2FpbVafJzKhhMLQ0n/Gi7LwZs53c 7Iqeo8XVeBVlw== Received: from pat.atlnz.lc (Not Verified[10.32.16.33]) by svr-chch-seg1.atlnz.lc with Trustwave SEG (v8,2,6,11305) id ; Tue, 21 Sep 2021 08:44:42 +1200 Received: from coled-dl.ws.atlnz.lc (coled-dl.ws.atlnz.lc [10.33.25.26]) by pat.atlnz.lc (Postfix) with ESMTP id DB4F513EEA3; Tue, 21 Sep 2021 08:44:42 +1200 (NZST) Received: by coled-dl.ws.atlnz.lc (Postfix, from userid 1801) id D5EF1242823; Tue, 21 Sep 2021 08:44:42 +1200 (NZST) From: Cole Dishington To: pablo@netfilter.org, kadlec@netfilter.org, fw@strlen.de, davem@davemloft.net, kuba@kernel.org, shuah@kernel.org Cc: linux-kernel@vger.kernel.org, netfilter-devel@vger.kernel.org, coreteam@netfilter.org, netdev@vger.kernel.org, Cole Dishington , Anthony Lineham , Scott Parlane , Blair Steven Subject: [PATCH net v6 1/2] net: netfilter: Limit the number of ftp helper port attempts Date: Tue, 21 Sep 2021 08:44:38 +1200 Message-Id: <20210920204439.13179-2-Cole.Dishington@alliedtelesis.co.nz> X-Mailer: git-send-email 2.33.0 In-Reply-To: <20210920204439.13179-1-Cole.Dishington@alliedtelesis.co.nz> References: <20210920204439.13179-1-Cole.Dishington@alliedtelesis.co.nz> MIME-Version: 1.0 X-SEG-SpamProfiler-Analysis: v=2.3 cv=FtN7AFjq c=1 sm=1 tr=0 a=KLBiSEs5mFS1a/PbTCJxuA==:117 a=7QKq2e-ADPsA:10 a=v0C0h8vM-w728WOTjeQA:9 a=7Zwj6sZBwVKJAoWSPKxL6X1jA+E=:19 X-SEG-SpamProfiler-Score: 0 x-atlnz-ls: pat Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org In preparation of fixing the port selection of ftp helper when using NF_NAT_RANGE_PROTO_SPECIFIED, limit the number of ftp helper port attempts to 128. Looping a large port range takes too long. Instead select a random offset within [ntohs(exp->saved_proto.tcp.port), 65535] and try 128 ports. Co-developed-by: Anthony Lineham Signed-off-by: Anthony Lineham Co-developed-by: Scott Parlane Signed-off-by: Scott Parlane Co-developed-by: Blair Steven Signed-off-by: Blair Steven Signed-off-by: Cole Dishington Acked-by: Florian Westphal --- Notes: Thanks for your time reviewing! Changes: - Add missing argument from nf_ct_helper_log. - Add Acked-by: Florian Westphal net/netfilter/nf_nat_ftp.c | 39 +++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/net/netfilter/nf_nat_ftp.c b/net/netfilter/nf_nat_ftp.c index aace6768a64e..2da29e5d4309 100644 --- a/net/netfilter/nf_nat_ftp.c +++ b/net/netfilter/nf_nat_ftp.c @@ -72,8 +72,11 @@ static unsigned int nf_nat_ftp(struct sk_buff *skb, u_int16_t port; int dir = CTINFO2DIR(ctinfo); struct nf_conn *ct = exp->master; + unsigned int i, min, max, range_size; + static const unsigned int max_attempts = 128; char buffer[sizeof("|1||65535|") + INET6_ADDRSTRLEN]; unsigned int buflen; + int ret; pr_debug("type %i, off %u len %u\n", type, matchoff, matchlen); @@ -86,22 +89,32 @@ static unsigned int nf_nat_ftp(struct sk_buff *skb, * this one. */ exp->expectfn = nf_nat_follow_master; - /* Try to get same port: if not, try to change it. */ - for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) { - int ret; - - exp->tuple.dst.u.tcp.port = htons(port); - ret = nf_ct_expect_related(exp, 0); - if (ret == 0) - break; - else if (ret != -EBUSY) { - port = 0; - break; + min = ntohs(exp->saved_proto.tcp.port); + max = 65535; + + /* Try to get same port */ + ret = nf_ct_expect_related(exp, 0); + + /* if same port is not in range or available, try to change it. */ + if (ret != 0) { + range_size = max - min + 1; + if (range_size > max_attempts) + range_size = max_attempts; + + port = min + prandom_u32_max(max - min); + for (i = 0; i < range_size; i++) { + exp->tuple.dst.u.tcp.port = htons(port); + ret = nf_ct_expect_related(exp, 0); + if (ret != -EBUSY) + break; + port++; + if (port > max) + port = min; } } - if (port == 0) { - nf_ct_helper_log(skb, ct, "all ports in use"); + if (ret != 0) { + nf_ct_helper_log(skb, ct, "tried %u ports, all were in use", range_size); return NF_DROP; } From patchwork Mon Sep 20 20:44:39 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cole Dishington X-Patchwork-Id: 12506305 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 10756C4332F for ; Mon, 20 Sep 2021 20:46:51 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E475061100 for ; Mon, 20 Sep 2021 20:46:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235733AbhITUsQ (ORCPT ); Mon, 20 Sep 2021 16:48:16 -0400 Received: from gate2.alliedtelesis.co.nz ([202.36.163.20]:40611 "EHLO gate2.alliedtelesis.co.nz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231135AbhITUqO (ORCPT ); Mon, 20 Sep 2021 16:46:14 -0400 Received: from svr-chch-seg1.atlnz.lc (mmarshal3.atlnz.lc [10.32.18.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by gate2.alliedtelesis.co.nz (Postfix) with ESMTPS id 45AA183651; Tue, 21 Sep 2021 08:44:43 +1200 (NZST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=alliedtelesis.co.nz; s=mail181024; t=1632170683; bh=5Dpx2kyLtsztDA5WSZjmj6Onx7HoL/rUbWRMnRjR2r8=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=DQ7bWwFyztEbSiEADX/fa93IDL+bcVRC9Txh1QN8afVAXojA6PWeltZbMCncrP6ih ARFqALGJe60XmK2obiGJpNhCtKchYK5VOIwehj6Aubewn+tne1aao2lEdphb5HpXNy 3zKXLssiYpfiCyOoRVHUshBl02toBn4yaFPrcnrxp/2SP7vyTEIQ/p3zdJRBRH/cH4 j8BBKTF9cJsY+sIB6xklOdu4RCMcftMjBT7FQeIVn0ysktKqrwhibuejzL9tBm7iiw bbW4J8fyH+md0jXhoIrrSV2NwTUfgoaO5XpvaeWG6jJRta1QOBkeEGAXobTJdJrC5m rPAKvxhJ2Hwcg== Received: from pat.atlnz.lc (Not Verified[10.32.16.33]) by svr-chch-seg1.atlnz.lc with Trustwave SEG (v8,2,6,11305) id ; Tue, 21 Sep 2021 08:44:42 +1200 Received: from coled-dl.ws.atlnz.lc (coled-dl.ws.atlnz.lc [10.33.25.26]) by pat.atlnz.lc (Postfix) with ESMTP id E240D13EEA7; Tue, 21 Sep 2021 08:44:42 +1200 (NZST) Received: by coled-dl.ws.atlnz.lc (Postfix, from userid 1801) id DE640242827; Tue, 21 Sep 2021 08:44:42 +1200 (NZST) From: Cole Dishington To: pablo@netfilter.org, kadlec@netfilter.org, fw@strlen.de, davem@davemloft.net, kuba@kernel.org, shuah@kernel.org Cc: linux-kernel@vger.kernel.org, netfilter-devel@vger.kernel.org, coreteam@netfilter.org, netdev@vger.kernel.org, Cole Dishington , Anthony Lineham , Scott Parlane , Blair Steven Subject: [PATCH net v6 2/2] net: netfilter: Fix port selection of FTP for NF_NAT_RANGE_PROTO_SPECIFIED Date: Tue, 21 Sep 2021 08:44:39 +1200 Message-Id: <20210920204439.13179-3-Cole.Dishington@alliedtelesis.co.nz> X-Mailer: git-send-email 2.33.0 In-Reply-To: <20210920204439.13179-1-Cole.Dishington@alliedtelesis.co.nz> References: <20210920204439.13179-1-Cole.Dishington@alliedtelesis.co.nz> MIME-Version: 1.0 X-SEG-SpamProfiler-Analysis: v=2.3 cv=FtN7AFjq c=1 sm=1 tr=0 a=KLBiSEs5mFS1a/PbTCJxuA==:117 a=7QKq2e-ADPsA:10 a=xOT0nC9th1TpZTiSAT0A:9 a=7Zwj6sZBwVKJAoWSPKxL6X1jA+E=:19 X-SEG-SpamProfiler-Score: 0 x-atlnz-ls: pat Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org FTP port selection ignores specified port ranges (with iptables masquerade --to-ports) when creating an expectation, based on FTP commands PORT or PASV, for the data connection. For masquerading, this issue allows an FTP client to use unassigned source ports for their data connection (in both the PORT and PASV cases). This can cause problems in setups that allocate different masquerade port ranges for each client. The proposed fix involves storing a port range (on nf_conn_nat) to: - Fix FTP PORT data connections using the stored port range to select a port number in nf_conntrack_ftp. - Fix FTP PASV data connections using the stored port range to specify a port range on source port in nf_nat_helper if the FTP PORT/PASV packet comes from the client. Co-developed-by: Anthony Lineham Signed-off-by: Anthony Lineham Co-developed-by: Scott Parlane Signed-off-by: Scott Parlane Co-developed-by: Blair Steven Signed-off-by: Blair Steven Signed-off-by: Cole Dishington Acked-by: Florian Westphal --- Notes: Thanks for your time reviewing! Changes: - Add Acked-by: Florian Westphal include/net/netfilter/nf_nat.h | 6 ++++++ net/netfilter/nf_nat_core.c | 9 +++++++++ net/netfilter/nf_nat_ftp.c | 22 +++++++++++++++++++--- net/netfilter/nf_nat_helper.c | 10 ++++++++++ 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/include/net/netfilter/nf_nat.h b/include/net/netfilter/nf_nat.h index 0d412dd63707..231cffc16722 100644 --- a/include/net/netfilter/nf_nat.h +++ b/include/net/netfilter/nf_nat.h @@ -27,12 +27,18 @@ union nf_conntrack_nat_help { #endif }; +struct nf_conn_nat_range_info { + union nf_conntrack_man_proto min_proto; + union nf_conntrack_man_proto max_proto; +}; + /* The structure embedded in the conntrack structure. */ struct nf_conn_nat { union nf_conntrack_nat_help help; #if IS_ENABLED(CONFIG_NF_NAT_MASQUERADE) int masq_index; #endif + struct nf_conn_nat_range_info range_info; }; /* Set up the info structure to map into this range. */ diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index b7c3c902290f..2acec7fd56bd 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -623,6 +623,15 @@ nf_nat_setup_info(struct nf_conn *ct, &ct->tuplehash[IP_CT_DIR_REPLY].tuple); get_unique_tuple(&new_tuple, &curr_tuple, range, ct, maniptype); + if (range && (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)) { + struct nf_conn_nat *nat = nf_ct_nat_ext_add(ct); + + if (!nat) + return NF_DROP; + + nat->range_info.min_proto = range->min_proto; + nat->range_info.max_proto = range->max_proto; + } if (!nf_ct_tuple_equal(&new_tuple, &curr_tuple)) { struct nf_conntrack_tuple reply; diff --git a/net/netfilter/nf_nat_ftp.c b/net/netfilter/nf_nat_ftp.c index 2da29e5d4309..7af1519d7a89 100644 --- a/net/netfilter/nf_nat_ftp.c +++ b/net/netfilter/nf_nat_ftp.c @@ -72,12 +72,16 @@ static unsigned int nf_nat_ftp(struct sk_buff *skb, u_int16_t port; int dir = CTINFO2DIR(ctinfo); struct nf_conn *ct = exp->master; + struct nf_conn_nat *nat = nfct_nat(ct); unsigned int i, min, max, range_size; static const unsigned int max_attempts = 128; char buffer[sizeof("|1||65535|") + INET6_ADDRSTRLEN]; unsigned int buflen; int ret; + if (WARN_ON_ONCE(!nat)) + return NF_DROP; + pr_debug("type %i, off %u len %u\n", type, matchoff, matchlen); /* Connection will come from wherever this packet goes, hence !dir */ @@ -89,11 +93,23 @@ static unsigned int nf_nat_ftp(struct sk_buff *skb, * this one. */ exp->expectfn = nf_nat_follow_master; - min = ntohs(exp->saved_proto.tcp.port); - max = 65535; + /* Avoid applying nat->range to the reply direction */ + if (!exp->dir || !nat->range_info.min_proto.all || !nat->range_info.max_proto.all) { + min = ntohs(exp->saved_proto.tcp.port); + max = 65535; + } else { + min = ntohs(nat->range_info.min_proto.all); + max = ntohs(nat->range_info.max_proto.all); + if (unlikely(max < min)) + swap(max, min); + } /* Try to get same port */ - ret = nf_ct_expect_related(exp, 0); + ret = -1; + port = ntohs(exp->saved_proto.tcp.port); + if (min < port && port < max) { + ret = nf_ct_expect_related(exp, 0); + } /* if same port is not in range or available, try to change it. */ if (ret != 0) { diff --git a/net/netfilter/nf_nat_helper.c b/net/netfilter/nf_nat_helper.c index a263505455fc..718fc423bc44 100644 --- a/net/netfilter/nf_nat_helper.c +++ b/net/netfilter/nf_nat_helper.c @@ -188,6 +188,16 @@ void nf_nat_follow_master(struct nf_conn *ct, range.flags = NF_NAT_RANGE_MAP_IPS; range.min_addr = range.max_addr = ct->master->tuplehash[!exp->dir].tuple.dst.u3; + if (!exp->dir) { + struct nf_conn_nat *nat = nfct_nat(exp->master); + + if (nat && nat->range_info.min_proto.all && + nat->range_info.max_proto.all) { + range.min_proto = nat->range_info.min_proto; + range.max_proto = nat->range_info.max_proto; + range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; + } + } nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC); /* For DST manip, map port here to where it's expected. */