blob: 722ac09cbb84d1f786e4002563644ee9c5542f74 [file] [log] [blame]
/*
* Copyright (c) 2017-2025 Nordic Semiconductor ASA
* Copyright (c) 2015 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <stdint.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/bluetooth/buf.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/addr.h>
#include "common/bt_str.h"
#include "host/keys.h"
#include "host/hci_core.h"
#include "host/conn_internal.h"
#include "l2cap_br_internal.h"
#define LOG_LEVEL CONFIG_BT_HCI_CORE_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_ssp);
enum pairing_method {
LEGACY, /* Legacy (pre-SSP) pairing */
JUST_WORKS, /* JustWorks pairing */
PASSKEY_INPUT, /* Passkey Entry input */
PASSKEY_DISPLAY, /* Passkey Entry display */
PASSKEY_CONFIRM, /* Passkey confirm */
};
/* based on table 5.7, Core Spec 4.2, Vol.3 Part C, 5.2.2.6 */
static const uint8_t ssp_method[4 /* remote */][4 /* local */] = {
{ JUST_WORKS, JUST_WORKS, PASSKEY_INPUT, JUST_WORKS },
{ JUST_WORKS, PASSKEY_CONFIRM, PASSKEY_INPUT, JUST_WORKS },
{ PASSKEY_DISPLAY, PASSKEY_DISPLAY, PASSKEY_INPUT, JUST_WORKS },
{ JUST_WORKS, JUST_WORKS, JUST_WORKS, JUST_WORKS },
};
static int pin_code_neg_reply(const bt_addr_t *bdaddr)
{
struct bt_hci_cp_pin_code_neg_reply *cp;
struct net_buf *buf;
LOG_DBG("");
buf = bt_hci_cmd_alloc(K_FOREVER);
if (!buf) {
return -ENOBUFS;
}
cp = net_buf_add(buf, sizeof(*cp));
bt_addr_copy(&cp->bdaddr, bdaddr);
return bt_hci_cmd_send_sync(BT_HCI_OP_PIN_CODE_NEG_REPLY, buf, NULL);
}
static int pin_code_reply(struct bt_conn *conn, const char *pin, uint8_t len)
{
struct bt_hci_cp_pin_code_reply *cp;
struct net_buf *buf;
LOG_DBG("");
buf = bt_hci_cmd_alloc(K_FOREVER);
if (!buf) {
return -ENOBUFS;
}
cp = net_buf_add(buf, sizeof(*cp));
bt_addr_copy(&cp->bdaddr, &conn->br.dst);
cp->pin_len = len;
strncpy((char *)cp->pin_code, pin, sizeof(cp->pin_code));
return bt_hci_cmd_send_sync(BT_HCI_OP_PIN_CODE_REPLY, buf, NULL);
}
int bt_conn_auth_pincode_entry(struct bt_conn *conn, const char *pin)
{
size_t len;
if (!bt_auth) {
return -EINVAL;
}
if (!bt_conn_is_type(conn, BT_CONN_TYPE_BR)) {
LOG_DBG("Invalid connection type: %u for %p", conn->type, conn);
return -EINVAL;
}
len = strlen(pin);
if (len > 16) {
return -EINVAL;
}
if (conn->required_sec_level == BT_SECURITY_L3 && len < 16) {
LOG_WRN("PIN code for %s is not 16 bytes wide", bt_addr_str(&conn->br.dst));
return -EPERM;
}
/* Allow user send entered PIN to remote, then reset user state. */
if (!atomic_test_and_clear_bit(conn->flags, BT_CONN_USER)) {
return -EPERM;
}
if (len == 16) {
atomic_set_bit(conn->flags, BT_CONN_BR_LEGACY_SECURE);
}
return pin_code_reply(conn, pin, len);
}
static void pin_code_req(struct bt_conn *conn)
{
if (bt_auth && bt_auth->pincode_entry) {
bool secure = false;
if (conn->required_sec_level == BT_SECURITY_L3) {
secure = true;
}
atomic_set_bit(conn->flags, BT_CONN_USER);
atomic_set_bit(conn->flags, BT_CONN_BR_PAIRING);
bt_auth->pincode_entry(conn, secure);
} else {
pin_code_neg_reply(&conn->br.dst);
}
}
static uint8_t get_io_capa(void)
{
if (!bt_auth) {
return BT_IO_NO_INPUT_OUTPUT;
}
if (bt_auth->passkey_confirm && bt_auth->passkey_display) {
return BT_IO_DISPLAY_YESNO;
}
if (bt_auth->passkey_entry) {
return BT_IO_KEYBOARD_ONLY;
}
if (bt_auth->passkey_display) {
return BT_IO_DISPLAY_ONLY;
}
return BT_IO_NO_INPUT_OUTPUT;
}
static uint8_t ssp_pair_method(const struct bt_conn *conn)
{
return ssp_method[conn->br.remote_io_capa][get_io_capa()];
}
static uint8_t ssp_get_auth(const struct bt_conn *conn)
{
bt_security_t max_sec_level;
/* Check if the MITM is required by service */
max_sec_level = bt_l2cap_br_get_max_sec_level();
/*
* The local device shall only set the MITM protection required flag
* if the local device itself requires MITM protection.
*/
if ((max_sec_level > BT_SECURITY_L2) && (ssp_pair_method(conn) > JUST_WORKS)) {
return conn->br.remote_auth | BT_MITM;
}
/* No MITM protection possible so ignore remote MITM requirement. */
return (conn->br.remote_auth & ~BT_MITM);
}
static int ssp_confirm_reply(struct bt_conn *conn)
{
struct bt_hci_cp_user_confirm_reply *cp;
struct net_buf *buf;
LOG_DBG("");
buf = bt_hci_cmd_alloc(K_FOREVER);
if (!buf) {
return -ENOBUFS;
}
cp = net_buf_add(buf, sizeof(*cp));
bt_addr_copy(&cp->bdaddr, &conn->br.dst);
return bt_hci_cmd_send_sync(BT_HCI_OP_USER_CONFIRM_REPLY, buf, NULL);
}
static int ssp_confirm_neg_reply(struct bt_conn *conn)
{
struct bt_hci_cp_user_confirm_reply *cp;
struct net_buf *buf;
LOG_DBG("");
buf = bt_hci_cmd_alloc(K_FOREVER);
if (!buf) {
return -ENOBUFS;
}
cp = net_buf_add(buf, sizeof(*cp));
bt_addr_copy(&cp->bdaddr, &conn->br.dst);
return bt_hci_cmd_send_sync(BT_HCI_OP_USER_CONFIRM_NEG_REPLY, buf,
NULL);
}
static void ssp_pairing_complete(struct bt_conn *conn, uint8_t status)
{
/* When the ssp pairing complete event notified,
* clear the pairing flag.
*/
atomic_clear_bit(conn->flags, BT_CONN_BR_PAIRING);
atomic_set_bit_to(conn->flags, BT_CONN_BR_PAIRED, !status);
LOG_DBG("Pairing completed status %d", status);
if (!status) {
bool bond = !atomic_test_bit(conn->flags, BT_CONN_BR_NOBOND);
struct bt_conn_auth_info_cb *listener, *next;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&bt_auth_info_cbs, listener,
next, node) {
if (listener->pairing_complete) {
listener->pairing_complete(conn, bond);
}
}
} else {
struct bt_conn_auth_info_cb *listener, *next;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&bt_auth_info_cbs, listener,
next, node) {
if (listener->pairing_failed) {
listener->pairing_failed(conn, status);
}
}
}
}
#define BR_SSP_AUTH_MITM_DISABLED(auth) (((auth) & BT_MITM) == 0)
static void ssp_auth(struct bt_conn *conn, uint32_t passkey)
{
conn->br.pairing_method = ssp_pair_method(conn);
if (BR_SSP_AUTH_MITM_DISABLED(conn->br.local_auth) &&
BR_SSP_AUTH_MITM_DISABLED(conn->br.remote_auth)) {
/*
* If the MITM flag of both sides is false, the pairing method is `just works`.
*/
conn->br.pairing_method = JUST_WORKS;
}
/*
* If local required security is HIGH then MITM is mandatory.
* MITM protection is no achievable when SSP 'justworks' is applied.
*/
if (conn->required_sec_level > BT_SECURITY_L2 &&
conn->br.pairing_method == JUST_WORKS) {
LOG_DBG("MITM protection infeasible for required security");
ssp_confirm_neg_reply(conn);
return;
}
switch (conn->br.pairing_method) {
case PASSKEY_CONFIRM:
atomic_set_bit(conn->flags, BT_CONN_USER);
bt_auth->passkey_confirm(conn, passkey);
break;
case PASSKEY_DISPLAY:
atomic_set_bit(conn->flags, BT_CONN_USER);
bt_auth->passkey_display(conn, passkey);
break;
case PASSKEY_INPUT:
atomic_set_bit(conn->flags, BT_CONN_USER);
bt_auth->passkey_entry(conn);
break;
case JUST_WORKS:
/*
* When local host works as pairing acceptor and 'justworks'
* model is applied then notify user about such pairing request.
* [BT Core 4.2 table 5.7, Vol 3, Part C, 5.2.2.6]
*/
if (bt_auth && bt_auth->pairing_confirm &&
!atomic_test_bit(conn->flags,
BT_CONN_BR_PAIRING_INITIATOR)) {
atomic_set_bit(conn->flags, BT_CONN_USER);
bt_auth->pairing_confirm(conn);
break;
}
ssp_confirm_reply(conn);
break;
default:
break;
}
}
static int ssp_passkey_reply(struct bt_conn *conn, unsigned int passkey)
{
struct bt_hci_cp_user_passkey_reply *cp;
struct net_buf *buf;
LOG_DBG("");
buf = bt_hci_cmd_alloc(K_FOREVER);
if (!buf) {
return -ENOBUFS;
}
cp = net_buf_add(buf, sizeof(*cp));
bt_addr_copy(&cp->bdaddr, &conn->br.dst);
cp->passkey = sys_cpu_to_le32(passkey);
return bt_hci_cmd_send_sync(BT_HCI_OP_USER_PASSKEY_REPLY, buf, NULL);
}
static int ssp_passkey_neg_reply(struct bt_conn *conn)
{
struct bt_hci_cp_user_passkey_neg_reply *cp;
struct net_buf *buf;
LOG_DBG("");
buf = bt_hci_cmd_alloc(K_FOREVER);
if (!buf) {
return -ENOBUFS;
}
cp = net_buf_add(buf, sizeof(*cp));
bt_addr_copy(&cp->bdaddr, &conn->br.dst);
return bt_hci_cmd_send_sync(BT_HCI_OP_USER_PASSKEY_NEG_REPLY, buf,
NULL);
}
static int conn_auth(struct bt_conn *conn)
{
struct bt_hci_cp_auth_requested *auth;
struct net_buf *buf;
LOG_DBG("");
buf = bt_hci_cmd_alloc(K_FOREVER);
if (!buf) {
return -ENOBUFS;
}
auth = net_buf_add(buf, sizeof(*auth));
auth->handle = sys_cpu_to_le16(conn->handle);
atomic_set_bit(conn->flags, BT_CONN_BR_PAIRING_INITIATOR);
return bt_hci_cmd_send_sync(BT_HCI_OP_AUTH_REQUESTED, buf, NULL);
}
int bt_ssp_start_security(struct bt_conn *conn)
{
if (atomic_test_bit(conn->flags, BT_CONN_BR_PAIRING)) {
return -EBUSY;
}
if (get_io_capa() == BT_IO_NO_INPUT_OUTPUT &&
conn->required_sec_level > BT_SECURITY_L2) {
return -EINVAL;
}
return conn_auth(conn);
}
int bt_ssp_auth_passkey_entry(struct bt_conn *conn, unsigned int passkey)
{
/* User entered passkey, reset user state. */
if (!atomic_test_and_clear_bit(conn->flags, BT_CONN_USER)) {
return -EPERM;
}
if (conn->br.pairing_method == PASSKEY_INPUT) {
return ssp_passkey_reply(conn, passkey);
}
return -EINVAL;
}
int bt_ssp_auth_passkey_confirm(struct bt_conn *conn)
{
/* Allow user confirm passkey value, then reset user state. */
if (!atomic_test_and_clear_bit(conn->flags, BT_CONN_USER)) {
return -EPERM;
}
return ssp_confirm_reply(conn);
}
int bt_ssp_auth_pairing_confirm(struct bt_conn *conn)
{
return ssp_confirm_reply(conn);
}
int bt_ssp_auth_cancel(struct bt_conn *conn)
{
/* Allow user cancel authentication, then reset user state. */
if (!atomic_test_and_clear_bit(conn->flags, BT_CONN_USER)) {
return -EPERM;
}
switch (conn->br.pairing_method) {
case JUST_WORKS:
case PASSKEY_CONFIRM:
return ssp_confirm_neg_reply(conn);
case PASSKEY_INPUT:
return ssp_passkey_neg_reply(conn);
case PASSKEY_DISPLAY:
return bt_conn_disconnect(conn,
BT_HCI_ERR_AUTH_FAIL);
case LEGACY:
return pin_code_neg_reply(&conn->br.dst);
default:
break;
}
return -EINVAL;
}
void bt_hci_pin_code_req(struct net_buf *buf)
{
struct bt_hci_evt_pin_code_req *evt = (void *)buf->data;
struct bt_conn *conn;
LOG_DBG("");
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
if (!conn) {
LOG_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
return;
}
pin_code_req(conn);
bt_conn_unref(conn);
}
void bt_hci_link_key_notify(struct net_buf *buf)
{
struct bt_hci_evt_link_key_notify *evt = (void *)buf->data;
struct bt_conn *conn;
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
if (!conn) {
LOG_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
return;
}
LOG_DBG("%s, link type 0x%02x", bt_addr_str(&evt->bdaddr), evt->key_type);
if (IS_ENABLED(CONFIG_BT_SMP_SC_ONLY) && (evt->key_type != BT_LK_AUTH_COMBINATION_P256)) {
/*
* When in Secure Connections Only mode, all services
* (except those allowed to have Security Mode 4, Level 0)
* available on the BR/EDR physical transport require Security
* Mode 4, Level 4.
* Link key type should be P-256 based Secure Simple Pairing
* and Secure Authentication.
*/
LOG_WRN("For SC only mode, link key type should be %d",
BT_LK_AUTH_COMBINATION_P256);
ssp_pairing_complete(conn, bt_security_err_get(BT_HCI_ERR_AUTH_FAIL));
bt_conn_disconnect(conn, BT_HCI_ERR_AUTH_FAIL);
bt_conn_unref(conn);
return;
}
if (!conn->br.link_key) {
conn->br.link_key = bt_keys_get_link_key(&evt->bdaddr);
}
if (!conn->br.link_key) {
LOG_ERR("Can't update keys for %s", bt_addr_str(&evt->bdaddr));
bt_conn_unref(conn);
return;
}
/* clear any old Link Key flags */
conn->br.link_key->flags = 0U;
switch (evt->key_type) {
case BT_LK_COMBINATION:
/*
* Setting Combination Link Key as AUTHENTICATED means it was
* successfully generated by 16 digits wide PIN code.
*/
if (atomic_test_and_clear_bit(conn->flags,
BT_CONN_BR_LEGACY_SECURE)) {
conn->br.link_key->flags |= BT_LINK_KEY_AUTHENTICATED;
}
memcpy(conn->br.link_key->val, evt->link_key, 16);
break;
case BT_LK_AUTH_COMBINATION_P192:
conn->br.link_key->flags |= BT_LINK_KEY_AUTHENTICATED;
__fallthrough;
case BT_LK_UNAUTH_COMBINATION_P192:
memcpy(conn->br.link_key->val, evt->link_key, 16);
break;
case BT_LK_AUTH_COMBINATION_P256:
conn->br.link_key->flags |= BT_LINK_KEY_AUTHENTICATED;
__fallthrough;
case BT_LK_UNAUTH_COMBINATION_P256:
conn->br.link_key->flags |= BT_LINK_KEY_SC;
memcpy(conn->br.link_key->val, evt->link_key, 16);
break;
default:
LOG_WRN("Unsupported Link Key type %u", evt->key_type);
(void)memset(conn->br.link_key->val, 0,
sizeof(conn->br.link_key->val));
break;
}
if (IS_ENABLED(CONFIG_BT_SETTINGS) &&
!atomic_test_bit(conn->flags, BT_CONN_BR_NOBOND)) {
bt_keys_link_key_store(conn->br.link_key);
}
bt_conn_unref(conn);
}
void link_key_neg_reply(const bt_addr_t *bdaddr)
{
struct bt_hci_cp_link_key_neg_reply *cp;
struct net_buf *buf;
LOG_DBG("");
buf = bt_hci_cmd_alloc(K_FOREVER);
if (!buf) {
LOG_ERR("Out of command buffers");
return;
}
cp = net_buf_add(buf, sizeof(*cp));
bt_addr_copy(&cp->bdaddr, bdaddr);
bt_hci_cmd_send_sync(BT_HCI_OP_LINK_KEY_NEG_REPLY, buf, NULL);
}
void link_key_reply(const bt_addr_t *bdaddr, const uint8_t *lk)
{
struct bt_hci_cp_link_key_reply *cp;
struct net_buf *buf;
LOG_DBG("");
buf = bt_hci_cmd_alloc(K_FOREVER);
if (!buf) {
LOG_ERR("Out of command buffers");
return;
}
cp = net_buf_add(buf, sizeof(*cp));
bt_addr_copy(&cp->bdaddr, bdaddr);
memcpy(cp->link_key, lk, 16);
bt_hci_cmd_send_sync(BT_HCI_OP_LINK_KEY_REPLY, buf, NULL);
}
void bt_hci_link_key_req(struct net_buf *buf)
{
struct bt_hci_evt_link_key_req *evt = (void *)buf->data;
struct bt_conn *conn;
LOG_DBG("%s", bt_addr_str(&evt->bdaddr));
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
if (!conn) {
LOG_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
link_key_neg_reply(&evt->bdaddr);
return;
}
if (!conn->br.link_key) {
conn->br.link_key = bt_keys_find_link_key(&evt->bdaddr);
}
if (!conn->br.link_key) {
link_key_neg_reply(&evt->bdaddr);
bt_conn_unref(conn);
return;
}
/*
* Enforce regenerate by controller stronger link key since found one
* in database not covers requested security level.
*/
if (!(conn->br.link_key->flags & BT_LINK_KEY_AUTHENTICATED) &&
conn->required_sec_level > BT_SECURITY_L2) {
link_key_neg_reply(&evt->bdaddr);
bt_conn_unref(conn);
return;
}
link_key_reply(&evt->bdaddr, conn->br.link_key->val);
bt_conn_unref(conn);
}
void io_capa_neg_reply(const bt_addr_t *bdaddr, const uint8_t reason)
{
struct bt_hci_cp_io_capability_neg_reply *cp;
struct net_buf *resp_buf;
resp_buf = bt_hci_cmd_alloc(K_FOREVER);
if (!resp_buf) {
LOG_ERR("Out of command buffers");
return;
}
cp = net_buf_add(resp_buf, sizeof(*cp));
bt_addr_copy(&cp->bdaddr, bdaddr);
cp->reason = reason;
bt_hci_cmd_send_sync(BT_HCI_OP_IO_CAPABILITY_NEG_REPLY, resp_buf, NULL);
}
void bt_hci_io_capa_resp(struct net_buf *buf)
{
struct bt_hci_evt_io_capa_resp *evt = (void *)buf->data;
struct bt_conn *conn;
LOG_DBG("remote %s, IOcapa 0x%02x, auth 0x%02x", bt_addr_str(&evt->bdaddr), evt->capability,
evt->authentication);
if (evt->authentication > BT_HCI_GENERAL_BONDING_MITM) {
LOG_ERR("Invalid remote authentication requirements");
io_capa_neg_reply(&evt->bdaddr,
BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL);
return;
}
if (evt->capability > BT_IO_NO_INPUT_OUTPUT) {
LOG_ERR("Invalid remote io capability requirements");
io_capa_neg_reply(&evt->bdaddr,
BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL);
return;
}
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
if (!conn) {
LOG_ERR("Unable to find conn for %s", bt_addr_str(&evt->bdaddr));
return;
}
if (atomic_test_bit(conn->flags, BT_CONN_BR_PAIRING_INITIATOR) &&
(evt->authentication > BT_HCI_NO_BONDING_MITM) &&
!atomic_test_bit(conn->flags, BT_CONN_BR_BONDABLE)) {
/*
* BLUETOOTH CORE SPECIFICATION Version 6.0 | Vol 3, Part C, section 9.4.2.
* A device in the non-bondable mode does not allow a bond to be created with a
* peer device.
*
* If the local is SSP initiator and non-bondable mode, and the bonding is required
* by peer device, reports the pairing failure and disconnects the ACL connection
* with error `BT_HCI_ERR_AUTH_FAIL`.
*/
LOG_WRN("Bonding flag mismatch (initiator:false != responder:true)");
ssp_pairing_complete(conn, bt_security_err_get(BT_HCI_ERR_AUTH_FAIL));
bt_conn_disconnect(conn, BT_HCI_ERR_AUTH_FAIL);
bt_conn_unref(conn);
return;
}
conn->br.remote_io_capa = evt->capability;
conn->br.remote_auth = evt->authentication;
atomic_set_bit(conn->flags, BT_CONN_BR_PAIRING);
bt_conn_unref(conn);
}
/* Clear Bonding flag */
#define BT_HCI_SET_NO_BONDING(auth) ((auth) & 0x01)
/* Clear MITM flag */
#define BT_HCI_SET_NO_MITM(auth) ((auth) & (~0x01))
void bt_hci_io_capa_req(struct net_buf *buf)
{
struct bt_hci_evt_io_capa_req *evt = (void *)buf->data;
struct net_buf *resp_buf;
struct bt_conn *conn;
struct bt_hci_cp_io_capability_reply *cp;
uint8_t auth;
LOG_DBG("");
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
if (!conn) {
LOG_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
return;
}
#if defined(CONFIG_BT_SMP_APP_PAIRING_ACCEPT)
if (bt_auth && bt_auth->pairing_accept) {
enum bt_security_err err;
err = bt_auth->pairing_accept(conn, NULL);
if (err != BT_SECURITY_ERR_SUCCESS) {
io_capa_neg_reply(&evt->bdaddr, BT_HCI_ERR_PAIRING_NOT_ALLOWED);
bt_conn_unref(conn);
return;
}
}
#endif
/*
* Set authentication requirements when acting as pairing initiator to
* 'dedicated bond' with MITM protection set if local IO capa
* potentially allows it, and for acceptor, based on local IO capa and
* remote's authentication set.
*/
if (atomic_test_bit(conn->flags, BT_CONN_BR_PAIRING_INITIATOR)) {
if (get_io_capa() != BT_IO_NO_INPUT_OUTPUT) {
if (atomic_test_bit(conn->flags, BT_CONN_BR_GENERAL_BONDING)) {
auth = BT_HCI_GENERAL_BONDING_MITM;
} else {
auth = BT_HCI_DEDICATED_BONDING_MITM;
}
} else {
if (atomic_test_bit(conn->flags, BT_CONN_BR_GENERAL_BONDING)) {
auth = BT_HCI_GENERAL_BONDING;
} else {
auth = BT_HCI_DEDICATED_BONDING;
}
}
if (conn->required_sec_level < BT_SECURITY_L3) {
/* If security level less than L3, clear MITM flag. */
auth = BT_HCI_SET_NO_MITM(auth);
}
} else {
auth = ssp_get_auth(conn);
/*
* Core v6.0, Vol 3, Part C, Section 4.3.1 Non-bondable mode
* When a Bluetooth device is in non-bondable mode it shall not accept a
* pairing request that results in bonding. Devices in non-bondable mode
* may accept connections that do not request or require bonding.
*
* If the peer supports bonding mode, but the local is in non-bondable
* mode, it will send a negative response with error code
* `BT_HCI_ERR_PAIRING_NOT_ALLOWED`.
*/
if (!atomic_test_bit(conn->flags, BT_CONN_BR_BONDABLE) &&
(conn->br.remote_auth > BT_HCI_NO_BONDING_MITM)) {
LOG_WRN("Invalid remote bonding requirements");
io_capa_neg_reply(&evt->bdaddr,
BT_HCI_ERR_PAIRING_NOT_ALLOWED);
bt_conn_unref(conn);
return;
}
}
if (!atomic_test_bit(conn->flags, BT_CONN_BR_BONDABLE)) {
/* If bondable is false, clear bonding flag. */
auth = BT_HCI_SET_NO_BONDING(auth);
}
conn->br.local_auth = auth;
resp_buf = bt_hci_cmd_alloc(K_FOREVER);
if (!resp_buf) {
LOG_ERR("Out of command buffers");
bt_conn_unref(conn);
return;
}
cp = net_buf_add(resp_buf, sizeof(*cp));
bt_addr_copy(&cp->bdaddr, &evt->bdaddr);
cp->capability = get_io_capa();
cp->authentication = auth;
cp->oob_data = 0U;
bt_hci_cmd_send_sync(BT_HCI_OP_IO_CAPABILITY_REPLY, resp_buf, NULL);
bt_conn_unref(conn);
}
void bt_hci_ssp_complete(struct net_buf *buf)
{
struct bt_hci_evt_ssp_complete *evt = (void *)buf->data;
struct bt_conn *conn;
LOG_DBG("status 0x%02x", evt->status);
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
if (!conn) {
LOG_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
return;
}
/* Mark no-bond so that link-key will be removed on disconnection */
if (ssp_get_auth(conn) < BT_HCI_DEDICATED_BONDING) {
atomic_set_bit(conn->flags, BT_CONN_BR_NOBOND);
}
ssp_pairing_complete(conn, bt_security_err_get(evt->status));
if (evt->status) {
bt_conn_disconnect(conn, BT_HCI_ERR_AUTH_FAIL);
}
bt_conn_unref(conn);
}
void bt_hci_user_confirm_req(struct net_buf *buf)
{
struct bt_hci_evt_user_confirm_req *evt = (void *)buf->data;
struct bt_conn *conn;
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
if (!conn) {
LOG_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
return;
}
ssp_auth(conn, sys_le32_to_cpu(evt->passkey));
bt_conn_unref(conn);
}
void bt_hci_user_passkey_notify(struct net_buf *buf)
{
struct bt_hci_evt_user_passkey_notify *evt = (void *)buf->data;
struct bt_conn *conn;
LOG_DBG("");
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
if (!conn) {
LOG_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
return;
}
ssp_auth(conn, sys_le32_to_cpu(evt->passkey));
bt_conn_unref(conn);
}
void bt_hci_user_passkey_req(struct net_buf *buf)
{
struct bt_hci_evt_user_passkey_req *evt = (void *)buf->data;
struct bt_conn *conn;
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
if (!conn) {
LOG_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
return;
}
ssp_auth(conn, 0);
bt_conn_unref(conn);
}
static void link_encr(const uint16_t handle)
{
struct bt_hci_cp_set_conn_encrypt *encr;
struct net_buf *buf;
LOG_DBG("");
buf = bt_hci_cmd_alloc(K_FOREVER);
if (!buf) {
LOG_ERR("Out of command buffers");
return;
}
encr = net_buf_add(buf, sizeof(*encr));
encr->handle = sys_cpu_to_le16(handle);
encr->encrypt = 0x01;
bt_hci_cmd_send_sync(BT_HCI_OP_SET_CONN_ENCRYPT, buf, NULL);
}
void bt_hci_auth_complete(struct net_buf *buf)
{
struct bt_hci_evt_auth_complete *evt = (void *)buf->data;
struct bt_conn *conn;
uint16_t handle = sys_le16_to_cpu(evt->handle);
LOG_DBG("status 0x%02x, handle %u", evt->status, handle);
conn = bt_conn_lookup_handle(handle, BT_CONN_TYPE_BR);
if (!conn) {
LOG_ERR("Can't find conn for handle %u", handle);
return;
}
if (evt->status) {
/*
* Inform layers above HCI about non-zero authentication
* status to make them able cleanup pending jobs.
*/
bt_conn_security_changed(conn, evt->status,
bt_security_err_get(evt->status));
} else {
link_encr(handle);
}
bt_conn_unref(conn);
}