Commit message: mISDN: enforce CAP_NET_RAW for raw sockets

When creating a raw AF_ISDN socket, CAP_NET_RAW needs to be checked
first.

Signed-off-by: Ori Nimron <orinimron123@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
CVE: CVE-2019-17055
CWE: CWE-862
Vulnerability description: base_sock_create in drivers/isdn/mISDN/socket.c in the AF_ISDN network module in the Linux kernel through 5.3.2 does not enforce CAP_NET_RAW, which means that unprivileged users can create a raw socket, aka CID-b91ee4aa2a21.

Project: linux
File: socket.c

base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
	int			err = 0, id;
	struct mISDNdevice	*dev;
	struct mISDNversion	ver;

	switch (cmd) {
	case IMGETVERSION:
		ver.major = MISDN_MAJOR_VERSION;
		ver.minor = MISDN_MINOR_VERSION;
		ver.release = MISDN_RELEASE;
		if (copy_to_user((void __user *)arg, &ver, sizeof(ver)))
			err = -EFAULT;
		break;
	case IMGETCOUNT:
		id = get_mdevice_count();
		if (put_user(id, (int __user *)arg))
			err = -EFAULT;
		break;
	case IMGETDEVINFO:
		if (get_user(id, (int __user *)arg)) {
			err = -EFAULT;
			break;
		}
		dev = get_mdevice(id);
		if (dev) {
			struct mISDN_devinfo di;

			memset(&di, 0, sizeof(di));
			di.id = dev->id;
			di.Dprotocols = dev->Dprotocols;
			di.Bprotocols = dev->Bprotocols | get_all_Bprotocols();
			di.protocol = dev->D.protocol;
			memcpy(di.channelmap, dev->channelmap,
			       sizeof(di.channelmap));
			di.nrbchan = dev->nrbchan;
			strscpy(di.name, dev_name(&dev->dev), sizeof(di.name));
			if (copy_to_user((void __user *)arg, &di, sizeof(di)))
				err = -EFAULT;
		} else
			err = -ENODEV;
		break;
	case IMSETDEVNAME:
	{
		struct mISDN_devrename dn;
		if (copy_from_user(&dn, (void __user *)arg,
				   sizeof(dn))) {
			err = -EFAULT;
			break;
		}
		dn.name[sizeof(dn.name) - 1] = '\0';
		dev = get_mdevice(dn.id);
		if (dev)
			err = device_rename(&dev->dev, dn.name);
		else
			err = -ENODEV;
	}
	break;
	default:
		err = -EINVAL;
	}
	return err;
}

Project: linux
File: socket.c

base_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
{
	struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
	struct sock *sk = sock->sk;
	int err = 0;

	if (addr_len < sizeof(struct sockaddr_mISDN))
		return -EINVAL;

	if (!maddr || maddr->family != AF_ISDN)
		return -EINVAL;

	lock_sock(sk);

	if (_pms(sk)->dev) {
		err = -EALREADY;
		goto done;
	}

	_pms(sk)->dev = get_mdevice(maddr->dev);
	if (!_pms(sk)->dev) {
		err = -ENODEV;
		goto done;
	}
	sk->sk_state = MISDN_BOUND;

done:
	release_sock(sk);
	return err;
}

Project: linux
File: socket.c

data_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
	int			err = 0, id;
	struct sock		*sk = sock->sk;
	struct mISDNdevice	*dev;
	struct mISDNversion	ver;

	switch (cmd) {
	case IMGETVERSION:
		ver.major = MISDN_MAJOR_VERSION;
		ver.minor = MISDN_MINOR_VERSION;
		ver.release = MISDN_RELEASE;
		if (copy_to_user((void __user *)arg, &ver, sizeof(ver)))
			err = -EFAULT;
		break;
	case IMGETCOUNT:
		id = get_mdevice_count();
		if (put_user(id, (int __user *)arg))
			err = -EFAULT;
		break;
	case IMGETDEVINFO:
		if (get_user(id, (int __user *)arg)) {
			err = -EFAULT;
			break;
		}
		dev = get_mdevice(id);
		if (dev) {
			struct mISDN_devinfo di;

			memset(&di, 0, sizeof(di));
			di.id = dev->id;
			di.Dprotocols = dev->Dprotocols;
			di.Bprotocols = dev->Bprotocols | get_all_Bprotocols();
			di.protocol = dev->D.protocol;
			memcpy(di.channelmap, dev->channelmap,
			       sizeof(di.channelmap));
			di.nrbchan = dev->nrbchan;
			strscpy(di.name, dev_name(&dev->dev), sizeof(di.name));
			if (copy_to_user((void __user *)arg, &di, sizeof(di)))
				err = -EFAULT;
		} else
			err = -ENODEV;
		break;
	default:
		if (sk->sk_state == MISDN_BOUND)
			err = data_sock_ioctl_bound(sk, cmd,
						    (void __user *)arg);
		else
			err = -ENOTCONN;
	}
	return err;
}

Project: linux
File: socket.c

data_sock_create(struct net *net, struct socket *sock, int protocol, int kern)
{
	struct sock *sk;

	if (sock->type != SOCK_DGRAM)
		return -ESOCKTNOSUPPORT;

	sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto, kern);
	if (!sk)
		return -ENOMEM;

	sock_init_data(sock, sk);

	sock->ops = &data_sock_ops;
	sock->state = SS_UNCONNECTED;
	sock_reset_flag(sk, SOCK_ZAPPED);

	sk->sk_protocol = protocol;
	sk->sk_state    = MISDN_OPEN;
	mISDN_sock_link(&data_sockets, sk);

	return 0;
}

Project: linux
File: socket.c

mISDN_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
{
	struct __kernel_old_timeval	tv;

	if (_pms(sk)->cmask & MISDN_TIME_STAMP) {
		skb_get_timestamp(skb, &tv);
		put_cmsg(msg, SOL_MISDN, MISDN_TIME_STAMP, sizeof(tv), &tv);
	}
}

Project: linux
File: socket.c

data_sock_getname(struct socket *sock, struct sockaddr *addr,
		  int peer)
{
	struct sockaddr_mISDN	*maddr = (struct sockaddr_mISDN *) addr;
	struct sock		*sk = sock->sk;

	if (!_pms(sk)->dev)
		return -EBADFD;

	lock_sock(sk);

	maddr->family = AF_ISDN;
	maddr->dev = _pms(sk)->dev->id;
	maddr->channel = _pms(sk)->ch.nr;
	maddr->sapi = _pms(sk)->ch.addr & 0xff;
	maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xff;
	release_sock(sk);
	return sizeof(*maddr);
}

Project: linux
File: socket.c

mISDN_sock_create(struct net *net, struct socket *sock, int proto, int kern)
{
	int err = -EPROTONOSUPPORT;

	switch (proto) {
	case ISDN_P_BASE:
		err = base_sock_create(net, sock, proto, kern);
		break;
	case ISDN_P_TE_S0:
	case ISDN_P_NT_S0:
	case ISDN_P_TE_E1:
	case ISDN_P_NT_E1:
	case ISDN_P_LAPD_TE:
	case ISDN_P_LAPD_NT:
	case ISDN_P_B_RAW:
	case ISDN_P_B_HDLC:
	case ISDN_P_B_X75SLP:
	case ISDN_P_B_L2DTMF:
	case ISDN_P_B_L2DSP:
	case ISDN_P_B_L2DSPHDLC:
		err = data_sock_create(net, sock, proto, kern);
		break;
	default:
		return err;
	}

	return err;
}

Project: linux
File: socket.c

mISDN_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
		   int flags)
{
	struct sk_buff		*skb;
	struct sock		*sk = sock->sk;

	int		copied, err;

	if (*debug & DEBUG_SOCKET)
		printk(KERN_DEBUG "%s: len %d, flags %x ch.nr %d, proto %x\n",
		       __func__, (int)len, flags, _pms(sk)->ch.nr,
		       sk->sk_protocol);
	if (flags & (MSG_OOB))
		return -EOPNOTSUPP;

	if (sk->sk_state == MISDN_CLOSED)
		return 0;

	skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &err);
	if (!skb)
		return err;

	if (msg->msg_name) {
		DECLARE_SOCKADDR(struct sockaddr_mISDN *, maddr, msg->msg_name);

		maddr->family = AF_ISDN;
		maddr->dev = _pms(sk)->dev->id;
		if ((sk->sk_protocol == ISDN_P_LAPD_TE) ||
		    (sk->sk_protocol == ISDN_P_LAPD_NT)) {
			maddr->channel = (mISDN_HEAD_ID(skb) >> 16) & 0xff;
			maddr->tei =  (mISDN_HEAD_ID(skb) >> 8) & 0xff;
			maddr->sapi = mISDN_HEAD_ID(skb) & 0xff;
		} else {
			maddr->channel = _pms(sk)->ch.nr;
			maddr->sapi = _pms(sk)->ch.addr & 0xFF;
			maddr->tei =  (_pms(sk)->ch.addr >> 8) & 0xFF;
		}
		msg->msg_namelen = sizeof(*maddr);
	}

	copied = skb->len + MISDN_HEADER_LEN;
	if (len < copied) {
		if (flags & MSG_PEEK)
			refcount_dec(&skb->users);
		else
			skb_queue_head(&sk->sk_receive_queue, skb);
		return -ENOSPC;
	}
	memcpy(skb_push(skb, MISDN_HEADER_LEN), mISDN_HEAD_P(skb),
	       MISDN_HEADER_LEN);

	err = skb_copy_datagram_msg(skb, 0, msg, copied);

	mISDN_sock_cmsg(sk, msg, skb);

	skb_free_datagram(sk, skb);

	return err ? : copied;
}

Project: linux
File: socket.c

base_sock_create(struct net *net, struct socket *sock, int protocol, int kern)
{
	struct sock *sk;

	if (sock->type != SOCK_RAW)
		return -ESOCKTNOSUPPORT;
	if (!capable(CAP_NET_RAW))
		return -EPERM;

	sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto, kern);
	if (!sk)
		return -ENOMEM;

	sock_init_data(sock, sk);
	sock->ops = &base_sock_ops;
	sock->state = SS_UNCONNECTED;
	sock_reset_flag(sk, SOCK_ZAPPED);
	sk->sk_protocol = protocol;
	sk->sk_state    = MISDN_OPEN;
	mISDN_sock_link(&base_sockets, sk);

	return 0;
}

Project: linux
File: socket.c

mISDN_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
{
	struct sock		*sk = sock->sk;
	struct sk_buff		*skb;
	int			err = -ENOMEM;

	if (*debug & DEBUG_SOCKET)
		printk(KERN_DEBUG "%s: len %d flags %x ch %d proto %x\n",
		       __func__, (int)len, msg->msg_flags, _pms(sk)->ch.nr,
		       sk->sk_protocol);

	if (msg->msg_flags & MSG_OOB)
		return -EOPNOTSUPP;

	if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_NOSIGNAL | MSG_ERRQUEUE))
		return -EINVAL;

	if (len < MISDN_HEADER_LEN)
		return -EINVAL;

	if (sk->sk_state != MISDN_BOUND)
		return -EBADFD;

	lock_sock(sk);

	skb = _l2_alloc_skb(len, GFP_KERNEL);
	if (!skb)
		goto done;

	if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
		err = -EFAULT;
		goto done;
	}

	memcpy(mISDN_HEAD_P(skb), skb->data, MISDN_HEADER_LEN);
	skb_pull(skb, MISDN_HEADER_LEN);

	if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) {
		/* if we have a address, we use it */
		DECLARE_SOCKADDR(struct sockaddr_mISDN *, maddr, msg->msg_name);
		mISDN_HEAD_ID(skb) = maddr->channel;
	} else { /* use default for L2 messages */
		if ((sk->sk_protocol == ISDN_P_LAPD_TE) ||
		    (sk->sk_protocol == ISDN_P_LAPD_NT))
			mISDN_HEAD_ID(skb) = _pms(sk)->ch.nr;
	}

	if (*debug & DEBUG_SOCKET)
		printk(KERN_DEBUG "%s: ID:%x\n",
		       __func__, mISDN_HEAD_ID(skb));

	err = -ENODEV;
	if (!_pms(sk)->ch.peer)
		goto done;
	err = _pms(sk)->ch.recv(_pms(sk)->ch.peer, skb);
	if (err)
		goto done;
	else {
		skb = NULL;
		err = len;
	}

done:
	kfree_skb(skb);
	release_sock(sk);
	return err;
}