static int sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
{
	struct sctp_sock *sp = sctp_sk(sk);
	bool reuse = (sk->sk_reuse || sp->reuse);
	struct sctp_bind_hashbucket *head; /* hash list */
	struct net *net = sock_net(sk);
	kuid_t uid = sock_i_uid(sk);
	struct sctp_bind_bucket *pp;
	unsigned short snum;
	int ret;

	snum = ntohs(addr->v4.sin_port);

	pr_debug("%s: begins, snum:%d\n", __func__, snum);

	if (snum == 0) {
		/* Search for an available port. */
		int low, high, remaining, index;
		unsigned int rover;

		inet_get_local_port_range(net, &low, &high);
		remaining = (high - low) + 1;
		rover = prandom_u32() % remaining + low;

		do {
			rover++;
			if ((rover < low) || (rover > high))
				rover = low;
			if (inet_is_local_reserved_port(net, rover))
				continue;
			index = sctp_phashfn(net, rover);
			head = &sctp_port_hashtable[index];
			spin_lock_bh(&head->lock);
			sctp_for_each_hentry(pp, &head->chain)
				if ((pp->port == rover) &&
				    net_eq(net, pp->net))
					goto next;
			break;
		next:
			spin_unlock_bh(&head->lock);
			cond_resched();
		} while (--remaining > 0);

		/* Exhausted local port range during search? */
		ret = 1;
		if (remaining <= 0)
			return ret;

		/* OK, here is the one we will use.  HEAD (the port
		 * hash table list entry) is non-NULL and we hold it's
		 * mutex.
		 */
		snum = rover;
	} else {
		/* We are given an specific port number; we verify
		 * that it is not being used. If it is used, we will
		 * exahust the search in the hash list corresponding
		 * to the port number (snum) - we detect that with the
		 * port iterator, pp being NULL.
		 */
		head = &sctp_port_hashtable[sctp_phashfn(net, snum)];
		spin_lock_bh(&head->lock);
		sctp_for_each_hentry(pp, &head->chain) {
			if ((pp->port == snum) && net_eq(pp->net, net))
				goto pp_found;
		}
	}
	pp = NULL;
	goto pp_not_found;
pp_found:
	if (!hlist_empty(&pp->owner)) {
		/* We had a port hash table hit - there is an
		 * available port (pp != NULL) and it is being
		 * used by other socket (pp->owner not empty); that other
		 * socket is going to be sk2.
		 */
		struct sock *sk2;

		pr_debug("%s: found a possible match\n", __func__);

		if ((pp->fastreuse && reuse &&
		     sk->sk_state != SCTP_SS_LISTENING) ||
		    (pp->fastreuseport && sk->sk_reuseport &&
		     uid_eq(pp->fastuid, uid)))
			goto success;

		/* Run through the list of sockets bound to the port
		 * (pp->port) [via the pointers bind_next and
		 * bind_pprev in the struct sock *sk2 (pp->sk)]. On each one,
		 * we get the endpoint they describe and run through
		 * the endpoint's list of IP (v4 or v6) addresses,
		 * comparing each of the addresses with the address of
		 * the socket sk. If we find a match, then that means
		 * that this port/socket (sk) combination are already
		 * in an endpoint.
		 */
		sk_for_each_bound(sk2, &pp->owner) {
			struct sctp_sock *sp2 = sctp_sk(sk2);
			struct sctp_endpoint *ep2 = sp2->ep;

			if (sk == sk2 ||
			    (reuse && (sk2->sk_reuse || sp2->reuse) &&
			     sk2->sk_state != SCTP_SS_LISTENING) ||
			    (sk->sk_reuseport && sk2->sk_reuseport &&
			     uid_eq(uid, sock_i_uid(sk2))))
				continue;

			if (sctp_bind_addr_conflict(&ep2->base.bind_addr,
						    addr, sp2, sp)) {
				ret = 1;
				goto fail_unlock;
			}
		}

		pr_debug("%s: found a match\n", __func__);
	}
pp_not_found:
	/* If there was a hash table miss, create a new port.  */
	ret = 1;
	if (!pp && !(pp = sctp_bucket_create(head, net, snum)))
		goto fail_unlock;

	/* In either case (hit or miss), make sure fastreuse is 1 only
	 * if sk->sk_reuse is too (that is, if the caller requested
	 * SO_REUSEADDR on this socket -sk-).
	 */
	if (hlist_empty(&pp->owner)) {
		if (reuse && sk->sk_state != SCTP_SS_LISTENING)
			pp->fastreuse = 1;
		else
			pp->fastreuse = 0;

		if (sk->sk_reuseport) {
			pp->fastreuseport = 1;
			pp->fastuid = uid;
		} else {
			pp->fastreuseport = 0;
		}
	} else {
		if (pp->fastreuse &&
		    (!reuse || sk->sk_state == SCTP_SS_LISTENING))
			pp->fastreuse = 0;

		if (pp->fastreuseport &&
		    (!sk->sk_reuseport || !uid_eq(pp->fastuid, uid)))
			pp->fastreuseport = 0;
	}

	/* We are set, so fill up all the data in the hash table
	 * entry, tie the socket list information with the rest of the
	 * sockets FIXME: Blurry, NPI (ipg).
	 */
success:
	if (!sp->bind_hash) {
		inet_sk(sk)->inet_num = snum;
		sk_add_bind_node(sk, &pp->owner);
		sp->bind_hash = pp;
	}
	ret = 0;

fail_unlock:
	spin_unlock_bh(&head->lock);
	return ret;
}


static int sctp_getsockopt_reconfig_supported(struct sock *sk, int len,
					      char __user *optval,
					      int __user *optlen)
{
	struct sctp_assoc_value params;
	struct sctp_association *asoc;
	int retval = -EFAULT;

	if (len < sizeof(params)) {
		retval = -EINVAL;
		goto out;
	}

	len = sizeof(params);
	if (copy_from_user(&params, optval, len))
		goto out;

	asoc = sctp_id2assoc(sk, params.assoc_id);
	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
	    sctp_style(sk, UDP)) {
		retval = -EINVAL;
		goto out;
	}

	params.assoc_value = asoc ? asoc->peer.reconf_capable
				  : sctp_sk(sk)->ep->reconf_enable;

	if (put_user(len, optlen))
		goto out;

	if (copy_to_user(optval, &params, len))
		goto out;

	retval = 0;

out:
	return retval;
}

net/sctp: fix race condition in sctp_destroy_sock

If sctp_destroy_sock is called without sock_net(sk)->sctp.addr_wq_lock
held and sp->do_auto_asconf is true, then an element is removed
from the auto_asconf_splist without any proper locking.

This can happen in the following functions:
1. In sctp_accept, if sctp_sock_migrate fails.
2. In inet_create or inet6_create, if there is a bpf program
   attached to BPF_CGROUP_INET_SOCK_CREATE which denies
   creation of the sctp socket.

The bug is fixed by acquiring addr_wq_lock in sctp_destroy_sock
instead of sctp_close.

This addresses CVE-2021-23133.

Reported-by: Or Cohen <orcohen@paloaltonetworks.com>
Reviewed-by: Xin Long <lucien.xin@gmail.com>
Fixes: 610236587600 ("bpf: Add new cgroup attach type to enable sock modifications")
Signed-off-by: Or Cohen <orcohen@paloaltonetworks.com>
Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

static bool sctp_writeable(struct sock *sk);

static void sctp_wfree(struct sk_buff *skb);

static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
				size_t msg_len);

static int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p);

static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p);

static int sctp_wait_for_accept(struct sock *sk, long timeo);

static void sctp_wait_for_close(struct sock *sk, long timeo);

static void sctp_destruct_sock(struct sock *sk);

static struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt,
					union sctp_addr *addr, int len);

static int sctp_bindx_add(struct sock *, struct sockaddr *, int);

static int sctp_bindx_rem(struct sock *, struct sockaddr *, int);

static int sctp_send_asconf_add_ip(struct sock *, struct sockaddr *, int);

static int sctp_send_asconf_del_ip(struct sock *, struct sockaddr *, int);

static int sctp_send_asconf(struct sctp_association *asoc,
			    struct sctp_chunk *chunk);

static int sctp_do_bind(struct sock *, union sctp_addr *, int);

static int sctp_autobind(struct sock *sk);

static int sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
			     struct sctp_association *assoc,
			     enum sctp_socket_type type);

static unsigned long sctp_memory_pressure;

static atomic_long_t sctp_memory_allocated;

struct percpu_counter sctp_sockets_allocated;

#define traverse_and_process()	\
do {				\
	msg = chunk->msg;	\
	if (msg == prev_msg)	\
		continue;	\
	list_for_each_entry(c, &msg->chunks, frag_list) {	\
		if ((clear && asoc->base.sk == c->skb->sk) ||	\
		    (!clear && asoc->base.sk != c->skb->sk))	\
			cb(c);	\
	}			\
	prev_msg = msg;		\
} while (0)

static int sctp_get_port_local(struct sock *, union sctp_addr *);

struct compat_sctp_getaddrs_old {
	sctp_assoc_t	assoc_id;
	s32		addr_num;
	compat_uptr_t	addrs;		/* struct sockaddr * */
}

static int sctp_msghdr_parse(const struct msghdr *msg,
			     struct sctp_cmsgs *cmsgs);

static struct sctp_bind_bucket *sctp_bucket_create(
	struct sctp_bind_hashbucket *head, struct net *, unsigned short snum);


void sctp_put_port(struct sock *sk)
{
	local_bh_disable();
	__sctp_put_port(sk);
	local_bh_enable();
}

static int sctp_writeable(struct sock *sk);

static void sctp_wfree(struct sk_buff *skb);

static int sctp_wait_for_sndbuf(struct sctp_association *, long *timeo_p,
				size_t msg_len);

static int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p);

static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p);

static int sctp_wait_for_accept(struct sock *sk, long timeo);

static void sctp_wait_for_close(struct sock *sk, long timeo);

static void sctp_destruct_sock(struct sock *sk);

static struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt,
					union sctp_addr *addr, int len);

static int sctp_bindx_add(struct sock *, struct sockaddr *, int);

static int sctp_bindx_rem(struct sock *, struct sockaddr *, int);

static int sctp_send_asconf_add_ip(struct sock *, struct sockaddr *, int);

static int sctp_send_asconf_del_ip(struct sock *, struct sockaddr *, int);

static int sctp_send_asconf(struct sctp_association *asoc,
			    struct sctp_chunk *chunk);

static int sctp_do_bind(struct sock *, union sctp_addr *, int);

static int sctp_autobind(struct sock *sk);

static void sctp_sock_migrate(struct sock *, struct sock *,
			      struct sctp_association *, sctp_socket_type_t);

struct percpu_counter sctp_sockets_allocated;

static long sctp_get_port_local(struct sock *, union sctp_addr *);

struct compat_sctp_getaddrs_old {
	sctp_assoc_t	assoc_id;
	s32		addr_num;
	compat_uptr_t	addrs;		/* struct sockaddr * */
}

static int sctp_msghdr_parse(const struct msghdr *, sctp_cmsgs_t *);

static struct sctp_bind_bucket *sctp_bucket_create(
	struct sctp_bind_hashbucket *head, struct net *, unsigned short snum);

struct proto sctp_prot = {
	.name        =	"SCTP",
	.owner       =	THIS_MODULE,
	.close       =	sctp_close,
	.connect     =	sctp_connect,
	.disconnect  =	sctp_disconnect,
	.accept      =	sctp_accept,
	.ioctl       =	sctp_ioctl,
	.init        =	sctp_init_sock,
	.destroy     =	sctp_destroy_sock,
	.shutdown    =	sctp_shutdown,
	.setsockopt  =	sctp_setsockopt,
	.getsockopt  =	sctp_getsockopt,
	.sendmsg     =	sctp_sendmsg,
	.recvmsg     =	sctp_recvmsg,
	.bind        =	sctp_bind,
	.backlog_rcv =	sctp_backlog_rcv,
	.hash        =	sctp_hash,
	.unhash      =	sctp_unhash,
	.get_port    =	sctp_get_port,
	.obj_size    =  sizeof(struct sctp_sock),
	.sysctl_mem  =  sysctl_sctp_mem,
	.sysctl_rmem =  sysctl_sctp_rmem,
	.sysctl_wmem =  sysctl_sctp_wmem,
	.memory_pressure = &sctp_memory_pressure,
	.enter_memory_pressure = sctp_enter_memory_pressure,
	.memory_allocated = &sctp_memory_allocated,
	.sockets_allocated = &sctp_sockets_allocated,
};


static int sctp_getsockopt_maxseg(struct sock *sk, int len,
				  char __user *optval, int __user *optlen)
{
	struct sctp_assoc_value params;
	struct sctp_association *asoc;

	if (len == sizeof(int)) {
		pr_warn_ratelimited(DEPRECATED
				    "%s (pid %d) "
				    "Use of int in maxseg socket option.\n"
				    "Use struct sctp_assoc_value instead\n",
				    current->comm, task_pid_nr(current));
		params.assoc_id = SCTP_FUTURE_ASSOC;
	} else if (len >= sizeof(struct sctp_assoc_value)) {
		len = sizeof(struct sctp_assoc_value);
		if (copy_from_user(&params, optval, len))
			return -EFAULT;
	} else
		return -EINVAL;

	asoc = sctp_id2assoc(sk, params.assoc_id);
	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
	    sctp_style(sk, UDP))
		return -EINVAL;

	if (asoc)
		params.assoc_value = asoc->frag_point;
	else
		params.assoc_value = sctp_sk(sk)->user_frag;

	if (put_user(len, optlen))
		return -EFAULT;
	if (len == sizeof(int)) {
		if (copy_to_user(optval, &params.assoc_value, len))
			return -EFAULT;
	} else {
		if (copy_to_user(optval, &params, len))
			return -EFAULT;
	}

	return 0;
}


static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to,
			    size_t space_left, int *bytes_copied)
{
	struct sctp_sockaddr_entry *addr;
	union sctp_addr temp;
	int cnt = 0;
	int addrlen;
	struct net *net = sock_net(sk);

	rcu_read_lock();
	list_for_each_entry_rcu(addr, &net->sctp.local_addr_list, list) {
		if (!addr->valid)
			continue;

		if ((PF_INET == sk->sk_family) &&
		    (AF_INET6 == addr->a.sa.sa_family))
			continue;
		if ((PF_INET6 == sk->sk_family) &&
		    inet_v6_ipv6only(sk) &&
		    (AF_INET == addr->a.sa.sa_family))
			continue;
		memcpy(&temp, &addr->a, sizeof(temp));
		if (!temp.v4.sin_port)
			temp.v4.sin_port = htons(port);

		addrlen = sctp_get_pf_specific(sk->sk_family)
			      ->addr_to_user(sctp_sk(sk), &temp);

		if (space_left < addrlen) {
			cnt =  -ENOMEM;
			break;
		}
		memcpy(to, &temp, addrlen);

		to += addrlen;
		cnt++;
		space_left -= addrlen;
		*bytes_copied += addrlen;
	}
	rcu_read_unlock();

	return cnt;
}

static int sctp_writeable(struct sock *sk);

static void sctp_wfree(struct sk_buff *skb);

static int sctp_wait_for_sndbuf(struct sctp_association *, long *timeo_p,
				size_t msg_len);

static int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p);

static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p);

static int sctp_wait_for_accept(struct sock *sk, long timeo);

static void sctp_wait_for_close(struct sock *sk, long timeo);

static void sctp_destruct_sock(struct sock *sk);

static struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt,
					union sctp_addr *addr, int len);

static int sctp_bindx_add(struct sock *, struct sockaddr *, int);

static int sctp_bindx_rem(struct sock *, struct sockaddr *, int);

static int sctp_send_asconf_add_ip(struct sock *, struct sockaddr *, int);

static int sctp_send_asconf_del_ip(struct sock *, struct sockaddr *, int);

static int sctp_send_asconf(struct sctp_association *asoc,
			    struct sctp_chunk *chunk);

static int sctp_do_bind(struct sock *, union sctp_addr *, int);

static int sctp_autobind(struct sock *sk);

static void sctp_sock_migrate(struct sock *, struct sock *,
			      struct sctp_association *, sctp_socket_type_t);

static int sctp_memory_pressure;

static atomic_long_t sctp_memory_allocated;

struct percpu_counter sctp_sockets_allocated;

static long sctp_get_port_local(struct sock *, union sctp_addr *);

struct compat_sctp_getaddrs_old {
	sctp_assoc_t	assoc_id;
	s32		addr_num;
	compat_uptr_t	addrs;		/* struct sockaddr * */
}

static int sctp_msghdr_parse(const struct msghdr *, sctp_cmsgs_t *);

static struct sctp_bind_bucket *sctp_bucket_create(
	struct sctp_bind_hashbucket *head, struct net *, unsigned short snum);

struct proto sctp_prot = {
	.name        =	"SCTP",
	.owner       =	THIS_MODULE,
	.close       =	sctp_close,
	.connect     =	sctp_connect,
	.disconnect  =	sctp_disconnect,
	.accept      =	sctp_accept,
	.ioctl       =	sctp_ioctl,
	.init        =	sctp_init_sock,
	.destroy     =	sctp_destroy_sock,
	.shutdown    =	sctp_shutdown,
	.setsockopt  =	sctp_setsockopt,
	.getsockopt  =	sctp_getsockopt,
	.sendmsg     =	sctp_sendmsg,
	.recvmsg     =	sctp_recvmsg,
	.bind        =	sctp_bind,
	.backlog_rcv =	sctp_backlog_rcv,
	.hash        =	sctp_hash,
	.unhash      =	sctp_unhash,
	.get_port    =	sctp_get_port,
	.obj_size    =  sizeof(struct sctp_sock),
	.sysctl_mem  =  sysctl_sctp_mem,
	.sysctl_rmem =  sysctl_sctp_rmem,
	.sysctl_wmem =  sysctl_sctp_wmem,
	.memory_pressure = &sctp_memory_pressure,
	.enter_memory_pressure = sctp_enter_memory_pressure,
	.memory_allocated = &sctp_memory_allocated,
	.sockets_allocated = &sctp_sockets_allocated,
};


static int sctp_getsockopt_paddr_thresholds(struct sock *sk,
					    char __user *optval, int len,
					    int __user *optlen, bool v2)
{
	struct sctp_paddrthlds_v2 val;
	struct sctp_transport *trans;
	struct sctp_association *asoc;
	int min;

	min = v2 ? sizeof(val) : sizeof(struct sctp_paddrthlds);
	if (len < min)
		return -EINVAL;
	len = min;
	if (copy_from_user(&val, optval, len))
		return -EFAULT;

	if (!sctp_is_any(sk, (const union sctp_addr *)&val.spt_address)) {
		trans = sctp_addr_id2transport(sk, &val.spt_address,
					       val.spt_assoc_id);
		if (!trans)
			return -ENOENT;

		val.spt_pathmaxrxt = trans->pathmaxrxt;
		val.spt_pathpfthld = trans->pf_retrans;
		val.spt_pathcpthld = trans->ps_retrans;

		goto out;
	}

	asoc = sctp_id2assoc(sk, val.spt_assoc_id);
	if (!asoc && val.spt_assoc_id != SCTP_FUTURE_ASSOC &&
	    sctp_style(sk, UDP))
		return -EINVAL;

	if (asoc) {
		val.spt_pathpfthld = asoc->pf_retrans;
		val.spt_pathmaxrxt = asoc->pathmaxrxt;
		val.spt_pathcpthld = asoc->ps_retrans;
	} else {
		struct sctp_sock *sp = sctp_sk(sk);

		val.spt_pathpfthld = sp->pf_retrans;
		val.spt_pathmaxrxt = sp->pathmaxrxt;
		val.spt_pathcpthld = sp->ps_retrans;
	}

out:
	if (put_user(len, optlen) || copy_to_user(optval, &val, len))
		return -EFAULT;

	return 0;
}


static int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg)
{
	int rc = -ENOTCONN;

	lock_sock(sk);

	/*
	 * SEQPACKET-style sockets in LISTENING state are valid, for
	 * SCTP, so only discard TCP-style sockets in LISTENING state.
	 */
	if (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING))
		goto out;

	switch (cmd) {
	case SIOCINQ: {
		struct sk_buff *skb;
		unsigned int amount = 0;

		skb = skb_peek(&sk->sk_receive_queue);
		if (skb != NULL) {
			/*
			 * We will only return the amount of this packet since
			 * that is all that will be read.
			 */
			amount = skb->len;
		}
		rc = put_user(amount, (int __user *)arg);
		break;
	}
	default:
		rc = -ENOIOCTLCMD;
		break;
	}
out:
	release_sock(sk);
	return rc;
}


static int sctp_bind(struct sock *sk, struct sockaddr *addr, int addr_len)
{
	int retval = 0;

	lock_sock(sk);

	pr_debug("%s: sk:%p, addr:%p, addr_len:%d\n", __func__, sk,
		 addr, addr_len);

	/* Disallow binding twice. */
	if (!sctp_sk(sk)->ep->base.bind_addr.port)
		retval = sctp_do_bind(sk, (union sctp_addr *)addr,
				      addr_len);
	else
		retval = -EINVAL;

	release_sock(sk);

	return retval;
}


static int sctp_connect_add_peer(struct sctp_association *asoc,
				 union sctp_addr *daddr, int addr_len)
{
	struct sctp_endpoint *ep = asoc->ep;
	struct sctp_association *old;
	struct sctp_transport *t;
	int err;

	err = sctp_verify_addr(ep->base.sk, daddr, addr_len);
	if (err)
		return err;

	old = sctp_endpoint_lookup_assoc(ep, daddr, &t);
	if (old && old != asoc)
		return old->state >= SCTP_STATE_ESTABLISHED ? -EISCONN
							    : -EALREADY;

	if (sctp_endpoint_is_peeled_off(ep, daddr))
		return -EADDRNOTAVAIL;

	t = sctp_assoc_add_peer(asoc, daddr, GFP_KERNEL, SCTP_UNKNOWN);
	if (!t)
		return -ENOMEM;

	return 0;
}


struct sctp_association *sctp_id2assoc(struct sock *sk, sctp_assoc_t id)
{
	struct sctp_association *asoc = NULL;

	/* If this is not a UDP-style socket, assoc id should be ignored. */
	if (!sctp_style(sk, UDP)) {
		/* Return NULL if the socket state is not ESTABLISHED. It
		 * could be a TCP-style listening socket or a socket which
		 * hasn't yet called connect() to establish an association.
		 */
		if (!sctp_sstate(sk, ESTABLISHED) && !sctp_sstate(sk, CLOSING))
			return NULL;

		/* Get the first and the only association from the list. */
		if (!list_empty(&sctp_sk(sk)->ep->asocs))
			asoc = list_entry(sctp_sk(sk)->ep->asocs.next,
					  struct sctp_association, asocs);
		return asoc;
	}

	/* Otherwise this is a UDP-style socket. */
	if (id <= SCTP_ALL_ASSOC)
		return NULL;

	spin_lock_bh(&sctp_assocs_id_lock);
	asoc = (struct sctp_association *)idr_find(&sctp_assocs_id, (int)id);
	if (asoc && (asoc->base.sk != sk || asoc->base.dead))
		asoc = NULL;
	spin_unlock_bh(&sctp_assocs_id_lock);

	return asoc;
}

static bool sctp_writeable(struct sock *sk);

static void sctp_wfree(struct sk_buff *skb);

static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
				size_t msg_len);

static int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p);

static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p);

static int sctp_wait_for_accept(struct sock *sk, long timeo);

static void sctp_wait_for_close(struct sock *sk, long timeo);

static void sctp_destruct_sock(struct sock *sk);

static struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt,
					union sctp_addr *addr, int len);

static int sctp_bindx_add(struct sock *, struct sockaddr *, int);

static int sctp_bindx_rem(struct sock *, struct sockaddr *, int);

static int sctp_send_asconf_add_ip(struct sock *, struct sockaddr *, int);

static int sctp_send_asconf_del_ip(struct sock *, struct sockaddr *, int);

static int sctp_send_asconf(struct sctp_association *asoc,
			    struct sctp_chunk *chunk);

static int sctp_do_bind(struct sock *, union sctp_addr *, int);

static int sctp_autobind(struct sock *sk);

static int sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
			     struct sctp_association *assoc,
			     enum sctp_socket_type type);

struct percpu_counter sctp_sockets_allocated;

#define traverse_and_process()	\
do {				\
	msg = chunk->msg;	\
	if (msg == prev_msg)	\
		continue;	\
	list_for_each_entry(c, &msg->chunks, frag_list) {	\
		if ((clear && asoc->base.sk == c->skb->sk) ||	\
		    (!clear && asoc->base.sk != c->skb->sk))	\
			cb(c);	\
	}			\
	prev_msg = msg;		\
} while (0)

static int sctp_get_port_local(struct sock *, union sctp_addr *);

struct compat_sctp_getaddrs_old {
	sctp_assoc_t	assoc_id;
	s32		addr_num;
	compat_uptr_t	addrs;		/* struct sockaddr * */
}

static int sctp_msghdr_parse(const struct msghdr *msg,
			     struct sctp_cmsgs *cmsgs);

static struct sctp_bind_bucket *sctp_bucket_create(
	struct sctp_bind_hashbucket *head, struct net *, unsigned short snum);


static void sctp_clear_owner_w(struct sctp_chunk *chunk)
{
	skb_orphan(chunk->skb);
}


static int sctp_sendmsg_parse(struct sock *sk, struct sctp_cmsgs *cmsgs,
			      struct sctp_sndrcvinfo *srinfo,
			      const struct msghdr *msg, size_t msg_len)
{
	__u16 sflags;
	int err;

	if (sctp_sstate(sk, LISTENING) && sctp_style(sk, TCP))
		return -EPIPE;

	if (msg_len > sk->sk_sndbuf)
		return -EMSGSIZE;

	memset(cmsgs, 0, sizeof(*cmsgs));
	err = sctp_msghdr_parse(msg, cmsgs);
	if (err) {
		pr_debug("%s: msghdr parse err:%x\n", __func__, err);
		return err;
	}

	memset(srinfo, 0, sizeof(*srinfo));
	if (cmsgs->srinfo) {
		srinfo->sinfo_stream = cmsgs->srinfo->sinfo_stream;
		srinfo->sinfo_flags = cmsgs->srinfo->sinfo_flags;
		srinfo->sinfo_ppid = cmsgs->srinfo->sinfo_ppid;
		srinfo->sinfo_context = cmsgs->srinfo->sinfo_context;
		srinfo->sinfo_assoc_id = cmsgs->srinfo->sinfo_assoc_id;
		srinfo->sinfo_timetolive = cmsgs->srinfo->sinfo_timetolive;
	}

	if (cmsgs->sinfo) {
		srinfo->sinfo_stream = cmsgs->sinfo->snd_sid;
		srinfo->sinfo_flags = cmsgs->sinfo->snd_flags;
		srinfo->sinfo_ppid = cmsgs->sinfo->snd_ppid;
		srinfo->sinfo_context = cmsgs->sinfo->snd_context;
		srinfo->sinfo_assoc_id = cmsgs->sinfo->snd_assoc_id;
	}

	if (cmsgs->prinfo) {
		srinfo->sinfo_timetolive = cmsgs->prinfo->pr_value;
		SCTP_PR_SET_POLICY(srinfo->sinfo_flags,
				   cmsgs->prinfo->pr_policy);
	}

	sflags = srinfo->sinfo_flags;
	if (!sflags && msg_len)
		return 0;

	if (sctp_style(sk, TCP) && (sflags & (SCTP_EOF | SCTP_ABORT)))
		return -EINVAL;

	if (((sflags & SCTP_EOF) && msg_len > 0) ||
	    (!(sflags & (SCTP_EOF | SCTP_ABORT)) && msg_len == 0))
		return -EINVAL;

	if ((sflags & SCTP_ADDR_OVER) && !msg->msg_name)
		return -EINVAL;

	return 0;
}


static int sctp_getsockopt_recvrcvinfo(struct sock *sk,	int len,
				       char __user *optval,
				       int __user *optlen)
{
	int val = 0;

	if (len < sizeof(int))
		return -EINVAL;

	len = sizeof(int);
	if (sctp_sk(sk)->recvrcvinfo)
		val = 1;
	if (put_user(len, optlen))
		return -EFAULT;
	if (copy_to_user(optval, &val, len))
		return -EFAULT;

	return 0;
}


static void sctp_for_each_tx_datachunk(struct sctp_association *asoc,
				       bool clear,
				       void (*cb)(struct sctp_chunk *))

{
	struct sctp_datamsg *msg, *prev_msg = NULL;
	struct sctp_outq *q = &asoc->outqueue;
	struct sctp_chunk *chunk, *c;
	struct sctp_transport *t;

	list_for_each_entry(t, &asoc->peer.transport_addr_list, transports)
		list_for_each_entry(chunk, &t->transmitted, transmitted_list)
			traverse_and_process();

	list_for_each_entry(chunk, &q->retransmit, transmitted_list)
		traverse_and_process();

	list_for_each_entry(chunk, &q->sacked, transmitted_list)
		traverse_and_process();

	list_for_each_entry(chunk, &q->abandoned, transmitted_list)
		traverse_and_process();

	list_for_each_entry(chunk, &q->out_chunk_list, list)
		traverse_and_process();
}


int sctp_get_sctp_info(struct sock *sk, struct sctp_association *asoc,
		       struct sctp_info *info)
{
	struct sctp_transport *prim;
	struct list_head *pos;
	int mask;

	memset(info, 0, sizeof(*info));
	if (!asoc) {
		struct sctp_sock *sp = sctp_sk(sk);

		info->sctpi_s_autoclose = sp->autoclose;
		info->sctpi_s_adaptation_ind = sp->adaptation_ind;
		info->sctpi_s_pd_point = sp->pd_point;
		info->sctpi_s_nodelay = sp->nodelay;
		info->sctpi_s_disable_fragments = sp->disable_fragments;
		info->sctpi_s_v4mapped = sp->v4mapped;
		info->sctpi_s_frag_interleave = sp->frag_interleave;
		info->sctpi_s_type = sp->type;

		return 0;
	}

	info->sctpi_tag = asoc->c.my_vtag;
	info->sctpi_state = asoc->state;
	info->sctpi_rwnd = asoc->a_rwnd;
	info->sctpi_unackdata = asoc->unack_data;
	info->sctpi_penddata = sctp_tsnmap_pending(&asoc->peer.tsn_map);
	info->sctpi_instrms = asoc->stream.incnt;
	info->sctpi_outstrms = asoc->stream.outcnt;
	list_for_each(pos, &asoc->base.inqueue.in_chunk_list)
		info->sctpi_inqueue++;
	list_for_each(pos, &asoc->outqueue.out_chunk_list)
		info->sctpi_outqueue++;
	info->sctpi_overall_error = asoc->overall_error_count;
	info->sctpi_max_burst = asoc->max_burst;
	info->sctpi_maxseg = asoc->frag_point;
	info->sctpi_peer_rwnd = asoc->peer.rwnd;
	info->sctpi_peer_tag = asoc->c.peer_vtag;

	mask = asoc->peer.ecn_capable << 1;
	mask = (mask | asoc->peer.ipv4_address) << 1;
	mask = (mask | asoc->peer.ipv6_address) << 1;
	mask = (mask | asoc->peer.hostname_address) << 1;
	mask = (mask | asoc->peer.asconf_capable) << 1;
	mask = (mask | asoc->peer.prsctp_capable) << 1;
	mask = (mask | asoc->peer.auth_capable);
	info->sctpi_peer_capable = mask;
	mask = asoc->peer.sack_needed << 1;
	mask = (mask | asoc->peer.sack_generation) << 1;
	mask = (mask | asoc->peer.zero_window_announced);
	info->sctpi_peer_sack = mask;

	info->sctpi_isacks = asoc->stats.isacks;
	info->sctpi_osacks = asoc->stats.osacks;
	info->sctpi_opackets = asoc->stats.opackets;
	info->sctpi_ipackets = asoc->stats.ipackets;
	info->sctpi_rtxchunks = asoc->stats.rtxchunks;
	info->sctpi_outofseqtsns = asoc->stats.outofseqtsns;
	info->sctpi_idupchunks = asoc->stats.idupchunks;
	info->sctpi_gapcnt = asoc->stats.gapcnt;
	info->sctpi_ouodchunks = asoc->stats.ouodchunks;
	info->sctpi_iuodchunks = asoc->stats.iuodchunks;
	info->sctpi_oodchunks = asoc->stats.oodchunks;
	info->sctpi_iodchunks = asoc->stats.iodchunks;
	info->sctpi_octrlchunks = asoc->stats.octrlchunks;
	info->sctpi_ictrlchunks = asoc->stats.ictrlchunks;

	prim = asoc->peer.primary_path;
	memcpy(&info->sctpi_p_address, &prim->ipaddr, sizeof(prim->ipaddr));
	info->sctpi_p_state = prim->state;
	info->sctpi_p_cwnd = prim->cwnd;
	info->sctpi_p_srtt = prim->srtt;
	info->sctpi_p_rto = jiffies_to_msecs(prim->rto);
	info->sctpi_p_hbinterval = prim->hbinterval;
	info->sctpi_p_pathmaxrxt = prim->pathmaxrxt;
	info->sctpi_p_sackdelay = jiffies_to_msecs(prim->sackdelay);
	info->sctpi_p_ssthresh = prim->ssthresh;
	info->sctpi_p_partial_bytes_acked = prim->partial_bytes_acked;
	info->sctpi_p_flight_size = prim->flight_size;
	info->sctpi_p_error = prim->error_count;

	return 0;
}


static int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p)
{
	struct sock *sk = asoc->base.sk;
	int err = 0;
	long current_timeo = *timeo_p;
	DEFINE_WAIT(wait);

	pr_debug("%s: asoc:%p, timeo:%ld\n", __func__, asoc, *timeo_p);

	/* Increment the association's refcnt.  */
	sctp_association_hold(asoc);

	for (;;) {
		prepare_to_wait_exclusive(&asoc->wait, &wait,
					  TASK_INTERRUPTIBLE);
		if (!*timeo_p)
			goto do_nonblock;
		if (sk->sk_shutdown & RCV_SHUTDOWN)
			break;
		if (sk->sk_err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING ||
		    asoc->base.dead)
			goto do_error;
		if (signal_pending(current))
			goto do_interrupted;

		if (sctp_state(asoc, ESTABLISHED))
			break;

		/* Let another process have a go.  Since we are going
		 * to sleep anyway.
		 */
		release_sock(sk);
		current_timeo = schedule_timeout(current_timeo);
		lock_sock(sk);

		*timeo_p = current_timeo;
	}

out:
	finish_wait(&asoc->wait, &wait);

	/* Release the association's refcnt.  */
	sctp_association_put(asoc);

	return err;

do_error:
	if (asoc->init_err_counter + 1 > asoc->max_init_attempts)
		err = -ETIMEDOUT;
	else
		err = -ECONNREFUSED;
	goto out;

do_interrupted:
	err = sock_intr_errno(*timeo_p);
	goto out;

do_nonblock:
	err = -EINPROGRESS;
	goto out;
}


static void sctp_for_each_rx_skb(struct sctp_association *asoc, struct sock *sk,
				 void (*cb)(struct sk_buff *, struct sock *))

{
	struct sk_buff *skb, *tmp;

	sctp_skb_for_each(skb, &asoc->ulpq.lobby, tmp)
		cb(skb, sk);

	sctp_skb_for_each(skb, &asoc->ulpq.reasm, tmp)
		cb(skb, sk);

	sctp_skb_for_each(skb, &asoc->ulpq.reasm_uo, tmp)
		cb(skb, sk);
}


static int sctp_setsockopt_reconfig_supported(struct sock *sk,
					      struct sctp_assoc_value *params,
					      unsigned int optlen)
{
	struct sctp_association *asoc;
	int retval = -EINVAL;

	if (optlen != sizeof(*params))
		goto out;

	asoc = sctp_id2assoc(sk, params->assoc_id);
	if (!asoc && params->assoc_id != SCTP_FUTURE_ASSOC &&
	    sctp_style(sk, UDP))
		goto out;

	sctp_sk(sk)->ep->reconf_enable = !!params->assoc_value;

	retval = 0;

out:
	return retval;
}


static void sctp_wfree(struct sk_buff *skb)
{
	struct sctp_chunk *chunk = skb_shinfo(skb)->destructor_arg;
	struct sctp_association *asoc = chunk->asoc;
	struct sock *sk = asoc->base.sk;

	sk_mem_uncharge(sk, skb->truesize);
	sk->sk_wmem_queued -= skb->truesize + sizeof(struct sctp_chunk);
	asoc->sndbuf_used -= skb->truesize + sizeof(struct sctp_chunk);
	WARN_ON(refcount_sub_and_test(sizeof(struct sctp_chunk),
				      &sk->sk_wmem_alloc));

	if (chunk->shkey) {
		struct sctp_shared_key *shkey = chunk->shkey;

		/* refcnt == 2 and !list_empty mean after this release, it's
		 * not being used anywhere, and it's time to notify userland
		 * that this shkey can be freed if it's been deactivated.
		 */
		if (shkey->deactivated && !list_empty(&shkey->key_list) &&
		    refcount_read(&shkey->refcnt) == 2) {
			struct sctp_ulpevent *ev;

			ev = sctp_ulpevent_make_authkey(asoc, shkey->key_id,
							SCTP_AUTH_FREE_KEY,
							GFP_KERNEL);
			if (ev)
				asoc->stream.si->enqueue_event(&asoc->ulpq, ev);
		}
		sctp_auth_shkey_release(chunk->shkey);
	}

	sock_wfree(skb);
	sctp_wake_up_waiters(sk, asoc);

	sctp_association_put(asoc);
}


static int sctp_getsockopt_connectx3(struct sock *sk, int len,
				     char __user *optval,
				     int __user *optlen)
{
	struct sctp_getaddrs_old param;
	sctp_assoc_t assoc_id = 0;
	struct sockaddr *kaddrs;
	int err = 0;

#ifdef CONFIG_COMPAT
	if (in_compat_syscall()) {
		struct compat_sctp_getaddrs_old param32;

		if (len < sizeof(param32))
			return -EINVAL;
		if (copy_from_user(&param32, optval, sizeof(param32)))
			return -EFAULT;

		param.assoc_id = param32.assoc_id;
		param.addr_num = param32.addr_num;
		param.addrs = compat_ptr(param32.addrs);
	} else
#endif
	{
		if (len < sizeof(param))
			return -EINVAL;
		if (copy_from_user(&param, optval, sizeof(param)))
			return -EFAULT;
	}

	kaddrs = memdup_user(param.addrs, param.addr_num);
	if (IS_ERR(kaddrs))
		return PTR_ERR(kaddrs);

	err = __sctp_setsockopt_connectx(sk, kaddrs, param.addr_num, &assoc_id);
	kfree(kaddrs);
	if (err == 0 || err == -EINPROGRESS) {
		if (copy_to_user(optval, &assoc_id, sizeof(assoc_id)))
			return -EFAULT;
		if (put_user(sizeof(assoc_id), optlen))
			return -EFAULT;
	}

	return err;
}

static bool sctp_writeable(struct sock *sk);

static void sctp_wfree(struct sk_buff *skb);

static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
				size_t msg_len);

static int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p);

static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p);

static int sctp_wait_for_accept(struct sock *sk, long timeo);

static void sctp_wait_for_close(struct sock *sk, long timeo);

static void sctp_destruct_sock(struct sock *sk);

static struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt,
					union sctp_addr *addr, int len);

static int sctp_bindx_add(struct sock *, struct sockaddr *, int);

static int sctp_bindx_rem(struct sock *, struct sockaddr *, int);

static int sctp_send_asconf_add_ip(struct sock *, struct sockaddr *, int);

static int sctp_send_asconf_del_ip(struct sock *, struct sockaddr *, int);

static int sctp_send_asconf(struct sctp_association *asoc,
			    struct sctp_chunk *chunk);

static int sctp_do_bind(struct sock *, union sctp_addr *, int);

static int sctp_autobind(struct sock *sk);

static int sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
			     struct sctp_association *assoc,
			     enum sctp_socket_type type);

static unsigned long sctp_memory_pressure;

static atomic_long_t sctp_memory_allocated;

struct percpu_counter sctp_sockets_allocated;

static int sctp_get_port_local(struct sock *, union sctp_addr *);

struct compat_sctp_getaddrs_old {
	sctp_assoc_t	assoc_id;
	s32		addr_num;
	compat_uptr_t	addrs;		/* struct sockaddr * */
}

static int sctp_msghdr_parse(const struct msghdr *msg,
			     struct sctp_cmsgs *cmsgs);

static struct sctp_bind_bucket *sctp_bucket_create(
	struct sctp_bind_hashbucket *head, struct net *, unsigned short snum);

struct proto sctp_prot = {
	.name        =	"SCTP",
	.owner       =	THIS_MODULE,
	.close       =	sctp_close,
	.disconnect  =	sctp_disconnect,
	.accept      =	sctp_accept,
	.ioctl       =	sctp_ioctl,
	.init        =	sctp_init_sock,
	.destroy     =	sctp_destroy_sock,
	.shutdown    =	sctp_shutdown,
	.setsockopt  =	sctp_setsockopt,
	.getsockopt  =	sctp_getsockopt,
	.sendmsg     =	sctp_sendmsg,
	.recvmsg     =	sctp_recvmsg,
	.bind        =	sctp_bind,
	.bind_add    =  sctp_bind_add,
	.backlog_rcv =	sctp_backlog_rcv,
	.hash        =	sctp_hash,
	.unhash      =	sctp_unhash,
	.no_autobind =	true,
	.obj_size    =  sizeof(struct sctp_sock),
	.useroffset  =  offsetof(struct sctp_sock, subscribe),
	.usersize    =  offsetof(struct sctp_sock, initmsg) -
				offsetof(struct sctp_sock, subscribe) +
				sizeof_field(struct sctp_sock, initmsg),
	.sysctl_mem  =  sysctl_sctp_mem,
	.sysctl_rmem =  sysctl_sctp_rmem,
	.sysctl_wmem =  sysctl_sctp_wmem,
	.memory_pressure = &sctp_memory_pressure,
	.enter_memory_pressure = sctp_enter_memory_pressure,
	.memory_allocated = &sctp_memory_allocated,
	.sockets_allocated = &sctp_sockets_allocated,
};


static int sctp_setsockopt_scheduler_value(struct sock *sk,
					   struct sctp_stream_value *params,
					   unsigned int optlen)
{
	struct sctp_association *asoc;
	int retval = -EINVAL;

	if (optlen < sizeof(*params))
		goto out;

	asoc = sctp_id2assoc(sk, params->assoc_id);
	if (!asoc && params->assoc_id != SCTP_CURRENT_ASSOC &&
	    sctp_style(sk, UDP))
		goto out;

	if (asoc) {
		retval = sctp_sched_set_value(asoc, params->stream_id,
					      params->stream_value, GFP_KERNEL);
		goto out;
	}

	retval = 0;

	list_for_each_entry(asoc, &sctp_sk(sk)->ep->asocs, asocs) {
		int ret = sctp_sched_set_value(asoc, params->stream_id,
					       params->stream_value,
					       GFP_KERNEL);
		if (ret && !retval) /* try to return the 1st error. */
			retval = ret;
	}

out:
	return retval;
}


static int sctp_setsockopt_reset_streams(struct sock *sk,
					 struct sctp_reset_streams *params,
					 unsigned int optlen)
{
	struct sctp_association *asoc;

	if (optlen < sizeof(*params))
		return -EINVAL;
	/* srs_number_streams is u16, so optlen can't be bigger than this. */
	optlen = min_t(unsigned int, optlen, USHRT_MAX +
					     sizeof(__u16) * sizeof(*params));

	if (params->srs_number_streams * sizeof(__u16) >
	    optlen - sizeof(*params))
		return -EINVAL;

	asoc = sctp_id2assoc(sk, params->srs_assoc_id);
	if (!asoc)
		return -EINVAL;

	return sctp_send_reset_streams(asoc, params);
}


int sctp_transport_lookup_process(int (*cb)(struct sctp_transport *, void *),
				  struct net *net,
				  const union sctp_addr *laddr,
				  const union sctp_addr *paddr, void *p)
{
	struct sctp_transport *transport;
	int err;

	rcu_read_lock();
	transport = sctp_addrs_lookup_transport(net, laddr, paddr);
	rcu_read_unlock();
	if (!transport)
		return -ENOENT;

	err = cb(transport, p);
	sctp_transport_put(transport);

	return err;
}


static int sctp_getsockopt_default_sndinfo(struct sock *sk, int len,
					   char __user *optval,
					   int __user *optlen)
{
	struct sctp_sock *sp = sctp_sk(sk);
	struct sctp_association *asoc;
	struct sctp_sndinfo info;

	if (len < sizeof(info))
		return -EINVAL;

	len = sizeof(info);

	if (copy_from_user(&info, optval, len))
		return -EFAULT;

	asoc = sctp_id2assoc(sk, info.snd_assoc_id);
	if (!asoc && info.snd_assoc_id != SCTP_FUTURE_ASSOC &&
	    sctp_style(sk, UDP))
		return -EINVAL;

	if (asoc) {
		info.snd_sid = asoc->default_stream;
		info.snd_flags = asoc->default_flags;
		info.snd_ppid = asoc->default_ppid;
		info.snd_context = asoc->default_context;
	} else {
		info.snd_sid = sp->default_stream;
		info.snd_flags = sp->default_flags;
		info.snd_ppid = sp->default_ppid;
		info.snd_context = sp->default_context;
	}

	if (put_user(len, optlen))
		return -EFAULT;
	if (copy_to_user(optval, &info, len))
		return -EFAULT;

	return 0;
}


static int sctp_bindx_add(struct sock *sk, struct sockaddr *addrs, int addrcnt)
{
	int cnt;
	int retval = 0;
	void *addr_buf;
	struct sockaddr *sa_addr;
	struct sctp_af *af;

	pr_debug("%s: sk:%p, addrs:%p, addrcnt:%d\n", __func__, sk,
		 addrs, addrcnt);

	addr_buf = addrs;
	for (cnt = 0; cnt < addrcnt; cnt++) {
		/* The list may contain either IPv4 or IPv6 address;
		 * determine the address length for walking thru the list.
		 */
		sa_addr = addr_buf;
		af = sctp_get_af_specific(sa_addr->sa_family);
		if (!af) {
			retval = -EINVAL;
			goto err_bindx_add;
		}

		retval = sctp_do_bind(sk, (union sctp_addr *)sa_addr,
				      af->sockaddr_len);

		addr_buf += af->sockaddr_len;

err_bindx_add:
		if (retval < 0) {
			/* Failed. Cleanup the ones that have been added */
			if (cnt > 0)
				sctp_bindx_rem(sk, addrs, cnt);
			return retval;
		}
	}

	return retval;
}


static void mcba_usb_xmit_read_fw_ver(struct mcba_priv *priv, u8 pic)
{
	struct mcba_usb_msg_fw_ver usb_msg = {
		.cmd_id = MBCA_CMD_READ_FW_VERSION,
		.pic = pic
	};

	mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg);
}

can: mcba_usb: fix use-after-free on disconnect

The driver was accessing its driver data after having freed it.

Fixes: 51f3baad7de9 ("can: mcba_usb: Add support for Microchip CAN BUS Analyzer")
Cc: stable <stable@vger.kernel.org>     # 4.12
Cc: Remigiusz Kołłątaj <remigiusz.kollataj@mobica.com>
Reported-by: syzbot+e29b17e5042bbc56fae9@syzkaller.appspotmail.com
Signed-off-by: Johan Hovold <johan@kernel.org>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>

#define MCBA_USB_TX_BUFF_SIZE (sizeof(struct mcba_usb_msg))

#define MBCA_CMD_READ_FW_VERSION 0xA9

struct mcba_usb_ctx {
	struct mcba_priv *priv;
	u32 ndx;
	bool can;
}

struct mcba_priv {
	struct can_priv can; /* must be the first member */
	struct sk_buff *echo_skb[MCBA_MAX_TX_URBS];
	struct mcba_usb_ctx tx_context[MCBA_MAX_TX_URBS];
	struct usb_device *udev;
	struct net_device *netdev;
	struct usb_anchor tx_submitted;
	struct usb_anchor rx_submitted;
	struct can_berr_counter bec;
	bool usb_ka_first_pass;
	bool can_ka_first_pass;
	bool can_speed_check;
	atomic_t free_ctx_cnt;
	void *rxbuf[MCBA_MAX_RX_URBS];
	dma_addr_t rxbuf_dma[MCBA_MAX_RX_URBS];
}

static const struct usb_device_id mcba_usb_table[] = {
	{ USB_DEVICE(MCBA_VENDOR_ID, MCBA_PRODUCT_ID) },
	{} /* Terminating entry */
};

static const u16 mcba_termination[] = { MCBA_TERMINATION_DISABLED,
					MCBA_TERMINATION_ENABLED };

static const u32 mcba_bitrate[] = { 20000,  33333,  50000,  80000,  83333,
				    100000, 125000, 150000, 175000, 200000,
				    225000, 250000, 275000, 300000, 500000,
				    625000, 800000, 1000000 };

static const struct net_device_ops mcba_netdev_ops = {
	.ndo_open = mcba_usb_open,
	.ndo_stop = mcba_usb_close,
	.ndo_start_xmit = mcba_usb_start_xmit,
};

static struct usb_driver mcba_usb_driver = {
	.name = MCBA_MODULE_NAME,
	.probe = mcba_usb_probe,
	.disconnect = mcba_usb_disconnect,
	.id_table = mcba_usb_table,
};


static void mcba_urb_unlink(struct mcba_priv *priv)
{
	usb_kill_anchored_urbs(&priv->rx_submitted);
	usb_kill_anchored_urbs(&priv->tx_submitted);
}


static netdev_tx_t mcba_usb_xmit(struct mcba_priv *priv,
				 struct mcba_usb_msg *usb_msg,
				 struct mcba_usb_ctx *ctx)
{
	struct urb *urb;
	u8 *buf;
	int err;

	/* create a URB, and a buffer for it, and copy the data to the URB */
	urb = usb_alloc_urb(0, GFP_ATOMIC);
	if (!urb)
		return -ENOMEM;

	buf = usb_alloc_coherent(priv->udev, MCBA_USB_TX_BUFF_SIZE, GFP_ATOMIC,
				 &urb->transfer_dma);
	if (!buf) {
		err = -ENOMEM;
		goto nomembuf;
	}

	memcpy(buf, usb_msg, MCBA_USB_TX_BUFF_SIZE);

	usb_fill_bulk_urb(urb, priv->udev,
			  usb_sndbulkpipe(priv->udev, MCBA_USB_EP_OUT), buf,
			  MCBA_USB_TX_BUFF_SIZE, mcba_usb_write_bulk_callback,
			  ctx);

	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
	usb_anchor_urb(urb, &priv->tx_submitted);

	err = usb_submit_urb(urb, GFP_ATOMIC);
	if (unlikely(err))
		goto failed;

	/* Release our reference to this URB, the USB core will eventually free
	 * it entirely.
	 */
	usb_free_urb(urb);

	return 0;

failed:
	usb_unanchor_urb(urb);
	usb_free_coherent(priv->udev, MCBA_USB_TX_BUFF_SIZE, buf,
			  urb->transfer_dma);

	if (err == -ENODEV)
		netif_device_detach(priv->netdev);
	else
		netdev_warn(priv->netdev, "failed tx_urb %d\n", err);

nomembuf:
	usb_free_urb(urb);

	return err;
}

#define MCBA_USB_TX_BUFF_SIZE (sizeof(struct mcba_usb_msg))

#define MCBA_USB_EP_OUT 1

struct mcba_usb_ctx {
	struct mcba_priv *priv;
	u32 ndx;
	bool can;
}

struct mcba_priv {
	struct can_priv can; /* must be the first member */
	struct sk_buff *echo_skb[MCBA_MAX_TX_URBS];
	struct mcba_usb_ctx tx_context[MCBA_MAX_TX_URBS];
	struct usb_device *udev;
	struct net_device *netdev;
	struct usb_anchor tx_submitted;
	struct usb_anchor rx_submitted;
	struct can_berr_counter bec;
	bool usb_ka_first_pass;
	bool can_ka_first_pass;
	bool can_speed_check;
	atomic_t free_ctx_cnt;
	void *rxbuf[MCBA_MAX_RX_URBS];
	dma_addr_t rxbuf_dma[MCBA_MAX_RX_URBS];
}

static const struct usb_device_id mcba_usb_table[] = {
	{ USB_DEVICE(MCBA_VENDOR_ID, MCBA_PRODUCT_ID) },
	{} /* Terminating entry */
};

static const u16 mcba_termination[] = { MCBA_TERMINATION_DISABLED,
					MCBA_TERMINATION_ENABLED };

static const u32 mcba_bitrate[] = { 20000,  33333,  50000,  80000,  83333,
				    100000, 125000, 150000, 175000, 200000,
				    225000, 250000, 275000, 300000, 500000,
				    625000, 800000, 1000000 };

static const struct net_device_ops mcba_netdev_ops = {
	.ndo_open = mcba_usb_open,
	.ndo_stop = mcba_usb_close,
	.ndo_start_xmit = mcba_usb_start_xmit,
};

static struct usb_driver mcba_usb_driver = {
	.name = MCBA_MODULE_NAME,
	.probe = mcba_usb_probe,
	.disconnect = mcba_usb_disconnect,
	.id_table = mcba_usb_table,
};


static int mcba_usb_open(struct net_device *netdev)
{
	struct mcba_priv *priv = netdev_priv(netdev);
	int err;

	/* common open */
	err = open_candev(netdev);
	if (err)
		return err;

	priv->can_speed_check = true;
	priv->can.state = CAN_STATE_ERROR_ACTIVE;

	can_led_event(netdev, CAN_LED_EVENT_OPEN);
	netif_start_queue(netdev);

	return 0;
}

#define MCBA_USB_TX_BUFF_SIZE (sizeof(struct mcba_usb_msg))

struct mcba_usb_ctx {
	struct mcba_priv *priv;
	u32 ndx;
	bool can;
}

struct mcba_priv {
	struct can_priv can; /* must be the first member */
	struct sk_buff *echo_skb[MCBA_MAX_TX_URBS];
	struct mcba_usb_ctx tx_context[MCBA_MAX_TX_URBS];
	struct usb_device *udev;
	struct net_device *netdev;
	struct usb_anchor tx_submitted;
	struct usb_anchor rx_submitted;
	struct can_berr_counter bec;
	bool usb_ka_first_pass;
	bool can_ka_first_pass;
	bool can_speed_check;
	atomic_t free_ctx_cnt;
	void *rxbuf[MCBA_MAX_RX_URBS];
	dma_addr_t rxbuf_dma[MCBA_MAX_RX_URBS];
}

static const struct usb_device_id mcba_usb_table[] = {
	{ USB_DEVICE(MCBA_VENDOR_ID, MCBA_PRODUCT_ID) },
	{} /* Terminating entry */
};

static const u16 mcba_termination[] = { MCBA_TERMINATION_DISABLED,
					MCBA_TERMINATION_ENABLED };

static const u32 mcba_bitrate[] = { 20000,  33333,  50000,  80000,  83333,
				    100000, 125000, 150000, 175000, 200000,
				    225000, 250000, 275000, 300000, 500000,
				    625000, 800000, 1000000 };

static const struct net_device_ops mcba_netdev_ops = {
	.ndo_open = mcba_usb_open,
	.ndo_stop = mcba_usb_close,
	.ndo_start_xmit = mcba_usb_start_xmit,
};

static struct usb_driver mcba_usb_driver = {
	.name = MCBA_MODULE_NAME,
	.probe = mcba_usb_probe,
	.disconnect = mcba_usb_disconnect,
	.id_table = mcba_usb_table,
};


static netdev_tx_t mcba_usb_start_xmit(struct sk_buff *skb,
				       struct net_device *netdev)
{
	struct mcba_priv *priv = netdev_priv(netdev);
	struct can_frame *cf = (struct can_frame *)skb->data;
	struct mcba_usb_ctx *ctx = NULL;
	struct net_device_stats *stats = &priv->netdev->stats;
	u16 sid;
	int err;
	struct mcba_usb_msg_can usb_msg = {
		.cmd_id = MBCA_CMD_TRANSMIT_MESSAGE_EV
	};

	if (can_dropped_invalid_skb(netdev, skb))
		return NETDEV_TX_OK;

	ctx = mcba_usb_get_free_ctx(priv, cf);
	if (!ctx)
		return NETDEV_TX_BUSY;

	can_put_echo_skb(skb, priv->netdev, ctx->ndx);

	if (cf->can_id & CAN_EFF_FLAG) {
		/* SIDH    | SIDL                 | EIDH   | EIDL
		 * 28 - 21 | 20 19 18 x x x 17 16 | 15 - 8 | 7 - 0
		 */
		sid = MCBA_SIDL_EXID_MASK;
		/* store 28-18 bits */
		sid |= (cf->can_id & 0x1ffc0000) >> 13;
		/* store 17-16 bits */
		sid |= (cf->can_id & 0x30000) >> 16;
		put_unaligned_be16(sid, &usb_msg.sid);

		/* store 15-0 bits */
		put_unaligned_be16(cf->can_id & 0xffff, &usb_msg.eid);
	} else {
		/* SIDH   | SIDL
		 * 10 - 3 | 2 1 0 x x x x x
		 */
		put_unaligned_be16((cf->can_id & CAN_SFF_MASK) << 5,
				   &usb_msg.sid);
		usb_msg.eid = 0;
	}

	usb_msg.dlc = cf->can_dlc;

	memcpy(usb_msg.data, cf->data, usb_msg.dlc);

	if (cf->can_id & CAN_RTR_FLAG)
		usb_msg.dlc |= MCBA_DLC_RTR_MASK;

	err = mcba_usb_xmit(priv, (struct mcba_usb_msg *)&usb_msg, ctx);
	if (err)
		goto xmit_failed;

	return NETDEV_TX_OK;

xmit_failed:
	can_free_echo_skb(priv->netdev, ctx->ndx);
	mcba_usb_free_ctx(ctx);
	dev_kfree_skb(skb);
	stats->tx_dropped++;

	return NETDEV_TX_OK;
}


static int mcba_set_termination(struct net_device *netdev, u16 term)
{
	struct mcba_priv *priv = netdev_priv(netdev);
	struct mcba_usb_msg_termination usb_msg = {
		.cmd_id = MBCA_CMD_SETUP_TERMINATION_RESISTANCE
	};

	if (term == MCBA_TERMINATION_ENABLED)
		usb_msg.termination = 1;
	else
		usb_msg.termination = 0;

	mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg);

	return 0;
}

#define MCBA_USB_TX_BUFF_SIZE (sizeof(struct mcba_usb_msg))

#define MBCA_CMD_SETUP_TERMINATION_RESISTANCE 0xA8

#define MCBA_TERMINATION_ENABLED 120

struct mcba_usb_ctx {
	struct mcba_priv *priv;
	u32 ndx;
	bool can;
}

struct mcba_priv {
	struct can_priv can; /* must be the first member */
	struct sk_buff *echo_skb[MCBA_MAX_TX_URBS];
	struct mcba_usb_ctx tx_context[MCBA_MAX_TX_URBS];
	struct usb_device *udev;
	struct net_device *netdev;
	struct usb_anchor tx_submitted;
	struct usb_anchor rx_submitted;
	struct can_berr_counter bec;
	bool usb_ka_first_pass;
	bool can_ka_first_pass;
	bool can_speed_check;
	atomic_t free_ctx_cnt;
	void *rxbuf[MCBA_MAX_RX_URBS];
	dma_addr_t rxbuf_dma[MCBA_MAX_RX_URBS];
}

static const struct usb_device_id mcba_usb_table[] = {
	{ USB_DEVICE(MCBA_VENDOR_ID, MCBA_PRODUCT_ID) },
	{} /* Terminating entry */
};

static const u16 mcba_termination[] = { MCBA_TERMINATION_DISABLED,
					MCBA_TERMINATION_ENABLED };

static const u32 mcba_bitrate[] = { 20000,  33333,  50000,  80000,  83333,
				    100000, 125000, 150000, 175000, 200000,
				    225000, 250000, 275000, 300000, 500000,
				    625000, 800000, 1000000 };

static const struct net_device_ops mcba_netdev_ops = {
	.ndo_open = mcba_usb_open,
	.ndo_stop = mcba_usb_close,
	.ndo_start_xmit = mcba_usb_start_xmit,
};

static struct usb_driver mcba_usb_driver = {
	.name = MCBA_MODULE_NAME,
	.probe = mcba_usb_probe,
	.disconnect = mcba_usb_disconnect,
	.id_table = mcba_usb_table,
};


static inline void mcba_init_ctx(struct mcba_priv *priv)
{
	int i = 0;

	for (i = 0; i < MCBA_MAX_TX_URBS; i++) {
		priv->tx_context[i].ndx = MCBA_CTX_FREE;
		priv->tx_context[i].priv = priv;
	}

	atomic_set(&priv->free_ctx_cnt, ARRAY_SIZE(priv->tx_context));
}

#define MCBA_MAX_TX_URBS 20

#define MCBA_CTX_FREE MCBA_MAX_TX_URBS

#define MCBA_USB_TX_BUFF_SIZE (sizeof(struct mcba_usb_msg))

struct mcba_usb_ctx {
	struct mcba_priv *priv;
	u32 ndx;
	bool can;
}

struct mcba_priv {
	struct can_priv can; /* must be the first member */
	struct sk_buff *echo_skb[MCBA_MAX_TX_URBS];
	struct mcba_usb_ctx tx_context[MCBA_MAX_TX_URBS];
	struct usb_device *udev;
	struct net_device *netdev;
	struct usb_anchor tx_submitted;
	struct usb_anchor rx_submitted;
	struct can_berr_counter bec;
	bool usb_ka_first_pass;
	bool can_ka_first_pass;
	bool can_speed_check;
	atomic_t free_ctx_cnt;
	void *rxbuf[MCBA_MAX_RX_URBS];
	dma_addr_t rxbuf_dma[MCBA_MAX_RX_URBS];
}

static const struct usb_device_id mcba_usb_table[] = {
	{ USB_DEVICE(MCBA_VENDOR_ID, MCBA_PRODUCT_ID) },
	{} /* Terminating entry */
};

static const u16 mcba_termination[] = { MCBA_TERMINATION_DISABLED,
					MCBA_TERMINATION_ENABLED };

static const u32 mcba_bitrate[] = { 20000,  33333,  50000,  80000,  83333,
				    100000, 125000, 150000, 175000, 200000,
				    225000, 250000, 275000, 300000, 500000,
				    625000, 800000, 1000000 };

static const struct net_device_ops mcba_netdev_ops = {
	.ndo_open = mcba_usb_open,
	.ndo_stop = mcba_usb_close,
	.ndo_start_xmit = mcba_usb_start_xmit,
};

static struct usb_driver mcba_usb_driver = {
	.name = MCBA_MODULE_NAME,
	.probe = mcba_usb_probe,
	.disconnect = mcba_usb_disconnect,
	.id_table = mcba_usb_table,
};


static void mcba_usb_xmit_change_bitrate(struct mcba_priv *priv, u16 bitrate)
{
	struct mcba_usb_msg_change_bitrate usb_msg = {
		.cmd_id = MBCA_CMD_CHANGE_BIT_RATE
	};

	put_unaligned_be16(bitrate, &usb_msg.bitrate);

	mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg);
}

#define MCBA_USB_TX_BUFF_SIZE (sizeof(struct mcba_usb_msg))

#define MBCA_CMD_CHANGE_BIT_RATE 0xA1

struct mcba_usb_ctx {
	struct mcba_priv *priv;
	u32 ndx;
	bool can;
}

struct mcba_priv {
	struct can_priv can; /* must be the first member */
	struct sk_buff *echo_skb[MCBA_MAX_TX_URBS];
	struct mcba_usb_ctx tx_context[MCBA_MAX_TX_URBS];
	struct usb_device *udev;
	struct net_device *netdev;
	struct usb_anchor tx_submitted;
	struct usb_anchor rx_submitted;
	struct can_berr_counter bec;
	bool usb_ka_first_pass;
	bool can_ka_first_pass;
	bool can_speed_check;
	atomic_t free_ctx_cnt;
	void *rxbuf[MCBA_MAX_RX_URBS];
	dma_addr_t rxbuf_dma[MCBA_MAX_RX_URBS];
}

static const struct usb_device_id mcba_usb_table[] = {
	{ USB_DEVICE(MCBA_VENDOR_ID, MCBA_PRODUCT_ID) },
	{} /* Terminating entry */
};

static const u16 mcba_termination[] = { MCBA_TERMINATION_DISABLED,
					MCBA_TERMINATION_ENABLED };

static const u32 mcba_bitrate[] = { 20000,  33333,  50000,  80000,  83333,
				    100000, 125000, 150000, 175000, 200000,
				    225000, 250000, 275000, 300000, 500000,
				    625000, 800000, 1000000 };

static const struct net_device_ops mcba_netdev_ops = {
	.ndo_open = mcba_usb_open,
	.ndo_stop = mcba_usb_close,
	.ndo_start_xmit = mcba_usb_start_xmit,
};

static struct usb_driver mcba_usb_driver = {
	.name = MCBA_MODULE_NAME,
	.probe = mcba_usb_probe,
	.disconnect = mcba_usb_disconnect,
	.id_table = mcba_usb_table,
};


static int mcba_net_get_berr_counter(const struct net_device *netdev,
				     struct can_berr_counter *bec)
{
	struct mcba_priv *priv = netdev_priv(netdev);

	bec->txerr = priv->bec.txerr;
	bec->rxerr = priv->bec.rxerr;

	return 0;
}


static int mcba_usb_probe(struct usb_interface *intf,
			  const struct usb_device_id *id)
{
	struct net_device *netdev;
	struct mcba_priv *priv;
	int err = -ENOMEM;
	struct usb_device *usbdev = interface_to_usbdev(intf);

	netdev = alloc_candev(sizeof(struct mcba_priv), MCBA_MAX_TX_URBS);
	if (!netdev) {
		dev_err(&intf->dev, "Couldn't alloc candev\n");
		return -ENOMEM;
	}

	priv = netdev_priv(netdev);

	priv->udev = usbdev;
	priv->netdev = netdev;
	priv->usb_ka_first_pass = true;
	priv->can_ka_first_pass = true;
	priv->can_speed_check = false;

	init_usb_anchor(&priv->rx_submitted);
	init_usb_anchor(&priv->tx_submitted);

	usb_set_intfdata(intf, priv);

	/* Init CAN device */
	priv->can.state = CAN_STATE_STOPPED;
	priv->can.termination_const = mcba_termination;
	priv->can.termination_const_cnt = ARRAY_SIZE(mcba_termination);
	priv->can.bitrate_const = mcba_bitrate;
	priv->can.bitrate_const_cnt = ARRAY_SIZE(mcba_bitrate);

	priv->can.do_set_termination = mcba_set_termination;
	priv->can.do_set_mode = mcba_net_set_mode;
	priv->can.do_get_berr_counter = mcba_net_get_berr_counter;
	priv->can.do_set_bittiming = mcba_net_set_bittiming;

	netdev->netdev_ops = &mcba_netdev_ops;

	netdev->flags |= IFF_ECHO; /* we support local echo */

	SET_NETDEV_DEV(netdev, &intf->dev);

	err = register_candev(netdev);
	if (err) {
		netdev_err(netdev, "couldn't register CAN device: %d\n", err);

		goto cleanup_free_candev;
	}

	devm_can_led_init(netdev);

	/* Start USB dev only if we have successfully registered CAN device */
	err = mcba_usb_start(priv);
	if (err) {
		if (err == -ENODEV)
			netif_device_detach(priv->netdev);

		netdev_warn(netdev, "couldn't start device: %d\n", err);

		goto cleanup_unregister_candev;
	}

	dev_info(&intf->dev, "Microchip CAN BUS Analyzer connected\n");

	return 0;

cleanup_unregister_candev:
	unregister_candev(priv->netdev);

cleanup_free_candev:
	free_candev(netdev);

	return err;
}


static void mcba_usb_disconnect(struct usb_interface *intf)
{
	struct mcba_priv *priv = usb_get_intfdata(intf);

	usb_set_intfdata(intf, NULL);

	netdev_info(priv->netdev, "device disconnected\n");

	unregister_candev(priv->netdev);
	mcba_urb_unlink(priv);
	free_candev(priv->netdev);
}


static void mcba_usb_process_can(struct mcba_priv *priv,
				 struct mcba_usb_msg_can *msg)
{
	struct can_frame *cf;
	struct sk_buff *skb;
	struct net_device_stats *stats = &priv->netdev->stats;
	u16 sid;

	skb = alloc_can_skb(priv->netdev, &cf);
	if (!skb)
		return;

	sid = get_unaligned_be16(&msg->sid);

	if (sid & MCBA_SIDL_EXID_MASK) {
		/* SIDH    | SIDL                 | EIDH   | EIDL
		 * 28 - 21 | 20 19 18 x x x 17 16 | 15 - 8 | 7 - 0
		 */
		cf->can_id = CAN_EFF_FLAG;

		/* store 28-18 bits */
		cf->can_id |= (sid & 0xffe0) << 13;
		/* store 17-16 bits */
		cf->can_id |= (sid & 3) << 16;
		/* store 15-0 bits */
		cf->can_id |= get_unaligned_be16(&msg->eid);
	} else {
		/* SIDH   | SIDL
		 * 10 - 3 | 2 1 0 x x x x x
		 */
		cf->can_id = (sid & 0xffe0) >> 5;
	}

	if (msg->dlc & MCBA_DLC_RTR_MASK)
		cf->can_id |= CAN_RTR_FLAG;

	cf->can_dlc = get_can_dlc(msg->dlc & MCBA_DLC_MASK);

	memcpy(cf->data, msg->data, cf->can_dlc);

	stats->rx_packets++;
	stats->rx_bytes += cf->can_dlc;

	can_led_event(priv->netdev, CAN_LED_EVENT_RX);
	netif_rx(skb);
}


static inline void mcba_usb_free_ctx(struct mcba_usb_ctx *ctx)
{
	/* Increase number of free ctxs before freeing ctx */
	atomic_inc(&ctx->priv->free_ctx_cnt);

	ctx->ndx = MCBA_CTX_FREE;

	/* Wake up the queue once ctx is marked free */
	netif_wake_queue(ctx->priv->netdev);
}

#define MCBA_CTX_FREE MCBA_MAX_TX_URBS

#define MCBA_USB_TX_BUFF_SIZE (sizeof(struct mcba_usb_msg))

struct mcba_usb_ctx {
	struct mcba_priv *priv;
	u32 ndx;
	bool can;
}

struct mcba_priv {
	struct can_priv can; /* must be the first member */
	struct sk_buff *echo_skb[MCBA_MAX_TX_URBS];
	struct mcba_usb_ctx tx_context[MCBA_MAX_TX_URBS];
	struct usb_device *udev;
	struct net_device *netdev;
	struct usb_anchor tx_submitted;
	struct usb_anchor rx_submitted;
	struct can_berr_counter bec;
	bool usb_ka_first_pass;
	bool can_ka_first_pass;
	bool can_speed_check;
	atomic_t free_ctx_cnt;
	void *rxbuf[MCBA_MAX_RX_URBS];
	dma_addr_t rxbuf_dma[MCBA_MAX_RX_URBS];
}

static const struct usb_device_id mcba_usb_table[] = {
	{ USB_DEVICE(MCBA_VENDOR_ID, MCBA_PRODUCT_ID) },
	{} /* Terminating entry */
};

static const u16 mcba_termination[] = { MCBA_TERMINATION_DISABLED,
					MCBA_TERMINATION_ENABLED };

static const u32 mcba_bitrate[] = { 20000,  33333,  50000,  80000,  83333,
				    100000, 125000, 150000, 175000, 200000,
				    225000, 250000, 275000, 300000, 500000,
				    625000, 800000, 1000000 };

static const struct net_device_ops mcba_netdev_ops = {
	.ndo_open = mcba_usb_open,
	.ndo_stop = mcba_usb_close,
	.ndo_start_xmit = mcba_usb_start_xmit,
};

static struct usb_driver mcba_usb_driver = {
	.name = MCBA_MODULE_NAME,
	.probe = mcba_usb_probe,
	.disconnect = mcba_usb_disconnect,
	.id_table = mcba_usb_table,
};


static int mcba_usb_start(struct mcba_priv *priv)
{
	struct net_device *netdev = priv->netdev;
	int err, i;

	mcba_init_ctx(priv);

	for (i = 0; i < MCBA_MAX_RX_URBS; i++) {
		struct urb *urb = NULL;
		u8 *buf;

		/* create a URB, and a buffer for it */
		urb = usb_alloc_urb(0, GFP_KERNEL);
		if (!urb) {
			err = -ENOMEM;
			break;
		}

		buf = usb_alloc_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE,
					 GFP_KERNEL, &urb->transfer_dma);
		if (!buf) {
			netdev_err(netdev, "No memory left for USB buffer\n");
			usb_free_urb(urb);
			err = -ENOMEM;
			break;
		}

		usb_fill_bulk_urb(urb, priv->udev,
				  usb_rcvbulkpipe(priv->udev, MCBA_USB_EP_IN),
				  buf, MCBA_USB_RX_BUFF_SIZE,
				  mcba_usb_read_bulk_callback, priv);
		urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
		usb_anchor_urb(urb, &priv->rx_submitted);

		err = usb_submit_urb(urb, GFP_KERNEL);
		if (err) {
			usb_unanchor_urb(urb);
			usb_free_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE,
					  buf, urb->transfer_dma);
			usb_free_urb(urb);
			break;
		}

		/* Drop reference, USB core will take care of freeing it */
		usb_free_urb(urb);
	}

	/* Did we submit any URBs */
	if (i == 0) {
		netdev_warn(netdev, "couldn't setup read URBs\n");
		return err;
	}

	/* Warn if we've couldn't transmit all the URBs */
	if (i < MCBA_MAX_RX_URBS)
		netdev_warn(netdev, "rx performance may be slow\n");

	mcba_usb_xmit_read_fw_ver(priv, MCBA_VER_REQ_USB);
	mcba_usb_xmit_read_fw_ver(priv, MCBA_VER_REQ_CAN);

	return err;
}


static void mcba_usb_process_rx(struct mcba_priv *priv,
				struct mcba_usb_msg *msg)
{
	switch (msg->cmd_id) {
	case MBCA_CMD_I_AM_ALIVE_FROM_CAN:
		mcba_usb_process_ka_can(priv,
					(struct mcba_usb_msg_ka_can *)msg);
		break;

	case MBCA_CMD_I_AM_ALIVE_FROM_USB:
		mcba_usb_process_ka_usb(priv,
					(struct mcba_usb_msg_ka_usb *)msg);
		break;

	case MBCA_CMD_RECEIVE_MESSAGE:
		mcba_usb_process_can(priv, (struct mcba_usb_msg_can *)msg);
		break;

	case MBCA_CMD_NOTHING_TO_SEND:
		/* Side effect of communication between PIC_USB and PIC_CAN.
		 * PIC_CAN is telling us that it has nothing to send
		 */
		break;

	case MBCA_CMD_TRANSMIT_MESSAGE_RSP:
		/* Transmission response from the device containing timestamp */
		break;

	default:
		netdev_warn(priv->netdev, "Unsupported msg (0x%hhX)",
			    msg->cmd_id);
		break;
	}
}


static void mcba_usb_write_bulk_callback(struct urb *urb)
{
	struct mcba_usb_ctx *ctx = urb->context;
	struct net_device *netdev;

	WARN_ON(!ctx);

	netdev = ctx->priv->netdev;

	/* free up our allocated buffer */
	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
			  urb->transfer_buffer, urb->transfer_dma);

	if (ctx->can) {
		if (!netif_device_present(netdev))
			return;

		netdev->stats.tx_packets++;
		netdev->stats.tx_bytes += ctx->dlc;

		can_led_event(netdev, CAN_LED_EVENT_TX);
		can_get_echo_skb(netdev, ctx->ndx);
	}

	if (urb->status)
		netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status);

	/* Release the context */
	mcba_usb_free_ctx(ctx);
}


static void mcba_usb_xmit_cmd(struct mcba_priv *priv,
			      struct mcba_usb_msg *usb_msg)
{
	struct mcba_usb_ctx *ctx = NULL;
	int err;

	ctx = mcba_usb_get_free_ctx(priv, NULL);
	if (!ctx) {
		netdev_err(priv->netdev,
			   "Lack of free ctx. Sending (%d) cmd aborted",
			   usb_msg->cmd_id);

		return;
	}

	err = mcba_usb_xmit(priv, usb_msg, ctx);
	if (err)
		netdev_err(priv->netdev, "Failed to send cmd (%d)",
			   usb_msg->cmd_id);
}


static void mcba_usb_process_ka_can(struct mcba_priv *priv,
				    struct mcba_usb_msg_ka_can *msg)
{
	if (unlikely(priv->can_ka_first_pass)) {
		netdev_info(priv->netdev, "PIC CAN version %hhu.%hhu\n",
			    msg->soft_ver_major, msg->soft_ver_minor);

		priv->can_ka_first_pass = false;
	}

	if (unlikely(priv->can_speed_check)) {
		const u32 bitrate = convert_can2host_bitrate(msg);

		priv->can_speed_check = false;

		if (bitrate != priv->can.bittiming.bitrate)
			netdev_err(
			    priv->netdev,
			    "Wrong bitrate reported by the device (%u). Expected %u",
			    bitrate, priv->can.bittiming.bitrate);
	}

	priv->bec.txerr = msg->tx_err_cnt;
	priv->bec.rxerr = msg->rx_err_cnt;

	if (msg->tx_bus_off)
		priv->can.state = CAN_STATE_BUS_OFF;

	else if ((priv->bec.txerr > MCBA_CAN_STATE_ERR_PSV_TH) ||
		 (priv->bec.rxerr > MCBA_CAN_STATE_ERR_PSV_TH))
		priv->can.state = CAN_STATE_ERROR_PASSIVE;

	else if ((priv->bec.txerr > MCBA_CAN_STATE_WRN_TH) ||
		 (priv->bec.rxerr > MCBA_CAN_STATE_WRN_TH))
		priv->can.state = CAN_STATE_ERROR_WARNING;
}


static u32 convert_can2host_bitrate(struct mcba_usb_msg_ka_can *msg)
{
	const u32 bitrate = get_unaligned_be16(&msg->can_bitrate);

	if ((bitrate == 33) || (bitrate == 83))
		return bitrate * 1000 + 333;
	else
		return bitrate * 1000;
}


static void mcba_usb_process_ka_usb(struct mcba_priv *priv,
				    struct mcba_usb_msg_ka_usb *msg)
{
	if (unlikely(priv->usb_ka_first_pass)) {
		netdev_info(priv->netdev, "PIC USB version %hhu.%hhu\n",
			    msg->soft_ver_major, msg->soft_ver_minor);

		priv->usb_ka_first_pass = false;
	}

	if (msg->termination_state)
		priv->can.termination = MCBA_TERMINATION_ENABLED;
	else
		priv->can.termination = MCBA_TERMINATION_DISABLED;
}


static int mcba_net_set_mode(struct net_device *netdev, enum can_mode mode)
{
	return 0;
}


static void mcba_usb_read_bulk_callback(struct urb *urb)
{
	struct mcba_priv *priv = urb->context;
	struct net_device *netdev;
	int retval;
	int pos = 0;

	netdev = priv->netdev;

	if (!netif_device_present(netdev))
		return;

	switch (urb->status) {
	case 0: /* success */
		break;

	case -ENOENT:
	case -EPIPE:
	case -EPROTO:
	case -ESHUTDOWN:
		return;

	default:
		netdev_info(netdev, "Rx URB aborted (%d)\n", urb->status);

		goto resubmit_urb;
	}

	while (pos < urb->actual_length) {
		struct mcba_usb_msg *msg;

		if (pos + sizeof(struct mcba_usb_msg) > urb->actual_length) {
			netdev_err(priv->netdev, "format error\n");
			break;
		}

		msg = (struct mcba_usb_msg *)(urb->transfer_buffer + pos);
		mcba_usb_process_rx(priv, msg);

		pos += sizeof(struct mcba_usb_msg);
	}

resubmit_urb:

	usb_fill_bulk_urb(urb, priv->udev,
			  usb_rcvbulkpipe(priv->udev, MCBA_USB_EP_OUT),
			  urb->transfer_buffer, MCBA_USB_RX_BUFF_SIZE,
			  mcba_usb_read_bulk_callback, priv);

	retval = usb_submit_urb(urb, GFP_ATOMIC);

	if (retval == -ENODEV)
		netif_device_detach(netdev);
	else if (retval)
		netdev_err(netdev, "failed resubmitting read bulk urb: %d\n",
			   retval);
}

#define MCBA_USB_RX_BUFF_SIZE 64

#define MCBA_USB_TX_BUFF_SIZE (sizeof(struct mcba_usb_msg))

#define MCBA_USB_EP_OUT 1

struct mcba_usb_ctx {
	struct mcba_priv *priv;
	u32 ndx;
	bool can;
}

struct mcba_priv {
	struct can_priv can; /* must be the first member */
	struct sk_buff *echo_skb[MCBA_MAX_TX_URBS];
	struct mcba_usb_ctx tx_context[MCBA_MAX_TX_URBS];
	struct usb_device *udev;
	struct net_device *netdev;
	struct usb_anchor tx_submitted;
	struct usb_anchor rx_submitted;
	struct can_berr_counter bec;
	bool usb_ka_first_pass;
	bool can_ka_first_pass;
	bool can_speed_check;
	atomic_t free_ctx_cnt;
	void *rxbuf[MCBA_MAX_RX_URBS];
	dma_addr_t rxbuf_dma[MCBA_MAX_RX_URBS];
}

static const struct usb_device_id mcba_usb_table[] = {
	{ USB_DEVICE(MCBA_VENDOR_ID, MCBA_PRODUCT_ID) },
	{} /* Terminating entry */
};

static const u16 mcba_termination[] = { MCBA_TERMINATION_DISABLED,
					MCBA_TERMINATION_ENABLED };

static const u32 mcba_bitrate[] = { 20000,  33333,  50000,  80000,  83333,
				    100000, 125000, 150000, 175000, 200000,
				    225000, 250000, 275000, 300000, 500000,
				    625000, 800000, 1000000 };

static const struct net_device_ops mcba_netdev_ops = {
	.ndo_open = mcba_usb_open,
	.ndo_stop = mcba_usb_close,
	.ndo_start_xmit = mcba_usb_start_xmit,
};

static struct usb_driver mcba_usb_driver = {
	.name = MCBA_MODULE_NAME,
	.probe = mcba_usb_probe,
	.disconnect = mcba_usb_disconnect,
	.id_table = mcba_usb_table,
};


static int mcba_usb_close(struct net_device *netdev)
{
	struct mcba_priv *priv = netdev_priv(netdev);

	priv->can.state = CAN_STATE_STOPPED;

	netif_stop_queue(netdev);

	/* Stop polling */
	mcba_urb_unlink(priv);

	close_candev(netdev);
	can_led_event(netdev, CAN_LED_EVENT_STOP);

	return 0;
}


static inline struct mcba_usb_ctx *mcba_usb_get_free_ctx(struct mcba_priv *priv,
							 struct can_frame *cf)
{
	int i = 0;
	struct mcba_usb_ctx *ctx = NULL;

	for (i = 0; i < MCBA_MAX_TX_URBS; i++) {
		if (priv->tx_context[i].ndx == MCBA_CTX_FREE) {
			ctx = &priv->tx_context[i];
			ctx->ndx = i;

			if (cf) {
				ctx->can = true;
				ctx->dlc = cf->can_dlc;
			} else {
				ctx->can = false;
				ctx->dlc = 0;
			}

			atomic_dec(&priv->free_ctx_cnt);
			break;
		}
	}

	if (!atomic_read(&priv->free_ctx_cnt))
		/* That was the last free ctx. Slow down tx path */
		netif_stop_queue(priv->netdev);

	return ctx;
}


static int mcba_net_set_bittiming(struct net_device *netdev)
{
	struct mcba_priv *priv = netdev_priv(netdev);
	const u16 bitrate_kbps = priv->can.bittiming.bitrate / 1000;

	mcba_usb_xmit_change_bitrate(priv, bitrate_kbps);

	return 0;
}


int license_recv(rdpLicense* license, wStream* s)
{
	BYTE flags;
	BYTE bMsgType;
	UINT16 wMsgSize;
	UINT16 length;
	UINT16 channelId;
	UINT16 securityFlags = 0;

	if (!rdp_read_header(license->rdp, s, &length, &channelId))
	{
		WLog_ERR(TAG, "Incorrect RDP header.");
		return -1;
	}

	if (!rdp_read_security_header(s, &securityFlags, &length))
		return -1;

	if (securityFlags & SEC_ENCRYPT)
	{
		if (!rdp_decrypt(license->rdp, s, &length, securityFlags))
		{
			WLog_ERR(TAG, "rdp_decrypt failed");
			return -1;
		}
	}

	if (!(securityFlags & SEC_LICENSE_PKT))
	{
		int status;

		if (!(securityFlags & SEC_ENCRYPT))
			Stream_Rewind(s, RDP_SECURITY_HEADER_LENGTH);

		status = rdp_recv_out_of_sequence_pdu(license->rdp, s);
		if (status < 0)
		{
			WLog_ERR(TAG, "unexpected license packet.");
			return status;
		}

		return 0;
	}

	if (!license_read_preamble(s, &bMsgType, &flags, &wMsgSize)) /* preamble (4 bytes) */
		return -1;

	DEBUG_LICENSE("Receiving %s Packet", LICENSE_MESSAGE_STRINGS[bMsgType & 0x1F]);

	switch (bMsgType)
	{
		case LICENSE_REQUEST:
			if (!license_read_license_request_packet(license, s))
				return -1;

			if (!license_answer_license_request(license))
				return -1;
			break;

		case PLATFORM_CHALLENGE:
			if (!license_read_platform_challenge_packet(license, s))
				return -1;

			if (!license_send_platform_challenge_response_packet(license))
				return -1;
			break;

		case NEW_LICENSE:
		case UPGRADE_LICENSE:
			if (!license_read_new_or_upgrade_license_packet(license, s))
				return -1;
			break;

		case ERROR_ALERT:
			if (!license_read_error_alert_packet(license, s))
				return -1;
			break;

		default:
			WLog_ERR(TAG, "invalid bMsgType:%" PRIu8 "", bMsgType);
			return -1;
	}

	if (!tpkt_ensure_stream_consumed(s, length))
		return -1;
	return 0;
}

Fixed OOB Read in license_read_new_or_upgrade_license_packet

CVE-2020-11099 thanks to @antonio-morales for finding this.

#define TAG FREERDP_TAG("core.license")

static wStream* license_send_stream_init(rdpLicense* license);

static void license_generate_randoms(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);

static LICENSE_BLOB* license_new_binary_blob(UINT16 type);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);

static const char* const LICENSE_MESSAGE_STRINGS[] = { "",
	                                                   "License Request",
	                                                   "Platform Challenge",
	                                                   "New License",
	                                                   "Upgrade License",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "License Info",
	                                                   "New License Request",
	                                                   "",
	                                                   "Platform Challenge Response",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "Error Alert" };


BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s)
{
	BYTE macData[16];
	UINT32 ConnectFlags = 0;

	DEBUG_LICENSE("Receiving Platform Challenge Packet");

	if (Stream_GetRemainingLength(s) < 4)
		return FALSE;

	Stream_Read_UINT32(s, ConnectFlags); /* ConnectFlags, Reserved (4 bytes) */

	/* EncryptedPlatformChallenge */
	license->EncryptedPlatformChallenge->type = BB_ANY_BLOB;
	if (!license_read_binary_blob(s, license->EncryptedPlatformChallenge))
		return FALSE;
	license->EncryptedPlatformChallenge->type = BB_ENCRYPTED_DATA_BLOB;

	/* MACData (16 bytes) */
	if (Stream_GetRemainingLength(s) < 16)
		return FALSE;

	Stream_Read(s, macData, 16);
	if (!license_decrypt_and_check_MAC(license, license->EncryptedPlatformChallenge->data,
	                                   license->EncryptedPlatformChallenge->length,
	                                   license->PlatformChallenge, macData))
		return FALSE;

#ifdef WITH_DEBUG_LICENSE
	WLog_DBG(TAG, "ConnectFlags: 0x%08" PRIX32 "", ConnectFlags);
	WLog_DBG(TAG, "EncryptedPlatformChallenge:");
	winpr_HexDump(TAG, WLOG_DEBUG, license->EncryptedPlatformChallenge->data,
	              license->EncryptedPlatformChallenge->length);
	WLog_DBG(TAG, "PlatformChallenge:");
	winpr_HexDump(TAG, WLOG_DEBUG, license->PlatformChallenge->data,
	              license->PlatformChallenge->length);
	WLog_DBG(TAG, "MacData:");
	winpr_HexDump(TAG, WLOG_DEBUG, macData, 16);
#endif
	return TRUE;
}


SCOPE_LIST* license_new_scope_list()
{
	return (SCOPE_LIST*)calloc(1, sizeof(SCOPE_LIST));
}

static SCOPE_LIST* license_new_scope_list(void);

static void license_free_scope_list(SCOPE_LIST* scopeList);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);


BOOL license_encrypt_premaster_secret(rdpLicense* license)
{
	BYTE* EncryptedPremasterSecret;

	if (!license_get_server_rsa_public_key(license))
		return FALSE;

#ifdef WITH_DEBUG_LICENSE
	WLog_DBG(TAG, "Modulus (%" PRIu32 " bits):", license->ModulusLength * 8);
	winpr_HexDump(TAG, WLOG_DEBUG, license->Modulus, license->ModulusLength);
	WLog_DBG(TAG, "Exponent:");
	winpr_HexDump(TAG, WLOG_DEBUG, license->Exponent, 4);
#endif

	EncryptedPremasterSecret = (BYTE*)calloc(1, license->ModulusLength);
	if (!EncryptedPremasterSecret)
		return FALSE;

	license->EncryptedPremasterSecret->type = BB_RANDOM_BLOB;
	license->EncryptedPremasterSecret->length = PREMASTER_SECRET_LENGTH;
#ifndef LICENSE_NULL_PREMASTER_SECRET
	license->EncryptedPremasterSecret->length = crypto_rsa_public_encrypt(
	    license->PremasterSecret, PREMASTER_SECRET_LENGTH, license->ModulusLength, license->Modulus,
	    license->Exponent, EncryptedPremasterSecret);
#endif
	license->EncryptedPremasterSecret->data = EncryptedPremasterSecret;
	return TRUE;
}

#define TAG FREERDP_TAG("core.license")

#define LICENSE_NULL_PREMASTER_SECRET 1

static wStream* license_send_stream_init(rdpLicense* license);

static void license_generate_randoms(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);

static LICENSE_BLOB* license_new_binary_blob(UINT16 type);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);


BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob)
{
	if (!Stream_EnsureRemainingCapacity(s, blob->length + 4))
		return FALSE;

	Stream_Write_UINT16(s, blob->type);   /* wBlobType (2 bytes) */
	Stream_Write_UINT16(s, blob->length); /* wBlobLen (2 bytes) */

	if (blob->length > 0)
		Stream_Write(s, blob->data, blob->length); /* blobData */
	return TRUE;
}

static wStream* license_send_stream_init(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);

static LICENSE_BLOB* license_new_binary_blob(UINT16 type);

static void license_free_binary_blob(LICENSE_BLOB* blob);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);

static const char* const LICENSE_MESSAGE_STRINGS[] = { "",
	                                                   "License Request",
	                                                   "Platform Challenge",
	                                                   "New License",
	                                                   "Upgrade License",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "License Info",
	                                                   "New License Request",
	                                                   "",
	                                                   "Platform Challenge Response",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "Error Alert" };

static const char* const error_codes[] = { "ERR_UNKNOWN",
	                                       "ERR_INVALID_SERVER_CERTIFICATE",
	                                       "ERR_NO_LICENSE",
	                                       "ERR_INVALID_MAC",
	                                       "ERR_INVALID_SCOPE",
	                                       "ERR_UNKNOWN",
	                                       "ERR_NO_LICENSE_SERVER",
	                                       "STATUS_VALID_CLIENT",
	                                       "ERR_INVALID_CLIENT",
	                                       "ERR_UNKNOWN",
	                                       "ERR_UNKNOWN",
	                                       "ERR_INVALID_PRODUCT_ID",
	                                       "ERR_INVALID_MESSAGE_LENGTH" };

static const char* const state_transitions[] = { "ST_UNKNOWN", "ST_TOTAL_ABORT", "ST_NO_TRANSITION",
	                                             "ST_RESET_PHASE_TO_START",
	                                             "ST_RESEND_LAST_MESSAGE" };

static const char licenseStore[] = "licenses";


static BOOL license_write_encrypted_premaster_secret_blob(wStream* s, const LICENSE_BLOB* blob,
                                                          UINT32 ModulusLength)
{
	UINT32 length;
	length = ModulusLength + 8;

	if (blob->length > ModulusLength)
	{
		WLog_ERR(TAG, "license_write_encrypted_premaster_secret_blob: invalid blob");
		return FALSE;
	}

	if (!Stream_EnsureRemainingCapacity(s, length + 4))
		return FALSE;
	Stream_Write_UINT16(s, blob->type); /* wBlobType (2 bytes) */
	Stream_Write_UINT16(s, length);     /* wBlobLen (2 bytes) */

	if (blob->length > 0)
		Stream_Write(s, blob->data, blob->length); /* blobData */

	Stream_Zero(s, length - blob->length);
	return TRUE;
}

#define TAG FREERDP_TAG("core.license")

static wStream* license_send_stream_init(rdpLicense* license);

static void license_generate_randoms(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static LICENSE_PRODUCT_INFO* license_new_product_info(void);

static void license_free_product_info(LICENSE_PRODUCT_INFO* productInfo);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);

static LICENSE_BLOB* license_new_binary_blob(UINT16 type);

static void license_free_binary_blob(LICENSE_BLOB* blob);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static SCOPE_LIST* license_new_scope_list(void);

static void license_free_scope_list(SCOPE_LIST* scopeList);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);

static const char* const LICENSE_MESSAGE_STRINGS[] = { "",
	                                                   "License Request",
	                                                   "Platform Challenge",
	                                                   "New License",
	                                                   "Upgrade License",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "License Info",
	                                                   "New License Request",
	                                                   "",
	                                                   "Platform Challenge Response",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "Error Alert" };

static const char* const error_codes[] = { "ERR_UNKNOWN",
	                                       "ERR_INVALID_SERVER_CERTIFICATE",
	                                       "ERR_NO_LICENSE",
	                                       "ERR_INVALID_MAC",
	                                       "ERR_INVALID_SCOPE",
	                                       "ERR_UNKNOWN",
	                                       "ERR_NO_LICENSE_SERVER",
	                                       "STATUS_VALID_CLIENT",
	                                       "ERR_INVALID_CLIENT",
	                                       "ERR_UNKNOWN",
	                                       "ERR_UNKNOWN",
	                                       "ERR_INVALID_PRODUCT_ID",
	                                       "ERR_INVALID_MESSAGE_LENGTH" };

static const char* const state_transitions[] = { "ST_UNKNOWN", "ST_TOTAL_ABORT", "ST_NO_TRANSITION",
	                                             "ST_RESET_PHASE_TO_START",
	                                             "ST_RESEND_LAST_MESSAGE" };

static const char licenseStore[] = "licenses";


static BOOL computeCalHash(const char* hostname, char* hashStr)
{
	WINPR_DIGEST_CTX* sha1 = NULL;
	BOOL ret = FALSE;
	BYTE hash[20];
	size_t i;

	if (!(sha1 = winpr_Digest_New()))
		goto out;
	if (!winpr_Digest_Init(sha1, WINPR_MD_SHA1))
		goto out;
	if (!winpr_Digest_Update(sha1, (const BYTE*)hostname, strlen(hostname)))
		goto out;
	if (!winpr_Digest_Final(sha1, hash, sizeof(hash)))
		goto out;

	for (i = 0; i < sizeof(hash); i++, hashStr += 2)
		sprintf_s(hashStr, 3, "%.2x", hash[i]);

	ret = TRUE;
out:
	winpr_Digest_Free(sha1);
	return ret;
}

static wStream* license_send_stream_init(rdpLicense* license);

static void license_generate_randoms(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static LICENSE_PRODUCT_INFO* license_new_product_info(void);

static void license_free_product_info(LICENSE_PRODUCT_INFO* productInfo);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);

static LICENSE_BLOB* license_new_binary_blob(UINT16 type);

static void license_free_binary_blob(LICENSE_BLOB* blob);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static SCOPE_LIST* license_new_scope_list(void);

static void license_free_scope_list(SCOPE_LIST* scopeList);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);

static const char* const LICENSE_MESSAGE_STRINGS[] = { "",
	                                                   "License Request",
	                                                   "Platform Challenge",
	                                                   "New License",
	                                                   "Upgrade License",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "License Info",
	                                                   "New License Request",
	                                                   "",
	                                                   "Platform Challenge Response",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "",
	                                                   "Error Alert" };

static const char* const error_codes[] = { "ERR_UNKNOWN",
	                                       "ERR_INVALID_SERVER_CERTIFICATE",
	                                       "ERR_NO_LICENSE",
	                                       "ERR_INVALID_MAC",
	                                       "ERR_INVALID_SCOPE",
	                                       "ERR_UNKNOWN",
	                                       "ERR_NO_LICENSE_SERVER",
	                                       "STATUS_VALID_CLIENT",
	                                       "ERR_INVALID_CLIENT",
	                                       "ERR_UNKNOWN",
	                                       "ERR_UNKNOWN",
	                                       "ERR_INVALID_PRODUCT_ID",
	                                       "ERR_INVALID_MESSAGE_LENGTH" };

static const char* const state_transitions[] = { "ST_UNKNOWN", "ST_TOTAL_ABORT", "ST_NO_TRANSITION",
	                                             "ST_RESET_PHASE_TO_START",
	                                             "ST_RESEND_LAST_MESSAGE" };

static const char licenseStore[] = "licenses";


static BYTE* loadCalFile(rdpSettings* settings, const char* hostname, int* dataLen)
{
	char *licenseStorePath = NULL, *calPath = NULL;
	char calFilename[MAX_PATH];
	char hash[41];
	int length, status;
	FILE* fp;
	BYTE* ret = NULL;

	if (!computeCalHash(hostname, hash))
	{
		WLog_ERR(TAG, "loadCalFile: unable to compute hostname hash");
		return NULL;
	}

	sprintf_s(calFilename, sizeof(calFilename) - 1, "%s.cal", hash);

	if (!(licenseStorePath = GetCombinedPath(settings->ConfigPath, licenseStore)))
		return NULL;

	if (!(calPath = GetCombinedPath(licenseStorePath, calFilename)))
		goto error_path;

	fp = fopen(calPath, "rb");
	if (!fp)
		goto error_open;

	_fseeki64(fp, 0, SEEK_END);
	length = _ftelli64(fp);
	_fseeki64(fp, 0, SEEK_SET);

	ret = (BYTE*)malloc(length);
	if (!ret)
		goto error_malloc;

	status = fread(ret, length, 1, fp);
	if (status <= 0)
		goto error_read;

	*dataLen = length;

	fclose(fp);
	free(calPath);
	free(licenseStorePath);
	return ret;

error_read:
	free(ret);
error_malloc:
	fclose(fp);
error_open:
	free(calPath);
error_path:
	free(licenseStorePath);
	return NULL;
}


static BOOL license_encrypt_and_MAC(rdpLicense* license, const BYTE* input, size_t len,
                                    LICENSE_BLOB* target, BYTE* mac)
{
	return license_rc4_with_licenseKey(license, input, len, target) &&
	       security_mac_data(license->MacSaltKey, input, len, mac);
}


LICENSE_BLOB* license_new_binary_blob(UINT16 type)
{
	LICENSE_BLOB* blob;
	blob = (LICENSE_BLOB*)calloc(1, sizeof(LICENSE_BLOB));
	if (blob)
		blob->type = type;
	return blob;
}

static LICENSE_BLOB* license_new_binary_blob(UINT16 type);

static void license_free_binary_blob(LICENSE_BLOB* blob);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);


wStream* license_send_stream_init(rdpLicense* license)
{
	wStream* s;
	BOOL do_crypt = license->rdp->do_crypt;

	license->rdp->sec_flags = SEC_LICENSE_PKT;

	/**
	 * Encryption of licensing packets is optional even if the rdp security
	 * layer is used. If the peer has not indicated that it is capable of
	 * processing encrypted licensing packets (rdp->do_crypt_license) we turn
	 * off encryption (via rdp->do_crypt) before initializing the rdp stream
	 * and reenable it afterwards.
	 */

	if (do_crypt)
	{
		license->rdp->sec_flags |= SEC_LICENSE_ENCRYPT_CS;
		license->rdp->do_crypt = license->rdp->do_crypt_license;
	}

	s = rdp_send_stream_init(license->rdp);
	if (!s)
		return NULL;

	license->rdp->do_crypt = do_crypt;
	license->PacketHeaderLength = Stream_GetPosition(s);
	if (!Stream_SafeSeek(s, LICENSE_PREAMBLE_LENGTH))
		goto fail;
	return s;

fail:
	Stream_Release(s);
	return NULL;
}

#define TAG FREERDP_TAG("core.license")

static wStream* license_send_stream_init(rdpLicense* license);

static void license_generate_randoms(rdpLicense* license);

static BOOL license_generate_keys(rdpLicense* license);

static BOOL license_generate_hwid(rdpLicense* license);

static BOOL license_encrypt_premaster_secret(rdpLicense* license);

static BOOL license_read_product_info(wStream* s, LICENSE_PRODUCT_INFO* productInfo);

static BOOL license_read_binary_blob(wStream* s, LICENSE_BLOB* blob);

static BOOL license_write_binary_blob(wStream* s, const LICENSE_BLOB* blob);

static BOOL license_read_scope_list(wStream* s, SCOPE_LIST* scopeList);

static BOOL license_read_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_read_platform_challenge_packet(rdpLicense* license, wStream* s);

static BOOL license_read_new_or_upgrade_license_packet(rdpLicense* license, wStream* s);

static BOOL license_read_error_alert_packet(rdpLicense* license, wStream* s);

static BOOL license_write_new_license_request_packet(rdpLicense* license, wStream* s);

static BOOL license_answer_license_request(rdpLicense* license);

static BOOL license_write_platform_challenge_response_packet(rdpLicense* license, wStream* s,
                                                             const BYTE* mac_data);

static BOOL license_send_platform_challenge_response_packet(rdpLicense* license);

static BOOL license_send_client_info(rdpLicense* license, const LICENSE_BLOB* calBlob,
                                     BYTE* signature);