]> pilppa.com Git - linux-2.6-omap-h63xx.git/commitdiff
[AF_PACKET]: Allow for > 8 byte hardware addresses.
authorEric W. Biederman <ebiederm@xmission.com>
Wed, 21 Sep 2005 07:11:37 +0000 (00:11 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 21 Sep 2005 07:11:37 +0000 (00:11 -0700)
The convention is that longer addresses will simply extend
the hardeware address byte arrays at the end of sockaddr_ll and
packet_mreq.

In making this change a small information leak was also closed.
The code only initializes the hardware address bytes that are
used, but all of struct sockaddr_ll was copied to userspace.
Now we just copy sockaddr_ll to the last byte of the hardware
address used.

For error checking larger structures than our internal
maximums continue to be allowed but an error is signaled if we can
not fit the hardware address into our internal structure.

Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/packet/af_packet.c

index 8690f171c1ef28136e7e57e160e980955d6b742d..ee865d88183bb930d4d05faf01e584263a80f51f 100644 (file)
  *     Michal Ostrowski        :       Module initialization cleanup.
  *         Ulises Alonso        :       Frame number limit removal and 
  *                                      packet_set_ring memory leak.
+ *             Eric Biederman  :       Allow for > 8 byte hardware addresses.
+ *                                     The convention is that longer addresses
+ *                                     will simply extend the hardware address
+ *                                     byte arrays at the end of sockaddr_ll 
+ *                                     and packet_mreq.
  *
  *             This program is free software; you can redistribute it and/or
  *             modify it under the terms of the GNU General Public License
@@ -161,7 +166,17 @@ struct packet_mclist
        int                     count;
        unsigned short          type;
        unsigned short          alen;
-       unsigned char           addr[8];
+       unsigned char           addr[MAX_ADDR_LEN];
+};
+/* identical to struct packet_mreq except it has
+ * a longer address field.
+ */
+struct packet_mreq_max
+{
+       int             mr_ifindex;
+       unsigned short  mr_type;
+       unsigned short  mr_alen;
+       unsigned char   mr_address[MAX_ADDR_LEN];
 };
 #endif
 #ifdef CONFIG_PACKET_MMAP
@@ -716,6 +731,8 @@ static int packet_sendmsg(struct kiocb *iocb, struct socket *sock,
                err = -EINVAL;
                if (msg->msg_namelen < sizeof(struct sockaddr_ll))
                        goto out;
+               if (msg->msg_namelen < (saddr->sll_halen + offsetof(struct sockaddr_ll, sll_addr)))
+                       goto out;
                ifindex = saddr->sll_ifindex;
                proto   = saddr->sll_protocol;
                addr    = saddr->sll_addr;
@@ -744,6 +761,12 @@ static int packet_sendmsg(struct kiocb *iocb, struct socket *sock,
        if (dev->hard_header) {
                int res;
                err = -EINVAL;
+               if (saddr) {
+                       if (saddr->sll_halen != dev->addr_len)
+                               goto out_free;
+                       if (saddr->sll_hatype != dev->type)
+                               goto out_free;
+               }
                res = dev->hard_header(skb, dev, ntohs(proto), addr, NULL, len);
                if (sock->type != SOCK_DGRAM) {
                        skb->tail = skb->data;
@@ -1045,6 +1068,7 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
        struct sock *sk = sock->sk;
        struct sk_buff *skb;
        int copied, err;
+       struct sockaddr_ll *sll;
 
        err = -EINVAL;
        if (flags & ~(MSG_PEEK|MSG_DONTWAIT|MSG_TRUNC|MSG_CMSG_COMPAT))
@@ -1056,16 +1080,6 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
                return -ENODEV;
 #endif
 
-       /*
-        *      If the address length field is there to be filled in, we fill
-        *      it in now.
-        */
-
-       if (sock->type == SOCK_PACKET)
-               msg->msg_namelen = sizeof(struct sockaddr_pkt);
-       else
-               msg->msg_namelen = sizeof(struct sockaddr_ll);
-
        /*
         *      Call the generic datagram receiver. This handles all sorts
         *      of horrible races and re-entrancy so we can forget about it
@@ -1086,6 +1100,17 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
        if(skb==NULL)
                goto out;
 
+       /*
+        *      If the address length field is there to be filled in, we fill
+        *      it in now.
+        */
+
+       sll = (struct sockaddr_ll*)skb->cb;
+       if (sock->type == SOCK_PACKET)
+               msg->msg_namelen = sizeof(struct sockaddr_pkt);
+       else
+               msg->msg_namelen = sll->sll_halen + offsetof(struct sockaddr_ll, sll_addr);
+
        /*
         *      You lose any data beyond the buffer you gave. If it worries a
         *      user program they can ask the device for its MTU anyway.
@@ -1166,7 +1191,7 @@ static int packet_getname(struct socket *sock, struct sockaddr *uaddr,
                sll->sll_hatype = 0;    /* Bad: we have no ARPHRD_UNSPEC */
                sll->sll_halen = 0;
        }
-       *uaddr_len = sizeof(*sll);
+       *uaddr_len = offsetof(struct sockaddr_ll, sll_addr) + sll->sll_halen;
 
        return 0;
 }
@@ -1199,7 +1224,7 @@ static void packet_dev_mclist(struct net_device *dev, struct packet_mclist *i, i
        }
 }
 
-static int packet_mc_add(struct sock *sk, struct packet_mreq *mreq)
+static int packet_mc_add(struct sock *sk, struct packet_mreq_max *mreq)
 {
        struct packet_sock *po = pkt_sk(sk);
        struct packet_mclist *ml, *i;
@@ -1249,7 +1274,7 @@ done:
        return err;
 }
 
-static int packet_mc_drop(struct sock *sk, struct packet_mreq *mreq)
+static int packet_mc_drop(struct sock *sk, struct packet_mreq_max *mreq)
 {
        struct packet_mclist *ml, **mlp;
 
@@ -1315,11 +1340,17 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv
        case PACKET_ADD_MEMBERSHIP:     
        case PACKET_DROP_MEMBERSHIP:
        {
-               struct packet_mreq mreq;
-               if (optlen<sizeof(mreq))
+               struct packet_mreq_max mreq;
+               int len = optlen;
+               memset(&mreq, 0, sizeof(mreq));
+               if (len < sizeof(struct packet_mreq))
                        return -EINVAL;
-               if (copy_from_user(&mreq,optval,sizeof(mreq)))
+               if (len > sizeof(mreq))
+                       len = sizeof(mreq);
+               if (copy_from_user(&mreq,optval,len))
                        return -EFAULT;
+               if (len < (mreq.mr_alen + offsetof(struct packet_mreq, mr_address)))
+                       return -EINVAL;
                if (optname == PACKET_ADD_MEMBERSHIP)
                        ret = packet_mc_add(sk, &mreq);
                else