Commit 963bdfc7 authored by David S. Miller's avatar David S. Miller

Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf

Pablo Neira Ayuso says:

====================
Netfilter fixes for net

The following patchset contains Netfilter fixes for net:

1) Set VLAN tag in tcp reset/icmp unreachable packets to reject
   connections in the bridge family, from Michael Braun.

2) Incorrect subcounter flag update in ipset, from Phil Sutter.

3) Possible buffer overflow in the pptp conntrack helper, based
   on patch from Dan Carpenter.

4) Restore userspace conntrack helper hook logic that broke after
   hook consolidation rework.

5) Unbreak userspace conntrack helper registration via
   nfnetlink_cthelper.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 1a6da4fc 703acd70
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
#include <net/netfilter/nf_conntrack_expect.h> #include <net/netfilter/nf_conntrack_expect.h>
#include <uapi/linux/netfilter/nf_conntrack_tuple_common.h> #include <uapi/linux/netfilter/nf_conntrack_tuple_common.h>
extern const char *const pptp_msg_name[]; extern const char *const pptp_msg_name(u_int16_t msg);
/* state of the control session */ /* state of the control session */
enum pptp_ctrlsess_state { enum pptp_ctrlsess_state {
......
...@@ -31,6 +31,12 @@ static void nft_reject_br_push_etherhdr(struct sk_buff *oldskb, ...@@ -31,6 +31,12 @@ static void nft_reject_br_push_etherhdr(struct sk_buff *oldskb,
ether_addr_copy(eth->h_dest, eth_hdr(oldskb)->h_source); ether_addr_copy(eth->h_dest, eth_hdr(oldskb)->h_source);
eth->h_proto = eth_hdr(oldskb)->h_proto; eth->h_proto = eth_hdr(oldskb)->h_proto;
skb_pull(nskb, ETH_HLEN); skb_pull(nskb, ETH_HLEN);
if (skb_vlan_tag_present(oldskb)) {
u16 vid = skb_vlan_tag_get(oldskb);
__vlan_hwaccel_put_tag(nskb, oldskb->vlan_proto, vid);
}
} }
static int nft_bridge_iphdr_validate(struct sk_buff *skb) static int nft_bridge_iphdr_validate(struct sk_buff *skb)
......
...@@ -166,8 +166,7 @@ pptp_outbound_pkt(struct sk_buff *skb, ...@@ -166,8 +166,7 @@ pptp_outbound_pkt(struct sk_buff *skb,
break; break;
default: default:
pr_debug("unknown outbound packet 0x%04x:%s\n", msg, pr_debug("unknown outbound packet 0x%04x:%s\n", msg,
msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] : pptp_msg_name(msg));
pptp_msg_name[0]);
fallthrough; fallthrough;
case PPTP_SET_LINK_INFO: case PPTP_SET_LINK_INFO:
/* only need to NAT in case PAC is behind NAT box */ /* only need to NAT in case PAC is behind NAT box */
...@@ -268,9 +267,7 @@ pptp_inbound_pkt(struct sk_buff *skb, ...@@ -268,9 +267,7 @@ pptp_inbound_pkt(struct sk_buff *skb,
pcid_off = offsetof(union pptp_ctrl_union, setlink.peersCallID); pcid_off = offsetof(union pptp_ctrl_union, setlink.peersCallID);
break; break;
default: default:
pr_debug("unknown inbound packet %s\n", pr_debug("unknown inbound packet %s\n", pptp_msg_name(msg));
msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] :
pptp_msg_name[0]);
fallthrough; fallthrough;
case PPTP_START_SESSION_REQUEST: case PPTP_START_SESSION_REQUEST:
case PPTP_START_SESSION_REPLY: case PPTP_START_SESSION_REPLY:
......
...@@ -59,7 +59,7 @@ list_set_ktest(struct ip_set *set, const struct sk_buff *skb, ...@@ -59,7 +59,7 @@ list_set_ktest(struct ip_set *set, const struct sk_buff *skb,
/* Don't lookup sub-counters at all */ /* Don't lookup sub-counters at all */
opt->cmdflags &= ~IPSET_FLAG_MATCH_COUNTERS; opt->cmdflags &= ~IPSET_FLAG_MATCH_COUNTERS;
if (opt->cmdflags & IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE) if (opt->cmdflags & IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE)
opt->cmdflags &= ~IPSET_FLAG_SKIP_COUNTER_UPDATE; opt->cmdflags |= IPSET_FLAG_SKIP_COUNTER_UPDATE;
list_for_each_entry_rcu(e, &map->members, list) { list_for_each_entry_rcu(e, &map->members, list) {
ret = ip_set_test(e->id, skb, par, opt); ret = ip_set_test(e->id, skb, par, opt);
if (ret <= 0) if (ret <= 0)
......
...@@ -2016,22 +2016,18 @@ static void nf_conntrack_attach(struct sk_buff *nskb, const struct sk_buff *skb) ...@@ -2016,22 +2016,18 @@ static void nf_conntrack_attach(struct sk_buff *nskb, const struct sk_buff *skb)
nf_conntrack_get(skb_nfct(nskb)); nf_conntrack_get(skb_nfct(nskb));
} }
static int nf_conntrack_update(struct net *net, struct sk_buff *skb) static int __nf_conntrack_update(struct net *net, struct sk_buff *skb,
struct nf_conn *ct)
{ {
struct nf_conntrack_tuple_hash *h; struct nf_conntrack_tuple_hash *h;
struct nf_conntrack_tuple tuple; struct nf_conntrack_tuple tuple;
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
struct nf_nat_hook *nat_hook; struct nf_nat_hook *nat_hook;
unsigned int status; unsigned int status;
struct nf_conn *ct;
int dataoff; int dataoff;
u16 l3num; u16 l3num;
u8 l4num; u8 l4num;
ct = nf_ct_get(skb, &ctinfo);
if (!ct || nf_ct_is_confirmed(ct))
return 0;
l3num = nf_ct_l3num(ct); l3num = nf_ct_l3num(ct);
dataoff = get_l4proto(skb, skb_network_offset(skb), l3num, &l4num); dataoff = get_l4proto(skb, skb_network_offset(skb), l3num, &l4num);
...@@ -2088,6 +2084,76 @@ static int nf_conntrack_update(struct net *net, struct sk_buff *skb) ...@@ -2088,6 +2084,76 @@ static int nf_conntrack_update(struct net *net, struct sk_buff *skb)
return 0; return 0;
} }
/* This packet is coming from userspace via nf_queue, complete the packet
* processing after the helper invocation in nf_confirm().
*/
static int nf_confirm_cthelper(struct sk_buff *skb, struct nf_conn *ct,
enum ip_conntrack_info ctinfo)
{
const struct nf_conntrack_helper *helper;
const struct nf_conn_help *help;
unsigned int protoff;
help = nfct_help(ct);
if (!help)
return 0;
helper = rcu_dereference(help->helper);
if (!(helper->flags & NF_CT_HELPER_F_USERSPACE))
return 0;
switch (nf_ct_l3num(ct)) {
case NFPROTO_IPV4:
protoff = skb_network_offset(skb) + ip_hdrlen(skb);
break;
#if IS_ENABLED(CONFIG_IPV6)
case NFPROTO_IPV6: {
__be16 frag_off;
u8 pnum;
pnum = ipv6_hdr(skb)->nexthdr;
protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &pnum,
&frag_off);
if (protoff < 0 || (frag_off & htons(~0x7)) != 0)
return 0;
break;
}
#endif
default:
return 0;
}
if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) &&
!nf_is_loopback_packet(skb)) {
if (!nf_ct_seq_adjust(skb, ct, ctinfo, protoff)) {
NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop);
return -1;
}
}
/* We've seen it coming out the other side: confirm it */
return nf_conntrack_confirm(skb) == NF_DROP ? - 1 : 0;
}
static int nf_conntrack_update(struct net *net, struct sk_buff *skb)
{
enum ip_conntrack_info ctinfo;
struct nf_conn *ct;
int err;
ct = nf_ct_get(skb, &ctinfo);
if (!ct)
return 0;
if (!nf_ct_is_confirmed(ct)) {
err = __nf_conntrack_update(net, skb, ct);
if (err < 0)
return err;
}
return nf_confirm_cthelper(skb, ct, ctinfo);
}
static bool nf_conntrack_get_tuple_skb(struct nf_conntrack_tuple *dst_tuple, static bool nf_conntrack_get_tuple_skb(struct nf_conntrack_tuple *dst_tuple,
const struct sk_buff *skb) const struct sk_buff *skb)
{ {
......
...@@ -72,24 +72,32 @@ EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_expectfn); ...@@ -72,24 +72,32 @@ EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_expectfn);
#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) #if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG)
/* PptpControlMessageType names */ /* PptpControlMessageType names */
const char *const pptp_msg_name[] = { static const char *const pptp_msg_name_array[PPTP_MSG_MAX + 1] = {
"UNKNOWN_MESSAGE", [0] = "UNKNOWN_MESSAGE",
"START_SESSION_REQUEST", [PPTP_START_SESSION_REQUEST] = "START_SESSION_REQUEST",
"START_SESSION_REPLY", [PPTP_START_SESSION_REPLY] = "START_SESSION_REPLY",
"STOP_SESSION_REQUEST", [PPTP_STOP_SESSION_REQUEST] = "STOP_SESSION_REQUEST",
"STOP_SESSION_REPLY", [PPTP_STOP_SESSION_REPLY] = "STOP_SESSION_REPLY",
"ECHO_REQUEST", [PPTP_ECHO_REQUEST] = "ECHO_REQUEST",
"ECHO_REPLY", [PPTP_ECHO_REPLY] = "ECHO_REPLY",
"OUT_CALL_REQUEST", [PPTP_OUT_CALL_REQUEST] = "OUT_CALL_REQUEST",
"OUT_CALL_REPLY", [PPTP_OUT_CALL_REPLY] = "OUT_CALL_REPLY",
"IN_CALL_REQUEST", [PPTP_IN_CALL_REQUEST] = "IN_CALL_REQUEST",
"IN_CALL_REPLY", [PPTP_IN_CALL_REPLY] = "IN_CALL_REPLY",
"IN_CALL_CONNECT", [PPTP_IN_CALL_CONNECT] = "IN_CALL_CONNECT",
"CALL_CLEAR_REQUEST", [PPTP_CALL_CLEAR_REQUEST] = "CALL_CLEAR_REQUEST",
"CALL_DISCONNECT_NOTIFY", [PPTP_CALL_DISCONNECT_NOTIFY] = "CALL_DISCONNECT_NOTIFY",
"WAN_ERROR_NOTIFY", [PPTP_WAN_ERROR_NOTIFY] = "WAN_ERROR_NOTIFY",
"SET_LINK_INFO" [PPTP_SET_LINK_INFO] = "SET_LINK_INFO"
}; };
const char *const pptp_msg_name(u_int16_t msg)
{
if (msg > PPTP_MSG_MAX)
return pptp_msg_name_array[0];
return pptp_msg_name_array[msg];
}
EXPORT_SYMBOL(pptp_msg_name); EXPORT_SYMBOL(pptp_msg_name);
#endif #endif
...@@ -276,7 +284,7 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff, ...@@ -276,7 +284,7 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff,
typeof(nf_nat_pptp_hook_inbound) nf_nat_pptp_inbound; typeof(nf_nat_pptp_hook_inbound) nf_nat_pptp_inbound;
msg = ntohs(ctlh->messageType); msg = ntohs(ctlh->messageType);
pr_debug("inbound control message %s\n", pptp_msg_name[msg]); pr_debug("inbound control message %s\n", pptp_msg_name(msg));
switch (msg) { switch (msg) {
case PPTP_START_SESSION_REPLY: case PPTP_START_SESSION_REPLY:
...@@ -311,7 +319,7 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff, ...@@ -311,7 +319,7 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff,
pcid = pptpReq->ocack.peersCallID; pcid = pptpReq->ocack.peersCallID;
if (info->pns_call_id != pcid) if (info->pns_call_id != pcid)
goto invalid; goto invalid;
pr_debug("%s, CID=%X, PCID=%X\n", pptp_msg_name[msg], pr_debug("%s, CID=%X, PCID=%X\n", pptp_msg_name(msg),
ntohs(cid), ntohs(pcid)); ntohs(cid), ntohs(pcid));
if (pptpReq->ocack.resultCode == PPTP_OUTCALL_CONNECT) { if (pptpReq->ocack.resultCode == PPTP_OUTCALL_CONNECT) {
...@@ -328,7 +336,7 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff, ...@@ -328,7 +336,7 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff,
goto invalid; goto invalid;
cid = pptpReq->icreq.callID; cid = pptpReq->icreq.callID;
pr_debug("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid)); pr_debug("%s, CID=%X\n", pptp_msg_name(msg), ntohs(cid));
info->cstate = PPTP_CALL_IN_REQ; info->cstate = PPTP_CALL_IN_REQ;
info->pac_call_id = cid; info->pac_call_id = cid;
break; break;
...@@ -347,7 +355,7 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff, ...@@ -347,7 +355,7 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff,
if (info->pns_call_id != pcid) if (info->pns_call_id != pcid)
goto invalid; goto invalid;
pr_debug("%s, PCID=%X\n", pptp_msg_name[msg], ntohs(pcid)); pr_debug("%s, PCID=%X\n", pptp_msg_name(msg), ntohs(pcid));
info->cstate = PPTP_CALL_IN_CONF; info->cstate = PPTP_CALL_IN_CONF;
/* we expect a GRE connection from PAC to PNS */ /* we expect a GRE connection from PAC to PNS */
...@@ -357,7 +365,7 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff, ...@@ -357,7 +365,7 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff,
case PPTP_CALL_DISCONNECT_NOTIFY: case PPTP_CALL_DISCONNECT_NOTIFY:
/* server confirms disconnect */ /* server confirms disconnect */
cid = pptpReq->disc.callID; cid = pptpReq->disc.callID;
pr_debug("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid)); pr_debug("%s, CID=%X\n", pptp_msg_name(msg), ntohs(cid));
info->cstate = PPTP_CALL_NONE; info->cstate = PPTP_CALL_NONE;
/* untrack this call id, unexpect GRE packets */ /* untrack this call id, unexpect GRE packets */
...@@ -384,7 +392,7 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff, ...@@ -384,7 +392,7 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff,
invalid: invalid:
pr_debug("invalid %s: type=%d cid=%u pcid=%u " pr_debug("invalid %s: type=%d cid=%u pcid=%u "
"cstate=%d sstate=%d pns_cid=%u pac_cid=%u\n", "cstate=%d sstate=%d pns_cid=%u pac_cid=%u\n",
msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] : pptp_msg_name[0], pptp_msg_name(msg),
msg, ntohs(cid), ntohs(pcid), info->cstate, info->sstate, msg, ntohs(cid), ntohs(pcid), info->cstate, info->sstate,
ntohs(info->pns_call_id), ntohs(info->pac_call_id)); ntohs(info->pns_call_id), ntohs(info->pac_call_id));
return NF_ACCEPT; return NF_ACCEPT;
...@@ -404,7 +412,7 @@ pptp_outbound_pkt(struct sk_buff *skb, unsigned int protoff, ...@@ -404,7 +412,7 @@ pptp_outbound_pkt(struct sk_buff *skb, unsigned int protoff,
typeof(nf_nat_pptp_hook_outbound) nf_nat_pptp_outbound; typeof(nf_nat_pptp_hook_outbound) nf_nat_pptp_outbound;
msg = ntohs(ctlh->messageType); msg = ntohs(ctlh->messageType);
pr_debug("outbound control message %s\n", pptp_msg_name[msg]); pr_debug("outbound control message %s\n", pptp_msg_name(msg));
switch (msg) { switch (msg) {
case PPTP_START_SESSION_REQUEST: case PPTP_START_SESSION_REQUEST:
...@@ -426,7 +434,7 @@ pptp_outbound_pkt(struct sk_buff *skb, unsigned int protoff, ...@@ -426,7 +434,7 @@ pptp_outbound_pkt(struct sk_buff *skb, unsigned int protoff,
info->cstate = PPTP_CALL_OUT_REQ; info->cstate = PPTP_CALL_OUT_REQ;
/* track PNS call id */ /* track PNS call id */
cid = pptpReq->ocreq.callID; cid = pptpReq->ocreq.callID;
pr_debug("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid)); pr_debug("%s, CID=%X\n", pptp_msg_name(msg), ntohs(cid));
info->pns_call_id = cid; info->pns_call_id = cid;
break; break;
...@@ -440,7 +448,7 @@ pptp_outbound_pkt(struct sk_buff *skb, unsigned int protoff, ...@@ -440,7 +448,7 @@ pptp_outbound_pkt(struct sk_buff *skb, unsigned int protoff,
pcid = pptpReq->icack.peersCallID; pcid = pptpReq->icack.peersCallID;
if (info->pac_call_id != pcid) if (info->pac_call_id != pcid)
goto invalid; goto invalid;
pr_debug("%s, CID=%X PCID=%X\n", pptp_msg_name[msg], pr_debug("%s, CID=%X PCID=%X\n", pptp_msg_name(msg),
ntohs(cid), ntohs(pcid)); ntohs(cid), ntohs(pcid));
if (pptpReq->icack.resultCode == PPTP_INCALL_ACCEPT) { if (pptpReq->icack.resultCode == PPTP_INCALL_ACCEPT) {
...@@ -480,7 +488,7 @@ pptp_outbound_pkt(struct sk_buff *skb, unsigned int protoff, ...@@ -480,7 +488,7 @@ pptp_outbound_pkt(struct sk_buff *skb, unsigned int protoff,
invalid: invalid:
pr_debug("invalid %s: type=%d cid=%u pcid=%u " pr_debug("invalid %s: type=%d cid=%u pcid=%u "
"cstate=%d sstate=%d pns_cid=%u pac_cid=%u\n", "cstate=%d sstate=%d pns_cid=%u pac_cid=%u\n",
msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] : pptp_msg_name[0], pptp_msg_name(msg),
msg, ntohs(cid), ntohs(pcid), info->cstate, info->sstate, msg, ntohs(cid), ntohs(pcid), info->cstate, info->sstate,
ntohs(info->pns_call_id), ntohs(info->pac_call_id)); ntohs(info->pns_call_id), ntohs(info->pac_call_id));
return NF_ACCEPT; return NF_ACCEPT;
......
...@@ -103,7 +103,7 @@ nfnl_cthelper_from_nlattr(struct nlattr *attr, struct nf_conn *ct) ...@@ -103,7 +103,7 @@ nfnl_cthelper_from_nlattr(struct nlattr *attr, struct nf_conn *ct)
if (help->helper->data_len == 0) if (help->helper->data_len == 0)
return -EINVAL; return -EINVAL;
nla_memcpy(help->data, nla_data(attr), sizeof(help->data)); nla_memcpy(help->data, attr, sizeof(help->data));
return 0; return 0;
} }
...@@ -240,6 +240,7 @@ nfnl_cthelper_create(const struct nlattr * const tb[], ...@@ -240,6 +240,7 @@ nfnl_cthelper_create(const struct nlattr * const tb[],
ret = -ENOMEM; ret = -ENOMEM;
goto err2; goto err2;
} }
helper->data_len = size;
helper->flags |= NF_CT_HELPER_F_USERSPACE; helper->flags |= NF_CT_HELPER_F_USERSPACE;
memcpy(&helper->tuple, tuple, sizeof(struct nf_conntrack_tuple)); memcpy(&helper->tuple, tuple, sizeof(struct nf_conntrack_tuple));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment