diff --git a/subsys/bluetooth/host/CMakeLists.txt b/subsys/bluetooth/host/CMakeLists.txt index 0765d9d5535..93b2dd7a16c 100644 --- a/subsys/bluetooth/host/CMakeLists.txt +++ b/subsys/bluetooth/host/CMakeLists.txt @@ -18,6 +18,7 @@ zephyr_library_sources_ifdef( keys_br.c l2cap_br.c sdp.c + ssp.c ) zephyr_library_sources_ifdef( diff --git a/subsys/bluetooth/host/conn.c b/subsys/bluetooth/host/conn.c index 7aaee90e166..3526d52233e 100644 --- a/subsys/bluetooth/host/conn.c +++ b/subsys/bluetooth/host/conn.c @@ -32,6 +32,7 @@ #include "l2cap_internal.h" #include "keys.h" #include "smp.h" +#include "ssp.h" #include "att_internal.h" #include "gatt_internal.h" @@ -76,22 +77,6 @@ K_FIFO_DEFINE(free_tx); #if defined(CONFIG_BT_BREDR) static struct bt_conn sco_conns[CONFIG_BT_MAX_SCO_CONN]; - -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 }, -}; #endif /* CONFIG_BT_BREDR */ struct k_sem *bt_conn_get_pkts(struct bt_conn *conn) @@ -659,279 +644,6 @@ struct bt_conn *bt_conn_add_br(const bt_addr_t *peer) return conn; } -static int pin_code_neg_reply(const bt_addr_t *bdaddr) -{ - struct bt_hci_cp_pin_code_neg_reply *cp; - struct net_buf *buf; - - BT_DBG(""); - - buf = bt_hci_cmd_create(BT_HCI_OP_PIN_CODE_NEG_REPLY, sizeof(*cp)); - 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; - - BT_DBG(""); - - buf = bt_hci_cmd_create(BT_HCI_OP_PIN_CODE_REPLY, sizeof(*cp)); - 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 (conn->type != BT_CONN_TYPE_BR) { - return -EINVAL; - } - - len = strlen(pin); - if (len > 16) { - return -EINVAL; - } - - if (conn->required_sec_level == BT_SECURITY_L3 && len < 16) { - BT_WARN("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); -} - -void bt_conn_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); - } -} - -uint8_t bt_conn_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][bt_conn_get_io_capa()]; -} - -uint8_t bt_conn_ssp_get_auth(const struct bt_conn *conn) -{ - /* Validate no bond auth request, and if valid use it. */ - if ((conn->br.remote_auth == BT_HCI_NO_BONDING) || - ((conn->br.remote_auth == BT_HCI_NO_BONDING_MITM) && - (ssp_pair_method(conn) > JUST_WORKS))) { - return conn->br.remote_auth; - } - - /* Local & remote have enough IO capabilities to get MITM protection. */ - if (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; - - BT_DBG(""); - - buf = bt_hci_cmd_create(BT_HCI_OP_USER_CONFIRM_REPLY, sizeof(*cp)); - 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; - - BT_DBG(""); - - buf = bt_hci_cmd_create(BT_HCI_OP_USER_CONFIRM_NEG_REPLY, sizeof(*cp)); - 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); -} - -void bt_conn_ssp_auth_complete(struct bt_conn *conn, uint8_t status) -{ - if (!status) { - bool bond = !atomic_test_bit(conn->flags, BT_CONN_BR_NOBOND); - - if (bt_auth && bt_auth->pairing_complete) { - bt_auth->pairing_complete(conn, bond); - } - } else { - if (bt_auth && bt_auth->pairing_failed) { - bt_auth->pairing_failed(conn, status); - } - } -} - -void bt_conn_ssp_auth(struct bt_conn *conn, uint32_t passkey) -{ - conn->br.pairing_method = ssp_pair_method(conn); - - /* - * 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) { - BT_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; - - BT_DBG(""); - - buf = bt_hci_cmd_create(BT_HCI_OP_USER_PASSKEY_REPLY, sizeof(*cp)); - 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; - - BT_DBG(""); - - buf = bt_hci_cmd_create(BT_HCI_OP_USER_PASSKEY_NEG_REPLY, sizeof(*cp)); - 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 bt_hci_connect_br_cancel(struct bt_conn *conn) { struct bt_hci_cp_connect_cancel *cp; @@ -961,25 +673,6 @@ static int bt_hci_connect_br_cancel(struct bt_conn *conn) return err; } -static int conn_auth(struct bt_conn *conn) -{ - struct bt_hci_cp_auth_requested *auth; - struct net_buf *buf; - - BT_DBG(""); - - buf = bt_hci_cmd_create(BT_HCI_OP_AUTH_REQUESTED, sizeof(*auth)); - 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); -} #endif /* CONFIG_BT_BREDR */ #if defined(CONFIG_BT_SMP) @@ -1116,24 +809,9 @@ void bt_conn_security_changed(struct bt_conn *conn, uint8_t hci_err, static int start_security(struct bt_conn *conn) { -#if defined(CONFIG_BT_BREDR) - if (conn->type == BT_CONN_TYPE_BR) { - if (atomic_test_bit(conn->flags, BT_CONN_BR_PAIRING)) { - return -EBUSY; - } - - if (conn->required_sec_level > BT_SECURITY_L3) { - return -ENOTSUP; - } - - if (bt_conn_get_io_capa() == BT_IO_NO_INPUT_OUTPUT && - conn->required_sec_level > BT_SECURITY_L2) { - return -EINVAL; - } - - return conn_auth(conn); + if (IS_ENABLED(CONFIG_BT_BREDR) && conn->type == BT_CONN_TYPE_BR) { + return bt_ssp_start_security(conn); } -#endif /* CONFIG_BT_BREDR */ if (IS_ENABLED(CONFIG_BT_SMP)) { return bt_smp_start_security(conn); @@ -2661,18 +2339,9 @@ int bt_conn_auth_passkey_entry(struct bt_conn *conn, unsigned int passkey) return 0; } -#if defined(CONFIG_BT_BREDR) - if (conn->type == BT_CONN_TYPE_BR) { - /* 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); - } + if (IS_ENABLED(CONFIG_BT_BREDR) && conn->type == BT_CONN_TYPE_BR) { + return bt_ssp_auth_passkey_entry(conn, passkey); } -#endif /* CONFIG_BT_BREDR */ return -EINVAL; } @@ -2688,16 +2357,10 @@ int bt_conn_auth_passkey_confirm(struct bt_conn *conn) return bt_smp_auth_passkey_confirm(conn); } -#if defined(CONFIG_BT_BREDR) - if (conn->type == BT_CONN_TYPE_BR) { - /* 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); + if (IS_ENABLED(CONFIG_BT_BREDR) && + conn->type == BT_CONN_TYPE_BR) { + return bt_ssp_auth_passkey_confirm(conn); } -#endif /* CONFIG_BT_BREDR */ return -EINVAL; } @@ -2714,25 +2377,7 @@ int bt_conn_auth_cancel(struct bt_conn *conn) #if defined(CONFIG_BT_BREDR) if (conn->type == BT_CONN_TYPE_BR) { - /* 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 bt_ssp_auth_cancel(conn); } #endif /* CONFIG_BT_BREDR */ @@ -2752,7 +2397,7 @@ int bt_conn_auth_pairing_confirm(struct bt_conn *conn) #endif /* CONFIG_BT_SMP */ #if defined(CONFIG_BT_BREDR) case BT_CONN_TYPE_BR: - return ssp_confirm_reply(conn); + return bt_ssp_auth_pairing_confirm(conn); #endif /* CONFIG_BT_BREDR */ default: return -EINVAL; diff --git a/subsys/bluetooth/host/conn_internal.h b/subsys/bluetooth/host/conn_internal.h index 0be6f3070a5..9802de0701a 100644 --- a/subsys/bluetooth/host/conn_internal.h +++ b/subsys/bluetooth/host/conn_internal.h @@ -205,12 +205,6 @@ struct bt_conn *bt_conn_lookup_addr_sco(const bt_addr_t *peer); /* Look up an existing connection by BT address */ struct bt_conn *bt_conn_lookup_addr_br(const bt_addr_t *peer); -void bt_conn_pin_code_req(struct bt_conn *conn); -uint8_t bt_conn_get_io_capa(void); -uint8_t bt_conn_ssp_get_auth(const struct bt_conn *conn); -void bt_conn_ssp_auth(struct bt_conn *conn, uint32_t passkey); -void bt_conn_ssp_auth_complete(struct bt_conn *conn, uint8_t status); - void bt_conn_disconnect_all(uint8_t id); /* Look up an existing connection */ diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index 00d9722dfac..21c7ed694df 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -2695,7 +2695,7 @@ int bt_unpair(uint8_t id, const bt_addr_le_t *addr) #endif /* CONFIG_BT_CONN */ #if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR) -static enum bt_security_err security_err_get(uint8_t hci_err) +enum bt_security_err bt_security_err_get(uint8_t hci_err) { switch (hci_err) { case BT_HCI_ERR_SUCCESS: @@ -2993,344 +2993,6 @@ static void conn_complete(struct net_buf *buf) bt_hci_cmd_send_sync(BT_HCI_OP_READ_REMOTE_FEATURES, buf, NULL); } -static void pin_code_req(struct net_buf *buf) -{ - struct bt_hci_evt_pin_code_req *evt = (void *)buf->data; - struct bt_conn *conn; - - BT_DBG(""); - - conn = bt_conn_lookup_addr_br(&evt->bdaddr); - if (!conn) { - BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr)); - return; - } - - bt_conn_pin_code_req(conn); - bt_conn_unref(conn); -} - -static void 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) { - BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr)); - return; - } - - BT_DBG("%s, link type 0x%02x", bt_addr_str(&evt->bdaddr), evt->key_type); - - if (!conn->br.link_key) { - conn->br.link_key = bt_keys_get_link_key(&evt->bdaddr); - } - if (!conn->br.link_key) { - BT_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; - /* fall through */ - case BT_LK_UNAUTH_COMBINATION_P192: - /* Mark no-bond so that link-key is removed on disconnection */ - if (bt_conn_ssp_get_auth(conn) < BT_HCI_DEDICATED_BONDING) { - atomic_set_bit(conn->flags, BT_CONN_BR_NOBOND); - } - - 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; - /* fall through */ - case BT_LK_UNAUTH_COMBINATION_P256: - conn->br.link_key->flags |= BT_LINK_KEY_SC; - - /* Mark no-bond so that link-key is removed on disconnection */ - if (bt_conn_ssp_get_auth(conn) < BT_HCI_DEDICATED_BONDING) { - atomic_set_bit(conn->flags, BT_CONN_BR_NOBOND); - } - - memcpy(conn->br.link_key->val, evt->link_key, 16); - break; - default: - BT_WARN("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); -} - -static void link_key_neg_reply(const bt_addr_t *bdaddr) -{ - struct bt_hci_cp_link_key_neg_reply *cp; - struct net_buf *buf; - - BT_DBG(""); - - buf = bt_hci_cmd_create(BT_HCI_OP_LINK_KEY_NEG_REPLY, sizeof(*cp)); - if (!buf) { - BT_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); -} - -static 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; - - BT_DBG(""); - - buf = bt_hci_cmd_create(BT_HCI_OP_LINK_KEY_REPLY, sizeof(*cp)); - if (!buf) { - BT_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); -} - -static void link_key_req(struct net_buf *buf) -{ - struct bt_hci_evt_link_key_req *evt = (void *)buf->data; - struct bt_conn *conn; - - BT_DBG("%s", bt_addr_str(&evt->bdaddr)); - - conn = bt_conn_lookup_addr_br(&evt->bdaddr); - if (!conn) { - BT_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); -} - -static 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_create(BT_HCI_OP_IO_CAPABILITY_NEG_REPLY, - sizeof(*cp)); - if (!resp_buf) { - BT_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); -} - -static void io_capa_resp(struct net_buf *buf) -{ - struct bt_hci_evt_io_capa_resp *evt = (void *)buf->data; - struct bt_conn *conn; - - BT_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) { - BT_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) { - BT_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) { - BT_ERR("Unable to find conn for %s", bt_addr_str(&evt->bdaddr)); - 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); -} - -static void 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; - - BT_DBG(""); - - conn = bt_conn_lookup_addr_br(&evt->bdaddr); - if (!conn) { - BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr)); - return; - } - - resp_buf = bt_hci_cmd_create(BT_HCI_OP_IO_CAPABILITY_REPLY, - sizeof(*cp)); - if (!resp_buf) { - BT_ERR("Out of command buffers"); - bt_conn_unref(conn); - return; - } - - /* - * 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 (bt_conn_get_io_capa() != BT_IO_NO_INPUT_OUTPUT) { - auth = BT_HCI_DEDICATED_BONDING_MITM; - } else { - auth = BT_HCI_DEDICATED_BONDING; - } - } else { - auth = bt_conn_ssp_get_auth(conn); - } - - cp = net_buf_add(resp_buf, sizeof(*cp)); - bt_addr_copy(&cp->bdaddr, &evt->bdaddr); - cp->capability = bt_conn_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); -} - -static void ssp_complete(struct net_buf *buf) -{ - struct bt_hci_evt_ssp_complete *evt = (void *)buf->data; - struct bt_conn *conn; - - BT_DBG("status 0x%02x", evt->status); - - conn = bt_conn_lookup_addr_br(&evt->bdaddr); - if (!conn) { - BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr)); - return; - } - - bt_conn_ssp_auth_complete(conn, security_err_get(evt->status)); - if (evt->status) { - bt_conn_disconnect(conn, BT_HCI_ERR_AUTH_FAIL); - } - - bt_conn_unref(conn); -} - -static void 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) { - BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr)); - return; - } - - bt_conn_ssp_auth(conn, sys_le32_to_cpu(evt->passkey)); - bt_conn_unref(conn); -} - -static void user_passkey_notify(struct net_buf *buf) -{ - struct bt_hci_evt_user_passkey_notify *evt = (void *)buf->data; - struct bt_conn *conn; - - BT_DBG(""); - - conn = bt_conn_lookup_addr_br(&evt->bdaddr); - if (!conn) { - BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr)); - return; - } - - bt_conn_ssp_auth(conn, sys_le32_to_cpu(evt->passkey)); - bt_conn_unref(conn); -} - -static void 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) { - BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr)); - return; - } - - bt_conn_ssp_auth(conn, 0); - bt_conn_unref(conn); -} - struct discovery_priv { uint16_t clock_offset; uint8_t pscan_rep_mode; @@ -3644,54 +3306,6 @@ check_names: discovery_results_count = 0; } -static void link_encr(const uint16_t handle) -{ - struct bt_hci_cp_set_conn_encrypt *encr; - struct net_buf *buf; - - BT_DBG(""); - - buf = bt_hci_cmd_create(BT_HCI_OP_SET_CONN_ENCRYPT, sizeof(*encr)); - if (!buf) { - BT_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); -} - -static void 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); - - BT_DBG("status 0x%02x, handle %u", evt->status, handle); - - conn = bt_conn_lookup_handle(handle); - if (!conn) { - BT_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, - security_err_get(evt->status)); - } else { - link_encr(handle); - } - - bt_conn_unref(conn); -} - static void read_remote_features_complete(struct net_buf *buf) { struct bt_hci_evt_remote_features *evt = (void *)buf->data; @@ -4131,7 +3745,7 @@ static void hci_encrypt_change(struct net_buf *buf) if (evt->status) { bt_conn_security_changed(conn, evt->status, - security_err_get(evt->status)); + bt_security_err_get(evt->status)); bt_conn_unref(conn); return; } @@ -4197,7 +3811,7 @@ static void hci_encrypt_key_refresh_complete(struct net_buf *buf) if (evt->status) { bt_conn_security_changed(conn, evt->status, - security_err_get(evt->status)); + bt_security_err_get(evt->status)); bt_conn_unref(conn); return; } @@ -5379,23 +4993,24 @@ static const struct event_handler normal_events[] = { sizeof(struct bt_hci_evt_conn_request)), EVENT_HANDLER(BT_HCI_EVT_CONN_COMPLETE, conn_complete, sizeof(struct bt_hci_evt_conn_complete)), - EVENT_HANDLER(BT_HCI_EVT_PIN_CODE_REQ, pin_code_req, + EVENT_HANDLER(BT_HCI_EVT_PIN_CODE_REQ, hci_evt_pin_code_req, sizeof(struct bt_hci_evt_pin_code_req)), - EVENT_HANDLER(BT_HCI_EVT_LINK_KEY_NOTIFY, link_key_notify, + EVENT_HANDLER(BT_HCI_EVT_LINK_KEY_NOTIFY, hci_evt_link_key_notify, sizeof(struct bt_hci_evt_link_key_notify)), - EVENT_HANDLER(BT_HCI_EVT_LINK_KEY_REQ, link_key_req, + EVENT_HANDLER(BT_HCI_EVT_LINK_KEY_REQ, hci_evt_link_key_req, sizeof(struct bt_hci_evt_link_key_req)), - EVENT_HANDLER(BT_HCI_EVT_IO_CAPA_RESP, io_capa_resp, + EVENT_HANDLER(BT_HCI_EVT_IO_CAPA_RESP, hci_evt_io_capa_resp, sizeof(struct bt_hci_evt_io_capa_resp)), - EVENT_HANDLER(BT_HCI_EVT_IO_CAPA_REQ, io_capa_req, + EVENT_HANDLER(BT_HCI_EVT_IO_CAPA_REQ, hci_evt_io_capa_req, sizeof(struct bt_hci_evt_io_capa_req)), - EVENT_HANDLER(BT_HCI_EVT_SSP_COMPLETE, ssp_complete, + EVENT_HANDLER(BT_HCI_EVT_SSP_COMPLETE, hci_evt_ssp_complete, sizeof(struct bt_hci_evt_ssp_complete)), - EVENT_HANDLER(BT_HCI_EVT_USER_CONFIRM_REQ, user_confirm_req, + EVENT_HANDLER(BT_HCI_EVT_USER_CONFIRM_REQ, hci_evt_user_confirm_req, sizeof(struct bt_hci_evt_user_confirm_req)), - EVENT_HANDLER(BT_HCI_EVT_USER_PASSKEY_NOTIFY, user_passkey_notify, + EVENT_HANDLER(BT_HCI_EVT_USER_PASSKEY_NOTIFY, + hci_evt_user_passkey_notify, sizeof(struct bt_hci_evt_user_passkey_notify)), - EVENT_HANDLER(BT_HCI_EVT_USER_PASSKEY_REQ, user_passkey_req, + EVENT_HANDLER(BT_HCI_EVT_USER_PASSKEY_REQ, hci_evt_user_passkey_req, sizeof(struct bt_hci_evt_user_passkey_req)), EVENT_HANDLER(BT_HCI_EVT_INQUIRY_COMPLETE, inquiry_complete, sizeof(struct bt_hci_evt_inquiry_complete)), @@ -5408,7 +5023,7 @@ static const struct event_handler normal_events[] = { EVENT_HANDLER(BT_HCI_EVT_REMOTE_NAME_REQ_COMPLETE, remote_name_request_complete, sizeof(struct bt_hci_evt_remote_name_req_complete)), - EVENT_HANDLER(BT_HCI_EVT_AUTH_COMPLETE, auth_complete, + EVENT_HANDLER(BT_HCI_EVT_AUTH_COMPLETE, hci_evt_auth_complete, sizeof(struct bt_hci_evt_auth_complete)), EVENT_HANDLER(BT_HCI_EVT_REMOTE_FEATURES, read_remote_features_complete, diff --git a/subsys/bluetooth/host/hci_core.h b/subsys/bluetooth/host/hci_core.h index a3308e78932..f533abe6273 100644 --- a/subsys/bluetooth/host/hci_core.h +++ b/subsys/bluetooth/host/hci_core.h @@ -295,6 +295,8 @@ struct bt_dev { extern struct bt_dev bt_dev; #if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR) extern const struct bt_conn_auth_cb *bt_auth; + +enum bt_security_err bt_security_err_get(uint8_t hci_err); #endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */ int bt_hci_disconnect(uint16_t handle, uint8_t reason); @@ -331,3 +333,15 @@ int bt_le_adv_start_internal(const struct bt_le_adv_param *param, void bt_le_adv_resume(void); bool bt_le_scan_random_addr_check(void); + +/* HCI event handlers */ +void hci_evt_pin_code_req(struct net_buf *buf); +void hci_evt_link_key_notify(struct net_buf *buf); +void hci_evt_link_key_req(struct net_buf *buf); +void hci_evt_io_capa_resp(struct net_buf *buf); +void hci_evt_io_capa_req(struct net_buf *buf); +void hci_evt_ssp_complete(struct net_buf *buf); +void hci_evt_user_confirm_req(struct net_buf *buf); +void hci_evt_user_passkey_notify(struct net_buf *buf); +void hci_evt_user_passkey_req(struct net_buf *buf); +void hci_evt_auth_complete(struct net_buf *buf); diff --git a/subsys/bluetooth/host/ssp.c b/subsys/bluetooth/host/ssp.c new file mode 100644 index 00000000000..8afdb41436b --- /dev/null +++ b/subsys/bluetooth/host/ssp.c @@ -0,0 +1,789 @@ +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include + +#include +#include + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_CORE) +#define LOG_MODULE_NAME bt_ssp +#include "common/log.h" + +#include "keys.h" + +#include "hci_core.h" +#include "conn_internal.h" + +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; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_PIN_CODE_NEG_REPLY, sizeof(*cp)); + 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; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_PIN_CODE_REPLY, sizeof(*cp)); + 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 (conn->type != BT_CONN_TYPE_BR) { + return -EINVAL; + } + + len = strlen(pin); + if (len > 16) { + return -EINVAL; + } + + if (conn->required_sec_level == BT_SECURITY_L3 && len < 16) { + BT_WARN("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) +{ + /* Validate no bond auth request, and if valid use it. */ + if ((conn->br.remote_auth == BT_HCI_NO_BONDING) || + ((conn->br.remote_auth == BT_HCI_NO_BONDING_MITM) && + (ssp_pair_method(conn) > JUST_WORKS))) { + return conn->br.remote_auth; + } + + /* Local & remote have enough IO capabilities to get MITM protection. */ + if (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; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_USER_CONFIRM_REPLY, sizeof(*cp)); + 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; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_USER_CONFIRM_NEG_REPLY, sizeof(*cp)); + 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) +{ + if (!status) { + bool bond = !atomic_test_bit(conn->flags, BT_CONN_BR_NOBOND); + + if (bt_auth && bt_auth->pairing_complete) { + bt_auth->pairing_complete(conn, bond); + } + } else { + if (bt_auth && bt_auth->pairing_failed) { + bt_auth->pairing_failed(conn, status); + } + } +} + +static void ssp_auth(struct bt_conn *conn, uint32_t passkey) +{ + conn->br.pairing_method = ssp_pair_method(conn); + + /* + * 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) { + BT_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; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_USER_PASSKEY_REPLY, sizeof(*cp)); + 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; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_USER_PASSKEY_NEG_REPLY, sizeof(*cp)); + 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; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_AUTH_REQUESTED, sizeof(*auth)); + 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 (conn->required_sec_level > BT_SECURITY_L3) { + return -ENOTSUP; + } + + 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 hci_evt_pin_code_req(struct net_buf *buf) +{ + struct bt_hci_evt_pin_code_req *evt = (void *)buf->data; + struct bt_conn *conn; + + BT_DBG(""); + + conn = bt_conn_lookup_addr_br(&evt->bdaddr); + if (!conn) { + BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr)); + return; + } + + pin_code_req(conn); + bt_conn_unref(conn); +} + +void hci_evt_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) { + BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr)); + return; + } + + BT_DBG("%s, link type 0x%02x", bt_addr_str(&evt->bdaddr), evt->key_type); + + if (!conn->br.link_key) { + conn->br.link_key = bt_keys_get_link_key(&evt->bdaddr); + } + if (!conn->br.link_key) { + BT_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; + /* fall through */ + case BT_LK_UNAUTH_COMBINATION_P192: + /* Mark no-bond so that link-key is removed on disconnection */ + if (ssp_get_auth(conn) < BT_HCI_DEDICATED_BONDING) { + atomic_set_bit(conn->flags, BT_CONN_BR_NOBOND); + } + + 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; + /* fall through */ + case BT_LK_UNAUTH_COMBINATION_P256: + conn->br.link_key->flags |= BT_LINK_KEY_SC; + + /* Mark no-bond so that link-key is removed on disconnection */ + if (ssp_get_auth(conn) < BT_HCI_DEDICATED_BONDING) { + atomic_set_bit(conn->flags, BT_CONN_BR_NOBOND); + } + + memcpy(conn->br.link_key->val, evt->link_key, 16); + break; + default: + BT_WARN("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; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_LINK_KEY_NEG_REPLY, sizeof(*cp)); + if (!buf) { + BT_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; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_LINK_KEY_REPLY, sizeof(*cp)); + if (!buf) { + BT_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 hci_evt_link_key_req(struct net_buf *buf) +{ + struct bt_hci_evt_link_key_req *evt = (void *)buf->data; + struct bt_conn *conn; + + BT_DBG("%s", bt_addr_str(&evt->bdaddr)); + + conn = bt_conn_lookup_addr_br(&evt->bdaddr); + if (!conn) { + BT_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_create(BT_HCI_OP_IO_CAPABILITY_NEG_REPLY, + sizeof(*cp)); + if (!resp_buf) { + BT_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 hci_evt_io_capa_resp(struct net_buf *buf) +{ + struct bt_hci_evt_io_capa_resp *evt = (void *)buf->data; + struct bt_conn *conn; + + BT_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) { + BT_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) { + BT_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) { + BT_ERR("Unable to find conn for %s", bt_addr_str(&evt->bdaddr)); + 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); +} + +void hci_evt_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; + + BT_DBG(""); + + conn = bt_conn_lookup_addr_br(&evt->bdaddr); + if (!conn) { + BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr)); + return; + } + + resp_buf = bt_hci_cmd_create(BT_HCI_OP_IO_CAPABILITY_REPLY, + sizeof(*cp)); + if (!resp_buf) { + BT_ERR("Out of command buffers"); + bt_conn_unref(conn); + return; + } + + /* + * 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) { + auth = BT_HCI_DEDICATED_BONDING_MITM; + } else { + auth = BT_HCI_DEDICATED_BONDING; + } + } else { + auth = ssp_get_auth(conn); + } + + 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 hci_evt_ssp_complete(struct net_buf *buf) +{ + struct bt_hci_evt_ssp_complete *evt = (void *)buf->data; + struct bt_conn *conn; + + BT_DBG("status 0x%02x", evt->status); + + conn = bt_conn_lookup_addr_br(&evt->bdaddr); + if (!conn) { + BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr)); + return; + } + + 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 hci_evt_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) { + BT_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 hci_evt_user_passkey_notify(struct net_buf *buf) +{ + struct bt_hci_evt_user_passkey_notify *evt = (void *)buf->data; + struct bt_conn *conn; + + BT_DBG(""); + + conn = bt_conn_lookup_addr_br(&evt->bdaddr); + if (!conn) { + BT_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 hci_evt_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) { + BT_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; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_SET_CONN_ENCRYPT, sizeof(*encr)); + if (!buf) { + BT_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 hci_evt_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); + + BT_DBG("status 0x%02x, handle %u", evt->status, handle); + + conn = bt_conn_lookup_handle(handle); + if (!conn) { + BT_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); +} diff --git a/subsys/bluetooth/host/ssp.h b/subsys/bluetooth/host/ssp.h new file mode 100644 index 00000000000..6d779d785d0 --- /dev/null +++ b/subsys/bluetooth/host/ssp.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +int bt_ssp_start_security(struct bt_conn *conn); + +int bt_ssp_auth_passkey_entry(struct bt_conn *conn, unsigned int passkey); +int bt_ssp_auth_passkey_confirm(struct bt_conn *conn); +int bt_ssp_auth_cancel(struct bt_conn *conn); +int bt_ssp_auth_pairing_confirm(struct bt_conn *conn);