Bluetooth: Fix race condition with L2CAP information request
[pandora-kernel.git] / net / bluetooth / l2cap.c
index b2d279c..07fdbc7 100644 (file)
@@ -50,7 +50,7 @@
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/l2cap.h>
 
-#define VERSION "2.11"
+#define VERSION "2.12"
 
 static u32 l2cap_feat_mask = 0x0000;
 
@@ -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)
@@ -306,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);
@@ -441,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);
 }
 
@@ -1248,10 +1267,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 +1411,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 +1421,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));
@@ -1757,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);
        }
 
@@ -1827,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;
@@ -2146,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;
 }
@@ -2404,6 +2445,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);