diff --git a/debian/changelog b/debian/changelog index e70466184..451922e03 100644 --- a/debian/changelog +++ b/debian/changelog @@ -233,6 +233,9 @@ linux (4.5.1-1) UNRELEASED; urgency=medium * linux-support: Include udeb configuration from debian/installer for use by the linux-signed package * Set ABI to 1 + * netfilter: x_tables: Fix parsing of IPT_SO_SET_REPLACE blobs (CVE-2016-3134) + - validate e->target_offset early + - make sure e->next_offset covers remaining blob size [ Aurelien Jarno ] * [mipsel/mips/config.loongson-2f] Disable VIDEO_CX23885, VIDEO_IVTV, diff --git a/debian/patches/bugfix/all/netfilter-x_tables-make-sure-e-next_offset-covers-re.patch b/debian/patches/bugfix/all/netfilter-x_tables-make-sure-e-next_offset-covers-re.patch new file mode 100644 index 000000000..5b29f88af --- /dev/null +++ b/debian/patches/bugfix/all/netfilter-x_tables-make-sure-e-next_offset-covers-re.patch @@ -0,0 +1,88 @@ +From: Florian Westphal +Date: Tue, 22 Mar 2016 18:02:50 +0100 +Subject: [2/2] netfilter: x_tables: make sure e->next_offset covers remaining + blob size +Origin: https://git.kernel.org/linus/6e94e0cfb0887e4013b3b930fa6ab1fe6bb6ba91 + +Otherwise this function may read data beyond the ruleset blob. + +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +--- + net/ipv4/netfilter/arp_tables.c | 6 ++++-- + net/ipv4/netfilter/ip_tables.c | 6 ++++-- + net/ipv6/netfilter/ip6_tables.c | 6 ++++-- + 3 files changed, 12 insertions(+), 6 deletions(-) + +diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c +index 830bbe8ec13d..51d4fe56b807 100644 +--- a/net/ipv4/netfilter/arp_tables.c ++++ b/net/ipv4/netfilter/arp_tables.c +@@ -573,7 +573,8 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e, + int err; + + if ((unsigned long)e % __alignof__(struct arpt_entry) != 0 || +- (unsigned char *)e + sizeof(struct arpt_entry) >= limit) { ++ (unsigned char *)e + sizeof(struct arpt_entry) >= limit || ++ (unsigned char *)e + e->next_offset > limit) { + duprintf("Bad offset %p\n", e); + return -EINVAL; + } +@@ -1232,7 +1233,8 @@ check_compat_entry_size_and_hooks(struct compat_arpt_entry *e, + + duprintf("check_compat_entry_size_and_hooks %p\n", e); + if ((unsigned long)e % __alignof__(struct compat_arpt_entry) != 0 || +- (unsigned char *)e + sizeof(struct compat_arpt_entry) >= limit) { ++ (unsigned char *)e + sizeof(struct compat_arpt_entry) >= limit || ++ (unsigned char *)e + e->next_offset > limit) { + duprintf("Bad offset %p, limit = %p\n", e, limit); + return -EINVAL; + } +diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c +index 1d72a3c4a7e7..fb7694e6663e 100644 +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -738,7 +738,8 @@ check_entry_size_and_hooks(struct ipt_entry *e, + int err; + + if ((unsigned long)e % __alignof__(struct ipt_entry) != 0 || +- (unsigned char *)e + sizeof(struct ipt_entry) >= limit) { ++ (unsigned char *)e + sizeof(struct ipt_entry) >= limit || ++ (unsigned char *)e + e->next_offset > limit) { + duprintf("Bad offset %p\n", e); + return -EINVAL; + } +@@ -1492,7 +1493,8 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e, + + duprintf("check_compat_entry_size_and_hooks %p\n", e); + if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0 || +- (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit) { ++ (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit || ++ (unsigned char *)e + e->next_offset > limit) { + duprintf("Bad offset %p, limit = %p\n", e, limit); + return -EINVAL; + } +diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c +index 26a5ad1cc4fd..b248528f2a17 100644 +--- a/net/ipv6/netfilter/ip6_tables.c ++++ b/net/ipv6/netfilter/ip6_tables.c +@@ -750,7 +750,8 @@ check_entry_size_and_hooks(struct ip6t_entry *e, + int err; + + if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0 || +- (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) { ++ (unsigned char *)e + sizeof(struct ip6t_entry) >= limit || ++ (unsigned char *)e + e->next_offset > limit) { + duprintf("Bad offset %p\n", e); + return -EINVAL; + } +@@ -1504,7 +1505,8 @@ check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e, + + duprintf("check_compat_entry_size_and_hooks %p\n", e); + if ((unsigned long)e % __alignof__(struct compat_ip6t_entry) != 0 || +- (unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit) { ++ (unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit || ++ (unsigned char *)e + e->next_offset > limit) { + duprintf("Bad offset %p, limit = %p\n", e, limit); + return -EINVAL; + } diff --git a/debian/patches/bugfix/all/netfilter-x_tables-validate-e-target_offset-early.patch b/debian/patches/bugfix/all/netfilter-x_tables-validate-e-target_offset-early.patch new file mode 100644 index 000000000..6db0739c6 --- /dev/null +++ b/debian/patches/bugfix/all/netfilter-x_tables-validate-e-target_offset-early.patch @@ -0,0 +1,197 @@ +From: Florian Westphal +Date: Tue, 22 Mar 2016 18:02:49 +0100 +Subject: [1/2] netfilter: x_tables: validate e->target_offset early +Origin: https://git.kernel.org/linus/bdf533de6968e9686df777dc178486f600c6e617 + +We should check that e->target_offset is sane before +mark_source_chains gets called since it will fetch the target entry +for loop detection. + +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +--- + net/ipv4/netfilter/arp_tables.c | 17 ++++++++--------- + net/ipv4/netfilter/ip_tables.c | 17 ++++++++--------- + net/ipv6/netfilter/ip6_tables.c | 17 ++++++++--------- + 3 files changed, 24 insertions(+), 27 deletions(-) + +diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c +index bf081927e06b..830bbe8ec13d 100644 +--- a/net/ipv4/netfilter/arp_tables.c ++++ b/net/ipv4/netfilter/arp_tables.c +@@ -474,14 +474,12 @@ next: + return 1; + } + +-static inline int check_entry(const struct arpt_entry *e, const char *name) ++static inline int check_entry(const struct arpt_entry *e) + { + const struct xt_entry_target *t; + +- if (!arp_checkentry(&e->arp)) { +- duprintf("arp_tables: arp check failed %p %s.\n", e, name); ++ if (!arp_checkentry(&e->arp)) + return -EINVAL; +- } + + if (e->target_offset + sizeof(struct xt_entry_target) > e->next_offset) + return -EINVAL; +@@ -522,10 +520,6 @@ find_check_entry(struct arpt_entry *e, const char *name, unsigned int size) + struct xt_target *target; + int ret; + +- ret = check_entry(e, name); +- if (ret) +- return ret; +- + e->counters.pcnt = xt_percpu_counter_alloc(); + if (IS_ERR_VALUE(e->counters.pcnt)) + return -ENOMEM; +@@ -576,6 +570,7 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e, + unsigned int valid_hooks) + { + unsigned int h; ++ int err; + + if ((unsigned long)e % __alignof__(struct arpt_entry) != 0 || + (unsigned char *)e + sizeof(struct arpt_entry) >= limit) { +@@ -590,6 +585,10 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e, + return -EINVAL; + } + ++ err = check_entry(e); ++ if (err) ++ return err; ++ + /* Check hooks & underflows */ + for (h = 0; h < NF_ARP_NUMHOOKS; h++) { + if (!(valid_hooks & (1 << h))) +@@ -1246,7 +1245,7 @@ check_compat_entry_size_and_hooks(struct compat_arpt_entry *e, + } + + /* For purposes of check_entry casting the compat entry is fine */ +- ret = check_entry((struct arpt_entry *)e, name); ++ ret = check_entry((struct arpt_entry *)e); + if (ret) + return ret; + +diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c +index e53f8d6f326d..1d72a3c4a7e7 100644 +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -569,14 +569,12 @@ static void cleanup_match(struct xt_entry_match *m, struct net *net) + } + + static int +-check_entry(const struct ipt_entry *e, const char *name) ++check_entry(const struct ipt_entry *e) + { + const struct xt_entry_target *t; + +- if (!ip_checkentry(&e->ip)) { +- duprintf("ip check failed %p %s.\n", e, name); ++ if (!ip_checkentry(&e->ip)) + return -EINVAL; +- } + + if (e->target_offset + sizeof(struct xt_entry_target) > + e->next_offset) +@@ -666,10 +664,6 @@ find_check_entry(struct ipt_entry *e, struct net *net, const char *name, + struct xt_mtchk_param mtpar; + struct xt_entry_match *ematch; + +- ret = check_entry(e, name); +- if (ret) +- return ret; +- + e->counters.pcnt = xt_percpu_counter_alloc(); + if (IS_ERR_VALUE(e->counters.pcnt)) + return -ENOMEM; +@@ -741,6 +735,7 @@ check_entry_size_and_hooks(struct ipt_entry *e, + unsigned int valid_hooks) + { + unsigned int h; ++ int err; + + if ((unsigned long)e % __alignof__(struct ipt_entry) != 0 || + (unsigned char *)e + sizeof(struct ipt_entry) >= limit) { +@@ -755,6 +750,10 @@ check_entry_size_and_hooks(struct ipt_entry *e, + return -EINVAL; + } + ++ err = check_entry(e); ++ if (err) ++ return err; ++ + /* Check hooks & underflows */ + for (h = 0; h < NF_INET_NUMHOOKS; h++) { + if (!(valid_hooks & (1 << h))) +@@ -1506,7 +1505,7 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e, + } + + /* For purposes of check_entry casting the compat entry is fine */ +- ret = check_entry((struct ipt_entry *)e, name); ++ ret = check_entry((struct ipt_entry *)e); + if (ret) + return ret; + +diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c +index 84f9baf7aee8..26a5ad1cc4fd 100644 +--- a/net/ipv6/netfilter/ip6_tables.c ++++ b/net/ipv6/netfilter/ip6_tables.c +@@ -581,14 +581,12 @@ static void cleanup_match(struct xt_entry_match *m, struct net *net) + } + + static int +-check_entry(const struct ip6t_entry *e, const char *name) ++check_entry(const struct ip6t_entry *e) + { + const struct xt_entry_target *t; + +- if (!ip6_checkentry(&e->ipv6)) { +- duprintf("ip_tables: ip check failed %p %s.\n", e, name); ++ if (!ip6_checkentry(&e->ipv6)) + return -EINVAL; +- } + + if (e->target_offset + sizeof(struct xt_entry_target) > + e->next_offset) +@@ -679,10 +677,6 @@ find_check_entry(struct ip6t_entry *e, struct net *net, const char *name, + struct xt_mtchk_param mtpar; + struct xt_entry_match *ematch; + +- ret = check_entry(e, name); +- if (ret) +- return ret; +- + e->counters.pcnt = xt_percpu_counter_alloc(); + if (IS_ERR_VALUE(e->counters.pcnt)) + return -ENOMEM; +@@ -753,6 +747,7 @@ check_entry_size_and_hooks(struct ip6t_entry *e, + unsigned int valid_hooks) + { + unsigned int h; ++ int err; + + if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0 || + (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) { +@@ -767,6 +762,10 @@ check_entry_size_and_hooks(struct ip6t_entry *e, + return -EINVAL; + } + ++ err = check_entry(e); ++ if (err) ++ return err; ++ + /* Check hooks & underflows */ + for (h = 0; h < NF_INET_NUMHOOKS; h++) { + if (!(valid_hooks & (1 << h))) +@@ -1518,7 +1517,7 @@ check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e, + } + + /* For purposes of check_entry casting the compat entry is fine */ +- ret = check_entry((struct ip6t_entry *)e, name); ++ ret = check_entry((struct ip6t_entry *)e); + if (ret) + return ret; + diff --git a/debian/patches/series b/debian/patches/series index 811e0d329..2d1301184 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -138,3 +138,5 @@ bugfix/all/tools-build-remove-bpf-run-time-check-at-build-time.patch bugfix/all/power-cpupower-fix-manpages-NAME.patch bugfix/all/tools-lib-traceevent-fix-use-of-uninitialized-variables.patch bugfix/all/scripts-fix-x.509-pem-support-in-sign-file.patch +bugfix/all/netfilter-x_tables-validate-e-target_offset-early.patch +bugfix/all/netfilter-x_tables-make-sure-e-next_offset-covers-re.patch