SCTP: Fix sctp_getsockopt_get_peer_addrs
[sfrench/cifs-2.6.git] / net / sctp / socket.c
index 9f1a908776deac0a53be04e1b46aa1b87d59a98a..2fc036699d4833a964396ab65e432a214d9d02dc 100644 (file)
@@ -333,12 +333,19 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
        if (!sp->pf->bind_verify(sp, addr))
                return -EADDRNOTAVAIL;
 
-       /* We must either be unbound, or bind to the same port.  */
-       if (bp->port && (snum != bp->port)) {
-               SCTP_DEBUG_PRINTK("sctp_do_bind:"
+       /* We must either be unbound, or bind to the same port.
+        * It's OK to allow 0 ports if we are already bound.
+        * We'll just inhert an already bound port in this case
+        */
+       if (bp->port) {
+               if (!snum)
+                       snum = bp->port;
+               else if (snum != bp->port) {
+                       SCTP_DEBUG_PRINTK("sctp_do_bind:"
                                  " New port %d does not match existing port "
                                  "%d.\n", snum, bp->port);
-               return -EINVAL;
+                       return -EINVAL;
+               }
        }
 
        if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
@@ -1655,6 +1662,9 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
                goto out_free;
        }
 
+       if (asoc->pmtu_pending)
+               sctp_assoc_pending_pmtu(asoc);
+
        /* If fragmentation is disabled and the message length exceeds the
         * association fragmentation point, return EMSGSIZE.  The I-D
         * does not specify what this error is, but this looks like
@@ -2586,7 +2596,7 @@ static int sctp_setsockopt_rtoinfo(struct sock *sk, char __user *optval, int opt
  *
  * 7.1.2 SCTP_ASSOCINFO
  *
- * This option is used to tune the the maximum retransmission attempts
+ * This option is used to tune the maximum retransmission attempts
  * of the association.
  * Returns an error if the new association retransmission value is
  * greater than the sum of the retransmission value  of the peer.
@@ -3365,12 +3375,13 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len,
        sctp_assoc_t associd;
        int retval = 0;
 
-       if (len != sizeof(status)) {
+       if (len < sizeof(status)) {
                retval = -EINVAL;
                goto out;
        }
 
-       if (copy_from_user(&status, optval, sizeof(status))) {
+       len = sizeof(status);
+       if (copy_from_user(&status, optval, len)) {
                retval = -EFAULT;
                goto out;
        }
@@ -3442,12 +3453,13 @@ static int sctp_getsockopt_peer_addr_info(struct sock *sk, int len,
        struct sctp_transport *transport;
        int retval = 0;
 
-       if (len != sizeof(pinfo)) {
+       if (len < sizeof(pinfo)) {
                retval = -EINVAL;
                goto out;
        }
 
-       if (copy_from_user(&pinfo, optval, sizeof(pinfo))) {
+       len = sizeof(pinfo);
+       if (copy_from_user(&pinfo, optval, len)) {
                retval = -EFAULT;
                goto out;
        }
@@ -3513,8 +3525,11 @@ static int sctp_getsockopt_disable_fragments(struct sock *sk, int len,
 static int sctp_getsockopt_events(struct sock *sk, int len, char __user *optval,
                                  int __user *optlen)
 {
-       if (len != sizeof(struct sctp_event_subscribe))
+       if (len < sizeof(struct sctp_event_subscribe))
                return -EINVAL;
+       len = sizeof(struct sctp_event_subscribe);
+       if (put_user(len, optlen))
+               return -EFAULT;
        if (copy_to_user(optval, &sctp_sk(sk)->subscribe, len))
                return -EFAULT;
        return 0;
@@ -3536,9 +3551,12 @@ static int sctp_getsockopt_autoclose(struct sock *sk, int len, char __user *optv
        /* Applicable to UDP-style socket only */
        if (sctp_style(sk, TCP))
                return -EOPNOTSUPP;
-       if (len != sizeof(int))
+       if (len < sizeof(int))
                return -EINVAL;
-       if (copy_to_user(optval, &sctp_sk(sk)->autoclose, len))
+       len = sizeof(int);
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &sctp_sk(sk)->autoclose, sizeof(int)))
                return -EFAULT;
        return 0;
 }
@@ -3550,6 +3568,7 @@ SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc,
        struct sock *sk = asoc->base.sk;
        struct socket *sock;
        struct inet_sock *inetsk;
+       struct sctp_af *af;
        int err = 0;
 
        /* An association cannot be branched off from an already peeled-off
@@ -3571,8 +3590,9 @@ SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc,
        /* Make peeled-off sockets more like 1-1 accepted sockets.
         * Set the daddr and initialize id to something more random
         */
+       af = sctp_get_af_specific(asoc->peer.primary_addr.sa.sa_family);
+       af->to_sk_daddr(&asoc->peer.primary_addr, sk);
        inetsk = inet_sk(sock->sk);
-       inetsk->daddr = asoc->peer.primary_addr.v4.sin_addr.s_addr;
        inetsk->id = asoc->next_tsn ^ jiffies;
 
        *sockp = sock;
@@ -3587,8 +3607,9 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval
        int retval = 0;
        struct sctp_association *asoc;
 
-       if (len != sizeof(sctp_peeloff_arg_t))
+       if (len < sizeof(sctp_peeloff_arg_t))
                return -EINVAL;
+       len = sizeof(sctp_peeloff_arg_t);
        if (copy_from_user(&peeloff, optval, len))
                return -EFAULT;
 
@@ -3616,6 +3637,8 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval
 
        /* Return the fd mapped to the new socket.  */
        peeloff.sd = retval;
+       if (put_user(len, optlen))
+               return -EFAULT;
        if (copy_to_user(optval, &peeloff, len))
                retval = -EFAULT;
 
@@ -3724,9 +3747,9 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
        struct sctp_association *asoc = NULL;
        struct sctp_sock        *sp = sctp_sk(sk);
 
-       if (len != sizeof(struct sctp_paddrparams))
+       if (len < sizeof(struct sctp_paddrparams))
                return -EINVAL;
-
+       len = sizeof(struct sctp_paddrparams);
        if (copy_from_user(&params, optval, len))
                return -EFAULT;
 
@@ -3825,9 +3848,11 @@ static int sctp_getsockopt_delayed_ack_time(struct sock *sk, int len,
        struct sctp_association *asoc = NULL;
        struct sctp_sock        *sp = sctp_sk(sk);
 
-       if (len != sizeof(struct sctp_assoc_value))
+       if (len < sizeof(struct sctp_assoc_value))
                return - EINVAL;
 
+       len = sizeof(struct sctp_assoc_value);
+
        if (copy_from_user(&params, optval, len))
                return -EFAULT;
 
@@ -3876,8 +3901,11 @@ static int sctp_getsockopt_delayed_ack_time(struct sock *sk, int len,
  */
 static int sctp_getsockopt_initmsg(struct sock *sk, int len, char __user *optval, int __user *optlen)
 {
-       if (len != sizeof(struct sctp_initmsg))
+       if (len < sizeof(struct sctp_initmsg))
                return -EINVAL;
+       len = sizeof(struct sctp_initmsg);
+       if (put_user(len, optlen))
+               return -EFAULT;
        if (copy_to_user(optval, &sctp_sk(sk)->initmsg, len))
                return -EFAULT;
        return 0;
@@ -3892,7 +3920,7 @@ static int sctp_getsockopt_peer_addrs_num_old(struct sock *sk, int len,
        struct list_head *pos;
        int cnt = 0;
 
-       if (len != sizeof(sctp_assoc_t))
+       if (len < sizeof(sctp_assoc_t))
                return -EINVAL;
 
        if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
@@ -3928,10 +3956,12 @@ static int sctp_getsockopt_peer_addrs_old(struct sock *sk, int len,
        struct sctp_sock *sp = sctp_sk(sk);
        int addrlen;
 
-       if (len != sizeof(struct sctp_getaddrs_old))
+       if (len < sizeof(struct sctp_getaddrs_old))
                return -EINVAL;
 
-       if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs_old)))
+       len = sizeof(struct sctp_getaddrs_old);
+
+       if (copy_from_user(&getaddrs, optval, len))
                return -EFAULT;
 
        if (getaddrs.addr_num <= 0) return -EINVAL;
@@ -3954,7 +3984,9 @@ static int sctp_getsockopt_peer_addrs_old(struct sock *sk, int len,
                if (cnt >= getaddrs.addr_num) break;
        }
        getaddrs.addr_num = cnt;
-       if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old)))
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &getaddrs, len))
                return -EFAULT;
 
        return 0;
@@ -3987,8 +4019,7 @@ static int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
                return -EINVAL;
 
        to = optval + offsetof(struct sctp_getaddrs,addrs);
-       space_left = len - sizeof(struct sctp_getaddrs) -
-                       offsetof(struct sctp_getaddrs,addrs);
+       space_left = len - offsetof(struct sctp_getaddrs,addrs);
 
        list_for_each(pos, &asoc->peer.transport_addr_list) {
                from = list_entry(pos, struct sctp_transport, transports);
@@ -4025,7 +4056,7 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
        rwlock_t *addr_lock;
        int cnt = 0;
 
-       if (len != sizeof(sctp_assoc_t))
+       if (len < sizeof(sctp_assoc_t))
                return -EINVAL;
 
        if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
@@ -4164,12 +4195,14 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
        rwlock_t *addr_lock;
        int err = 0;
        void *addrs;
+       void *buf;
        int bytes_copied = 0;
 
-       if (len != sizeof(struct sctp_getaddrs_old))
+       if (len < sizeof(struct sctp_getaddrs_old))
                return -EINVAL;
 
-       if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs_old)))
+       len = sizeof(struct sctp_getaddrs_old);
+       if (copy_from_user(&getaddrs, optval, len))
                return -EFAULT;
 
        if (getaddrs.addr_num <= 0) return -EINVAL;
@@ -4217,13 +4250,14 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
                }
        }
 
+       buf = addrs;
        list_for_each(pos, &bp->address_list) {
                addr = list_entry(pos, struct sctp_sockaddr_entry, list);
                memcpy(&temp, &addr->a, sizeof(temp));
                sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
                addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
-               memcpy(addrs, &temp, addrlen);
-               to += addrlen;
+               memcpy(buf, &temp, addrlen);
+               buf += addrlen;
                bytes_copied += addrlen;
                cnt ++;
                if (cnt >= getaddrs.addr_num) break;
@@ -4240,7 +4274,7 @@ copy_getaddrs:
 
        /* copy the leading structure back to user */
        getaddrs.addr_num = cnt;
-       if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old)))
+       if (copy_to_user(optval, &getaddrs, len))
                err = -EFAULT;
 
 error:
@@ -4266,8 +4300,9 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
        size_t space_left;
        int bytes_copied = 0;
        void *addrs;
+       void *buf;
 
-       if (len <= sizeof(struct sctp_getaddrs))
+       if (len < sizeof(struct sctp_getaddrs))
                return -EINVAL;
 
        if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
@@ -4291,8 +4326,8 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
        }
 
        to = optval + offsetof(struct sctp_getaddrs,addrs);
-       space_left = len - sizeof(struct sctp_getaddrs) -
-                        offsetof(struct sctp_getaddrs,addrs);
+       space_left = len - offsetof(struct sctp_getaddrs,addrs);
+
        addrs = kmalloc(space_left, GFP_KERNEL);
        if (!addrs)
                return -ENOMEM;
@@ -4316,6 +4351,7 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
                }
        }
 
+       buf = addrs;
        list_for_each(pos, &bp->address_list) {
                addr = list_entry(pos, struct sctp_sockaddr_entry, list);
                memcpy(&temp, &addr->a, sizeof(temp));
@@ -4325,8 +4361,8 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
                        err =  -ENOMEM; /*fixme: right error?*/
                        goto error;
                }
-               memcpy(addrs, &temp, addrlen);
-               to += addrlen;
+               memcpy(buf, &temp, addrlen);
+               buf += addrlen;
                bytes_copied += addrlen;
                cnt ++;
                space_left -= addrlen;
@@ -4339,11 +4375,12 @@ copy_getaddrs:
                err = -EFAULT;
                goto error;
        }
-       if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num))
-               return -EFAULT;
+       if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num)) {
+               err = -EFAULT;
+               goto error;
+       }
        if (put_user(bytes_copied, optlen))
-               return -EFAULT;
-
+               err = -EFAULT;
 error:
        kfree(addrs);
        return err;
@@ -4362,10 +4399,12 @@ static int sctp_getsockopt_primary_addr(struct sock *sk, int len,
        struct sctp_association *asoc;
        struct sctp_sock *sp = sctp_sk(sk);
 
-       if (len != sizeof(struct sctp_prim))
+       if (len < sizeof(struct sctp_prim))
                return -EINVAL;
 
-       if (copy_from_user(&prim, optval, sizeof(struct sctp_prim)))
+       len = sizeof(struct sctp_prim);
+
+       if (copy_from_user(&prim, optval, len))
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, prim.ssp_assoc_id);
@@ -4381,7 +4420,9 @@ static int sctp_getsockopt_primary_addr(struct sock *sk, int len,
        sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp,
                        (union sctp_addr *)&prim.ssp_addr);
 
-       if (copy_to_user(optval, &prim, sizeof(struct sctp_prim)))
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &prim, len))
                return -EFAULT;
 
        return 0;
@@ -4398,10 +4439,15 @@ static int sctp_getsockopt_adaptation_layer(struct sock *sk, int len,
 {
        struct sctp_setadaptation adaptation;
 
-       if (len != sizeof(struct sctp_setadaptation))
+       if (len < sizeof(struct sctp_setadaptation))
                return -EINVAL;
 
+       len = sizeof(struct sctp_setadaptation);
+
        adaptation.ssb_adaptation_ind = sctp_sk(sk)->adaptation_ind;
+
+       if (put_user(len, optlen))
+               return -EFAULT;
        if (copy_to_user(optval, &adaptation, len))
                return -EFAULT;
 
@@ -4435,9 +4481,12 @@ static int sctp_getsockopt_default_send_param(struct sock *sk,
        struct sctp_association *asoc;
        struct sctp_sock *sp = sctp_sk(sk);
 
-       if (len != sizeof(struct sctp_sndrcvinfo))
+       if (len < sizeof(struct sctp_sndrcvinfo))
                return -EINVAL;
-       if (copy_from_user(&info, optval, sizeof(struct sctp_sndrcvinfo)))
+
+       len = sizeof(struct sctp_sndrcvinfo);
+
+       if (copy_from_user(&info, optval, len))
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, info.sinfo_assoc_id);
@@ -4458,7 +4507,9 @@ static int sctp_getsockopt_default_send_param(struct sock *sk,
                info.sinfo_timetolive = sp->default_timetolive;
        }
 
-       if (copy_to_user(optval, &info, sizeof(struct sctp_sndrcvinfo)))
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &info, len))
                return -EFAULT;
 
        return 0;
@@ -4509,10 +4560,12 @@ static int sctp_getsockopt_rtoinfo(struct sock *sk, int len,
        struct sctp_rtoinfo rtoinfo;
        struct sctp_association *asoc;
 
-       if (len != sizeof (struct sctp_rtoinfo))
+       if (len < sizeof (struct sctp_rtoinfo))
                return -EINVAL;
 
-       if (copy_from_user(&rtoinfo, optval, sizeof (struct sctp_rtoinfo)))
+       len = sizeof(struct sctp_rtoinfo);
+
+       if (copy_from_user(&rtoinfo, optval, len))
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, rtoinfo.srto_assoc_id);
@@ -4547,7 +4600,7 @@ static int sctp_getsockopt_rtoinfo(struct sock *sk, int len,
  *
  * 7.1.2 SCTP_ASSOCINFO
  *
- * This option is used to tune the the maximum retransmission attempts
+ * This option is used to tune the maximum retransmission attempts
  * of the association.
  * Returns an error if the new association retransmission value is
  * greater than the sum of the retransmission value  of the peer.
@@ -4564,11 +4617,12 @@ static int sctp_getsockopt_associnfo(struct sock *sk, int len,
        struct list_head *pos;
        int cnt = 0;
 
-       if (len != sizeof (struct sctp_assocparams))
+       if (len < sizeof (struct sctp_assocparams))
                return -EINVAL;
 
-       if (copy_from_user(&assocparams, optval,
-                       sizeof (struct sctp_assocparams)))
+       len = sizeof(struct sctp_assocparams);
+
+       if (copy_from_user(&assocparams, optval, len))
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, assocparams.sasoc_assoc_id);
@@ -4654,9 +4708,11 @@ static int sctp_getsockopt_context(struct sock *sk, int len,
        struct sctp_sock *sp;
        struct sctp_association *asoc;
 
-       if (len != sizeof(struct sctp_assoc_value))
+       if (len < sizeof(struct sctp_assoc_value))
                return -EINVAL;
 
+       len = sizeof(struct sctp_assoc_value);
+
        if (copy_from_user(&params, optval, len))
                return -EFAULT;
 
@@ -5227,7 +5283,12 @@ int sctp_inet_listen(struct socket *sock, int backlog)
        /* Allocate HMAC for generating cookie. */
        if (sctp_hmac_alg) {
                tfm = crypto_alloc_hash(sctp_hmac_alg, 0, CRYPTO_ALG_ASYNC);
-               if (!tfm) {
+               if (IS_ERR(tfm)) {
+                       if (net_ratelimit()) {
+                               printk(KERN_INFO
+                                      "SCTP: failed to load transform for %s: %ld\n",
+                                       sctp_hmac_alg, PTR_ERR(tfm));
+                       }
                        err = -ENOSYS;
                        goto out;
                }