Bluetooth: Add LE SecMgr and mgmtops support
Subsystems (2)
RFC 1/1 Bluetooth: Add LE SecMgr and mgmtops support
Implemented missing components of MGMTOPS interface Differentiated as needed between BR/EDR pairing and LE pairing
These are a combination of changes we made to get make MGMTOPS more robust, and provide MITM pairing options for LE. Much work has been put into merging these changes into the current master branch of Bluetooth Next, but they have not been extensively tested on that build.
This is anticipated to be the first step towards upstreaming these changes,
--- include/net/bluetooth/hci.h | 36 ++ include/net/bluetooth/hci_core.h | 32 ++- include/net/bluetooth/l2cap.h | 5 + include/net/bluetooth/mgmt.h | 24 ++- include/net/bluetooth/smp.h | 14 +- net/bluetooth/hci_conn.c | 58 +++- net/bluetooth/hci_core.c | 64 +++- net/bluetooth/hci_event.c | 166 ++++++--- net/bluetooth/hci_sock.c | 9 +- net/bluetooth/l2cap_core.c | 15 +- net/bluetooth/mgmt.c | 743 ++++++++++++++++++++++++++++++++------ net/bluetooth/smp.c | 661 ++++++++++++++++++++++++--------- 12 files changed, 1441 insertions(+), 386 deletions(-)
include/net/bluetooth/hci.h +36 -0
@@ -632,6 +632,12 @@ struct hci_cp_host_buffer_size {__le16 sco_max_pkt; } __packed; +#define HCI_OP_WRITE_CURRENT_IAC_LAP 0x0c3a +struct hci_cp_write_current_iac_lap { +__u8 num_current_iac; +__u8 lap[6 ]; +} __packed; +#define HCI_OP_WRITE_INQUIRY_MODE 0x0c45#define HCI_MAX_EIR_LENGTH 240
@@ -753,6 +759,15 @@ struct hci_rp_le_read_buffer_size {__u8 le_max_pkt; } __packed; +#define HCI_OP_LE_SET_SCAN_PARAMETERS 0x200b +struct hci_cp_le_set_scan_parameters { +__u8 type; +__le16 interval; +__le16 window; +__u8 own_bdaddr_type; +__u8 filter; +} __packed; +#define HCI_OP_LE_SET_SCAN_ENABLE 0x200cstruct hci_cp_le_set_scan_enable {__u8 enable;
@@ -788,6 +803,16 @@ struct hci_cp_le_conn_update {__le16 max_ce_len; } __packed; +#define HCI_OP_LE_ENCRYPT 0x2017 +struct hci_cp_le_encrypt { +__u8 key[16 ]; +__u8 data[16 ]; +} __packed; +struct hci_cp_le_encrypt_reply { +__u8 status; +__u8 encrypted[16 ]; +} __packed; +#define HCI_OP_LE_START_ENC 0x2019struct hci_cp_le_start_enc {__le16 handle;
@@ -1069,6 +1094,11 @@ struct hci_ev_user_confirm_req {__le32 passkey; } __packed; +#define HCI_EV_USER_PASSKEY_REQUEST 0x34 +struct hci_ev_user_passkey_request { +bdaddr_t bdaddr; +} __packed; +#define HCI_EV_REMOTE_OOB_DATA_REQUEST 0x35struct hci_ev_remote_oob_data_request {bdaddr_t bdaddr;
@@ -1080,6 +1110,12 @@ struct hci_ev_simple_pair_complete {bdaddr_t bdaddr; } __packed; +#define HCI_EV_USER_PASSKEY_NOTIFICATION 0x3b +struct hci_ev_user_passkey_notification { +bdaddr_t bdaddr; +__le32 passkey; +} __packed; +#define HCI_EV_REMOTE_HOST_FEATURES 0x3dstruct hci_ev_remote_host_features {bdaddr_t bdaddr;
include/net/bluetooth/hci_core.h +16 -1
@@ -89,11 +89,17 @@ struct key_master_id {u8 rand[8 ]; } __packed; +#define KEY_TYPE_LE_BASE 0x11 +#define KEY_TYPE_LTK 0x11 +#define KEY_TYPE_IRK 0x12 +#define KEY_TYPE_CSRK 0x13 +struct link_key_data {bdaddr_t bdaddr;u8 type;u8 val[16 ];u8 pin_len; +u8 auth;u8 dlen;u8 data[0 ]; } __packed;
@@ -101,9 +107,11 @@ struct link_key_data {struct link_key {struct list_head list;bdaddr_t bdaddr; -u8 type; +u8 addr_type; +u8 key_type;u8 val[16 ];u8 pin_len; +u8 auth;u8 dlen;u8 data[0 ]; };
@@ -119,6 +127,7 @@ struct adv_entry {struct list_head list;bdaddr_t bdaddr;u8 bdaddr_type; +u8 flags; };#define NUM_REASSEMBLY 4
@@ -215,6 +224,8 @@ struct hci_dev {__u16 init_last_cmd; +struct crypto_blkcipher *tfm; +struct inquiry_cache inq_cache;struct hci_conn_hash conn_hash;struct list_head blacklist;
@@ -282,6 +293,7 @@ struct hci_conn {__u8 pin_length;__u8 enc_key_size;__u8 io_capability; +__u8 auth_initiator;__u8 power_save;__u16 disc_timeout;unsigned long pend;
@@ -311,6 +323,9 @@ struct hci_conn {struct hci_conn *link; +/* Low Energy SMP Connection */ +void *smp_conn; +void (*connect_cfm_cb) (struct hci_conn *conn,u8 status);void (*security_cfm_cb) (struct hci_conn *conn,u8 status);void (*disconn_cfm_cb) (struct hci_conn *conn,u8 reason);
@@ -627,8 +642,8 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, struct link_key *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8
rand[8]);
struct link_key *hci_find_link_key_type(struct hci_dev *hdev,
bdaddr_t *bdaddr, u8 type);
-int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr,
- u8 key_size, __le16 ediv, u8 rand[8], u8 ltk[16]);
+int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, u8 type,
+ u8 auth, u8 key_size, __le16 ediv, u8 rand[8], u8 ltk[16]);
int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr);
int hci_remote_oob_data_clear(struct hci_dev *hdev);
@@ -918,6 +933,7 @@ int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status);
int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8
status);
int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value,
u8 confirm_hint);
+int mgmt_user_oob_request(u16 index, bdaddr_t *bdaddr);
int mgmt_user_confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8
status);
int mgmt_user_confirm_neg_reply_complete(u16 index, bdaddr_t *bdaddr,
u8 status);
@@ -925,14 +941,18 @@ int mgmt_auth_failed(u16 index, bdaddr_t *bdaddr, u8 status);
int mgmt_set_local_name_complete(u16 index, u8 *name, u8 status);
int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8
*randomizer,
u8 status);
-int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi,
- u8 *eir);
-int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name);
+int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 type, u8 le,
+ u8 *dev_class, s8 rssi, u8 eir_len, u8 *eir);
+int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 status, u8 *name);
int mgmt_inquiry_failed(u16 index, u8 status);
int mgmt_discovering(u16 index, u8 discovering);
+void mgmt_inquiry_complete_evt(u16 index, u8 status);
int mgmt_device_blocked(u16 index, bdaddr_t *bdaddr);
int mgmt_device_unblocked(u16 index, bdaddr_t *bdaddr);
+/* LE SMP Management interface */
+int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, void *cp); +
/* HCI info for socket */
#define hci_pi(sk) ((struct hci_pinfo *) sk)
include/net/bluetooth/l2cap.h +5 -0
@@ -538,6 +538,11 @@ struct l2cap_conn {rwlock_t chan_lock; }; +struct sock_del_list { +struct sock *sk; +struct list_head list; +}; +#define L2CAP_INFO_CL_MTU_REQ_SENT 0x01#define L2CAP_INFO_FEAT_MASK_REQ_SENT 0x04#define L2CAP_INFO_FEAT_MASK_REQ_DONE 0x08
include/net/bluetooth/mgmt.h +23 -1
@@ -90,6 +90,8 @@ struct mgmt_cp_set_dev_class {__u8 major;__u8 minor; } __packed; +#define MGMT_MAJOR_CLASS_MASK 0x1F +#define MGMT_MAJOR_CLASS_LIMITED 0x20#define MGMT_OP_SET_SERVICE_CACHE 0x000Cstruct mgmt_cp_set_service_cache {
@@ -98,9 +100,13 @@ struct mgmt_cp_set_service_cache {struct mgmt_key_info {bdaddr_t bdaddr; -u8 type; +u8 addr_type; +u8 key_type;u8 val[16 ];u8 pin_len; +u8 auth; +u8 dlen; +u8 data[10 ]; } __packed;#define MGMT_OP_LOAD_KEYS 0x000D
@@ -214,6 +220,19 @@ struct mgmt_cp_set_fast_connectable {__u8 enable; } __packed; +#define MGMT_OP_USER_PASSKEY_REPLY 0x0020 +struct mgmt_cp_user_passkey_reply { +bdaddr_t bdaddr; +__le32 passkey; +} __packed; + +#define MGMT_OP_RESOLVE_NAME 0x0021 +struct mgmt_cp_resolve_name { +bdaddr_t bdaddr; +} __packed; + +#define MGMT_OP_SET_LIMIT_DISCOVERABLE 0x0022 +#define MGMT_EV_CMD_COMPLETE 0x0001struct mgmt_ev_cmd_complete {__le16 opcode;
@@ -295,12 +314,15 @@ struct mgmt_ev_device_found {bdaddr_t bdaddr;__u8 dev_class[3 ];__s8 rssi; +__u8 le; +__u8 type;__u8 eir[HCI_MAX_EIR_LENGTH ]; } __packed;#define MGMT_EV_REMOTE_NAME 0x0013struct mgmt_ev_remote_name {bdaddr_t bdaddr; +__u8 status;__u8 name[MGMT_MAX_NAME_LENGTH ]; } __packed;
include/net/bluetooth/smp.h +13 -1
@@ -55,6 +55,13 @@ struct smp_cmd_pairing {#define SMP_AUTH_BONDING 0x01#define SMP_AUTH_MITM 0x04 +#define SMP_JUST_WORKS 0x00 +#define SMP_JUST_CFM 0x01 +#define SMP_REQ_PASSKEY 0x02 +#define SMP_CFM_PASSKEY 0x03 +#define SMP_REQ_OOB 0x04 +#define SMP_OVERLAP 0xFF +#define SMP_CMD_PAIRING_CONFIRM 0x03struct smp_cmd_pairing_confirm {__u8 confirm_val[16 ];
@@ -123,7 +130,11 @@ struct smp_chan {u8 rrnd[16 ];/* SMP Pairing Random (remote) */ u8 pcnf[16 ];/* SMP Pairing Confirm */ u8 tk[16 ];/* SMP Temporary Key */ +u8 smp_tk_valid; +u8 smp_cfm_pending;u8 smp_key_size; +u8 smp_sec_req; +u8 smp_auth;struct crypto_blkcipher *tfm;struct work_struct confirm;struct work_struct random;
@@ -133,7 +144,8 @@ struct smp_chan {/* SMP Commands */ int smp_conn_security (struct l2cap_conn *conn,__u8 sec_level);int smp_sig_channel (struct l2cap_conn *conn,struct sk_buff *skb); -int smp_distribute_keys (struct l2cap_conn *conn,__u8 force); +int smp_link_encrypt_cmplt (struct l2cap_conn *conn,__u8 status,__u8 encrypt); +void smp_timeout (struct l2cap_conn *conn);void smp_chan_destroy (struct l2cap_conn *conn);
net/bluetooth/hci_conn.c +29 -14
96 lines not on this page · raw message
auth_type)
{
- BT_DBG("conn %p", conn);
+ BT_DBG("conn %p %d %d", conn, sec_level, auth_type);
/* For sdp we don't need the link key. */
if (sec_level == BT_SECURITY_SDP)
@@ -674,8 +689,19 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
goto encrypt;
auth:
- if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
+ if (conn->type == LE_LINK) {
+ if (conn->pending_sec_level > sec_level)
+ sec_level = conn->pending_sec_level;
+
+ if (sec_level > conn->sec_level)
+ conn->pending_sec_level = sec_level;
+ hci_proto_connect_cfm(conn, 0);
+ return 0;
+ } else if (conn->link_mode & HCI_LM_ENCRYPT) {
+ return hci_conn_auth(conn, sec_level, auth_type);
+ } else if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) {
return 0;
+ }
if (!hci_conn_auth(conn, sec_level, auth_type))
return 0;
net/bluetooth/hci_core.c +44 -20
173 lines not on this page · raw message
net/bluetooth/hci_event.c +33 -8
77 lines not on this page · raw message
sk_buff *skb)
@@ -1403,8 +1428,8 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
data.rssi = 0x00;
data.ssp_mode = 0x00;
hci_inquiry_cache_update(hdev, &data);
- mgmt_device_found(hdev->id, &info->bdaddr, info->dev_class, 0,
- NULL);
+ mgmt_device_found(hdev->id, &info->bdaddr, 0, 0,
+ info->dev_class, 0, 0, NULL);
}
hci_dev_unlock(hdev);
@@ -1438,6 +1463,10 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
conn->state = BT_CONFIG;
hci_conn_hold(conn);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
+ mgmt_connected(hdev->id, &ev->bdaddr, 0);
+ } else if (conn->type == LE_LINK) {
+ conn->state = BT_CONNECTED;
+ conn->disc_timeout = HCI_DISCONN_TIMEOUT;
mgmt_connected(hdev->id, &ev->bdaddr, conn->type);
} else
conn->state = BT_CONNECTED;
@@ -1469,7 +1498,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
}
} else {
conn->state = BT_CLOSED;
- if (conn->type == ACL_LINK)
+ if (conn->type == ACL_LINK || conn->type == LE_LINK)
mgmt_connect_failed(hdev->id, &ev->bdaddr, ev->status);
}
@@ -1668,8 +1697,8 @@ static inline void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb
hci_dev_lock(hdev);
- if (ev->status == 0 && test_bit(HCI_MGMT, &hdev->flags))
- mgmt_remote_name(hdev->id, &ev->bdaddr, ev->name);
+ if (test_bit(HCI_MGMT, &hdev->flags))
+ mgmt_remote_name(hdev->id, &ev->bdaddr, ev->status, ev->name);
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
if (!conn)
@@ -2252,32 +2281,32 @@ static inline void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff
goto not_found;
}
- BT_DBG("%s found key type %u for %s", hdev->name, key->type,
+ BT_DBG("%s found key type %u for %s", hdev->name, key->key_type,
batostr(&ev->bdaddr));
if (!test_bit(HCI_DEBUG_KEYS, &hdev->flags) &&
- key->type == HCI_LK_DEBUG_COMBINATION) {
+ key->key_type == HCI_LK_DEBUG_COMBINATION) {
BT_DBG("%s ignoring debug key", hdev->name); goto not_found;
}
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); if (conn) {
- if (key->type == HCI_LK_UNAUTH_COMBINATION &&
+ if (key->key_type == HCI_LK_UNAUTH_COMBINATION &&
conn->auth_type != 0xff && (conn->auth_type & 0x01)) {
BT_DBG("%s ignoring unauthenticated key", hdev->name); goto not_found;
}
- if (key->type == HCI_LK_COMBINATION && key->pin_len < 16 &&
+ if (key->key_type == HCI_LK_COMBINATION && key->pin_len < 16 &&
conn->pending_sec_level == BT_SECURITY_HIGH) {
BT_DBG("%s ignoring key unauthenticated for high \
security", hdev->name);
goto not_found;
}
- conn->key_type = key->type;
+ conn->key_type = key->key_type;
conn->pin_length = key->pin_len;
}
@@ -2413,9 +2442,9 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
data.rssi = info->rssi;
data.ssp_mode = 0x00;
hci_inquiry_cache_update(hdev, &data);
- mgmt_device_found(hdev->id, &info->bdaddr,
+ mgmt_device_found(hdev->id, &info->bdaddr, 0, 0,
info->dev_class, info->rssi,
- NULL);
+ 0, NULL);
}
} else {
struct inquiry_info_with_rssi *info = (void *) (skb->data + 1);
@@ -2430,9 +2459,9 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
data.rssi = info->rssi;
data.ssp_mode = 0x00;
hci_inquiry_cache_update(hdev, &data);
- mgmt_device_found(hdev->id, &info->bdaddr,
+ mgmt_device_found(hdev->id, &info->bdaddr, 0, 0,
info->dev_class, info->rssi,
- NULL);
+ 0, NULL);
}
}
@@ -2579,8 +2608,9 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
data.rssi = info->rssi;
data.ssp_mode = 0x01;
hci_inquiry_cache_update(hdev, &data);
- mgmt_device_found(hdev->id, &info->bdaddr, info->dev_class,
- info->rssi, info->data);
+ mgmt_device_found(hdev->id, &info->bdaddr, 0, 0,
+ info->dev_class, info->rssi,
+ HCI_MAX_EIR_LENGTH, info->data);
}
hci_dev_unlock(hdev);
@@ -2588,19 +2618,23 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
static inline u8 hci_get_auth_req(struct hci_conn *conn)
{
+ BT_DBG("%p", conn);
+
/* If remote requests dedicated bonding follow that lead */ if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03) {
/* If both remote and local IO capabilities allow MITM
* protection then require it, otherwise don't */
- if (conn->remote_cap == 0x03 || conn->io_capability == 0x03)
+ if (conn->remote_cap == 0x03 || conn->io_capability == 0x03) { return 0x02;
- else
+ } else {
+ conn->auth_type |= 0x01;
return 0x03;
+ }
}
/* If remote requests no-bonding follow that lead */
- if (conn->remote_auth == 0x00 || conn->remote_auth == 0x01)
- return conn->remote_auth | (conn->auth_type & 0x01);
+ if (conn->remote_auth <= 0x01)
+ return 0x00;
return conn->auth_type;
}
@@ -2626,11 +2660,15 @@ static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff
if (test_bit(HCI_PAIRABLE, &hdev->flags) ||
(conn->remote_auth & ~0x01) == HCI_AT_NO_BONDING) {
struct hci_cp_io_capability_reply cp;
+ u8 io_cap = conn->io_capability;
+ /* ACL-SSP does not support IO CAP 0x04 */
+ cp.capability = (io_cap == 0x04) ? 0x01 : io_cap;
bacpy(&cp.bdaddr, &ev->bdaddr);
- cp.capability = conn->io_capability;
- conn->auth_type = hci_get_auth_req(conn);
- cp.authentication = conn->auth_type;
+ if (conn->auth_initiator)
+ cp.authentication = conn->auth_type;
+ else
+ cp.authentication = hci_get_auth_req(conn);
if ((conn->out == 0x01 || conn->remote_oob == 0x01) &&
hci_find_remote_oob_data(hdev, &conn->dst))
@@ -2675,24 +2713,28 @@ unlock:
hci_dev_unlock(hdev);
}
-static inline void hci_user_confirm_request_evt(struct hci_dev *hdev,
- struct sk_buff *skb)
+static inline void hci_user_ssp_confirmation_evt(struct hci_dev *hdev,
+ u8 event, struct sk_buff *skb)
{
struct hci_ev_user_confirm_req *ev = (void *) skb->data; int loc_mitm, rem_mitm, confirm_hint = 0;
struct hci_conn *conn;
+ __le32 passkey = 0;
BT_DBG("%s", hdev->name);
hci_dev_lock(hdev);
- if (!test_bit(HCI_MGMT, &hdev->flags))
- goto unlock;
-
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); if (!conn)
goto unlock;
+ if (event != HCI_EV_USER_PASSKEY_REQUEST)
+ passkey = ev->passkey;
+
+ if (event != HCI_EV_USER_CONFIRM_REQUEST)
+ goto confirm;
+
loc_mitm = (conn->auth_type & 0x01);
rem_mitm = (conn->remote_auth & 0x01);
@@ -2736,7 +2778,8 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev,
}
confirm:
- mgmt_user_confirm_request(hdev->id, &ev->bdaddr, ev->passkey,
+ if (test_bit(HCI_MGMT, &hdev->flags))
+ mgmt_user_confirm_request(hdev->id, &ev->bdaddr, passkey,
confirm_hint);
unlock:
@@ -2855,7 +2898,10 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
conn->sec_level = BT_SECURITY_LOW;
conn->handle = __le16_to_cpu(ev->handle);
conn->state = BT_CONNECTED;
+ conn->disc_timeout = HCI_DISCONN_TIMEOUT;
+ mgmt_connected(hdev->id, &ev->bdaddr, 1);
+ hci_conn_hold(conn);
hci_conn_hold_device(conn);
hci_conn_add_sysfs(conn);
@@ -2865,25 +2911,6 @@ unlock:
hci_dev_unlock(hdev);
}
-static inline void hci_le_adv_report_evt(struct hci_dev *hdev,
- struct sk_buff *skb)
-{
- u8 num_reports = skb->data[0];
- void *ptr = &skb->data[1];
-
- hci_dev_lock(hdev);
-
- while (num_reports--) {
- struct hci_ev_le_advertising_info *ev = ptr;
-
- hci_add_adv_entry(hdev, ev);
-
- ptr += sizeof(*ev) + ev->length + 1;
- }
-
- hci_dev_unlock(hdev);
-}
-
static inline void hci_le_ltk_request_evt(struct hci_dev *hdev,
struct sk_buff *skb)
{
@@ -2921,6 +2948,27 @@ not_found:
hci_dev_unlock(hdev);
}
+static inline void hci_le_adv_report_evt(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_ev_le_advertising_info *ev;
+ u8 num_reports;
+
+ num_reports = skb->data[0];
+ ev = (void *) &skb->data[1];
+
+ hci_dev_lock(hdev);
+
+ while (num_reports--) {
+ mgmt_device_found(hdev->id, &ev->bdaddr, ev->bdaddr_type,
+ 1, NULL, 0, ev->length, ev->data);
+ hci_add_adv_entry(hdev, ev);
+ ev = (void *) (ev->data + ev->length + 1);
+ }
+
+ hci_dev_unlock(hdev);
+}
+
static inline void hci_le_meta_evt(struct hci_dev *hdev, structsk_buff *skb)
{
struct hci_ev_le_meta *le_ev = (void *) skb->data;
@@ -2950,6 +2998,8 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
struct hci_event_hdr *hdr = (void *) skb->data;
__u8 event = hdr->evt;
+ BT_DBG("");
+
skb_pull(skb, HCI_EVENT_HDR_SIZE);
switch (event) {
@@ -3077,8 +3127,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_io_capa_reply_evt(hdev, skb);
break;
+ case HCI_EV_USER_PASSKEY_REQUEST:
+ case HCI_EV_USER_PASSKEY_NOTIFICATION:
case HCI_EV_USER_CONFIRM_REQUEST:
- hci_user_confirm_request_evt(hdev, skb);
+ hci_user_ssp_confirmation_evt(hdev, event, skb);
break;
case HCI_EV_SIMPLE_PAIR_COMPLETE:
net/bluetooth/hci_sock.c +7 -2
@@ -49,7 +49,7 @@#include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> -static int enable_mgmt; +static int enable_mgmt =1 ;/* ----- HCI socket interface ----- */
@@ -281,7 +281,12 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long acase HCIDEVUP :if (!capable (CAP_NET_ADMIN ))return -EACCES ; -return hci_dev_open (arg); + + err =hci_dev_open (arg); +if (!err || err == -EALREADY ) +return 0 ; +else +return err;case HCIDEVDOWN :if (!capable (CAP_NET_ADMIN ))
net/bluetooth/l2cap_core.c +13 -2
@@ -947,7 +947,13 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)bh_lock_sock (sk);if (conn->hcon->type ==LE_LINK ) { -if (smp_conn_security (conn, chan->sec_level)) +u8 sec_level = chan->sec_level; +u8 pending_sec = conn->hcon->pending_sec_level; + +if (pending_sec > sec_level) + sec_level = pending_sec; + +if (smp_conn_security (conn, sec_level))l2cap_chan_ready (sk); }else if (chan->chan_type !=L2CAP_CHAN_CONN_ORIENTED ) {
@@ -958,6 +964,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) }else if (chan->state ==BT_CONNECT )l2cap_do_start (chan); +bh_unlock_sock (sk); }
@@ -1033,6 +1040,7 @@ static void security_timeout(unsigned long arg) {struct l2cap_conn *conn = (void *) arg; +smp_timeout (conn);l2cap_conn_del (conn->hcon,ETIMEDOUT ); }
@@ -4456,7 +4464,6 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)BT_DBG ("conn %p" , conn);if (hcon->type ==LE_LINK ) { -smp_distribute_keys (conn,0 );del_timer (&conn->security_timer); }
@@ -4475,6 +4482,10 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)l2cap_chan_ready (sk); } +del_timer (&conn->security_timer); +l2cap_chan_ready (sk); +smp_link_encrypt_cmplt (conn, status, encrypt); +bh_unlock_sock (sk);continue ; }
net/bluetooth/mgmt.c +151 -1
212 lines not on this page · raw message
*data,
u16 len)
{
@@ -472,6 +622,8 @@ static int mgmt_event(u16 event, u16 index, void *data, u16 data_len,
struct sk_buff *skb;
struct mgmt_hdr *hdr;
+ BT_DBG("hci%d %d", index, event);
+
skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC); if (!skb)
return -ENOMEM;
@@ -684,36 +836,6 @@ static int update_eir(struct hci_dev *hdev)
return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
}
-static u8 get_service_classes(struct hci_dev *hdev)
-{
- struct bt_uuid *uuid;
- u8 val = 0;
-
- list_for_each_entry(uuid, &hdev->uuids, list)
- val |= uuid->svc_hint;
-
- return val;
-}
-
-static int update_class(struct hci_dev *hdev)
-{
- u8 cod[3];
-
- BT_DBG("%s", hdev->name);
-
- if (test_bit(HCI_SERVICE_CACHE, &hdev->flags))
- return 0;
-
- cod[0] = hdev->minor_class;
- cod[1] = hdev->major_class;
- cod[2] = get_service_classes(hdev);
-
- if (memcmp(cod, hdev->dev_class, 3) == 0)
- return 0;
-
- return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
-}
-
static int add_uuid(struct sock *sk, u16 index, unsigned char *data,
u16 len)
{
struct mgmt_cp_add_uuid *cp;
@@ -842,7 +964,8 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data,
hci_dev_lock_bh(hdev);
- hdev->major_class = cp->major;
+ hdev->major_class &= ~MGMT_MAJOR_CLASS_MASK;
+ hdev->major_class |= cp->major & MGMT_MAJOR_CLASS_MASK;
hdev->minor_class = cp->minor;
err = update_class(hdev);
@@ -914,9 +1037,9 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)
key_count = get_unaligned_le16(&cp->key_count);
expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_key_info);
- if (expected_len != len) {
+ if (expected_len > len) {
BT_ERR("load_keys: expected %u bytes, got %u bytes",
- len, expected_len);
+ expected_len, len);
return cmd_status(sk, index, MGMT_OP_LOAD_KEYS, EINVAL);
}
@@ -938,11 +1061,31 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)
else
clear_bit(HCI_DEBUG_KEYS, &hdev->flags);
- for (i = 0; i < key_count; i++) {
- struct mgmt_key_info *key = &cp->keys[i];
+ len -= sizeof(*cp);
+ i = 0;
+
+ while (i < len) {
+ struct mgmt_key_info *key = (void *) cp->keys + i;
+
+ i += sizeof(*key);
+
+ if (key->key_type == KEY_TYPE_LTK) {
+ struct key_master_id *id = (void *) key->data;
- hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val, key->type,
- key->pin_len);
+ i += key->dlen;
+
+ if (key->dlen != sizeof(struct key_master_id))
+ continue;
+
+ hci_add_ltk(hdev, 0, &key->bdaddr, key->addr_type,
+ key->pin_len, key->auth, id->ediv,
+ id->rand, key->val);
+
+ continue;
+ }
+
+ hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val,
+ key->key_type, key->pin_len);
}
hci_dev_unlock_bh(hdev);
@@ -1283,6 +1426,8 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status)
struct mgmt_rp_pair_device rp;
struct hci_conn *conn = cmd->user_data;
+ BT_DBG(" %u", status);
+
bacpy(&rp.bdaddr, &conn->dst);
rp.status = status;
@@ -1293,8 +1438,6 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status)
conn->security_cfm_cb = NULL;
conn->disconn_cfm_cb = NULL;
- hci_conn_put(conn);
-
mgmt_pending_remove(cmd);
}
@@ -1302,7 +1445,7 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status)
{
struct pending_cmd *cmd;
- BT_DBG("status %u", status);
+ BT_DBG(" %u", status);
cmd = find_pairing(conn);
if (!cmd) {
@@ -1313,13 +1456,65 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status)
pairing_complete(cmd, status);
}
+static void pairing_security_complete_cb(struct hci_conn *conn, u8 status) +{
+ struct pending_cmd *cmd;
+
+ BT_DBG(" %u", status);
+
+ cmd = find_pairing(conn);
+ if (!cmd) {
+ BT_DBG("Unable to find a pending command");
+ return;
+ }
+
+ if (conn->type == LE_LINK)
+ smp_link_encrypt_cmplt(conn->l2cap_data, status,
+ status ? 0 : 1);
+ else
+ pairing_complete(cmd, status);
+}
+
+static void pairing_connect_complete_cb(struct hci_conn *conn, u8 status) +{
+ struct pending_cmd *cmd;
+
+ BT_DBG("conn: %p %u", conn, status);
+
+ cmd = find_pairing(conn);
+ if (!cmd) {
+ BT_DBG("Unable to find a pending command");
+ return;
+ }
+
+ if (status)
+ pairing_complete(cmd, status);
+
+ hci_conn_put(conn);
+}
+
+static void discovery_terminated(struct pending_cmd *cmd, void *data) +{
+ struct mgmt_mode ev = {0};
+ struct disco_interleave *ilp = cmd->param;
+
+ BT_DBG("");
+ del_timer_sync(&ilp->le_timer);
+ del_timer_sync(&ilp->timer);
+ mgmt_event(MGMT_EV_DISCOVERING, cmd->index, &ev, sizeof(ev), NULL);
+
+ list_del(&cmd->list);
+
+ mgmt_pending_free(cmd);
+}
+
static int pair_device(struct sock *sk, u16 index, unsigned char
*data, u16 len)
{
struct hci_dev *hdev;
struct mgmt_cp_pair_device *cp;
struct pending_cmd *cmd;
struct adv_entry *entry;
- u8 sec_level, auth_type;
+ u8 sec_level, auth_type, io_cap;
struct hci_conn *conn;
int err;
@@ -1331,24 +1526,30 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, EINVAL);
hdev = hci_dev_get(index);
+
if (!hdev)
return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, ENODEV);
hci_dev_lock_bh(hdev);
sec_level = BT_SECURITY_MEDIUM;
- if (cp->io_cap == 0x03)
+ io_cap = cp->io_cap;
+ if ((io_cap == 0) || (io_cap == 0x03))
auth_type = HCI_AT_DEDICATED_BONDING;
else
auth_type = HCI_AT_DEDICATED_BONDING_MITM;
entry = hci_find_adv_entry(hdev, &cp->bdaddr);
- if (entry)
+ if (entry && entry->flags & 0x04) {
conn = hci_connect(hdev, LE_LINK, &cp->bdaddr, sec_level,
auth_type);
- else
+ } else {
+ /* ACL-SSP does not support io_cap 0x04 (KeyboadDisplay) */
+ if (io_cap == 0x04)
+ io_cap = 0x01;
conn = hci_connect(hdev, ACL_LINK, &cp->bdaddr, sec_level,
auth_type);
+ }
if (IS_ERR(conn)) {
err = PTR_ERR(conn);
@@ -1369,12 +1570,10 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
}
/* For LE, just connecting isn't a proof that the pairing finished */
- if (!entry)
- conn->connect_cfm_cb = pairing_complete_cb;
-
- conn->security_cfm_cb = pairing_complete_cb;
+ conn->connect_cfm_cb = pairing_connect_complete_cb;
+ conn->security_cfm_cb = pairing_security_complete_cb;
conn->disconn_cfm_cb = pairing_complete_cb;
- conn->io_capability = cp->io_cap;
+ conn->io_capability = io_cap;
cmd->user_data = conn;
if (conn->state == BT_CONNECTED &&
@@ -1391,25 +1590,23 @@ unlock:
}
static int user_confirm_reply(struct sock *sk, u16 index, unsigned
char *data,
- u16 len, int success)
+ u16 len, u16 opcode)
{
struct mgmt_cp_user_confirm_reply *cp = (void *) data;
- u16 mgmt_op, hci_op;
+ u16 mgmt_op = opcode, hci_op;
struct pending_cmd *cmd;
struct hci_dev *hdev;
+ struct hci_conn *le_conn;
int err;
- BT_DBG("");
+ BT_DBG("%d", mgmt_op);
- if (success) {
- mgmt_op = MGMT_OP_USER_CONFIRM_REPLY;
- hci_op = HCI_OP_USER_CONFIRM_REPLY;
- } else {
- mgmt_op = MGMT_OP_USER_CONFIRM_NEG_REPLY;
+ if (mgmt_op == MGMT_OP_USER_CONFIRM_NEG_REPLY)
hci_op = HCI_OP_USER_CONFIRM_NEG_REPLY;
- }
+ else
+ hci_op = HCI_OP_USER_CONFIRM_REPLY;
- if (len != sizeof(*cp))
+ if (len < sizeof(*cp))
return cmd_status(sk, index, mgmt_op, EINVAL);
hdev = hci_dev_get(index);
@@ -1420,19 +1617,67 @@ static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data,
if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, mgmt_op, ENETDOWN);
- goto failed;
+ goto done;
+ }
+
+ le_conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr);
+ if (le_conn) {
+ err = smp_user_confirm_reply(le_conn, mgmt_op, (void *) cp);
+ goto done;
}
+ BT_DBG("BR/EDR: %s", mgmt_op == MGMT_OP_USER_CONFIRM_NEG_REPLY ?
+ "Reject" : "Accept");
cmd = mgmt_pending_add(sk, mgmt_op, index, data, len); if (!cmd) {
err = -ENOMEM;
- goto failed;
+ goto done;
}
err = hci_send_cmd(hdev, hci_op, sizeof(cp->bdaddr), &cp->bdaddr); if (err < 0)
mgmt_pending_remove(cmd);
+done:
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
+static int resolve_name(struct sock *sk, u16 index, unsigned char *data,
+ u16 len)
+{
+ struct mgmt_cp_resolve_name *mgmt_cp = (void *) data;
+ struct hci_cp_remote_name_req hci_cp;
+ struct hci_dev *hdev;
+ struct pending_cmd *cmd;
+ int err;
+
+ BT_DBG("");
+
+ if (len != sizeof(*mgmt_cp))
+ return cmd_status(sk, index, MGMT_OP_RESOLVE_NAME, EINVAL);
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_RESOLVE_NAME, ENODEV);
+
+ hci_dev_lock(hdev);
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_RESOLVE_NAME, index, data, len);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ memset(&hci_cp, 0, sizeof(hci_cp));
+ bacpy(&hci_cp.bdaddr, &mgmt_cp->bdaddr);
+ err = hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(hci_cp),
+ &hci_cp);
+ if (err < 0)
+ mgmt_pending_remove(cmd);
+
failed:
hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
@@ -1479,6 +1724,169 @@ failed:
return err;
}
+static void discovery_rsp(struct pending_cmd *cmd, void *data)
+{
+ struct mgmt_mode ev;
+
+ BT_DBG("");
+ if (cmd->opcode == MGMT_OP_START_DISCOVERY) {
+ ev.val = 1;
+ cmd_status(cmd->sk, cmd->index, MGMT_OP_START_DISCOVERY, 0);
+ } else {
+ ev.val = 0;
+ cmd_complete(cmd->sk, cmd->index, MGMT_OP_STOP_DISCOVERY,
+ NULL, 0);
+ if (cmd->opcode == MGMT_OP_STOP_DISCOVERY) {
+ struct disco_interleave *ilp = cmd->param;
+
+ del_timer_sync(&ilp->le_timer);
+ del_timer_sync(&ilp->timer);
+ }
+ }
+
+ mgmt_event(MGMT_EV_DISCOVERING, cmd->index, &ev, sizeof(ev), NULL);
+
+ list_del(&cmd->list);
+
+ mgmt_pending_free(cmd);
+}
+
+void mgmt_inquiry_started(u16 index)
+{
+ BT_DBG("");
+ mgmt_pending_foreach(MGMT_OP_START_DISCOVERY, index,
+ discovery_rsp, NULL);
+}
+
+void mgmt_inquiry_complete_evt(u16 index, u8 status)
+{
+ struct hci_dev *hdev;
+ struct hci_cp_le_set_scan_enable le_cp = {1, 0};
+ struct pending_cmd *cmd;
+ int err = -1;
+
+ BT_DBG("");
+
+ hdev = hci_dev_get(index);
+
+ if (hdev)
+ hci_dev_lock(hdev);
+
+ if (!hdev || !lmp_le_capable(hdev)) {
+ struct mgmt_mode cp = {0};
+
+ mgmt_pending_foreach(MGMT_OP_STOP_DISCOVERY, index,
+ discovery_terminated, NULL);
+
+ mgmt_event(MGMT_EV_DISCOVERING, index, &cp, sizeof(cp), NULL);
+
+ if (hdev)
+ goto done;
+ else
+ return;
+ }
+
+ cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index);
+ if (cmd && cmd->param) {
+ struct disco_interleave *ilp = cmd->param;
+
+ err = hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE,
+ sizeof(le_cp), &le_cp);
+ if (err >= 0) {
+ mod_timer(&ilp->le_timer, jiffies +
+ msecs_to_jiffies(ilp->int_phase * 1000));
+ ilp->mode = SCAN_LE;
+ } else
+ ilp->mode = SCAN_IDLE;
+ }
+
+ if (err < 0)
+ mgmt_pending_foreach(MGMT_OP_STOP_DISCOVERY, index,
+ discovery_terminated, NULL);
+
+done:
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+}
+
+static void disco_to(unsigned long data)
+{
+ struct disco_interleave *ilp = (void *)data;
+ struct hci_dev *hdev;
+ struct pending_cmd *cmd;
+
+ BT_DBG("hci%d", ilp->index);
+
+ del_timer_sync(&ilp->le_timer);
+ hdev = hci_dev_get(ilp->index);
+
+ if (hdev) {
+ hci_dev_lock(hdev);
+
+ cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, ilp->index);
+
+ if (ilp->mode != SCAN_IDLE) {
+ struct hci_cp_le_set_scan_enable le_cp = {0, 0};
+
+ if (ilp->mode == SCAN_LE)
+ hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE,
+ sizeof(le_cp), &le_cp);
+ else
+ hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL,
+ 0, NULL);
+
+ ilp->mode = SCAN_IDLE;
+ }
+
+ if (cmd) {
+ struct mgmt_mode cp = {0};
+
+ mgmt_event(MGMT_EV_DISCOVERING, ilp->index, &cp,
+ sizeof(cp), NULL);
+ mgmt_pending_remove(cmd);
+ }
+
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+ }
+}
+
+static void disco_le_to(unsigned long data)
+{
+ struct disco_interleave *ilp = (void *)data;
+ struct hci_dev *hdev;
+ struct pending_cmd *cmd;
+ struct hci_cp_le_set_scan_enable le_cp = {0, 0};
+
+ BT_DBG("hci%d", ilp->index);
+
+ hdev = hci_dev_get(ilp->index);
+
+ if (hdev) {
+ hci_dev_lock(hdev);
+
+ cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, ilp->index);
+
+ if (ilp->mode == SCAN_LE)
+ hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE,
+ sizeof(le_cp), &le_cp);
+
+ /* re-start BR scan */
+ if (cmd) {
+ struct hci_cp_inquiry cp = {{0x33, 0x8b, 0x9e}, 4, 0};
+ ilp->int_phase *= 2;
+ ilp->int_count = 0;
+ cp.num_rsp = (u8) ilp->int_phase;
+ hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp);
+ ilp->mode = SCAN_BR;
+ } else
+ ilp->mode = SCAN_IDLE;
+
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+ }
+}
+
static int read_local_oob_data(struct sock *sk, u16 index)
{
struct hci_dev *hdev;
@@ -1598,19 +2006,18 @@ static int remove_remote_oob_data(struct sock *sk, u16 index,
static int start_discovery(struct sock *sk, u16 index) {
- u8 lap[3] = { 0x33, 0x8b, 0x9e };
- struct hci_cp_inquiry cp;
- struct pending_cmd *cmd;
+ struct hci_cp_inquiry cp = {{0x33, 0x8b, 0x9e}, 8, 0};
struct hci_dev *hdev;
+ struct pending_cmd *cmd;
int err;
- BT_DBG("hci%u", index);
+ BT_DBG("");
hdev = hci_dev_get(index);
if (!hdev)
return cmd_status(sk, index, MGMT_OP_START_DISCOVERY, ENODEV);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, index, NULL, 0); if (!cmd) {
@@ -1618,17 +2025,54 @@ static int start_discovery(struct sock *sk, u16 index)
goto failed;
}
- memset(&cp, 0, sizeof(cp));
- memcpy(&cp.lap, lap, 3);
- cp.length = 0x08;
- cp.num_rsp = 0x00;
+ /* If LE Capable, we will alternate between BR/EDR and LE */
+ if (lmp_le_capable(hdev)) {
+ struct hci_cp_le_set_scan_parameters le_cp;
+
+ /* Shorten BR scan params */
+ cp.num_rsp = 1;
+ cp.length /= 2;
+
+ /* Setup LE scan params */
+ memset(&le_cp, 0, sizeof(le_cp));
+ le_cp.type = 0x01; /* Active scanning */
+ /* The recommended value for scan interval and window is
+ * 11.25 msec. It is calculated by: time = n * 0.625 msec */
+ le_cp.interval = cpu_to_le16(0x0012);
+ le_cp.window = cpu_to_le16(0x0012);
+ le_cp.own_bdaddr_type = 0; /* Public address */
+ le_cp.filter = 0; /* Accept all adv packets */
+
+ hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_PARAMETERS,
+ sizeof(le_cp), &le_cp);
+ }
err = hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp);
+
if (err < 0)
mgmt_pending_remove(cmd);
+ else if (lmp_le_capable(hdev)) {
+ struct disco_interleave il, *ilp;
+
+ il.int_phase = 1;
+ il.int_count = 0;
+ il.index = index;
+ il.mode = SCAN_BR;
+ mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, index, &il,
+ sizeof(struct disco_interleave));
+ cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index);
+ if (cmd) {
+ ilp = cmd->param;
+ setup_timer(&ilp->le_timer, disco_le_to,
+ (unsigned long) ilp);
+ setup_timer(&ilp->timer, disco_to, (unsigned long) ilp);
+ mod_timer(&ilp->timer,
+ jiffies + msecs_to_jiffies(20000));
+ }
+ }
failed:
- hci_dev_unlock_bh(hdev);
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
@@ -1636,33 +2080,60 @@ failed:
static int stop_discovery(struct sock *sk, u16 index) {
+ struct hci_cp_le_set_scan_enable le_cp = {0, 0};
+ struct mgmt_mode mode_cp = {0};
+ struct disco_interleave *ilp = NULL;
struct hci_dev *hdev;
- struct pending_cmd *cmd;
- int err;
+ struct pending_cmd *cmd = NULL;
+ int err = -EPERM;
- BT_DBG("hci%u", index);
+ BT_DBG("");
hdev = hci_dev_get(index);
if (!hdev)
return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY, ENODEV);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
- cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, index, NULL, 0);
- if (!cmd) {
- err = -ENOMEM;
- goto failed;
+ if (lmp_le_capable(hdev)) {
+ cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ ilp = cmd->param;
}
- err = hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL);
- if (err < 0)
+ if (lmp_le_capable(hdev) && ilp && (ilp->mode == SCAN_LE))
+ err = hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE,
+ sizeof(le_cp), &le_cp);
+
+ if (err < 0) {
+ if (!ilp || (ilp->mode == SCAN_BR))
+ err = hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL,
+ 0, NULL);
+ }
+
+ if (ilp) {
+ ilp->mode = SCAN_IDLE;
+ del_timer_sync(&ilp->le_timer);
+ del_timer_sync(&ilp->timer);
+ }
+
+ if (err < 0 && cmd)
mgmt_pending_remove(cmd);
+ mgmt_event(MGMT_EV_DISCOVERING, index, &mode_cp, sizeof(mode_cp), NULL);
+
failed:
- hci_dev_unlock_bh(hdev);
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
- return err;
+ if (err < 0)
+ return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY, -err);
+ else
+ return err;
}
static int block_device(struct sock *sk, u16 index, unsigned char *data,
@@ -1840,6 +2311,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
goto done;
}
+ BT_DBG("got opcode %x", opcode);
switch (opcode) {
case MGMT_OP_READ_VERSION:
err = read_version(sk);
@@ -1856,6 +2328,10 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
case MGMT_OP_SET_DISCOVERABLE:
err = set_discoverable(sk, index, buf + sizeof(*hdr), len); break;
+ case MGMT_OP_SET_LIMIT_DISCOVERABLE:
+ err = set_limited_discoverable(sk, index, buf + sizeof(*hdr),
+ len);
+ break;
case MGMT_OP_SET_CONNECTABLE:
err = set_connectable(sk, index, buf + sizeof(*hdr), len); break;
@@ -1899,14 +2375,17 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
err = pair_device(sk, index, buf + sizeof(*hdr), len);
break;
case MGMT_OP_USER_CONFIRM_REPLY:
- err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len, 1);
- break;
+ case MGMT_OP_USER_PASSKEY_REPLY:
case MGMT_OP_USER_CONFIRM_NEG_REPLY:
- err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len, 0);
+ err = user_confirm_reply(sk, index, buf + sizeof(*hdr),
+ len, opcode);
break;
case MGMT_OP_SET_LOCAL_NAME:
err = set_local_name(sk, index, buf + sizeof(*hdr), len); break;
+ case MGMT_OP_RESOLVE_NAME:
+ err = resolve_name(sk, index, buf + sizeof(*hdr), len);
+ break;
case MGMT_OP_READ_LOCAL_OOB_DATA:
err = read_local_oob_data(sk, index);
break;
@@ -1959,6 +2438,7 @@ static void cmd_status_rsp(struct pending_cmd *cmd, void *data)
int mgmt_index_added(u16 index)
{
+ BT_DBG("%d", index);
return mgmt_event(MGMT_EV_INDEX_ADDED, index, NULL, 0, NULL);
}
@@ -2002,6 +2482,8 @@ int mgmt_powered(u16 index, u8 powered)
struct cmd_lookup match = { powered, NULL }; int ret;
+ BT_DBG("hci%u %d", index, powered);
+
mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, mode_rsp, &match);
if (!powered) {
@@ -2058,17 +2540,29 @@ int mgmt_connectable(u16 index, u8 connectable)
int mgmt_new_key(u16 index, struct link_key *key, u8 persistent) {
- struct mgmt_ev_new_key ev;
+ struct mgmt_ev_new_key *ev;
+ int err, total;
- memset(&ev, 0, sizeof(ev));
+ total = sizeof(struct mgmt_ev_new_key) + key->dlen;
+ ev = kzalloc(total, GFP_ATOMIC);
+ if (!ev)
+ return -ENOMEM;
+
+ ev->store_hint = persistent;
+ bacpy(&ev->key.bdaddr, &key->bdaddr);
+ ev->key.addr_type = key->addr_type;
+ ev->key.key_type = key->key_type;
+ memcpy(ev->key.val, key->val, 16);
+ ev->key.pin_len = key->pin_len;
+ ev->key.auth = key->auth;
+ ev->key.dlen = key->dlen;
+ memcpy(ev->key.data, key->data, key->dlen);
+
+ err = mgmt_event(MGMT_EV_NEW_KEY, index, ev, total, NULL);
- ev.store_hint = persistent;
- bacpy(&ev.key.bdaddr, &key->bdaddr);
- ev.key.type = key->type;
- memcpy(ev.key.val, key->val, 16);
- ev.key.pin_len = key->pin_len;
+ kfree(ev);
- return mgmt_event(MGMT_EV_NEW_KEY, index, &ev, sizeof(ev), NULL);
+ return err;
}
int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type)
@@ -2145,6 +2639,8 @@ int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr, u8 secure)
{
struct mgmt_ev_pin_code_request ev;
+ BT_DBG("hci%u", index);
+
bacpy(&ev.bdaddr, bdaddr);
ev.secure = secure;
@@ -2323,32 +2819,75 @@ int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer,
return err;
}
-int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi,
- u8 *eir)
+int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 type, u8 le,
+ u8 *dev_class, s8 rssi, u8 eir_len, u8 *eir)
{
struct mgmt_ev_device_found ev;
+ struct pending_cmd *cmd;
+ int err;
+
+ BT_DBG("le: %d", le);
memset(&ev, 0, sizeof(ev));
bacpy(&ev.bdaddr, bdaddr);
ev.rssi = rssi;
-
- if (eir)
- memcpy(ev.eir, eir, sizeof(ev.eir));
+ ev.type = type;
+ ev.le = le;
if (dev_class)
memcpy(ev.dev_class, dev_class, sizeof(ev.dev_class));
- return mgmt_event(MGMT_EV_DEVICE_FOUND, index, &ev, sizeof(ev), NULL);
+ if (eir && eir_len)
+ memcpy(ev.eir, eir, eir_len);
+
+ err = mgmt_event(MGMT_EV_DEVICE_FOUND, index, &ev, sizeof(ev), NULL);
+
+ if (err < 0)
+ return err;
+
+ cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index);
+ if (cmd) {
+ struct disco_interleave *ilp = cmd->param;
+ struct hci_dev *hdev = hci_dev_get(index);
+
+ ilp->int_count++;
+ if (hdev && ilp->int_count >= ilp->int_phase) {
+ /* Inquiry scan for General Discovery LAP */
+ struct hci_cp_inquiry cp = {{0x33, 0x8b, 0x9e}, 4, 0};
+ struct hci_cp_le_set_scan_enable le_cp = {0, 0};
+
+ ilp->int_phase *= 2;
+ ilp->int_count = 0;
+ if (ilp->mode == SCAN_LE) {
+ /* cancel LE scan */
+ hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE,
+ sizeof(le_cp), &le_cp);
+ /* start BR scan */
+ cp.num_rsp = (u8) ilp->int_phase;
+ hci_send_cmd(hdev, HCI_OP_INQUIRY,
+ sizeof(cp), &cp);
+ ilp->mode = SCAN_BR;
+ del_timer_sync(&ilp->le_timer);
+ }
+ }
+
+ if (hdev)
+ hci_dev_put(hdev);
+ }
+
+ return 0;
}
-int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name) +
+int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 status, u8 *name)
{
struct mgmt_ev_remote_name ev;
memset(&ev, 0, sizeof(ev));
bacpy(&ev.bdaddr, bdaddr);
+ ev.status = status;
memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
return mgmt_event(MGMT_EV_REMOTE_NAME, index, &ev, sizeof(ev), NULL);
net/bluetooth/smp.c +216 -117
423 lines not on this page · raw message
*skb)
{
struct smp_cmd_pairing rsp, *req = (void *) skb->data; struct smp_chan *smp;
u8 key_size;
+ u8 auth = SMP_AUTH_NONE;
int ret;
BT_DBG("conn %p", conn);
@@ -401,26 +501,35 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
memcpy(&smp->preq[1], req, sizeof(*req));
skb_pull(skb, sizeof(*req));
- if (req->oob_flag)
- return SMP_OOB_NOT_AVAIL;
+ /* We didn't start the pairing, so match remote */
+ if (req->auth_req & SMP_AUTH_BONDING)
+ auth = req->auth_req;
- /* We didn't start the pairing, so no requirements */
- build_pairing_cmd(conn, req, &rsp, SMP_AUTH_NONE);
+ build_pairing_cmd(conn, req, &rsp, auth);
key_size = min(req->max_key_size, rsp.max_key_size); if (check_enc_key_size(conn, key_size))
return SMP_ENC_KEY_SIZE;
- /* Just works */
- memset(smp->tk, 0, sizeof(smp->tk));
-
ret = smp_rand(smp->prnd);
if (ret)
return SMP_UNSPECIFIED;
+ /* Request setup of TK */
+ ret = tk_request(conn, req->oob_flag, auth, rsp.io_capability,
+ req->io_capability);
+ if (ret)
+ return SMP_UNSPECIFIED;
+
+ smp->prsp[0] = SMP_CMD_PAIRING_RSP;
+ memcpy(&smp->prsp[1], &rsp, sizeof(rsp));
+
smp->prsp[0] = SMP_CMD_PAIRING_RSP;
memcpy(&smp->prsp[1], &rsp, sizeof(rsp));
+ mod_timer(&conn->security_timer,
+ jiffies + msecs_to_jiffies(SMP_TIMEOUT));
+
smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp);
return 0;
@@ -430,8 +539,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct smp_cmd_pairing *req, *rsp = (void *) skb->data;
struct smp_chan *smp = conn->smp_chan;
- struct hci_dev *hdev = conn->hcon->hdev;
- u8 key_size;
+ u8 key_size, auth = SMP_AUTH_NONE;
int ret;
BT_DBG("conn %p", conn);
@@ -444,20 +552,33 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
if (check_enc_key_size(conn, key_size))
return SMP_ENC_KEY_SIZE;
- if (rsp->oob_flag)
- return SMP_OOB_NOT_AVAIL;
-
- /* Just works */
- memset(smp->tk, 0, sizeof(smp->tk));
+ smp->prsp[0] = SMP_CMD_PAIRING_RSP;
+ memcpy(&smp->prsp[1], rsp, sizeof(*rsp));
ret = smp_rand(smp->prnd);
if (ret)
return SMP_UNSPECIFIED;
- smp->prsp[0] = SMP_CMD_PAIRING_RSP;
- memcpy(&smp->prsp[1], rsp, sizeof(*rsp));
+ if ((req->auth_req & SMP_AUTH_BONDING) &&
+ (rsp->auth_req & SMP_AUTH_BONDING))
+ auth = SMP_AUTH_BONDING;
+
+ auth |= (req->auth_req | rsp->auth_req) & SMP_AUTH_MITM;
+
+ ret = tk_request(conn, req->oob_flag, auth, rsp->io_capability,
+ req->io_capability);
+ if (ret)
+ return SMP_UNSPECIFIED;
+
+ smp->smp_cfm_pending = TRUE;
- queue_work(hdev->workqueue, &smp->confirm);
+ /* Can't compose response until we have been confirmed */
+ if (!smp->smp_tk_valid)
+ return 0;
+
+ ret = send_pairing_confirm(conn);
+ if (ret)
+ return SMP_CONFIRM_FAILED;
return 0;
}
@@ -465,7 +586,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, structsk_buff *skb)
{
struct smp_chan *smp = conn->smp_chan;
- struct hci_dev *hdev = conn->hcon->hdev;
+ int ret;
BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
@@ -478,69 +599,154 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
swap128(smp->prnd, random);
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random),
random);
- } else {
- queue_work(hdev->workqueue, &smp->confirm);
- }
+ } else if (smp->smp_tk_valid) {
+ ret = send_pairing_confirm(conn);
+
+ if (ret)
+ return SMP_CONFIRM_FAILED;
+ } else
+ smp->smp_cfm_pending = TRUE;
+
+
+ mod_timer(&conn->security_timer,
+ jiffies + msecs_to_jiffies(SMP_TIMEOUT));
return 0;
}
static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, structsk_buff *skb)
{
+ struct hci_conn *hcon = conn->hcon;
struct smp_chan *smp = conn->smp_chan;
- struct hci_dev *hdev = conn->hcon->hdev;
+ struct crypto_blkcipher *tfm = smp->tfm;
+ int ret;
+ u8 key[16], res[16], random[16], confirm[16];
+ u8 stk[16], rand[8];
+ __le16 ediv;
- BT_DBG("conn %p", conn);
- swap128(skb->data, smp->rrnd);
- skb_pull(skb, sizeof(smp->rrnd));
+ swap128(skb->data, random);
+ skb_pull(skb, sizeof(random));
+
+ if (hcon->out)
+ ret = smp_c1(tfm, smp->tk, random, smp->preq, smp->prsp, 0,
+ conn->src, hcon->dst_type, conn->dst,
+ res);
+ else
+ ret = smp_c1(tfm, smp->tk, random, smp->preq, smp->prsp,
+ hcon->dst_type, conn->dst, 0, conn->src,
+ res);
+ if (ret)
+ return SMP_UNSPECIFIED;
+
+ BT_DBG("conn %p %s", conn, hcon->out ? "master" : "slave");
- queue_work(hdev->workqueue, &smp->random);
+ swap128(res, confirm);
+
+ if (memcmp(smp->pcnf, confirm, sizeof(smp->pcnf)) != 0) {
+ BT_ERR("Pairing failed (confirmation values mismatch)");
+ return SMP_CONFIRM_FAILED;
+ }
+
+ memset(rand, 0, sizeof(rand));
+ ediv = 0;
+
+ if (hcon->out) {
+ smp_s1(tfm, smp->tk, random, smp->prnd, key);
+ swap128(key, stk);
+
+ memset(stk + smp->smp_key_size, 0,
+ SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size);
+
+ hci_le_start_enc(hcon, ediv, rand, stk);
+ hcon->enc_key_size = smp->smp_key_size;
+ } else {
+ u8 r[16];
+
+ swap128(smp->prnd, r);
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r);
+
+ smp_s1(tfm, smp->tk, smp->prnd, random, key);
+ swap128(key, stk);
+
+ memset(stk + smp->smp_key_size, 0,
+ SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size);
+
+ hci_add_ltk(hcon->hdev, 0, conn->dst, hcon->dst_type,
+ smp->smp_key_size, smp->smp_auth, ediv, rand, stk);
+ }
return 0;
}
-static u8 smp_ltk_encrypt(struct l2cap_conn *conn)
+static int smp_encrypt_link(struct l2cap_conn *conn, struct link_key *key)
{
- struct link_key *key;
+ struct smp_chan *smp = conn->smp_chan;
struct key_master_id *master;
- struct hci_conn *hcon = conn->hcon;
+ u8 sec_level;
+ u8 zerobuf[8];
- key = hci_find_link_key_type(hcon->hdev, conn->dst,
- HCI_LK_SMP_LTK);
- if (!key)
- return 0;
+ if (!conn->hcon || !key || !key->data)
+ return -EINVAL;
- if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND,
- &hcon->pend))
- return 1;
+ memset(zerobuf, 0, sizeof(zerobuf));
master = (void *) key->data;
- hci_le_start_enc(hcon, master->ediv, master->rand,
- key->val);
- hcon->enc_key_size = key->pin_len;
- return 1;
+ if (!master->ediv && !memcmp(master->rand, zerobuf, sizeof(zerobuf)))
+ return -EINVAL;
+
+ conn->hcon->enc_key_size = key->pin_len;
+ smp->smp_sec_req = TRUE;
+ sec_level = authreq_to_seclevel(key->auth);
+
+ BT_DBG("cur %d, req: %d", conn->hcon->sec_level, sec_level);
+
+ if (sec_level > conn->hcon->sec_level)
+ conn->hcon->pending_sec_level = sec_level;
+
+ if (!(conn->hcon->link_mode & HCI_LM_ENCRYPT))
+ hci_conn_hold(conn->hcon);
+
+ hci_le_start_enc(conn->hcon, master->ediv, master->rand, key->val);
+
+ return 0;
}
+
static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff
*skb)
{
- struct smp_cmd_security_req *rp = (void *) skb->data;
- struct smp_cmd_pairing cp;
struct hci_conn *hcon = conn->hcon;
struct smp_chan *smp;
+ struct smp_cmd_security_req *rp = (void *) skb->data;
+ struct smp_cmd_pairing cp;
+ struct link_key *key;
BT_DBG("conn %p", conn);
- hcon->pending_sec_level = BT_SECURITY_MEDIUM;
-
- if (smp_ltk_encrypt(conn))
+ if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
return 0;
- if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend))
+ key = hci_find_link_key_type(hcon->hdev, conn->dst, KEY_TYPE_LTK);
+ if (key && ((key->auth & SMP_AUTH_MITM) ||
+ !(rp->auth_req & SMP_AUTH_MITM))) {
+
+ if (smp_encrypt_link(conn, key) < 0)
+ goto invalid_key;
+
return 0;
+ }
+invalid_key:
smp = smp_chan_create(conn);
+ if (!smp)
+ return SMP_CMD_NOTSUPP;
+
+ smp->smp_sec_req = FALSE;
+
+ /* Switch to Pairing Connection Parameters */
+ hci_le_conn_update(hcon, SMP_MIN_CONN_INTERVAL, SMP_MAX_CONN_INTERVAL,
+ SMP_MAX_CONN_LATENCY, SMP_SUPERVISION_TIMEOUT);
skb_pull(skb, sizeof(*rp));
@@ -552,6 +758,13 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
+ mod_timer(&conn->security_timer,
+ jiffies + msecs_to_jiffies(SMP_TIMEOUT));
+
+ set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
+
+ hci_conn_hold(hcon);
+
return 0;
}
@@ -559,6 +772,7 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
{
struct hci_conn *hcon = conn->hcon;
struct smp_chan *smp = conn->smp_chan;
+ u8 authreq;
BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level);
@@ -571,22 +785,48 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
if (hcon->sec_level >= sec_level)
return 1;
- if (hcon->link_mode & HCI_LM_MASTER)
- if (smp_ltk_encrypt(conn))
- goto done;
-
if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend))
return 0;
+ if (hcon->sec_level >= sec_level)
+ return 1;
+
+ authreq = seclevel_to_authreq(sec_level);
+
smp = smp_chan_create(conn);
+ hcon->smp_conn = conn;
+ hcon->pending_sec_level = sec_level;
+
+ if ((hcon->link_mode & HCI_LM_MASTER) && !smp->smp_sec_req) {
+ struct link_key *key;
+
+ key = hci_find_link_key_type(hcon->hdev, conn->dst,
+ KEY_TYPE_LTK);
+
+ if (smp_encrypt_link(conn, key) == 0)
+ goto done;
+ }
+
+ smp->smp_sec_req = FALSE;
+
+ if (!smp)
+ return 1;
if (hcon->link_mode & HCI_LM_MASTER) {
struct smp_cmd_pairing cp;
- build_pairing_cmd(conn, &cp, NULL, SMP_AUTH_NONE);
+ /* Switch to Pairing Connection Parameters */
+ hci_le_conn_update(hcon, SMP_MIN_CONN_INTERVAL,
+ SMP_MAX_CONN_INTERVAL, SMP_MAX_CONN_LATENCY,
+ SMP_SUPERVISION_TIMEOUT);
+
+ build_pairing_cmd(conn, &cp, NULL, authreq);
smp->preq[0] = SMP_CMD_PAIRING_REQ; memcpy(&smp->preq[1], &cp, sizeof(cp));
+ mod_timer(&conn->security_timer,
+ jiffies + msecs_to_jiffies(SMP_TIMEOUT));
+
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
} else {
struct smp_cmd_security_req cp;
@@ -595,7 +835,7 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
}
done:
- hcon->pending_sec_level = sec_level;
+ set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
return 0;
}
@@ -616,29 +856,45 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct smp_cmd_master_ident *rp = (void *) skb->data;
struct smp_chan *smp = conn->smp_chan;
+ struct smp_cmd_pairing *paircmd = (void *) &smp->prsp[1];
+ u8 *keydist;
skb_pull(skb, sizeof(*rp));
- hci_add_ltk(conn->hcon->hdev, 1, conn->src, smp->smp_key_size,
- rp->ediv, rp->rand, smp->tk);
+ hci_add_ltk(conn->hcon->hdev, 1, conn->dst, conn->hcon->dst_type,
+ smp->smp_key_size, smp->smp_auth, rp->ediv,
+ rp->rand, smp->tk);
+
+ if (conn->hcon->out)
+ keydist = &paircmd->resp_key_dist;
+ else
+ keydist = &paircmd->init_key_dist;
+
+ BT_DBG("keydist 0x%x", *keydist);
- smp_distribute_keys(conn, 1);
+ *keydist &= ~SMP_DIST_ENC_KEY;
+ if (conn->hcon->out) {
+ if (!(*keydist))
+ smp_distribute_keys(conn, 1);
+ }
return 0;
}
int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) {
+ struct hci_conn *hcon = conn->hcon;
__u8 code = skb->data[0];
__u8 reason;
int err = 0;
- if (!lmp_host_le_capable(conn->hcon->hdev)) {
+ if (!lmp_host_le_capable(hcon->hdev)) {
err = -ENOTSUPP;
reason = SMP_PAIRING_NOTSUPP;
goto done;
}
+ hcon->smp_conn = conn;
skb_pull(skb, sizeof(code));
switch (code) {
@@ -649,6 +905,11 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
case SMP_CMD_PAIRING_FAIL:
reason = 0;
err = -EPERM;
+ clear_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
+ mgmt_auth_failed(hcon->hdev->id, conn->dst, skb->data[0]);
+ del_timer(&conn->security_timer);
+ if (conn->smp_chan)
+ smp_chan_destroy(conn);
break;
case SMP_CMD_PAIRING_RSP:
@@ -691,15 +952,21 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
}
done:
- if (reason)
+ if (reason) {
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
&reason);
+ clear_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
+ mgmt_auth_failed(hcon->hdev->id, conn->dst, reason);
+ del_timer(&conn->security_timer);
+ if (conn->smp_chan)
+ smp_chan_destroy(conn);
+ }
kfree_skb(skb);
return err;
}
-int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) +static int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
{
struct smp_cmd_pairing *req, *rsp;
struct smp_chan *smp = conn->smp_chan;
@@ -740,8 +1007,9 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc);
- hci_add_ltk(conn->hcon->hdev, 1, conn->dst, smp->smp_key_size,
- ediv, ident.rand, enc.ltk);
+ hci_add_ltk(conn->hcon->hdev, 1, conn->dst,
+ conn->hcon->dst_type, smp->smp_key_size,
+ smp->smp_auth, ediv, ident.rand, enc.ltk);
ident.ediv = cpu_to_le16(ediv);
@@ -780,11 +1048,46 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
*keydist &= ~SMP_DIST_SIGN;
}
- if (conn->hcon->out || force) {
- clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend);
+ if (conn->hcon->out || rsp->resp_key_dist) {
+ if (conn->hcon->disconn_cfm_cb)
+ conn->hcon->disconn_cfm_cb(conn->hcon, 0);
+
+ clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->pend);
del_timer(&conn->security_timer);
smp_chan_destroy(conn);
}
return 0;
}
+
+int smp_link_encrypt_cmplt(struct l2cap_conn *conn, u8 status, u8 encrypt) +{
+ struct hci_conn *hcon = conn->hcon;
+ struct smp_chan *smp = conn->smp_chan;
+
+ BT_DBG("smp: %d %d %d", status, encrypt, smp->smp_sec_req);
+
+ clear_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
+
+ if (!status && encrypt && !smp->smp_sec_req)
+ smp_distribute_keys(conn, 0);
+
+ /* Fall back to Pairing request if failed a Link Security request */
+ else if (smp->smp_sec_req && (status || !encrypt))
+ smp_conn_security(conn, hcon->sec_level);
+ else
+ smp_chan_destroy(conn);
+
+ return 0;
+}
+
+void smp_timeout(struct l2cap_conn *conn)
+{
+ u8 reason = SMP_UNSPECIFIED;
+
+ BT_DBG("%p", conn);
+
+ smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason);
+ clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->pend);
+ mgmt_auth_failed(conn->hcon->hdev->id, conn->dst, SMP_UNSPECIFIED);
+}
Signature · 6 lines
1.7.7.1 -- Brian Gix [email protected] Employee of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
Hi Brian,
Quoted 12 lines · Brian Gix
Implemented missing components of MGMTOPS interface Differentiated as needed between BR/EDR pairing and LE pairing
These are a combination of changes we made to get make MGMTOPS more robust, and provide MITM pairing options for LE. Much work has been put into
merging these changes into the current master branch of Bluetooth Next, but they have not been extensively tested on that build.This is anticipated to be the first step towards upstreaming these changes,
Signed-off-by Brian Gix <[email protected]>
Quoted 15 lines
--- include/net/bluetooth/hci.h | 36 ++ include/net/bluetooth/hci_core.h | 32 ++- include/net/bluetooth/l2cap.h | 5 + include/net/bluetooth/mgmt.h | 24 ++- include/net/bluetooth/smp.h | 14 +- net/bluetooth/hci_conn.c | 58 +++- net/bluetooth/hci_core.c | 64 +++- net/bluetooth/hci_event.c | 166 ++++++--- net/bluetooth/hci_sock.c | 9 +- net/bluetooth/l2cap_core.c | 15 +- net/bluetooth/mgmt.c | 743 ++++++++++++++++++++++++++++++++------ net/bluetooth/smp.c | 661 ++++++++++++++++++++++++--------- 12 files changed, 1441 insertions(+), 386 deletions(-)
We really appreciate your contribution to upstream, but this need to be broken in more patches, otherwise its impossible to me review and accept it.
Try to split this in logical and atomic changes, upstream the easiest things first, like small l2cap or smp changes. New MGMT commands need one commit for each because they need to be discussed and so on.
The are many examples of this in the mailing list.
Gustavo
Quoted 35 lines · Gustavo Padovan
Hi Brian,
Quoted 12 lines · Brian Gix
Implemented missing components of MGMTOPS interface Differentiated as needed between BR/EDR pairing and LE pairing
These are a combination of changes we made to get make MGMTOPS more robust, and provide MITM pairing options for LE. Much work has been put into
merging these changes into the current master branch of Bluetooth Next, but they have not been extensively tested on that build.This is anticipated to be the first step towards upstreaming these changes,
Signed-off-by Brian Gix<[email protected]>Quoted 15 lines
--- include/net/bluetooth/hci.h | 36 ++ include/net/bluetooth/hci_core.h | 32 ++- include/net/bluetooth/l2cap.h | 5 + include/net/bluetooth/mgmt.h | 24 ++- include/net/bluetooth/smp.h | 14 +- net/bluetooth/hci_conn.c | 58 +++- net/bluetooth/hci_core.c | 64 +++- net/bluetooth/hci_event.c | 166 ++++++--- net/bluetooth/hci_sock.c | 9 +- net/bluetooth/l2cap_core.c | 15 +- net/bluetooth/mgmt.c | 743 ++++++++++++++++++++++++++++++++------ net/bluetooth/smp.c | 661 ++++++++++++++++++++++++--------- 12 files changed, 1441 insertions(+), 386 deletions(-)We really appreciate your contribution to upstream, but this need to be broken in more patches, otherwise its impossible to me review and accept it.
Quoted 7 lines
Try to split this in logical and atomic changes, upstream the easiest things first, like small l2cap or smp changes. New MGMT commands need one commit for each because they need to be discussed and so on.
The are many examples of this in the mailing list.
Gustavo
I understand. This was a first rough pass, which I expect to undergo a variety of changes, including but not limited to conformance to the MGMTOPS api as agreed upon in Prague, and the splitting into many more smaller patches. I did not intend for this single behemoth to be accepted in whole or in part at this point, but rather as a delivery mechanism for those who have been waiting for something that actually could be used with mainline code, which was impossible in the form we had published elsewhere. This is why I chose RFC rather than PATCH in the subject header.
Signature · 4 lines
Brian Gix [email protected] Employee of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
Hi Brian,
On Mon, Nov 07, 2011 at 02:00:16PM -0800, Brian Gix wrote:
...
Quoted 9 lines
hci_extended_inquiry_result_evt(struct
hci_dev*hdev, structstatic inline u8
hci_get_auth_req(struct hci_conn *conn){(quoted diff) +2
-0+ BT_DBG("%p", conn); + /* If remote requests dedicated bonding follow that lead */ if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03) { /* If both remote and local IO capabilities allow MITM
Review
Quoted 10 lines
(quoted diff) +4
-2* protection then require it, otherwise don't */ - if (conn->remote_cap == 0x03 || conn->io_capability == 0x03) + if (conn->remote_cap == 0x03 || conn->io_capability == 0x03) { return 0x02; - else + } else { + conn->auth_type |= 0x01; return 0x03; + } }
Maybe you could also fix those magic numbers issues. We shall define capabilities.
Quoted 30 lines
(quoted diff) +2
-2/* If remote requests no-bonding follow that lead */ - if (conn->remote_auth == 0x00 || conn->remote_auth == 0x01) - return conn->remote_auth | (conn->auth_type & 0x01); + if (conn->remote_auth <= 0x01) + return 0x00; return conn->auth_type; }@@ -2626,11 +2660,15 @@ static inline voidhci_io_capa_request_evt(struct
hci_dev*hdev, structsk_buff(quoted diff) +7
-3if (test_bit(HCI_PAIRABLE, &hdev->flags) || (conn->remote_auth & ~0x01) == HCI_AT_NO_BONDING) { struct hci_cp_io_capability_reply cp; + u8 io_cap = conn->io_capability; + /* ACL-SSP does not support IO CAP 0x04 */ + cp.capability = (io_cap == 0x04) ? 0x01 : io_cap; bacpy(&cp.bdaddr, &ev->bdaddr); - cp.capability = conn->io_capability; - conn->auth_type = hci_get_auth_req(conn); - cp.authentication = conn->auth_type; + if (conn->auth_initiator) + cp.authentication = conn->auth_type; + else + cp.authentication = hci_get_auth_req(conn); if ((conn->out == 0x01 || conn->remote_oob == 0x01) && hci_find_remote_oob_data(hdev, &conn->dst))
Review unlock: · line 2713
Quoted 1 line
(quoted diff) +0
-0@@ -2675,24 +2713,28 @@ unlock: hci_dev_unlock(hdev);
...
Best regards
Andrei Emeltchenko