From 91aa35a5aa3540223066bf6b51c935418c63a35d Mon Sep 17 00:00:00 2001 From: Victor Shcherbatyuk Date: Thu, 15 Jan 2009 21:52:12 +0100 Subject: [PATCH 01/38] Bluetooth: Fix issue with return value of rfcomm_sock_sendmsg() In case of connection failures the rfcomm_sock_sendmsg() should return an error and not a 0 value. Signed-off-by: Victor Shcherbatyuk Signed-off-by: Marcel Holtmann --- net/bluetooth/rfcomm/sock.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index d3fc6fca38d0..ce505f2a755b 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -570,8 +570,11 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock, skb = sock_alloc_send_skb(sk, size + RFCOMM_SKB_RESERVE, msg->msg_flags & MSG_DONTWAIT, &err); - if (!skb) + if (!skb) { + if (sent == 0) + sent = err; break; + } skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE); err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); From d58daf42d29a3a4a4d4be46cf47ceee096789680 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 15 Jan 2009 21:52:14 +0100 Subject: [PATCH 02/38] Bluetooth: Preparation for usage of SOL_BLUETOOTH The socket option levels SOL_L2CAP, SOL_RFOMM and SOL_SCO are currently in use by various Bluetooth applications. Going forward the common option level SOL_BLUETOOTH should be used. This patch prepares the clean split of the old and new option levels while keeping everything backward compatibility. Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 51 +++++++++++++++++++++++++++++++++++-- net/bluetooth/rfcomm/sock.c | 51 +++++++++++++++++++++++++++++++++++-- net/bluetooth/sco.c | 27 +++++++++++++++++++- 3 files changed, 124 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index b93748e224ff..df1a95e185c6 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1106,7 +1106,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms return err; } -static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen) +static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, int optlen) { struct sock *sk = sock->sk; struct l2cap_options opts; @@ -1152,7 +1152,29 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch return err; } -static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) +static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen) +{ + struct sock *sk = sock->sk; + int err = 0; + + BT_DBG("sk %p", sk); + + if (level == SOL_L2CAP) + return l2cap_sock_setsockopt_old(sock, optname, optval, optlen); + + lock_sock(sk); + + switch (optname) { + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + return err; +} + +static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; struct l2cap_options opts; @@ -1208,6 +1230,31 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch return err; } +static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) +{ + struct sock *sk = sock->sk; + int len, err = 0; + + BT_DBG("sk %p", sk); + + if (level == SOL_L2CAP) + return l2cap_sock_getsockopt_old(sock, optname, optval, optlen); + + if (get_user(len, optlen)) + return -EFAULT; + + lock_sock(sk); + + switch (optname) { + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + return err; +} + static int l2cap_sock_shutdown(struct socket *sock, int how) { struct sock *sk = sock->sk; diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index ce505f2a755b..65dd7133d72b 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -713,7 +713,7 @@ static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock, return copied ? : err; } -static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen) +static int rfcomm_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, int optlen) { struct sock *sk = sock->sk; int err = 0; @@ -742,7 +742,29 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c return err; } -static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) +static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen) +{ + struct sock *sk = sock->sk; + int err = 0; + + BT_DBG("sk %p", sk); + + if (level == SOL_RFCOMM) + return rfcomm_sock_setsockopt_old(sock, optname, optval, optlen); + + lock_sock(sk); + + switch (optname) { + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + return err; +} + +static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; struct sock *l2cap_sk; @@ -788,6 +810,31 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c return err; } +static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) +{ + struct sock *sk = sock->sk; + int len, err = 0; + + BT_DBG("sk %p", sk); + + if (level == SOL_RFCOMM) + return rfcomm_sock_getsockopt_old(sock, optname, optval, optlen); + + if (get_user(len, optlen)) + return -EFAULT; + + lock_sock(sk); + + switch (optname) { + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + return err; +} + static int rfcomm_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk __maybe_unused = sock->sk; diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 46fd8bf9a690..dea40d4bb6f5 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -668,7 +668,7 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char return err; } -static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) +static int sco_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; struct sco_options opts; @@ -723,6 +723,31 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char return err; } +static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) +{ + struct sock *sk = sock->sk; + int len, err = 0; + + BT_DBG("sk %p", sk); + + if (level == SOL_SCO) + return sco_sock_getsockopt_old(sock, optname, optval, optlen); + + if (get_user(len, optlen)) + return -EFAULT; + + lock_sock(sk); + + switch (optname) { + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + return err; +} + static int sco_sock_release(struct socket *sock) { struct sock *sk = sock->sk; From c4f912e155504e94dd4f3d63c378dab0ff03dbda Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 15 Jan 2009 21:52:16 +0100 Subject: [PATCH 03/38] Bluetooth: Add global deferred socket parameter The L2CAP and RFCOMM applications require support for authorization and the ability of rejecting incoming connection requests. The socket interface is not really able to support this. This patch does the ground work for a socket option to defer connection setup. Setting this option allows calling of accept() and then the first read() will trigger the final connection setup. Calling close() would reject the connection. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/bluetooth.h | 3 +++ net/bluetooth/af_bluetooth.c | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index a04f8463ac7e..847e9e6df08b 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -53,6 +53,8 @@ #define SOL_SCO 17 #define SOL_RFCOMM 18 +#define BT_DEFER_SETUP 7 + #define BT_INFO(fmt, arg...) printk(KERN_INFO "Bluetooth: " fmt "\n" , ## arg) #define BT_ERR(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __func__ , ## arg) #define BT_DBG(fmt, arg...) pr_debug("%s: " fmt "\n" , __func__ , ## arg) @@ -108,6 +110,7 @@ struct bt_sock { bdaddr_t dst; struct list_head accept_q; struct sock *parent; + u32 defer_setup; }; struct bt_sock_list { diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 744ed3f07ef3..7c0031ff8cfb 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -217,7 +217,8 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) continue; } - if (sk->sk_state == BT_CONNECTED || !newsock) { + if (sk->sk_state == BT_CONNECTED || !newsock || + bt_sk(parent)->defer_setup) { bt_accept_unlink(sk); if (newsock) sock_graft(sk, newsock); @@ -232,7 +233,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) EXPORT_SYMBOL(bt_accept_dequeue); int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags) + struct msghdr *msg, size_t len, int flags) { int noblock = flags & MSG_DONTWAIT; struct sock *sk = sock->sk; @@ -275,6 +276,9 @@ static inline unsigned int bt_accept_poll(struct sock *parent) struct list_head *p, *n; struct sock *sk; + if (bt_sk(parent)->defer_setup) + return POLLIN | POLLRDNORM; + list_for_each_safe(p, n, &bt_sk(parent)->accept_q) { sk = (struct sock *) list_entry(p, struct bt_sock, accept_q); if (sk->sk_state == BT_CONNECTED) From bb23c0ab824653be4aa7dfca15b07b3059717004 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 15 Jan 2009 21:56:48 +0100 Subject: [PATCH 04/38] Bluetooth: Add support for deferring RFCOMM connection setup In order to decide if listening RFCOMM sockets should be accept()ed the BD_ADDR of the remote device needs to be known. This patch adds a socket option which defines a timeout for deferring the actual connection setup. The connection setup is done after reading from the socket for the first time. Until then writing to the socket returns ENOTCONN. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/rfcomm.h | 6 ++-- net/bluetooth/rfcomm/core.c | 56 +++++++++++++++++++++++++--------- net/bluetooth/rfcomm/sock.c | 44 +++++++++++++++++++++++++- 3 files changed, 88 insertions(+), 18 deletions(-) diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index 4dc8d92a4638..71b45f459687 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -185,6 +185,7 @@ struct rfcomm_dlc { u8 out; u32 link_mode; + u32 defer_setup; uint mtu; uint cfc; @@ -202,10 +203,11 @@ struct rfcomm_dlc { #define RFCOMM_RX_THROTTLED 0 #define RFCOMM_TX_THROTTLED 1 #define RFCOMM_TIMED_OUT 2 -#define RFCOMM_MSC_PENDING 3 +#define RFCOMM_MSC_PENDING 3 #define RFCOMM_AUTH_PENDING 4 #define RFCOMM_AUTH_ACCEPT 5 #define RFCOMM_AUTH_REJECT 6 +#define RFCOMM_DEFER_SETUP 7 /* Scheduling flags and events */ #define RFCOMM_SCHED_STATE 0 @@ -239,6 +241,7 @@ int rfcomm_dlc_close(struct rfcomm_dlc *d, int reason); int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb); int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig); int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig); +void rfcomm_dlc_accept(struct rfcomm_dlc *d); #define rfcomm_dlc_lock(d) spin_lock(&d->lock) #define rfcomm_dlc_unlock(d) spin_unlock(&d->lock) @@ -333,7 +336,6 @@ struct rfcomm_dev_req { bdaddr_t src; bdaddr_t dst; u8 channel; - }; struct rfcomm_dev_info { diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index acd84fd524b8..edee49e00fbf 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -421,9 +421,16 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err) d, d->state, d->dlci, err, s); switch (d->state) { - case BT_CONNECTED: - case BT_CONFIG: case BT_CONNECT: + case BT_CONFIG: + if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { + set_bit(RFCOMM_AUTH_REJECT, &d->flags); + rfcomm_schedule(RFCOMM_SCHED_AUTH); + break; + } + /* Fall through */ + + case BT_CONNECTED: d->state = BT_DISCONN; if (skb_queue_empty(&d->tx_queue)) { rfcomm_send_disc(s, d->dlci); @@ -434,6 +441,14 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err) } break; + case BT_OPEN: + if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { + set_bit(RFCOMM_AUTH_REJECT, &d->flags); + rfcomm_schedule(RFCOMM_SCHED_AUTH); + break; + } + /* Fall through */ + default: rfcomm_dlc_clear_timer(d); @@ -1162,7 +1177,7 @@ static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci) return 0; } -static void rfcomm_dlc_accept(struct rfcomm_dlc *d) +void rfcomm_dlc_accept(struct rfcomm_dlc *d) { struct sock *sk = d->session->sock->sk; @@ -1181,6 +1196,20 @@ static void rfcomm_dlc_accept(struct rfcomm_dlc *d) rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig); } +static void rfcomm_check_accept(struct rfcomm_dlc *d) +{ + if (rfcomm_check_link_mode(d)) { + set_bit(RFCOMM_AUTH_PENDING, &d->flags); + rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); + } else { + if (d->defer_setup) { + set_bit(RFCOMM_DEFER_SETUP, &d->flags); + rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); + } else + rfcomm_dlc_accept(d); + } +} + static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci) { struct rfcomm_dlc *d; @@ -1203,11 +1232,7 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci) if (d) { if (d->state == BT_OPEN) { /* DLC was previously opened by PN request */ - if (rfcomm_check_link_mode(d)) { - set_bit(RFCOMM_AUTH_PENDING, &d->flags); - rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); - } else - rfcomm_dlc_accept(d); + rfcomm_check_accept(d); } return 0; } @@ -1219,11 +1244,7 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci) d->addr = __addr(s->initiator, dlci); rfcomm_dlc_link(s, d); - if (rfcomm_check_link_mode(d)) { - set_bit(RFCOMM_AUTH_PENDING, &d->flags); - rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); - } else - rfcomm_dlc_accept(d); + rfcomm_check_accept(d); } else { rfcomm_send_dm(s, dlci); } @@ -1717,8 +1738,13 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s) if (d->out) { rfcomm_send_pn(s, 1, d); rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT); - } else - rfcomm_dlc_accept(d); + } else { + if (d->defer_setup) { + set_bit(RFCOMM_DEFER_SETUP, &d->flags); + rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); + } else + rfcomm_dlc_accept(d); + } if (d->link_mode & RFCOMM_LM_SECURE) { struct sock *sk = s->sock->sk; hci_conn_change_link_key(l2cap_pi(sk)->conn->hcon); diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 65dd7133d72b..d37a829a81e4 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -262,8 +262,10 @@ static void rfcomm_sock_init(struct sock *sk, struct sock *parent) if (parent) { sk->sk_type = parent->sk_type; pi->link_mode = rfcomm_pi(parent)->link_mode; + pi->dlc->defer_setup = bt_sk(parent)->defer_setup; } else { pi->link_mode = 0; + pi->dlc->defer_setup = 0; } pi->dlc->link_mode = pi->link_mode; @@ -554,6 +556,9 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct sk_buff *skb; int sent = 0; + if (test_bit(RFCOMM_DEFER_SETUP, &d->flags)) + return -ENOTCONN; + if (msg->msg_flags & MSG_OOB) return -EOPNOTSUPP; @@ -633,10 +638,16 @@ static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t size, int flags) { struct sock *sk = sock->sk; + struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; int err = 0; size_t target, copied = 0; long timeo; + if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { + rfcomm_dlc_accept(d); + return 0; + } + if (flags & MSG_OOB) return -EOPNOTSUPP; @@ -746,6 +757,7 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c { struct sock *sk = sock->sk; int err = 0; + u32 opt; BT_DBG("sk %p", sk); @@ -755,6 +767,20 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c lock_sock(sk); switch (optname) { + case BT_DEFER_SETUP: + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { + err = -EINVAL; + break; + } + + if (get_user(opt, (u32 __user *) optval)) { + err = -EFAULT; + break; + } + + bt_sk(sk)->defer_setup = opt; + break; + default: err = -ENOPROTOOPT; break; @@ -785,7 +811,8 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u break; case RFCOMM_CONNINFO: - if (sk->sk_state != BT_CONNECTED) { + if (sk->sk_state != BT_CONNECTED && + !rfcomm_pi(sk)->dlc->defer_setup) { err = -ENOTCONN; break; } @@ -826,6 +853,17 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c lock_sock(sk); switch (optname) { + case BT_DEFER_SETUP: + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { + err = -EINVAL; + break; + } + + if (put_user(bt_sk(sk)->defer_setup, (u32 __user *) optval)) + err = -EFAULT; + + break; + default: err = -ENOPROTOOPT; break; @@ -938,6 +976,10 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc * done: bh_unlock_sock(parent); + + if (bt_sk(parent)->defer_setup) + parent->sk_state_change(parent); + return result; } From f66dc81f44d918ee1aa1a9d821bb2f25c7592bc0 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 15 Jan 2009 21:57:00 +0100 Subject: [PATCH 05/38] Bluetooth: Add support for deferring L2CAP connection setup In order to decide if listening L2CAP sockets should be accept()ed the BD_ADDR of the remote device needs to be known. This patch adds a socket option which defines a timeout for deferring the actual connection setup. Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 110 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 101 insertions(+), 9 deletions(-) diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index df1a95e185c6..123efb46d3f5 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -372,9 +372,17 @@ static void l2cap_conn_start(struct l2cap_conn *conn) rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); if (l2cap_check_link_mode(sk)) { - sk->sk_state = BT_CONFIG; - rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS); - rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); + if (bt_sk(sk)->defer_setup) { + struct sock *parent = bt_sk(sk)->parent; + rsp.result = cpu_to_le16(L2CAP_CR_PEND); + rsp.status = cpu_to_le16(L2CAP_CS_AUTHOR_PEND); + parent->sk_data_ready(parent, 0); + + } else { + sk->sk_state = BT_CONFIG; + rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS); + rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); + } } else { rsp.result = cpu_to_le16(L2CAP_CR_PEND); rsp.status = cpu_to_le16(L2CAP_CS_AUTHEN_PEND); @@ -608,7 +616,6 @@ static void __l2cap_sock_close(struct sock *sk, int reason) case BT_CONNECTED: case BT_CONFIG: - case BT_CONNECT2: if (sk->sk_type == SOCK_SEQPACKET) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct l2cap_disconn_req req; @@ -624,6 +631,27 @@ static void __l2cap_sock_close(struct sock *sk, int reason) l2cap_chan_del(sk, reason); break; + case BT_CONNECT2: + if (sk->sk_type == SOCK_SEQPACKET) { + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct l2cap_conn_rsp rsp; + __u16 result; + + if (bt_sk(sk)->defer_setup) + result = L2CAP_CR_SEC_BLOCK; + else + result = L2CAP_CR_BAD_PSM; + + rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); + rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); + rsp.result = cpu_to_le16(result); + rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); + l2cap_send_cmd(conn, l2cap_pi(sk)->ident, + L2CAP_CONN_RSP, sizeof(rsp), &rsp); + } else + l2cap_chan_del(sk, reason); + break; + case BT_CONNECT: case BT_DISCONN: l2cap_chan_del(sk, reason); @@ -653,6 +681,8 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) if (parent) { sk->sk_type = parent->sk_type; + bt_sk(sk)->defer_setup = bt_sk(parent)->defer_setup; + pi->imtu = l2cap_pi(parent)->imtu; pi->omtu = l2cap_pi(parent)->omtu; pi->link_mode = l2cap_pi(parent)->link_mode; @@ -1106,6 +1136,33 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms return err; } +static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags) +{ + struct sock *sk = sock->sk; + + lock_sock(sk); + + if (sk->sk_state == BT_CONNECT2 && bt_sk(sk)->defer_setup) { + struct l2cap_conn_rsp rsp; + + sk->sk_state = BT_CONFIG; + + rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); + rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); + rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS); + rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); + l2cap_send_cmd(l2cap_pi(sk)->conn, l2cap_pi(sk)->ident, + L2CAP_CONN_RSP, sizeof(rsp), &rsp); + + release_sock(sk); + return 0; + } + + release_sock(sk); + + return bt_sock_recvmsg(iocb, sock, msg, len, flags); +} + static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, int optlen) { struct sock *sk = sock->sk; @@ -1156,6 +1213,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch { struct sock *sk = sock->sk; int err = 0; + u32 opt; BT_DBG("sk %p", sk); @@ -1165,6 +1223,20 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch lock_sock(sk); switch (optname) { + case BT_DEFER_SETUP: + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { + err = -EINVAL; + break; + } + + if (get_user(opt, (u32 __user *) optval)) { + err = -EFAULT; + break; + } + + bt_sk(sk)->defer_setup = opt; + break; + default: err = -ENOPROTOOPT; break; @@ -1207,7 +1279,9 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us break; case L2CAP_CONNINFO: - if (sk->sk_state != BT_CONNECTED) { + if (sk->sk_state != BT_CONNECTED && + !(sk->sk_state == BT_CONNECT2 && + bt_sk(sk)->defer_setup)) { err = -ENOTCONN; break; } @@ -1246,6 +1320,17 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch lock_sock(sk); switch (optname) { + case BT_DEFER_SETUP: + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { + err = -EINVAL; + break; + } + + if (put_user(bt_sk(sk)->defer_setup, (u32 __user *) optval)) + err = -EFAULT; + + break; + default: err = -ENOPROTOOPT; break; @@ -1670,9 +1755,16 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) { if (l2cap_check_link_mode(sk)) { - sk->sk_state = BT_CONFIG; - result = L2CAP_CR_SUCCESS; - status = L2CAP_CS_NO_INFO; + if (bt_sk(sk)->defer_setup) { + sk->sk_state = BT_CONNECT2; + result = L2CAP_CR_PEND; + status = L2CAP_CS_AUTHOR_PEND; + parent->sk_data_ready(parent, 0); + } else { + sk->sk_state = BT_CONFIG; + result = L2CAP_CR_SUCCESS; + status = L2CAP_CS_NO_INFO; + } } else { sk->sk_state = BT_CONNECT2; result = L2CAP_CR_PEND; @@ -2494,7 +2586,7 @@ static const struct proto_ops l2cap_sock_ops = { .accept = l2cap_sock_accept, .getname = l2cap_sock_getname, .sendmsg = l2cap_sock_sendmsg, - .recvmsg = bt_sock_recvmsg, + .recvmsg = l2cap_sock_recvmsg, .poll = bt_sock_poll, .ioctl = bt_sock_ioctl, .mmap = sock_no_mmap, From 71aeeaa1fd88fe7446391e0553336f0e0c2cfe6a Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 15 Jan 2009 21:57:02 +0100 Subject: [PATCH 06/38] Bluetooth: Reject incoming SCO connections without listeners All SCO and eSCO connection are auto-accepted no matter if there is a corresponding listening socket for them. This patch changes this and connection requests for SCO and eSCO without any socket are rejected. Signed-off-by: Marcel Holtmann --- net/bluetooth/sco.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index dea40d4bb6f5..71df982c09c9 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -857,10 +857,30 @@ static void sco_conn_ready(struct sco_conn *conn) /* ----- SCO interface with lower layer (HCI) ----- */ static int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type) { + register struct sock *sk; + struct hlist_node *node; + int lm = 0; + + if (type != SCO_LINK && type != ESCO_LINK) + return 0; + BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr)); - /* Always accept connection */ - return HCI_LM_ACCEPT; + /* Find listening sockets */ + read_lock(&sco_sk_list.lock); + sk_for_each(sk, node, &sco_sk_list.head) { + if (sk->sk_state != BT_LISTEN) + continue; + + if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) || + !bacmp(&bt_sk(sk)->src, BDADDR_ANY)) { + lm |= HCI_LM_ACCEPT; + break; + } + } + read_unlock(&sco_sk_list.lock); + + return lm; } static int sco_connect_cfm(struct hci_conn *hcon, __u8 status) From c89b6e6bda4c8021195778f47567d0cc9dbfe7ec Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 15 Jan 2009 21:57:03 +0100 Subject: [PATCH 07/38] Bluetooth: Fix SCO state handling for incoming connections When the remote device supports only SCO connections, on receipt of the HCI_EV_CONN_COMPLETE event packet, the connect state is changed to BT_CONNECTED, but the socket state is not updated. Hence, the connect() call times out even though the SCO connection has been successfully established. Based on a report by Jaikumar Ganesh Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index f91ba690f5d2..beea9dbb6562 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -914,7 +914,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s if (ev->status) { hci_proto_connect_cfm(conn, ev->status); hci_conn_del(conn); - } + } else if (ev->link_type != ACL_LINK) + hci_proto_connect_cfm(conn, ev->status); unlock: hci_dev_unlock(hdev); From 8c1b235594fbab9a13240a1dac12ea9fd99b6440 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 15 Jan 2009 21:58:04 +0100 Subject: [PATCH 08/38] Bluetooth: Add enhanced security model for Simple Pairing The current security model is based around the flags AUTH, ENCRYPT and SECURE. Starting with support for the Bluetooth 2.1 specification this is no longer sufficient. The different security levels are now defined as SDP, LOW, MEDIUM and SECURE. Previously it was possible to set each security independently, but this actually doesn't make a lot of sense. For Bluetooth the encryption depends on a previous successful authentication. Also you can only update your existing link key if you successfully created at least one before. And of course the update of link keys without having proper encryption in place is a security issue. The new security levels from the Bluetooth 2.1 specification are now used internally. All old settings are mapped to the new values and this way it ensures that old applications still work. The only limitation is that it is no longer possible to set authentication without also enabling encryption. No application should have done this anyway since this is actually a security issue. Without encryption the integrity of the authentication can't be guaranteed. As default for a new L2CAP or RFCOMM connection, the LOW security level is used. The only exception here are the service discovery sessions on PSM 1 where SDP level is used. To have similar security strength as with a Bluetooth 2.0 and before combination key, the MEDIUM level should be used. This is according to the Bluetooth specification. The MEDIUM level will not require any kind of man-in-the-middle (MITM) protection. Only the HIGH security level will require this. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/bluetooth.h | 9 ++ include/net/bluetooth/hci_core.h | 56 ++++++++----- net/bluetooth/hci_conn.c | 45 ++++++---- net/bluetooth/hci_event.c | 3 +- net/bluetooth/l2cap.c | 134 +++++++++--------------------- net/bluetooth/rfcomm/core.c | 81 ++++++------------ net/bluetooth/sco.c | 2 +- 7 files changed, 139 insertions(+), 191 deletions(-) diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 847e9e6df08b..3ad5390a4dd5 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -53,6 +53,15 @@ #define SOL_SCO 17 #define SOL_RFCOMM 18 +#define BT_SECURITY 4 +struct bt_security { + __u8 level; +}; +#define BT_SECURITY_SDP 0 +#define BT_SECURITY_LOW 1 +#define BT_SECURITY_MEDIUM 2 +#define BT_SECURITY_HIGH 3 + #define BT_DEFER_SETUP 7 #define BT_INFO(fmt, arg...) printk(KERN_INFO "Bluetooth: " fmt "\n" , ## arg) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 46a43b721dd6..4b14972c1694 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -169,6 +169,7 @@ struct hci_conn { __u16 link_policy; __u32 link_mode; __u8 auth_type; + __u8 sec_level; __u8 power_save; unsigned long pend; @@ -325,12 +326,11 @@ int hci_conn_del(struct hci_conn *conn); void hci_conn_hash_flush(struct hci_dev *hdev); void hci_conn_check_pending(struct hci_dev *hdev); -struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 auth_type); +struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type); int hci_conn_check_link_mode(struct hci_conn *conn); -int hci_conn_auth(struct hci_conn *conn); -int hci_conn_encrypt(struct hci_conn *conn); +int hci_conn_security(struct hci_conn *conn, __u8 sec_level); int hci_conn_change_link_key(struct hci_conn *conn); -int hci_conn_switch_role(struct hci_conn *conn, uint8_t role); +int hci_conn_switch_role(struct hci_conn *conn, __u8 role); void hci_conn_enter_active_mode(struct hci_conn *conn); void hci_conn_enter_sniff_mode(struct hci_conn *conn); @@ -470,26 +470,25 @@ void hci_conn_del_sysfs(struct hci_conn *conn); /* ----- HCI protocols ----- */ struct hci_proto { - char *name; + char *name; unsigned int id; unsigned long flags; void *priv; - int (*connect_ind) (struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type); + int (*connect_ind) (struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type); int (*connect_cfm) (struct hci_conn *conn, __u8 status); int (*disconn_ind) (struct hci_conn *conn, __u8 reason); int (*recv_acldata) (struct hci_conn *conn, struct sk_buff *skb, __u16 flags); int (*recv_scodata) (struct hci_conn *conn, struct sk_buff *skb); - int (*auth_cfm) (struct hci_conn *conn, __u8 status); - int (*encrypt_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt); + int (*security_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt); }; static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type) { register struct hci_proto *hp; int mask = 0; - + hp = hci_proto[HCI_PROTO_L2CAP]; if (hp && hp->connect_ind) mask |= hp->connect_ind(hdev, bdaddr, type); @@ -530,14 +529,20 @@ static inline void hci_proto_disconn_ind(struct hci_conn *conn, __u8 reason) static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status) { register struct hci_proto *hp; + __u8 encrypt; + + if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) + return; + + encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00; hp = hci_proto[HCI_PROTO_L2CAP]; - if (hp && hp->auth_cfm) - hp->auth_cfm(conn, status); + if (hp && hp->security_cfm) + hp->security_cfm(conn, status, encrypt); hp = hci_proto[HCI_PROTO_SCO]; - if (hp && hp->auth_cfm) - hp->auth_cfm(conn, status); + if (hp && hp->security_cfm) + hp->security_cfm(conn, status, encrypt); } static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encrypt) @@ -545,12 +550,12 @@ static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status, __u register struct hci_proto *hp; hp = hci_proto[HCI_PROTO_L2CAP]; - if (hp && hp->encrypt_cfm) - hp->encrypt_cfm(conn, status, encrypt); + if (hp && hp->security_cfm) + hp->security_cfm(conn, status, encrypt); hp = hci_proto[HCI_PROTO_SCO]; - if (hp && hp->encrypt_cfm) - hp->encrypt_cfm(conn, status, encrypt); + if (hp && hp->security_cfm) + hp->security_cfm(conn, status, encrypt); } int hci_register_proto(struct hci_proto *hproto); @@ -562,8 +567,7 @@ struct hci_cb { char *name; - void (*auth_cfm) (struct hci_conn *conn, __u8 status); - void (*encrypt_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt); + void (*security_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt); void (*key_change_cfm) (struct hci_conn *conn, __u8 status); void (*role_switch_cfm) (struct hci_conn *conn, __u8 status, __u8 role); }; @@ -571,14 +575,20 @@ struct hci_cb { static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) { struct list_head *p; + __u8 encrypt; hci_proto_auth_cfm(conn, status); + if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) + return; + + encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00; + read_lock_bh(&hci_cb_list_lock); list_for_each(p, &hci_cb_list) { struct hci_cb *cb = list_entry(p, struct hci_cb, list); - if (cb->auth_cfm) - cb->auth_cfm(conn, status); + if (cb->security_cfm) + cb->security_cfm(conn, status, encrypt); } read_unlock_bh(&hci_cb_list_lock); } @@ -592,8 +602,8 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encr read_lock_bh(&hci_cb_list_lock); list_for_each(p, &hci_cb_list) { struct hci_cb *cb = list_entry(p, struct hci_cb, list); - if (cb->encrypt_cfm) - cb->encrypt_cfm(conn, status, encrypt); + if (cb->security_cfm) + cb->security_cfm(conn, status, encrypt); } read_unlock_bh(&hci_cb_list_lock); } diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index a4a789f24c8d..98f97a1e9bbb 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -325,7 +325,7 @@ EXPORT_SYMBOL(hci_get_route); /* Create SCO or ACL connection. * Device _must_ be locked */ -struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 auth_type) +struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type) { struct hci_conn *acl; struct hci_conn *sco; @@ -340,6 +340,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 hci_conn_hold(acl); if (acl->state == BT_OPEN || acl->state == BT_CLOSED) { + acl->sec_level = sec_level; acl->auth_type = auth_type; hci_acl_connect(acl); } @@ -385,16 +386,17 @@ int hci_conn_check_link_mode(struct hci_conn *conn) EXPORT_SYMBOL(hci_conn_check_link_mode); /* Authenticate remote device */ -int hci_conn_auth(struct hci_conn *conn) +static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level) { BT_DBG("conn %p", conn); - if (conn->ssp_mode > 0 && conn->hdev->ssp_mode > 0) { - if (!(conn->auth_type & 0x01)) { - conn->auth_type |= 0x01; - conn->link_mode &= ~HCI_LM_AUTH; - } - } + if (sec_level > conn->sec_level) + conn->link_mode &= ~HCI_LM_AUTH; + + conn->sec_level = sec_level; + + if (sec_level == BT_SECURITY_HIGH) + conn->auth_type |= 0x01; if (conn->link_mode & HCI_LM_AUTH) return 1; @@ -405,31 +407,42 @@ int hci_conn_auth(struct hci_conn *conn) hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); } + return 0; } -EXPORT_SYMBOL(hci_conn_auth); -/* Enable encryption */ -int hci_conn_encrypt(struct hci_conn *conn) +/* Enable security */ +int hci_conn_security(struct hci_conn *conn, __u8 sec_level) { BT_DBG("conn %p", conn); + if (sec_level == BT_SECURITY_SDP) + return 1; + + if (sec_level == BT_SECURITY_LOW) { + if (conn->ssp_mode > 0 && conn->hdev->ssp_mode > 0) + return hci_conn_auth(conn, sec_level); + else + return 1; + } + if (conn->link_mode & HCI_LM_ENCRYPT) - return hci_conn_auth(conn); + return hci_conn_auth(conn, sec_level); if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) return 0; - if (hci_conn_auth(conn)) { + if (hci_conn_auth(conn, sec_level)) { struct hci_cp_set_conn_encrypt cp; cp.handle = cpu_to_le16(conn->handle); cp.encrypt = 1; hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), &cp); } + return 0; } -EXPORT_SYMBOL(hci_conn_encrypt); +EXPORT_SYMBOL(hci_conn_security); /* Change link key */ int hci_conn_change_link_key(struct hci_conn *conn) @@ -442,12 +455,13 @@ int hci_conn_change_link_key(struct hci_conn *conn) hci_send_cmd(conn->hdev, HCI_OP_CHANGE_CONN_LINK_KEY, sizeof(cp), &cp); } + return 0; } EXPORT_SYMBOL(hci_conn_change_link_key); /* Switch role */ -int hci_conn_switch_role(struct hci_conn *conn, uint8_t role) +int hci_conn_switch_role(struct hci_conn *conn, __u8 role) { BT_DBG("conn %p", conn); @@ -460,6 +474,7 @@ int hci_conn_switch_role(struct hci_conn *conn, uint8_t role) cp.role = role; hci_send_cmd(conn->hdev, HCI_OP_SWITCH_ROLE, sizeof(cp), &cp); } + return 0; } EXPORT_SYMBOL(hci_conn_switch_role); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index beea9dbb6562..014fc8b320ba 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1601,7 +1601,8 @@ static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_b if (conn->state == BT_CONFIG) { if (!ev->status && hdev->ssp_mode > 0 && - conn->ssp_mode > 0 && conn->out) { + conn->ssp_mode > 0 && conn->out && + conn->sec_level != BT_SECURITY_SDP) { struct hci_cp_auth_requested cp; cp.handle = ev->handle; hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 123efb46d3f5..eadf09231866 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -263,12 +263,17 @@ static inline int l2cap_check_link_mode(struct sock *sk) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; - if ((l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) || - (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE)) - return hci_conn_encrypt(conn->hcon); + if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) + return hci_conn_security(conn->hcon, BT_SECURITY_HIGH); + + if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) + return hci_conn_security(conn->hcon, BT_SECURITY_MEDIUM); if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH) - return hci_conn_auth(conn->hcon); + return hci_conn_security(conn->hcon, BT_SECURITY_LOW); + + if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) + return hci_conn_security(conn->hcon, BT_SECURITY_SDP); return 1; } @@ -803,6 +808,7 @@ static int l2cap_do_connect(struct sock *sk) struct l2cap_conn *conn; struct hci_conn *hcon; struct hci_dev *hdev; + __u8 sec_level; __u8 auth_type; int err = 0; @@ -815,21 +821,37 @@ static int l2cap_do_connect(struct sock *sk) err = -ENOMEM; - if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH || - l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT || - l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) { - if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) - auth_type = HCI_AT_NO_BONDING_MITM; + if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) + sec_level = BT_SECURITY_HIGH; + else if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) + sec_level = BT_SECURITY_SDP; + else if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) + sec_level = BT_SECURITY_MEDIUM; + else + sec_level = BT_SECURITY_LOW; + + if (sk->sk_type == SOCK_RAW) { + if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) + auth_type = HCI_AT_DEDICATED_BONDING_MITM; + else if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) + auth_type = HCI_AT_DEDICATED_BONDING; else - auth_type = HCI_AT_GENERAL_BONDING_MITM; - } else { - if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) auth_type = HCI_AT_NO_BONDING; + } else if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) { + if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) + auth_type = HCI_AT_NO_BONDING_MITM; else + auth_type = HCI_AT_NO_BONDING; + } else { + if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) + auth_type = HCI_AT_GENERAL_BONDING_MITM; + else if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) auth_type = HCI_AT_GENERAL_BONDING; + else + auth_type = HCI_AT_NO_BONDING; } - hcon = hci_connect(hdev, ACL_LINK, dst, auth_type); + hcon = hci_connect(hdev, ACL_LINK, dst, sec_level, auth_type); if (!hcon) goto done; @@ -1402,11 +1424,6 @@ static void l2cap_chan_ready(struct sock *sk) */ parent->sk_data_ready(parent, 0); } - - if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) { - struct l2cap_conn *conn = l2cap_pi(sk)->conn; - hci_conn_change_link_key(conn->hcon); - } } /* Copy frame to all raw sockets on that connection */ @@ -2323,77 +2340,7 @@ static int l2cap_disconn_ind(struct hci_conn *hcon, u8 reason) return 0; } -static int l2cap_auth_cfm(struct hci_conn *hcon, u8 status) -{ - struct l2cap_chan_list *l; - struct l2cap_conn *conn = hcon->l2cap_data; - struct sock *sk; - - if (!conn) - return 0; - - l = &conn->chan_list; - - BT_DBG("conn %p", conn); - - read_lock(&l->lock); - - for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { - struct l2cap_pinfo *pi = l2cap_pi(sk); - - bh_lock_sock(sk); - - if ((pi->link_mode & (L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)) && - !(hcon->link_mode & HCI_LM_ENCRYPT) && - !status) { - bh_unlock_sock(sk); - continue; - } - - if (sk->sk_state == BT_CONNECT) { - if (!status) { - struct l2cap_conn_req req; - req.scid = cpu_to_le16(l2cap_pi(sk)->scid); - req.psm = l2cap_pi(sk)->psm; - - l2cap_pi(sk)->ident = l2cap_get_ident(conn); - - l2cap_send_cmd(conn, l2cap_pi(sk)->ident, - L2CAP_CONN_REQ, sizeof(req), &req); - } else { - l2cap_sock_clear_timer(sk); - l2cap_sock_set_timer(sk, HZ / 10); - } - } else if (sk->sk_state == BT_CONNECT2) { - struct l2cap_conn_rsp rsp; - __u16 result; - - if (!status) { - sk->sk_state = BT_CONFIG; - result = L2CAP_CR_SUCCESS; - } else { - sk->sk_state = BT_DISCONN; - l2cap_sock_set_timer(sk, HZ / 10); - result = L2CAP_CR_SEC_BLOCK; - } - - rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); - rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); - rsp.result = cpu_to_le16(result); - rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); - l2cap_send_cmd(conn, l2cap_pi(sk)->ident, - L2CAP_CONN_RSP, sizeof(rsp), &rsp); - } - - bh_unlock_sock(sk); - } - - read_unlock(&l->lock); - - return 0; -} - -static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) +static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) { struct l2cap_chan_list *l; struct l2cap_conn *conn = hcon->l2cap_data; @@ -2413,10 +2360,10 @@ static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) bh_lock_sock(sk); - if ((pi->link_mode & (L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)) && + if (!status && encrypt == 0x00 && + (pi->link_mode & L2CAP_LM_SECURE) && (sk->sk_state == BT_CONNECTED || - sk->sk_state == BT_CONFIG) && - !status && encrypt == 0x00) { + sk->sk_state == BT_CONFIG)) { __l2cap_sock_close(sk, ECONNREFUSED); bh_unlock_sock(sk); continue; @@ -2608,8 +2555,7 @@ static struct hci_proto l2cap_hci_proto = { .connect_ind = l2cap_connect_ind, .connect_cfm = l2cap_connect_cfm, .disconn_ind = l2cap_disconn_ind, - .auth_cfm = l2cap_auth_cfm, - .encrypt_cfm = l2cap_encrypt_cfm, + .security_cfm = l2cap_security_cfm, .recv_acldata = l2cap_recv_acldata }; diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index edee49e00fbf..68f70c5270c6 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -226,16 +226,18 @@ static int rfcomm_l2sock_create(struct socket **sock) static inline int rfcomm_check_link_mode(struct rfcomm_dlc *d) { struct sock *sk = d->session->sock->sk; + struct l2cap_conn *conn = l2cap_pi(sk)->conn; - if (d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) { - if (!hci_conn_encrypt(l2cap_pi(sk)->conn->hcon)) - return 1; - } else if (d->link_mode & RFCOMM_LM_AUTH) { - if (!hci_conn_auth(l2cap_pi(sk)->conn->hcon)) - return 1; - } + if (d->link_mode & RFCOMM_LM_SECURE) + return hci_conn_security(conn->hcon, BT_SECURITY_HIGH); - return 0; + if (d->link_mode & RFCOMM_LM_ENCRYPT) + return hci_conn_security(conn->hcon, BT_SECURITY_MEDIUM); + + if (d->link_mode & RFCOMM_LM_AUTH) + return hci_conn_security(conn->hcon, BT_SECURITY_LOW); + + return 1; } /* ---- RFCOMM DLCs ---- */ @@ -389,9 +391,9 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, if (s->state == BT_CONNECTED) { if (rfcomm_check_link_mode(d)) - set_bit(RFCOMM_AUTH_PENDING, &d->flags); - else rfcomm_send_pn(s, 1, d); + else + set_bit(RFCOMM_AUTH_PENDING, &d->flags); } rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT); @@ -1199,14 +1201,14 @@ void rfcomm_dlc_accept(struct rfcomm_dlc *d) static void rfcomm_check_accept(struct rfcomm_dlc *d) { if (rfcomm_check_link_mode(d)) { - set_bit(RFCOMM_AUTH_PENDING, &d->flags); - rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); - } else { if (d->defer_setup) { set_bit(RFCOMM_DEFER_SETUP, &d->flags); rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); } else rfcomm_dlc_accept(d); + } else { + set_bit(RFCOMM_AUTH_PENDING, &d->flags); + rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); } } @@ -1659,10 +1661,11 @@ static void rfcomm_process_connect(struct rfcomm_session *s) if (d->state == BT_CONFIG) { d->mtu = s->mtu; if (rfcomm_check_link_mode(d)) { + rfcomm_send_pn(s, 1, d); + } else { set_bit(RFCOMM_AUTH_PENDING, &d->flags); rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); - } else - rfcomm_send_pn(s, 1, d); + } } } } @@ -1973,42 +1976,7 @@ static int rfcomm_run(void *unused) return 0; } -static void rfcomm_auth_cfm(struct hci_conn *conn, u8 status) -{ - struct rfcomm_session *s; - struct rfcomm_dlc *d; - struct list_head *p, *n; - - BT_DBG("conn %p status 0x%02x", conn, status); - - s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst); - if (!s) - return; - - rfcomm_session_hold(s); - - list_for_each_safe(p, n, &s->dlcs) { - d = list_entry(p, struct rfcomm_dlc, list); - - if ((d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) && - !(conn->link_mode & HCI_LM_ENCRYPT) && !status) - continue; - - if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags)) - continue; - - if (!status) - set_bit(RFCOMM_AUTH_ACCEPT, &d->flags); - else - set_bit(RFCOMM_AUTH_REJECT, &d->flags); - } - - rfcomm_session_put(s); - - rfcomm_schedule(RFCOMM_SCHED_AUTH); -} - -static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt) +static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt) { struct rfcomm_session *s; struct rfcomm_dlc *d; @@ -2025,10 +1993,10 @@ static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt) list_for_each_safe(p, n, &s->dlcs) { d = list_entry(p, struct rfcomm_dlc, list); - if ((d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) && + if (!status && encrypt == 0x00 && + (d->link_mode & RFCOMM_LM_ENCRYPT) && (d->state == BT_CONNECTED || - d->state == BT_CONFIG) && - !status && encrypt == 0x00) { + d->state == BT_CONFIG)) { __rfcomm_dlc_close(d, ECONNREFUSED); continue; } @@ -2036,7 +2004,7 @@ static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt) if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags)) continue; - if (!status && encrypt) + if (!status) set_bit(RFCOMM_AUTH_ACCEPT, &d->flags); else set_bit(RFCOMM_AUTH_REJECT, &d->flags); @@ -2049,8 +2017,7 @@ static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt) static struct hci_cb rfcomm_cb = { .name = "RFCOMM", - .auth_cfm = rfcomm_auth_cfm, - .encrypt_cfm = rfcomm_encrypt_cfm + .security_cfm = rfcomm_security_cfm }; static ssize_t rfcomm_dlc_sysfs_show(struct class *dev, char *buf) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 71df982c09c9..7f10f97cd697 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -195,7 +195,7 @@ static int sco_connect(struct sock *sk) else type = SCO_LINK; - hcon = hci_connect(hdev, type, dst, HCI_AT_NO_BONDING); + hcon = hci_connect(hdev, type, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING); if (!hcon) goto done; From 2af6b9d518ddfbc4d6990d5f9c9b1a05341c1cef Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 15 Jan 2009 21:58:38 +0100 Subject: [PATCH 09/38] Bluetooth: Replace L2CAP link mode with security level Change the L2CAP internals to use the new security levels and remove the link mode details. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 5 +- net/bluetooth/l2cap.c | 160 +++++++++++++++++++++++----------- 2 files changed, 113 insertions(+), 52 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 73e115bc12dd..29f720e6188a 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -237,8 +237,9 @@ struct l2cap_pinfo { __u16 imtu; __u16 omtu; __u16 flush_to; - - __u32 link_mode; + __u8 sec_level; + __u8 role_switch; + __u8 force_reliable; __u8 conf_req[64]; __u8 conf_len; diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index eadf09231866..e899a9371c00 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -78,8 +78,7 @@ static void l2cap_sock_timeout(unsigned long arg) bh_lock_sock(sk); if (sk->sk_state == BT_CONNECT && - (l2cap_pi(sk)->link_mode & (L2CAP_LM_AUTH | - L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE))) + l2cap_pi(sk)->sec_level != BT_SECURITY_SDP) reason = ECONNREFUSED; else reason = ETIMEDOUT; @@ -259,23 +258,11 @@ static void l2cap_chan_del(struct sock *sk, int err) } /* Service level security */ -static inline int l2cap_check_link_mode(struct sock *sk) +static inline int l2cap_check_security(struct sock *sk) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; - if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) - return hci_conn_security(conn->hcon, BT_SECURITY_HIGH); - - if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) - return hci_conn_security(conn->hcon, BT_SECURITY_MEDIUM); - - if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH) - return hci_conn_security(conn->hcon, BT_SECURITY_LOW); - - if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) - return hci_conn_security(conn->hcon, BT_SECURITY_SDP); - - return 1; + return hci_conn_security(conn->hcon, l2cap_pi(sk)->sec_level); } static inline u8 l2cap_get_ident(struct l2cap_conn *conn) @@ -317,7 +304,7 @@ static void l2cap_do_start(struct sock *sk) struct l2cap_conn *conn = l2cap_pi(sk)->conn; if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) { - if (l2cap_check_link_mode(sk)) { + if (l2cap_check_security(sk)) { struct l2cap_conn_req req; req.scid = cpu_to_le16(l2cap_pi(sk)->scid); req.psm = l2cap_pi(sk)->psm; @@ -361,7 +348,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) } if (sk->sk_state == BT_CONNECT) { - if (l2cap_check_link_mode(sk)) { + if (l2cap_check_security(sk)) { struct l2cap_conn_req req; req.scid = cpu_to_le16(l2cap_pi(sk)->scid); req.psm = l2cap_pi(sk)->psm; @@ -376,7 +363,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); - if (l2cap_check_link_mode(sk)) { + if (l2cap_check_security(sk)) { if (bt_sk(sk)->defer_setup) { struct sock *parent = bt_sk(sk)->parent; rsp.result = cpu_to_le16(L2CAP_CR_PEND); @@ -439,7 +426,7 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) read_lock(&l->lock); for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { - if (l2cap_pi(sk)->link_mode & L2CAP_LM_RELIABLE) + if (l2cap_pi(sk)->force_reliable) sk->sk_err = err; } @@ -690,11 +677,15 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) pi->imtu = l2cap_pi(parent)->imtu; pi->omtu = l2cap_pi(parent)->omtu; - pi->link_mode = l2cap_pi(parent)->link_mode; + pi->sec_level = l2cap_pi(parent)->sec_level; + pi->role_switch = l2cap_pi(parent)->role_switch; + pi->force_reliable = l2cap_pi(parent)->force_reliable; } else { pi->imtu = L2CAP_DEFAULT_MTU; pi->omtu = 0; - pi->link_mode = 0; + pi->sec_level = BT_SECURITY_LOW; + pi->role_switch = 0; + pi->force_reliable = 0; } /* Default config options */ @@ -792,6 +783,9 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_ l2cap_pi(sk)->psm = la->l2_psm; l2cap_pi(sk)->sport = la->l2_psm; sk->sk_state = BT_BOUND; + + if (btohs(la->l2_psm) == 0x0001) + l2cap_pi(sk)->sec_level = BT_SECURITY_SDP; } write_unlock_bh(&l2cap_sk_list.lock); @@ -808,7 +802,6 @@ static int l2cap_do_connect(struct sock *sk) struct l2cap_conn *conn; struct hci_conn *hcon; struct hci_dev *hdev; - __u8 sec_level; __u8 auth_type; int err = 0; @@ -821,37 +814,39 @@ static int l2cap_do_connect(struct sock *sk) err = -ENOMEM; - if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) - sec_level = BT_SECURITY_HIGH; - else if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) - sec_level = BT_SECURITY_SDP; - else if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) - sec_level = BT_SECURITY_MEDIUM; - else - sec_level = BT_SECURITY_LOW; - if (sk->sk_type == SOCK_RAW) { - if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) + switch (l2cap_pi(sk)->sec_level) { + case BT_SECURITY_HIGH: auth_type = HCI_AT_DEDICATED_BONDING_MITM; - else if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) + break; + case BT_SECURITY_MEDIUM: auth_type = HCI_AT_DEDICATED_BONDING; - else + break; + default: auth_type = HCI_AT_NO_BONDING; + break; + } } else if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) { - if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) + if (l2cap_pi(sk)->sec_level == BT_SECURITY_HIGH) auth_type = HCI_AT_NO_BONDING_MITM; else auth_type = HCI_AT_NO_BONDING; } else { - if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) + switch (l2cap_pi(sk)->sec_level) { + case BT_SECURITY_HIGH: auth_type = HCI_AT_GENERAL_BONDING_MITM; - else if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) + break; + case BT_SECURITY_MEDIUM: auth_type = HCI_AT_GENERAL_BONDING; - else + break; + default: auth_type = HCI_AT_NO_BONDING; + break; + } } - hcon = hci_connect(hdev, ACL_LINK, dst, sec_level, auth_type); + hcon = hci_connect(hdev, ACL_LINK, dst, + l2cap_pi(sk)->sec_level, auth_type); if (!hcon) goto done; @@ -1219,7 +1214,15 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us break; } - l2cap_pi(sk)->link_mode = opt; + if (opt & L2CAP_LM_AUTH) + l2cap_pi(sk)->sec_level = BT_SECURITY_LOW; + if (opt & L2CAP_LM_ENCRYPT) + l2cap_pi(sk)->sec_level = BT_SECURITY_MEDIUM; + if (opt & L2CAP_LM_SECURE) + l2cap_pi(sk)->sec_level = BT_SECURITY_HIGH; + + l2cap_pi(sk)->role_switch = (opt & L2CAP_LM_MASTER); + l2cap_pi(sk)->force_reliable = (opt & L2CAP_LM_RELIABLE); break; default: @@ -1234,7 +1237,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen) { struct sock *sk = sock->sk; - int err = 0; + struct bt_security sec; + int len, err = 0; u32 opt; BT_DBG("sk %p", sk); @@ -1245,6 +1249,24 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch lock_sock(sk); switch (optname) { + case BT_SECURITY: + sec.level = BT_SECURITY_LOW; + + len = min_t(unsigned int, sizeof(sec), optlen); + if (copy_from_user((char *) &sec, optval, len)) { + err = -EFAULT; + break; + } + + if (sec.level < BT_SECURITY_LOW || + sec.level > BT_SECURITY_HIGH) { + err = -EINVAL; + break; + } + + l2cap_pi(sk)->sec_level = sec.level; + break; + case BT_DEFER_SETUP: if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { err = -EINVAL; @@ -1274,6 +1296,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us struct l2cap_options opts; struct l2cap_conninfo cinfo; int len, err = 0; + u32 opt; BT_DBG("sk %p", sk); @@ -1296,7 +1319,29 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us break; case L2CAP_LM: - if (put_user(l2cap_pi(sk)->link_mode, (u32 __user *) optval)) + switch (l2cap_pi(sk)->sec_level) { + case BT_SECURITY_LOW: + opt = L2CAP_LM_AUTH; + break; + case BT_SECURITY_MEDIUM: + opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT; + break; + case BT_SECURITY_HIGH: + opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | + L2CAP_LM_SECURE; + break; + default: + opt = 0; + break; + } + + if (l2cap_pi(sk)->role_switch) + opt |= L2CAP_LM_MASTER; + + if (l2cap_pi(sk)->force_reliable) + opt |= L2CAP_LM_RELIABLE; + + if (put_user(opt, (u32 __user *) optval)) err = -EFAULT; break; @@ -1329,6 +1374,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; + struct bt_security sec; int len, err = 0; BT_DBG("sk %p", sk); @@ -1342,6 +1388,15 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch lock_sock(sk); switch (optname) { + case BT_SECURITY: + sec.level = l2cap_pi(sk)->sec_level; + + len = min_t(unsigned int, len, sizeof(sec)); + if (copy_to_user(optval, (char *) &sec, len)) + err = -EFAULT; + + break; + case BT_DEFER_SETUP: if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { err = -EINVAL; @@ -1771,7 +1826,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd l2cap_pi(sk)->ident = cmd->ident; if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) { - if (l2cap_check_link_mode(sk)) { + if (l2cap_check_security(sk)) { if (bt_sk(sk)->defer_setup) { sk->sk_state = BT_CONNECT2; result = L2CAP_CR_PEND; @@ -2299,10 +2354,15 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) continue; if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) { - lm1 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode); + lm1 |= HCI_LM_ACCEPT; + if (l2cap_pi(sk)->role_switch) + lm1 |= HCI_LM_MASTER; exact++; - } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) - lm2 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode); + } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) { + lm2 |= HCI_LM_ACCEPT; + if (l2cap_pi(sk)->role_switch) + lm2 |= HCI_LM_MASTER; + } } read_unlock(&l2cap_sk_list.lock); @@ -2361,7 +2421,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) bh_lock_sock(sk); if (!status && encrypt == 0x00 && - (pi->link_mode & L2CAP_LM_SECURE) && + pi->sec_level == BT_SECURITY_HIGH && (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG)) { __l2cap_sock_close(sk, ECONNREFUSED); @@ -2510,10 +2570,10 @@ static ssize_t l2cap_sysfs_show(struct class *dev, char *buf) sk_for_each(sk, node, &l2cap_sk_list.head) { struct l2cap_pinfo *pi = l2cap_pi(sk); - str += sprintf(str, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d 0x%x\n", + str += sprintf(str, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d\n", batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), sk->sk_state, btohs(pi->psm), pi->scid, pi->dcid, - pi->imtu, pi->omtu, pi->link_mode); + pi->imtu, pi->omtu, pi->sec_level); } read_unlock_bh(&l2cap_sk_list.lock); From 9f2c8a03fbb3048cf38b158f87aa0c3c09bca084 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 15 Jan 2009 21:58:40 +0100 Subject: [PATCH 10/38] Bluetooth: Replace RFCOMM link mode with security level Change the RFCOMM internals to use the new security levels and remove the link mode details. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/rfcomm.h | 7 ++-- net/bluetooth/rfcomm/core.c | 28 ++++--------- net/bluetooth/rfcomm/sock.c | 75 ++++++++++++++++++++++++++++++---- 3 files changed, 79 insertions(+), 31 deletions(-) diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index 71b45f459687..bda68d833ddd 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -183,8 +183,8 @@ struct rfcomm_dlc { u8 remote_v24_sig; u8 mscex; u8 out; - - u32 link_mode; + u8 sec_level; + u8 role_switch; u32 defer_setup; uint mtu; @@ -307,7 +307,8 @@ struct rfcomm_pinfo { struct bt_sock bt; struct rfcomm_dlc *dlc; u8 channel; - u32 link_mode; + u8 sec_level; + u8 role_switch; }; int rfcomm_init_sockets(void); diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 68f70c5270c6..db83f92d274c 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -223,21 +223,11 @@ static int rfcomm_l2sock_create(struct socket **sock) return err; } -static inline int rfcomm_check_link_mode(struct rfcomm_dlc *d) +static inline int rfcomm_check_security(struct rfcomm_dlc *d) { struct sock *sk = d->session->sock->sk; - struct l2cap_conn *conn = l2cap_pi(sk)->conn; - if (d->link_mode & RFCOMM_LM_SECURE) - return hci_conn_security(conn->hcon, BT_SECURITY_HIGH); - - if (d->link_mode & RFCOMM_LM_ENCRYPT) - return hci_conn_security(conn->hcon, BT_SECURITY_MEDIUM); - - if (d->link_mode & RFCOMM_LM_AUTH) - return hci_conn_security(conn->hcon, BT_SECURITY_LOW); - - return 1; + return hci_conn_security(l2cap_pi(sk)->conn->hcon, d->sec_level); } /* ---- RFCOMM DLCs ---- */ @@ -390,7 +380,7 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc; if (s->state == BT_CONNECTED) { - if (rfcomm_check_link_mode(d)) + if (rfcomm_check_security(d)) rfcomm_send_pn(s, 1, d); else set_bit(RFCOMM_AUTH_PENDING, &d->flags); @@ -1192,7 +1182,7 @@ void rfcomm_dlc_accept(struct rfcomm_dlc *d) d->state_change(d, 0); rfcomm_dlc_unlock(d); - if (d->link_mode & RFCOMM_LM_MASTER) + if (d->role_switch) hci_conn_switch_role(l2cap_pi(sk)->conn->hcon, 0x00); rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig); @@ -1200,7 +1190,7 @@ void rfcomm_dlc_accept(struct rfcomm_dlc *d) static void rfcomm_check_accept(struct rfcomm_dlc *d) { - if (rfcomm_check_link_mode(d)) { + if (rfcomm_check_security(d)) { if (d->defer_setup) { set_bit(RFCOMM_DEFER_SETUP, &d->flags); rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); @@ -1660,7 +1650,7 @@ static void rfcomm_process_connect(struct rfcomm_session *s) d = list_entry(p, struct rfcomm_dlc, list); if (d->state == BT_CONFIG) { d->mtu = s->mtu; - if (rfcomm_check_link_mode(d)) { + if (rfcomm_check_security(d)) { rfcomm_send_pn(s, 1, d); } else { set_bit(RFCOMM_AUTH_PENDING, &d->flags); @@ -1748,10 +1738,6 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s) } else rfcomm_dlc_accept(d); } - if (d->link_mode & RFCOMM_LM_SECURE) { - struct sock *sk = s->sock->sk; - hci_conn_change_link_key(l2cap_pi(sk)->conn->hcon); - } continue; } else if (test_and_clear_bit(RFCOMM_AUTH_REJECT, &d->flags)) { rfcomm_dlc_clear_timer(d); @@ -1994,7 +1980,7 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt) d = list_entry(p, struct rfcomm_dlc, list); if (!status && encrypt == 0x00 && - (d->link_mode & RFCOMM_LM_ENCRYPT) && + d->sec_level == BT_SECURITY_HIGH && (d->state == BT_CONNECTED || d->state == BT_CONFIG)) { __rfcomm_dlc_close(d, ECONNREFUSED); diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index d37a829a81e4..9986ef35c890 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -261,14 +261,19 @@ static void rfcomm_sock_init(struct sock *sk, struct sock *parent) if (parent) { sk->sk_type = parent->sk_type; - pi->link_mode = rfcomm_pi(parent)->link_mode; pi->dlc->defer_setup = bt_sk(parent)->defer_setup; + + pi->sec_level = rfcomm_pi(parent)->sec_level; + pi->role_switch = rfcomm_pi(parent)->role_switch; } else { - pi->link_mode = 0; pi->dlc->defer_setup = 0; + + pi->sec_level = BT_SECURITY_LOW; + pi->role_switch = 0; } - pi->dlc->link_mode = pi->link_mode; + pi->dlc->sec_level = pi->sec_level; + pi->dlc->role_switch = pi->role_switch; } static struct proto rfcomm_proto = { @@ -408,7 +413,8 @@ static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int a bacpy(&bt_sk(sk)->dst, &sa->rc_bdaddr); rfcomm_pi(sk)->channel = sa->rc_channel; - d->link_mode = rfcomm_pi(sk)->link_mode; + d->sec_level = rfcomm_pi(sk)->sec_level; + d->role_switch = rfcomm_pi(sk)->role_switch; err = rfcomm_dlc_open(d, &bt_sk(sk)->src, &sa->rc_bdaddr, sa->rc_channel); if (!err) @@ -741,7 +747,14 @@ static int rfcomm_sock_setsockopt_old(struct socket *sock, int optname, char __u break; } - rfcomm_pi(sk)->link_mode = opt; + if (opt & RFCOMM_LM_AUTH) + rfcomm_pi(sk)->sec_level = BT_SECURITY_LOW; + if (opt & RFCOMM_LM_ENCRYPT) + rfcomm_pi(sk)->sec_level = BT_SECURITY_MEDIUM; + if (opt & RFCOMM_LM_SECURE) + rfcomm_pi(sk)->sec_level = BT_SECURITY_HIGH; + + rfcomm_pi(sk)->role_switch = (opt & RFCOMM_LM_MASTER); break; default: @@ -756,7 +769,8 @@ static int rfcomm_sock_setsockopt_old(struct socket *sock, int optname, char __u static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen) { struct sock *sk = sock->sk; - int err = 0; + struct bt_security sec; + int len, err = 0; u32 opt; BT_DBG("sk %p", sk); @@ -767,6 +781,23 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c lock_sock(sk); switch (optname) { + case BT_SECURITY: + sec.level = BT_SECURITY_LOW; + + len = min_t(unsigned int, sizeof(sec), optlen); + if (copy_from_user((char *) &sec, optval, len)) { + err = -EFAULT; + break; + } + + if (sec.level > BT_SECURITY_HIGH) { + err = -EINVAL; + break; + } + + rfcomm_pi(sk)->sec_level = sec.level; + break; + case BT_DEFER_SETUP: if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { err = -EINVAL; @@ -796,6 +827,7 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u struct sock *l2cap_sk; struct rfcomm_conninfo cinfo; int len, err = 0; + u32 opt; BT_DBG("sk %p", sk); @@ -806,7 +838,26 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u switch (optname) { case RFCOMM_LM: - if (put_user(rfcomm_pi(sk)->link_mode, (u32 __user *) optval)) + switch (rfcomm_pi(sk)->sec_level) { + case BT_SECURITY_LOW: + opt = RFCOMM_LM_AUTH; + break; + case BT_SECURITY_MEDIUM: + opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT; + break; + case BT_SECURITY_HIGH: + opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | + RFCOMM_LM_SECURE; + break; + default: + opt = 0; + break; + } + + if (rfcomm_pi(sk)->role_switch) + opt |= RFCOMM_LM_MASTER; + + if (put_user(opt, (u32 __user *) optval)) err = -EFAULT; break; @@ -840,6 +891,7 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; + struct bt_security sec; int len, err = 0; BT_DBG("sk %p", sk); @@ -853,6 +905,15 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c lock_sock(sk); switch (optname) { + case BT_SECURITY: + sec.level = rfcomm_pi(sk)->sec_level; + + len = min_t(unsigned int, len, sizeof(sec)); + if (copy_to_user(optval, (char *) &sec, len)) + err = -EFAULT; + + break; + case BT_DEFER_SETUP: if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { err = -EINVAL; From 8c84b83076b5062f59b6167cdda90d9e5124aa71 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 16 Jan 2009 08:17:51 +0100 Subject: [PATCH 11/38] Bluetooth: Pause RFCOMM TX when encryption drops A role switch with devices following the Bluetooth pre-2.1 standards or without Encryption Pause and Resume support is not possible if encryption is enabled. Most newer headsets require the role switch, but also require that the connection is encrypted. For connections with a high security mode setting, the link will be immediately dropped. When the connection uses medium security mode setting, then a grace period is introduced where the TX is halted and the remote device gets a change to re-enable encryption after the role switch. If not re-enabled the link will be dropped. Based on initial work by Ville Tervo Signed-off-by: Marcel Holtmann --- include/net/bluetooth/rfcomm.h | 9 +++++---- net/bluetooth/rfcomm/core.c | 23 +++++++++++++++++------ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index bda68d833ddd..80072611d26a 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -204,10 +204,11 @@ struct rfcomm_dlc { #define RFCOMM_TX_THROTTLED 1 #define RFCOMM_TIMED_OUT 2 #define RFCOMM_MSC_PENDING 3 -#define RFCOMM_AUTH_PENDING 4 -#define RFCOMM_AUTH_ACCEPT 5 -#define RFCOMM_AUTH_REJECT 6 -#define RFCOMM_DEFER_SETUP 7 +#define RFCOMM_SEC_PENDING 4 +#define RFCOMM_AUTH_PENDING 5 +#define RFCOMM_AUTH_ACCEPT 6 +#define RFCOMM_AUTH_REJECT 7 +#define RFCOMM_DEFER_SETUP 8 /* Scheduling flags and events */ #define RFCOMM_SCHED_STATE 0 diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index db83f92d274c..dafaee91cdfb 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -1979,12 +1979,23 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt) list_for_each_safe(p, n, &s->dlcs) { d = list_entry(p, struct rfcomm_dlc, list); - if (!status && encrypt == 0x00 && - d->sec_level == BT_SECURITY_HIGH && - (d->state == BT_CONNECTED || - d->state == BT_CONFIG)) { - __rfcomm_dlc_close(d, ECONNREFUSED); - continue; + if (test_and_clear_bit(RFCOMM_SEC_PENDING, &d->flags)) { + rfcomm_dlc_clear_timer(d); + if (status || encrypt == 0x00) { + __rfcomm_dlc_close(d, ECONNREFUSED); + continue; + } + } + + if (d->state == BT_CONNECTED && !status && encrypt == 0x00) { + if (d->sec_level == BT_SECURITY_MEDIUM) { + set_bit(RFCOMM_SEC_PENDING, &d->flags); + rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); + continue; + } else if (d->sec_level == BT_SECURITY_HIGH) { + __rfcomm_dlc_close(d, ECONNREFUSED); + continue; + } } if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags)) From f62e4323ab43c59e7cd7f72c1eb392d7c767ce5a Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 15 Jan 2009 21:58:44 +0100 Subject: [PATCH 12/38] Bluetooth: Disconnect L2CAP connections without encryption For L2CAP connections with high security setting, the link will be immediately dropped when the encryption gets disabled. For L2CAP connections with medium security there will be grace period where the remote device has the chance to re-enable encryption. If it doesn't happen then the link will also be disconnected. The requirement for the grace period with medium security comes from Bluetooth 2.0 and earlier devices that require role switching. Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index e899a9371c00..b2d279c245cf 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -77,7 +77,9 @@ static void l2cap_sock_timeout(unsigned long arg) bh_lock_sock(sk); - if (sk->sk_state == BT_CONNECT && + if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG) + reason = ECONNREFUSED; + else if (sk->sk_state == BT_CONNECT && l2cap_pi(sk)->sec_level != BT_SECURITY_SDP) reason = ECONNREFUSED; else @@ -2400,6 +2402,20 @@ static int l2cap_disconn_ind(struct hci_conn *hcon, u8 reason) return 0; } +static inline void l2cap_check_encryption(struct sock *sk, u8 encrypt) +{ + if (encrypt == 0x00) { + if (l2cap_pi(sk)->sec_level == BT_SECURITY_MEDIUM) { + l2cap_sock_clear_timer(sk); + l2cap_sock_set_timer(sk, HZ * 5); + } else if (l2cap_pi(sk)->sec_level == BT_SECURITY_HIGH) + __l2cap_sock_close(sk, ECONNREFUSED); + } else { + if (l2cap_pi(sk)->sec_level == BT_SECURITY_MEDIUM) + l2cap_sock_clear_timer(sk); + } +} + static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) { struct l2cap_chan_list *l; @@ -2416,15 +2432,11 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) read_lock(&l->lock); for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { - struct l2cap_pinfo *pi = l2cap_pi(sk); - bh_lock_sock(sk); - if (!status && encrypt == 0x00 && - pi->sec_level == BT_SECURITY_HIGH && - (sk->sk_state == BT_CONNECTED || + if (!status && (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG)) { - __l2cap_sock_close(sk, ECONNREFUSED); + l2cap_check_encryption(sk, encrypt); bh_unlock_sock(sk); continue; } From 0588d94fd7e414367a7ae517569d2222441c255f Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 16 Jan 2009 10:06:13 +0100 Subject: [PATCH 13/38] Bluetooth: Restrict application of socket options The new socket options should only be evaluated for SOL_BLUETOOTH level and not for every other level. Previously this causes some minor issues when detecting if a kernel with certain features is available. Also restrict BT_SECURITY to SOCK_SEQPACKET for L2CAP and SOCK_STREAM for the RFCOMM protocol. Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 16 ++++++++++++++++ net/bluetooth/rfcomm/sock.c | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index b2d279c245cf..82a9e692baed 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1248,10 +1248,18 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch if (level == SOL_L2CAP) return l2cap_sock_setsockopt_old(sock, optname, optval, optlen); + if (level != SOL_BLUETOOTH) + return -ENOPROTOOPT; + lock_sock(sk); switch (optname) { case BT_SECURITY: + if (sk->sk_type != SOCK_SEQPACKET) { + err = -EINVAL; + break; + } + sec.level = BT_SECURITY_LOW; len = min_t(unsigned int, sizeof(sec), optlen); @@ -1384,6 +1392,9 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch if (level == SOL_L2CAP) return l2cap_sock_getsockopt_old(sock, optname, optval, optlen); + if (level != SOL_BLUETOOTH) + return -ENOPROTOOPT; + if (get_user(len, optlen)) return -EFAULT; @@ -1391,6 +1402,11 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch switch (optname) { case BT_SECURITY: + if (sk->sk_type != SOCK_SEQPACKET) { + err = -EINVAL; + break; + } + sec.level = l2cap_pi(sk)->sec_level; len = min_t(unsigned int, len, sizeof(sec)); diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 9986ef35c890..7f482784e9f7 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -778,10 +778,18 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c if (level == SOL_RFCOMM) return rfcomm_sock_setsockopt_old(sock, optname, optval, optlen); + if (level != SOL_BLUETOOTH) + return -ENOPROTOOPT; + lock_sock(sk); switch (optname) { case BT_SECURITY: + if (sk->sk_type != SOCK_STREAM) { + err = -EINVAL; + break; + } + sec.level = BT_SECURITY_LOW; len = min_t(unsigned int, sizeof(sec), optlen); @@ -899,6 +907,9 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c if (level == SOL_RFCOMM) return rfcomm_sock_getsockopt_old(sock, optname, optval, optlen); + if (level != SOL_BLUETOOTH) + return -ENOPROTOOPT; + if (get_user(len, optlen)) return -EFAULT; @@ -906,6 +917,11 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c switch (optname) { case BT_SECURITY: + if (sk->sk_type != SOCK_STREAM) { + err = -EINVAL; + break; + } + sec.level = rfcomm_pi(sk)->sec_level; len = min_t(unsigned int, len, sizeof(sec)); From 5f9018af004fa8635bbbe3ab2dc61e8a686edfaa Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 16 Jan 2009 10:09:50 +0100 Subject: [PATCH 14/38] Bluetooth: Update version numbers With the support for the enhanced security model and the support for deferring connection setup, it is a good idea to increase various version numbers. This is purely cosmetic and has no effect on the behavior, but can be really helpful when debugging problems in different kernel versions. Signed-off-by: Marcel Holtmann --- net/bluetooth/af_bluetooth.c | 2 +- net/bluetooth/l2cap.c | 2 +- net/bluetooth/rfcomm/core.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 7c0031ff8cfb..0d1b94da978e 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -41,7 +41,7 @@ #include -#define VERSION "2.14" +#define VERSION "2.15" /* Bluetooth sockets */ #define BT_MAX_PROTO 8 diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 82a9e692baed..71a064fa0285 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -50,7 +50,7 @@ #include #include -#define VERSION "2.11" +#define VERSION "2.12" static u32 l2cap_feat_mask = 0x0000; diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index dafaee91cdfb..ad0e25a21832 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -46,7 +46,7 @@ #include #include -#define VERSION "1.10" +#define VERSION "1.11" static int disable_cfc = 0; static int channel_mtu = -1; From dd2efd03b49d56ae795c71335bc7358022514c32 Mon Sep 17 00:00:00 2001 From: Dave Young Date: Sat, 31 Jan 2009 13:51:15 +0800 Subject: [PATCH 15/38] Bluetooth: Remove CONFIG_DEBUG_LOCK_ALLOC ifdefs Due to lockdep changes, the CONFIG_DEBUG_LOCK_ALLOC ifdef is not needed now. So just remove it here. The following commit fixed the !lockdep build warnings: commit e8f6fbf62de37cbc2e179176ac7010d5f4396b67 Author: Ingo Molnar Date: Wed Nov 12 01:38:36 2008 +0000 lockdep: include/linux/lockdep.h - fix warning in net/bluetooth/af_bluetooth.c Signed-off-by: Dave Young Signed-off-by: Marcel Holtmann --- net/bluetooth/af_bluetooth.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 0d1b94da978e..ded57974390e 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -48,7 +48,6 @@ static struct net_proto_family *bt_proto[BT_MAX_PROTO]; static DEFINE_RWLOCK(bt_proto_lock); -#ifdef CONFIG_DEBUG_LOCK_ALLOC static struct lock_class_key bt_lock_key[BT_MAX_PROTO]; static const char *bt_key_strings[BT_MAX_PROTO] = { "sk_lock-AF_BLUETOOTH-BTPROTO_L2CAP", @@ -86,11 +85,6 @@ static inline void bt_sock_reclassify_lock(struct socket *sock, int proto) bt_slock_key_strings[proto], &bt_slock_key[proto], bt_key_strings[proto], &bt_lock_key[proto]); } -#else -static inline void bt_sock_reclassify_lock(struct socket *sock, int proto) -{ -} -#endif int bt_sock_register(int proto, struct net_proto_family *ops) { From 34a55eda483e8177c9044f93fd2c9107f02bf1c7 Mon Sep 17 00:00:00 2001 From: Andre Haupt Date: Mon, 2 Feb 2009 14:45:11 -0800 Subject: [PATCH 16/38] Bluetooth: Eliminate a sparse warning in bt3c driver This eliminates a sparse warning that symbol 'stat' shadows an earlier one. Signed-off-by: Andre Haupt Signed-off-by: Marcel Holtmann --- drivers/bluetooth/bt3c_cs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index ff195c230825..d58e22b9f06a 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -359,9 +359,9 @@ static irqreturn_t bt3c_interrupt(int irq, void *dev_inst) BT_ERR("Very strange (stat=0x%04x)", stat); } else if ((stat & 0xff) != 0xff) { if (stat & 0x0020) { - int stat = bt3c_read(iobase, 0x7002) & 0x10; + int status = bt3c_read(iobase, 0x7002) & 0x10; BT_INFO("%s: Antenna %s", info->hdev->name, - stat ? "out" : "in"); + status ? "out" : "in"); } if (stat & 0x0001) bt3c_receive(info); From 6e1031a40029492c10509e8c3dcac9b611438ccb Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Mon, 2 Feb 2009 18:03:57 -0800 Subject: [PATCH 17/38] Bluetooth: When encryption is dropped, do not send RFCOMM packets During a role change with pre-Bluetooth 2.1 devices, the remote side drops the encryption of the RFCOMM connection. We allow a grace period for the encryption to be re-established, before dropping the connection. During this grace period, the RFCOMM_SEC_PENDING flag is set. Check this flag before sending RFCOMM packets. Signed-off-by: Jaikumar Ganesh Signed-off-by: Marcel Holtmann --- net/bluetooth/rfcomm/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index ad0e25a21832..3717c25ba33a 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -1749,6 +1749,9 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s) continue; } + if (test_bit(RFCOMM_SEC_PENDING, &d->flags)) + continue; + if (test_bit(RFCOMM_TX_THROTTLED, &s->flags)) continue; From 43c2e57f94c15744495fee564610aa24602b3824 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 4 Feb 2009 17:41:38 +0100 Subject: [PATCH 18/38] Bluetooth: Submit bulk URBs along with interrupt URBs Submitting the bulk URBs for ACL data transfers only on demand has no real benefit compared to just submit them when a Bluetooth device gets opened. So when submitting the interrupt URBs for HCI events, just submit the bulk URBs, too. This solves a problem with some Bluetooth USB dongles that has been reported over the last few month. These devices require that the bulk URBs are actually present. These devices are really broken, but there is nothing we can do about it. Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btusb.c | 40 ++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index b5fbda6d490a..e70c57ee4221 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -35,7 +35,7 @@ #include #include -#define VERSION "0.4" +#define VERSION "0.5" static int ignore_dga; static int ignore_csr; @@ -171,6 +171,7 @@ struct btusb_data { __u8 cmdreq_type; + unsigned int sco_num; int isoc_altsetting; int suspend_count; }; @@ -496,11 +497,23 @@ static int btusb_open(struct hci_dev *hdev) return 0; err = btusb_submit_intr_urb(hdev, GFP_KERNEL); + if (err < 0) + goto failed; + + err = btusb_submit_bulk_urb(hdev, GFP_KERNEL); if (err < 0) { - clear_bit(BTUSB_INTR_RUNNING, &data->flags); - clear_bit(HCI_RUNNING, &hdev->flags); + usb_kill_anchored_urbs(&data->intr_anchor); + goto failed; } + set_bit(BTUSB_BULK_RUNNING, &data->flags); + btusb_submit_bulk_urb(hdev, GFP_KERNEL); + + return 0; + +failed: + clear_bit(BTUSB_INTR_RUNNING, &data->flags); + clear_bit(HCI_RUNNING, &hdev->flags); return err; } @@ -655,19 +668,10 @@ static void btusb_notify(struct hci_dev *hdev, unsigned int evt) BT_DBG("%s evt %d", hdev->name, evt); - if (hdev->conn_hash.acl_num > 0) { - if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) { - if (btusb_submit_bulk_urb(hdev, GFP_ATOMIC) < 0) - clear_bit(BTUSB_BULK_RUNNING, &data->flags); - else - btusb_submit_bulk_urb(hdev, GFP_ATOMIC); - } - } else { - clear_bit(BTUSB_BULK_RUNNING, &data->flags); - usb_unlink_anchored_urbs(&data->bulk_anchor); + if (hdev->conn_hash.sco_num != data->sco_num) { + data->sco_num = hdev->conn_hash.sco_num; + schedule_work(&data->work); } - - schedule_work(&data->work); } static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting) @@ -982,9 +986,11 @@ static int btusb_resume(struct usb_interface *intf) } if (test_bit(BTUSB_BULK_RUNNING, &data->flags)) { - if (btusb_submit_bulk_urb(hdev, GFP_NOIO) < 0) + err = btusb_submit_bulk_urb(hdev, GFP_NOIO); + if (err < 0) { clear_bit(BTUSB_BULK_RUNNING, &data->flags); - else + return err; + } else btusb_submit_bulk_urb(hdev, GFP_NOIO); } From 255c76014af74165428e7aa16414b857e2bdccf2 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 4 Feb 2009 21:07:19 +0100 Subject: [PATCH 19/38] Bluetooth: Don't check encryption for L2CAP raw sockets For L2CAP sockets with medium and high security requirement a missing encryption will enforce the closing of the link. For the L2CAP raw sockets this is not needed, so skip that check. This fixes a crash when pairing Bluetooth 2.0 (and earlier) devices since the L2CAP state machine got confused and then locked up the whole system. Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 71a064fa0285..b677af671f31 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -2420,6 +2420,9 @@ static int l2cap_disconn_ind(struct hci_conn *hcon, u8 reason) static inline void l2cap_check_encryption(struct sock *sk, u8 encrypt) { + if (sk->sk_type != SOCK_SEQPACKET) + return; + if (encrypt == 0x00) { if (l2cap_pi(sk)->sec_level == BT_SECURITY_MEDIUM) { l2cap_sock_clear_timer(sk); From efc7688b557dd1be10eead7399b315efcb1dbc74 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 6 Feb 2009 09:13:37 +0100 Subject: [PATCH 20/38] Bluetooth: Add SCO fallback for eSCO connection attempts When attempting to setup eSCO connections it can happen that some link manager implementations fail to properly negotiate the eSCO parameters and thus fail the eSCO setup. Normally the link manager is responsible for the negotiation of the parameters and actually fallback to SCO if no agreement can be reached. In cases where the link manager is just too stupid, then at least try to establish a SCO link if eSCO fails. For the Bluetooth devices with EDR support this includes handling packet types of EDR basebands. This is particular tricky since for the EDR the logic of enabling/disabling one specific packet type is turned around. This fix contains an extra bitmask to disable eSCO EDR packet when trying to fallback to a SCO connection. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 8 ++++++++ net/bluetooth/hci_conn.c | 9 +++++++-- net/bluetooth/hci_event.c | 16 ++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 3645139e68c7..f69f015bbcc0 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -133,8 +133,13 @@ enum { #define ESCO_EV3 0x0008 #define ESCO_EV4 0x0010 #define ESCO_EV5 0x0020 +#define ESCO_2EV3 0x0040 +#define ESCO_3EV3 0x0080 +#define ESCO_2EV5 0x0100 +#define ESCO_3EV5 0x0200 #define SCO_ESCO_MASK (ESCO_HV1 | ESCO_HV2 | ESCO_HV3) +#define EDR_ESCO_MASK (ESCO_2EV3 | ESCO_3EV3 | ESCO_2EV5 | ESCO_3EV5) /* ACL flags */ #define ACL_CONT 0x01 @@ -176,6 +181,9 @@ enum { #define LMP_EV5 0x02 #define LMP_SNIFF_SUBR 0x02 +#define LMP_EDR_ESCO_2M 0x20 +#define LMP_EDR_ESCO_3M 0x40 +#define LMP_EDR_3S_ESCO 0x80 #define LMP_SIMPLE_PAIR 0x08 diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 98f97a1e9bbb..2435e830ba60 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -123,6 +123,8 @@ void hci_add_sco(struct hci_conn *conn, __u16 handle) conn->state = BT_CONNECT; conn->out = 1; + conn->attempt++; + cp.handle = cpu_to_le16(handle); cp.pkt_type = cpu_to_le16(conn->pkt_type); @@ -139,6 +141,8 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle) conn->state = BT_CONNECT; conn->out = 1; + conn->attempt++; + cp.handle = cpu_to_le16(handle); cp.pkt_type = cpu_to_le16(conn->pkt_type); @@ -216,12 +220,13 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) break; case SCO_LINK: if (lmp_esco_capable(hdev)) - conn->pkt_type = hdev->esco_type & SCO_ESCO_MASK; + conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) | + (hdev->esco_type & EDR_ESCO_MASK); else conn->pkt_type = hdev->pkt_type & SCO_PTYPE_MASK; break; case ESCO_LINK: - conn->pkt_type = hdev->esco_type; + conn->pkt_type = hdev->esco_type & ~EDR_ESCO_MASK; break; } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 014fc8b320ba..899b8991a466 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -484,6 +484,15 @@ static void hci_cc_read_local_features(struct hci_dev *hdev, struct sk_buff *skb if (hdev->features[4] & LMP_EV5) hdev->esco_type |= (ESCO_EV5); + if (hdev->features[5] & LMP_EDR_ESCO_2M) + hdev->esco_type |= (ESCO_2EV3); + + if (hdev->features[5] & LMP_EDR_ESCO_3M) + hdev->esco_type |= (ESCO_3EV3); + + if (hdev->features[5] & LMP_EDR_3S_ESCO) + hdev->esco_type |= (ESCO_2EV5 | ESCO_3EV5); + BT_DBG("%s features 0x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x", hdev->name, hdev->features[0], hdev->features[1], hdev->features[2], hdev->features[3], @@ -1639,6 +1648,13 @@ static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_bu conn->type = SCO_LINK; } + if (conn->out && ev->status == 0x1c && conn->attempt < 2) { + conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) | + (hdev->esco_type & EDR_ESCO_MASK); + hci_setup_sync(conn, conn->link->handle); + goto unlock; + } + if (!ev->status) { conn->handle = __le16_to_cpu(ev->handle); conn->state = BT_CONNECTED; From 0684e5f9fb9e3f7e168ab831dfca693bcb44805b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 9 Feb 2009 02:48:38 +0100 Subject: [PATCH 21/38] Bluetooth: Use general bonding whenever possible When receiving incoming connection to specific services, always use general bonding. This ensures that the link key gets stored and can be used for further authentications. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_conn.c | 23 +++++++++-------------- net/bluetooth/l2cap.c | 16 +++++++++++++++- net/bluetooth/rfcomm/core.c | 16 +++++++++++++++- 4 files changed, 40 insertions(+), 17 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4b14972c1694..f75028b33883 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -328,7 +328,7 @@ void hci_conn_check_pending(struct hci_dev *hdev); struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type); int hci_conn_check_link_mode(struct hci_conn *conn); -int hci_conn_security(struct hci_conn *conn, __u8 sec_level); +int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type); int hci_conn_change_link_key(struct hci_conn *conn); int hci_conn_switch_role(struct hci_conn *conn, __u8 role); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 2435e830ba60..7fc4c048b57b 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -391,19 +391,14 @@ int hci_conn_check_link_mode(struct hci_conn *conn) EXPORT_SYMBOL(hci_conn_check_link_mode); /* Authenticate remote device */ -static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level) +static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) { BT_DBG("conn %p", conn); - if (sec_level > conn->sec_level) - conn->link_mode &= ~HCI_LM_AUTH; - - conn->sec_level = sec_level; - - if (sec_level == BT_SECURITY_HIGH) - conn->auth_type |= 0x01; - - if (conn->link_mode & HCI_LM_AUTH) + if (sec_level > conn->sec_level) { + conn->sec_level = sec_level; + conn->auth_type = auth_type; + } else if (conn->link_mode & HCI_LM_AUTH) return 1; if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) { @@ -417,7 +412,7 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level) } /* Enable security */ -int hci_conn_security(struct hci_conn *conn, __u8 sec_level) +int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) { BT_DBG("conn %p", conn); @@ -426,18 +421,18 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level) if (sec_level == BT_SECURITY_LOW) { if (conn->ssp_mode > 0 && conn->hdev->ssp_mode > 0) - return hci_conn_auth(conn, sec_level); + return hci_conn_auth(conn, sec_level, auth_type); else return 1; } if (conn->link_mode & HCI_LM_ENCRYPT) - return hci_conn_auth(conn, sec_level); + return hci_conn_auth(conn, sec_level, auth_type); if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) return 0; - if (hci_conn_auth(conn, sec_level)) { + if (hci_conn_auth(conn, sec_level, auth_type)) { struct hci_cp_set_conn_encrypt cp; cp.handle = cpu_to_le16(conn->handle); cp.encrypt = 1; diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index b677af671f31..8a93dde4095b 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -263,8 +263,22 @@ static void l2cap_chan_del(struct sock *sk, int err) static inline int l2cap_check_security(struct sock *sk) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; + __u8 auth_type; + + switch (l2cap_pi(sk)->sec_level) { + case BT_SECURITY_HIGH: + auth_type = HCI_AT_GENERAL_BONDING_MITM; + break; + case BT_SECURITY_MEDIUM: + auth_type = HCI_AT_GENERAL_BONDING; + break; + default: + auth_type = HCI_AT_NO_BONDING; + break; + } - return hci_conn_security(conn->hcon, l2cap_pi(sk)->sec_level); + return hci_conn_security(conn->hcon, l2cap_pi(sk)->sec_level, + auth_type); } static inline u8 l2cap_get_ident(struct l2cap_conn *conn) diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 3717c25ba33a..1828ec06ad1c 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -226,8 +226,22 @@ static int rfcomm_l2sock_create(struct socket **sock) static inline int rfcomm_check_security(struct rfcomm_dlc *d) { struct sock *sk = d->session->sock->sk; + __u8 auth_type; - return hci_conn_security(l2cap_pi(sk)->conn->hcon, d->sec_level); + switch (d->sec_level) { + case BT_SECURITY_HIGH: + auth_type = HCI_AT_GENERAL_BONDING_MITM; + break; + case BT_SECURITY_MEDIUM: + auth_type = HCI_AT_GENERAL_BONDING; + break; + default: + auth_type = HCI_AT_NO_BONDING; + break; + } + + return hci_conn_security(l2cap_pi(sk)->conn->hcon, d->sec_level, + auth_type); } /* ---- RFCOMM DLCs ---- */ From 657e17b03c80bec817975984d221bef716f83558 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 6 Feb 2009 19:45:36 +0100 Subject: [PATCH 22/38] Bluetooth: Set authentication requirements if not available When no authentication requirements are selected, but an outgoing or incoming connection has requested any kind of security enforcement, then set these authentication requirements. This ensures that the userspace always gets informed about the authentication requirements (if available). Only when no security enforcement has happened, the kernel will signal invalid requirements. Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_conn.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 7fc4c048b57b..dcdaa4be7847 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -416,6 +416,9 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) { BT_DBG("conn %p", conn); + if (conn->auth_type == 0xff) + conn->auth_type = auth_type; + if (sec_level == BT_SECURITY_SDP) return 1; From 984947dc64f82bc6cafa4d84ba1a139718f634a8 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 6 Feb 2009 23:35:19 +0100 Subject: [PATCH 23/38] Bluetooth: Fix race condition with L2CAP information request When two L2CAP connections are requested quickly after the ACL link has been established there exists a window for a race condition where a connection request is sent before the information response has been received. Any connection request should only be sent after an exchange of the extended features mask has been finished. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 3 ++- net/bluetooth/l2cap.c | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 29f720e6188a..1c8cf3e9b1ca 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -223,7 +223,8 @@ struct l2cap_conn { }; #define L2CAP_INFO_CL_MTU_REQ_SENT 0x01 -#define L2CAP_INFO_FEAT_MASK_REQ_SENT 0x02 +#define L2CAP_INFO_FEAT_MASK_REQ_SENT 0x04 +#define L2CAP_INFO_FEAT_MASK_REQ_DONE 0x08 /* ----- L2CAP channel and socket info ----- */ #define l2cap_pi(sk) ((struct l2cap_pinfo *) sk) diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 8a93dde4095b..07fdbc7dd54d 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -320,6 +320,9 @@ static void l2cap_do_start(struct sock *sk) struct l2cap_conn *conn = l2cap_pi(sk)->conn; if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) { + if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)) + return; + if (l2cap_check_security(sk)) { struct l2cap_conn_req req; req.scid = cpu_to_le16(l2cap_pi(sk)->scid); @@ -455,6 +458,8 @@ static void l2cap_info_timeout(unsigned long arg) conn->info_ident = 0; + conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; + l2cap_conn_start(conn); } @@ -1787,6 +1792,9 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd cmd->ident == conn->info_ident) { conn->info_ident = 0; del_timer(&conn->info_timer); + + conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; + l2cap_conn_start(conn); } @@ -1857,7 +1865,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd l2cap_pi(sk)->ident = cmd->ident; - if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) { + if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) { if (l2cap_check_security(sk)) { if (bt_sk(sk)->defer_setup) { sk->sk_state = BT_CONNECT2; @@ -2176,10 +2184,13 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm del_timer(&conn->info_timer); - if (type == L2CAP_IT_FEAT_MASK) + if (type == L2CAP_IT_FEAT_MASK) { conn->feat_mask = get_unaligned_le32(rsp->data); - l2cap_conn_start(conn); + conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; + + l2cap_conn_start(conn); + } return 0; } From 6a8d3010b313d99adbb28f1826fac0234395bb26 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 6 Feb 2009 23:56:36 +0100 Subject: [PATCH 24/38] Bluetooth: Fix double L2CAP connection request If the remote L2CAP server uses authentication pending stage and encryption is enabled it can happen that a L2CAP connection request is sent twice due to a race condition in the connection state machine. When the remote side indicates any kind of connection pending, then track this state and skip sending of L2CAP commands for this period. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 1c8cf3e9b1ca..4781d285b2e9 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -259,6 +259,7 @@ struct l2cap_pinfo { #define L2CAP_CONF_REQ_SENT 0x01 #define L2CAP_CONF_INPUT_DONE 0x02 #define L2CAP_CONF_OUTPUT_DONE 0x04 +#define L2CAP_CONF_CONNECT_PEND 0x80 #define L2CAP_CONF_MAX_RETRIES 2 diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 07fdbc7dd54d..01f750142d55 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1946,11 +1946,14 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd l2cap_pi(sk)->dcid = dcid; l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; + l2cap_pi(sk)->conf_state &= ~L2CAP_CONF_CONNECT_PEND; + l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req); break; case L2CAP_CR_PEND: + l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND; break; default: @@ -2478,6 +2481,11 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { bh_lock_sock(sk); + if (l2cap_pi(sk)->conf_state & L2CAP_CONF_CONNECT_PEND) { + bh_unlock_sock(sk); + continue; + } + if (!status && (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG)) { l2cap_check_encryption(sk, encrypt); From 435fef20acfc48f46476abad55b0cd3aa47b8365 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 9 Feb 2009 03:55:28 +0100 Subject: [PATCH 25/38] Bluetooth: Don't enforce authentication for L2CAP PSM 1 and 3 The recommendation for the L2CAP PSM 1 (SDP) is to not use any kind of authentication or encryption. So don't trigger authentication for incoming and outgoing SDP connections. For L2CAP PSM 3 (RFCOMM) there is no clear requirement, but with Bluetooth 2.1 the initiator is required to enable authentication and encryption first and this gets enforced. So there is no need to trigger an additional authentication step. The RFCOMM service security will make sure that a secure enough link key is present. When the encryption gets enabled after the SDP connection setup, then switch the security level from SDP to low security. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 3 +++ net/bluetooth/l2cap.c | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index f75028b33883..9473fce499e7 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -597,6 +597,9 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encr { struct list_head *p; + if (conn->sec_level == BT_SECURITY_SDP) + conn->sec_level = BT_SECURITY_LOW; + hci_proto_encrypt_cfm(conn, status, encrypt); read_lock_bh(&hci_cb_list_lock); diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 01f750142d55..88340d24d11d 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -805,7 +805,7 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_ l2cap_pi(sk)->sport = la->l2_psm; sk->sk_state = BT_BOUND; - if (btohs(la->l2_psm) == 0x0001) + if (btohs(la->l2_psm) == 0x0001 || btohs(la->l2_psm) == 0x0003) l2cap_pi(sk)->sec_level = BT_SECURITY_SDP; } @@ -852,6 +852,9 @@ static int l2cap_do_connect(struct sock *sk) auth_type = HCI_AT_NO_BONDING_MITM; else auth_type = HCI_AT_NO_BONDING; + + if (l2cap_pi(sk)->sec_level == BT_SECURITY_LOW) + l2cap_pi(sk)->sec_level = BT_SECURITY_SDP; } else { switch (l2cap_pi(sk)->sec_level) { case BT_SECURITY_HIGH: From e1027a7c69700301d14db03d2e049ee60c4f92df Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 9 Feb 2009 09:18:02 +0100 Subject: [PATCH 26/38] Bluetooth: Request L2CAP fixed channel list if available If the extended features mask indicates support for fixed channels, request the list of available fixed channels. This also enables the fixed channel features bit so remote implementations can request information about it. Currently only the signal channel will be listed. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap.c | 40 +++++++++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 4781d285b2e9..abfec8847718 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -185,6 +185,7 @@ struct l2cap_info_rsp { /* info type */ #define L2CAP_IT_CL_MTU 0x0001 #define L2CAP_IT_FEAT_MASK 0x0002 +#define L2CAP_IT_FIXED_CHAN 0x0003 /* info result */ #define L2CAP_IR_SUCCESS 0x0000 diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 88340d24d11d..985366c36f48 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -50,9 +50,10 @@ #include #include -#define VERSION "2.12" +#define VERSION "2.13" -static u32 l2cap_feat_mask = 0x0000; +static u32 l2cap_feat_mask = 0x0080; +static u8 l2cap_fixed_chan[8] = { 0x02, }; static const struct proto_ops l2cap_sock_ops; @@ -456,9 +457,8 @@ static void l2cap_info_timeout(unsigned long arg) { struct l2cap_conn *conn = (void *) arg; - conn->info_ident = 0; - conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; + conn->info_ident = 0; l2cap_conn_start(conn); } @@ -1793,10 +1793,10 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) && cmd->ident == conn->info_ident) { - conn->info_ident = 0; del_timer(&conn->info_timer); conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; + conn->info_ident = 0; l2cap_conn_start(conn); } @@ -2165,6 +2165,14 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm put_unaligned(cpu_to_le32(l2cap_feat_mask), (__le32 *) rsp->data); l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf), buf); + } else if (type == L2CAP_IT_FIXED_CHAN) { + u8 buf[12]; + struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf; + rsp->type = cpu_to_le16(L2CAP_IT_FIXED_CHAN); + rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS); + memcpy(buf + 4, l2cap_fixed_chan, 8); + l2cap_send_cmd(conn, cmd->ident, + L2CAP_INFO_RSP, sizeof(buf), buf); } else { struct l2cap_info_rsp rsp; rsp.type = cpu_to_le16(type); @@ -2186,14 +2194,28 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm BT_DBG("type 0x%4.4x result 0x%2.2x", type, result); - conn->info_ident = 0; - del_timer(&conn->info_timer); if (type == L2CAP_IT_FEAT_MASK) { conn->feat_mask = get_unaligned_le32(rsp->data); + if (conn->feat_mask & 0x0080) { + struct l2cap_info_req req; + req.type = cpu_to_le16(L2CAP_IT_FIXED_CHAN); + + conn->info_ident = l2cap_get_ident(conn); + + l2cap_send_cmd(conn, conn->info_ident, + L2CAP_INFO_REQ, sizeof(req), &req); + } else { + conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; + conn->info_ident = 0; + + l2cap_conn_start(conn); + } + } else if (type == L2CAP_IT_FIXED_CHAN) { conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; + conn->info_ident = 0; l2cap_conn_start(conn); } @@ -2589,7 +2611,7 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl goto drop; skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), - skb->len); + skb->len); conn->rx_len = len - skb->len; } else { BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len); @@ -2611,7 +2633,7 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl } skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), - skb->len); + skb->len); conn->rx_len -= skb->len; if (!conn->rx_len) { From f29972de8e7476706ab3c01304a505e7c95d9040 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 12 Feb 2009 05:07:45 +0100 Subject: [PATCH 27/38] Bluetooth: Add CID field to L2CAP socket address structure In preparation for L2CAP fixed channel support, the CID value of a L2CAP connection needs to be accessible via the socket interface. The CID is the connection identifier and exists as source and destination value. So extend the L2CAP socket address structure with this field and change getsockname() and getpeername() to fill it in. The bind() and connect() functions have been modified to handle L2CAP socket address structures of variable sizes. This makes them future proof if additional fields need to be added. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap.c | 55 ++++++++++++++++++++++------------- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index abfec8847718..54737c5dc86c 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -37,6 +37,7 @@ struct sockaddr_l2 { sa_family_t l2_family; __le16 l2_psm; bdaddr_t l2_bdaddr; + __le16 l2_cid; }; /* L2CAP socket options */ diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 985366c36f48..7bba469b6828 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -770,17 +770,21 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol) return 0; } -static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) +static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) { - struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; struct sock *sk = sock->sk; - int err = 0; + struct sockaddr_l2 la; + int len, err = 0; - BT_DBG("sk %p, %s %d", sk, batostr(&la->l2_bdaddr), la->l2_psm); + BT_DBG("sk %p", sk); if (!addr || addr->sa_family != AF_BLUETOOTH) return -EINVAL; + memset(&la, 0, sizeof(la)); + len = min_t(unsigned int, sizeof(la), alen); + memcpy(&la, addr, len); + lock_sock(sk); if (sk->sk_state != BT_OPEN) { @@ -788,7 +792,7 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_ goto done; } - if (la->l2_psm && btohs(la->l2_psm) < 0x1001 && + if (la.l2_psm && btohs(la.l2_psm) < 0x1001 && !capable(CAP_NET_BIND_SERVICE)) { err = -EACCES; goto done; @@ -796,16 +800,16 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_ write_lock_bh(&l2cap_sk_list.lock); - if (la->l2_psm && __l2cap_get_sock_by_addr(la->l2_psm, &la->l2_bdaddr)) { + if (la.l2_psm && __l2cap_get_sock_by_addr(la.l2_psm, &la.l2_bdaddr)) { err = -EADDRINUSE; } else { /* Save source address */ - bacpy(&bt_sk(sk)->src, &la->l2_bdaddr); - l2cap_pi(sk)->psm = la->l2_psm; - l2cap_pi(sk)->sport = la->l2_psm; + bacpy(&bt_sk(sk)->src, &la.l2_bdaddr); + l2cap_pi(sk)->psm = la.l2_psm; + l2cap_pi(sk)->sport = la.l2_psm; sk->sk_state = BT_BOUND; - if (btohs(la->l2_psm) == 0x0001 || btohs(la->l2_psm) == 0x0003) + if (btohs(la.l2_psm) == 0x0001 || btohs(la.l2_psm) == 0x0003) l2cap_pi(sk)->sec_level = BT_SECURITY_SDP; } @@ -826,7 +830,8 @@ static int l2cap_do_connect(struct sock *sk) __u8 auth_type; int err = 0; - BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), l2cap_pi(sk)->psm); + BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), + l2cap_pi(sk)->psm); if (!(hdev = hci_get_route(dst, src))) return -EHOSTUNREACH; @@ -906,20 +911,24 @@ static int l2cap_do_connect(struct sock *sk) static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) { - struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; struct sock *sk = sock->sk; - int err = 0; + struct sockaddr_l2 la; + int len, err = 0; lock_sock(sk); BT_DBG("sk %p", sk); - if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_l2)) { + if (!addr || addr->sa_family != AF_BLUETOOTH) { err = -EINVAL; goto done; } - if (sk->sk_type == SOCK_SEQPACKET && !la->l2_psm) { + memset(&la, 0, sizeof(la)); + len = min_t(unsigned int, sizeof(la), alen); + memcpy(&la, addr, len); + + if (sk->sk_type == SOCK_SEQPACKET && !la.l2_psm) { err = -EINVAL; goto done; } @@ -946,8 +955,8 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al } /* Set destination address and psm */ - bacpy(&bt_sk(sk)->dst, &la->l2_bdaddr); - l2cap_pi(sk)->psm = la->l2_psm; + bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr); + l2cap_pi(sk)->psm = la.l2_psm; if ((err = l2cap_do_connect(sk))) goto done; @@ -1071,12 +1080,16 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l addr->sa_family = AF_BLUETOOTH; *len = sizeof(struct sockaddr_l2); - if (peer) + if (peer) { + la->l2_psm = l2cap_pi(sk)->psm; bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst); - else + la->l2_cid = htobs(l2cap_pi(sk)->dcid); + } else { + la->l2_psm = l2cap_pi(sk)->sport; bacpy(&la->l2_bdaddr, &bt_sk(sk)->src); + la->l2_cid = htobs(l2cap_pi(sk)->scid); + } - la->l2_psm = l2cap_pi(sk)->psm; return 0; } @@ -1208,7 +1221,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us { struct sock *sk = sock->sk; struct l2cap_options opts; - int err = 0, len; + int len, err = 0; u32 opt; BT_DBG("sk %p", sk); From 2950f21acb0f6b8fcd964485c2ebf1e06545ac20 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 12 Feb 2009 14:02:50 +0100 Subject: [PATCH 28/38] Bluetooth: Ask upper layers for HCI disconnect reason Some of the qualification tests demand that in case of failures in L2CAP the HCI disconnect should indicate a reason why L2CAP fails. This is a bluntly layer violation since multiple L2CAP connections could be using the same ACL and thus forcing a disconnect reason is not a good idea. To comply with the Bluetooth test specification, the disconnect reason is now stored in the L2CAP connection structure and every time a new L2CAP channel is added it will set back to its default. So only in the case where the L2CAP channel with the disconnect reason is really the last one, it will propagated to the HCI layer. The HCI layer has been extended with a disconnect indication that allows it to ask upper layers for a disconnect reason. The upper layer must not support this callback and in that case it will nicely default to the existing behavior. If an upper layer like L2CAP can provide a disconnect reason that one will be used to disconnect the ACL or SCO link. No modification to the ACL disconnect timeout have been made. So in case of Linux to Linux connection the initiator will disconnect the ACL link before the acceptor side can signal the specific disconnect reason. That is perfectly fine since Linux doesn't make use of this value anyway. The L2CAP layer has a perfect valid error code for rejecting connection due to a security violation. It is unclear why the Bluetooth specification insists on having specific HCI disconnect reason. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 25 +++++++++++++++++++++---- include/net/bluetooth/l2cap.h | 2 ++ net/bluetooth/hci_conn.c | 6 ++++-- net/bluetooth/hci_event.c | 2 +- net/bluetooth/l2cap.c | 20 +++++++++++++++++++- net/bluetooth/sco.c | 4 ++-- 6 files changed, 49 insertions(+), 10 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 9473fce499e7..01f9316b4c23 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -478,7 +478,8 @@ struct hci_proto { int (*connect_ind) (struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type); int (*connect_cfm) (struct hci_conn *conn, __u8 status); - int (*disconn_ind) (struct hci_conn *conn, __u8 reason); + int (*disconn_ind) (struct hci_conn *conn); + int (*disconn_cfm) (struct hci_conn *conn, __u8 reason); int (*recv_acldata) (struct hci_conn *conn, struct sk_buff *skb, __u16 flags); int (*recv_scodata) (struct hci_conn *conn, struct sk_buff *skb); int (*security_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt); @@ -513,17 +514,33 @@ static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status) hp->connect_cfm(conn, status); } -static inline void hci_proto_disconn_ind(struct hci_conn *conn, __u8 reason) +static inline int hci_proto_disconn_ind(struct hci_conn *conn) { register struct hci_proto *hp; + int reason = 0x13; hp = hci_proto[HCI_PROTO_L2CAP]; if (hp && hp->disconn_ind) - hp->disconn_ind(conn, reason); + reason = hp->disconn_ind(conn); hp = hci_proto[HCI_PROTO_SCO]; if (hp && hp->disconn_ind) - hp->disconn_ind(conn, reason); + reason = hp->disconn_ind(conn); + + return reason; +} + +static inline void hci_proto_disconn_cfm(struct hci_conn *conn, __u8 reason) +{ + register struct hci_proto *hp; + + hp = hci_proto[HCI_PROTO_L2CAP]; + if (hp && hp->disconn_cfm) + hp->disconn_cfm(conn, reason); + + hp = hci_proto[HCI_PROTO_SCO]; + if (hp && hp->disconn_cfm) + hp->disconn_cfm(conn, reason); } static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 54737c5dc86c..f566aa1f0a4c 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -221,6 +221,8 @@ struct l2cap_conn { __u8 rx_ident; __u8 tx_ident; + __u8 disc_reason; + struct l2cap_chan_list chan_list; }; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index dcdaa4be7847..96281a11a186 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -159,6 +159,7 @@ static void hci_conn_timeout(unsigned long arg) { struct hci_conn *conn = (void *) arg; struct hci_dev *hdev = conn->hdev; + __u8 reason; BT_DBG("conn %p state %d", conn, conn->state); @@ -177,7 +178,8 @@ static void hci_conn_timeout(unsigned long arg) break; case BT_CONFIG: case BT_CONNECTED: - hci_acl_disconn(conn, 0x13); + reason = hci_proto_disconn_ind(conn); + hci_acl_disconn(conn, reason); break; default: conn->state = BT_CLOSED; @@ -562,7 +564,7 @@ void hci_conn_hash_flush(struct hci_dev *hdev) hci_conn_del_sysfs(c); - hci_proto_disconn_ind(c, 0x16); + hci_proto_disconn_cfm(c, 0x16); hci_conn_del(c); } } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 899b8991a466..c396542c2b82 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1021,7 +1021,7 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff hci_conn_del_sysfs(conn); - hci_proto_disconn_ind(conn, ev->reason); + hci_proto_disconn_cfm(conn, ev->reason); hci_conn_del(conn); } diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 7bba469b6828..d563f2ebcbb3 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -206,6 +206,8 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid); + conn->disc_reason = 0x13; + l2cap_pi(sk)->conn = conn; if (sk->sk_type == SOCK_SEQPACKET) { @@ -491,6 +493,8 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) spin_lock_init(&conn->lock); rwlock_init(&conn->chan_list.lock); + conn->disc_reason = 0x13; + return conn; } @@ -1840,6 +1844,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd /* Check if the ACL is secure enough (if not SDP) */ if (psm != cpu_to_le16(0x0001) && !hci_conn_check_link_mode(conn->hcon)) { + conn->disc_reason = 0x05; result = L2CAP_CR_SEC_BLOCK; goto response; } @@ -2472,7 +2477,19 @@ static int l2cap_connect_cfm(struct hci_conn *hcon, u8 status) return 0; } -static int l2cap_disconn_ind(struct hci_conn *hcon, u8 reason) +static int l2cap_disconn_ind(struct hci_conn *hcon) +{ + struct l2cap_conn *conn = hcon->l2cap_data; + + BT_DBG("hcon %p", hcon); + + if (hcon->type != ACL_LINK || !conn) + return 0x13; + + return conn->disc_reason; +} + +static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) { BT_DBG("hcon %p reason %d", hcon, reason); @@ -2717,6 +2734,7 @@ static struct hci_proto l2cap_hci_proto = { .connect_ind = l2cap_connect_ind, .connect_cfm = l2cap_connect_cfm, .disconn_ind = l2cap_disconn_ind, + .disconn_cfm = l2cap_disconn_cfm, .security_cfm = l2cap_security_cfm, .recv_acldata = l2cap_recv_acldata }; diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 7f10f97cd697..51ae0c3e470a 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -902,7 +902,7 @@ static int sco_connect_cfm(struct hci_conn *hcon, __u8 status) return 0; } -static int sco_disconn_ind(struct hci_conn *hcon, __u8 reason) +static int sco_disconn_cfm(struct hci_conn *hcon, __u8 reason) { BT_DBG("hcon %p reason %d", hcon, reason); @@ -985,7 +985,7 @@ static struct hci_proto sco_hci_proto = { .id = HCI_PROTO_SCO, .connect_ind = sco_connect_ind, .connect_cfm = sco_connect_cfm, - .disconn_ind = sco_disconn_ind, + .disconn_cfm = sco_disconn_cfm, .recv_scodata = sco_recv_scodata }; From 00ae4af91d8c5b6814e2bb3bfaaf743845f989eb Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 12 Feb 2009 16:19:45 +0100 Subject: [PATCH 29/38] Bluetooth: Fix authentication requirements for L2CAP security check The L2CAP layer can trigger the authentication via an ACL connection or later on to increase the security level. When increasing the security level it didn't use the same authentication requirements when triggering a new ACL connection. Make sure that exactly the same authentication requirements are used. The only exception here are the L2CAP raw sockets which are only used for dedicated bonding. Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index d563f2ebcbb3..79a4325a1388 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -268,16 +268,26 @@ static inline int l2cap_check_security(struct sock *sk) struct l2cap_conn *conn = l2cap_pi(sk)->conn; __u8 auth_type; - switch (l2cap_pi(sk)->sec_level) { - case BT_SECURITY_HIGH: - auth_type = HCI_AT_GENERAL_BONDING_MITM; - break; - case BT_SECURITY_MEDIUM: - auth_type = HCI_AT_GENERAL_BONDING; - break; - default: - auth_type = HCI_AT_NO_BONDING; - break; + if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) { + if (l2cap_pi(sk)->sec_level == BT_SECURITY_HIGH) + auth_type = HCI_AT_NO_BONDING_MITM; + else + auth_type = HCI_AT_NO_BONDING; + + if (l2cap_pi(sk)->sec_level == BT_SECURITY_LOW) + l2cap_pi(sk)->sec_level = BT_SECURITY_SDP; + } else { + switch (l2cap_pi(sk)->sec_level) { + case BT_SECURITY_HIGH: + auth_type = HCI_AT_GENERAL_BONDING_MITM; + break; + case BT_SECURITY_MEDIUM: + auth_type = HCI_AT_GENERAL_BONDING; + break; + default: + auth_type = HCI_AT_NO_BONDING; + break; + } } return hci_conn_security(conn->hcon, l2cap_pi(sk)->sec_level, From 96a3183322cba1a2846771b067c99b9d6f481263 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 12 Feb 2009 16:23:03 +0100 Subject: [PATCH 30/38] Bluetooth: Set authentication requirement before requesting it The authentication requirement got only updated when the security level increased. This is a wrong behavior. The authentication requirement is read by the Bluetooth daemon to make proper decisions when handling the IO capabilities exchange. So set the value that is currently expected by the higher layers like L2CAP and RFCOMM. Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_conn.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 96281a11a186..efd5c926cc1b 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -397,12 +397,13 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) { BT_DBG("conn %p", conn); - if (sec_level > conn->sec_level) { + if (sec_level > conn->sec_level) conn->sec_level = sec_level; - conn->auth_type = auth_type; - } else if (conn->link_mode & HCI_LM_AUTH) + else if (conn->link_mode & HCI_LM_AUTH) return 1; + conn->auth_type = auth_type; + if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) { struct hci_cp_auth_requested cp; cp.handle = cpu_to_le16(conn->handle); @@ -418,9 +419,6 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) { BT_DBG("conn %p", conn); - if (conn->auth_type == 0xff) - conn->auth_type = auth_type; - if (sec_level == BT_SECURITY_SDP) return 1; From d5f2d2be68876f65dd051b978a7b66265fde9ffd Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Feb 2009 02:57:30 +0100 Subject: [PATCH 31/38] Bluetooth: Fix poll() misbehavior when using BT_DEFER_SETUP When BT_DEFER_SETUP has been enabled on a Bluetooth socket it keeps signaling POLLIN all the time. This is a wrong behavior. The POLLIN should only be signaled if the client socket is in BT_CONNECT2 state and the parent has been BT_DEFER_SETUP enabled. Signed-off-by: Marcel Holtmann --- net/bluetooth/af_bluetooth.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index ded57974390e..02b9baa1930b 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -270,12 +270,11 @@ static inline unsigned int bt_accept_poll(struct sock *parent) struct list_head *p, *n; struct sock *sk; - if (bt_sk(parent)->defer_setup) - return POLLIN | POLLRDNORM; - list_for_each_safe(p, n, &bt_sk(parent)->accept_q) { sk = (struct sock *) list_entry(p, struct bt_sock, accept_q); - if (sk->sk_state == BT_CONNECTED) + if (sk->sk_state == BT_CONNECTED || + (bt_sk(parent)->defer_setup && + sk->sk_state == BT_CONNECT2)) return POLLIN | POLLRDNORM; } From 8bf4794174659b06d43cc5e290cd384757374613 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Feb 2009 02:59:49 +0100 Subject: [PATCH 32/38] Bluetooth: Change RFCOMM to use BT_CONNECT2 for BT_DEFER_SETUP When BT_DEFER_SETUP is enabled on a RFCOMM socket, then switch its current state from BT_OPEN to BT_CONNECT2. This gives the Bluetooth core a unified way to handle L2CAP and RFCOMM sockets. The BT_CONNECT2 state is designated for incoming connections. Signed-off-by: Marcel Holtmann --- net/bluetooth/rfcomm/core.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 1828ec06ad1c..5576c8191507 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -448,6 +448,7 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err) break; case BT_OPEN: + case BT_CONNECT2: if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { set_bit(RFCOMM_AUTH_REJECT, &d->flags); rfcomm_schedule(RFCOMM_SCHED_AUTH); @@ -1208,6 +1209,11 @@ static void rfcomm_check_accept(struct rfcomm_dlc *d) if (d->defer_setup) { set_bit(RFCOMM_DEFER_SETUP, &d->flags); rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); + + rfcomm_dlc_lock(d); + d->state = BT_CONNECT2; + d->state_change(d, 0); + rfcomm_dlc_unlock(d); } else rfcomm_dlc_accept(d); } else { @@ -1749,6 +1755,11 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s) if (d->defer_setup) { set_bit(RFCOMM_DEFER_SETUP, &d->flags); rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); + + rfcomm_dlc_lock(d); + d->state = BT_CONNECT2; + d->state_change(d, 0); + rfcomm_dlc_unlock(d); } else rfcomm_dlc_accept(d); } From 2a517ca687232adc8f14893730644da712010ffc Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Feb 2009 03:20:31 +0100 Subject: [PATCH 33/38] Bluetooth: Disallow usage of L2CAP CID setting for now In the future the L2CAP layer will have full support for fixed channels and right now it already can export the channel assignment, but for the functions bind() and connect() the usage of only CID 0 is allowed. This allows an easy detection if the kernel supports fixed channels or not, because otherwise it would impossible for application to tell. Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 79a4325a1388..7c6768c2a530 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -799,6 +799,9 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) len = min_t(unsigned int, sizeof(la), alen); memcpy(&la, addr, len); + if (la.l2_cid) + return -EINVAL; + lock_sock(sk); if (sk->sk_state != BT_OPEN) { @@ -929,19 +932,20 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al struct sockaddr_l2 la; int len, err = 0; - lock_sock(sk); - BT_DBG("sk %p", sk); - if (!addr || addr->sa_family != AF_BLUETOOTH) { - err = -EINVAL; - goto done; - } + if (!addr || addr->sa_family != AF_BLUETOOTH) + return -EINVAL; memset(&la, 0, sizeof(la)); len = min_t(unsigned int, sizeof(la), alen); memcpy(&la, addr, len); + if (la.l2_cid) + return -EINVAL; + + lock_sock(sk); + if (sk->sk_type == SOCK_SEQPACKET && !la.l2_psm) { err = -EINVAL; goto done; From 37e62f5516cfb210e64fe53457932df4341b0ad1 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 17 Feb 2009 21:49:33 +0100 Subject: [PATCH 34/38] Bluetooth: Fix RFCOMM usage of in-kernel L2CAP sockets The CID value of L2CAP sockets need to be set to zero. All userspace applications do this via memset() on the sockaddr_l2 structure. The RFCOMM implementation uses in-kernel L2CAP sockets and so it has to make sure that l2_cid is set to zero. Signed-off-by: Marcel Holtmann --- net/bluetooth/rfcomm/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 5576c8191507..1d0fb0f23c63 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -658,6 +658,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst bacpy(&addr.l2_bdaddr, src); addr.l2_family = AF_BLUETOOTH; addr.l2_psm = 0; + addr.l2_cid = 0; *err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr)); if (*err < 0) goto failed; @@ -679,6 +680,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst bacpy(&addr.l2_bdaddr, dst); addr.l2_family = AF_BLUETOOTH; addr.l2_psm = htobs(RFCOMM_PSM); + addr.l2_cid = 0; *err = kernel_connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK); if (*err == 0 || *err == -EINPROGRESS) return s; @@ -1919,6 +1921,7 @@ static int rfcomm_add_listener(bdaddr_t *ba) bacpy(&addr.l2_bdaddr, ba); addr.l2_family = AF_BLUETOOTH; addr.l2_psm = htobs(RFCOMM_PSM); + addr.l2_cid = 0; err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0) { BT_ERR("Bind failed %d", err); From 2526d3d8b2f671a7d36cc486af984052cd5a690f Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 20 Feb 2009 20:54:06 +0100 Subject: [PATCH 35/38] Bluetooth: Permit BT_SECURITY also for L2CAP raw sockets Userspace pairing code can be simplified if it doesn't have to fall back to using L2CAP_LM in the case of L2CAP raw sockets. This patch allows the BT_SECURITY socket option to be used for these sockets. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 7c6768c2a530..db6fbf129be0 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1308,7 +1308,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch switch (optname) { case BT_SECURITY: - if (sk->sk_type != SOCK_SEQPACKET) { + if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_RAW) { err = -EINVAL; break; } @@ -1455,7 +1455,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch switch (optname) { case BT_SECURITY: - if (sk->sk_type != SOCK_SEQPACKET) { + if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_RAW) { err = -EINVAL; break; } From 2ae9a6be5f476f3512839a4d11a8f432bfd2914c Mon Sep 17 00:00:00 2001 From: Dave Young Date: Sat, 21 Feb 2009 16:13:34 +0800 Subject: [PATCH 36/38] Bluetooth: Move hci_conn_del_sysfs() back to avoid device destruct too early The following commit introduce a regression: commit 7d0db0a373195385a2e0b19d1f5e4b186fdcffac Author: Marcel Holtmann Date: Mon Jul 14 20:13:51 2008 +0200 [Bluetooth] Use a more unique bus name for connections I get panic as following (by netconsole): [ 2709.344034] usb 5-1: new full speed USB device using uhci_hcd and address 4 [ 2709.505776] usb 5-1: configuration #1 chosen from 1 choice [ 2709.569207] Bluetooth: Generic Bluetooth USB driver ver 0.4 [ 2709.570169] usbcore: registered new interface driver btusb [ 2845.742781] BUG: unable to handle kernel paging request at 6b6b6c2f [ 2845.742958] IP: [] __lock_acquire+0x6c/0xa80 [ 2845.743087] *pde = 00000000 [ 2845.743206] Oops: 0002 [#1] SMP [ 2845.743377] last sysfs file: /sys/class/bluetooth/hci0/hci0:6/type [ 2845.743742] Modules linked in: btusb netconsole snd_seq_dummy snd_seq_oss snd_seq_midi_event snd_seq snd_seq_device snd_pcm_oss snd_mixer_oss rfcomm l2cap bluetooth vfat fuse snd_hda_codec_idt snd_hda_intel snd_hda_codec snd_hwdep snd_pcm pl2303 snd_timer psmouse usbserial snd 3c59x e100 serio_raw soundcore i2c_i801 intel_agp mii agpgart snd_page_alloc rtc_cmos rtc_core thermal processor rtc_lib button thermal_sys sg evdev [ 2845.743742] [ 2845.743742] Pid: 0, comm: swapper Not tainted (2.6.29-rc5-smp #54) Dell DM051 [ 2845.743742] EIP: 0060:[] EFLAGS: 00010002 CPU: 0 [ 2845.743742] EIP is at __lock_acquire+0x6c/0xa80 [ 2845.743742] EAX: 00000046 EBX: 00000046 ECX: 6b6b6b6b EDX: 00000002 [ 2845.743742] ESI: 6b6b6b6b EDI: 00000000 EBP: c064fd14 ESP: c064fcc8 [ 2845.743742] DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068 [ 2845.743742] Process swapper (pid: 0, ti=c064e000 task=c05d1400 task.ti=c064e000) [ 2845.743742] Stack: [ 2845.743742] c05d1400 00000002 c05d1400 00000001 00000002 00000000 f65388dc c05d1400 [ 2845.743742] 6b6b6b6b 00000292 c064fd0c c0153732 00000000 00000000 00000001 f700fa50 [ 2845.743742] 00000046 00000000 00000000 c064fd40 c0155be6 00000000 00000002 00000001 [ 2845.743742] Call Trace: [ 2845.743742] [] ? trace_hardirqs_on_caller+0x72/0x1c0 [ 2845.743742] [] ? lock_acquire+0x76/0xa0 [ 2845.743742] [] ? skb_dequeue+0x1d/0x70 [ 2845.743742] [] ? _spin_lock_irqsave+0x45/0x80 [ 2845.743742] [] ? skb_dequeue+0x1d/0x70 [ 2845.743742] [] ? skb_dequeue+0x1d/0x70 [ 2845.743742] [] ? skb_queue_purge+0x14/0x20 [ 2845.743742] [] ? hci_conn_del+0x10a/0x1c0 [bluetooth] [ 2845.743742] [] ? l2cap_disconn_ind+0x59/0xb0 [l2cap] [ 2845.743742] [] ? hci_conn_del_sysfs+0x8e/0xd0 [bluetooth] [ 2845.743742] [] ? hci_event_packet+0x5f8/0x31c0 [bluetooth] [ 2845.743742] [] ? sock_def_readable+0x59/0x80 [ 2845.743742] [] ? _read_unlock+0x1d/0x20 [ 2845.743742] [] ? hci_send_to_sock+0xe9/0x1d0 [bluetooth] [ 2845.743742] [] ? trace_hardirqs_on+0xb/0x10 [ 2845.743742] [] ? hci_rx_task+0x2ba/0x490 [bluetooth] [ 2845.743742] [] ? tasklet_action+0x31/0xc0 [ 2845.743742] [] ? tasklet_action+0x4c/0xc0 [ 2845.743742] [] ? __do_softirq+0xa7/0x170 [ 2845.743742] [] ? ack_apic_level+0x5c/0x1c0 [ 2845.743742] [] ? do_softirq+0x57/0x60 [ 2845.743742] [] ? irq_exit+0x7c/0x90 [ 2845.743742] [] ? do_IRQ+0x4b/0x90 [ 2845.743742] [] ? irq_exit+0x75/0x90 [ 2845.743742] [] ? common_interrupt+0x2c/0x34 [ 2845.743742] [] ? mwait_idle+0x4f/0x70 [ 2845.743742] [] ? cpu_idle+0x65/0xb0 [ 2845.743742] [] ? rest_init+0x4e/0x60 [ 2845.743742] Code: 0f 84 69 02 00 00 83 ff 07 0f 87 1e 06 00 00 85 ff 0f 85 08 05 00 00 8b 4d cc 8b 49 04 85 c9 89 4d d4 0f 84 f7 04 00 00 8b 75 d4 ff 86 c4 00 00 00 89 f0 e8 56 a9 ff ff 85 c0 0f 85 6e 03 00 [ 2845.743742] EIP: [] __lock_acquire+0x6c/0xa80 SS:ESP 0068:c064fcc8 [ 2845.743742] ---[ end trace 4c985b38f022279f ]--- [ 2845.743742] Kernel panic - not syncing: Fatal exception in interrupt [ 2845.743742] ------------[ cut here ]------------ [ 2845.743742] WARNING: at kernel/smp.c:329 smp_call_function_many+0x151/0x200() [ 2845.743742] Hardware name: Dell DM051 [ 2845.743742] Modules linked in: btusb netconsole snd_seq_dummy snd_seq_oss snd_seq_midi_event snd_seq snd_seq_device snd_pcm_oss snd_mixer_oss rfcomm l2cap bluetooth vfat fuse snd_hda_codec_idt snd_hda_intel snd_hda_codec snd_hwdep snd_pcm pl2303 snd_timer psmouse usbserial snd 3c59x e100 serio_raw soundcore i2c_i801 intel_agp mii agpgart snd_page_alloc rtc_cmos rtc_core thermal processor rtc_lib button thermal_sys sg evdev [ 2845.743742] Pid: 0, comm: swapper Tainted: G D 2.6.29-rc5-smp #54 [ 2845.743742] Call Trace: [ 2845.743742] [] warn_slowpath+0x86/0xa0 [ 2845.743742] [] ? trace_hardirqs_off+0xb/0x10 [ 2845.743742] [] ? up+0x14/0x40 [ 2845.743742] [] ? release_console_sem+0x31/0x1e0 [ 2845.743742] [] ? _spin_lock_irqsave+0x6b/0x80 [ 2845.743742] [] ? trace_hardirqs_off+0xb/0x10 [ 2845.743742] [] ? _read_lock_irqsave+0x40/0x80 [ 2845.743742] [] ? release_console_sem+0x1c2/0x1e0 [ 2845.743742] [] ? up+0x14/0x40 [ 2845.743742] [] ? trace_hardirqs_off+0xb/0x10 [ 2845.743742] [] ? __mutex_unlock_slowpath+0x97/0x160 [ 2845.743742] [] ? mutex_trylock+0xb3/0x180 [ 2845.743742] [] ? mutex_unlock+0x8/0x10 [ 2845.743742] [] smp_call_function_many+0x151/0x200 [ 2845.743742] [] ? stop_this_cpu+0x0/0x40 [ 2845.743742] [] smp_call_function+0x21/0x30 [ 2845.743742] [] native_smp_send_stop+0x1e/0x50 [ 2845.743742] [] panic+0x55/0x110 [ 2845.743742] [] oops_end+0xb8/0xc0 [ 2845.743742] [] die+0x4f/0x70 [ 2845.743742] [] do_page_fault+0x269/0x610 [ 2845.743742] [] ? do_page_fault+0x0/0x610 [ 2845.743742] [] error_code+0x77/0x7c [ 2845.743742] [] ? __lock_acquire+0x6c/0xa80 [ 2845.743742] [] ? trace_hardirqs_on_caller+0x72/0x1c0 [ 2845.743742] [] lock_acquire+0x76/0xa0 [ 2845.743742] [] ? skb_dequeue+0x1d/0x70 [ 2845.743742] [] _spin_lock_irqsave+0x45/0x80 [ 2845.743742] [] ? skb_dequeue+0x1d/0x70 [ 2845.743742] [] skb_dequeue+0x1d/0x70 [ 2845.743742] [] skb_queue_purge+0x14/0x20 [ 2845.743742] [] hci_conn_del+0x10a/0x1c0 [bluetooth] [ 2845.743742] [] ? l2cap_disconn_ind+0x59/0xb0 [l2cap] [ 2845.743742] [] ? hci_conn_del_sysfs+0x8e/0xd0 [bluetooth] [ 2845.743742] [] hci_event_packet+0x5f8/0x31c0 [bluetooth] [ 2845.743742] [] ? sock_def_readable+0x59/0x80 [ 2845.743742] [] ? _read_unlock+0x1d/0x20 [ 2845.743742] [] ? hci_send_to_sock+0xe9/0x1d0 [bluetooth] [ 2845.743742] [] ? trace_hardirqs_on+0xb/0x10 [ 2845.743742] [] hci_rx_task+0x2ba/0x490 [bluetooth] [ 2845.743742] [] ? tasklet_action+0x31/0xc0 [ 2845.743742] [] tasklet_action+0x4c/0xc0 [ 2845.743742] [] __do_softirq+0xa7/0x170 [ 2845.743742] [] ? ack_apic_level+0x5c/0x1c0 [ 2845.743742] [] do_softirq+0x57/0x60 [ 2845.743742] [] irq_exit+0x7c/0x90 [ 2845.743742] [] do_IRQ+0x4b/0x90 [ 2845.743742] [] ? irq_exit+0x75/0x90 [ 2845.743742] [] common_interrupt+0x2c/0x34 [ 2845.743742] [] ? mwait_idle+0x4f/0x70 [ 2845.743742] [] cpu_idle+0x65/0xb0 [ 2845.743742] [] rest_init+0x4e/0x60 [ 2845.743742] ---[ end trace 4c985b38f02227a0 ]--- [ 2845.743742] ------------[ cut here ]------------ [ 2845.743742] WARNING: at kernel/smp.c:226 smp_call_function_single+0x8e/0x110() [ 2845.743742] Hardware name: Dell DM051 [ 2845.743742] Modules linked in: btusb netconsole snd_seq_dummy snd_seq_oss snd_seq_midi_event snd_seq snd_seq_device snd_pcm_oss snd_mixer_oss rfcomm l2cap bluetooth vfat fuse snd_hda_codec_idt snd_hda_intel snd_hda_codec snd_hwdep snd_pcm pl2303 snd_timer psmouse usbserial snd 3c59x e100 serio_raw soundcore i2c_i801 intel_agp mii agpgart snd_page_alloc rtc_cmos rtc_core thermal processor rtc_lib button thermal_sys sg evdev [ 2845.743742] Pid: 0, comm: swapper Tainted: G D W 2.6.29-rc5-smp #54 [ 2845.743742] Call Trace: [ 2845.743742] [] warn_slowpath+0x86/0xa0 [ 2845.743742] [] ? warn_slowpath+0x10/0xa0 [ 2845.743742] [] ? trace_hardirqs_off+0xb/0x10 [ 2845.743742] [] ? up+0x14/0x40 [ 2845.743742] [] ? release_console_sem+0x31/0x1e0 [ 2845.743742] [] ? _spin_lock_irqsave+0x6b/0x80 [ 2845.743742] [] ? trace_hardirqs_off+0xb/0x10 [ 2845.743742] [] ? _read_lock_irqsave+0x40/0x80 [ 2845.743742] [] ? release_console_sem+0x1c2/0x1e0 [ 2845.743742] [] ? up+0x14/0x40 [ 2845.743742] [] smp_call_function_single+0x8e/0x110 [ 2845.743742] [] ? stop_this_cpu+0x0/0x40 [ 2845.743742] [] ? cpumask_next_and+0x1f/0x40 [ 2845.743742] [] smp_call_function_many+0x11a/0x200 [ 2845.743742] [] ? stop_this_cpu+0x0/0x40 [ 2845.743742] [] smp_call_function+0x21/0x30 [ 2845.743742] [] native_smp_send_stop+0x1e/0x50 [ 2845.743742] [] panic+0x55/0x110 [ 2845.743742] [] oops_end+0xb8/0xc0 [ 2845.743742] [] die+0x4f/0x70 [ 2845.743742] [] do_page_fault+0x269/0x610 [ 2845.743742] [] ? do_page_fault+0x0/0x610 [ 2845.743742] [] error_code+0x77/0x7c [ 2845.743742] [] ? __lock_acquire+0x6c/0xa80 [ 2845.743742] [] ? trace_hardirqs_on_caller+0x72/0x1c0 [ 2845.743742] [] lock_acquire+0x76/0xa0 [ 2845.743742] [] ? skb_dequeue+0x1d/0x70 [ 2845.743742] [] _spin_lock_irqsave+0x45/0x80 [ 2845.743742] [] ? skb_dequeue+0x1d/0x70 [ 2845.743742] [] skb_dequeue+0x1d/0x70 [ 2845.743742] [] skb_queue_purge+0x14/0x20 [ 2845.743742] [] hci_conn_del+0x10a/0x1c0 [bluetooth] [ 2845.743742] [] ? l2cap_disconn_ind+0x59/0xb0 [l2cap] [ 2845.743742] [] ? hci_conn_del_sysfs+0x8e/0xd0 [bluetooth] [ 2845.743742] [] hci_event_packet+0x5f8/0x31c0 [bluetooth] [ 2845.743742] [] ? sock_def_readable+0x59/0x80 [ 2845.743742] [] ? _read_unlock+0x1d/0x20 [ 2845.743742] [] ? hci_send_to_sock+0xe9/0x1d0 [bluetooth] [ 2845.743742] [] ? trace_hardirqs_on+0xb/0x10 [ 2845.743742] [] hci_rx_task+0x2ba/0x490 [bluetooth] [ 2845.743742] [] ? tasklet_action+0x31/0xc0 [ 2845.743742] [] tasklet_action+0x4c/0xc0 [ 2845.743742] [] __do_softirq+0xa7/0x170 [ 2845.743742] [] ? ack_apic_level+0x5c/0x1c0 [ 2845.743742] [] do_softirq+0x57/0x60 [ 2845.743742] [] irq_exit+0x7c/0x90 [ 2845.743742] [] do_IRQ+0x4b/0x90 [ 2845.743742] [] ? irq_exit+0x75/0x90 [ 2845.743742] [] common_interrupt+0x2c/0x34 [ 2845.743742] [] ? mwait_idle+0x4f/0x70 [ 2845.743742] [] cpu_idle+0x65/0xb0 [ 2845.743742] [] rest_init+0x4e/0x60 [ 2845.743742] ---[ end trace 4c985b38f02227a1 ]--- [ 2845.743742] Rebooting in 3 seconds.. My logitec bluetooth mouse trying connect to pc, but pc side reject the connection again and again. then panic happens. The reason is due to hci_conn_del_sysfs now called in hci_event_packet, the del work is done in a workqueue, so it's possible done before skb_queue_purge called. I move the hci_conn_del_sysfs after skb_queue_purge just as that before marcel's commit. Remove the hci_conn_del_sysfs in hci_conn_hash_flush as well due to hci_conn_del will deal with the work. Signed-off-by: Dave Young Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_conn.c | 4 ++-- net/bluetooth/hci_event.c | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index efd5c926cc1b..1181db08d9de 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -287,6 +287,8 @@ int hci_conn_del(struct hci_conn *conn) skb_queue_purge(&conn->data_q); + hci_conn_del_sysfs(conn); + return 0; } @@ -560,8 +562,6 @@ void hci_conn_hash_flush(struct hci_dev *hdev) c->state = BT_CLOSED; - hci_conn_del_sysfs(c); - hci_proto_disconn_cfm(c, 0x16); hci_conn_del(c); } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index c396542c2b82..55534244c3a0 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1019,8 +1019,6 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff if (conn) { conn->state = BT_CLOSED; - hci_conn_del_sysfs(conn); - hci_proto_disconn_cfm(conn, ev->reason); hci_conn_del(conn); } From 7585b97a48180f754ebdade1be94092e36bef365 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 25 Feb 2009 18:29:52 +0800 Subject: [PATCH 37/38] Bluetooth: Remove some pointless conditionals before kfree_skb() Remove some pointless conditionals before kfree_skb(). Signed-off-by: Wei Yongjun Signed-off-by: Marcel Holtmann --- net/bluetooth/cmtp/core.c | 3 +-- net/bluetooth/hci_core.c | 3 +-- net/bluetooth/l2cap.c | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c index c9cac7719efe..0073ec8495da 100644 --- a/net/bluetooth/cmtp/core.c +++ b/net/bluetooth/cmtp/core.c @@ -126,8 +126,7 @@ static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const session->reassembly[id] = nskb; - if (skb) - kfree_skb(skb); + kfree_skb(skb); } static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index ba78cc1eb8d9..cd061510b6bd 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1565,8 +1565,7 @@ static void hci_cmd_task(unsigned long arg) /* Send queued commands */ if (atomic_read(&hdev->cmd_cnt) && (skb = skb_dequeue(&hdev->cmd_q))) { - if (hdev->sent_cmd) - kfree_skb(hdev->sent_cmd); + kfree_skb(hdev->sent_cmd); if ((hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC))) { atomic_dec(&hdev->cmd_cnt); diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index db6fbf129be0..ca4d3b40d5ce 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -518,8 +518,7 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) BT_DBG("hcon %p conn %p, err %d", hcon, conn, err); - if (conn->rx_skb) - kfree_skb(conn->rx_skb); + kfree_skb(conn->rx_skb); /* Kill channels */ while ((sk = conn->chan_list.head)) { From b1fb06830dc870d862f7f80e276130c0ab84d59f Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 25 Feb 2009 18:09:33 +0800 Subject: [PATCH 38/38] Bluetooth: Remove some pointless conditionals before kfree_skb() Remove some pointless conditionals before kfree_skb(). Signed-off-by: Wei Yongjun Signed-off-by: Marcel Holtmann --- drivers/bluetooth/bfusb.c | 3 +-- drivers/bluetooth/hci_h4.c | 3 +-- drivers/bluetooth/hci_ll.c | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index d3f14bee0f19..2a00707aba3b 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -257,8 +257,7 @@ static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned ch if (hdr & 0x10) { BT_ERR("%s error in block", data->hdev->name); - if (data->reassembly) - kfree_skb(data->reassembly); + kfree_skb(data->reassembly); data->reassembly = NULL; return -EIO; } diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index b0fafb055996..c0ce8134814e 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -102,8 +102,7 @@ static int h4_close(struct hci_uart *hu) skb_queue_purge(&h4->txq); - if (h4->rx_skb) - kfree_skb(h4->rx_skb); + kfree_skb(h4->rx_skb); hu->priv = NULL; kfree(h4); diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c index b91d45a41b2f..5c65014635be 100644 --- a/drivers/bluetooth/hci_ll.c +++ b/drivers/bluetooth/hci_ll.c @@ -163,8 +163,7 @@ static int ll_close(struct hci_uart *hu) skb_queue_purge(&ll->tx_wait_q); skb_queue_purge(&ll->txq); - if (ll->rx_skb) - kfree_skb(ll->rx_skb); + kfree_skb(ll->rx_skb); hu->priv = NULL;