nfsd: nfsd should drop CAP_MKNOD for non-root
[safe/jmp/linux-2.6] / net / x25 / af_x25.c
index 72b6ff3..9fc5b02 100644 (file)
@@ -3,7 +3,7 @@
  *
  *     This is ALPHA test software. This code may break your machine,
  *     randomly fail to work with new releases, misbehave and/or generally
- *     screw up. It might even work. 
+ *     screw up. It might even work.
  *
  *     This code REQUIRES 2.1.15 or higher
  *
  *     X.25 002        Jonathan Naylor Centralised disconnect handling.
  *                                     New timer architecture.
  *     2000-03-11      Henner Eisen    MSG_EOR handling more POSIX compliant.
- *     2000-03-22      Daniela Squassoni Allowed disabling/enabling of 
- *                                       facilities negotiation and increased 
+ *     2000-03-22      Daniela Squassoni Allowed disabling/enabling of
+ *                                       facilities negotiation and increased
  *                                       the throughput upper limit.
  *     2000-08-27      Arnaldo C. Melo s/suser/capable/ + micro cleanups
- *     2000-09-04      Henner Eisen    Set sock->state in x25_accept(). 
+ *     2000-09-04      Henner Eisen    Set sock->state in x25_accept().
  *                                     Fixed x25_output() related skb leakage.
  *     2000-10-02      Henner Eisen    Made x25_kick() single threaded per socket.
  *     2000-10-27      Henner Eisen    MSG_DONTWAIT for fragment allocation.
@@ -35,7 +35,6 @@
  *                                     response
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/capability.h>
 #include <linux/errno.h>
 #include <linux/termios.h>     /* For TIOCINQ/OUTQ */
 #include <linux/notifier.h>
 #include <linux/init.h>
+#include <linux/compat.h>
+
 #include <net/x25.h>
+#include <net/compat.h>
 
 int sysctl_x25_restart_request_timeout = X25_DEFAULT_T20;
 int sysctl_x25_call_request_timeout    = X25_DEFAULT_T21;
 int sysctl_x25_reset_request_timeout   = X25_DEFAULT_T22;
 int sysctl_x25_clear_request_timeout   = X25_DEFAULT_T23;
 int sysctl_x25_ack_holdback_timeout    = X25_DEFAULT_T2;
+int sysctl_x25_forward                 = 0;
 
 HLIST_HEAD(x25_list);
 DEFINE_RWLOCK(x25_list_lock);
@@ -69,12 +72,20 @@ static const struct proto_ops x25_proto_ops;
 
 static struct x25_address null_x25_address = {"               "};
 
+#ifdef CONFIG_COMPAT
+struct compat_x25_subscrip_struct {
+       char device[200-sizeof(compat_ulong_t)];
+       compat_ulong_t global_facil_mask;
+       compat_uint_t extended;
+};
+#endif
+
 int x25_addr_ntoa(unsigned char *p, struct x25_address *called_addr,
                  struct x25_address *calling_addr)
 {
-       int called_len, calling_len;
+       unsigned int called_len, calling_len;
        char *called, *calling;
-       int i;
+       unsigned int i;
 
        called_len  = (*p >> 0) & 0x0F;
        calling_len = (*p >> 4) & 0x0F;
@@ -180,6 +191,9 @@ static int x25_device_event(struct notifier_block *this, unsigned long event,
        struct net_device *dev = ptr;
        struct x25_neigh *nb;
 
+       if (!net_eq(dev_net(dev), &init_net))
+               return NOTIFY_DONE;
+
        if (dev->type == ARPHRD_X25
 #if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
         || dev->type == ARPHRD_ETHER
@@ -245,8 +259,8 @@ static struct sock *x25_find_listener(struct x25_address *addr,
                         * call user data vs this sockets call user data
                         */
                        if(skb->len > 0 && x25_sk(s)->cudmatchlength > 0) {
-                               if((memcmp(x25_sk(s)->calluserdata.cuddata,
-                                       skb->data,
+                               if((memcmp(x25_sk(s)->calluserdata.cuddata,
+                                       skb->data,
                                        x25_sk(s)->cudmatchlength)) == 0) {
                                        sock_hold(s);
                                        goto found;
@@ -410,7 +424,7 @@ static int x25_getsockopt(struct socket *sock, int level, int optname,
 {
        struct sock *sk = sock->sk;
        int val, len, rc = -ENOPROTOOPT;
-       
+
        if (level != SOL_X25 || optname != X25_QBITINCL)
                goto out;
 
@@ -423,7 +437,7 @@ static int x25_getsockopt(struct socket *sock, int level, int optname,
        rc = -EINVAL;
        if (len < 0)
                goto out;
-               
+
        rc = -EFAULT;
        if (put_user(len, optlen))
                goto out;
@@ -455,10 +469,10 @@ static struct proto x25_proto = {
        .obj_size = sizeof(struct x25_sock),
 };
 
-static struct sock *x25_alloc_socket(void)
+static struct sock *x25_alloc_socket(struct net *net)
 {
        struct x25_sock *x25;
-       struct sock *sk = sk_alloc(AF_X25, GFP_ATOMIC, &x25_proto, 1);
+       struct sock *sk = sk_alloc(net, AF_X25, GFP_ATOMIC, &x25_proto);
 
        if (!sk)
                goto out;
@@ -474,19 +488,20 @@ out:
        return sk;
 }
 
-void x25_init_timers(struct sock *sk);
-
-static int x25_create(struct socket *sock, int protocol)
+static int x25_create(struct net *net, struct socket *sock, int protocol)
 {
        struct sock *sk;
        struct x25_sock *x25;
        int rc = -ESOCKTNOSUPPORT;
 
+       if (net != &init_net)
+               return -EAFNOSUPPORT;
+
        if (sock->type != SOCK_SEQPACKET || protocol)
                goto out;
 
        rc = -ENOMEM;
-       if ((sk = x25_alloc_socket()) == NULL)
+       if ((sk = x25_alloc_socket(net)) == NULL)
                goto out;
 
        x25 = x25_sk(sk);
@@ -514,6 +529,13 @@ static int x25_create(struct socket *sock, int protocol)
        x25->facilities.pacsize_out = X25_DEFAULT_PACKET_SIZE;
        x25->facilities.throughput  = X25_DEFAULT_THROUGHPUT;
        x25->facilities.reverse     = X25_DEFAULT_REVERSE;
+       x25->dte_facilities.calling_len = 0;
+       x25->dte_facilities.called_len = 0;
+       memset(x25->dte_facilities.called_ae, '\0',
+                       sizeof(x25->dte_facilities.called_ae));
+       memset(x25->dte_facilities.calling_ae, '\0',
+                       sizeof(x25->dte_facilities.calling_ae));
+
        rc = 0;
 out:
        return rc;
@@ -527,19 +549,17 @@ static struct sock *x25_make_new(struct sock *osk)
        if (osk->sk_type != SOCK_SEQPACKET)
                goto out;
 
-       if ((sk = x25_alloc_socket()) == NULL)
+       if ((sk = x25_alloc_socket(sock_net(osk))) == NULL)
                goto out;
 
        x25 = x25_sk(sk);
 
        sk->sk_type        = osk->sk_type;
-       sk->sk_socket      = osk->sk_socket;
        sk->sk_priority    = osk->sk_priority;
        sk->sk_protocol    = osk->sk_protocol;
        sk->sk_rcvbuf      = osk->sk_rcvbuf;
        sk->sk_sndbuf      = osk->sk_sndbuf;
        sk->sk_state       = TCP_ESTABLISHED;
-       sk->sk_sleep       = osk->sk_sleep;
        sk->sk_backlog_rcv = osk->sk_backlog_rcv;
        sock_copy_flags(sk, osk);
 
@@ -550,6 +570,7 @@ static struct sock *x25_make_new(struct sock *osk)
        x25->t2         = ox25->t2;
        x25->facilities = ox25->facilities;
        x25->qbitincl   = ox25->qbitincl;
+       x25->dte_facilities = ox25->dte_facilities;
        x25->cudmatchlength = ox25->cudmatchlength;
        x25->accptapprv = ox25->accptapprv;
 
@@ -591,8 +612,7 @@ static int x25_release(struct socket *sock)
                        break;
        }
 
-       sock->sk        = NULL; 
-       sk->sk_socket   = NULL; /* Not used, but we should do this */
+       sock_orphan(sk);
 out:
        return 0;
 }
@@ -618,7 +638,7 @@ static int x25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 static int x25_wait_for_connection_establishment(struct sock *sk)
 {
        DECLARE_WAITQUEUE(wait, current);
-        int rc;
+       int rc;
 
        add_wait_queue_exclusive(sk->sk_sleep, &wait);
        for (;;) {
@@ -669,7 +689,7 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr,
        if (sk->sk_state == TCP_ESTABLISHED)
                goto out;
 
-       sk->sk_state   = TCP_CLOSE;     
+       sk->sk_state   = TCP_CLOSE;
        sock->state = SS_UNCONNECTED;
 
        rc = -EINVAL;
@@ -733,7 +753,7 @@ out:
        return rc;
 }
 
-static int x25_wait_for_data(struct sock *sk, int timeout)
+static int x25_wait_for_data(struct sock *sk, long timeout)
 {
        DECLARE_WAITQUEUE(wait, current);
        int rc = 0;
@@ -761,7 +781,7 @@ static int x25_wait_for_data(struct sock *sk, int timeout)
        remove_wait_queue(sk->sk_sleep, &wait);
        return rc;
 }
-       
+
 static int x25_accept(struct socket *sock, struct socket *newsock, int flags)
 {
        struct sock *sk = sock->sk;
@@ -785,14 +805,12 @@ static int x25_accept(struct socket *sock, struct socket *newsock, int flags)
        if (!skb->sk)
                goto out2;
        newsk            = skb->sk;
-       newsk->sk_socket = newsock;
-       newsk->sk_sleep  = &newsock->wait;
+       sock_graft(newsk, newsock);
 
        /* Now attach up the new socket */
        skb->sk = NULL;
        kfree_skb(skb);
        sk->sk_ack_backlog--;
-       newsock->sk    = newsk;
        newsock->state = SS_CONNECTED;
        rc = 0;
 out2:
@@ -820,7 +838,7 @@ static int x25_getname(struct socket *sock, struct sockaddr *uaddr,
 
        return 0;
 }
+
 int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb,
                        unsigned int lci)
 {
@@ -829,7 +847,8 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb,
        struct x25_sock *makex25;
        struct x25_address source_addr, dest_addr;
        struct x25_facilities facilities;
-       int len, rc;
+       struct x25_dte_facilities dte_facilities;
+       int len, addr_len, rc;
 
        /*
         *      Remove the LCI and frame type.
@@ -840,7 +859,8 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb,
         *      Extract the X.25 addresses and convert them to ASCII strings,
         *      and remove them.
         */
-       skb_pull(skb, x25_addr_ntoa(skb->data, &source_addr, &dest_addr));
+       addr_len = x25_addr_ntoa(skb->data, &source_addr, &dest_addr);
+       skb_pull(skb, addr_len);
 
        /*
         *      Get the length of the facilities, skip past them for the moment
@@ -856,16 +876,34 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb,
        sk = x25_find_listener(&source_addr,skb);
        skb_push(skb,len);
 
+       if (sk != NULL && sk_acceptq_is_full(sk)) {
+               goto out_sock_put;
+       }
+
        /*
-        *      We can't accept the Call Request.
+        *      We dont have any listeners for this incoming call.
+        *      Try forwarding it.
         */
-       if (sk == NULL || sk_acceptq_is_full(sk))
-               goto out_clear_request;
+       if (sk == NULL) {
+               skb_push(skb, addr_len + X25_STD_MIN_LEN);
+               if (sysctl_x25_forward &&
+                               x25_forward_call(&dest_addr, nb, skb, lci) > 0)
+               {
+                       /* Call was forwarded, dont process it any more */
+                       kfree_skb(skb);
+                       rc = 1;
+                       goto out;
+               } else {
+                       /* No listeners, can't forward, clear the call */
+                       goto out_clear_request;
+               }
+       }
 
        /*
         *      Try to reach a compromise on the requested facilities.
         */
-       if ((len = x25_negotiate_facilities(skb, sk, &facilities)) == -1)
+       len = x25_negotiate_facilities(skb, sk, &facilities, &dte_facilities);
+       if (len == -1)
                goto out_sock_put;
 
        /*
@@ -896,9 +934,12 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb,
        makex25->source_addr   = source_addr;
        makex25->neighbour     = nb;
        makex25->facilities    = facilities;
+       makex25->dte_facilities= dte_facilities;
        makex25->vc_facil_mask = x25_sk(sk)->vc_facil_mask;
        /* ensure no reverse facil on accept */
        makex25->vc_facil_mask &= ~X25_MASK_REVERSE;
+       /* ensure no calling address extension on accept */
+       makex25->vc_facil_mask &= ~X25_MASK_CALLING_AE;
        makex25->cudmatchlength = x25_sk(sk)->cudmatchlength;
 
        /* Normally all calls are accepted immediatly */
@@ -911,7 +952,7 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb,
         *      Incoming Call User Data.
         */
        if (skb->len >= 0) {
-               memcpy(makex25->calluserdata.cuddata, skb->data, skb->len);
+               skb_copy_from_linear_data(skb, makex25->calluserdata.cuddata, skb->len);
                makex25->calluserdata.cudlength = skb->len;
        }
 
@@ -1018,9 +1059,10 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock,
         */
        SOCK_DEBUG(sk, "x25_sendmsg: Copying user data\n");
 
-       asmptr = skb->h.raw = skb_put(skb, len);
+       skb_reset_transport_header(skb);
+       skb_put(skb, len);
 
-       rc = memcpy_fromiovec(asmptr, msg->msg_iov, len);
+       rc = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len);
        if (rc)
                goto out_kfree_skb;
 
@@ -1080,7 +1122,7 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock,
        if (msg->msg_flags & MSG_OOB)
                skb_queue_tail(&x25->interrupt_out_queue, skb);
        else {
-               len = x25_output(sk, skb);
+               len = x25_output(sk, skb);
                if (len < 0)
                        kfree_skb(skb);
                else if (x25->qbitincl)
@@ -1170,8 +1212,7 @@ static int x25_recvmsg(struct kiocb *iocb, struct socket *sock,
                }
        }
 
-       skb->h.raw = skb->data;
-
+       skb_reset_transport_header(skb);
        copied = skb->len;
 
        if (copied > size) {
@@ -1179,7 +1220,7 @@ static int x25_recvmsg(struct kiocb *iocb, struct socket *sock,
                msg->msg_flags |= MSG_TRUNC;
        }
 
-       /* Currently, each datagram always contains a complete record */ 
+       /* Currently, each datagram always contains a complete record */
        msg->msg_flags |= MSG_EOR;
 
        rc = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
@@ -1237,8 +1278,14 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                case SIOCGSTAMP:
                        rc = -EINVAL;
                        if (sk)
-                               rc = sock_get_timestamp(sk, 
-                                               (struct timeval __user *)argp); 
+                               rc = sock_get_timestamp(sk,
+                                               (struct timeval __user *)argp);
+                       break;
+               case SIOCGSTAMPNS:
+                       rc = -EINVAL;
+                       if (sk)
+                               rc = sock_get_timestampns(sk,
+                                               (struct timespec __user *)argp);
                        break;
                case SIOCGIFADDR:
                case SIOCSIFADDR:
@@ -1305,6 +1352,36 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                        break;
                }
 
+               case SIOCX25GDTEFACILITIES: {
+                       rc = copy_to_user(argp, &x25->dte_facilities,
+                                               sizeof(x25->dte_facilities));
+                       if (rc)
+                               rc = -EFAULT;
+                       break;
+               }
+
+               case SIOCX25SDTEFACILITIES: {
+                       struct x25_dte_facilities dtefacs;
+                       rc = -EFAULT;
+                       if (copy_from_user(&dtefacs, argp, sizeof(dtefacs)))
+                               break;
+                       rc = -EINVAL;
+                       if (sk->sk_state != TCP_LISTEN &&
+                                       sk->sk_state != TCP_CLOSE)
+                               break;
+                       if (dtefacs.calling_len > X25_MAX_AE_LEN)
+                               break;
+                       if (dtefacs.calling_ae == NULL)
+                               break;
+                       if (dtefacs.called_len > X25_MAX_AE_LEN)
+                               break;
+                       if (dtefacs.called_ae == NULL)
+                               break;
+                       x25->dte_facilities = dtefacs;
+                       rc = 0;
+                       break;
+               }
+
                case SIOCX25GCALLUSERDATA: {
                        struct x25_calluserdata cud = x25->calluserdata;
                        rc = copy_to_user(argp, &cud,
@@ -1344,7 +1421,7 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                        if (copy_from_user(&sub_addr, argp,
                                        sizeof(sub_addr)))
                                break;
-                       rc = -EINVAL;
+                       rc = -EINVAL;
                        if(sub_addr.cudmatchlength > X25_MAX_CUD_LEN)
                                break;
                        x25->cudmatchlength = sub_addr.cudmatchlength;
@@ -1373,7 +1450,7 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                        break;
                }
 
-               default:
+               default:
                        rc = -ENOIOCTLCMD;
                        break;
        }
@@ -1387,6 +1464,124 @@ static struct net_proto_family x25_family_ops = {
        .owner  =       THIS_MODULE,
 };
 
+#ifdef CONFIG_COMPAT
+static int compat_x25_subscr_ioctl(unsigned int cmd,
+               struct compat_x25_subscrip_struct __user *x25_subscr32)
+{
+       struct compat_x25_subscrip_struct x25_subscr;
+       struct x25_neigh *nb;
+       struct net_device *dev;
+       int rc = -EINVAL;
+
+       rc = -EFAULT;
+       if (copy_from_user(&x25_subscr, x25_subscr32, sizeof(*x25_subscr32)))
+               goto out;
+
+       rc = -EINVAL;
+       dev = x25_dev_get(x25_subscr.device);
+       if (dev == NULL)
+               goto out;
+
+       nb = x25_get_neigh(dev);
+       if (nb == NULL)
+               goto out_dev_put;
+
+       dev_put(dev);
+
+       if (cmd == SIOCX25GSUBSCRIP) {
+               x25_subscr.extended = nb->extended;
+               x25_subscr.global_facil_mask = nb->global_facil_mask;
+               rc = copy_to_user(x25_subscr32, &x25_subscr,
+                               sizeof(*x25_subscr32)) ? -EFAULT : 0;
+       } else {
+               rc = -EINVAL;
+               if (x25_subscr.extended == 0 || x25_subscr.extended == 1) {
+                       rc = 0;
+                       nb->extended = x25_subscr.extended;
+                       nb->global_facil_mask = x25_subscr.global_facil_mask;
+               }
+       }
+       x25_neigh_put(nb);
+out:
+       return rc;
+out_dev_put:
+       dev_put(dev);
+       goto out;
+}
+
+static int compat_x25_ioctl(struct socket *sock, unsigned int cmd,
+                               unsigned long arg)
+{
+       void __user *argp = compat_ptr(arg);
+       struct sock *sk = sock->sk;
+
+       int rc = -ENOIOCTLCMD;
+
+       switch(cmd) {
+       case TIOCOUTQ:
+       case TIOCINQ:
+               rc = x25_ioctl(sock, cmd, (unsigned long)argp);
+               break;
+       case SIOCGSTAMP:
+               rc = -EINVAL;
+               if (sk)
+                       rc = compat_sock_get_timestamp(sk,
+                                       (struct timeval __user*)argp);
+               break;
+       case SIOCGSTAMPNS:
+               rc = -EINVAL;
+               if (sk)
+                       rc = compat_sock_get_timestampns(sk,
+                                       (struct timespec __user*)argp);
+               break;
+       case SIOCGIFADDR:
+       case SIOCSIFADDR:
+       case SIOCGIFDSTADDR:
+       case SIOCSIFDSTADDR:
+       case SIOCGIFBRDADDR:
+       case SIOCSIFBRDADDR:
+       case SIOCGIFNETMASK:
+       case SIOCSIFNETMASK:
+       case SIOCGIFMETRIC:
+       case SIOCSIFMETRIC:
+               rc = -EINVAL;
+               break;
+       case SIOCADDRT:
+       case SIOCDELRT:
+               rc = -EPERM;
+               if (!capable(CAP_NET_ADMIN))
+                       break;
+               rc = x25_route_ioctl(cmd, argp);
+               break;
+       case SIOCX25GSUBSCRIP:
+               rc = compat_x25_subscr_ioctl(cmd, argp);
+               break;
+       case SIOCX25SSUBSCRIP:
+               rc = -EPERM;
+               if (!capable(CAP_NET_ADMIN))
+                       break;
+               rc = compat_x25_subscr_ioctl(cmd, argp);
+               break;
+       case SIOCX25GFACILITIES:
+       case SIOCX25SFACILITIES:
+       case SIOCX25GDTEFACILITIES:
+       case SIOCX25SDTEFACILITIES:
+       case SIOCX25GCALLUSERDATA:
+       case SIOCX25SCALLUSERDATA:
+       case SIOCX25GCAUSEDIAG:
+       case SIOCX25SCUDMATCHLEN:
+       case SIOCX25CALLACCPTAPPRV:
+       case SIOCX25SENDCALLACCPT:
+               rc = x25_ioctl(sock, cmd, (unsigned long)argp);
+               break;
+       default:
+               rc = -ENOIOCTLCMD;
+               break;
+       }
+       return rc;
+}
+#endif
+
 static const struct proto_ops SOCKOPS_WRAPPED(x25_proto_ops) = {
        .family =       AF_X25,
        .owner =        THIS_MODULE,
@@ -1398,6 +1593,9 @@ static const struct proto_ops SOCKOPS_WRAPPED(x25_proto_ops) = {
        .getname =      x25_getname,
        .poll =         datagram_poll,
        .ioctl =        x25_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = compat_x25_ioctl,
+#endif
        .listen =       x25_listen,
        .shutdown =     sock_no_shutdown,
        .setsockopt =   x25_setsockopt,
@@ -1408,7 +1606,6 @@ static const struct proto_ops SOCKOPS_WRAPPED(x25_proto_ops) = {
        .sendpage =     sock_no_sendpage,
 };
 
-#include <linux/smp_lock.h>
 SOCKOPS_WRAP(x25_proto, AF_X25);
 
 static struct packet_type x25_packet_type = {
@@ -1432,6 +1629,9 @@ void x25_kill_by_neigh(struct x25_neigh *nb)
                        x25_disconnect(s, ENETUNREACH, 0, 0);
 
        write_unlock_bh(&x25_list_lock);
+
+       /* Remove any related forwards */
+       x25_clear_forward_by_dev(nb->dev);
 }
 
 static int __init x25_init(void)
@@ -1447,7 +1647,7 @@ static int __init x25_init(void)
 
        register_netdevice_notifier(&x25_dev_notifier);
 
-       printk(KERN_INFO "X.25 for Linux. Version 0.2 for Linux 2.1.15\n");
+       printk(KERN_INFO "X.25 for Linux Version 0.2\n");
 
 #ifdef CONFIG_SYSCTL
        x25_register_sysctl();