Unverified Commit 4f3b4e79 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!8472 fix CVE-2024-27415

Merge Pull Request from: @ci-robot 
 
PR sync from: Zhengchao Shao <shaozhengchao@huawei.com>
https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/HWMDZRHQZMYJWSXI3IABSMRRNGTOREW5/ 
Fix CVE-2024-27415.

Florian Westphal (1):
  netfilter: bridge: confirm multicast packets before passing them up
    the stack

Pablo Neira Ayuso (1):
  netfilter: br_netfilter: skip conntrack input hook for promisc packets


-- 
2.34.1
 
https://gitee.com/src-openeuler/kernel/issues/I9Q8LZ 
 
Link:https://gitee.com/openeuler/kernel/pulls/8472

 

Reviewed-by: default avatarYue Haibing <yuehaibing@huawei.com>
Signed-off-by: default avatarJialin Zhang <zhangjialin11@huawei.com>
parents 9e9bdf81 6f3f5930
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -457,6 +457,7 @@ struct nf_ct_hook {
	void (*destroy)(struct nf_conntrack *);
	bool (*get_tuple_skb)(struct nf_conntrack_tuple *,
			      const struct sk_buff *);
	int (*confirm)(struct sk_buff *skb);

	KABI_RESERVE(1)
};
+11 −4
Original line number Diff line number Diff line
@@ -30,7 +30,7 @@ br_netif_receive_skb(struct net *net, struct sock *sk, struct sk_buff *skb)
	return netif_receive_skb(skb);
}

static int br_pass_frame_up(struct sk_buff *skb)
static int br_pass_frame_up(struct sk_buff *skb, bool promisc)
{
	struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev;
	struct net_bridge *br = netdev_priv(brdev);
@@ -69,6 +69,8 @@ static int br_pass_frame_up(struct sk_buff *skb)
	br_multicast_count(br, NULL, skb, br_multicast_igmp_type(skb),
			   BR_MCAST_DIR_TX);

	BR_INPUT_SKB_CB(skb)->promisc = promisc;

	return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN,
		       dev_net(indev), NULL, skb, indev, NULL,
		       br_netif_receive_skb);
@@ -83,6 +85,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
	struct net_bridge_mdb_entry *mdst;
	bool local_rcv, mcast_hit = false;
	struct net_bridge *br;
	bool promisc;
	u16 vid = 0;
	u8 state;

@@ -101,7 +104,9 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
	if (p->flags & BR_LEARNING)
		br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, 0);

	local_rcv = !!(br->dev->flags & IFF_PROMISC);
	promisc = !!(br->dev->flags & IFF_PROMISC);
	local_rcv = promisc;

	if (is_multicast_ether_addr(eth_hdr(skb)->h_dest)) {
		/* by definition the broadcast is also a multicast address */
		if (is_broadcast_ether_addr(eth_hdr(skb)->h_dest)) {
@@ -163,7 +168,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
		unsigned long now = jiffies;

		if (test_bit(BR_FDB_LOCAL, &dst->flags))
			return br_pass_frame_up(skb);
			return br_pass_frame_up(skb, false);

		if (now != dst->used)
			dst->used = now;
@@ -176,7 +181,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
	}

	if (local_rcv)
		return br_pass_frame_up(skb);
		return br_pass_frame_up(skb, promisc);

out:
	return 0;
@@ -336,6 +341,8 @@ static rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
				goto forward;
		}

		BR_INPUT_SKB_CB(skb)->promisc = false;

		/* The else clause should be hit when nf_hook():
		 *   - returns < 0 (drop/error)
		 *   - returns = 0 (stolen/nf_queue)
+102 −0
Original line number Diff line number Diff line
@@ -43,6 +43,10 @@
#include <linux/sysctl.h>
#endif

#if IS_ENABLED(CONFIG_NF_CONNTRACK)
#include <net/netfilter/nf_conntrack_core.h>
#endif

static unsigned int brnf_net_id __read_mostly;

struct brnf_net {
@@ -551,6 +555,96 @@ static unsigned int br_nf_pre_routing(void *priv,
	return NF_STOLEN;
}

#if IS_ENABLED(CONFIG_NF_CONNTRACK)
/* conntracks' nf_confirm logic cannot handle cloned skbs referencing
 * the same nf_conn entry, which will happen for multicast (broadcast)
 * Frames on bridges.
 *
 * Example:
 *      macvlan0
 *      br0
 *  ethX  ethY
 *
 * ethX (or Y) receives multicast or broadcast packet containing
 * an IP packet, not yet in conntrack table.
 *
 * 1. skb passes through bridge and fake-ip (br_netfilter)Prerouting.
 *    -> skb->_nfct now references a unconfirmed entry
 * 2. skb is broad/mcast packet. bridge now passes clones out on each bridge
 *    interface.
 * 3. skb gets passed up the stack.
 * 4. In macvlan case, macvlan driver retains clone(s) of the mcast skb
 *    and schedules a work queue to send them out on the lower devices.
 *
 *    The clone skb->_nfct is not a copy, it is the same entry as the
 *    original skb.  The macvlan rx handler then returns RX_HANDLER_PASS.
 * 5. Normal conntrack hooks (in NF_INET_LOCAL_IN) confirm the orig skb.
 *
 * The Macvlan broadcast worker and normal confirm path will race.
 *
 * This race will not happen if step 2 already confirmed a clone. In that
 * case later steps perform skb_clone() with skb->_nfct already confirmed (in
 * hash table).  This works fine.
 *
 * But such confirmation won't happen when eb/ip/nftables rules dropped the
 * packets before they reached the nf_confirm step in postrouting.
 *
 * Work around this problem by explicit confirmation of the entry at
 * LOCAL_IN time, before upper layer has a chance to clone the unconfirmed
 * entry.
 *
 */
static unsigned int br_nf_local_in(void *priv,
				   struct sk_buff *skb,
				   const struct nf_hook_state *state)
{
	bool promisc = BR_INPUT_SKB_CB(skb)->promisc;
	struct nf_conntrack *nfct = skb_nfct(skb);
	const struct nf_ct_hook *ct_hook;
	struct nf_conn *ct;
	int ret;

	if (promisc) {
		nf_reset_ct(skb);
		return NF_ACCEPT;
	}

	if (!nfct || skb->pkt_type == PACKET_HOST)
		return NF_ACCEPT;

	ct = container_of(nfct, struct nf_conn, ct_general);
	if (likely(nf_ct_is_confirmed(ct)))
		return NF_ACCEPT;

	WARN_ON_ONCE(skb_shared(skb));
	WARN_ON_ONCE(atomic_read(&nfct->use) != 1);

	/* We can't call nf_confirm here, it would create a dependency
	 * on nf_conntrack module.
	 */
	ct_hook = rcu_dereference(nf_ct_hook);
	if (!ct_hook) {
		skb->_nfct = 0ul;
		nf_conntrack_put(nfct);
		return NF_ACCEPT;
	}

	nf_bridge_pull_encap_header(skb);
	ret = ct_hook->confirm(skb);
	switch (ret & NF_VERDICT_MASK) {
	case NF_STOLEN:
		return NF_STOLEN;
	default:
		nf_bridge_push_encap_header(skb);
		break;
	}

	ct = container_of(nfct, struct nf_conn, ct_general);
	WARN_ON_ONCE(!nf_ct_is_confirmed(ct));

	return ret;
}
#endif

/* PF_BRIDGE/FORWARD *************************************************/
static int br_nf_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
@@ -960,6 +1054,14 @@ static const struct nf_hook_ops br_nf_ops[] = {
		.hooknum = NF_BR_PRE_ROUTING,
		.priority = NF_BR_PRI_BRNF,
	},
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
	{
		.hook = br_nf_local_in,
		.pf = NFPROTO_BRIDGE,
		.hooknum = NF_BR_LOCAL_IN,
		.priority = NF_BR_PRI_LAST,
	},
#endif
	{
		.hook = br_nf_forward_ip,
		.pf = NFPROTO_BRIDGE,
+1 −0
Original line number Diff line number Diff line
@@ -497,6 +497,7 @@ struct br_input_skb_cb {
#endif
	u8 proxyarp_replied:1;
	u8 src_port_isolated:1;
	u8 promisc:1;
#ifdef CONFIG_BRIDGE_VLAN_FILTERING
	u8 vlan_filtered:1;
#endif
+36 −0
Original line number Diff line number Diff line
@@ -290,6 +290,36 @@ static unsigned int nf_ct_bridge_pre(void *priv, struct sk_buff *skb,
	return nf_conntrack_in(skb, &bridge_state);
}

static unsigned int nf_ct_bridge_in(void *priv, struct sk_buff *skb,
				    const struct nf_hook_state *state)
{
	bool promisc = BR_INPUT_SKB_CB(skb)->promisc;
	struct nf_conntrack *nfct = skb_nfct(skb);
	struct nf_conn *ct;

	if (promisc) {
		nf_reset_ct(skb);
		return NF_ACCEPT;
	}

	if (!nfct || skb->pkt_type == PACKET_HOST)
		return NF_ACCEPT;

	/* nf_conntrack_confirm() cannot handle concurrent clones,
	 * this happens for broad/multicast frames with e.g. macvlan on top
	 * of the bridge device.
	 */
	ct = container_of(nfct, struct nf_conn, ct_general);
	if (nf_ct_is_confirmed(ct) || nf_ct_is_template(ct))
		return NF_ACCEPT;

	/* let inet prerouting call conntrack again */
	skb->_nfct = 0;
	nf_ct_put(ct);

	return NF_ACCEPT;
}

static void nf_ct_bridge_frag_save(struct sk_buff *skb,
				   struct nf_bridge_frag_data *data)
{
@@ -414,6 +444,12 @@ static struct nf_hook_ops nf_ct_bridge_hook_ops[] __read_mostly = {
		.hooknum	= NF_BR_PRE_ROUTING,
		.priority	= NF_IP_PRI_CONNTRACK,
	},
	{
		.hook		= nf_ct_bridge_in,
		.pf		= NFPROTO_BRIDGE,
		.hooknum	= NF_BR_LOCAL_IN,
		.priority	= NF_IP_PRI_CONNTRACK_CONFIRM,
	},
	{
		.hook		= nf_ct_bridge_post,
		.pf		= NFPROTO_BRIDGE,
Loading