Bluetooth: notify userspace of security level change
authorGustavo Padovan <gustavo@padovan.org>
Sun, 13 May 2012 06:20:07 +0000 (03:20 -0300)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 14 May 2012 17:51:25 +0000 (13:51 -0400)
It fixes L2CAP socket based security level elevation during a
connection. The HID profile needs this (for keyboards) and it is the only
way to achieve the security level elevation when using the management
interface to talk to the kernel (hence the management enabling patch
being the one that exposes this issue).

It enables the userspace a security level change when the socket is
already connected and create a way to notify the socket the result of the
request. At the moment of the request the socket is made non writable, if
the request fails the connections closes, otherwise the socket is made
writable again, POLL_OUT is emmited.

Signed-off-by: Gustavo Padovan <gustavo@padovan.org>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
include/net/bluetooth/bluetooth.h
net/bluetooth/af_bluetooth.c
net/bluetooth/hci_event.c
net/bluetooth/l2cap_core.c
net/bluetooth/l2cap_sock.c

index 262ebd1747d4d2d0121f20a5114741d48c8a48fe..a65910bda3811551ba0ae089abb47f428dee3e94 100644 (file)
@@ -191,6 +191,7 @@ struct bt_sock {
        struct list_head accept_q;
        struct sock *parent;
        u32 defer_setup;
+       bool suspended;
 };
 
 struct bt_sock_list {
index 72eb187a5f605f9835d71c2de9f980e1e52d6a4e..6fb68a9743af7ebef18adc506f55d1f048bc9181 100644 (file)
@@ -450,7 +450,7 @@ unsigned int bt_sock_poll(struct file *file, struct socket *sock, poll_table *wa
                        sk->sk_state == BT_CONFIG)
                return mask;
 
-       if (sock_writeable(sk))
+       if (!bt_sk(sk)->suspended && sock_writeable(sk))
                mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
        else
                set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
index 6c065254afc03dcfbd11bad037791561b7f99f45..53680fe84628f51903088a4f94cb398896a8e100 100644 (file)
@@ -2039,6 +2039,12 @@ static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *
 
                clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
 
+               if (ev->status && conn->state == BT_CONNECTED) {
+                       hci_acl_disconn(conn, 0x13);
+                       hci_conn_put(conn);
+                       goto unlock;
+               }
+
                if (conn->state == BT_CONFIG) {
                        if (!ev->status)
                                conn->state = BT_CONNECTED;
@@ -2049,6 +2055,7 @@ static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *
                        hci_encrypt_cfm(conn, ev->status, ev->encrypt);
        }
 
+unlock:
        hci_dev_unlock(hdev);
 }
 
index 94552b33d528447eea4b604b996c7d90ba0ab036..6f9c25b633a604ce35824a8b466186df6ddb25c3 100644 (file)
@@ -4589,6 +4589,11 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
 
                if (!status && (chan->state == BT_CONNECTED ||
                                                chan->state == BT_CONFIG)) {
+                       struct sock *sk = chan->sk;
+
+                       bt_sk(sk)->suspended = false;
+                       sk->sk_state_change(sk);
+
                        l2cap_check_encryption(chan, encrypt);
                        l2cap_chan_unlock(chan);
                        continue;
index 29122ed28ea96965433fb6086756244d9eabd508..04e7c172d49c9e0ce421b7f378f693e28bda888f 100644 (file)
@@ -592,10 +592,14 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
                        sk->sk_state = BT_CONFIG;
                        chan->state = BT_CONFIG;
 
-               /* or for ACL link, under defer_setup time */
-               } else if (sk->sk_state == BT_CONNECT2 &&
-                                       bt_sk(sk)->defer_setup) {
-                       err = l2cap_chan_check_security(chan);
+               /* or for ACL link */
+               } else if ((sk->sk_state == BT_CONNECT2 &&
+                          bt_sk(sk)->defer_setup) ||
+                          sk->sk_state == BT_CONNECTED) {
+                       if (!l2cap_chan_check_security(chan))
+                               bt_sk(sk)->suspended = true;
+                       else
+                               sk->sk_state_change(sk);
                } else {
                        err = -EINVAL;
                }