Bluetooth: SMP: Add strict checks for allowed PDUs
Specification clearly defines when each PDU is allowed to be send. With this patch unexpected PDUs are rejected. This allows for better resilience when remote device misbehaves. Another benefit is that PDUs handlers doesn't have to do additional checks for unexpected sequence or missing PDUs. Specification is not clear on reject policy though. It could be responding with Pairing Failed, disconnect link or just drop unexpected data. This patch implements last option. Dropping unexpected data is also present in other widely used implementations and proved to be sufficient. Change-Id: I9a28a0b7bea71775e656300f71172e60b2df3d9c Signed-off-by: Szymon Janc <szymon.janc@tieto.com>
This commit is contained in:
parent
5f36dbf0bb
commit
4cd3cbf16a
1 changed files with 60 additions and 4 deletions
|
@ -58,6 +58,9 @@ struct bt_smp {
|
|||
/* The connection this context is associated with */
|
||||
struct bt_conn *conn;
|
||||
|
||||
/* Commands that remote is allowed to send */
|
||||
atomic_t allowed_cmds;
|
||||
|
||||
/* If we're waiting for an encryption change event */
|
||||
bool pending_encrypt;
|
||||
|
||||
|
@ -380,6 +383,8 @@ static uint8_t smp_pairing_req(struct bt_conn *conn, struct bt_buf *buf)
|
|||
|
||||
bt_l2cap_send(conn, BT_L2CAP_CID_SMP, rsp_buf);
|
||||
|
||||
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -423,6 +428,8 @@ int smp_send_pairing_req(struct bt_conn *conn)
|
|||
|
||||
bt_l2cap_send(conn, BT_L2CAP_CID_SMP, req_buf);
|
||||
|
||||
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RSP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -481,6 +488,8 @@ static uint8_t smp_pairing_rsp(struct bt_conn *conn, struct bt_buf *buf)
|
|||
smp->prsp[0] = BT_SMP_CMD_PAIRING_RSP;
|
||||
memcpy(smp->prsp + 1, rsp, sizeof(*rsp));
|
||||
|
||||
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM);
|
||||
|
||||
return smp_send_pairing_confirm(conn);
|
||||
}
|
||||
|
||||
|
@ -513,6 +522,8 @@ static uint8_t smp_pairing_confirm(struct bt_conn *conn, struct bt_buf *buf)
|
|||
|
||||
memcpy(smp->pcnf, req->val, sizeof(smp->pcnf));
|
||||
|
||||
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM);
|
||||
|
||||
if (conn->role == BT_HCI_ROLE_SLAVE) {
|
||||
return smp_send_pairing_confirm(conn);
|
||||
}
|
||||
|
@ -653,6 +664,7 @@ static void bt_smp_distribute_keys(struct bt_conn *conn)
|
|||
static uint8_t smp_pairing_encrypt(struct bt_conn *conn, struct bt_buf *buf)
|
||||
{
|
||||
struct bt_smp_encrypt_info *req = (void *)buf->data;
|
||||
struct bt_smp *smp = conn->smp;
|
||||
struct bt_keys *keys;
|
||||
|
||||
BT_DBG("\n");
|
||||
|
@ -666,6 +678,8 @@ static uint8_t smp_pairing_encrypt(struct bt_conn *conn, struct bt_buf *buf)
|
|||
|
||||
memcpy(keys->ltk.val, req->ltk, 16);
|
||||
|
||||
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_MASTER_IDENT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -692,6 +706,12 @@ static uint8_t smp_pairing_master(struct bt_conn *conn, struct bt_buf *buf)
|
|||
if (!smp->remote_dist) {
|
||||
bt_smp_distribute_keys(conn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (smp->remote_dist & BT_SMP_DIST_ID_KEY) {
|
||||
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_IDENT_INFO);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -700,6 +720,7 @@ static uint8_t smp_pairing_master(struct bt_conn *conn, struct bt_buf *buf)
|
|||
static uint8_t smp_ident_info(struct bt_conn *conn, struct bt_buf *buf)
|
||||
{
|
||||
struct bt_smp_ident_info *req = (void *)buf->data;
|
||||
struct bt_smp *smp = conn->smp;
|
||||
struct bt_keys *keys;
|
||||
|
||||
BT_DBG("\n");
|
||||
|
@ -713,6 +734,8 @@ static uint8_t smp_ident_info(struct bt_conn *conn, struct bt_buf *buf)
|
|||
|
||||
memcpy(keys->irk.val, req->irk, 16);
|
||||
|
||||
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_IDENT_ADDR_INFO);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -809,6 +832,7 @@ static const struct {
|
|||
static void bt_smp_recv(struct bt_conn *conn, struct bt_buf *buf)
|
||||
{
|
||||
struct bt_smp_hdr *hdr = (void *)buf->data;
|
||||
struct bt_smp *smp = conn->smp;
|
||||
uint8_t err;
|
||||
|
||||
if (buf->len < sizeof(*hdr)) {
|
||||
|
@ -824,6 +848,11 @@ static void bt_smp_recv(struct bt_conn *conn, struct bt_buf *buf)
|
|||
BT_WARN("Unhandled SMP code 0x%02x\n", hdr->code);
|
||||
err = BT_SMP_ERR_CMD_NOTSUPP;
|
||||
} else {
|
||||
if (!atomic_test_and_clear_bit(&smp->allowed_cmds, hdr->code)) {
|
||||
BT_WARN("Unexpected SMP code 0x%02x\n", hdr->code);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (buf->len != handlers[hdr->code].expect_len) {
|
||||
BT_ERR("Invalid len %u for code 0x%02x\n", buf->len,
|
||||
hdr->code);
|
||||
|
@ -835,6 +864,16 @@ static void bt_smp_recv(struct bt_conn *conn, struct bt_buf *buf)
|
|||
|
||||
if (err) {
|
||||
send_err_rsp(conn, err);
|
||||
|
||||
atomic_set(&smp->allowed_cmds, 0);
|
||||
|
||||
if (conn->role == BT_HCI_ROLE_MASTER) {
|
||||
atomic_set_bit(&smp->allowed_cmds,
|
||||
BT_SMP_CMD_SECURITY_REQUEST);
|
||||
} else {
|
||||
atomic_set_bit(&smp->allowed_cmds,
|
||||
BT_SMP_CMD_PAIRING_REQ);
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
|
@ -850,11 +889,22 @@ static void bt_smp_connected(struct bt_conn *conn)
|
|||
for (i = 0; i < ARRAY_SIZE(bt_smp_pool); i++) {
|
||||
struct bt_smp *smp = &bt_smp_pool[i];
|
||||
|
||||
if (!smp->conn) {
|
||||
smp->conn = conn;
|
||||
conn->smp = smp;
|
||||
return;
|
||||
if (smp->conn) {
|
||||
continue;
|
||||
}
|
||||
|
||||
smp->conn = conn;
|
||||
conn->smp = smp;
|
||||
|
||||
if (conn->role == BT_HCI_ROLE_MASTER) {
|
||||
atomic_set_bit(&smp->allowed_cmds,
|
||||
BT_SMP_CMD_SECURITY_REQUEST);
|
||||
} else {
|
||||
atomic_set_bit(&smp->allowed_cmds,
|
||||
BT_SMP_CMD_PAIRING_REQ);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
BT_ERR("No available SMP context for conn %p\n", conn);
|
||||
|
@ -891,6 +941,12 @@ static void bt_smp_encrypt_change(struct bt_conn *conn)
|
|||
|
||||
smp->pending_encrypt = false;
|
||||
|
||||
if (smp->remote_dist & BT_SMP_DIST_ENC_KEY) {
|
||||
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_ENCRYPT_INFO);
|
||||
} else if (smp->remote_dist & BT_SMP_DIST_ID_KEY) {
|
||||
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_IDENT_INFO);
|
||||
}
|
||||
|
||||
/* Slave distributes it's keys first */
|
||||
if (conn->role == BT_HCI_ROLE_MASTER && smp->remote_dist) {
|
||||
return;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue