| From 076ed3da0c9b2f88d9157dbe7044a45641ae369e Mon Sep 17 00:00:00 2001 |
| From: Stefan Nuernberger <snu@amazon.com> |
| Date: Mon, 17 Sep 2018 19:46:53 +0200 |
| Subject: net/ipv4: defensive cipso option parsing |
| |
| From: Stefan Nuernberger <snu@amazon.com> |
| |
| commit 076ed3da0c9b2f88d9157dbe7044a45641ae369e upstream. |
| |
| commit 40413955ee26 ("Cipso: cipso_v4_optptr enter infinite loop") fixed |
| a possible infinite loop in the IP option parsing of CIPSO. The fix |
| assumes that ip_options_compile filtered out all zero length options and |
| that no other one-byte options beside IPOPT_END and IPOPT_NOOP exist. |
| While this assumption currently holds true, add explicit checks for zero |
| length and invalid length options to be safe for the future. Even though |
| ip_options_compile should have validated the options, the introduction of |
| new one-byte options can still confuse this code without the additional |
| checks. |
| |
| Signed-off-by: Stefan Nuernberger <snu@amazon.com> |
| Cc: David Woodhouse <dwmw@amazon.co.uk> |
| Cc: Simon Veith <sveith@amazon.de> |
| Cc: stable@vger.kernel.org |
| Acked-by: Paul Moore <paul@paul-moore.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| net/ipv4/cipso_ipv4.c | 11 +++++++---- |
| 1 file changed, 7 insertions(+), 4 deletions(-) |
| |
| --- a/net/ipv4/cipso_ipv4.c |
| +++ b/net/ipv4/cipso_ipv4.c |
| @@ -1512,7 +1512,7 @@ static int cipso_v4_parsetag_loc(const s |
| * |
| * Description: |
| * Parse the packet's IP header looking for a CIPSO option. Returns a pointer |
| - * to the start of the CIPSO option on success, NULL if one if not found. |
| + * to the start of the CIPSO option on success, NULL if one is not found. |
| * |
| */ |
| unsigned char *cipso_v4_optptr(const struct sk_buff *skb) |
| @@ -1522,10 +1522,8 @@ unsigned char *cipso_v4_optptr(const str |
| int optlen; |
| int taglen; |
| |
| - for (optlen = iph->ihl*4 - sizeof(struct iphdr); optlen > 0; ) { |
| + for (optlen = iph->ihl*4 - sizeof(struct iphdr); optlen > 1; ) { |
| switch (optptr[0]) { |
| - case IPOPT_CIPSO: |
| - return optptr; |
| case IPOPT_END: |
| return NULL; |
| case IPOPT_NOOP: |
| @@ -1534,6 +1532,11 @@ unsigned char *cipso_v4_optptr(const str |
| default: |
| taglen = optptr[1]; |
| } |
| + if (!taglen || taglen > optlen) |
| + return NULL; |
| + if (optptr[0] == IPOPT_CIPSO) |
| + return optptr; |
| + |
| optlen -= taglen; |
| optptr += taglen; |
| } |