Bluetooth: Replace RFCOMM link mode with security level
authorMarcel Holtmann <marcel@holtmann.org>
Thu, 15 Jan 2009 20:58:40 +0000 (21:58 +0100)
committerMarcel Holtmann <marcel@holtmann.org>
Fri, 27 Feb 2009 05:14:26 +0000 (06:14 +0100)
Change the RFCOMM internals to use the new security levels and remove
the link mode details.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
include/net/bluetooth/rfcomm.h
net/bluetooth/rfcomm/core.c
net/bluetooth/rfcomm/sock.c

index 71b45f45968796bc8d55b2381d5a1bc246ace2a4..bda68d833dddfe91c1949c503c57bfcffd27c3af 100644 (file)
@@ -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);
index 68f70c5270c62c5c801fdb516c81354c9d48f006..db83f92d274c5a302137c8466bb95ed705105661 100644 (file)
@@ -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);
index d37a829a81e47d3dc3e41788d74955c9222fe72f..9986ef35c8904275983b88b70fcc100f9d37d26e 100644 (file)
@@ -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;