Commit message: wan: Missing capability checks in sbni_ioctl()

There are missing capability checks in the following code:

1300 static int
1301 sbni_ioctl( struct net_device  *dev,  struct ifreq  *ifr,  int  cmd)
1302 {
[...]
1319     case  SIOCDEVRESINSTATS :
1320         if( current->euid != 0 )    /* root only */
1321             return  -EPERM;
[...]
1336     case  SIOCDEVSHWSTATE :
1337         if( current->euid != 0 )    /* root only */
1338             return  -EPERM;
[...]
1357     case  SIOCDEVENSLAVE :
1358         if( current->euid != 0 )    /* root only */
1359             return  -EPERM;
[...]
1372     case  SIOCDEVEMANSIPATE :
1373         if( current->euid != 0 )    /* root only */
1374             return  -EPERM;

Here's my proposed fix:

Missing capability checks.

Signed-off-by: Eugene Teo <eugeneteo@kernel.sg>
Signed-off-by: David S. Miller <davem@davemloft.net>
CVE: CVE-2008-3525
CWE: CWE-264
Vulnerability description: The sbni_ioctl function in drivers/net/wan/sbni.c in the wan subsystem in the Linux kernel 2.6.26.3 does not check for the CAP_NET_ADMIN capability before processing a (1) SIOCDEVRESINSTATS, (2) SIOCDEVSHWSTATE, (3) SIOCDEVENSLAVE, or (4) SIOCDEVEMANSIPATE ioctl request, which allows local users to bypass intended capability restrictions.

Project: linux-2.6

sbni_ioctl( struct net_device  *dev,  struct ifreq  *ifr,  int  cmd )
{
	struct net_local  *nl = (struct net_local *) dev->priv; 
	struct sbni_flags  flags;
	int  error = 0;

#ifdef CONFIG_SBNI_MULTILINE
	struct net_device  *slave_dev;
	char  slave_name[ 8 ];
#endif
  
	switch( cmd ) {
	case  SIOCDEVGETINSTATS :
		if (copy_to_user( ifr->ifr_data, &nl->in_stats,
					sizeof(struct sbni_in_stats) ))
			error = -EFAULT;
		break;

	case  SIOCDEVRESINSTATS :
		if( current->euid != 0 )	/* root only */
			return  -EPERM;
		memset( &nl->in_stats, 0, sizeof(struct sbni_in_stats) );
		break;

	case  SIOCDEVGHWSTATE :
		flags.mac_addr	= *(u32 *)(dev->dev_addr + 3);
		flags.rate	= nl->csr1.rate;
		flags.slow_mode	= (nl->state & FL_SLOW_MODE) != 0;
		flags.rxl	= nl->cur_rxl_index;
		flags.fixed_rxl	= nl->delta_rxl == 0;

		if (copy_to_user( ifr->ifr_data, &flags, sizeof flags ))
			error = -EFAULT;
		break;

	case  SIOCDEVSHWSTATE :
		if( current->euid != 0 )	/* root only */
			return  -EPERM;

		spin_lock( &nl->lock );
		flags = *(struct sbni_flags*) &ifr->ifr_ifru;
		if( flags.fixed_rxl )
			nl->delta_rxl = 0,
			nl->cur_rxl_index = flags.rxl;
		else
			nl->delta_rxl = DEF_RXL_DELTA,
			nl->cur_rxl_index = DEF_RXL;

		nl->csr1.rxl = rxl_tab[ nl->cur_rxl_index ];
		nl->csr1.rate = flags.rate;
		outb( *(u8 *)&nl->csr1 | PR_RES, dev->base_addr + CSR1 );
		spin_unlock( &nl->lock );
		break;

#ifdef CONFIG_SBNI_MULTILINE

	case  SIOCDEVENSLAVE :
		if( current->euid != 0 )	/* root only */
			return  -EPERM;

		if (copy_from_user( slave_name, ifr->ifr_data, sizeof slave_name ))
			return -EFAULT;
		slave_dev = dev_get_by_name(&init_net, slave_name );
		if( !slave_dev  ||  !(slave_dev->flags & IFF_UP) ) {
			printk( KERN_ERR "%s: trying to enslave non-active "
				"device %s\n", dev->name, slave_name );
			return  -EPERM;
		}

		return  enslave( dev, slave_dev );

	case  SIOCDEVEMANSIPATE :
		if( current->euid != 0 )	/* root only */
			return  -EPERM;

		return  emancipate( dev );

#endif	/* CONFIG_SBNI_MULTILINE */

	default :
		return  -EOPNOTSUPP;
	}

	return  error;
}