Bluetooth: Mesh: store rpl only if incoming data is accepted

Commit prevents updating replay protection list on any frame came
from the network layer.

Replay protection cache is updated only if
    - transport handles transport command without errors
    - access layer handles model message without errors
    - transport frame has correct length

Signed-off-by: Aleksandr Khromykh <aleksandr.khromykh@nordicsemi.no>
This commit is contained in:
Aleksandr Khromykh 2024-03-19 14:42:34 +01:00 committed by Anas Nashif
commit 21eafe6b91
3 changed files with 36 additions and 32 deletions

View file

@ -675,7 +675,7 @@ int bt_mesh_lpn_friend_offer(struct bt_mesh_net_rx *rx,
err = send_friend_poll(); err = send_friend_poll();
if (err) { if (err) {
/* Will retry sending later */ LOG_WRN("LPN didn't succeed poll sending (err %d)", err);
for (int i = 0; i < ARRAY_SIZE(lpn->cred); i++) { for (int i = 0; i < ARRAY_SIZE(lpn->cred); i++) {
if (lpn->sub->keys[i].valid) { if (lpn->sub->keys[i].valid) {
bt_mesh_friend_cred_destroy(&lpn->cred[i]); bt_mesh_friend_cred_destroy(&lpn->cred[i]);
@ -686,7 +686,6 @@ int bt_mesh_lpn_friend_offer(struct bt_mesh_net_rx *rx,
lpn->frnd = BT_MESH_ADDR_UNASSIGNED; lpn->frnd = BT_MESH_ADDR_UNASSIGNED;
lpn->recv_win = 0U; lpn->recv_win = 0U;
lpn->queue_size = 0U; lpn->queue_size = 0U;
return err;
} }
return 0; return 0;

View file

@ -102,12 +102,12 @@ void bt_mesh_rpl_update(struct bt_mesh_rpl *rpl,
} }
/* Check the Replay Protection List for a replay attempt. If non-NULL match /* Check the Replay Protection List for a replay attempt. If non-NULL match
* parameter is given the RPL slot is returned but it is not immediately * parameter is given the RPL slot is returned, but it is not immediately
* updated (needed for segmented messages), whereas if a NULL match is given * updated. This is used to prevent storing data in RPL that has been rejected
* the RPL is immediately updated (used for unsegmented messages). * by upper logic (access, transport commands) and for receiving the segmented messages.
* If a NULL match is given the RPL is immediately updated (used for proxy configuration).
*/ */
bool bt_mesh_rpl_check(struct bt_mesh_net_rx *rx, bool bt_mesh_rpl_check(struct bt_mesh_net_rx *rx, struct bt_mesh_rpl **match)
struct bt_mesh_rpl **match)
{ {
struct bt_mesh_rpl *rpl; struct bt_mesh_rpl *rpl;
int i; int i;

View file

@ -796,23 +796,22 @@ static int sdu_recv(struct bt_mesh_net_rx *rx, uint8_t hdr, uint8_t aszmic,
LOG_DBG("AKF %u AID 0x%02x", !ctx.crypto.dev_key, AID(&hdr)); LOG_DBG("AKF %u AID 0x%02x", !ctx.crypto.dev_key, AID(&hdr));
if (!rx->local_match) { if (!rx->local_match) {
return 0; /* if friend_match was set the frame is for LPN which we are friends. */
return rx->friend_match ? 0 : -ENXIO;
} }
rx->ctx.app_idx = bt_mesh_app_key_find(ctx.crypto.dev_key, AID(&hdr), rx->ctx.app_idx = bt_mesh_app_key_find(ctx.crypto.dev_key, AID(&hdr),
rx, sdu_try_decrypt, &ctx); rx, sdu_try_decrypt, &ctx);
if (rx->ctx.app_idx == BT_MESH_KEY_UNUSED) { if (rx->ctx.app_idx == BT_MESH_KEY_UNUSED) {
LOG_DBG("No matching AppKey"); LOG_DBG("No matching AppKey");
return 0; return -EACCES;
} }
rx->ctx.uuid = ctx.crypto.ad; rx->ctx.uuid = ctx.crypto.ad;
LOG_DBG("Decrypted (AppIdx: 0x%03x)", rx->ctx.app_idx); LOG_DBG("Decrypted (AppIdx: 0x%03x)", rx->ctx.app_idx);
(void)bt_mesh_model_recv(&rx->ctx, sdu); return bt_mesh_access_recv(&rx->ctx, sdu);
return 0;
} }
static struct seg_tx *seg_tx_lookup(uint16_t seq_zero, uint8_t obo, uint16_t addr) static struct seg_tx *seg_tx_lookup(uint16_t seq_zero, uint8_t obo, uint16_t addr)
@ -1027,6 +1026,8 @@ static int trans_unseg(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx,
{ {
NET_BUF_SIMPLE_DEFINE_STATIC(sdu, BT_MESH_SDU_UNSEG_MAX); NET_BUF_SIMPLE_DEFINE_STATIC(sdu, BT_MESH_SDU_UNSEG_MAX);
uint8_t hdr; uint8_t hdr;
struct bt_mesh_rpl *rpl = NULL;
int err;
LOG_DBG("AFK %u AID 0x%02x", AKF(buf->data), AID(buf->data)); LOG_DBG("AFK %u AID 0x%02x", AKF(buf->data), AID(buf->data));
@ -1035,7 +1036,7 @@ static int trans_unseg(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx,
return -EBADMSG; return -EBADMSG;
} }
if (bt_mesh_rpl_check(rx, NULL)) { if (bt_mesh_rpl_check(rx, &rpl)) {
LOG_WRN("Replay: src 0x%04x dst 0x%04x seq 0x%06x", rx->ctx.addr, rx->ctx.recv_dst, LOG_WRN("Replay: src 0x%04x dst 0x%04x seq 0x%06x", rx->ctx.addr, rx->ctx.recv_dst,
rx->seq); rx->seq);
return -EINVAL; return -EINVAL;
@ -1044,18 +1045,22 @@ static int trans_unseg(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx,
hdr = net_buf_simple_pull_u8(buf); hdr = net_buf_simple_pull_u8(buf);
if (rx->ctl) { if (rx->ctl) {
return ctl_recv(rx, hdr, buf, seq_auth); err = ctl_recv(rx, hdr, buf, seq_auth);
} } else if (buf->len < 1 + APP_MIC_LEN(0)) {
if (buf->len < 1 + APP_MIC_LEN(0)) {
LOG_ERR("Too short SDU + MIC"); LOG_ERR("Too short SDU + MIC");
return -EINVAL; err = -EINVAL;
} else {
/* Adjust the length to not contain the MIC at the end */
buf->len -= APP_MIC_LEN(0);
err = sdu_recv(rx, hdr, 0, buf, &sdu, NULL);
} }
/* Adjust the length to not contain the MIC at the end */ /* Update rpl only if there is place and upper logic accepted incoming data. */
buf->len -= APP_MIC_LEN(0); if (err == 0 && rpl != NULL) {
bt_mesh_rpl_update(rpl, rx);
}
return sdu_recv(rx, hdr, 0, buf, &sdu, NULL); return err;
} }
int bt_mesh_ctl_send(struct bt_mesh_net_tx *tx, uint8_t ctl_op, void *data, int bt_mesh_ctl_send(struct bt_mesh_net_tx *tx, uint8_t ctl_op, void *data,
@ -1558,17 +1563,6 @@ found_rx:
} }
LOG_DBG("Complete SDU"); LOG_DBG("Complete SDU");
if (rpl) {
bt_mesh_rpl_update(rpl, net_rx);
/* Update the seg, unless it has already been surpassed:
* This needs to happen after rpl_update to ensure that the IV
* update reset logic inside rpl_update doesn't overwrite the
* change.
*/
rpl->seg = MAX(rpl->seg, auth_seqnum);
}
*pdu_type = BT_MESH_FRIEND_PDU_COMPLETE; *pdu_type = BT_MESH_FRIEND_PDU_COMPLETE;
/* If this fails, the work handler will either exit early because the /* If this fails, the work handler will either exit early because the
@ -1602,6 +1596,17 @@ found_rx:
err = sdu_recv(net_rx, *hdr, ASZMIC(hdr), &seg_buf, &sdu, rx); err = sdu_recv(net_rx, *hdr, ASZMIC(hdr), &seg_buf, &sdu, rx);
} }
/* Update rpl only if there is place and upper logic accepted incoming data. */
if (err == 0 && rpl != NULL) {
bt_mesh_rpl_update(rpl, net_rx);
/* Update the seg, unless it has already been surpassed:
* This needs to happen after rpl_update to ensure that the IV
* update reset logic inside rpl_update doesn't overwrite the
* change.
*/
rpl->seg = MAX(rpl->seg, auth_seqnum);
}
seg_rx_reset(rx, false); seg_rx_reset(rx, false);
return err; return err;