xref: /openbmc/linux/net/bluetooth/hidp/sock.c (revision ec1b4cf74c81bfd0fbe5bf62bafc86c45917e72f)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds    HIDP implementation for Linux Bluetooth stack (BlueZ).
31da177e4SLinus Torvalds    Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
41da177e4SLinus Torvalds 
51da177e4SLinus Torvalds    This program is free software; you can redistribute it and/or modify
61da177e4SLinus Torvalds    it under the terms of the GNU General Public License version 2 as
71da177e4SLinus Torvalds    published by the Free Software Foundation;
81da177e4SLinus Torvalds 
91da177e4SLinus Torvalds    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
101da177e4SLinus Torvalds    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
111da177e4SLinus Torvalds    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
121da177e4SLinus Torvalds    IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
131da177e4SLinus Torvalds    CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
141da177e4SLinus Torvalds    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
151da177e4SLinus Torvalds    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
161da177e4SLinus Torvalds    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds    ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
191da177e4SLinus Torvalds    COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
201da177e4SLinus Torvalds    SOFTWARE IS DISCLAIMED.
211da177e4SLinus Torvalds */
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds #include <linux/module.h>
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds #include <linux/types.h>
264fc268d2SRandy Dunlap #include <linux/capability.h>
271da177e4SLinus Torvalds #include <linux/errno.h>
281da177e4SLinus Torvalds #include <linux/kernel.h>
291da177e4SLinus Torvalds #include <linux/slab.h>
301da177e4SLinus Torvalds #include <linux/poll.h>
311da177e4SLinus Torvalds #include <linux/fcntl.h>
321da177e4SLinus Torvalds #include <linux/skbuff.h>
331da177e4SLinus Torvalds #include <linux/socket.h>
341da177e4SLinus Torvalds #include <linux/ioctl.h>
351da177e4SLinus Torvalds #include <linux/file.h>
361da177e4SLinus Torvalds #include <linux/init.h>
37e9c5702eSMarcel Holtmann #include <linux/compat.h>
381da177e4SLinus Torvalds #include <net/sock.h>
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds #include "hidp.h"
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds static int hidp_sock_release(struct socket *sock)
431da177e4SLinus Torvalds {
441da177e4SLinus Torvalds 	struct sock *sk = sock->sk;
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds 	BT_DBG("sock %p sk %p", sock, sk);
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds 	if (!sk)
491da177e4SLinus Torvalds 		return 0;
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds 	sock_orphan(sk);
521da177e4SLinus Torvalds 	sock_put(sk);
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds 	return 0;
551da177e4SLinus Torvalds }
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
581da177e4SLinus Torvalds {
591da177e4SLinus Torvalds 	void __user *argp = (void __user *) arg;
601da177e4SLinus Torvalds 	struct hidp_connadd_req ca;
611da177e4SLinus Torvalds 	struct hidp_conndel_req cd;
621da177e4SLinus Torvalds 	struct hidp_connlist_req cl;
631da177e4SLinus Torvalds 	struct hidp_conninfo ci;
641da177e4SLinus Torvalds 	struct socket *csock;
651da177e4SLinus Torvalds 	struct socket *isock;
661da177e4SLinus Torvalds 	int err;
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds 	BT_DBG("cmd %x arg %lx", cmd, arg);
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds 	switch (cmd) {
711da177e4SLinus Torvalds 	case HIDPCONNADD:
721da177e4SLinus Torvalds 		if (!capable(CAP_NET_ADMIN))
731da177e4SLinus Torvalds 			return -EACCES;
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds 		if (copy_from_user(&ca, argp, sizeof(ca)))
761da177e4SLinus Torvalds 			return -EFAULT;
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds 		csock = sockfd_lookup(ca.ctrl_sock, &err);
791da177e4SLinus Torvalds 		if (!csock)
801da177e4SLinus Torvalds 			return err;
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds 		isock = sockfd_lookup(ca.intr_sock, &err);
831da177e4SLinus Torvalds 		if (!isock) {
8467b23219SJulia Lawall 			sockfd_put(csock);
851da177e4SLinus Torvalds 			return err;
861da177e4SLinus Torvalds 		}
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds 		if (csock->sk->sk_state != BT_CONNECTED || isock->sk->sk_state != BT_CONNECTED) {
8967b23219SJulia Lawall 			sockfd_put(csock);
9067b23219SJulia Lawall 			sockfd_put(isock);
911da177e4SLinus Torvalds 			return -EBADFD;
921da177e4SLinus Torvalds 		}
931da177e4SLinus Torvalds 
941da177e4SLinus Torvalds 		err = hidp_add_connection(&ca, csock, isock);
951da177e4SLinus Torvalds 		if (!err) {
961da177e4SLinus Torvalds 			if (copy_to_user(argp, &ca, sizeof(ca)))
971da177e4SLinus Torvalds 				err = -EFAULT;
981da177e4SLinus Torvalds 		} else {
9967b23219SJulia Lawall 			sockfd_put(csock);
10067b23219SJulia Lawall 			sockfd_put(isock);
1011da177e4SLinus Torvalds 		}
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds 		return err;
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds 	case HIDPCONNDEL:
1061da177e4SLinus Torvalds 		if (!capable(CAP_NET_ADMIN))
1071da177e4SLinus Torvalds 			return -EACCES;
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds 		if (copy_from_user(&cd, argp, sizeof(cd)))
1101da177e4SLinus Torvalds 			return -EFAULT;
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds 		return hidp_del_connection(&cd);
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds 	case HIDPGETCONNLIST:
1151da177e4SLinus Torvalds 		if (copy_from_user(&cl, argp, sizeof(cl)))
1161da177e4SLinus Torvalds 			return -EFAULT;
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds 		if (cl.cnum <= 0)
1191da177e4SLinus Torvalds 			return -EINVAL;
1201da177e4SLinus Torvalds 
1211da177e4SLinus Torvalds 		err = hidp_get_connlist(&cl);
1221da177e4SLinus Torvalds 		if (!err && copy_to_user(argp, &cl, sizeof(cl)))
1231da177e4SLinus Torvalds 			return -EFAULT;
1241da177e4SLinus Torvalds 
1251da177e4SLinus Torvalds 		return err;
1261da177e4SLinus Torvalds 
1271da177e4SLinus Torvalds 	case HIDPGETCONNINFO:
1281da177e4SLinus Torvalds 		if (copy_from_user(&ci, argp, sizeof(ci)))
1291da177e4SLinus Torvalds 			return -EFAULT;
1301da177e4SLinus Torvalds 
1311da177e4SLinus Torvalds 		err = hidp_get_conninfo(&ci);
1321da177e4SLinus Torvalds 		if (!err && copy_to_user(argp, &ci, sizeof(ci)))
1331da177e4SLinus Torvalds 			return -EFAULT;
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 		return err;
1361da177e4SLinus Torvalds 	}
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds 	return -EINVAL;
1391da177e4SLinus Torvalds }
1401da177e4SLinus Torvalds 
141e9c5702eSMarcel Holtmann #ifdef CONFIG_COMPAT
142e9c5702eSMarcel Holtmann struct compat_hidp_connadd_req {
143e9c5702eSMarcel Holtmann 	int   ctrl_sock;	// Connected control socket
144e9c5702eSMarcel Holtmann 	int   intr_sock;	// Connteted interrupt socket
145e9c5702eSMarcel Holtmann 	__u16 parser;
146e9c5702eSMarcel Holtmann 	__u16 rd_size;
147e9c5702eSMarcel Holtmann 	compat_uptr_t rd_data;
148e9c5702eSMarcel Holtmann 	__u8  country;
149e9c5702eSMarcel Holtmann 	__u8  subclass;
150e9c5702eSMarcel Holtmann 	__u16 vendor;
151e9c5702eSMarcel Holtmann 	__u16 product;
152e9c5702eSMarcel Holtmann 	__u16 version;
153e9c5702eSMarcel Holtmann 	__u32 flags;
154e9c5702eSMarcel Holtmann 	__u32 idle_to;
155e9c5702eSMarcel Holtmann 	char  name[128];
156e9c5702eSMarcel Holtmann };
157e9c5702eSMarcel Holtmann 
158e9c5702eSMarcel Holtmann static int hidp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
159e9c5702eSMarcel Holtmann {
160e9c5702eSMarcel Holtmann 	if (cmd == HIDPGETCONNLIST) {
161e9c5702eSMarcel Holtmann 		struct hidp_connlist_req cl;
162e9c5702eSMarcel Holtmann 		uint32_t uci;
163e9c5702eSMarcel Holtmann 		int err;
164e9c5702eSMarcel Holtmann 
165e9c5702eSMarcel Holtmann 		if (get_user(cl.cnum, (uint32_t __user *) arg) ||
166e9c5702eSMarcel Holtmann 				get_user(uci, (u32 __user *) (arg + 4)))
167e9c5702eSMarcel Holtmann 			return -EFAULT;
168e9c5702eSMarcel Holtmann 
169e9c5702eSMarcel Holtmann 		cl.ci = compat_ptr(uci);
170e9c5702eSMarcel Holtmann 
171e9c5702eSMarcel Holtmann 		if (cl.cnum <= 0)
172e9c5702eSMarcel Holtmann 			return -EINVAL;
173e9c5702eSMarcel Holtmann 
174e9c5702eSMarcel Holtmann 		err = hidp_get_connlist(&cl);
175e9c5702eSMarcel Holtmann 
176e9c5702eSMarcel Holtmann 		if (!err && put_user(cl.cnum, (uint32_t __user *) arg))
177e9c5702eSMarcel Holtmann 			err = -EFAULT;
178e9c5702eSMarcel Holtmann 
179e9c5702eSMarcel Holtmann 		return err;
180e9c5702eSMarcel Holtmann 	} else if (cmd == HIDPCONNADD) {
181e9c5702eSMarcel Holtmann 		struct compat_hidp_connadd_req ca;
182e9c5702eSMarcel Holtmann 		struct hidp_connadd_req __user *uca;
183e9c5702eSMarcel Holtmann 
184e9c5702eSMarcel Holtmann 		uca = compat_alloc_user_space(sizeof(*uca));
185e9c5702eSMarcel Holtmann 
18655e74744SAl Viro 		if (copy_from_user(&ca, (void __user *) arg, sizeof(ca)))
187e9c5702eSMarcel Holtmann 			return -EFAULT;
188e9c5702eSMarcel Holtmann 
189e9c5702eSMarcel Holtmann 		if (put_user(ca.ctrl_sock, &uca->ctrl_sock) ||
190e9c5702eSMarcel Holtmann 				put_user(ca.intr_sock, &uca->intr_sock) ||
191e9c5702eSMarcel Holtmann 				put_user(ca.parser, &uca->parser) ||
192a83d6c0dSMarcel Holtmann 				put_user(ca.rd_size, &uca->rd_size) ||
193e9c5702eSMarcel Holtmann 				put_user(compat_ptr(ca.rd_data), &uca->rd_data) ||
194e9c5702eSMarcel Holtmann 				put_user(ca.country, &uca->country) ||
195e9c5702eSMarcel Holtmann 				put_user(ca.subclass, &uca->subclass) ||
196e9c5702eSMarcel Holtmann 				put_user(ca.vendor, &uca->vendor) ||
197e9c5702eSMarcel Holtmann 				put_user(ca.product, &uca->product) ||
198e9c5702eSMarcel Holtmann 				put_user(ca.version, &uca->version) ||
199e9c5702eSMarcel Holtmann 				put_user(ca.flags, &uca->flags) ||
200e9c5702eSMarcel Holtmann 				put_user(ca.idle_to, &uca->idle_to) ||
201e9c5702eSMarcel Holtmann 				copy_to_user(&uca->name[0], &ca.name[0], 128))
202e9c5702eSMarcel Holtmann 			return -EFAULT;
203e9c5702eSMarcel Holtmann 
204e9c5702eSMarcel Holtmann 		arg = (unsigned long) uca;
205e9c5702eSMarcel Holtmann 
206e9c5702eSMarcel Holtmann 		/* Fall through. We don't actually write back any _changes_
207e9c5702eSMarcel Holtmann 		   to the structure anyway, so there's no need to copy back
208e9c5702eSMarcel Holtmann 		   into the original compat version */
209e9c5702eSMarcel Holtmann 	}
210e9c5702eSMarcel Holtmann 
211e9c5702eSMarcel Holtmann 	return hidp_sock_ioctl(sock, cmd, arg);
212e9c5702eSMarcel Holtmann }
213e9c5702eSMarcel Holtmann #endif
214e9c5702eSMarcel Holtmann 
21590ddc4f0SEric Dumazet static const struct proto_ops hidp_sock_ops = {
2161da177e4SLinus Torvalds 	.family		= PF_BLUETOOTH,
2171da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
2181da177e4SLinus Torvalds 	.release	= hidp_sock_release,
2191da177e4SLinus Torvalds 	.ioctl		= hidp_sock_ioctl,
220e9c5702eSMarcel Holtmann #ifdef CONFIG_COMPAT
221e9c5702eSMarcel Holtmann 	.compat_ioctl	= hidp_sock_compat_ioctl,
222e9c5702eSMarcel Holtmann #endif
2231da177e4SLinus Torvalds 	.bind		= sock_no_bind,
2241da177e4SLinus Torvalds 	.getname	= sock_no_getname,
2251da177e4SLinus Torvalds 	.sendmsg	= sock_no_sendmsg,
2261da177e4SLinus Torvalds 	.recvmsg	= sock_no_recvmsg,
2271da177e4SLinus Torvalds 	.poll		= sock_no_poll,
2281da177e4SLinus Torvalds 	.listen		= sock_no_listen,
2291da177e4SLinus Torvalds 	.shutdown	= sock_no_shutdown,
2301da177e4SLinus Torvalds 	.setsockopt	= sock_no_setsockopt,
2311da177e4SLinus Torvalds 	.getsockopt	= sock_no_getsockopt,
2321da177e4SLinus Torvalds 	.connect	= sock_no_connect,
2331da177e4SLinus Torvalds 	.socketpair	= sock_no_socketpair,
2341da177e4SLinus Torvalds 	.accept		= sock_no_accept,
2351da177e4SLinus Torvalds 	.mmap		= sock_no_mmap
2361da177e4SLinus Torvalds };
2371da177e4SLinus Torvalds 
2381da177e4SLinus Torvalds static struct proto hidp_proto = {
2391da177e4SLinus Torvalds 	.name		= "HIDP",
2401da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
2411da177e4SLinus Torvalds 	.obj_size	= sizeof(struct bt_sock)
2421da177e4SLinus Torvalds };
2431da177e4SLinus Torvalds 
2441b8d7ae4SEric W. Biederman static int hidp_sock_create(struct net *net, struct socket *sock, int protocol)
2451da177e4SLinus Torvalds {
2461da177e4SLinus Torvalds 	struct sock *sk;
2471da177e4SLinus Torvalds 
2481da177e4SLinus Torvalds 	BT_DBG("sock %p", sock);
2491da177e4SLinus Torvalds 
2501da177e4SLinus Torvalds 	if (sock->type != SOCK_RAW)
2511da177e4SLinus Torvalds 		return -ESOCKTNOSUPPORT;
2521da177e4SLinus Torvalds 
2536257ff21SPavel Emelyanov 	sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hidp_proto);
2541da177e4SLinus Torvalds 	if (!sk)
2551da177e4SLinus Torvalds 		return -ENOMEM;
2561da177e4SLinus Torvalds 
2571da177e4SLinus Torvalds 	sock_init_data(sock, sk);
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds 	sock->ops = &hidp_sock_ops;
2601da177e4SLinus Torvalds 
2611da177e4SLinus Torvalds 	sock->state = SS_UNCONNECTED;
2621da177e4SLinus Torvalds 
2631da177e4SLinus Torvalds 	sock_reset_flag(sk, SOCK_ZAPPED);
2641da177e4SLinus Torvalds 
2651da177e4SLinus Torvalds 	sk->sk_protocol = protocol;
2661da177e4SLinus Torvalds 	sk->sk_state	= BT_OPEN;
2671da177e4SLinus Torvalds 
2681da177e4SLinus Torvalds 	return 0;
2691da177e4SLinus Torvalds }
2701da177e4SLinus Torvalds 
271*ec1b4cf7SStephen Hemminger static const struct net_proto_family hidp_sock_family_ops = {
2721da177e4SLinus Torvalds 	.family	= PF_BLUETOOTH,
2731da177e4SLinus Torvalds 	.owner	= THIS_MODULE,
2741da177e4SLinus Torvalds 	.create	= hidp_sock_create
2751da177e4SLinus Torvalds };
2761da177e4SLinus Torvalds 
2771da177e4SLinus Torvalds int __init hidp_init_sockets(void)
2781da177e4SLinus Torvalds {
2791da177e4SLinus Torvalds 	int err;
2801da177e4SLinus Torvalds 
2811da177e4SLinus Torvalds 	err = proto_register(&hidp_proto, 0);
2821da177e4SLinus Torvalds 	if (err < 0)
2831da177e4SLinus Torvalds 		return err;
2841da177e4SLinus Torvalds 
2851da177e4SLinus Torvalds 	err = bt_sock_register(BTPROTO_HIDP, &hidp_sock_family_ops);
2861da177e4SLinus Torvalds 	if (err < 0)
2871da177e4SLinus Torvalds 		goto error;
2881da177e4SLinus Torvalds 
2891da177e4SLinus Torvalds 	return 0;
2901da177e4SLinus Torvalds 
2911da177e4SLinus Torvalds error:
2921da177e4SLinus Torvalds 	BT_ERR("Can't register HIDP socket");
2931da177e4SLinus Torvalds 	proto_unregister(&hidp_proto);
2941da177e4SLinus Torvalds 	return err;
2951da177e4SLinus Torvalds }
2961da177e4SLinus Torvalds 
2971da177e4SLinus Torvalds void __exit hidp_cleanup_sockets(void)
2981da177e4SLinus Torvalds {
2991da177e4SLinus Torvalds 	if (bt_sock_unregister(BTPROTO_HIDP) < 0)
3001da177e4SLinus Torvalds 		BT_ERR("Can't unregister HIDP socket");
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds 	proto_unregister(&hidp_proto);
3031da177e4SLinus Torvalds }
304