summaryrefslogtreecommitdiffstats
path: root/net/netrom/af_netrom.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/netrom/af_netrom.c')
-rw-r--r--net/netrom/af_netrom.c130
1 files changed, 82 insertions, 48 deletions
diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c
index a4b89d294..3ba0b8c81 100644
--- a/net/netrom/af_netrom.c
+++ b/net/netrom/af_netrom.c
@@ -47,6 +47,7 @@
#include <linux/net.h>
#include <linux/stat.h>
#include <net/ax25.h>
+#include <net/ax25_uid.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
@@ -347,6 +348,8 @@ static int nr_setsockopt(struct socket *sock, int level, int optname,
char *optval, int optlen)
{
struct sock *sk = sock->sk;
+ char devname[IFNAMSIZ];
+ struct net_device *dev;
int opt;
if (level != SOL_NETROM)
@@ -389,6 +392,21 @@ static int nr_setsockopt(struct socket *sock, int level, int optname,
sk->protinfo.nr->idle = opt * 60 * HZ;
return 0;
+ case SO_BINDTODEVICE:
+ if (optlen > IFNAMSIZ) optlen = IFNAMSIZ;
+ if (copy_from_user(devname, optval, optlen))
+ return -EFAULT;
+
+ dev = dev_get_by_name(devname);
+ if (dev == NULL) return -ENODEV;
+
+ if (sk->type == SOCK_SEQPACKET &&
+ (sock->state != SS_UNCONNECTED || sk->state == TCP_LISTEN))
+ return -EADDRNOTAVAIL;
+
+ sk->protinfo.nr->device=dev;
+ return 0;
+
default:
return -ENOPROTOOPT;
}
@@ -398,15 +416,24 @@ static int nr_getsockopt(struct socket *sock, int level, int optname,
char *optval, int *optlen)
{
struct sock *sk = sock->sk;
+ struct net_device *dev;
+ char devname[IFNAMSIZ];
+ void *valptr;
int val = 0;
- int len;
+ int maxlen, length;
if (level != SOL_NETROM)
return -ENOPROTOOPT;
- if (get_user(len, optlen))
+ if (get_user(maxlen, optlen))
return -EFAULT;
+ if (maxlen < 1)
+ return -EFAULT;
+
+ valptr = (void *) &val;
+ length = min(maxlen, sizeof(int));
+
switch (optname) {
case NETROM_T1:
val = sk->protinfo.nr->t1 / HZ;
@@ -428,16 +455,33 @@ static int nr_getsockopt(struct socket *sock, int level, int optname,
val = sk->protinfo.nr->idle / (60 * HZ);
break;
+ case SO_BINDTODEVICE:
+ dev = sk->protinfo.nr->device;
+
+ if (dev != NULL) {
+ strncpy(devname, dev->name, IFNAMSIZ);
+ length = min(strlen(dev->name)+1, maxlen);
+ devname[length-1] = '\0';
+ } else {
+ *devname = '\0';
+ length = 1;
+ }
+
+ valptr = (void *) devname;
+ break;
+
+
default:
return -ENOPROTOOPT;
}
- len = min(len, sizeof(int));
+ if (put_user(length, optlen))
+ return -EFAULT;
- if (put_user(len, optlen))
+ if (copy_to_user(optval, valptr, length))
return -EFAULT;
- return copy_to_user(optval, &val, len) ? -EFAULT : 0;
+ return 0;
}
static int nr_listen(struct socket *sock, int backlog)
@@ -589,13 +633,12 @@ static int nr_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
struct sock *sk = sock->sk;
struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr;
struct net_device *dev;
- ax25_address *user, *source;
+ ax25_address *user = NULL, *source = NULL;
if (sk->zapped == 0)
return -EINVAL;
- if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct
-full_sockaddr_ax25))
+ if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct full_sockaddr_ax25))
return -EINVAL;
if (addr_len < (addr->fsa_ax25.sax25_ndigis * sizeof(ax25_address) + sizeof(struct sockaddr_ax25)))
@@ -604,32 +647,42 @@ full_sockaddr_ax25))
if (addr->fsa_ax25.sax25_family != AF_NETROM)
return -EINVAL;
- if ((dev = nr_dev_get(&addr->fsa_ax25.sax25_call)) == NULL) {
- SOCK_DEBUG(sk, "NET/ROM: bind failed: invalid node callsign\n");
- return -EADDRNOTAVAIL;
- }
-
/*
- * Only the super user can set an arbitrary user callsign.
+ * User did not set the interfave with SO_BINDTODEVICE,
+ * use compatibility code.
*/
- if (addr->fsa_ax25.sax25_ndigis == 1) {
- if (!capable(CAP_NET_BIND_SERVICE))
- return -EACCES;
- sk->protinfo.nr->user_addr = addr->fsa_digipeater[0];
- sk->protinfo.nr->source_addr = addr->fsa_ax25.sax25_call;
- } else {
- source = &addr->fsa_ax25.sax25_call;
- if ((user = ax25_findbyuid(current->euid)) == NULL) {
- if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE))
- return -EPERM;
- user = source;
+ dev = sk->protinfo.nr->device;
+ if (dev == NULL) {
+ if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsax25_ndigis == 1) {
+ /* device callsign provided */
+ if(ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) &&
+ (dev = nr_dev_get(&addr->fsa_digipeater[0])) == NULL)
+ return -EADDRNOTAVAIL;
+ source = &addr->fsa_digipeater[0];
+ } else {
+ if ((dev = nr_dev_get(&addr->fsax25_call)) == NULL)
+ return -EADDRNOTAVAIL;
+ source = &addr->fsa_ax25.sax25_call;
}
- sk->protinfo.nr->user_addr = *user;
- sk->protinfo.nr->source_addr = *source;
+ sk->protinfo.nr->device = dev;
+ } else {
+ source = (ax25_address *) dev->dev_addr;
}
+ if (!capable(CAP_NET_BIND_SERVICE))
+ {
+ /* FIXME: should not be coupled with AX.25 */
+ user = ax25_find_match_for_uid(current->euid, &addr->fsax25_call, dev->name);
+ if (user == NULL && ax25_uid_policy)
+ return -EACCES;
+ }
+
+ if (user == NULL) user = &addr->fsax25_call;
+ sk->protinfo.nr->user_addr = *user;
+ sk->protinfo.nr->source_addr = *source;
+
sk->protinfo.nr->device = dev;
nr_insert_socket(sk);
@@ -643,7 +696,6 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr,
{
struct sock *sk = sock->sk;
struct sockaddr_ax25 *addr = (struct sockaddr_ax25 *)uaddr;
- ax25_address *user, *source = NULL;
struct net_device *dev;
if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
@@ -668,26 +720,8 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr,
if (addr->sax25_family != AF_NETROM)
return -EINVAL;
- if (sk->zapped) { /* Must bind first - autobinding in this may or may not work */
- sk->zapped = 0;
-
- if ((dev = nr_dev_first()) == NULL)
- return -ENETUNREACH;
-
- source = (ax25_address *)dev->dev_addr;
-
- if ((user = ax25_findbyuid(current->euid)) == NULL) {
- if (ax25_uid_policy && !capable(CAP_NET_ADMIN))
- return -EPERM;
- user = source;
- }
-
- sk->protinfo.nr->user_addr = *user;
- sk->protinfo.nr->source_addr = *source;
- sk->protinfo.nr->device = dev;
-
- nr_insert_socket(sk); /* Finish the bind */
- }
+ if (sk->zapped)
+ return -EADDRNOTAVAIL;
sk->protinfo.nr->dest_addr = addr->sax25_call;