Skip to content

Commit

Permalink
Bluetooth: L2CAP: Handle LE connection response security errors
Browse files Browse the repository at this point in the history
If there are security errors bump the security of the connection and
retry:

7C:7A:91:18:82:5F (public)> l2cap-connect 0x0080
[bt] [DBG] bt_l2cap_chan_connect: (0x0011ae8c) conn 0x0011b940 chan 0x00119700 psm 0x0080
[bt] [DBG] l2cap_chan_tx_init: (0x0011ae8c) chan 0x00119700
[bt] [DBG] l2cap_chan_rx_init: (0x0011ae8c) chan 0x00119700
[bt] [DBG] bt_l2cap_chan_add: (0x0011ae8c) conn 0x0011b940 chan 0x00119700
L2CAP connection pending
7C:7A:91:18:82:5F (public)> [bt] [DBG] bt_l2cap_recv: (0x0011ced8) Packet for CID 5 len 14
[bt] [DBG] l2cap_chan_recv: (0x0011ced8) chan 0x0011bd00 len 14
[bt] [DBG] l2cap_recv: (0x0011ced8) Signaling code 0x15 ident 2 len 10
[bt] [DBG] le_conn_rsp: (0x0011ced8) dcid 0x0000 mtu 0 mps 0 credits 0 result 0x0005
[bt] [DBG] bt_l2cap_recv: (0x0011ced8) Packet for CID 6 len 7
[bt] [DBG] l2cap_chan_recv: (0x0011ced8) chan 0x0011bf00 len 7
[bt] [DBG] bt_l2cap_recv: (0x0011ced8) Packet for CID 6 len 65
[bt] [DBG] l2cap_chan_recv: (0x0011ced8) chan 0x0011bf00 len 65
[bt] [DBG] bt_l2cap_recv: (0x0011ced8) Packet for CID 6 len 17
[bt] [DBG] l2cap_chan_recv: (0x0011ced8) chan 0x0011bf00 len 17
[bt] [DBG] bt_l2cap_recv: (0x0011ced8) Packet for CID 6 len 17
[bt] [DBG] l2cap_chan_recv: (0x0011ced8) chan 0x0011bf00 len 17
Security changed: 7C:7A:91:18:82:5F (public) level 2
[bt] [DBG] bt_l2cap_recv: (0x0011ced8) Packet for CID 5 len 14
[bt] [DBG] l2cap_chan_recv: (0x0011ced8) chan 0x0011bd00 len 14
[bt] [DBG] l2cap_recv: (0x0011ced8) Signaling code 0x15 ident 3 len 10
[bt] [DBG] le_conn_rsp: (0x0011ced8) dcid 0x0040 mtu 672 mps 230 credits 10 result 0x0000
Channel 0x00119700 connected

Change-Id: I2402fd86cc6bdf41a537053325e0a1973c23377a
Signed-off-by: Luiz Augusto von Dentz <[email protected]>
  • Loading branch information
Vudentz authored and Johan Hedberg committed Nov 4, 2016
1 parent d1ae81e commit 807bd5f
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 63 deletions.
4 changes: 2 additions & 2 deletions include/bluetooth/l2cap.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ struct bt_l2cap_chan {
struct nano_delayed_work rtx_work;
#if defined(CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL)
bt_l2cap_chan_state_t state;
/** Remote PSM to be connected */
uint16_t psm;
/** Helps match request context during CoC */
uint8_t ident;
bt_security_t required_sec_level;
Expand Down Expand Up @@ -134,8 +136,6 @@ struct bt_l2cap_br_chan {
struct bt_l2cap_br_endpoint tx;
/* For internal use only */
atomic_t flags[1];
/** Remote PSM to be connected */
uint16_t psm;
};

/** @brief L2CAP Channel operations structure. */
Expand Down
186 changes: 132 additions & 54 deletions subsys/bluetooth/host/l2cap.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,12 @@ void bt_l2cap_chan_del(struct bt_l2cap_chan *chan)
chan->conn = NULL;

destroy:
#if defined(CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL)
/* Reset internal members of common channel */
chan->state = BT_L2CAP_DISCONNECTED;
chan->psm = 0;
#endif

if (chan->destroy) {
chan->destroy(chan);
}
Expand Down Expand Up @@ -321,6 +327,77 @@ void bt_l2cap_disconnected(struct bt_conn *conn)
conn->channels = NULL;
}

#if defined(CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL)
static void l2cap_chan_send_req(struct bt_l2cap_le_chan *chan,
struct net_buf *buf, uint32_t ticks)
{
/* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part A] page 126:
*
* The value of this timer is implementation-dependent but the minimum
* initial value is 1 second and the maximum initial value is 60
* seconds. One RTX timer shall exist for each outstanding signaling
* request, including each Echo Request. The timer disappears on the
* final expiration, when the response is received, or the physical
* link is lost.
*/
if (ticks) {
nano_delayed_work_submit(&chan->chan.rtx_work, ticks);
} else {
nano_delayed_work_cancel(&chan->chan.rtx_work);
}

bt_l2cap_send(chan->chan.conn, BT_L2CAP_CID_LE_SIG, buf);
}

static int l2cap_le_conn_req(struct bt_l2cap_le_chan *ch)
{
struct net_buf *buf;
struct bt_l2cap_sig_hdr *hdr;
struct bt_l2cap_le_conn_req *req;

buf = bt_l2cap_create_pdu(&le_sig, 0);
if (!buf) {
BT_ERR("Unable to send L2CAP connection request");
return -ENOMEM;
}

ch->chan.ident = get_ident();

hdr = net_buf_add(buf, sizeof(*hdr));
hdr->code = BT_L2CAP_LE_CONN_REQ;
hdr->ident = ch->chan.ident;
hdr->len = sys_cpu_to_le16(sizeof(*req));

req = net_buf_add(buf, sizeof(*req));
req->psm = sys_cpu_to_le16(ch->chan.psm);
req->scid = sys_cpu_to_le16(ch->rx.cid);
req->mtu = sys_cpu_to_le16(ch->rx.mtu);
req->mps = sys_cpu_to_le16(ch->rx.mps);
req->credits = sys_cpu_to_le16(L2CAP_LE_MAX_CREDITS);

l2cap_chan_send_req(ch, buf, L2CAP_CONN_TIMEOUT);

return 0;
}

static void l2cap_le_encrypt_change(struct bt_l2cap_chan *chan, uint8_t status)
{
/* Skip channels already connected or with a pending request */
if (chan->state != BT_L2CAP_CONNECT || chan->ident) {
return;
}

if (status) {
l2cap_detach_chan(chan->conn, chan);
bt_l2cap_chan_del(chan);
return;
}

/* Retry to connect */
l2cap_le_conn_req(BT_L2CAP_LE_CHAN(chan));
}
#endif /* CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL */

void bt_l2cap_encrypt_change(struct bt_conn *conn, uint8_t hci_status)
{
struct bt_l2cap_chan *chan;
Expand All @@ -333,6 +410,10 @@ void bt_l2cap_encrypt_change(struct bt_conn *conn, uint8_t hci_status)
#endif /* CONFIG_BLUETOOTH_BREDR */

for (chan = conn->channels; chan; chan = chan->_next) {
#if defined(CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL)
l2cap_le_encrypt_change(chan, hci_status);
#endif

if (chan->ops->encrypt_change) {
chan->ops->encrypt_change(chan, hci_status);
}
Expand Down Expand Up @@ -651,6 +732,9 @@ static void le_conn_req(struct bt_l2cap *l2cap, uint8_t ident,
l2cap_chan_rx_init(ch);
l2cap_chan_rx_give_credits(ch, L2CAP_LE_MAX_CREDITS);

/* Update state */
chan->state = BT_L2CAP_CONNECTED;

if (chan->ops && chan->ops->connected) {
chan->ops->connected(chan);
}
Expand Down Expand Up @@ -750,6 +834,33 @@ static void le_disconn_req(struct bt_l2cap *l2cap, uint8_t ident,
bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
}

static int l2cap_change_security(struct bt_l2cap_le_chan *chan, uint16_t err)
{
switch (err) {
case BT_L2CAP_ERR_ENCRYPTION:
if (chan->chan.required_sec_level >= BT_SECURITY_MEDIUM) {
return -EALREADY;
}
chan->chan.required_sec_level = BT_SECURITY_MEDIUM;
break;
case BT_L2CAP_ERR_AUTHENTICATION:
if (chan->chan.required_sec_level < BT_SECURITY_MEDIUM) {
chan->chan.required_sec_level = BT_SECURITY_MEDIUM;
} else if (chan->chan.required_sec_level < BT_SECURITY_HIGH) {
chan->chan.required_sec_level = BT_SECURITY_HIGH;
} else if (chan->chan.required_sec_level < BT_SECURITY_FIPS) {
chan->chan.required_sec_level = BT_SECURITY_FIPS;
} else {
return -EALREADY;
}
break;
default:
return -EINVAL;
}

return bt_conn_security(chan->chan.conn, chan->chan.required_sec_level);
}

static void le_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
struct net_buf *buf)
{
Expand All @@ -772,7 +883,10 @@ static void le_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
BT_DBG("dcid 0x%04x mtu %u mps %u credits %u result 0x%04x", dcid,
mtu, mps, credits, result);

if (result == BT_L2CAP_SUCCESS) {
/* Keep the channel in case of security errors */
if (result == BT_L2CAP_SUCCESS ||
result == BT_L2CAP_ERR_AUTHENTICATION ||
result == BT_L2CAP_ERR_ENCRYPTION) {
chan = l2cap_lookup_ident(conn, ident);
} else {
chan = l2cap_remove_ident(conn, ident);
Expand All @@ -783,10 +897,14 @@ static void le_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
return;
}

/* Cancel RTX work */
nano_delayed_work_cancel(&chan->chan.rtx_work);

/* Reset ident since it got a response */
chan->chan.ident = 0;

switch (result) {
case BT_L2CAP_SUCCESS:
/* Reset ident since it is no longer pending */
chan->chan.ident = 0;
chan->tx.cid = dcid;
chan->tx.mtu = mtu;
chan->tx.mps = mps;
Expand All @@ -799,11 +917,14 @@ static void le_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
l2cap_chan_tx_give_credits(chan, credits);
l2cap_chan_rx_give_credits(chan, L2CAP_LE_MAX_CREDITS);

/* Cancel RTX work */
nano_delayed_work_cancel(&chan->chan.rtx_work);

break;
/* TODO: Retry on Authentication and Encryption errors */
case BT_L2CAP_ERR_AUTHENTICATION:
case BT_L2CAP_ERR_ENCRYPTION:
/* If security needs changing wait it to be completed */
if (l2cap_change_security(chan, result) == 0) {
return;
}
l2cap_detach_chan(conn, &chan->chan);
default:
bt_l2cap_chan_del(&chan->chan);
}
Expand Down Expand Up @@ -1278,34 +1399,9 @@ struct bt_l2cap_chan *bt_l2cap_le_lookup_rx_cid(struct bt_conn *conn,
}

#if defined(CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL)
static void l2cap_chan_send_req(struct bt_l2cap_le_chan *chan,
struct net_buf *buf, uint32_t ticks)
{
/* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part A] page 126:
*
* The value of this timer is implementation-dependent but the minimum
* initial value is 1 second and the maximum initial value is 60
* seconds. One RTX timer shall exist for each outstanding signaling
* request, including each Echo Request. The timer disappears on the
* final expiration, when the response is received, or the physical
* link is lost.
*/
if (ticks) {
nano_delayed_work_submit(&chan->chan.rtx_work, ticks);
} else {
nano_delayed_work_cancel(&chan->chan.rtx_work);
}

bt_l2cap_send(chan->chan.conn, BT_L2CAP_CID_LE_SIG, buf);
}

static int l2cap_le_connect(struct bt_conn *conn, struct bt_l2cap_le_chan *ch,
uint16_t psm)
{
struct net_buf *buf;
struct bt_l2cap_sig_hdr *hdr;
struct bt_l2cap_le_conn_req *req;

if (psm < L2CAP_LE_PSM_START || psm > L2CAP_LE_PSM_END) {
return -EINVAL;
}
Expand All @@ -1317,29 +1413,10 @@ static int l2cap_le_connect(struct bt_conn *conn, struct bt_l2cap_le_chan *ch,
return -ENOMEM;
}

buf = bt_l2cap_create_pdu(&le_sig, 0);
if (!buf) {
BT_ERR("Unable to send L2CAP connection request");
return -ENOMEM;
}
ch->chan.psm = psm;
ch->chan.state = BT_L2CAP_CONNECT;

ch->chan.ident = get_ident();

hdr = net_buf_add(buf, sizeof(*hdr));
hdr->code = BT_L2CAP_LE_CONN_REQ;
hdr->ident = ch->chan.ident;
hdr->len = sys_cpu_to_le16(sizeof(*req));

req = net_buf_add(buf, sizeof(*req));
req->psm = sys_cpu_to_le16(psm);
req->scid = sys_cpu_to_le16(ch->rx.cid);
req->mtu = sys_cpu_to_le16(ch->rx.mtu);
req->mps = sys_cpu_to_le16(ch->rx.mps);
req->credits = sys_cpu_to_le16(L2CAP_LE_MAX_CREDITS);

l2cap_chan_send_req(ch, buf, L2CAP_CONN_TIMEOUT);

return 0;
return l2cap_le_conn_req(ch);
}

int bt_l2cap_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan,
Expand Down Expand Up @@ -1411,6 +1488,7 @@ int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan)
req->scid = sys_cpu_to_le16(ch->rx.cid);

l2cap_chan_send_req(ch, buf, L2CAP_DISC_TIMEOUT);
ch->chan.state = BT_L2CAP_DISCONNECT;

return 0;
}
Expand Down
11 changes: 4 additions & 7 deletions subsys/bluetooth/host/l2cap_br.c
Original file line number Diff line number Diff line change
Expand Up @@ -293,10 +293,7 @@ static void l2cap_br_chan_destroy(struct bt_l2cap_chan *chan)

/* Cancel ongoing work */
nano_delayed_work_cancel(&chan->rtx_work);
/* Reset _ONLY_ internal members of common channel */
chan->state = BT_L2CAP_DISCONNECTED;
/* Reset _ONLY_ BR/EDR specific members on L2CAP app channel */
BR_CHAN(chan)->psm = 0;

atomic_clear(BR_CHAN(chan)->flags);
}

Expand Down Expand Up @@ -1361,7 +1358,7 @@ int bt_l2cap_br_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan,
return -EINVAL;
}

if (BR_CHAN(chan)->psm) {
if (chan->psm) {
return -EEXIST;
}

Expand Down Expand Up @@ -1395,7 +1392,7 @@ int bt_l2cap_br_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan,
return -ENOMEM;
}

BR_CHAN(chan)->psm = psm;
chan->psm = psm;
l2cap_br_state_set(chan, BT_L2CAP_CONNECT);
atomic_set_bit(BR_CHAN(chan)->flags, L2CAP_FLAG_CONN_PENDING);

Expand Down Expand Up @@ -1619,7 +1616,7 @@ static void l2cap_br_conn_pend(struct bt_l2cap_chan *chan, uint8_t status)
hdr->len = sys_cpu_to_le16(sizeof(*req));

req = net_buf_add(buf, sizeof(*req));
req->psm = sys_cpu_to_le16(BR_CHAN(chan)->psm);
req->psm = sys_cpu_to_le16(chan->psm);
req->scid = sys_cpu_to_le16(BR_CHAN(chan)->rx.cid);

l2cap_br_chan_send_req(BR_CHAN(chan), buf,
Expand Down

0 comments on commit 807bd5f

Please sign in to comment.