Skip to content

Commit

Permalink
[Bluetooth] Allow security for outgoing L2CAP connections
Browse files Browse the repository at this point in the history
When requested the L2CAP layer will now enforce authentication and
encryption on outgoing connections. The usefulness of this feature
is kinda limited since it will not allow proper connection ownership
tracking until the authentication procedure has been finished. This
is a limitation of Bluetooth 2.0 and before and can only be fixed by
using Simple Pairing.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
  • Loading branch information
Marcel Holtmann committed Jul 14, 2008
1 parent 7cb127d commit b1235d7
Showing 1 changed file with 108 additions and 64 deletions.
172 changes: 108 additions & 64 deletions net/bluetooth/l2cap.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,21 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
static void l2cap_sock_timeout(unsigned long arg)
{
struct sock *sk = (struct sock *) arg;
int reason;

BT_DBG("sock %p state %d", sk, sk->sk_state);

bh_lock_sock(sk);
__l2cap_sock_close(sk, ETIMEDOUT);

if (sk->sk_state == BT_CONNECT &&
(l2cap_pi(sk)->link_mode & (L2CAP_LM_AUTH |
L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)))
reason = ECONNREFUSED;
else
reason = ETIMEDOUT;

__l2cap_sock_close(sk, reason);

bh_unlock_sock(sk);

l2cap_sock_kill(sk);
Expand Down Expand Up @@ -240,7 +250,7 @@ static void l2cap_chan_del(struct sock *sk, int err)
hci_conn_put(conn->hcon);
}

sk->sk_state = BT_CLOSED;
sk->sk_state = BT_CLOSED;
sock_set_flag(sk, SOCK_ZAPPED);

if (err)
Expand Down Expand Up @@ -307,14 +317,16 @@ 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) {
struct l2cap_conn_req req;
req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
req.psm = l2cap_pi(sk)->psm;
if (l2cap_check_link_mode(sk)) {
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_pi(sk)->ident = l2cap_get_ident(conn);

l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
L2CAP_CONN_REQ, sizeof(req), &req);
}
} else {
struct l2cap_info_req req;
req.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
Expand Down Expand Up @@ -349,14 +361,16 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
}

if (sk->sk_state == BT_CONNECT) {
struct l2cap_conn_req req;
req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
req.psm = l2cap_pi(sk)->psm;
if (l2cap_check_link_mode(sk)) {
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_pi(sk)->ident = l2cap_get_ident(conn);

l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
L2CAP_CONN_REQ, sizeof(req), &req);
}
} else if (sk->sk_state == BT_CONNECT2) {
struct l2cap_conn_rsp rsp;
rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
Expand Down Expand Up @@ -455,7 +469,8 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)

conn->feat_mask = 0;

setup_timer(&conn->info_timer, l2cap_info_timeout, (unsigned long)conn);
setup_timer(&conn->info_timer, l2cap_info_timeout,
(unsigned long) conn);

spin_lock_init(&conn->lock);
rwlock_init(&conn->chan_list.lock);
Expand Down Expand Up @@ -567,7 +582,7 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
while ((sk = bt_accept_dequeue(parent, NULL)))
l2cap_sock_close(sk);

parent->sk_state = BT_CLOSED;
parent->sk_state = BT_CLOSED;
sock_set_flag(parent, SOCK_ZAPPED);
}

Expand Down Expand Up @@ -610,9 +625,8 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
l2cap_send_cmd(conn, l2cap_get_ident(conn),
L2CAP_DISCONN_REQ, sizeof(req), &req);
} else {
} else
l2cap_chan_del(sk, reason);
}
break;

case BT_CONNECT:
Expand Down Expand Up @@ -681,9 +695,9 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int p
sock_reset_flag(sk, SOCK_ZAPPED);

sk->sk_protocol = proto;
sk->sk_state = BT_OPEN;
sk->sk_state = BT_OPEN;

setup_timer(&sk->sk_timer, l2cap_sock_timeout, (unsigned long)sk);
setup_timer(&sk->sk_timer, l2cap_sock_timeout, (unsigned long) sk);

bt_sock_link(&l2cap_sk_list, sk);
return sk;
Expand Down Expand Up @@ -1201,7 +1215,8 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
__l2cap_sock_close(sk, 0);

if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime);
err = bt_sock_wait_state(sk, BT_CLOSED,
sk->sk_lingertime);
}
release_sock(sk);
return err;
Expand Down Expand Up @@ -1245,6 +1260,11 @@ 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 */
Expand Down Expand Up @@ -1778,7 +1798,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr

default:
sk->sk_state = BT_DISCONN;
sk->sk_err = ECONNRESET;
sk->sk_err = ECONNRESET;
l2cap_sock_set_timer(sk, HZ * 5);
{
struct l2cap_disconn_req req;
Expand Down Expand Up @@ -2151,9 +2171,7 @@ static int l2cap_auth_cfm(struct hci_conn *hcon, u8 status)
{
struct l2cap_chan_list *l;
struct l2cap_conn *conn = hcon->l2cap_data;
struct l2cap_conn_rsp rsp;
struct sock *sk;
int result;

if (!conn)
return 0;
Expand All @@ -2169,47 +2187,61 @@ static int l2cap_auth_cfm(struct hci_conn *hcon, u8 status)

bh_lock_sock(sk);

if (sk->sk_state != BT_CONNECT2) {
bh_unlock_sock(sk);
continue;
}

if ((pi->link_mode & (L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)) &&
!(hcon->link_mode & HCI_LM_ENCRYPT)) {
!(hcon->link_mode & HCI_LM_ENCRYPT) &&
!status) {
bh_unlock_sock(sk);
continue;
}

if (!status) {
sk->sk_state = BT_CONFIG;
result = 0;
} else {
sk->sk_state = BT_DISCONN;
l2cap_sock_set_timer(sk, HZ/10);
result = L2CAP_CR_SEC_BLOCK;
}
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(0);
l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
L2CAP_CONN_RSP, sizeof(rsp), &rsp);
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(0);
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)
{
struct l2cap_chan_list *l;
struct l2cap_conn *conn = hcon->l2cap_data;
struct l2cap_conn_rsp rsp;
struct sock *sk;
int result;

if (!conn)
return 0;
Expand All @@ -2234,34 +2266,46 @@ static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
continue;
}

if (sk->sk_state != BT_CONNECT2) {
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;

if (!status) {
sk->sk_state = BT_CONFIG;
result = 0;
} else {
sk->sk_state = BT_DISCONN;
l2cap_sock_set_timer(sk, HZ/10);
result = L2CAP_CR_SEC_BLOCK;
}
l2cap_pi(sk)->ident = l2cap_get_ident(conn);

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(0);
l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
L2CAP_CONN_RSP, sizeof(rsp), &rsp);
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 (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE)
hci_conn_change_link_key(hcon);
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(0);
l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
L2CAP_CONN_RSP, sizeof(rsp), &rsp);
}

bh_unlock_sock(sk);
}

read_unlock(&l->lock);

return 0;
}

Expand Down

0 comments on commit b1235d7

Please sign in to comment.