Skip to content

Commit

Permalink
Bluetooth: Add blacklist support for incoming connections
Browse files Browse the repository at this point in the history
In some circumstances it could be desirable to reject incoming
connections on the baseband level. This patch adds this feature through
two new ioctl's: HCIBLOCKADDR and HCIUNBLOCKADDR. Both take a simple
Bluetooth address as a parameter. BDADDR_ANY can be used with
HCIUNBLOCKADDR to remove all devices from the blacklist.

Signed-off-by: Johan Hedberg <johan.hedberg@nokia.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
  • Loading branch information
Johan Hedberg authored and holtmann committed Jul 21, 2010
1 parent 95ffa97 commit f035856
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 1 deletion.
2 changes: 2 additions & 0 deletions fs/compat_ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1328,6 +1328,8 @@ COMPATIBLE_IOCTL(HCISETLINKPOL)
COMPATIBLE_IOCTL(HCISETLINKMODE)
COMPATIBLE_IOCTL(HCISETACLMTU)
COMPATIBLE_IOCTL(HCISETSCOMTU)
COMPATIBLE_IOCTL(HCIBLOCKADDR)
COMPATIBLE_IOCTL(HCIUNBLOCKADDR)
COMPATIBLE_IOCTL(HCIINQUIRY)
COMPATIBLE_IOCTL(HCIUARTSETPROTO)
COMPATIBLE_IOCTL(HCIUARTGETPROTO)
Expand Down
3 changes: 3 additions & 0 deletions include/net/bluetooth/hci.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ enum {
#define HCISETACLMTU _IOW('H', 227, int)
#define HCISETSCOMTU _IOW('H', 228, int)

#define HCIBLOCKADDR _IOW('H', 230, int)
#define HCIUNBLOCKADDR _IOW('H', 231, int)

#define HCIINQUIRY _IOR('H', 240, int)

/* HCI timeouts */
Expand Down
9 changes: 9 additions & 0 deletions include/net/bluetooth/hci_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ struct hci_conn_hash {
unsigned int sco_num;
};

struct bdaddr_list {
struct list_head list;
bdaddr_t bdaddr;
};

struct hci_dev {
struct list_head list;
spinlock_t lock;
Expand Down Expand Up @@ -127,6 +132,7 @@ struct hci_dev {

struct inquiry_cache inq_cache;
struct hci_conn_hash conn_hash;
struct bdaddr_list blacklist;

struct hci_dev_stats stat;

Expand Down Expand Up @@ -424,6 +430,9 @@ int hci_get_conn_info(struct hci_dev *hdev, void __user *arg);
int hci_get_auth_info(struct hci_dev *hdev, void __user *arg);
int hci_inquiry(void __user *arg);

struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr);
int hci_blacklist_clear(struct hci_dev *hdev);

void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);

int hci_recv_frame(struct sk_buff *skb);
Expand Down
3 changes: 3 additions & 0 deletions net/bluetooth/hci_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
hci_dev_lock_bh(hdev);
inquiry_cache_flush(hdev);
hci_conn_hash_flush(hdev);
hci_blacklist_clear(hdev);
hci_dev_unlock_bh(hdev);

hci_notify(hdev, HCI_DEV_DOWN);
Expand Down Expand Up @@ -923,6 +924,8 @@ int hci_register_dev(struct hci_dev *hdev)

hci_conn_hash_init(hdev);

INIT_LIST_HEAD(&hdev->blacklist.list);

memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));

atomic_set(&hdev->promisc, 0);
Expand Down
2 changes: 1 addition & 1 deletion net/bluetooth/hci_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -952,7 +952,7 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk

mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type);

if (mask & HCI_LM_ACCEPT) {
if ((mask & HCI_LM_ACCEPT) && !hci_blacklist_lookup(hdev, &ev->bdaddr)) {
/* Connection accepted */
struct inquiry_entry *ie;
struct hci_conn *conn;
Expand Down
90 changes: 90 additions & 0 deletions net/bluetooth/hci_sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,86 @@ static int hci_sock_release(struct socket *sock)
return 0;
}

struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
{
struct list_head *p;
struct bdaddr_list *blacklist = &hdev->blacklist;

list_for_each(p, &blacklist->list) {
struct bdaddr_list *b;

b = list_entry(p, struct bdaddr_list, list);

if (bacmp(bdaddr, &b->bdaddr) == 0)
return b;
}

return NULL;
}

static int hci_blacklist_add(struct hci_dev *hdev, void __user *arg)
{
bdaddr_t bdaddr;
struct bdaddr_list *entry;

if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
return -EFAULT;

if (bacmp(&bdaddr, BDADDR_ANY) == 0)
return -EBADF;

if (hci_blacklist_lookup(hdev, &bdaddr))
return -EEXIST;

entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL);
if (!entry)
return -ENOMEM;

bacpy(&entry->bdaddr, &bdaddr);

list_add(&entry->list, &hdev->blacklist.list);

return 0;
}

int hci_blacklist_clear(struct hci_dev *hdev)
{
struct list_head *p, *n;
struct bdaddr_list *blacklist = &hdev->blacklist;

list_for_each_safe(p, n, &blacklist->list) {
struct bdaddr_list *b;

b = list_entry(p, struct bdaddr_list, list);

list_del(p);
kfree(b);
}

return 0;
}

static int hci_blacklist_del(struct hci_dev *hdev, void __user *arg)
{
bdaddr_t bdaddr;
struct bdaddr_list *entry;

if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
return -EFAULT;

if (bacmp(&bdaddr, BDADDR_ANY) == 0)
return hci_blacklist_clear(hdev);

entry = hci_blacklist_lookup(hdev, &bdaddr);
if (!entry)
return -ENOENT;

list_del(&entry->list);
kfree(entry);

return 0;
}

/* Ioctls that require bound socket */
static inline int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg)
{
Expand Down Expand Up @@ -194,6 +274,16 @@ static inline int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsign
case HCIGETAUTHINFO:
return hci_get_auth_info(hdev, (void __user *) arg);

case HCIBLOCKADDR:
if (!capable(CAP_NET_ADMIN))
return -EACCES;
return hci_blacklist_add(hdev, (void __user *) arg);

case HCIUNBLOCKADDR:
if (!capable(CAP_NET_ADMIN))
return -EACCES;
return hci_blacklist_del(hdev, (void __user *) arg);

default:
if (hdev->ioctl)
return hdev->ioctl(hdev, cmd, arg);
Expand Down

0 comments on commit f035856

Please sign in to comment.