aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2014-11-07 08:27:09 -0500
committerDavid S. Miller <davem@davemloft.net>2014-11-10 14:25:35 -0500
commitc008ba5bdc9fa830e1a349b20b0be5a137bdef7a (patch)
treedb116a813dd808850455b36ba1c5e34b523a251e /net/ipv4
parent32b5913a931fd753faf3d4e1124b2bc2edb364da (diff)
ipv4: Avoid reading user iov twice after raw_probe_proto_opt
Ever since raw_probe_proto_opt was added it had the problem of causing the user iov to be read twice, once during the probe for the protocol header and once again in ip_append_data. This is a potential security problem since it means that whatever we're probing may be invalid. This patch plugs the hole by firstly advancing the iov so we don't read the same spot again, and secondly saving what we read the first time around for use by ip_append_data. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r--net/ipv4/raw.c62
1 files changed, 54 insertions, 8 deletions
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 9be905057b24..43385a9fa441 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -79,6 +79,16 @@
79#include <linux/netfilter.h> 79#include <linux/netfilter.h>
80#include <linux/netfilter_ipv4.h> 80#include <linux/netfilter_ipv4.h>
81#include <linux/compat.h> 81#include <linux/compat.h>
82#include <linux/uio.h>
83
84struct raw_frag_vec {
85 struct iovec *iov;
86 union {
87 struct icmphdr icmph;
88 char c[1];
89 } hdr;
90 int hlen;
91};
82 92
83static struct raw_hashinfo raw_v4_hashinfo = { 93static struct raw_hashinfo raw_v4_hashinfo = {
84 .lock = __RW_LOCK_UNLOCKED(raw_v4_hashinfo.lock), 94 .lock = __RW_LOCK_UNLOCKED(raw_v4_hashinfo.lock),
@@ -420,25 +430,57 @@ error:
420 return err; 430 return err;
421} 431}
422 432
423static int raw_probe_proto_opt(struct flowi4 *fl4, struct msghdr *msg) 433static int raw_probe_proto_opt(struct raw_frag_vec *rfv, struct flowi4 *fl4)
424{ 434{
425 struct icmphdr icmph;
426 int err; 435 int err;
427 436
428 if (fl4->flowi4_proto != IPPROTO_ICMP) 437 if (fl4->flowi4_proto != IPPROTO_ICMP)
429 return 0; 438 return 0;
430 439
431 /* We only need the first two bytes. */ 440 /* We only need the first two bytes. */
432 err = memcpy_fromiovecend((void *)&icmph, msg->msg_iov, 0, 2); 441 rfv->hlen = 2;
442
443 err = memcpy_fromiovec(rfv->hdr.c, rfv->iov, rfv->hlen);
433 if (err) 444 if (err)
434 return err; 445 return err;
435 446
436 fl4->fl4_icmp_type = icmph.type; 447 fl4->fl4_icmp_type = rfv->hdr.icmph.type;
437 fl4->fl4_icmp_code = icmph.code; 448 fl4->fl4_icmp_code = rfv->hdr.icmph.code;
438 449
439 return 0; 450 return 0;
440} 451}
441 452
453static int raw_getfrag(void *from, char *to, int offset, int len, int odd,
454 struct sk_buff *skb)
455{
456 struct raw_frag_vec *rfv = from;
457
458 if (offset < rfv->hlen) {
459 int copy = min(rfv->hlen - offset, len);
460
461 if (skb->ip_summed == CHECKSUM_PARTIAL)
462 memcpy(to, rfv->hdr.c + offset, copy);
463 else
464 skb->csum = csum_block_add(
465 skb->csum,
466 csum_partial_copy_nocheck(rfv->hdr.c + offset,
467 to, copy, 0),
468 odd);
469
470 odd = 0;
471 offset += copy;
472 to += copy;
473 len -= copy;
474
475 if (!len)
476 return 0;
477 }
478
479 offset -= rfv->hlen;
480
481 return ip_generic_getfrag(rfv->iov, to, offset, len, odd, skb);
482}
483
442static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, 484static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
443 size_t len) 485 size_t len)
444{ 486{
@@ -452,6 +494,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
452 u8 tos; 494 u8 tos;
453 int err; 495 int err;
454 struct ip_options_data opt_copy; 496 struct ip_options_data opt_copy;
497 struct raw_frag_vec rfv;
455 498
456 err = -EMSGSIZE; 499 err = -EMSGSIZE;
457 if (len > 0xFFFF) 500 if (len > 0xFFFF)
@@ -557,7 +600,10 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
557 daddr, saddr, 0, 0); 600 daddr, saddr, 0, 0);
558 601
559 if (!inet->hdrincl) { 602 if (!inet->hdrincl) {
560 err = raw_probe_proto_opt(&fl4, msg); 603 rfv.iov = msg->msg_iov;
604 rfv.hlen = 0;
605
606 err = raw_probe_proto_opt(&rfv, &fl4);
561 if (err) 607 if (err)
562 goto done; 608 goto done;
563 } 609 }
@@ -588,8 +634,8 @@ back_from_confirm:
588 if (!ipc.addr) 634 if (!ipc.addr)
589 ipc.addr = fl4.daddr; 635 ipc.addr = fl4.daddr;
590 lock_sock(sk); 636 lock_sock(sk);
591 err = ip_append_data(sk, &fl4, ip_generic_getfrag, 637 err = ip_append_data(sk, &fl4, raw_getfrag,
592 msg->msg_iov, len, 0, 638 &rfv, len, 0,
593 &ipc, &rt, msg->msg_flags); 639 &ipc, &rt, msg->msg_flags);
594 if (err) 640 if (err)
595 ip_flush_pending_frames(sk); 641 ip_flush_pending_frames(sk);