Bluetooth: L2CAP: Handle LE connection response security errors
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 <luiz.von.dentz@intel.com>
This commit is contained in:
parent
d1ae81eddd
commit
807bd5fec8
3 changed files with 138 additions and 63 deletions
|
@ -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;
|
||||
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue