X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fipv4%2Fraw.c;h=d73ccfa2d825e235d1a25181c0672f6bf8f007cc;hb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;hp=d7b9238d1ea7093a012085ed78e8d9a60281eb6e;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index d7b9238d1..d73ccfa2d 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -102,6 +102,27 @@ static void raw_v4_unhash(struct sock *sk) write_unlock_bh(&raw_v4_lock); } + +/* + * Check if a given address matches for a socket + * + * nxi: the socket's nx_info if any + * addr: to be verified address + * saddr/baddr: socket addresses + */ +static inline int raw_addr_match ( + struct nx_info *nxi, + uint32_t addr, + uint32_t saddr, + uint32_t baddr) +{ + if (addr && (saddr == addr || baddr == addr)) + return 1; + if (!saddr) + return addr_in_nx_info(nxi, addr); + return 0; +} + struct sock *__raw_v4_lookup(struct sock *sk, unsigned short num, unsigned long raddr, unsigned long laddr, int dif) @@ -113,7 +134,8 @@ struct sock *__raw_v4_lookup(struct sock *sk, unsigned short num, if (inet->num == num && !(inet->daddr && inet->daddr != raddr) && - !(inet->rcv_saddr && inet->rcv_saddr != laddr) && + raw_addr_match(sk->sk_nx_info, laddr, + inet->rcv_saddr, inet->rcv_saddr2) && !(sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)) goto found; /* gotcha */ } @@ -305,6 +327,10 @@ static int raw_send_hdrinc(struct sock *sk, void *from, int length, iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); } + err = -EPERM; + if (!vx_check(0, VX_ADMIN) && !capable(CAP_NET_RAW) + && (!addr_in_nx_info(sk->sk_nx_info, iph->saddr))) + goto error; err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev, dst_output); @@ -319,10 +345,55 @@ error_fault: err = -EFAULT; kfree_skb(skb); error: - IP_INC_STATS(IpOutDiscards); + IP_INC_STATS(IPSTATS_MIB_OUTDISCARDS); return err; } +static void raw_probe_proto_opt(struct flowi *fl, struct msghdr *msg) +{ + struct iovec *iov; + u8 __user *type = NULL; + u8 __user *code = NULL; + int probed = 0; + int i; + + if (!msg->msg_iov) + return; + + for (i = 0; i < msg->msg_iovlen; i++) { + iov = &msg->msg_iov[i]; + if (!iov) + continue; + + switch (fl->proto) { + case IPPROTO_ICMP: + /* check if one-byte field is readable or not. */ + if (iov->iov_base && iov->iov_len < 1) + break; + + if (!type) { + type = iov->iov_base; + /* check if code field is readable or not. */ + if (iov->iov_len > 1) + code = type + 1; + } else if (!code) + code = iov->iov_base; + + if (type && code) { + get_user(fl->fl_icmp_type, type); + __get_user(fl->fl_icmp_code, code); + probed = 1; + } + break; + default: + probed = 1; + break; + } + if (probed) + break; + } +} + static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len) { @@ -429,6 +500,15 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, .proto = inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, }; + if (!inet->hdrincl) + raw_probe_proto_opt(&fl, msg); + + if (sk->sk_nx_info) { + err = ip_find_src(sk->sk_nx_info, &rt, &fl); + + if (err) + goto done; + } err = ip_route_output_flow(&rt, &fl, sk, !(msg->msg_flags&MSG_DONTWAIT)); } if (err) @@ -480,7 +560,7 @@ static void raw_close(struct sock *sk, long timeout) */ ip_ra_control(sk, 0, NULL); - inet_sock_release(sk); + sk_common_release(sk); } /* This gets rid of all the nasties in af_inet. -DaveM */ @@ -555,9 +635,11 @@ int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, } if (inet->cmsg_flags) ip_cmsg_recv(msg, skb); + if (flags & MSG_TRUNC) + copied = skb->len; done: skb_free_datagram(sk, skb); -out: return err ? : copied; +out: return err ? err : copied; } static int raw_init(struct sock *sk) @@ -568,7 +650,7 @@ static int raw_init(struct sock *sk) return 0; } -static int raw_seticmpfilter(struct sock *sk, char *optval, int optlen) +static int raw_seticmpfilter(struct sock *sk, char __user *optval, int optlen) { if (optlen > sizeof(struct icmp_filter)) optlen = sizeof(struct icmp_filter); @@ -577,7 +659,7 @@ static int raw_seticmpfilter(struct sock *sk, char *optval, int optlen) return 0; } -static int raw_geticmpfilter(struct sock *sk, char *optval, int *optlen) +static int raw_geticmpfilter(struct sock *sk, char __user *optval, int __user *optlen) { int len, ret = -EFAULT; @@ -597,7 +679,7 @@ out: return ret; } static int raw_setsockopt(struct sock *sk, int level, int optname, - char *optval, int optlen) + char __user *optval, int optlen) { if (level != SOL_RAW) return ip_setsockopt(sk, level, optname, optval, optlen); @@ -612,7 +694,7 @@ static int raw_setsockopt(struct sock *sk, int level, int optname, } static int raw_getsockopt(struct sock *sk, int level, int optname, - char *optval, int *optlen) + char __user *optval, int __user *optlen) { if (level != SOL_RAW) return ip_getsockopt(sk, level, optname, optval, optlen); @@ -631,7 +713,7 @@ static int raw_ioctl(struct sock *sk, int cmd, unsigned long arg) switch (cmd) { case SIOCOUTQ: { int amount = atomic_read(&sk->sk_wmem_alloc); - return put_user(amount, (int *)arg); + return put_user(amount, (int __user *)arg); } case SIOCINQ: { struct sk_buff *skb; @@ -642,12 +724,12 @@ static int raw_ioctl(struct sock *sk, int cmd, unsigned long arg) if (skb != NULL) amount = skb->len; spin_unlock_irq(&sk->sk_receive_queue.lock); - return put_user(amount, (int *)arg); + return put_user(amount, (int __user *)arg); } default: #ifdef CONFIG_IP_MROUTE - return ipmr_ioctl(sk, cmd, arg); + return ipmr_ioctl(sk, cmd, (void __user *)arg); #else return -ENOIOCTLCMD; #endif @@ -657,7 +739,7 @@ static int raw_ioctl(struct sock *sk, int cmd, unsigned long arg) struct proto raw_prot = { .name = "RAW", .close = raw_close, - .connect = udp_connect, + .connect = ip4_datagram_connect, .disconnect = udp_disconnect, .ioctl = raw_ioctl, .init = raw_init, @@ -669,6 +751,7 @@ struct proto raw_prot = { .backlog_rcv = raw_rcv_skb, .hash = raw_v4_hash, .unhash = raw_v4_unhash, + .slab_obj_size = sizeof(struct raw_sock), }; #ifdef CONFIG_PROC_FS @@ -687,7 +770,8 @@ static struct sock *raw_get_first(struct seq_file *seq) struct hlist_node *node; sk_for_each(sk, node, &raw_v4_htable[state->bucket]) - if (sk->sk_family == PF_INET) + if (sk->sk_family == PF_INET && + vx_check(sk->sk_xid, VX_WATCH|VX_IDENT)) goto found; } sk = NULL; @@ -703,7 +787,8 @@ static struct sock *raw_get_next(struct seq_file *seq, struct sock *sk) sk = sk_next(sk); try_again: ; - } while (sk && sk->sk_family != PF_INET); + } while (sk && (sk->sk_family != PF_INET || + !vx_check(sk->sk_xid, VX_WATCH|VX_IDENT))); if (!sk && ++state->bucket < RAWV4_HTABLE_SIZE) { sk = sk_head(&raw_v4_htable[state->bucket]);