Bluetooth: notify userspace of security level change
[linux-flexiantxendom0-3.2.10.git] / net / bluetooth / l2cap_sock.c
index 836d12e..04e7c17 100644 (file)
@@ -3,6 +3,7 @@
    Copyright (C) 2000-2001 Qualcomm Incorporated
    Copyright (C) 2009-2010 Gustavo F. Padovan <gustavo@padovan.org>
    Copyright (C) 2010 Google Inc.
+   Copyright (C) 2011 ProFUSION Embedded Systems
 
    Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
 
@@ -26,6 +27,9 @@
 
 /* Bluetooth L2CAP sockets. */
 
+#include <linux/security.h>
+#include <linux/export.h>
+
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/l2cap.h>
@@ -78,7 +82,7 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
        }
 
        if (la.l2_cid)
-               err = l2cap_add_scid(chan, la.l2_cid);
+               err = l2cap_add_scid(chan, __le16_to_cpu(la.l2_cid));
        else
                err = l2cap_add_psm(chan, &la.l2_bdaddr, la.l2_psm);
 
@@ -119,70 +123,18 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
        if (la.l2_cid && la.l2_psm)
                return -EINVAL;
 
-       lock_sock(sk);
-
-       if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED
-                       && !(la.l2_psm || la.l2_cid)) {
-               err = -EINVAL;
-               goto done;
-       }
-
-       switch (chan->mode) {
-       case L2CAP_MODE_BASIC:
-               break;
-       case L2CAP_MODE_ERTM:
-       case L2CAP_MODE_STREAMING:
-               if (!disable_ertm)
-                       break;
-               /* fall through */
-       default:
-               err = -ENOTSUPP;
-               goto done;
-       }
-
-       switch (sk->sk_state) {
-       case BT_CONNECT:
-       case BT_CONNECT2:
-       case BT_CONFIG:
-               /* Already connecting */
-               goto wait;
-
-       case BT_CONNECTED:
-               /* Already connected */
-               err = -EISCONN;
-               goto done;
-
-       case BT_OPEN:
-       case BT_BOUND:
-               /* Can connect */
-               break;
-
-       default:
-               err = -EBADFD;
-               goto done;
-       }
-
-       /* PSM must be odd and lsb of upper byte must be 0 */
-       if ((__le16_to_cpu(la.l2_psm) & 0x0101) != 0x0001 && !la.l2_cid &&
-                                       chan->chan_type != L2CAP_CHAN_RAW) {
-               err = -EINVAL;
-               goto done;
-       }
-
-       /* Set destination address and psm */
-       bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr);
-       chan->psm = la.l2_psm;
-       chan->dcid = la.l2_cid;
-
-       err = l2cap_chan_connect(l2cap_pi(sk)->chan);
+       err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid),
+                               &la.l2_bdaddr);
        if (err)
-               goto done;
+               return err;
+
+       lock_sock(sk);
 
-wait:
        err = bt_sock_wait_state(sk, BT_CONNECTED,
                        sock_sndtimeo(sk, flags & O_NONBLOCK));
-done:
+
        release_sock(sk);
+
        return err;
 }
 
@@ -467,6 +419,16 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
 
                break;
 
+       case BT_CHANNEL_POLICY:
+               if (!enable_hs) {
+                       err = -ENOPROTOOPT;
+                       break;
+               }
+
+               if (put_user(chan->chan_policy, (u32 __user *) optval))
+                       err = -EFAULT;
+               break;
+
        default:
                err = -ENOPROTOOPT;
                break;
@@ -613,8 +575,13 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
 
                chan->sec_level = sec.level;
 
+               if (!chan->conn)
+                       break;
+
                conn = chan->conn;
-               if (conn && chan->scid == L2CAP_CID_LE_DATA) {
+
+               /*change security for LE channels */
+               if (chan->scid == L2CAP_CID_LE_DATA) {
                        if (!conn->hcon->out) {
                                err = -EINVAL;
                                break;
@@ -622,9 +589,19 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
 
                        if (smp_conn_security(conn, sec.level))
                                break;
-
-                       err = 0;
                        sk->sk_state = BT_CONFIG;
+                       chan->state = BT_CONFIG;
+
+               /* 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;
                }
                break;
 
@@ -690,6 +667,31 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
                        clear_bit(FLAG_FORCE_ACTIVE, &chan->flags);
                break;
 
+       case BT_CHANNEL_POLICY:
+               if (!enable_hs) {
+                       err = -ENOPROTOOPT;
+                       break;
+               }
+
+               if (get_user(opt, (u32 __user *) optval)) {
+                       err = -EFAULT;
+                       break;
+               }
+
+               if (opt > BT_CHANNEL_POLICY_AMP_PREFERRED) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (chan->mode != L2CAP_MODE_ERTM &&
+                               chan->mode != L2CAP_MODE_STREAMING) {
+                       err = -EOPNOTSUPP;
+                       break;
+               }
+
+               chan->chan_policy = (u8) opt;
+               break;
+
        default:
                err = -ENOPROTOOPT;
                break;
@@ -721,7 +723,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms
                return -ENOTCONN;
        }
 
-       err = l2cap_chan_send(chan, msg, len);
+       err = l2cap_chan_send(chan, msg, len, sk->sk_priority);
 
        release_sock(sk);
        return err;
@@ -737,6 +739,7 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct ms
 
        if (sk->sk_state == BT_CONNECT2 && bt_sk(sk)->defer_setup) {
                sk->sk_state = BT_CONFIG;
+               pi->chan->state = BT_CONFIG;
 
                __l2cap_connect_rsp_defer(pi->chan);
                release_sock(sk);
@@ -787,7 +790,7 @@ static void l2cap_sock_kill(struct sock *sk)
        if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket)
                return;
 
-       BT_DBG("sk %p state %d", sk, sk->sk_state);
+       BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state));
 
        /* Kill poor orphan */
 
@@ -799,7 +802,8 @@ static void l2cap_sock_kill(struct sock *sk)
 static int l2cap_sock_shutdown(struct socket *sock, int how)
 {
        struct sock *sk = sock->sk;
-       struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+       struct l2cap_chan *chan;
+       struct l2cap_conn *conn;
        int err = 0;
 
        BT_DBG("sock %p, sk %p", sock, sk);
@@ -807,13 +811,24 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
        if (!sk)
                return 0;
 
+       chan = l2cap_pi(sk)->chan;
+       conn = chan->conn;
+
+       if (conn)
+               mutex_lock(&conn->chan_lock);
+
+       l2cap_chan_lock(chan);
        lock_sock(sk);
+
        if (!sk->sk_shutdown) {
                if (chan->mode == L2CAP_MODE_ERTM)
                        err = __l2cap_wait_ack(sk);
 
                sk->sk_shutdown = SHUTDOWN_MASK;
+
+               release_sock(sk);
                l2cap_chan_close(chan, 0);
+               lock_sock(sk);
 
                if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
                        err = bt_sock_wait_state(sk, BT_CLOSED,
@@ -824,6 +839,11 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
                err = -sk->sk_err;
 
        release_sock(sk);
+       l2cap_chan_unlock(chan);
+
+       if (conn)
+               mutex_unlock(&conn->chan_lock);
+
        return err;
 }
 
@@ -853,6 +873,8 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(void *data)
        if (!sk)
                return NULL;
 
+       bt_sock_reclassify_lock(sk, BTPROTO_L2CAP);
+
        l2cap_sock_init(sk, parent);
 
        return l2cap_pi(sk)->chan;
@@ -864,8 +886,12 @@ static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb)
        struct sock *sk = data;
        struct l2cap_pinfo *pi = l2cap_pi(sk);
 
-       if (pi->rx_busy_skb)
-               return -ENOMEM;
+       lock_sock(sk);
+
+       if (pi->rx_busy_skb) {
+               err = -ENOMEM;
+               goto done;
+       }
 
        err = sock_queue_rcv_skb(sk, skb);
 
@@ -884,6 +910,9 @@ static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb)
                err = 0;
        }
 
+done:
+       release_sock(sk);
+
        return err;
 }
 
@@ -901,12 +930,22 @@ static void l2cap_sock_state_change_cb(void *data, int state)
        sk->sk_state = state;
 }
 
+static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
+                                              unsigned long len, int nb,
+                                              int *err)
+{
+       struct sock *sk = chan->sk;
+
+       return bt_skb_send_alloc(sk, len, nb, err);
+}
+
 static struct l2cap_ops l2cap_chan_ops = {
        .name           = "L2CAP Socket Interface",
        .new_connection = l2cap_sock_new_connection_cb,
        .recv           = l2cap_sock_recv_cb,
        .close          = l2cap_sock_close_cb,
        .state_change   = l2cap_sock_state_change_cb,
+       .alloc_skb      = l2cap_sock_alloc_skb_cb,
 };
 
 static void l2cap_sock_destruct(struct sock *sk)
@@ -943,8 +982,11 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
                chan->fcs  = pchan->fcs;
                chan->max_tx = pchan->max_tx;
                chan->tx_win = pchan->tx_win;
+               chan->tx_win_max = pchan->tx_win_max;
                chan->sec_level = pchan->sec_level;
                chan->flags = pchan->flags;
+
+               security_sk_clone(parent, sk);
        } else {
 
                switch (sk->sk_type) {
@@ -971,6 +1013,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
                chan->max_tx = L2CAP_DEFAULT_MAX_TX;
                chan->fcs  = L2CAP_FCS_CRC16;
                chan->tx_win = L2CAP_DEFAULT_TX_WINDOW;
+               chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW;
                chan->sec_level = BT_SECURITY_LOW;
                chan->flags = 0;
                set_bit(FLAG_FORCE_ACTIVE, &chan->flags);