Commit b1235d79 authored by Marcel Holtmann's avatar Marcel Holtmann

[Bluetooth] Allow security for outgoing L2CAP connections

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: default avatarMarcel Holtmann <marcel@holtmann.org>
parent 7cb127d5
...@@ -76,11 +76,21 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, ...@@ -76,11 +76,21 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
static void l2cap_sock_timeout(unsigned long arg) static void l2cap_sock_timeout(unsigned long arg)
{ {
struct sock *sk = (struct sock *) arg; struct sock *sk = (struct sock *) arg;
int reason;
BT_DBG("sock %p state %d", sk, sk->sk_state); BT_DBG("sock %p state %d", sk, sk->sk_state);
bh_lock_sock(sk); 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); bh_unlock_sock(sk);
l2cap_sock_kill(sk); l2cap_sock_kill(sk);
...@@ -307,6 +317,7 @@ static void l2cap_do_start(struct sock *sk) ...@@ -307,6 +317,7 @@ static void l2cap_do_start(struct sock *sk)
struct l2cap_conn *conn = l2cap_pi(sk)->conn; 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_SENT) {
if (l2cap_check_link_mode(sk)) {
struct l2cap_conn_req req; struct l2cap_conn_req req;
req.scid = cpu_to_le16(l2cap_pi(sk)->scid); req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
req.psm = l2cap_pi(sk)->psm; req.psm = l2cap_pi(sk)->psm;
...@@ -315,6 +326,7 @@ static void l2cap_do_start(struct sock *sk) ...@@ -315,6 +326,7 @@ static void l2cap_do_start(struct sock *sk)
l2cap_send_cmd(conn, l2cap_pi(sk)->ident, l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
L2CAP_CONN_REQ, sizeof(req), &req); L2CAP_CONN_REQ, sizeof(req), &req);
}
} else { } else {
struct l2cap_info_req req; struct l2cap_info_req req;
req.type = cpu_to_le16(L2CAP_IT_FEAT_MASK); req.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
...@@ -349,6 +361,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) ...@@ -349,6 +361,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
} }
if (sk->sk_state == BT_CONNECT) { if (sk->sk_state == BT_CONNECT) {
if (l2cap_check_link_mode(sk)) {
struct l2cap_conn_req req; struct l2cap_conn_req req;
req.scid = cpu_to_le16(l2cap_pi(sk)->scid); req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
req.psm = l2cap_pi(sk)->psm; req.psm = l2cap_pi(sk)->psm;
...@@ -357,6 +370,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) ...@@ -357,6 +370,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
l2cap_send_cmd(conn, l2cap_pi(sk)->ident, l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
L2CAP_CONN_REQ, sizeof(req), &req); L2CAP_CONN_REQ, sizeof(req), &req);
}
} else if (sk->sk_state == BT_CONNECT2) { } else if (sk->sk_state == BT_CONNECT2) {
struct l2cap_conn_rsp rsp; struct l2cap_conn_rsp rsp;
rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
...@@ -455,7 +469,8 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) ...@@ -455,7 +469,8 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
conn->feat_mask = 0; 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); spin_lock_init(&conn->lock);
rwlock_init(&conn->chan_list.lock); rwlock_init(&conn->chan_list.lock);
...@@ -610,9 +625,8 @@ static void __l2cap_sock_close(struct sock *sk, int reason) ...@@ -610,9 +625,8 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
req.scid = cpu_to_le16(l2cap_pi(sk)->scid); req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
l2cap_send_cmd(conn, l2cap_get_ident(conn), l2cap_send_cmd(conn, l2cap_get_ident(conn),
L2CAP_DISCONN_REQ, sizeof(req), &req); L2CAP_DISCONN_REQ, sizeof(req), &req);
} else { } else
l2cap_chan_del(sk, reason); l2cap_chan_del(sk, reason);
}
break; break;
case BT_CONNECT: case BT_CONNECT:
...@@ -683,7 +697,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int p ...@@ -683,7 +697,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int p
sk->sk_protocol = proto; 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); bt_sock_link(&l2cap_sk_list, sk);
return sk; return sk;
...@@ -1201,7 +1215,8 @@ static int l2cap_sock_shutdown(struct socket *sock, int how) ...@@ -1201,7 +1215,8 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
__l2cap_sock_close(sk, 0); __l2cap_sock_close(sk, 0);
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) 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); release_sock(sk);
return err; return err;
...@@ -1245,6 +1260,11 @@ static void l2cap_chan_ready(struct sock *sk) ...@@ -1245,6 +1260,11 @@ static void l2cap_chan_ready(struct sock *sk)
*/ */
parent->sk_data_ready(parent, 0); 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 */ /* Copy frame to all raw sockets on that connection */
...@@ -2151,9 +2171,7 @@ static int l2cap_auth_cfm(struct hci_conn *hcon, u8 status) ...@@ -2151,9 +2171,7 @@ static int l2cap_auth_cfm(struct hci_conn *hcon, u8 status)
{ {
struct l2cap_chan_list *l; struct l2cap_chan_list *l;
struct l2cap_conn *conn = hcon->l2cap_data; struct l2cap_conn *conn = hcon->l2cap_data;
struct l2cap_conn_rsp rsp;
struct sock *sk; struct sock *sk;
int result;
if (!conn) if (!conn)
return 0; return 0;
...@@ -2169,23 +2187,37 @@ static int l2cap_auth_cfm(struct hci_conn *hcon, u8 status) ...@@ -2169,23 +2187,37 @@ static int l2cap_auth_cfm(struct hci_conn *hcon, u8 status)
bh_lock_sock(sk); bh_lock_sock(sk);
if (sk->sk_state != BT_CONNECT2) { if ((pi->link_mode & (L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)) &&
!(hcon->link_mode & HCI_LM_ENCRYPT) &&
!status) {
bh_unlock_sock(sk); bh_unlock_sock(sk);
continue; continue;
} }
if ((pi->link_mode & (L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)) && if (sk->sk_state == BT_CONNECT) {
!(hcon->link_mode & HCI_LM_ENCRYPT)) { if (!status) {
bh_unlock_sock(sk); struct l2cap_conn_req req;
continue; 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) { if (!status) {
sk->sk_state = BT_CONFIG; sk->sk_state = BT_CONFIG;
result = 0; result = L2CAP_CR_SUCCESS;
} else { } else {
sk->sk_state = BT_DISCONN; sk->sk_state = BT_DISCONN;
l2cap_sock_set_timer(sk, HZ/10); l2cap_sock_set_timer(sk, HZ / 10);
result = L2CAP_CR_SEC_BLOCK; result = L2CAP_CR_SEC_BLOCK;
} }
...@@ -2195,11 +2227,13 @@ static int l2cap_auth_cfm(struct hci_conn *hcon, u8 status) ...@@ -2195,11 +2227,13 @@ static int l2cap_auth_cfm(struct hci_conn *hcon, u8 status)
rsp.status = cpu_to_le16(0); rsp.status = cpu_to_le16(0);
l2cap_send_cmd(conn, l2cap_pi(sk)->ident, l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
L2CAP_CONN_RSP, sizeof(rsp), &rsp); L2CAP_CONN_RSP, sizeof(rsp), &rsp);
}
bh_unlock_sock(sk); bh_unlock_sock(sk);
} }
read_unlock(&l->lock); read_unlock(&l->lock);
return 0; return 0;
} }
...@@ -2207,9 +2241,7 @@ static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) ...@@ -2207,9 +2241,7 @@ static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
{ {
struct l2cap_chan_list *l; struct l2cap_chan_list *l;
struct l2cap_conn *conn = hcon->l2cap_data; struct l2cap_conn *conn = hcon->l2cap_data;
struct l2cap_conn_rsp rsp;
struct sock *sk; struct sock *sk;
int result;
if (!conn) if (!conn)
return 0; return 0;
...@@ -2234,17 +2266,30 @@ static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) ...@@ -2234,17 +2266,30 @@ static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
continue; continue;
} }
if (sk->sk_state != BT_CONNECT2) { if (sk->sk_state == BT_CONNECT) {
bh_unlock_sock(sk); if (!status) {
continue; 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) { if (!status) {
sk->sk_state = BT_CONFIG; sk->sk_state = BT_CONFIG;
result = 0; result = L2CAP_CR_SUCCESS;
} else { } else {
sk->sk_state = BT_DISCONN; sk->sk_state = BT_DISCONN;
l2cap_sock_set_timer(sk, HZ/10); l2cap_sock_set_timer(sk, HZ / 10);
result = L2CAP_CR_SEC_BLOCK; result = L2CAP_CR_SEC_BLOCK;
} }
...@@ -2254,14 +2299,13 @@ static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) ...@@ -2254,14 +2299,13 @@ static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
rsp.status = cpu_to_le16(0); rsp.status = cpu_to_le16(0);
l2cap_send_cmd(conn, l2cap_pi(sk)->ident, l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
L2CAP_CONN_RSP, sizeof(rsp), &rsp); L2CAP_CONN_RSP, sizeof(rsp), &rsp);
}
if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE)
hci_conn_change_link_key(hcon);
bh_unlock_sock(sk); bh_unlock_sock(sk);
} }
read_unlock(&l->lock); read_unlock(&l->lock);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment