From 5225791117b564cd8b5683cf82d9eea45b0f0d59 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 1 May 2007 01:31:47 +0200 Subject: [IPV6]: Disallow RH0 by default (CVE-2007-2242) A security issue is emerging. Disallow Routing Header Type 0 by default as we have been doing for IPv4. This version already includes a fix for the original patch. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: Adrian Bunk --- Documentation/networking/ip-sysctl.txt | 9 +++++++++ include/linux/ipv6.h | 9 +++++++++ include/linux/sysctl.h | 1 + net/ipv6/addrconf.c | 11 ++++++++++ net/ipv6/exthdrs.c | 37 +++++++++++++++++++++++++++------- 5 files changed, 60 insertions(+), 7 deletions(-) diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 26364d06ae92..d512f227dda6 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -723,6 +723,15 @@ accept_redirects - BOOLEAN Functional default: enabled if local forwarding is disabled. disabled if local forwarding is enabled. +accept_source_route - INTEGER + Accept source routing (routing extension header). + + > 0: Accept routing header. + = 0: Accept only routing header type 2. + < 0: Do not accept routing header. + + Default: 0 + autoconf - BOOLEAN Autoconfigure addresses using Prefix Information in Router Advertisements. diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 9c8f4c9ed429..501ddc9ae2b3 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -145,6 +145,7 @@ struct ipv6_devconf { __s32 max_desync_factor; #endif __s32 max_addresses; + __s32 accept_source_route; void *sysctl; }; @@ -167,6 +168,14 @@ enum { DEVCONF_MAX_DESYNC_FACTOR, DEVCONF_MAX_ADDRESSES, DEVCONF_FORCE_MLD_VERSION, + __DEVCONF_ACCEPT_RA_DEFRTR, + __DEVCONF_ACCEPT_RA_PINFO, + __DEVCONF_ACCEPT_RA_RTR_PREF, + __DEVCONF_RTR_PROBE_INTERVAL, + __DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN, + __DEVCONF_PROXY_NDP, + __DEVCONF_OPTIMISTIC_DAD, + DEVCONF_ACCEPT_SOURCE_ROUTE, DEVCONF_MAX }; diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index bac61db26456..d84d8cb691ad 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -531,6 +531,7 @@ enum { NET_IPV6_MAX_DESYNC_FACTOR=15, NET_IPV6_MAX_ADDRESSES=16, NET_IPV6_FORCE_MLD_VERSION=17, + NET_IPV6_ACCEPT_SOURCE_ROUTE=25, __NET_IPV6_MAX }; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index e9e9894d2cab..eef33b83a406 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -169,6 +169,7 @@ struct ipv6_devconf ipv6_devconf = { .max_desync_factor = MAX_DESYNC_FACTOR, #endif .max_addresses = IPV6_MAX_ADDRESSES, + .accept_source_route = 0, }; static struct ipv6_devconf ipv6_devconf_dflt = { @@ -190,6 +191,7 @@ static struct ipv6_devconf ipv6_devconf_dflt = { .max_desync_factor = MAX_DESYNC_FACTOR, #endif .max_addresses = IPV6_MAX_ADDRESSES, + .accept_source_route = 0, }; /* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */ @@ -3153,6 +3155,7 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_MAX_DESYNC_FACTOR] = cnf->max_desync_factor; #endif array[DEVCONF_MAX_ADDRESSES] = cnf->max_addresses; + array[DEVCONF_ACCEPT_SOURCE_ROUTE] = cnf->accept_source_route; } /* Maximum length of ifinfomsg attributes */ @@ -3620,6 +3623,14 @@ static struct addrconf_sysctl_table .mode = 0644, .proc_handler = &proc_dointvec, }, + { + .ctl_name = NET_IPV6_ACCEPT_SOURCE_ROUTE, + .procname = "accept_source_route", + .data = &ipv6_devconf.accept_source_route, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, { .ctl_name = 0, /* sentinel */ } diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index af78fe8bb8e5..a7cac228f5b5 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -221,10 +221,27 @@ static int ipv6_rthdr_rcv(struct sk_buff **skbp) struct inet6_skb_parm *opt = IP6CB(skb); struct in6_addr *addr; struct in6_addr daddr; + struct inet6_dev *idev; int n, i; - struct ipv6_rt_hdr *hdr; struct rt0_hdr *rthdr; + int accept_source_route = ipv6_devconf.accept_source_route; + + if (accept_source_route < 0 || + ((idev = in6_dev_get(skb->dev)) == NULL)) { + kfree_skb(skb); + return -1; + } + if (idev->cnf.accept_source_route < 0) { + in6_dev_put(idev); + kfree_skb(skb); + return -1; + } + + if (accept_source_route > idev->cnf.accept_source_route) + accept_source_route = idev->cnf.accept_source_route; + + in6_dev_put(idev); if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) || !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) { @@ -235,6 +252,18 @@ static int ipv6_rthdr_rcv(struct sk_buff **skbp) hdr = (struct ipv6_rt_hdr *) skb->h.raw; + switch (hdr->type) { + case IPV6_SRCRT_TYPE_0: + if (accept_source_route > 0) + break; + kfree_skb(skb); + return -1; + default: + IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS); + icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw); + return -1; + } + if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr) || skb->pkt_type != PACKET_HOST) { IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS); @@ -253,12 +282,6 @@ looped_back: return 1; } - if (hdr->type != IPV6_SRCRT_TYPE_0) { - IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS); - icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw); - return -1; - } - if (hdr->hdrlen & 0x01) { IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS); icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->hdrlen) - skb->nh.raw); -- cgit v1.2.3