From 0bd49fc75aab94a7bf3cd2f7c70e03b600635c65 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 2 Dec 2014 10:09:26 +0200 Subject: Bluetooth: Track both local and remote L2CAP fixed channel mask To pave the way for future fixed channels to be added easily we should track both the local and remote mask on a per-L2CAP connection (struct l2cap_conn) basis. So far the code has used a global variable in a racy way which anyway needs fixing. This patch renames the existing conn->fixed_chan_mask that tracked the remote mask to conn->remote_fixed_chan and adds a new variable conn->local_fixed_chan to track the local mask. Since the HS support info is now available in the local mask we can remove the conn->hs_enabled variable. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 061e648..d71dc35 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -619,8 +619,8 @@ struct l2cap_conn { unsigned int mtu; __u32 feat_mask; - __u8 fixed_chan_mask; - bool hs_enabled; + __u8 remote_fixed_chan; + __u8 local_fixed_chan; __u8 info_state; __u8 info_ident; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index c2d1489..43349ed 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -46,7 +46,6 @@ bool disable_ertm; static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD; -static u8 l2cap_fixed_chan[8] = { L2CAP_FC_SIG_BREDR | L2CAP_FC_CONNLESS, }; static LIST_HEAD(chan_list); static DEFINE_RWLOCK(chan_list_lock); @@ -1120,10 +1119,10 @@ static bool __amp_capable(struct l2cap_chan *chan) struct hci_dev *hdev; bool amp_available = false; - if (!conn->hs_enabled) + if (!(conn->local_fixed_chan & L2CAP_FC_A2MP)) return false; - if (!(conn->fixed_chan_mask & L2CAP_FC_A2MP)) + if (!(conn->remote_fixed_chan & L2CAP_FC_A2MP)) return false; read_lock(&hci_dev_list_lock); @@ -3096,12 +3095,14 @@ static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask) static inline bool __l2cap_ews_supported(struct l2cap_conn *conn) { - return conn->hs_enabled && conn->feat_mask & L2CAP_FEAT_EXT_WINDOW; + return ((conn->local_fixed_chan & L2CAP_FC_A2MP) && + (conn->feat_mask & L2CAP_FEAT_EXT_WINDOW)); } static inline bool __l2cap_efs_supported(struct l2cap_conn *conn) { - return conn->hs_enabled && conn->feat_mask & L2CAP_FEAT_EXT_FLOW; + return ((conn->local_fixed_chan & L2CAP_FC_A2MP) && + (conn->feat_mask & L2CAP_FEAT_EXT_FLOW)); } static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan, @@ -3330,7 +3331,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) break; case L2CAP_CONF_EWS: - if (!chan->conn->hs_enabled) + if (!(chan->conn->local_fixed_chan & L2CAP_FC_A2MP)) return -ECONNREFUSED; set_bit(FLAG_EXT_CTRL, &chan->flags); @@ -4334,7 +4335,7 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, if (!disable_ertm) feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING | L2CAP_FEAT_FCS; - if (conn->hs_enabled) + if (conn->local_fixed_chan & L2CAP_FC_A2MP) feat_mask |= L2CAP_FEAT_EXT_FLOW | L2CAP_FEAT_EXT_WINDOW; @@ -4345,14 +4346,10 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, u8 buf[12]; struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf; - if (conn->hs_enabled) - l2cap_fixed_chan[0] |= L2CAP_FC_A2MP; - else - l2cap_fixed_chan[0] &= ~L2CAP_FC_A2MP; - rsp->type = cpu_to_le16(L2CAP_IT_FIXED_CHAN); rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS); - memcpy(rsp->data, l2cap_fixed_chan, sizeof(l2cap_fixed_chan)); + rsp->data[0] = conn->local_fixed_chan; + memset(rsp->data + 1, 0, 7); l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf), buf); } else { @@ -4418,7 +4415,7 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, break; case L2CAP_IT_FIXED_CHAN: - conn->fixed_chan_mask = rsp->data[0]; + conn->remote_fixed_chan = rsp->data[0]; conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; conn->info_ident = 0; @@ -4442,7 +4439,7 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn, if (cmd_len != sizeof(*req)) return -EPROTO; - if (!conn->hs_enabled) + if (!(conn->local_fixed_chan & L2CAP_FC_A2MP)) return -EINVAL; psm = le16_to_cpu(req->psm); @@ -4872,7 +4869,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn, BT_DBG("icid 0x%4.4x, dest_amp_id %d", icid, req->dest_amp_id); - if (!conn->hs_enabled) + if (!(conn->local_fixed_chan & L2CAP_FC_A2MP)) return -EINVAL; chan = l2cap_get_chan_by_dcid(conn, icid); @@ -6964,9 +6961,11 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon) conn->feat_mask = 0; - if (hcon->type == ACL_LINK) - conn->hs_enabled = test_bit(HCI_HS_ENABLED, - &hcon->hdev->dev_flags); + conn->local_fixed_chan = L2CAP_FC_SIG_BREDR | L2CAP_FC_CONNLESS; + + if (hcon->type == ACL_LINK && + test_bit(HCI_HS_ENABLED, &hcon->hdev->dev_flags)) + conn->local_fixed_chan |= L2CAP_FC_A2MP; mutex_init(&conn->ident_lock); mutex_init(&conn->chan_lock); -- cgit v0.10.2 From 82c13d42bb9e54e0678cbbac16fb51cf3e2f0eff Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 3 Dec 2014 11:03:06 +0200 Subject: Bluetooth: Simplify Link Key Notification event handling logic When we get a Link Key Notification HCI event we should already have a hci_conn object. This should have been created either in the Connection Request event handler, the hci_connect_acl() function or the hci_cs_create_conn() function (if the request was not sent by the kernel). Since the only case that we'd end up not having a hci_conn in the Link Key Notification event handler would be essentially broken hardware it's safe to simply bail out from the function if this happens. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index bd0a801..d76d4f8 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3294,12 +3294,14 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (conn) { - hci_conn_hold(conn); - conn->disc_timeout = HCI_DISCONN_TIMEOUT; - hci_conn_drop(conn); - conn_set_key(conn, ev->key_type, conn->pin_length); - } + if (!conn) + goto unlock; + + hci_conn_hold(conn); + conn->disc_timeout = HCI_DISCONN_TIMEOUT; + hci_conn_drop(conn); + + conn_set_key(conn, ev->key_type, conn->pin_length); if (!test_bit(HCI_MGMT, &hdev->dev_flags)) goto unlock; @@ -3326,13 +3328,14 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) !test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags)) { list_del_rcu(&key->list); kfree_rcu(key, rcu); - } else if (conn) { - if (persistent) - clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags); - else - set_bit(HCI_CONN_FLUSH_KEY, &conn->flags); + goto unlock; } + if (persistent) + clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags); + else + set_bit(HCI_CONN_FLUSH_KEY, &conn->flags); + unlock: hci_dev_unlock(hdev); } -- cgit v0.10.2 From e65392e2ccbea210da1d8d8571de0d9821c42e7c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 8 May 2014 14:02:22 +0300 Subject: Bluetooth: Add basic SMP defines for LE Secure Connections This patch adds basic SMP defines for commands, error codes and PDU definitions for the LE Secure Connections feature. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h index f76083b..7c3c351 100644 --- a/net/bluetooth/smp.h +++ b/net/bluetooth/smp.h @@ -50,10 +50,13 @@ struct smp_cmd_pairing { #define SMP_DIST_ENC_KEY 0x01 #define SMP_DIST_ID_KEY 0x02 #define SMP_DIST_SIGN 0x04 +#define SMP_DIST_LINK_KEY 0x08 #define SMP_AUTH_NONE 0x00 #define SMP_AUTH_BONDING 0x01 #define SMP_AUTH_MITM 0x04 +#define SMP_AUTH_SC 0x08 +#define SMP_AUTH_KEYPRESS 0x10 #define SMP_CMD_PAIRING_CONFIRM 0x03 struct smp_cmd_pairing_confirm { @@ -102,7 +105,23 @@ struct smp_cmd_security_req { __u8 auth_req; } __packed; -#define SMP_CMD_MAX 0x0b +#define SMP_CMD_PUBLIC_KEY 0x0c +struct smp_cmd_public_key { + __u8 x[32]; + __u8 y[32]; +} __packed; + +#define SMP_CMD_DHKEY_CHECK 0x0d +struct smp_cmd_dhkey_check { + __u8 e[16]; +} __packed; + +#define SMP_CMD_KEYPRESS_NOTIFY 0x0e +struct smp_cmd_keypress_notify { + __u8 value; +} __packed; + +#define SMP_CMD_MAX 0x0e #define SMP_PASSKEY_ENTRY_FAILED 0x01 #define SMP_OOB_NOT_AVAIL 0x02 @@ -114,6 +133,10 @@ struct smp_cmd_security_req { #define SMP_UNSPECIFIED 0x08 #define SMP_REPEATED_ATTEMPTS 0x09 #define SMP_INVALID_PARAMS 0x0a +#define SMP_DHKEY_CHECK_FAILED 0x0b +#define SMP_NUMERIC_COMP_FAILED 0x0c +#define SMP_BREDR_PAIRING_IN_PROGRESS 0x0d +#define SMP_CROSS_TRANSP_NOT_ALLOWED 0x0e #define SMP_MIN_ENC_KEY_SIZE 7 #define SMP_MAX_ENC_KEY_SIZE 16 -- cgit v0.10.2 From 0edb14de5606e145d14ae218103030269abcc152 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 26 May 2014 13:29:28 +0300 Subject: Bluetooth: Make auth_req mask dependent on SC enabled or not If we haven't enabled SC support on our side we should use the same mask for the authentication requirement as we were using before SC support was added, otherwise we should use the extended mask for SC. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 069b76e..bfa839e 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -35,8 +35,9 @@ #define SMP_TIMEOUT msecs_to_jiffies(30000) -#define AUTH_REQ_MASK 0x07 -#define KEY_DIST_MASK 0x07 +#define AUTH_REQ_MASK(dev) (test_bit(HCI_SC_ENABLED, &(dev)->dev_flags) ? \ + 0x1f : 0x07) +#define KEY_DIST_MASK 0x07 enum { SMP_FLAG_TK_VALID, @@ -332,7 +333,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn, req->max_key_size = SMP_MAX_ENC_KEY_SIZE; req->init_key_dist = local_dist; req->resp_key_dist = remote_dist; - req->auth_req = (authreq & AUTH_REQ_MASK); + req->auth_req = (authreq & AUTH_REQ_MASK(hdev)); smp->remote_key_dist = remote_dist; return; @@ -343,7 +344,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn, rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE; rsp->init_key_dist = req->init_key_dist & remote_dist; rsp->resp_key_dist = req->resp_key_dist & local_dist; - rsp->auth_req = (authreq & AUTH_REQ_MASK); + rsp->auth_req = (authreq & AUTH_REQ_MASK(hdev)); smp->remote_key_dist = rsp->init_key_dist; } @@ -942,7 +943,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) return SMP_UNSPECIFIED; /* We didn't start the pairing, so match remote */ - auth = req->auth_req & AUTH_REQ_MASK; + auth = req->auth_req & AUTH_REQ_MASK(hdev); if (!test_bit(HCI_BONDABLE, &hdev->dev_flags) && (auth & SMP_AUTH_BONDING)) @@ -997,6 +998,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 l2cap_chan *chan = conn->smp; struct smp_chan *smp = chan->data; + struct hci_dev *hdev = conn->hcon->hdev; u8 key_size, auth; int ret; @@ -1016,7 +1018,7 @@ 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; - auth = rsp->auth_req & AUTH_REQ_MASK; + auth = rsp->auth_req & AUTH_REQ_MASK(hdev); /* If we need MITM check that it can be achieved */ if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) { @@ -1151,6 +1153,7 @@ 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 hci_dev *hdev = hcon->hdev; struct smp_chan *smp; u8 sec_level, auth; @@ -1162,7 +1165,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) if (hcon->role != HCI_ROLE_MASTER) return SMP_CMD_NOTSUPP; - auth = rp->auth_req & AUTH_REQ_MASK; + auth = rp->auth_req & AUTH_REQ_MASK(hdev); if (hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT) sec_level = BT_SECURITY_MEDIUM; -- cgit v0.10.2 From 656687769487b736674f111b92cef6ba6a31dea6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 16 May 2014 11:03:34 +0300 Subject: Bluetooth: Add SMP flag for SC and set it when necessary. This patch adds a new SMP flag for tracking whether Secure Connections is in use and sets the flag when both remote and local side have elected to use Secure Connections. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index bfa839e..3808ade 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -45,6 +45,7 @@ enum { SMP_FLAG_MITM_AUTH, SMP_FLAG_COMPLETE, SMP_FLAG_INITIATOR, + SMP_FLAG_SC, }; struct smp_chan { @@ -973,6 +974,9 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) build_pairing_cmd(conn, req, &rsp, auth); + if (rsp.auth_req & SMP_AUTH_SC) + set_bit(SMP_FLAG_SC, &smp->flags); + key_size = min(req->max_key_size, rsp.max_key_size); if (check_enc_key_size(conn, key_size)) return SMP_ENC_KEY_SIZE; @@ -1020,6 +1024,9 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) auth = rsp->auth_req & AUTH_REQ_MASK(hdev); + if ((req->auth_req & SMP_AUTH_SC) && (auth & SMP_AUTH_SC)) + set_bit(SMP_FLAG_SC, &smp->flags); + /* If we need MITM check that it can be achieved */ if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) { u8 method; -- cgit v0.10.2 From d2eb9e10f74fc1fe5e7e7abc9965ff965e1b4f54 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 16 May 2014 10:59:06 +0300 Subject: Bluetooth: Update SMP security level to/from auth_req for SC This patch updates the functions which map the SMP authentication request to a security level and vice-versa to take into account the Secure Connections feature. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 3808ade..4ecbf27 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -284,17 +284,22 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data) schedule_delayed_work(&smp->security_timer, SMP_TIMEOUT); } -static __u8 authreq_to_seclevel(__u8 authreq) +static u8 authreq_to_seclevel(u8 authreq) { - if (authreq & SMP_AUTH_MITM) - return BT_SECURITY_HIGH; - else + if (authreq & SMP_AUTH_MITM) { + if (authreq & SMP_AUTH_SC) + return BT_SECURITY_FIPS; + else + return BT_SECURITY_HIGH; + } else { return BT_SECURITY_MEDIUM; + } } static __u8 seclevel_to_authreq(__u8 sec_level) { switch (sec_level) { + case BT_SECURITY_FIPS: case BT_SECURITY_HIGH: return SMP_AUTH_MITM | SMP_AUTH_BONDING; case BT_SECURITY_MEDIUM: @@ -1026,6 +1031,8 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) if ((req->auth_req & SMP_AUTH_SC) && (auth & SMP_AUTH_SC)) set_bit(SMP_FLAG_SC, &smp->flags); + else if (conn->hcon->pending_sec_level > BT_SECURITY_HIGH) + conn->hcon->pending_sec_level = BT_SECURITY_HIGH; /* If we need MITM check that it can be achieved */ if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) { @@ -1255,6 +1262,9 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) authreq = seclevel_to_authreq(sec_level); + if (test_bit(HCI_SC_ENABLED, &hcon->hdev->dev_flags)) + authreq |= SMP_AUTH_SC; + /* Require MITM if IO Capability allows or the security level * requires it. */ -- cgit v0.10.2 From 23fb8de376181a30c35195351ec6230167098b48 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 23 May 2014 13:15:37 +0300 Subject: Bluetooth: Add mgmt support for LE Secure Connections LTK types We need a dedicated LTK type for LTK resulting from a Secure Connections based SMP pairing. This patch adds a new define for it and ensures that both the New LTK event as well as the Load LTKs command supports it. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index b391fd6..9b382ea 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -184,6 +184,9 @@ struct mgmt_cp_load_link_keys { #define MGMT_LTK_UNAUTHENTICATED 0x00 #define MGMT_LTK_AUTHENTICATED 0x01 +#define MGMT_LTK_P256_UNAUTH 0x02 +#define MGMT_LTK_P256_AUTH 0x03 +#define MGMT_LTK_P256_DEBUG 0x04 struct mgmt_ltk_info { struct mgmt_addr_info addr; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index cbeef5f..7974a39 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -4910,18 +4910,26 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, else addr_type = ADDR_LE_DEV_RANDOM; - if (key->master) - type = SMP_LTK; - else - type = SMP_LTK_SLAVE; - switch (key->type) { case MGMT_LTK_UNAUTHENTICATED: authenticated = 0x00; + type = key->master ? SMP_LTK : SMP_LTK_SLAVE; break; case MGMT_LTK_AUTHENTICATED: authenticated = 0x01; + type = key->master ? SMP_LTK : SMP_LTK_SLAVE; + break; + case MGMT_LTK_P256_UNAUTH: + authenticated = 0x00; + type = SMP_LTK_P256; + break; + case MGMT_LTK_P256_AUTH: + authenticated = 0x01; + type = SMP_LTK_P256; break; + case MGMT_LTK_P256_DEBUG: + authenticated = 0x00; + type = SMP_LTK_P256_DEBUG; default: continue; } @@ -6101,8 +6109,19 @@ void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, static u8 mgmt_ltk_type(struct smp_ltk *ltk) { - if (ltk->authenticated) - return MGMT_LTK_AUTHENTICATED; + switch (ltk->type) { + case SMP_LTK: + case SMP_LTK_SLAVE: + if (ltk->authenticated) + return MGMT_LTK_AUTHENTICATED; + return MGMT_LTK_UNAUTHENTICATED; + case SMP_LTK_P256: + if (ltk->authenticated) + return MGMT_LTK_P256_AUTH; + return MGMT_LTK_P256_UNAUTH; + case SMP_LTK_P256_DEBUG: + return MGMT_LTK_P256_DEBUG; + } return MGMT_LTK_UNAUTHENTICATED; } diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h index 7c3c351..81d24c0 100644 --- a/net/bluetooth/smp.h +++ b/net/bluetooth/smp.h @@ -146,8 +146,21 @@ enum { SMP_STK, SMP_LTK, SMP_LTK_SLAVE, + SMP_LTK_P256, + SMP_LTK_P256_DEBUG, }; +static inline bool smp_ltk_is_sc(struct smp_ltk *key) +{ + switch (key->type) { + case SMP_LTK_P256: + case SMP_LTK_P256_DEBUG: + return true; + } + + return false; +} + static inline u8 smp_ltk_sec_level(struct smp_ltk *key) { if (key->authenticated) -- cgit v0.10.2 From 8f5eeca321b4a618550a0c4c2d3da935aa7073b0 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 23 May 2014 13:36:10 +0300 Subject: Bluetooth: Set the correct security level for SC LTKs When the looked-up LTK is one generated by Secure Connections pairing the security level it gives is BT_SECURITY_FIPS. This patch updates the LTK request event handler to correctly set this level. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h index 81d24c0..f955d6b 100644 --- a/net/bluetooth/smp.h +++ b/net/bluetooth/smp.h @@ -163,8 +163,12 @@ static inline bool smp_ltk_is_sc(struct smp_ltk *key) static inline u8 smp_ltk_sec_level(struct smp_ltk *key) { - if (key->authenticated) - return BT_SECURITY_HIGH; + if (key->authenticated) { + if (smp_ltk_is_sc(key)) + return BT_SECURITY_FIPS; + else + return BT_SECURITY_HIGH; + } return BT_SECURITY_MEDIUM; } -- cgit v0.10.2 From 710f11c08e9f18a8a642024880804436c0969514 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 26 May 2014 11:21:22 +0300 Subject: Bluetooth: Use custom macro for testing BR/EDR SC enabled Since the HCI_SC_ENABLED flag will also be used for controllers without BR/EDR Secure Connections support whenever we need to check specifically for SC for BR/EDR we also need to check that the controller actually supports it. This patch adds a convenience macro for check all the necessary conditions and converts the places in the code that need it to use it. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 396c098..7f08ea3 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -999,6 +999,9 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define hdev_is_powered(hdev) (test_bit(HCI_UP, &hdev->flags) && \ !test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) +#define bredr_sc_enabled(dev) ((lmp_sc_capable(dev) || \ + test_bit(HCI_FORCE_SC, &(dev)->dbg_flags)) && \ + test_bit(HCI_SC_ENABLED, &(dev)->dev_flags)) /* ----- HCI protocols ----- */ #define HCI_PROTO_DEFER 0x01 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 5c319a4..2586e40 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1734,9 +1734,7 @@ static void hci_init4_req(struct hci_request *req, unsigned long opt) hci_req_add(req, HCI_OP_READ_SYNC_TRAIN_PARAMS, 0, NULL); /* Enable Secure Connections if supported and configured */ - if ((lmp_sc_capable(hdev) || - test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) && - test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) { + if (bredr_sc_enabled(hdev)) { u8 support = 0x01; hci_req_add(req, HCI_OP_WRITE_SC_SUPPORT, sizeof(support), &support); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index d76d4f8..f980fc4 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4027,7 +4027,7 @@ static void hci_remote_oob_data_request_evt(struct hci_dev *hdev, data = hci_find_remote_oob_data(hdev, &ev->bdaddr); if (data) { - if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) { + if (bredr_sc_enabled(hdev)) { struct hci_cp_remote_oob_ext_data_reply cp; bacpy(&cp.bdaddr, &ev->bdaddr); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 7974a39..8687695 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3562,7 +3562,7 @@ static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev, goto unlock; } - if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) + if (bredr_sc_enabled(hdev)) err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_EXT_DATA, 0, NULL); else @@ -6803,8 +6803,7 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, mgmt_status(status)); } else { - if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags) && - hash256 && rand256) { + if (bredr_sc_enabled(hdev) && hash256 && rand256) { struct mgmt_rp_read_local_oob_ext_data rp; memcpy(rp.hash192, hash192, sizeof(rp.hash192)); -- cgit v0.10.2 From a3209694f82a228c95e5e20f5e31fe63e040f33b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 26 May 2014 11:23:35 +0300 Subject: Bluetooth: Add mgmt_set_secure_conn support for any LE adapter Since LE Secure Connections is a purely host-side feature we should offer the Secure Connections mgmt setting for any adapter with LE support. This patch updates the supported settings value and the set_secure_conn command handler accordingly. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 8687695..6e81653 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -574,6 +574,7 @@ static u32 get_supported_settings(struct hci_dev *hdev) if (lmp_le_capable(hdev)) { settings |= MGMT_SETTING_LE; settings |= MGMT_SETTING_ADVERTISING; + settings |= MGMT_SETTING_SECURE_CONN; settings |= MGMT_SETTING_PRIVACY; } @@ -4572,18 +4573,13 @@ static int set_secure_conn(struct sock *sk, struct hci_dev *hdev, { struct mgmt_mode *cp = data; struct pending_cmd *cmd; - u8 val, status; + u8 val; int err; BT_DBG("request for %s", hdev->name); - status = mgmt_bredr_support(hdev); - if (status) - return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN, - status); - - if (!lmp_sc_capable(hdev) && - !test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) + if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags) && + !lmp_sc_capable(hdev) && !test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN, MGMT_STATUS_NOT_SUPPORTED); @@ -4593,7 +4589,10 @@ static int set_secure_conn(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); - if (!hdev_is_powered(hdev)) { + if (!hdev_is_powered(hdev) || + (!lmp_sc_capable(hdev) && + !test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) || + !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { bool changed; if (cp->val) { -- cgit v0.10.2 From 5378bc5622a9c221cb472ce86a5024290d6353c2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 29 May 2014 14:00:39 +0300 Subject: Bluetooth: Update LTK lookup to correctly deal with SC LTKs LTKs derived from Secure Connections based pairing are symmetric, i.e. they should match both master and slave role. This patch updates the LTK lookup functions to ignore the desired role when dealing with SC LTKs. Furthermore, with Secure Connections the EDiv and Rand values are not used and should always be set to zero. This patch updates the LTK lookup to first use the bdaddr as key and then do the necessary verifications of EDiv and Rand based on whether the found LTK is for SC or not. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 2586e40..e091e8f 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3195,11 +3195,18 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand, if (k->ediv != ediv || k->rand != rand) continue; - if (ltk_role(k->type) != role) - continue; + if (smp_ltk_is_sc(k)) { + if (k->type == SMP_LTK_P256_DEBUG && + !test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags)) + continue; + rcu_read_unlock(); + return k; + } - rcu_read_unlock(); - return k; + if (ltk_role(k->type) == role) { + rcu_read_unlock(); + return k; + } } rcu_read_unlock(); @@ -3213,9 +3220,18 @@ struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, rcu_read_lock(); list_for_each_entry_rcu(k, &hdev->long_term_keys, list) { - if (addr_type == k->bdaddr_type && - bacmp(bdaddr, &k->bdaddr) == 0 && - ltk_role(k->type) == role) { + if (addr_type != k->bdaddr_type || bacmp(bdaddr, &k->bdaddr)) + continue; + + if (smp_ltk_is_sc(k)) { + if (k->type == SMP_LTK_P256_DEBUG && + !test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags)) + continue; + rcu_read_unlock(); + return k; + } + + if (ltk_role(k->type) == role) { rcu_read_unlock(); return k; } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index f980fc4..0058b54 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4590,10 +4590,21 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb) if (conn == NULL) goto not_found; - ltk = hci_find_ltk(hdev, ev->ediv, ev->rand, conn->role); - if (ltk == NULL) + ltk = hci_find_ltk_by_addr(hdev, &conn->dst, conn->dst_type, + conn->role); + if (!ltk) goto not_found; + if (smp_ltk_is_sc(ltk)) { + /* With SC both EDiv and Rand are set to zero */ + if (ev->ediv || ev->rand) + goto not_found; + } else { + /* For non-SC keys check that EDiv and Rand match */ + if (ev->ediv != ltk->ediv || ev->rand != ltk->rand) + goto not_found; + } + memcpy(cp.ltk, ltk->val, sizeof(ltk->val)); cp.handle = cpu_to_le16(conn->handle); -- cgit v0.10.2 From 0ac3dbf9995281261abecfd2970406dd4e07955b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 29 May 2014 15:00:03 +0300 Subject: Bluetooth: Remove unused hci_find_ltk function Now that LTKs are always looked up based on bdaddr (with EDiv/Rand checks done after a successful lookup) the hci_find_ltk function is not needed anymore. This patch removes the function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 7f08ea3..b41969b 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -921,8 +921,6 @@ struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len, bool *persistent); -struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand, - u8 role); struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, u8 authenticated, u8 tk[16], u8 enc_size, __le16 ediv, __le64 rand); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index e091e8f..cb9bc8e 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3185,34 +3185,6 @@ static u8 ltk_role(u8 type) return HCI_ROLE_SLAVE; } -struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand, - u8 role) -{ - struct smp_ltk *k; - - rcu_read_lock(); - list_for_each_entry_rcu(k, &hdev->long_term_keys, list) { - if (k->ediv != ediv || k->rand != rand) - continue; - - if (smp_ltk_is_sc(k)) { - if (k->type == SMP_LTK_P256_DEBUG && - !test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags)) - continue; - rcu_read_unlock(); - return k; - } - - if (ltk_role(k->type) == role) { - rcu_read_unlock(); - return k; - } - } - rcu_read_unlock(); - - return NULL; -} - struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 role) { -- cgit v0.10.2 From f3a73d97b3b78584e111478d07dfd063453f112e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 29 May 2014 15:02:59 +0300 Subject: Bluetooth: Rename hci_find_ltk_by_addr to hci_find_ltk Now that hci_find_ltk_by_addr is the only LTK lookup function there's no need to keep the long name anymore. This patch shortens the function name to simply hci_find_ltk. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b41969b..9092964 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -924,8 +924,8 @@ struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, u8 authenticated, u8 tk[16], u8 enc_size, __le16 ediv, __le64 rand); -struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 addr_type, u8 role); +struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 addr_type, u8 role); int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type); void hci_smp_ltks_clear(struct hci_dev *hdev); int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index cb9bc8e..6c3220e 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3185,8 +3185,8 @@ static u8 ltk_role(u8 type) return HCI_ROLE_SLAVE; } -struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 addr_type, u8 role) +struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 addr_type, u8 role) { struct smp_ltk *k; @@ -3313,7 +3313,7 @@ struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, struct smp_ltk *key, *old_key; u8 role = ltk_role(type); - old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type, role); + old_key = hci_find_ltk(hdev, bdaddr, addr_type, role); if (old_key) key = old_key; else { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 0058b54..34ecbf0 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4590,8 +4590,7 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb) if (conn == NULL) goto not_found; - ltk = hci_find_ltk_by_addr(hdev, &conn->dst, conn->dst_type, - conn->role); + ltk = hci_find_ltk(hdev, &conn->dst, conn->dst_type, conn->role); if (!ltk) goto not_found; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 4ecbf27..0973e37 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1118,8 +1118,7 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level) struct smp_ltk *key; struct hci_conn *hcon = conn->hcon; - key = hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type, - hcon->role); + key = hci_find_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, hcon->role); if (!key) return false; @@ -1152,8 +1151,7 @@ bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level, */ if (key_pref == SMP_USE_LTK && test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) && - hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type, - hcon->role)) + hci_find_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, hcon->role)) return false; if (hcon->sec_level >= sec_level) -- cgit v0.10.2 From df8e1a4c7339f6447e75430e7c8172deddb489a9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 6 Jun 2014 10:39:56 +0300 Subject: Bluetooth: Set link key generation bit if necessary for LE SC Depending on whether Secure Connections is enabled or not we may need to add the link key generation bit to the key distribution. This patch does the necessary modifications to the build_pairing_cmd() function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 0973e37..d993d7d 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -333,6 +333,16 @@ static void build_pairing_cmd(struct l2cap_conn *conn, if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) local_dist |= SMP_DIST_ID_KEY; + if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) { + if ((authreq & SMP_AUTH_SC) && + test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { + local_dist |= SMP_DIST_LINK_KEY; + remote_dist |= SMP_DIST_LINK_KEY; + } + } else { + authreq &= ~SMP_AUTH_SC; + } + if (rsp == NULL) { req->io_capability = conn->hcon->io_capability; req->oob_flag = SMP_OOB_NOT_PRESENT; -- cgit v0.10.2 From 407cecf6c71e13da04f6b591bdbec76ab9a251c2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 2 May 2014 14:19:47 +0300 Subject: Bluetooth: Add basic support for AES-CMAC Most of the LE Secure Connections SMP crypto functions build on top of the AES-CMAC function. This patch adds access to AES-CMAC in the kernel crypto subsystem by allocating a crypto_hash handle for it in a similar way that we have one for AES-CBC. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index 5e97a8f..29bcafc 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -10,6 +10,7 @@ menuconfig BT select CRYPTO select CRYPTO_BLKCIPHER select CRYPTO_AES + select CRYPTO_CMAC select CRYPTO_ECB select CRYPTO_SHA256 help diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index d993d7d..4fed367 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -72,6 +72,7 @@ struct smp_chan { unsigned long flags; struct crypto_blkcipher *tfm_aes; + struct crypto_hash *tfm_cmac; }; static inline void swap_buf(const u8 *src, u8 *dst, size_t len) @@ -396,6 +397,7 @@ static void smp_chan_destroy(struct l2cap_conn *conn) kfree(smp->slave_csrk); crypto_free_blkcipher(smp->tfm_aes); + crypto_free_hash(smp->tfm_cmac); /* If pairing failed clean up any keys we might have */ if (!complete) { @@ -861,6 +863,14 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) return NULL; } + smp->tfm_cmac = crypto_alloc_hash("cmac(aes)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(smp->tfm_cmac)) { + BT_ERR("Unable to create CMAC crypto context"); + crypto_free_blkcipher(smp->tfm_aes); + kfree(smp); + return NULL; + } + smp->conn = conn; chan->data = smp; -- cgit v0.10.2 From 05ddb47a91fa591ed25ad877783a58ae44cc8212 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 29 Apr 2014 13:07:45 +0300 Subject: Bluetooth: Add ECC library for LE Secure Connections This patch adds a simple ECC library that will act as a fundamental building block for LE Secure Connections. The library has a simple API consisting of two functions: one for generating a public/private key pair and another one for generating a Diffie-Hellman key from a local private key and a remote public key. The code has been taken from https://github.com/kmackay/easy-ecc and modified to conform with the kernel coding style. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index 886e9aa..a5432a6 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -13,6 +13,6 @@ bluetooth_6lowpan-y := 6lowpan.o bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \ - a2mp.o amp.o + a2mp.o amp.o ecc.o subdir-ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/bluetooth/ecc.c b/net/bluetooth/ecc.c new file mode 100644 index 0000000..e1709f8 --- /dev/null +++ b/net/bluetooth/ecc.c @@ -0,0 +1,816 @@ +/* + * Copyright (c) 2013, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "ecc.h" + +/* 256-bit curve */ +#define ECC_BYTES 32 + +#define MAX_TRIES 16 + +/* Number of u64's needed */ +#define NUM_ECC_DIGITS (ECC_BYTES / 8) + +struct ecc_point { + u64 x[NUM_ECC_DIGITS]; + u64 y[NUM_ECC_DIGITS]; +}; + +typedef struct { + u64 m_low; + u64 m_high; +} uint128_t; + +#define CURVE_P_32 { 0xFFFFFFFFFFFFFFFFull, 0x00000000FFFFFFFFull, \ + 0x0000000000000000ull, 0xFFFFFFFF00000001ull } + +#define CURVE_G_32 { \ + { 0xF4A13945D898C296ull, 0x77037D812DEB33A0ull, \ + 0xF8BCE6E563A440F2ull, 0x6B17D1F2E12C4247ull }, \ + { 0xCBB6406837BF51F5ull, 0x2BCE33576B315ECEull, \ + 0x8EE7EB4A7C0F9E16ull, 0x4FE342E2FE1A7F9Bull } \ +} + +#define CURVE_N_32 { 0xF3B9CAC2FC632551ull, 0xBCE6FAADA7179E84ull, \ + 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFF00000000ull } + +static u64 curve_p[NUM_ECC_DIGITS] = CURVE_P_32; +static struct ecc_point curve_g = CURVE_G_32; +static u64 curve_n[NUM_ECC_DIGITS] = CURVE_N_32; + +static void vli_clear(u64 *vli) +{ + int i; + + for (i = 0; i < NUM_ECC_DIGITS; i++) + vli[i] = 0; +} + +/* Returns true if vli == 0, false otherwise. */ +static bool vli_is_zero(const u64 *vli) +{ + int i; + + for (i = 0; i < NUM_ECC_DIGITS; i++) { + if (vli[i]) + return false; + } + + return true; +} + +/* Returns nonzero if bit bit of vli is set. */ +static u64 vli_test_bit(const u64 *vli, unsigned int bit) +{ + return (vli[bit / 64] & ((u64) 1 << (bit % 64))); +} + +/* Counts the number of 64-bit "digits" in vli. */ +static unsigned int vli_num_digits(const u64 *vli) +{ + int i; + + /* Search from the end until we find a non-zero digit. + * We do it in reverse because we expect that most digits will + * be nonzero. + */ + for (i = NUM_ECC_DIGITS - 1; i >= 0 && vli[i] == 0; i--); + + return (i + 1); +} + +/* Counts the number of bits required for vli. */ +static unsigned int vli_num_bits(const u64 *vli) +{ + unsigned int i, num_digits; + u64 digit; + + num_digits = vli_num_digits(vli); + if (num_digits == 0) + return 0; + + digit = vli[num_digits - 1]; + for (i = 0; digit; i++) + digit >>= 1; + + return ((num_digits - 1) * 64 + i); +} + +/* Sets dest = src. */ +static void vli_set(u64 *dest, const u64 *src) +{ + int i; + + for (i = 0; i < NUM_ECC_DIGITS; i++) + dest[i] = src[i]; +} + +/* Returns sign of left - right. */ +static int vli_cmp(const u64 *left, const u64 *right) +{ + int i; + + for (i = NUM_ECC_DIGITS - 1; i >= 0; i--) { + if (left[i] > right[i]) + return 1; + else if (left[i] < right[i]) + return -1; + } + + return 0; +} + +/* Computes result = in << c, returning carry. Can modify in place + * (if result == in). 0 < shift < 64. + */ +static u64 vli_lshift(u64 *result, const u64 *in, + unsigned int shift) +{ + u64 carry = 0; + int i; + + for (i = 0; i < NUM_ECC_DIGITS; i++) { + u64 temp = in[i]; + + result[i] = (temp << shift) | carry; + carry = temp >> (64 - shift); + } + + return carry; +} + +/* Computes vli = vli >> 1. */ +static void vli_rshift1(u64 *vli) +{ + u64 *end = vli; + u64 carry = 0; + + vli += NUM_ECC_DIGITS; + + while (vli-- > end) { + u64 temp = *vli; + *vli = (temp >> 1) | carry; + carry = temp << 63; + } +} + +/* Computes result = left + right, returning carry. Can modify in place. */ +static u64 vli_add(u64 *result, const u64 *left, + const u64 *right) +{ + u64 carry = 0; + int i; + + for (i = 0; i < NUM_ECC_DIGITS; i++) { + u64 sum; + + sum = left[i] + right[i] + carry; + if (sum != left[i]) + carry = (sum < left[i]); + + result[i] = sum; + } + + return carry; +} + +/* Computes result = left - right, returning borrow. Can modify in place. */ +static u64 vli_sub(u64 *result, const u64 *left, const u64 *right) +{ + u64 borrow = 0; + int i; + + for (i = 0; i < NUM_ECC_DIGITS; i++) { + u64 diff; + + diff = left[i] - right[i] - borrow; + if (diff != left[i]) + borrow = (diff > left[i]); + + result[i] = diff; + } + + return borrow; +} + +static uint128_t mul_64_64(u64 left, u64 right) +{ + u64 a0 = left & 0xffffffffull; + u64 a1 = left >> 32; + u64 b0 = right & 0xffffffffull; + u64 b1 = right >> 32; + u64 m0 = a0 * b0; + u64 m1 = a0 * b1; + u64 m2 = a1 * b0; + u64 m3 = a1 * b1; + uint128_t result; + + m2 += (m0 >> 32); + m2 += m1; + + /* Overflow */ + if (m2 < m1) + m3 += 0x100000000ull; + + result.m_low = (m0 & 0xffffffffull) | (m2 << 32); + result.m_high = m3 + (m2 >> 32); + + return result; +} + +static uint128_t add_128_128(uint128_t a, uint128_t b) +{ + uint128_t result; + + result.m_low = a.m_low + b.m_low; + result.m_high = a.m_high + b.m_high + (result.m_low < a.m_low); + + return result; +} + +static void vli_mult(u64 *result, const u64 *left, const u64 *right) +{ + uint128_t r01 = { 0, 0 }; + u64 r2 = 0; + unsigned int i, k; + + /* Compute each digit of result in sequence, maintaining the + * carries. + */ + for (k = 0; k < NUM_ECC_DIGITS * 2 - 1; k++) { + unsigned int min; + + if (k < NUM_ECC_DIGITS) + min = 0; + else + min = (k + 1) - NUM_ECC_DIGITS; + + for (i = min; i <= k && i < NUM_ECC_DIGITS; i++) { + uint128_t product; + + product = mul_64_64(left[i], right[k - i]); + + r01 = add_128_128(r01, product); + r2 += (r01.m_high < product.m_high); + } + + result[k] = r01.m_low; + r01.m_low = r01.m_high; + r01.m_high = r2; + r2 = 0; + } + + result[NUM_ECC_DIGITS * 2 - 1] = r01.m_low; +} + +static void vli_square(u64 *result, const u64 *left) +{ + uint128_t r01 = { 0, 0 }; + u64 r2 = 0; + int i, k; + + for (k = 0; k < NUM_ECC_DIGITS * 2 - 1; k++) { + unsigned int min; + + if (k < NUM_ECC_DIGITS) + min = 0; + else + min = (k + 1) - NUM_ECC_DIGITS; + + for (i = min; i <= k && i <= k - i; i++) { + uint128_t product; + + product = mul_64_64(left[i], left[k - i]); + + if (i < k - i) { + r2 += product.m_high >> 63; + product.m_high = (product.m_high << 1) | + (product.m_low >> 63); + product.m_low <<= 1; + } + + r01 = add_128_128(r01, product); + r2 += (r01.m_high < product.m_high); + } + + result[k] = r01.m_low; + r01.m_low = r01.m_high; + r01.m_high = r2; + r2 = 0; + } + + result[NUM_ECC_DIGITS * 2 - 1] = r01.m_low; +} + +/* Computes result = (left + right) % mod. + * Assumes that left < mod and right < mod, result != mod. + */ +static void vli_mod_add(u64 *result, const u64 *left, const u64 *right, + const u64 *mod) +{ + u64 carry; + + carry = vli_add(result, left, right); + + /* result > mod (result = mod + remainder), so subtract mod to + * get remainder. + */ + if (carry || vli_cmp(result, mod) >= 0) + vli_sub(result, result, mod); +} + +/* Computes result = (left - right) % mod. + * Assumes that left < mod and right < mod, result != mod. + */ +static void vli_mod_sub(u64 *result, const u64 *left, const u64 *right, + const u64 *mod) +{ + u64 borrow = vli_sub(result, left, right); + + /* In this case, p_result == -diff == (max int) - diff. + * Since -x % d == d - x, we can get the correct result from + * result + mod (with overflow). + */ + if (borrow) + vli_add(result, result, mod); +} + +/* Computes result = product % curve_p + from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +static void vli_mmod_fast(u64 *result, const u64 *product) +{ + u64 tmp[NUM_ECC_DIGITS]; + int carry; + + /* t */ + vli_set(result, product); + + /* s1 */ + tmp[0] = 0; + tmp[1] = product[5] & 0xffffffff00000000ull; + tmp[2] = product[6]; + tmp[3] = product[7]; + carry = vli_lshift(tmp, tmp, 1); + carry += vli_add(result, result, tmp); + + /* s2 */ + tmp[1] = product[6] << 32; + tmp[2] = (product[6] >> 32) | (product[7] << 32); + tmp[3] = product[7] >> 32; + carry += vli_lshift(tmp, tmp, 1); + carry += vli_add(result, result, tmp); + + /* s3 */ + tmp[0] = product[4]; + tmp[1] = product[5] & 0xffffffff; + tmp[2] = 0; + tmp[3] = product[7]; + carry += vli_add(result, result, tmp); + + /* s4 */ + tmp[0] = (product[4] >> 32) | (product[5] << 32); + tmp[1] = (product[5] >> 32) | (product[6] & 0xffffffff00000000ull); + tmp[2] = product[7]; + tmp[3] = (product[6] >> 32) | (product[4] << 32); + carry += vli_add(result, result, tmp); + + /* d1 */ + tmp[0] = (product[5] >> 32) | (product[6] << 32); + tmp[1] = (product[6] >> 32); + tmp[2] = 0; + tmp[3] = (product[4] & 0xffffffff) | (product[5] << 32); + carry -= vli_sub(result, result, tmp); + + /* d2 */ + tmp[0] = product[6]; + tmp[1] = product[7]; + tmp[2] = 0; + tmp[3] = (product[4] >> 32) | (product[5] & 0xffffffff00000000ull); + carry -= vli_sub(result, result, tmp); + + /* d3 */ + tmp[0] = (product[6] >> 32) | (product[7] << 32); + tmp[1] = (product[7] >> 32) | (product[4] << 32); + tmp[2] = (product[4] >> 32) | (product[5] << 32); + tmp[3] = (product[6] << 32); + carry -= vli_sub(result, result, tmp); + + /* d4 */ + tmp[0] = product[7]; + tmp[1] = product[4] & 0xffffffff00000000ull; + tmp[2] = product[5]; + tmp[3] = product[6] & 0xffffffff00000000ull; + carry -= vli_sub(result, result, tmp); + + if (carry < 0) { + do { + carry += vli_add(result, result, curve_p); + } while (carry < 0); + } else { + while (carry || vli_cmp(curve_p, result) != 1) + carry -= vli_sub(result, result, curve_p); + } +} + +/* Computes result = (left * right) % curve_p. */ +static void vli_mod_mult_fast(u64 *result, const u64 *left, const u64 *right) +{ + u64 product[2 * NUM_ECC_DIGITS]; + + vli_mult(product, left, right); + vli_mmod_fast(result, product); +} + +/* Computes result = left^2 % curve_p. */ +static void vli_mod_square_fast(u64 *result, const u64 *left) +{ + u64 product[2 * NUM_ECC_DIGITS]; + + vli_square(product, left); + vli_mmod_fast(result, product); +} + +#define EVEN(vli) (!(vli[0] & 1)) +/* Computes result = (1 / p_input) % mod. All VLIs are the same size. + * See "From Euclid's GCD to Montgomery Multiplication to the Great Divide" + * https://labs.oracle.com/techrep/2001/smli_tr-2001-95.pdf + */ +static void vli_mod_inv(u64 *result, const u64 *input, const u64 *mod) +{ + u64 a[NUM_ECC_DIGITS], b[NUM_ECC_DIGITS]; + u64 u[NUM_ECC_DIGITS], v[NUM_ECC_DIGITS]; + u64 carry; + int cmp_result; + + if (vli_is_zero(input)) { + vli_clear(result); + return; + } + + vli_set(a, input); + vli_set(b, mod); + vli_clear(u); + u[0] = 1; + vli_clear(v); + + while ((cmp_result = vli_cmp(a, b)) != 0) { + carry = 0; + + if (EVEN(a)) { + vli_rshift1(a); + + if (!EVEN(u)) + carry = vli_add(u, u, mod); + + vli_rshift1(u); + if (carry) + u[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull; + } else if (EVEN(b)) { + vli_rshift1(b); + + if (!EVEN(v)) + carry = vli_add(v, v, mod); + + vli_rshift1(v); + if (carry) + v[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull; + } else if (cmp_result > 0) { + vli_sub(a, a, b); + vli_rshift1(a); + + if (vli_cmp(u, v) < 0) + vli_add(u, u, mod); + + vli_sub(u, u, v); + if (!EVEN(u)) + carry = vli_add(u, u, mod); + + vli_rshift1(u); + if (carry) + u[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull; + } else { + vli_sub(b, b, a); + vli_rshift1(b); + + if (vli_cmp(v, u) < 0) + vli_add(v, v, mod); + + vli_sub(v, v, u); + if (!EVEN(v)) + carry = vli_add(v, v, mod); + + vli_rshift1(v); + if (carry) + v[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull; + } + } + + vli_set(result, u); +} + +/* ------ Point operations ------ */ + +/* Returns true if p_point is the point at infinity, false otherwise. */ +static bool ecc_point_is_zero(const struct ecc_point *point) +{ + return (vli_is_zero(point->x) && vli_is_zero(point->y)); +} + +/* Point multiplication algorithm using Montgomery's ladder with co-Z + * coordinates. From http://eprint.iacr.org/2011/338.pdf + */ + +/* Double in place */ +static void ecc_point_double_jacobian(u64 *x1, u64 *y1, u64 *z1) +{ + /* t1 = x, t2 = y, t3 = z */ + u64 t4[NUM_ECC_DIGITS]; + u64 t5[NUM_ECC_DIGITS]; + + if (vli_is_zero(z1)) + return; + + vli_mod_square_fast(t4, y1); /* t4 = y1^2 */ + vli_mod_mult_fast(t5, x1, t4); /* t5 = x1*y1^2 = A */ + vli_mod_square_fast(t4, t4); /* t4 = y1^4 */ + vli_mod_mult_fast(y1, y1, z1); /* t2 = y1*z1 = z3 */ + vli_mod_square_fast(z1, z1); /* t3 = z1^2 */ + + vli_mod_add(x1, x1, z1, curve_p); /* t1 = x1 + z1^2 */ + vli_mod_add(z1, z1, z1, curve_p); /* t3 = 2*z1^2 */ + vli_mod_sub(z1, x1, z1, curve_p); /* t3 = x1 - z1^2 */ + vli_mod_mult_fast(x1, x1, z1); /* t1 = x1^2 - z1^4 */ + + vli_mod_add(z1, x1, x1, curve_p); /* t3 = 2*(x1^2 - z1^4) */ + vli_mod_add(x1, x1, z1, curve_p); /* t1 = 3*(x1^2 - z1^4) */ + if (vli_test_bit(x1, 0)) { + u64 carry = vli_add(x1, x1, curve_p); + vli_rshift1(x1); + x1[NUM_ECC_DIGITS - 1] |= carry << 63; + } else { + vli_rshift1(x1); + } + /* t1 = 3/2*(x1^2 - z1^4) = B */ + + vli_mod_square_fast(z1, x1); /* t3 = B^2 */ + vli_mod_sub(z1, z1, t5, curve_p); /* t3 = B^2 - A */ + vli_mod_sub(z1, z1, t5, curve_p); /* t3 = B^2 - 2A = x3 */ + vli_mod_sub(t5, t5, z1, curve_p); /* t5 = A - x3 */ + vli_mod_mult_fast(x1, x1, t5); /* t1 = B * (A - x3) */ + vli_mod_sub(t4, x1, t4, curve_p); /* t4 = B * (A - x3) - y1^4 = y3 */ + + vli_set(x1, z1); + vli_set(z1, y1); + vli_set(y1, t4); +} + +/* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */ +static void apply_z(u64 *x1, u64 *y1, u64 *z) +{ + u64 t1[NUM_ECC_DIGITS]; + + vli_mod_square_fast(t1, z); /* z^2 */ + vli_mod_mult_fast(x1, x1, t1); /* x1 * z^2 */ + vli_mod_mult_fast(t1, t1, z); /* z^3 */ + vli_mod_mult_fast(y1, y1, t1); /* y1 * z^3 */ +} + +/* P = (x1, y1) => 2P, (x2, y2) => P' */ +static void xycz_initial_double(u64 *x1, u64 *y1, u64 *x2, u64 *y2, + u64 *p_initial_z) +{ + u64 z[NUM_ECC_DIGITS]; + + vli_set(x2, x1); + vli_set(y2, y1); + + vli_clear(z); + z[0] = 1; + + if (p_initial_z) + vli_set(z, p_initial_z); + + apply_z(x1, y1, z); + + ecc_point_double_jacobian(x1, y1, z); + + apply_z(x2, y2, z); +} + +/* Input P = (x1, y1, Z), Q = (x2, y2, Z) + * Output P' = (x1', y1', Z3), P + Q = (x3, y3, Z3) + * or P => P', Q => P + Q + */ +static void xycz_add(u64 *x1, u64 *y1, u64 *x2, u64 *y2) +{ + /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ + u64 t5[NUM_ECC_DIGITS]; + + vli_mod_sub(t5, x2, x1, curve_p); /* t5 = x2 - x1 */ + vli_mod_square_fast(t5, t5); /* t5 = (x2 - x1)^2 = A */ + vli_mod_mult_fast(x1, x1, t5); /* t1 = x1*A = B */ + vli_mod_mult_fast(x2, x2, t5); /* t3 = x2*A = C */ + vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y2 - y1 */ + vli_mod_square_fast(t5, y2); /* t5 = (y2 - y1)^2 = D */ + + vli_mod_sub(t5, t5, x1, curve_p); /* t5 = D - B */ + vli_mod_sub(t5, t5, x2, curve_p); /* t5 = D - B - C = x3 */ + vli_mod_sub(x2, x2, x1, curve_p); /* t3 = C - B */ + vli_mod_mult_fast(y1, y1, x2); /* t2 = y1*(C - B) */ + vli_mod_sub(x2, x1, t5, curve_p); /* t3 = B - x3 */ + vli_mod_mult_fast(y2, y2, x2); /* t4 = (y2 - y1)*(B - x3) */ + vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y3 */ + + vli_set(x2, t5); +} + +/* Input P = (x1, y1, Z), Q = (x2, y2, Z) + * Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3) + * or P => P - Q, Q => P + Q + */ +static void xycz_add_c(u64 *x1, u64 *y1, u64 *x2, u64 *y2) +{ + /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ + u64 t5[NUM_ECC_DIGITS]; + u64 t6[NUM_ECC_DIGITS]; + u64 t7[NUM_ECC_DIGITS]; + + vli_mod_sub(t5, x2, x1, curve_p); /* t5 = x2 - x1 */ + vli_mod_square_fast(t5, t5); /* t5 = (x2 - x1)^2 = A */ + vli_mod_mult_fast(x1, x1, t5); /* t1 = x1*A = B */ + vli_mod_mult_fast(x2, x2, t5); /* t3 = x2*A = C */ + vli_mod_add(t5, y2, y1, curve_p); /* t4 = y2 + y1 */ + vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y2 - y1 */ + + vli_mod_sub(t6, x2, x1, curve_p); /* t6 = C - B */ + vli_mod_mult_fast(y1, y1, t6); /* t2 = y1 * (C - B) */ + vli_mod_add(t6, x1, x2, curve_p); /* t6 = B + C */ + vli_mod_square_fast(x2, y2); /* t3 = (y2 - y1)^2 */ + vli_mod_sub(x2, x2, t6, curve_p); /* t3 = x3 */ + + vli_mod_sub(t7, x1, x2, curve_p); /* t7 = B - x3 */ + vli_mod_mult_fast(y2, y2, t7); /* t4 = (y2 - y1)*(B - x3) */ + vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y3 */ + + vli_mod_square_fast(t7, t5); /* t7 = (y2 + y1)^2 = F */ + vli_mod_sub(t7, t7, t6, curve_p); /* t7 = x3' */ + vli_mod_sub(t6, t7, x1, curve_p); /* t6 = x3' - B */ + vli_mod_mult_fast(t6, t6, t5); /* t6 = (y2 + y1)*(x3' - B) */ + vli_mod_sub(y1, t6, y1, curve_p); /* t2 = y3' */ + + vli_set(x1, t7); +} + +static void ecc_point_mult(struct ecc_point *result, + const struct ecc_point *point, u64 *scalar, + u64 *initial_z, int num_bits) +{ + /* R0 and R1 */ + u64 rx[2][NUM_ECC_DIGITS]; + u64 ry[2][NUM_ECC_DIGITS]; + u64 z[NUM_ECC_DIGITS]; + int i, nb; + + vli_set(rx[1], point->x); + vli_set(ry[1], point->y); + + xycz_initial_double(rx[1], ry[1], rx[0], ry[0], initial_z); + + for (i = num_bits - 2; i > 0; i--) { + nb = !vli_test_bit(scalar, i); + xycz_add_c(rx[1 - nb], ry[1 - nb], rx[nb], ry[nb]); + xycz_add(rx[nb], ry[nb], rx[1 - nb], ry[1 - nb]); + } + + nb = !vli_test_bit(scalar, 0); + xycz_add_c(rx[1 - nb], ry[1 - nb], rx[nb], ry[nb]); + + /* Find final 1/Z value. */ + vli_mod_sub(z, rx[1], rx[0], curve_p); /* X1 - X0 */ + vli_mod_mult_fast(z, z, ry[1 - nb]); /* Yb * (X1 - X0) */ + vli_mod_mult_fast(z, z, point->x); /* xP * Yb * (X1 - X0) */ + vli_mod_inv(z, z, curve_p); /* 1 / (xP * Yb * (X1 - X0)) */ + vli_mod_mult_fast(z, z, point->y); /* yP / (xP * Yb * (X1 - X0)) */ + vli_mod_mult_fast(z, z, rx[1 - nb]); /* Xb * yP / (xP * Yb * (X1 - X0)) */ + /* End 1/Z calculation */ + + xycz_add(rx[nb], ry[nb], rx[1 - nb], ry[1 - nb]); + + apply_z(rx[0], ry[0], z); + + vli_set(result->x, rx[0]); + vli_set(result->y, ry[0]); +} + +static void ecc_bytes2native(const u8 bytes[ECC_BYTES], + u64 native[NUM_ECC_DIGITS]) +{ + int i; + + for (i = 0; i < NUM_ECC_DIGITS; i++) { + const u8 *digit = bytes + 8 * (NUM_ECC_DIGITS - 1 - i); + + native[NUM_ECC_DIGITS - 1 - i] = + ((u64) digit[0] << 0) | + ((u64) digit[1] << 8) | + ((u64) digit[2] << 16) | + ((u64) digit[3] << 24) | + ((u64) digit[4] << 32) | + ((u64) digit[5] << 40) | + ((u64) digit[6] << 48) | + ((u64) digit[7] << 56); + } +} + +static void ecc_native2bytes(const u64 native[NUM_ECC_DIGITS], + u8 bytes[ECC_BYTES]) +{ + int i; + + for (i = 0; i < NUM_ECC_DIGITS; i++) { + u8 *digit = bytes + 8 * (NUM_ECC_DIGITS - 1 - i); + + digit[0] = native[NUM_ECC_DIGITS - 1 - i] >> 0; + digit[1] = native[NUM_ECC_DIGITS - 1 - i] >> 8; + digit[2] = native[NUM_ECC_DIGITS - 1 - i] >> 16; + digit[3] = native[NUM_ECC_DIGITS - 1 - i] >> 24; + digit[4] = native[NUM_ECC_DIGITS - 1 - i] >> 32; + digit[5] = native[NUM_ECC_DIGITS - 1 - i] >> 40; + digit[6] = native[NUM_ECC_DIGITS - 1 - i] >> 48; + digit[7] = native[NUM_ECC_DIGITS - 1 - i] >> 56; + } +} + +bool ecc_make_key(u8 public_key[64], u8 private_key[32]) +{ + struct ecc_point pk; + u64 priv[NUM_ECC_DIGITS]; + unsigned int tries = 0; + + do { + if (tries++ >= MAX_TRIES) + return false; + + get_random_bytes(priv, ECC_BYTES); + + if (vli_is_zero(priv)) + continue; + + /* Make sure the private key is in the range [1, n-1]. */ + if (vli_cmp(curve_n, priv) != 1) + continue; + + ecc_point_mult(&pk, &curve_g, priv, NULL, vli_num_bits(priv)); + } while (ecc_point_is_zero(&pk)); + + ecc_native2bytes(priv, private_key); + ecc_native2bytes(pk.x, public_key); + ecc_native2bytes(pk.y, &public_key[32]); + + return true; +} + +bool ecdh_shared_secret(const u8 public_key[64], const u8 private_key[32], + u8 secret[32]) +{ + u64 priv[NUM_ECC_DIGITS]; + u64 rand[NUM_ECC_DIGITS]; + struct ecc_point product, pk; + + get_random_bytes(rand, ECC_BYTES); + + ecc_bytes2native(public_key, pk.x); + ecc_bytes2native(&public_key[32], pk.y); + ecc_bytes2native(private_key, priv); + + ecc_point_mult(&product, &pk, priv, rand, vli_num_bits(priv)); + + ecc_native2bytes(product.x, secret); + + return !ecc_point_is_zero(&product); +} diff --git a/net/bluetooth/ecc.h b/net/bluetooth/ecc.h new file mode 100644 index 0000000..8d6a2f4 --- /dev/null +++ b/net/bluetooth/ecc.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2013, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Create a public/private key pair. + * Outputs: + * public_key - Will be filled in with the public key. + * private_key - Will be filled in with the private key. + * + * Returns true if the key pair was generated successfully, false + * if an error occurred. The keys are with the LSB first. + */ +bool ecc_make_key(u8 public_key[64], u8 private_key[32]); + +/* Compute a shared secret given your secret key and someone else's + * public key. + * Note: It is recommended that you hash the result of ecdh_shared_secret + * before using it for symmetric encryption or HMAC. + * + * Inputs: + * public_key - The public key of the remote party + * private_key - Your private key. + * + * Outputs: + * secret - Will be filled in with the shared secret value. + * + * Returns true if the shared secret was generated successfully, false + * if an error occurred. Both input and output parameters are with the + * LSB first. + */ +bool ecdh_shared_secret(const u8 public_key[64], const u8 private_key[32], + u8 secret[32]); -- cgit v0.10.2 From 3b19146d23884172fe9d05f90924d4da3cdeb468 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 6 Jun 2014 10:50:15 +0300 Subject: Bluetooth: Add basic support for sending our LE SC public key When the initial pairing request & response PDUs have been exchanged and both have had the LE SC bit set the next step is to generate a ECDH key pair and to send the public key to the remote side. This patch adds basic support for generating the key pair and sending the public key using the new Public Key SMP PDU. It is the initiating device that sends the public key first and the non-initiating device responds by sending its public key respectively (in a subsequent patch). Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 4fed367..9317583 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -29,10 +29,14 @@ #include #include +#include "ecc.h" #include "smp.h" #define SMP_ALLOW_CMD(smp, code) set_bit(code, &smp->allow_cmd) +/* Keys which are not distributed with Secure Connections */ +#define SMP_SC_NO_DIST (SMP_DIST_ENC_KEY | SMP_DIST_LINK_KEY); + #define SMP_TIMEOUT msecs_to_jiffies(30000) #define AUTH_REQ_MASK(dev) (test_bit(HCI_SC_ENABLED, &(dev)->dev_flags) ? \ @@ -71,6 +75,10 @@ struct smp_chan { struct smp_irk *remote_irk; unsigned long flags; + /* Secure Connections variables */ + u8 local_pk[64]; + u8 local_sk[32]; + struct crypto_blkcipher *tfm_aes; struct crypto_hash *tfm_cmac; }; @@ -1012,7 +1020,18 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) memcpy(&smp->prsp[1], &rsp, sizeof(rsp)); smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp); - SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM); + + clear_bit(SMP_FLAG_INITIATOR, &smp->flags); + + if (test_bit(SMP_FLAG_SC, &smp->flags)) { + SMP_ALLOW_CMD(smp, SMP_CMD_PUBLIC_KEY); + /* Clear bits which are generated but not distributed */ + smp->remote_key_dist &= ~SMP_SC_NO_DIST; + /* Wait for Public Key from Initiating Device */ + return 0; + } else { + SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM); + } /* Request setup of TK */ ret = tk_request(conn, 0, auth, rsp.io_capability, req->io_capability); @@ -1022,6 +1041,23 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) return 0; } +static u8 sc_send_public_key(struct smp_chan *smp) +{ + BT_DBG(""); + + /* Generate local key pair for Secure Connections */ + if (!ecc_make_key(smp->local_pk, smp->local_sk)) + return SMP_UNSPECIFIED; + + BT_DBG("Local Public Key X: %32phN", smp->local_pk); + BT_DBG("Local Public Key Y: %32phN", &smp->local_pk[32]); + BT_DBG("Local Private Key: %32phN", smp->local_sk); + + smp_send_cmd(smp->conn, SMP_CMD_PUBLIC_KEY, 64, smp->local_pk); + + return 0; +} + static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing *req, *rsp = (void *) skb->data; @@ -1074,6 +1110,13 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) */ smp->remote_key_dist &= rsp->resp_key_dist; + if (test_bit(SMP_FLAG_SC, &smp->flags)) { + /* Clear bits which are generated but not distributed */ + smp->remote_key_dist &= ~SMP_SC_NO_DIST; + SMP_ALLOW_CMD(smp, SMP_CMD_PUBLIC_KEY); + return sc_send_public_key(smp); + } + auth |= req->auth_req; ret = tk_request(conn, 0, auth, req->io_capability, rsp->io_capability); -- cgit v0.10.2 From d8f8edbe93979bdf1f74cd768e68c7705806f0b9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 6 Jun 2014 11:09:28 +0300 Subject: Bluetooth: Add handler function for receiving LE SC public key This patch adds a handler function for the LE SC SMP Public Key PDU. When we receive the key we proceed with generating the shared DHKey value from the remote public key and local private key. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 9317583..1cc438c 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -50,6 +50,7 @@ enum { SMP_FLAG_COMPLETE, SMP_FLAG_INITIATOR, SMP_FLAG_SC, + SMP_FLAG_REMOTE_PK, }; struct smp_chan { @@ -78,6 +79,8 @@ struct smp_chan { /* Secure Connections variables */ u8 local_pk[64]; u8 local_sk[32]; + u8 remote_pk[64]; + u8 dhkey[32]; struct crypto_blkcipher *tfm_aes; struct crypto_hash *tfm_cmac; @@ -1513,6 +1516,43 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb) return 0; } +static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb) +{ + struct smp_cmd_public_key *key = (void *) skb->data; + struct hci_conn *hcon = conn->hcon; + struct l2cap_chan *chan = conn->smp; + struct smp_chan *smp = chan->data; + int err; + + BT_DBG("conn %p", conn); + + if (skb->len < sizeof(*key)) + return SMP_INVALID_PARAMS; + + memcpy(smp->remote_pk, key, 64); + + /* Non-initiating device sends its public key after receiving + * the key from the initiating device. + */ + if (!hcon->out) { + err = sc_send_public_key(smp); + if (err) + return err; + } + + BT_DBG("Remote Public Key X: %32phN", smp->remote_pk); + BT_DBG("Remote Public Key Y: %32phN", &smp->remote_pk[32]); + + if (!ecdh_shared_secret(smp->remote_pk, smp->local_sk, smp->dhkey)) + return SMP_UNSPECIFIED; + + BT_DBG("DHKey %32phN", smp->dhkey); + + set_bit(SMP_FLAG_REMOTE_PK, &smp->flags); + + return 0; +} + static int smp_sig_channel(struct l2cap_chan *chan, struct sk_buff *skb) { struct l2cap_conn *conn = chan->conn; @@ -1597,6 +1637,10 @@ static int smp_sig_channel(struct l2cap_chan *chan, struct sk_buff *skb) reason = smp_cmd_sign_info(conn, skb); break; + case SMP_CMD_PUBLIC_KEY: + reason = smp_cmd_public_key(conn, skb); + break; + default: BT_DBG("Unknown command code 0x%2.2x", code); reason = SMP_CMD_NOTSUPP; -- cgit v0.10.2 From cbbbe3e242f5629fd34836e7e41737235c049ce2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 6 Jun 2014 11:30:08 +0300 Subject: Bluetooth: Add support for sending LE SC Confirm value Once the public key exchange is complete the next step is for the non-initiating device to send a SMP Pairing Confirm PDU to the initiating device. This requires the use of a new smp_f4 confirm value generation function which in turn builds on the AES-CMAC cryptographic function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 1cc438c..0980b86 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -43,6 +43,9 @@ 0x1f : 0x07) #define KEY_DIST_MASK 0x07 +/* Maximum message length that can be passed to aes_cmac */ +#define CMAC_MSG_MAX 80 + enum { SMP_FLAG_TK_VALID, SMP_FLAG_CFM_PENDING, @@ -94,6 +97,84 @@ static inline void swap_buf(const u8 *src, u8 *dst, size_t len) dst[len - 1 - i] = src[i]; } +static int aes_cmac(struct crypto_hash *tfm, const u8 k[16], const u8 *m, + size_t len, u8 mac[16]) +{ + uint8_t tmp[16], mac_msb[16], msg_msb[CMAC_MSG_MAX]; + struct hash_desc desc; + struct scatterlist sg; + int err; + + if (len > CMAC_MSG_MAX) + return -EFBIG; + + if (!tfm) { + BT_ERR("tfm %p", tfm); + return -EINVAL; + } + + desc.tfm = tfm; + desc.flags = 0; + + crypto_hash_init(&desc); + + /* Swap key and message from LSB to MSB */ + swap_buf(k, tmp, 16); + swap_buf(m, msg_msb, len); + + BT_DBG("msg (len %zu) %*phN", len, (int) len, m); + BT_DBG("key %16phN", k); + + err = crypto_hash_setkey(tfm, tmp, 16); + if (err) { + BT_ERR("cipher setkey failed: %d", err); + return err; + } + + sg_init_one(&sg, msg_msb, len); + + err = crypto_hash_update(&desc, &sg, len); + if (err) { + BT_ERR("Hash update error %d", err); + return err; + } + + err = crypto_hash_final(&desc, mac_msb); + if (err) { + BT_ERR("Hash final error %d", err); + return err; + } + + swap_buf(mac_msb, mac, 16); + + BT_DBG("mac %16phN", mac); + + return 0; +} + +static int smp_f4(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32], + const u8 x[16], u8 z, u8 res[16]) +{ + u8 m[65]; + int err; + + BT_DBG("u %32phN", u); + BT_DBG("v %32phN", v); + BT_DBG("x %16phN z %02x", x, z); + + m[0] = z; + memcpy(m + 1, v, 32); + memcpy(m + 33, u, 32); + + err = aes_cmac(tfm_cmac, x, m, sizeof(m), res); + if (err) + return err; + + BT_DBG("res %16phN", res); + + return err; +} + static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r) { struct blkcipher_desc desc; @@ -1522,6 +1603,7 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb) struct hci_conn *hcon = conn->hcon; struct l2cap_chan *chan = conn->smp; struct smp_chan *smp = chan->data; + struct smp_cmd_pairing_confirm cfm; int err; BT_DBG("conn %p", conn); @@ -1550,6 +1632,20 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb) set_bit(SMP_FLAG_REMOTE_PK, &smp->flags); + /* The Initiating device waits for the non-initiating device to + * send the confirm value. + */ + if (conn->hcon->out) + return 0; + + err = smp_f4(smp->tfm_cmac, smp->local_pk, smp->remote_pk, smp->prnd, + 0, cfm.confirm_val); + if (err) + return SMP_UNSPECIFIED; + + smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cfm), &cfm); + SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM); + return 0; } -- cgit v0.10.2 From dcee2b32216ffa62195cac394d50fd801b51a41c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 6 Jun 2014 11:36:38 +0300 Subject: Bluetooth: Add LE SC support for responding to Pairing Confirm PDU When LE SC is being used we should always respond to it by sending our local random number. This patch adds a convenience function for it which also contains a check for the pre-requisite public key exchange completion Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 0980b86..6207f32 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1216,6 +1216,25 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) return 0; } +static u8 sc_check_confirm(struct smp_chan *smp) +{ + struct l2cap_conn *conn = smp->conn; + + BT_DBG(""); + + /* Public Key exchange must happen before any other steps */ + if (!test_bit(SMP_FLAG_REMOTE_PK, &smp->flags)) + return SMP_UNSPECIFIED; + + if (conn->hcon->out) { + smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd), + smp->prnd); + SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM); + } + + return 0; +} + static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) { struct l2cap_chan *chan = conn->smp; @@ -1229,6 +1248,9 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf)); skb_pull(skb, sizeof(smp->pcnf)); + if (test_bit(SMP_FLAG_SC, &smp->flags)) + return sc_check_confirm(smp); + if (conn->hcon->out) { smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd), smp->prnd); -- cgit v0.10.2 From 191dc7fe2d3a8dde70b37a1fd086a32c1f4e1dcf Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 6 Jun 2014 11:39:49 +0300 Subject: Bluetooth: Add support for LE SC numeric comparison After the Pairing Confirm and Random PDUs have been exchanged in LE SC it's time to generate a numeric comparison value using a new smp_g2 cryptographic function (which also builds on AES-CMAC). This patch adds the smp_g2 implementation and updates the Pairing Random PDU handler to proceed with the value genration and user confirmation. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 6207f32..103f05a 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -175,6 +175,32 @@ static int smp_f4(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32], return err; } +static int smp_g2(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32], + const u8 x[16], const u8 y[16], u32 *val) +{ + u8 m[80], tmp[16]; + int err; + + BT_DBG("u %32phN", u); + BT_DBG("v %32phN", v); + BT_DBG("x %16phN y %16phN", x, y); + + memcpy(m, y, 16); + memcpy(m + 16, v, 32); + memcpy(m + 48, u, 32); + + err = aes_cmac(tfm_cmac, x, m, sizeof(m), tmp); + if (err) + return err; + + *val = get_unaligned_le32(tmp); + *val %= 1000000; + + BT_DBG("val %06u", *val); + + return 0; +} + static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r) { struct blkcipher_desc desc; @@ -1270,6 +1296,10 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) { struct l2cap_chan *chan = conn->smp; struct smp_chan *smp = chan->data; + struct hci_conn *hcon = conn->hcon; + u8 *pkax, *pkbx, *na, *nb; + u32 passkey; + int err; BT_DBG("conn %p", conn); @@ -1279,7 +1309,46 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) memcpy(smp->rrnd, skb->data, sizeof(smp->rrnd)); skb_pull(skb, sizeof(smp->rrnd)); - return smp_random(smp); + if (!test_bit(SMP_FLAG_SC, &smp->flags)) + return smp_random(smp); + + if (hcon->out) { + u8 cfm[16]; + + err = smp_f4(smp->tfm_cmac, smp->remote_pk, smp->local_pk, + smp->rrnd, 0, cfm); + if (err) + return SMP_UNSPECIFIED; + + if (memcmp(smp->pcnf, cfm, 16)) + return SMP_CONFIRM_FAILED; + + pkax = smp->local_pk; + pkbx = smp->remote_pk; + na = smp->prnd; + nb = smp->rrnd; + } else { + smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd), + smp->prnd); + SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK); + + pkax = smp->remote_pk; + pkbx = smp->local_pk; + na = smp->rrnd; + nb = smp->prnd; + } + + err = smp_g2(smp->tfm_cmac, pkax, pkbx, na, nb, &passkey); + if (err) + return SMP_UNSPECIFIED; + + err = mgmt_user_confirm_request(hcon->hdev, &hcon->dst, + hcon->type, hcon->dst_type, + passkey, 0); + if (err) + return SMP_UNSPECIFIED; + + return 0; } static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level) -- cgit v0.10.2 From 760b018b6cf08ee16fed37249a0a0d52d930c356 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 6 Jun 2014 11:44:05 +0300 Subject: Bluetooth: Add support for handling LE SC user response With LE SC, once the user has responded to the numeric comparison it's time to send DHKey check values in both directions. The DHKey check value is generated using new smp_f5 and smp_f6 cryptographic functions. The smp_f5 function is responsible for generating the LTK and the MacKey values whereas the smp_f6 function takes the MacKey as input and generates the DHKey Check value. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 103f05a..b12f076 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -84,6 +84,7 @@ struct smp_chan { u8 local_sk[32]; u8 remote_pk[64]; u8 dhkey[32]; + u8 mackey[16]; struct crypto_blkcipher *tfm_aes; struct crypto_hash *tfm_cmac; @@ -175,6 +176,86 @@ static int smp_f4(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32], return err; } +static int smp_f5(struct crypto_hash *tfm_cmac, u8 w[32], u8 n1[16], u8 n2[16], + u8 a1[7], u8 a2[7], u8 mackey[16], u8 ltk[16]) +{ + /* The btle, salt and length "magic" values are as defined in + * the SMP section of the Bluetooth core specification. In ASCII + * the btle value ends up being 'btle'. The salt is just a + * random number whereas length is the value 256 in little + * endian format. + */ + const u8 btle[4] = { 0x65, 0x6c, 0x74, 0x62 }; + const u8 salt[16] = { 0xbe, 0x83, 0x60, 0x5a, 0xdb, 0x0b, 0x37, 0x60, + 0x38, 0xa5, 0xf5, 0xaa, 0x91, 0x83, 0x88, 0x6c }; + const u8 length[2] = { 0x00, 0x01 }; + u8 m[53], t[16]; + int err; + + BT_DBG("w %32phN", w); + BT_DBG("n1 %16phN n2 %16phN", n1, n2); + BT_DBG("a1 %7phN a2 %7phN", a1, a2); + + err = aes_cmac(tfm_cmac, salt, w, 32, t); + if (err) + return err; + + BT_DBG("t %16phN", t); + + memcpy(m, length, 2); + memcpy(m + 2, a2, 7); + memcpy(m + 9, a1, 7); + memcpy(m + 16, n2, 16); + memcpy(m + 32, n1, 16); + memcpy(m + 48, btle, 4); + + m[52] = 0; /* Counter */ + + err = aes_cmac(tfm_cmac, t, m, sizeof(m), mackey); + if (err) + return err; + + BT_DBG("mackey %16phN", mackey); + + m[52] = 1; /* Counter */ + + err = aes_cmac(tfm_cmac, t, m, sizeof(m), ltk); + if (err) + return err; + + BT_DBG("ltk %16phN", ltk); + + return 0; +} + +static int smp_f6(struct crypto_hash *tfm_cmac, const u8 w[16], + const u8 n1[16], u8 n2[16], const u8 r[16], + const u8 io_cap[3], const u8 a1[7], const u8 a2[7], + u8 res[16]) +{ + u8 m[65]; + int err; + + BT_DBG("w %16phN", w); + BT_DBG("n1 %16phN n2 %16phN", n1, n2); + BT_DBG("r %16phN io_cap %3phN a1 %7phN a2 %7phN", r, io_cap, a1, a2); + + memcpy(m, a2, 7); + memcpy(m + 7, a1, 7); + memcpy(m + 14, io_cap, 3); + memcpy(m + 17, r, 16); + memcpy(m + 33, n2, 16); + memcpy(m + 49, n1, 16); + + err = aes_cmac(tfm_cmac, w, m, sizeof(m), res); + if (err) + return err; + + BT_DBG("res %16phN", res); + + return err; +} + static int smp_g2(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32], const u8 x[16], const u8 y[16], u32 *val) { @@ -1001,6 +1082,69 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) return smp; } +static int sc_mackey_and_ltk(struct smp_chan *smp, u8 mackey[16], u8 ltk[16]) +{ + struct hci_conn *hcon = smp->conn->hcon; + u8 *na, *nb, a[7], b[7]; + + if (hcon->out) { + na = smp->prnd; + nb = smp->rrnd; + } else { + na = smp->rrnd; + nb = smp->prnd; + } + + memcpy(a, &hcon->init_addr, 6); + memcpy(b, &hcon->resp_addr, 6); + a[6] = hcon->init_addr_type; + b[6] = hcon->resp_addr_type; + + return smp_f5(smp->tfm_cmac, smp->dhkey, na, nb, a, b, mackey, ltk); +} + +static int sc_user_reply(struct smp_chan *smp, u16 mgmt_op, __le32 passkey) +{ + struct hci_conn *hcon = smp->conn->hcon; + struct smp_cmd_dhkey_check check; + u8 a[7], b[7], *local_addr, *remote_addr; + u8 io_cap[3], r[16]; + + switch (mgmt_op) { + case MGMT_OP_USER_PASSKEY_NEG_REPLY: + smp_failure(smp->conn, SMP_PASSKEY_ENTRY_FAILED); + return 0; + case MGMT_OP_USER_CONFIRM_NEG_REPLY: + smp_failure(smp->conn, SMP_NUMERIC_COMP_FAILED); + return 0; + } + + memcpy(a, &hcon->init_addr, 6); + memcpy(b, &hcon->resp_addr, 6); + a[6] = hcon->init_addr_type; + b[6] = hcon->resp_addr_type; + + if (hcon->out) { + local_addr = a; + remote_addr = b; + memcpy(io_cap, &smp->preq[1], 3); + } else { + local_addr = b; + remote_addr = a; + memcpy(io_cap, &smp->prsp[1], 3); + } + + memcpy(r, &passkey, sizeof(passkey)); + memset(r + sizeof(passkey), 0, sizeof(r) - sizeof(passkey)); + + smp_f6(smp->tfm_cmac, smp->mackey, smp->prnd, smp->rrnd, r, io_cap, + local_addr, remote_addr, check.e); + + smp_send_cmd(smp->conn, SMP_CMD_DHKEY_CHECK, sizeof(check), &check); + + return 0; +} + int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey) { struct l2cap_conn *conn = hcon->l2cap_data; @@ -1026,6 +1170,11 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey) smp = chan->data; + if (test_bit(SMP_FLAG_SC, &smp->flags)) { + err = sc_user_reply(smp, mgmt_op, passkey); + goto unlock; + } + switch (mgmt_op) { case MGMT_OP_USER_PASSKEY_REPLY: value = le32_to_cpu(passkey); @@ -1338,6 +1487,11 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) nb = smp->prnd; } + /* Generate MacKey and LTK */ + err = sc_mackey_and_ltk(smp, smp->mackey, smp->tk); + if (err) + return SMP_UNSPECIFIED; + err = smp_g2(smp->tfm_cmac, pkax, pkbx, na, nb, &passkey); if (err) return SMP_UNSPECIFIED; -- cgit v0.10.2 From 6433a9a2c47e5b3ddfdb86ee5b57a6eada1f6da7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 6 Jun 2014 11:47:30 +0300 Subject: Bluetooth: Add support for LE SC DHKey check PDU Once we receive the DHKey check PDU it's time to first verify that the value is correct and then proceed with encrypting the link. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index b12f076..f59f051 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1894,6 +1894,58 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb) return 0; } +static int smp_cmd_dhkey_check(struct l2cap_conn *conn, struct sk_buff *skb) +{ + struct smp_cmd_dhkey_check *check = (void *) skb->data; + struct l2cap_chan *chan = conn->smp; + struct hci_conn *hcon = conn->hcon; + struct smp_chan *smp = chan->data; + u8 a[7], b[7], *local_addr, *remote_addr; + u8 io_cap[3], r[16], e[16]; + int err; + + BT_DBG("conn %p", conn); + + if (skb->len < sizeof(*check)) + return SMP_INVALID_PARAMS; + + memcpy(a, &hcon->init_addr, 6); + memcpy(b, &hcon->resp_addr, 6); + a[6] = hcon->init_addr_type; + b[6] = hcon->resp_addr_type; + + if (hcon->out) { + local_addr = a; + remote_addr = b; + memcpy(io_cap, &smp->prsp[1], 3); + } else { + local_addr = b; + remote_addr = a; + memcpy(io_cap, &smp->preq[1], 3); + } + + memset(r, 0, sizeof(r)); + + err = smp_f6(smp->tfm_cmac, smp->mackey, smp->rrnd, smp->prnd, r, + io_cap, remote_addr, local_addr, e); + if (err) + return SMP_UNSPECIFIED; + + if (memcmp(check->e, e, 16)) + return SMP_DHKEY_CHECK_FAILED; + + smp->ltk = hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, + SMP_LTK_P256, 0, smp->tk, smp->enc_key_size, + 0, 0); + + if (hcon->out) { + hci_le_start_enc(hcon, 0, 0, smp->tk); + hcon->enc_key_size = smp->enc_key_size; + } + + return 0; +} + static int smp_sig_channel(struct l2cap_chan *chan, struct sk_buff *skb) { struct l2cap_conn *conn = chan->conn; @@ -1982,6 +2034,10 @@ static int smp_sig_channel(struct l2cap_chan *chan, struct sk_buff *skb) reason = smp_cmd_public_key(conn, skb); break; + case SMP_CMD_DHKEY_CHECK: + reason = smp_cmd_dhkey_check(conn, skb); + break; + default: BT_DBG("Unknown command code 0x%2.2x", code); reason = SMP_CMD_NOTSUPP; -- cgit v0.10.2 From 6a77083af57f2dc515a01c8ec82610ab0e7baa59 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 6 Jun 2014 11:54:04 +0300 Subject: Bluetooth: Add support for LE SC key generation As the last step of the LE SC pairing process it's time to generate and distribute keys. The generation part is unique to LE SC and so this patch adds a dedicated function for it. We also clear the distribution bits for keys which are not distributed with LE SC, so that the code shared with legacy SMP will not go ahead and try to distribute them. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index f59f051..20fa07a 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -77,6 +77,7 @@ struct smp_chan { struct smp_ltk *ltk; struct smp_ltk *slave_ltk; struct smp_irk *remote_irk; + u8 *link_key; unsigned long flags; /* Secure Connections variables */ @@ -321,6 +322,22 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r) return err; } +static int smp_h6(struct crypto_hash *tfm_cmac, const u8 w[16], + const u8 key_id[4], u8 res[16]) +{ + int err; + + BT_DBG("w %16phN key_id %4phN", w, key_id); + + err = aes_cmac(tfm_cmac, w, key_id, 4, res); + if (err) + return err; + + BT_DBG("res %16phN", res); + + return err; +} + static int smp_ah(struct crypto_blkcipher *tfm, u8 irk[16], u8 r[3], u8 res[3]) { u8 _res[16]; @@ -594,6 +611,7 @@ static void smp_chan_destroy(struct l2cap_conn *conn) kfree(smp->csrk); kfree(smp->slave_csrk); + kfree(smp->link_key); crypto_free_blkcipher(smp->tfm_aes); crypto_free_hash(smp->tfm_cmac); @@ -907,6 +925,37 @@ static void smp_notify_keys(struct l2cap_conn *conn) bacpy(&smp->slave_ltk->bdaddr, &hcon->dst); mgmt_new_ltk(hdev, smp->slave_ltk, persistent); } + + if (smp->link_key) { + hci_add_link_key(hdev, smp->conn->hcon, &hcon->dst, + smp->link_key, HCI_LK_AUTH_COMBINATION_P256, + 0, NULL); + } +} + +static void sc_generate_link_key(struct smp_chan *smp) +{ + /* These constants are as specified in the core specification. + * In ASCII they spell out to 'tmp1' and 'lebr'. + */ + const u8 tmp1[4] = { 0x31, 0x70, 0x6d, 0x74 }; + const u8 lebr[4] = { 0x72, 0x62, 0x65, 0x6c }; + + smp->link_key = kzalloc(16, GFP_KERNEL); + if (!smp->link_key) + return; + + if (smp_h6(smp->tfm_cmac, smp->tk, tmp1, smp->link_key)) { + kfree(smp->link_key); + smp->link_key = NULL; + return; + } + + if (smp_h6(smp->tfm_cmac, smp->link_key, lebr, smp->link_key)) { + kfree(smp->link_key); + smp->link_key = NULL; + return; + } } static void smp_allow_key_dist(struct smp_chan *smp) @@ -951,6 +1000,14 @@ static void smp_distribute_keys(struct smp_chan *smp) *keydist &= req->resp_key_dist; } + if (test_bit(SMP_FLAG_SC, &smp->flags)) { + if (*keydist & SMP_DIST_LINK_KEY) + sc_generate_link_key(smp); + + /* Clear the keys which are generated but not distributed */ + *keydist &= ~SMP_SC_NO_DIST; + } + BT_DBG("keydist 0x%x", *keydist); if (*keydist & SMP_DIST_ENC_KEY) { -- cgit v0.10.2 From 783e057462d514441fbd371bbb398cf886fe3376 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 31 May 2014 18:48:26 +0300 Subject: Bluetooth: Track authentication method in SMP context For Secure Connections we'll select the authentication method as soon as we receive the public key, but only use it later (both when actually triggering the method as well as when determining the quality of the resulting LTK). Store the method therefore in the SMP context. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 20fa07a..45e527d 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -79,6 +79,7 @@ struct smp_chan { struct smp_irk *remote_irk; u8 *link_key; unsigned long flags; + u8 method; /* Secure Connections variables */ u8 local_pk[64]; @@ -688,7 +689,6 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, struct hci_conn *hcon = conn->hcon; struct l2cap_chan *chan = conn->smp; struct smp_chan *smp = chan->data; - u8 method; u32 passkey = 0; int ret = 0; @@ -705,26 +705,28 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, * table. */ if (!(auth & SMP_AUTH_MITM)) - method = JUST_CFM; + smp->method = JUST_CFM; else - method = get_auth_method(smp, local_io, remote_io); + smp->method = get_auth_method(smp, local_io, remote_io); /* Don't confirm locally initiated pairing attempts */ - if (method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR, &smp->flags)) - method = JUST_WORKS; + if (smp->method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR, + &smp->flags)) + smp->method = JUST_WORKS; /* Don't bother user space with no IO capabilities */ - if (method == JUST_CFM && hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT) - method = JUST_WORKS; + if (smp->method == JUST_CFM && + hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT) + smp->method = JUST_WORKS; /* If Just Works, Continue with Zero TK */ - if (method == JUST_WORKS) { + if (smp->method == JUST_WORKS) { set_bit(SMP_FLAG_TK_VALID, &smp->flags); return 0; } /* Not Just Works/Confirm results in MITM Authentication */ - if (method != JUST_CFM) { + if (smp->method != JUST_CFM) { set_bit(SMP_FLAG_MITM_AUTH, &smp->flags); if (hcon->pending_sec_level < BT_SECURITY_HIGH) hcon->pending_sec_level = BT_SECURITY_HIGH; @@ -733,15 +735,15 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, /* If both devices have Keyoard-Display I/O, the master * Confirms and the slave Enters the passkey. */ - if (method == OVERLAP) { + if (smp->method == OVERLAP) { if (hcon->role == HCI_ROLE_MASTER) - method = CFM_PASSKEY; + smp->method = CFM_PASSKEY; else - method = REQ_PASSKEY; + smp->method = REQ_PASSKEY; } /* Generate random passkey. */ - if (method == CFM_PASSKEY) { + if (smp->method == CFM_PASSKEY) { memset(smp->tk, 0, sizeof(smp->tk)); get_random_bytes(&passkey, sizeof(passkey)); passkey %= 1000000; @@ -750,10 +752,10 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, set_bit(SMP_FLAG_TK_VALID, &smp->flags); } - if (method == REQ_PASSKEY) + if (smp->method == REQ_PASSKEY) ret = mgmt_user_passkey_request(hcon->hdev, &hcon->dst, hcon->type, hcon->dst_type); - else if (method == JUST_CFM) + else if (smp->method == JUST_CFM) ret = mgmt_user_confirm_request(hcon->hdev, &hcon->dst, hcon->type, hcon->dst_type, passkey, 1); -- cgit v0.10.2 From 5e3d3d9b3c0119e6ac86bf77c6ff9f6b0d09bcf5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 31 May 2014 18:51:02 +0300 Subject: Bluetooth: Add selection of the SC authentication method This patch adds code to select the authentication method for Secure Connections based on the local and remote capabilities. A new DSP_PASSKEY method is also added for displaying the passkey - something that is not part of legacy SMP pairing. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 45e527d..9e11931 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -661,6 +661,7 @@ static void smp_failure(struct l2cap_conn *conn, u8 reason) #define REQ_PASSKEY 0x02 #define CFM_PASSKEY 0x03 #define REQ_OOB 0x04 +#define DSP_PASSKEY 0x05 #define OVERLAP 0xFF static const u8 gen_method[5][5] = { @@ -671,6 +672,14 @@ static const u8 gen_method[5][5] = { { CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, OVERLAP }, }; +static const u8 sc_method[5][5] = { + { JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY }, + { JUST_WORKS, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY }, + { DSP_PASSKEY, DSP_PASSKEY, REQ_PASSKEY, JUST_WORKS, DSP_PASSKEY }, + { JUST_WORKS, JUST_CFM, JUST_WORKS, JUST_WORKS, JUST_CFM }, + { DSP_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY }, +}; + static u8 get_auth_method(struct smp_chan *smp, u8 local_io, u8 remote_io) { /* If either side has unknown io_caps, use JUST_CFM (which gets @@ -680,6 +689,9 @@ static u8 get_auth_method(struct smp_chan *smp, u8 local_io, u8 remote_io) remote_io > SMP_IO_KEYBOARD_DISPLAY) return JUST_CFM; + if (test_bit(SMP_FLAG_SC, &smp->flags)) + return sc_method[remote_io][local_io]; + return gen_method[remote_io][local_io]; } @@ -1305,6 +1317,11 @@ 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)); + build_pairing_cmd(conn, req, &rsp, auth); + + if (rsp.auth_req & SMP_AUTH_SC) + set_bit(SMP_FLAG_SC, &smp->flags); + if (conn->hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT) sec_level = BT_SECURITY_MEDIUM; else @@ -1323,11 +1340,6 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) return SMP_AUTH_REQUIREMENTS; } - build_pairing_cmd(conn, req, &rsp, auth); - - if (rsp.auth_req & SMP_AUTH_SC) - set_bit(SMP_FLAG_SC, &smp->flags); - key_size = min(req->max_key_size, rsp.max_key_size); if (check_enc_key_size(conn, key_size)) return SMP_ENC_KEY_SIZE; @@ -1901,12 +1913,54 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb) return 0; } +static u8 sc_select_method(struct smp_chan *smp) +{ + struct l2cap_conn *conn = smp->conn; + struct hci_conn *hcon = conn->hcon; + struct smp_cmd_pairing *local, *remote; + u8 local_mitm, remote_mitm, local_io, remote_io, method; + + /* The preq/prsp contain the raw Pairing Request/Response PDUs + * which are needed as inputs to some crypto functions. To get + * the "struct smp_cmd_pairing" from them we need to skip the + * first byte which contains the opcode. + */ + if (hcon->out) { + local = (void *) &smp->preq[1]; + remote = (void *) &smp->prsp[1]; + } else { + local = (void *) &smp->prsp[1]; + remote = (void *) &smp->preq[1]; + } + + local_io = local->io_capability; + remote_io = remote->io_capability; + + local_mitm = (local->auth_req & SMP_AUTH_MITM); + remote_mitm = (remote->auth_req & SMP_AUTH_MITM); + + /* If either side wants MITM, look up the method from the table, + * otherwise use JUST WORKS. + */ + if (local_mitm || remote_mitm) + method = get_auth_method(smp, local_io, remote_io); + else + method = JUST_WORKS; + + /* Don't confirm locally initiated pairing attempts */ + if (method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR, &smp->flags)) + method = JUST_WORKS; + + return method; +} + static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_public_key *key = (void *) skb->data; struct hci_conn *hcon = conn->hcon; struct l2cap_chan *chan = conn->smp; struct smp_chan *smp = chan->data; + struct hci_dev *hdev = hcon->hdev; struct smp_cmd_pairing_confirm cfm; int err; @@ -1936,6 +1990,16 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb) set_bit(SMP_FLAG_REMOTE_PK, &smp->flags); + smp->method = sc_select_method(smp); + + BT_DBG("%s selected method 0x%02x", hdev->name, smp->method); + + /* JUST_WORKS and JUST_CFM result in an unauthenticated key */ + if (smp->method == JUST_WORKS || smp->method == JUST_CFM) + hcon->pending_sec_level = BT_SECURITY_MEDIUM; + else + hcon->pending_sec_level = BT_SECURITY_FIPS; + /* The Initiating device waits for the non-initiating device to * send the confirm value. */ -- cgit v0.10.2 From aeb7d461f9c895bb6d09e9d175696849e9f290c8 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 31 May 2014 18:52:28 +0300 Subject: Bluetooth: Detect SMP SC debug keys We need to be able to detect if the remote side used a debug key for the pairing. This patch adds the debug key defines and sets a flag to indicate that a debug key was used. The debug private key (debug_sk) is also added in this patch but will only be used in a subsequent patch when local debug key support is implemented. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 9e11931..ddc76cf 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -54,6 +54,7 @@ enum { SMP_FLAG_INITIATOR, SMP_FLAG_SC, SMP_FLAG_REMOTE_PK, + SMP_FLAG_DEBUG_KEY, }; struct smp_chan { @@ -92,6 +93,29 @@ struct smp_chan { struct crypto_hash *tfm_cmac; }; +/* These debug key values are defined in the SMP section of the core + * specification. debug_pk is the public debug key and debug_sk the + * private debug key. + */ +static const u8 debug_pk[64] = { + 0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc, + 0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef, + 0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e, + 0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20, + + 0x8b, 0xd2, 0x89, 0x15, 0xd0, 0x8e, 0x1c, 0x74, + 0x24, 0x30, 0xed, 0x8f, 0xc2, 0x45, 0x63, 0x76, + 0x5c, 0x15, 0x52, 0x5a, 0xbf, 0x9a, 0x32, 0x63, + 0x6d, 0xeb, 0x2a, 0x65, 0x49, 0x9c, 0x80, 0xdc, +}; + +static const u8 debug_sk[32] = { + 0xbd, 0x1a, 0x3c, 0xcd, 0xa6, 0xb8, 0x99, 0x58, + 0x99, 0xb7, 0x40, 0xeb, 0x7b, 0x60, 0xff, 0x4a, + 0x50, 0x3f, 0x10, 0xd2, 0xe3, 0xb3, 0xc9, 0x74, + 0x38, 0x5f, 0xc5, 0xa3, 0xd4, 0xf6, 0x49, 0x3f, +}; + static inline void swap_buf(const u8 *src, u8 *dst, size_t len) { size_t i; @@ -2000,6 +2024,9 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb) else hcon->pending_sec_level = BT_SECURITY_FIPS; + if (!memcmp(debug_pk, smp->remote_pk, 64)) + set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags); + /* The Initiating device waits for the non-initiating device to * send the confirm value. */ -- cgit v0.10.2 From 6c0dcc5014caeb9c39db816a7e0169255923ccba Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 6 Jun 2014 15:33:30 +0300 Subject: Bluetooth: Add check for accidentally generating a debug key It is very unlikely, but to have a 100% guarantee of the generated key type we need to reject any keys which happen to match the debug key. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index ddc76cf..63d5ba7 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1399,9 +1399,17 @@ static u8 sc_send_public_key(struct smp_chan *smp) { BT_DBG(""); - /* Generate local key pair for Secure Connections */ - if (!ecc_make_key(smp->local_pk, smp->local_sk)) - return SMP_UNSPECIFIED; + while (true) { + /* Generate local key pair for Secure Connections */ + if (!ecc_make_key(smp->local_pk, smp->local_sk)) + return SMP_UNSPECIFIED; + + /* This is unlikely, but we need to check that we didn't + * accidentially generate a debug key. + */ + if (memcmp(smp->local_sk, debug_sk, 32)) + break; + } BT_DBG("Local Public Key X: %32phN", smp->local_pk); BT_DBG("Local Public Key Y: %32phN", &smp->local_pk[32]); -- cgit v0.10.2 From d378a2d77618464f511d35687bbbc6614b1bacda Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 31 May 2014 18:53:36 +0300 Subject: Bluetooth: Set correct LTK type and authentication for SC After generating the LTK we should set the correct type (normal SC or debug) and authentication information for it. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 63d5ba7..3043936 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -2060,6 +2060,7 @@ static int smp_cmd_dhkey_check(struct l2cap_conn *conn, struct sk_buff *skb) struct smp_chan *smp = chan->data; u8 a[7], b[7], *local_addr, *remote_addr; u8 io_cap[3], r[16], e[16]; + u8 key_type, auth; int err; BT_DBG("conn %p", conn); @@ -2092,8 +2093,18 @@ static int smp_cmd_dhkey_check(struct l2cap_conn *conn, struct sk_buff *skb) if (memcmp(check->e, e, 16)) return SMP_DHKEY_CHECK_FAILED; + if (test_bit(SMP_FLAG_DEBUG_KEY, &smp->flags)) + key_type = SMP_LTK_P256_DEBUG; + else + key_type = SMP_LTK_P256; + + if (hcon->pending_sec_level == BT_SECURITY_FIPS) + auth = 1; + else + auth = 0; + smp->ltk = hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, - SMP_LTK_P256, 0, smp->tk, smp->enc_key_size, + key_type, auth, smp->tk, smp->enc_key_size, 0, 0); if (hcon->out) { -- cgit v0.10.2 From dddd3059e3bdd02c4850b14b925b3bb37c23f248 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 1 Jun 2014 15:38:09 +0300 Subject: Bluetooth: Add support for SC just-works pairing If the just-works method was chosen we shouldn't send anything to user space but simply proceed with sending the DHKey Check PDU. This patch adds the necessary code for it. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 3043936..b6cdb55 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1198,22 +1198,13 @@ static int sc_mackey_and_ltk(struct smp_chan *smp, u8 mackey[16], u8 ltk[16]) return smp_f5(smp->tfm_cmac, smp->dhkey, na, nb, a, b, mackey, ltk); } -static int sc_user_reply(struct smp_chan *smp, u16 mgmt_op, __le32 passkey) +static void sc_dhkey_check(struct smp_chan *smp, __le32 passkey) { struct hci_conn *hcon = smp->conn->hcon; struct smp_cmd_dhkey_check check; u8 a[7], b[7], *local_addr, *remote_addr; u8 io_cap[3], r[16]; - switch (mgmt_op) { - case MGMT_OP_USER_PASSKEY_NEG_REPLY: - smp_failure(smp->conn, SMP_PASSKEY_ENTRY_FAILED); - return 0; - case MGMT_OP_USER_CONFIRM_NEG_REPLY: - smp_failure(smp->conn, SMP_NUMERIC_COMP_FAILED); - return 0; - } - memcpy(a, &hcon->init_addr, 6); memcpy(b, &hcon->resp_addr, 6); a[6] = hcon->init_addr_type; @@ -1229,13 +1220,29 @@ static int sc_user_reply(struct smp_chan *smp, u16 mgmt_op, __le32 passkey) memcpy(io_cap, &smp->prsp[1], 3); } - memcpy(r, &passkey, sizeof(passkey)); - memset(r + sizeof(passkey), 0, sizeof(r) - sizeof(passkey)); + memset(r, 0, sizeof(r)); + + if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY) + memcpy(r, &passkey, sizeof(passkey)); smp_f6(smp->tfm_cmac, smp->mackey, smp->prnd, smp->rrnd, r, io_cap, local_addr, remote_addr, check.e); smp_send_cmd(smp->conn, SMP_CMD_DHKEY_CHECK, sizeof(check), &check); +} + +static int sc_user_reply(struct smp_chan *smp, u16 mgmt_op, __le32 passkey) +{ + switch (mgmt_op) { + case MGMT_OP_USER_PASSKEY_NEG_REPLY: + smp_failure(smp->conn, SMP_PASSKEY_ENTRY_FAILED); + return 0; + case MGMT_OP_USER_CONFIRM_NEG_REPLY: + smp_failure(smp->conn, SMP_NUMERIC_COMP_FAILED); + return 0; + } + + sc_dhkey_check(smp, passkey); return 0; } @@ -1599,6 +1606,14 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) if (err) return SMP_UNSPECIFIED; + if (smp->method == JUST_WORKS) { + if (hcon->out) { + sc_dhkey_check(smp, passkey); + SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK); + } + return 0; + } + err = mgmt_user_confirm_request(hcon->hdev, &hcon->dst, hcon->type, hcon->dst_type, passkey, 0); -- cgit v0.10.2 From e3befab970a0230a80f7732fd59bc19df26f805f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 1 Jun 2014 16:33:39 +0300 Subject: Bluetooth: Fix BR/EDR Link Key type when derived through LE SC We need to set the correct Link Key type based on the properties of the LE SC pairing that it was derived from. If debug keys were used the type should be a debug key, and the authenticated vs unauthenticated information should be set on what kind of security level was reached. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 6c3220e..2fa9f2b 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3160,6 +3160,10 @@ static bool hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn, if (!conn) return true; + /* BR/EDR key derived using SC from an LE link */ + if (conn->type == LE_LINK) + return true; + /* Neither local nor remote side had no-bonding as requirement */ if (conn->auth_type > 0x01 && conn->remote_auth > 0x01) return true; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index b6cdb55..a322019 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -965,9 +965,30 @@ static void smp_notify_keys(struct l2cap_conn *conn) } if (smp->link_key) { - hci_add_link_key(hdev, smp->conn->hcon, &hcon->dst, - smp->link_key, HCI_LK_AUTH_COMBINATION_P256, - 0, NULL); + struct link_key *key; + u8 type; + + if (test_bit(SMP_FLAG_DEBUG_KEY, &smp->flags)) + type = HCI_LK_DEBUG_COMBINATION; + else if (hcon->sec_level == BT_SECURITY_FIPS) + type = HCI_LK_AUTH_COMBINATION_P256; + else + type = HCI_LK_UNAUTH_COMBINATION_P256; + + key = hci_add_link_key(hdev, smp->conn->hcon, &hcon->dst, + smp->link_key, type, 0, &persistent); + if (key) { + mgmt_new_link_key(hdev, key, persistent); + + /* Don't keep debug keys around if the relevant + * flag is not set. + */ + if (!test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags) && + key->type == HCI_LK_DEBUG_COMBINATION) { + list_del_rcu(&key->list); + kfree_rcu(key, rcu); + } + } } } -- cgit v0.10.2 From 38606f1418cc9c0ac4230f4d9133a2cae5e02416 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 25 Jun 2014 11:10:28 +0300 Subject: Bluetooth: Add passkey entry support for LE SC The passkey entry mechanism involves either both sides requesting the user for a passkey, or one side requesting the passkey while the other one displays it. The behavior as far as SMP PDUs are concerned are considerably different from numeric comparison and therefore requires several new functions to handle it. In essence passkey entry involves both sides gradually committing to each bit of the passkey which involves 20 rounds of pairing confirm and pairing random PDUS being sent in both directions. This patch adds a new smp->passkey_round variable to track the current round of the passkey commitment and reuses the variables already present in struct hci_conn for the passkey and entered key count. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index a322019..8cfa1c1 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -55,6 +55,7 @@ enum { SMP_FLAG_SC, SMP_FLAG_REMOTE_PK, SMP_FLAG_DEBUG_KEY, + SMP_FLAG_WAIT_USER, }; struct smp_chan { @@ -81,6 +82,7 @@ struct smp_chan { u8 *link_key; unsigned long flags; u8 method; + u8 passkey_round; /* Secure Connections variables */ u8 local_pk[64]; @@ -1219,7 +1221,7 @@ static int sc_mackey_and_ltk(struct smp_chan *smp, u8 mackey[16], u8 ltk[16]) return smp_f5(smp->tfm_cmac, smp->dhkey, na, nb, a, b, mackey, ltk); } -static void sc_dhkey_check(struct smp_chan *smp, __le32 passkey) +static void sc_dhkey_check(struct smp_chan *smp) { struct hci_conn *hcon = smp->conn->hcon; struct smp_cmd_dhkey_check check; @@ -1244,7 +1246,7 @@ static void sc_dhkey_check(struct smp_chan *smp, __le32 passkey) memset(r, 0, sizeof(r)); if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY) - memcpy(r, &passkey, sizeof(passkey)); + put_unaligned_le32(hcon->passkey_notify, r); smp_f6(smp->tfm_cmac, smp->mackey, smp->prnd, smp->rrnd, r, io_cap, local_addr, remote_addr, check.e); @@ -1252,8 +1254,124 @@ static void sc_dhkey_check(struct smp_chan *smp, __le32 passkey) smp_send_cmd(smp->conn, SMP_CMD_DHKEY_CHECK, sizeof(check), &check); } +static u8 sc_passkey_send_confirm(struct smp_chan *smp) +{ + struct l2cap_conn *conn = smp->conn; + struct hci_conn *hcon = conn->hcon; + struct smp_cmd_pairing_confirm cfm; + u8 r; + + r = ((hcon->passkey_notify >> smp->passkey_round) & 0x01); + r |= 0x80; + + get_random_bytes(smp->prnd, sizeof(smp->prnd)); + + if (smp_f4(smp->tfm_cmac, smp->local_pk, smp->remote_pk, smp->prnd, r, + cfm.confirm_val)) + return SMP_UNSPECIFIED; + + smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cfm), &cfm); + + return 0; +} + +static u8 sc_passkey_round(struct smp_chan *smp, u8 smp_op) +{ + struct l2cap_conn *conn = smp->conn; + struct hci_conn *hcon = conn->hcon; + struct hci_dev *hdev = hcon->hdev; + u8 cfm[16], r; + + /* Ignore the PDU if we've already done 20 rounds (0 - 19) */ + if (smp->passkey_round >= 20) + return 0; + + switch (smp_op) { + case SMP_CMD_PAIRING_RANDOM: + r = ((hcon->passkey_notify >> smp->passkey_round) & 0x01); + r |= 0x80; + + if (smp_f4(smp->tfm_cmac, smp->remote_pk, smp->local_pk, + smp->rrnd, r, cfm)) + return SMP_UNSPECIFIED; + + if (memcmp(smp->pcnf, cfm, 16)) + return SMP_CONFIRM_FAILED; + + smp->passkey_round++; + + if (smp->passkey_round == 20) { + /* Generate MacKey and LTK */ + if (sc_mackey_and_ltk(smp, smp->mackey, smp->tk)) + return SMP_UNSPECIFIED; + } + + /* The round is only complete when the initiator + * receives pairing random. + */ + if (!hcon->out) { + smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, + sizeof(smp->prnd), smp->prnd); + if (smp->passkey_round == 20) { + sc_dhkey_check(smp); + SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK); + } else { + SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM); + } + return 0; + } + + /* Start the next round */ + if (smp->passkey_round != 20) + return sc_passkey_round(smp, 0); + + /* Passkey rounds are complete - start DHKey Check */ + sc_dhkey_check(smp); + SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK); + + break; + + case SMP_CMD_PAIRING_CONFIRM: + if (test_bit(SMP_FLAG_WAIT_USER, &smp->flags)) { + set_bit(SMP_FLAG_CFM_PENDING, &smp->flags); + return 0; + } + + SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM); + + if (hcon->out) { + smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, + sizeof(smp->prnd), smp->prnd); + return 0; + } + + return sc_passkey_send_confirm(smp); + + case SMP_CMD_PUBLIC_KEY: + default: + /* Initiating device starts the round */ + if (!hcon->out) + return 0; + + BT_DBG("%s Starting passkey round %u", hdev->name, + smp->passkey_round + 1); + + SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM); + + return sc_passkey_send_confirm(smp); + } + + return 0; +} + static int sc_user_reply(struct smp_chan *smp, u16 mgmt_op, __le32 passkey) { + struct l2cap_conn *conn = smp->conn; + struct hci_conn *hcon = conn->hcon; + u8 smp_op; + + clear_bit(SMP_FLAG_WAIT_USER, &smp->flags); + switch (mgmt_op) { case MGMT_OP_USER_PASSKEY_NEG_REPLY: smp_failure(smp->conn, SMP_PASSKEY_ENTRY_FAILED); @@ -1261,9 +1379,22 @@ static int sc_user_reply(struct smp_chan *smp, u16 mgmt_op, __le32 passkey) case MGMT_OP_USER_CONFIRM_NEG_REPLY: smp_failure(smp->conn, SMP_NUMERIC_COMP_FAILED); return 0; + case MGMT_OP_USER_PASSKEY_REPLY: + hcon->passkey_notify = le32_to_cpu(passkey); + smp->passkey_round = 0; + + if (test_and_clear_bit(SMP_FLAG_CFM_PENDING, &smp->flags)) + smp_op = SMP_CMD_PAIRING_CONFIRM; + else + smp_op = 0; + + if (sc_passkey_round(smp, smp_op)) + return -EIO; + + return 0; } - sc_dhkey_check(smp, passkey); + sc_dhkey_check(smp); return 0; } @@ -1532,6 +1663,9 @@ static u8 sc_check_confirm(struct smp_chan *smp) if (!test_bit(SMP_FLAG_REMOTE_PK, &smp->flags)) return SMP_UNSPECIFIED; + if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY) + return sc_passkey_round(smp, SMP_CMD_PAIRING_CONFIRM); + if (conn->hcon->out) { smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd), smp->prnd); @@ -1592,6 +1726,10 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) if (!test_bit(SMP_FLAG_SC, &smp->flags)) return smp_random(smp); + /* Passkey entry has special treatment */ + if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY) + return sc_passkey_round(smp, SMP_CMD_PAIRING_RANDOM); + if (hcon->out) { u8 cfm[16]; @@ -1623,24 +1761,25 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) if (err) return SMP_UNSPECIFIED; - err = smp_g2(smp->tfm_cmac, pkax, pkbx, na, nb, &passkey); - if (err) - return SMP_UNSPECIFIED; - if (smp->method == JUST_WORKS) { if (hcon->out) { - sc_dhkey_check(smp, passkey); + sc_dhkey_check(smp); SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK); } return 0; } - err = mgmt_user_confirm_request(hcon->hdev, &hcon->dst, - hcon->type, hcon->dst_type, - passkey, 0); + err = smp_g2(smp->tfm_cmac, pkax, pkbx, na, nb, &passkey); + if (err) + return SMP_UNSPECIFIED; + + err = mgmt_user_confirm_request(hcon->hdev, &hcon->dst, hcon->type, + hcon->dst_type, passkey, 0); if (err) return SMP_UNSPECIFIED; + set_bit(SMP_FLAG_WAIT_USER, &smp->flags); + return 0; } @@ -2071,6 +2210,33 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb) if (!memcmp(debug_pk, smp->remote_pk, 64)) set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags); + if (smp->method == DSP_PASSKEY) { + get_random_bytes(&hcon->passkey_notify, + sizeof(hcon->passkey_notify)); + hcon->passkey_notify %= 1000000; + hcon->passkey_entered = 0; + smp->passkey_round = 0; + if (mgmt_user_passkey_notify(hdev, &hcon->dst, hcon->type, + hcon->dst_type, + hcon->passkey_notify, + hcon->passkey_entered)) + return SMP_UNSPECIFIED; + SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM); + return sc_passkey_round(smp, SMP_CMD_PUBLIC_KEY); + } + + if (hcon->out) + SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM); + + if (smp->method == REQ_PASSKEY) { + if (mgmt_user_passkey_request(hdev, &hcon->dst, hcon->type, + hcon->dst_type)) + return SMP_UNSPECIFIED; + SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM); + set_bit(SMP_FLAG_WAIT_USER, &smp->flags); + return 0; + } + /* The Initiating device waits for the non-initiating device to * send the confirm value. */ @@ -2121,6 +2287,9 @@ static int smp_cmd_dhkey_check(struct l2cap_conn *conn, struct sk_buff *skb) memset(r, 0, sizeof(r)); + if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY) + put_unaligned_le32(hcon->passkey_notify, r); + err = smp_f6(smp->tfm_cmac, smp->mackey, smp->rrnd, smp->prnd, r, io_cap, remote_addr, local_addr, e); if (err) -- cgit v0.10.2 From d3e54a876e35120b3355e22d78861e73a6acab6f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 4 Jun 2014 11:07:40 +0300 Subject: Bluetooth: Fix DHKey Check sending order for slave role According to the LE SC specification the initiating device sends its DHKey check first and the non-initiating devices sends its DHKey check as a response to this. It's also important that the non-initiating device doesn't send the response if it's still waiting for user input. In order to synchronize all this a new flag is added. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 8cfa1c1..9a4ce45 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -56,6 +56,7 @@ enum { SMP_FLAG_REMOTE_PK, SMP_FLAG_DEBUG_KEY, SMP_FLAG_WAIT_USER, + SMP_FLAG_DHKEY_PENDING, }; struct smp_chan { @@ -994,6 +995,29 @@ static void smp_notify_keys(struct l2cap_conn *conn) } } +static void sc_add_ltk(struct smp_chan *smp) +{ + struct hci_conn *hcon = smp->conn->hcon; + u8 key_type, auth; + + if (test_bit(SMP_FLAG_DEBUG_KEY, &smp->flags)) + key_type = SMP_LTK_P256_DEBUG; + else + key_type = SMP_LTK_P256; + + if (hcon->pending_sec_level == BT_SECURITY_FIPS) + auth = 1; + else + auth = 0; + + memset(smp->tk + smp->enc_key_size, 0, + SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); + + smp->ltk = hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, + key_type, auth, smp->tk, smp->enc_key_size, + 0, 0); +} + static void sc_generate_link_key(struct smp_chan *smp) { /* These constants are as specified in the core specification. @@ -1312,12 +1336,10 @@ static u8 sc_passkey_round(struct smp_chan *smp, u8 smp_op) if (!hcon->out) { smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd), smp->prnd); - if (smp->passkey_round == 20) { - sc_dhkey_check(smp); + if (smp->passkey_round == 20) SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK); - } else { + else SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM); - } return 0; } @@ -1394,7 +1416,14 @@ static int sc_user_reply(struct smp_chan *smp, u16 mgmt_op, __le32 passkey) return 0; } - sc_dhkey_check(smp); + /* Initiator sends DHKey check first */ + if (hcon->out) { + sc_dhkey_check(smp); + SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK); + } else if (test_and_clear_bit(SMP_FLAG_DHKEY_PENDING, &smp->flags)) { + sc_dhkey_check(smp); + sc_add_ltk(smp); + } return 0; } @@ -2262,7 +2291,6 @@ static int smp_cmd_dhkey_check(struct l2cap_conn *conn, struct sk_buff *skb) struct smp_chan *smp = chan->data; u8 a[7], b[7], *local_addr, *remote_addr; u8 io_cap[3], r[16], e[16]; - u8 key_type, auth; int err; BT_DBG("conn %p", conn); @@ -2298,19 +2326,17 @@ static int smp_cmd_dhkey_check(struct l2cap_conn *conn, struct sk_buff *skb) if (memcmp(check->e, e, 16)) return SMP_DHKEY_CHECK_FAILED; - if (test_bit(SMP_FLAG_DEBUG_KEY, &smp->flags)) - key_type = SMP_LTK_P256_DEBUG; - else - key_type = SMP_LTK_P256; + if (!hcon->out) { + if (test_bit(SMP_FLAG_WAIT_USER, &smp->flags)) { + set_bit(SMP_FLAG_DHKEY_PENDING, &smp->flags); + return 0; + } - if (hcon->pending_sec_level == BT_SECURITY_FIPS) - auth = 1; - else - auth = 0; + /* Slave sends DHKey check as response to master */ + sc_dhkey_check(smp); + } - smp->ltk = hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, - key_type, auth, smp->tk, smp->enc_key_size, - 0, 0); + sc_add_ltk(smp); if (hcon->out) { hci_le_start_enc(hcon, 0, 0, smp->tk); -- cgit v0.10.2 From 1408bb6efb04da3efdc998df1bc032c1d86d1842 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 4 Jun 2014 22:45:57 +0300 Subject: Bluetooth: Add dummy handler for LE SC keypress notification Since we don not actively try to clear the keypress notification bit we might get these PDUs. To avoid failing the pairing process add a simple dummy handler for these for now. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 9a4ce45..918ce03 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -2346,6 +2346,16 @@ static int smp_cmd_dhkey_check(struct l2cap_conn *conn, struct sk_buff *skb) return 0; } +static int smp_cmd_keypress_notify(struct l2cap_conn *conn, + struct sk_buff *skb) +{ + struct smp_cmd_keypress_notify *kp = (void *) skb->data; + + BT_DBG("value 0x%02x", kp->value); + + return 0; +} + static int smp_sig_channel(struct l2cap_chan *chan, struct sk_buff *skb) { struct l2cap_conn *conn = chan->conn; @@ -2438,6 +2448,10 @@ static int smp_sig_channel(struct l2cap_chan *chan, struct sk_buff *skb) reason = smp_cmd_dhkey_check(conn, skb); break; + case SMP_CMD_KEYPRESS_NOTIFY: + reason = smp_cmd_keypress_notify(conn, skb); + break; + default: BT_DBG("Unknown command code 0x%2.2x", code); reason = SMP_CMD_NOTSUPP; -- cgit v0.10.2 From 70157ef5391819a55bb4186c901ac873231fb6ea Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 24 Jun 2014 15:22:59 +0300 Subject: Bluetooth: Use debug keys for SMP when HCI_USE_DEBUG_KEYS is set The HCI_USE_DEBUG_KEYS flag is intended to force our side to always use debug keys for pairing. This means both BR/EDR SSP as well as SMP with LE Secure Connections. This patch updates the SMP code to use the debug keys instead of generating a random local key pair when the flag is set. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 918ce03..7791604 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1585,18 +1585,27 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) static u8 sc_send_public_key(struct smp_chan *smp) { + struct hci_dev *hdev = smp->conn->hcon->hdev; + BT_DBG(""); - while (true) { - /* Generate local key pair for Secure Connections */ - if (!ecc_make_key(smp->local_pk, smp->local_sk)) - return SMP_UNSPECIFIED; + if (test_bit(HCI_USE_DEBUG_KEYS, &hdev->dev_flags)) { + BT_DBG("Using debug keys"); + memcpy(smp->local_pk, debug_pk, 64); + memcpy(smp->local_sk, debug_sk, 32); + set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags); + } else { + while (true) { + /* Generate local key pair for Secure Connections */ + if (!ecc_make_key(smp->local_pk, smp->local_sk)) + return SMP_UNSPECIFIED; - /* This is unlikely, but we need to check that we didn't - * accidentially generate a debug key. - */ - if (memcmp(smp->local_sk, debug_sk, 32)) - break; + /* This is unlikely, but we need to check that + * we didn't accidentially generate a debug key. + */ + if (memcmp(smp->local_sk, debug_sk, 32)) + break; + } } BT_DBG("Local Public Key X: %32phN", smp->local_pk); -- cgit v0.10.2 From fe8bc5ac67a36b05c4ec91643736d44373096a76 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 14 Aug 2014 12:33:17 +0300 Subject: Bluetooth: Add hci_conn flag for new link key generation For LE Secure Connections we want to trigger cross transport key generation only if a new link key was actually created during the BR/EDR connection. This patch adds a new flag to track this information. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 9092964..42f9362 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -559,6 +559,7 @@ enum { HCI_CONN_AUTH_INITIATOR, HCI_CONN_DROP, HCI_CONN_PARAM_REMOVAL_PEND, + HCI_CONN_NEW_LINK_KEY, }; static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 34ecbf0..c3d6390 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3249,6 +3249,8 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb) conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); if (conn) { + clear_bit(HCI_CONN_NEW_LINK_KEY, &conn->flags); + if ((key->type == HCI_LK_UNAUTH_COMBINATION_P192 || key->type == HCI_LK_UNAUTH_COMBINATION_P256) && conn->auth_type != 0xff && (conn->auth_type & 0x01)) { @@ -3301,6 +3303,7 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) conn->disc_timeout = HCI_DISCONN_TIMEOUT; hci_conn_drop(conn); + set_bit(HCI_CONN_NEW_LINK_KEY, &conn->flags); conn_set_key(conn, ev->key_type, conn->pin_length); if (!test_bit(HCI_MGMT, &hdev->dev_flags)) -- cgit v0.10.2 From 858cdc78be8be19b7176cafe06d3db3acfb345d4 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 16 Oct 2014 10:45:31 +0200 Subject: Bluetooth: Add debugfs switch for forcing SMP over BR/EDR To make it possible to use LE SC functionality over BR/EDR with pre-4.1 controllers (that do not support BR/EDR SC links) it's useful to be able to force LE SC operations even over a traditional SSP protected link. This patch adds a debugfs switch to force a special debug flag which is used to skip the checks for BR/EDR SC support. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index e56f909..569c077 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -163,6 +163,7 @@ enum { enum { HCI_DUT_MODE, HCI_FORCE_SC, + HCI_FORCE_LESC, HCI_FORCE_STATIC_ADDR, }; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 2fa9f2b..581e13e 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -406,6 +406,49 @@ static const struct file_operations force_sc_support_fops = { .llseek = default_llseek, }; +static ssize_t force_lesc_support_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + char buf[3]; + + buf[0] = test_bit(HCI_FORCE_LESC, &hdev->dbg_flags) ? 'Y': 'N'; + buf[1] = '\n'; + buf[2] = '\0'; + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static ssize_t force_lesc_support_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + char buf[32]; + size_t buf_size = min(count, (sizeof(buf)-1)); + bool enable; + + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + buf[buf_size] = '\0'; + if (strtobool(buf, &enable)) + return -EINVAL; + + if (enable == test_bit(HCI_FORCE_LESC, &hdev->dbg_flags)) + return -EALREADY; + + change_bit(HCI_FORCE_LESC, &hdev->dbg_flags); + + return count; +} + +static const struct file_operations force_lesc_support_fops = { + .open = simple_open, + .read = force_lesc_support_read, + .write = force_lesc_support_write, + .llseek = default_llseek, +}; + static ssize_t sc_only_mode_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -1817,6 +1860,10 @@ static int __hci_init(struct hci_dev *hdev) hdev, &force_sc_support_fops); debugfs_create_file("sc_only_mode", 0444, hdev->debugfs, hdev, &sc_only_mode_fops); + if (lmp_le_capable(hdev)) + debugfs_create_file("force_lesc_support", 0644, + hdev->debugfs, hdev, + &force_lesc_support_fops); } if (lmp_sniff_capable(hdev)) { -- cgit v0.10.2 From ef8efe4bf8b5fe1a9342ae964c428aed1be7863b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 13 Aug 2014 15:12:32 +0300 Subject: Bluetooth: Add skeleton for BR/EDR SMP channel This patch adds the very basic code for creating and destroying SMP L2CAP channels for BR/EDR connections. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 42f9362..f39e650 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -306,6 +306,7 @@ struct hci_dev { __u32 req_result; void *smp_data; + void *smp_bredr_data; struct discovery_state discovery; struct hci_conn_hash conn_hash; diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index d71dc35..eee3ef5 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -141,6 +141,7 @@ struct l2cap_conninfo { #define L2CAP_FC_ATT 0x10 #define L2CAP_FC_SIG_LE 0x20 #define L2CAP_FC_SMP_LE 0x40 +#define L2CAP_FC_SMP_BREDR 0x80 /* L2CAP Control Field bit masks */ #define L2CAP_CTRL_SAR 0xC000 @@ -255,6 +256,7 @@ struct l2cap_conn_rsp { #define L2CAP_CID_ATT 0x0004 #define L2CAP_CID_LE_SIGNALING 0x0005 #define L2CAP_CID_SMP 0x0006 +#define L2CAP_CID_SMP_BREDR 0x0007 #define L2CAP_CID_DYN_START 0x0040 #define L2CAP_CID_DYN_END 0xffff #define L2CAP_CID_LE_DYN_END 0x007f diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 7791604..135e725 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -2504,6 +2504,9 @@ static void smp_resume_cb(struct l2cap_chan *chan) BT_DBG("chan %p", chan); + if (hcon->type == ACL_LINK) + return; + if (!smp) return; @@ -2527,10 +2530,14 @@ static void smp_ready_cb(struct l2cap_chan *chan) static int smp_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) { + struct hci_conn *hcon = chan->conn->hcon; int err; BT_DBG("chan %p", chan); + if (hcon->type == ACL_LINK) + return -EOPNOTSUPP; + err = smp_sig_channel(chan, skb); if (err) { struct smp_chan *smp = chan->data; @@ -2627,34 +2634,40 @@ static const struct l2cap_ops smp_root_chan_ops = { .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec, }; -int smp_register(struct hci_dev *hdev) +static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid) { struct l2cap_chan *chan; struct crypto_blkcipher *tfm_aes; - BT_DBG("%s", hdev->name); + if (cid == L2CAP_CID_SMP_BREDR) { + tfm_aes = NULL; + goto create_chan; + } tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, 0); if (IS_ERR(tfm_aes)) { - int err = PTR_ERR(tfm_aes); BT_ERR("Unable to create crypto context"); - return err; + return ERR_PTR(PTR_ERR(tfm_aes)); } +create_chan: chan = l2cap_chan_create(); if (!chan) { crypto_free_blkcipher(tfm_aes); - return -ENOMEM; + return ERR_PTR(-ENOMEM); } chan->data = tfm_aes; - l2cap_add_scid(chan, L2CAP_CID_SMP); + l2cap_add_scid(chan, cid); l2cap_chan_set_defaults(chan); bacpy(&chan->src, &hdev->bdaddr); - chan->src_type = BDADDR_LE_PUBLIC; + if (cid == L2CAP_CID_SMP) + chan->src_type = BDADDR_LE_PUBLIC; + else + chan->src_type = BDADDR_BREDR; chan->state = BT_LISTEN; chan->mode = L2CAP_MODE_BASIC; chan->imtu = L2CAP_DEFAULT_MTU; @@ -2663,20 +2676,14 @@ int smp_register(struct hci_dev *hdev) /* Set correct nesting level for a parent/listening channel */ atomic_set(&chan->nesting, L2CAP_NESTING_PARENT); - hdev->smp_data = chan; - - return 0; + return chan; } -void smp_unregister(struct hci_dev *hdev) +static void smp_del_chan(struct l2cap_chan *chan) { - struct l2cap_chan *chan = hdev->smp_data; - struct crypto_blkcipher *tfm_aes; - - if (!chan) - return; + struct crypto_blkcipher *tfm_aes; - BT_DBG("%s chan %p", hdev->name, chan); + BT_DBG("chan %p", chan); tfm_aes = chan->data; if (tfm_aes) { @@ -2684,6 +2691,52 @@ void smp_unregister(struct hci_dev *hdev) crypto_free_blkcipher(tfm_aes); } - hdev->smp_data = NULL; l2cap_chan_put(chan); } + +int smp_register(struct hci_dev *hdev) +{ + struct l2cap_chan *chan; + + BT_DBG("%s", hdev->name); + + chan = smp_add_cid(hdev, L2CAP_CID_SMP); + if (IS_ERR(chan)) + return PTR_ERR(chan); + + hdev->smp_data = chan; + + if (!lmp_sc_capable(hdev) && + !test_bit(HCI_FORCE_LESC, &hdev->dbg_flags)) + return 0; + + chan = smp_add_cid(hdev, L2CAP_CID_SMP_BREDR); + if (IS_ERR(chan)) { + int err = PTR_ERR(chan); + chan = hdev->smp_data; + hdev->smp_data = NULL; + smp_del_chan(chan); + return err; + } + + hdev->smp_bredr_data = chan; + + return 0; +} + +void smp_unregister(struct hci_dev *hdev) +{ + struct l2cap_chan *chan; + + if (hdev->smp_bredr_data) { + chan = hdev->smp_bredr_data; + hdev->smp_bredr_data = NULL; + smp_del_chan(chan); + } + + if (hdev->smp_data) { + chan = hdev->smp_data; + hdev->smp_data = NULL; + smp_del_chan(chan); + } +} -- cgit v0.10.2 From b5ae344d4c0f69ae6526565b55d52b64bc127087 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 14 Aug 2014 12:34:26 +0300 Subject: Bluetooth: Add full SMP BR/EDR support When doing SMP over BR/EDR some of the routines can be shared with the LE functionality whereas others needs to be split into their own BR/EDR specific branches. This patch implements the split of BR/EDR specific SMP code from the LE-only code, making sure SMP over BR/EDR works as specified. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 43349ed..a8da7ea 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -6967,6 +6967,10 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon) test_bit(HCI_HS_ENABLED, &hcon->hdev->dev_flags)) conn->local_fixed_chan |= L2CAP_FC_A2MP; + if (bredr_sc_enabled(hcon->hdev) && + test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags)) + conn->local_fixed_chan |= L2CAP_FC_SMP_BREDR; + mutex_init(&conn->ident_lock); mutex_init(&conn->chan_lock); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 135e725..2c6edf1 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -915,11 +915,13 @@ static void smp_notify_keys(struct l2cap_conn *conn) mgmt_new_irk(hdev, smp->remote_irk); /* Now that user space can be considered to know the * identity address track the connection based on it - * from now on. + * from now on (assuming this is an LE link). */ - bacpy(&hcon->dst, &smp->remote_irk->bdaddr); - hcon->dst_type = smp->remote_irk->addr_type; - queue_work(hdev->workqueue, &conn->id_addr_update_work); + if (hcon->type == LE_LINK) { + bacpy(&hcon->dst, &smp->remote_irk->bdaddr); + hcon->dst_type = smp->remote_irk->addr_type; + queue_work(hdev->workqueue, &conn->id_addr_update_work); + } /* When receiving an indentity resolving key for * a remote device that does not use a resolvable @@ -938,10 +940,20 @@ static void smp_notify_keys(struct l2cap_conn *conn) } } - /* The LTKs and CSRKs should be persistent only if both sides - * had the bonding bit set in their authentication requests. - */ - persistent = !!((req->auth_req & rsp->auth_req) & SMP_AUTH_BONDING); + if (hcon->type == ACL_LINK) { + if (hcon->key_type == HCI_LK_DEBUG_COMBINATION) + persistent = false; + else + persistent = !test_bit(HCI_CONN_FLUSH_KEY, + &hcon->flags); + } else { + /* The LTKs and CSRKs should be persistent only if both sides + * had the bonding bit set in their authentication requests. + */ + persistent = !!((req->auth_req & rsp->auth_req) & + SMP_AUTH_BONDING); + } + if (smp->csrk) { smp->csrk->bdaddr_type = hcon->dst_type; @@ -1057,6 +1069,35 @@ static void smp_allow_key_dist(struct smp_chan *smp) SMP_ALLOW_CMD(smp, SMP_CMD_SIGN_INFO); } +static void sc_generate_ltk(struct smp_chan *smp) +{ + /* These constants are as specified in the core specification. + * In ASCII they spell out to 'tmp2' and 'brle'. + */ + const u8 tmp2[4] = { 0x32, 0x70, 0x6d, 0x74 }; + const u8 brle[4] = { 0x65, 0x6c, 0x72, 0x62 }; + struct hci_conn *hcon = smp->conn->hcon; + struct hci_dev *hdev = hcon->hdev; + struct link_key *key; + + key = hci_find_link_key(hdev, &hcon->dst); + if (!key) { + BT_ERR("%s No Link Key found to generate LTK", hdev->name); + return; + } + + if (key->type == HCI_LK_DEBUG_COMBINATION) + set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags); + + if (smp_h6(smp->tfm_cmac, key->val, tmp2, smp->tk)) + return; + + if (smp_h6(smp->tfm_cmac, smp->tk, brle, smp->tk)) + return; + + sc_add_ltk(smp); +} + static void smp_distribute_keys(struct smp_chan *smp) { struct smp_cmd_pairing *req, *rsp; @@ -1086,8 +1127,10 @@ static void smp_distribute_keys(struct smp_chan *smp) } if (test_bit(SMP_FLAG_SC, &smp->flags)) { - if (*keydist & SMP_DIST_LINK_KEY) + if (hcon->type == LE_LINK && (*keydist & SMP_DIST_LINK_KEY)) sc_generate_link_key(smp); + if (hcon->type == ACL_LINK && (*keydist & SMP_DIST_ENC_KEY)) + sc_generate_ltk(smp); /* Clear the keys which are generated but not distributed */ *keydist &= ~SMP_SC_NO_DIST; @@ -1493,6 +1536,46 @@ unlock: return err; } +static void build_bredr_pairing_cmd(struct smp_chan *smp, + struct smp_cmd_pairing *req, + struct smp_cmd_pairing *rsp) +{ + struct l2cap_conn *conn = smp->conn; + struct hci_dev *hdev = conn->hcon->hdev; + u8 local_dist = 0, remote_dist = 0; + + if (test_bit(HCI_BONDABLE, &hdev->dev_flags)) { + local_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN; + remote_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN; + } + + if (test_bit(HCI_RPA_RESOLVING, &hdev->dev_flags)) + remote_dist |= SMP_DIST_ID_KEY; + + if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) + local_dist |= SMP_DIST_ID_KEY; + + if (!rsp) { + memset(req, 0, sizeof(*req)); + + req->init_key_dist = local_dist; + req->resp_key_dist = remote_dist; + req->max_key_size = SMP_MAX_ENC_KEY_SIZE; + + smp->remote_key_dist = remote_dist; + + return; + } + + memset(rsp, 0, sizeof(*rsp)); + + rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE; + rsp->init_key_dist = req->init_key_dist & remote_dist; + rsp->resp_key_dist = req->resp_key_dist & local_dist; + + smp->remote_key_dist = rsp->init_key_dist; +} + static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing rsp, *req = (void *) skb->data; @@ -1529,6 +1612,31 @@ 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)); + /* SMP over BR/EDR requires special treatment */ + if (conn->hcon->type == ACL_LINK) { + /* We must have a BR/EDR SC link */ + if (!test_bit(HCI_CONN_AES_CCM, &conn->hcon->flags)) + return SMP_CROSS_TRANSP_NOT_ALLOWED; + + set_bit(SMP_FLAG_SC, &smp->flags); + + build_bredr_pairing_cmd(smp, req, &rsp); + + key_size = min(req->max_key_size, rsp.max_key_size); + if (check_enc_key_size(conn, key_size)) + return SMP_ENC_KEY_SIZE; + + /* Clear bits which are generated but not distributed */ + smp->remote_key_dist &= ~SMP_SC_NO_DIST; + + smp->prsp[0] = SMP_CMD_PAIRING_RSP; + memcpy(&smp->prsp[1], &rsp, sizeof(rsp)); + smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp); + + smp_distribute_keys(smp); + return 0; + } + build_pairing_cmd(conn, req, &rsp, auth); if (rsp.auth_req & SMP_AUTH_SC) @@ -1644,6 +1752,22 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) auth = rsp->auth_req & AUTH_REQ_MASK(hdev); + smp->prsp[0] = SMP_CMD_PAIRING_RSP; + memcpy(&smp->prsp[1], rsp, sizeof(*rsp)); + + /* Update remote key distribution in case the remote cleared + * some bits that we had enabled in our request. + */ + smp->remote_key_dist &= rsp->resp_key_dist; + + /* For BR/EDR this means we're done and can start phase 3 */ + if (conn->hcon->type == ACL_LINK) { + /* Clear bits which are generated but not distributed */ + smp->remote_key_dist &= ~SMP_SC_NO_DIST; + smp_distribute_keys(smp); + return 0; + } + if ((req->auth_req & SMP_AUTH_SC) && (auth & SMP_AUTH_SC)) set_bit(SMP_FLAG_SC, &smp->flags); else if (conn->hcon->pending_sec_level > BT_SECURITY_HIGH) @@ -1661,9 +1785,6 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) get_random_bytes(smp->prnd, sizeof(smp->prnd)); - smp->prsp[0] = SMP_CMD_PAIRING_RSP; - memcpy(&smp->prsp[1], rsp, sizeof(*rsp)); - /* Update remote key distribution in case the remote cleared * some bits that we had enabled in our request. */ @@ -2373,11 +2494,6 @@ static int smp_sig_channel(struct l2cap_chan *chan, struct sk_buff *skb) __u8 code, reason; int err = 0; - if (hcon->type != LE_LINK) { - kfree_skb(skb); - return 0; - } - if (skb->len < 1) return -EILSEQ; @@ -2496,6 +2612,74 @@ static void smp_teardown_cb(struct l2cap_chan *chan, int err) l2cap_chan_put(chan); } +static void bredr_pairing(struct l2cap_chan *chan) +{ + struct l2cap_conn *conn = chan->conn; + struct hci_conn *hcon = conn->hcon; + struct hci_dev *hdev = hcon->hdev; + struct smp_cmd_pairing req; + struct smp_chan *smp; + + BT_DBG("chan %p", chan); + + /* Only new pairings are interesting */ + if (!test_bit(HCI_CONN_NEW_LINK_KEY, &hcon->flags)) + return; + + /* Don't bother if we're not encrypted */ + if (!test_bit(HCI_CONN_ENCRYPT, &hcon->flags)) + return; + + /* Only master may initiate SMP over BR/EDR */ + if (hcon->role != HCI_ROLE_MASTER) + return; + + /* Secure Connections support must be enabled */ + if (!test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) + return; + + /* BR/EDR must use Secure Connections for SMP */ + if (!test_bit(HCI_CONN_AES_CCM, &hcon->flags) && + !test_bit(HCI_FORCE_LESC, &hdev->dbg_flags)) + return; + + /* If our LE support is not enabled don't do anything */ + if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) + return; + + /* Don't bother if remote LE support is not enabled */ + if (!lmp_host_le_capable(hcon)) + return; + + /* Remote must support SMP fixed chan for BR/EDR */ + if (!(conn->remote_fixed_chan & L2CAP_FC_SMP_BREDR)) + return; + + /* Don't bother if SMP is already ongoing */ + if (chan->data) + return; + + smp = smp_chan_create(conn); + if (!smp) { + BT_ERR("%s unable to create SMP context for BR/EDR", + hdev->name); + return; + } + + set_bit(SMP_FLAG_SC, &smp->flags); + + BT_DBG("%s starting SMP over BR/EDR", hdev->name); + + /* Prepare and send the BR/EDR SMP Pairing Request */ + build_bredr_pairing_cmd(smp, &req, NULL); + + smp->preq[0] = SMP_CMD_PAIRING_REQ; + memcpy(&smp->preq[1], &req, sizeof(req)); + + smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(req), &req); + SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RSP); +} + static void smp_resume_cb(struct l2cap_chan *chan) { struct smp_chan *smp = chan->data; @@ -2504,8 +2688,10 @@ static void smp_resume_cb(struct l2cap_chan *chan) BT_DBG("chan %p", chan); - if (hcon->type == ACL_LINK) + if (hcon->type == ACL_LINK) { + bredr_pairing(chan); return; + } if (!smp) return; @@ -2521,23 +2707,23 @@ static void smp_resume_cb(struct l2cap_chan *chan) static void smp_ready_cb(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; + struct hci_conn *hcon = conn->hcon; BT_DBG("chan %p", chan); conn->smp = chan; l2cap_chan_hold(chan); + + if (hcon->type == ACL_LINK && test_bit(HCI_CONN_ENCRYPT, &hcon->flags)) + bredr_pairing(chan); } static int smp_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) { - struct hci_conn *hcon = chan->conn->hcon; int err; BT_DBG("chan %p", chan); - if (hcon->type == ACL_LINK) - return -EOPNOTSUPP; - err = smp_sig_channel(chan, skb); if (err) { struct smp_chan *smp = chan->data; -- cgit v0.10.2 From 903b71c78d56af56a5f4d53a8dbef8032d1949bf Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 8 Sep 2014 16:59:18 -0700 Subject: Bluetooth: Add SC-only mode support for SMP When Secure Connections-only mode is enabled we should reject any pairing command that does not have Secure Connections set in the authentication requirements. This patch adds the appropriate logic for this to the command handlers of Pairing Request/Response and Security Request. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 2c6edf1..589e015 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1608,6 +1608,9 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) (auth & SMP_AUTH_BONDING)) return SMP_PAIRING_NOTSUPP; + if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) && !(auth & SMP_AUTH_SC)) + return SMP_AUTH_REQUIREMENTS; + smp->preq[0] = SMP_CMD_PAIRING_REQ; memcpy(&smp->preq[1], req, sizeof(*req)); skb_pull(skb, sizeof(*req)); @@ -1752,6 +1755,9 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) auth = rsp->auth_req & AUTH_REQ_MASK(hdev); + if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) && !(auth & SMP_AUTH_SC)) + return SMP_AUTH_REQUIREMENTS; + smp->prsp[0] = SMP_CMD_PAIRING_RSP; memcpy(&smp->prsp[1], rsp, sizeof(*rsp)); @@ -2008,6 +2014,9 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) auth = rp->auth_req & AUTH_REQ_MASK(hdev); + if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) && !(auth & SMP_AUTH_SC)) + return SMP_AUTH_REQUIREMENTS; + if (hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT) sec_level = BT_SECURITY_MEDIUM; else -- cgit v0.10.2 From 81328d5cca7e1cff6296a63a3c1b671d09ddb3ee Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 26 Oct 2014 20:33:47 +0100 Subject: Bluetooth: Unify remote OOB data functions There's no need to duplicate code for the 192 vs 192+256 variants of the OOB data functions. This is also helpful to pave the way to support LE SC OOB data where only 256 bit data is provided. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index f39e650..3e89ece 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -944,10 +944,8 @@ void hci_remote_oob_data_clear(struct hci_dev *hdev); struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 *hash, u8 *rand); -int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 *hash192, u8 *rand192, - u8 *hash256, u8 *rand256); + u8 *hash192, u8 *rand192, + u8 *hash256, u8 *rand256); int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr); void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 581e13e..967fbfe 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3517,7 +3517,8 @@ void hci_remote_oob_data_clear(struct hci_dev *hdev) } int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 *hash, u8 *rand) + u8 *hash192, u8 *rand192, + u8 *hash256, u8 *rand256) { struct oob_data *data; @@ -3531,38 +3532,21 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, list_add(&data->list, &hdev->remote_oob_data); } - memcpy(data->hash192, hash, sizeof(data->hash192)); - memcpy(data->rand192, rand, sizeof(data->rand192)); - - memset(data->hash256, 0, sizeof(data->hash256)); - memset(data->rand256, 0, sizeof(data->rand256)); - - BT_DBG("%s for %pMR", hdev->name, bdaddr); - - return 0; -} - -int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 *hash192, u8 *rand192, - u8 *hash256, u8 *rand256) -{ - struct oob_data *data; - - data = hci_find_remote_oob_data(hdev, bdaddr); - if (!data) { - data = kmalloc(sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - bacpy(&data->bdaddr, bdaddr); - list_add(&data->list, &hdev->remote_oob_data); + if (hash192 && rand192) { + memcpy(data->hash192, hash192, sizeof(data->hash192)); + memcpy(data->rand192, rand192, sizeof(data->rand192)); + } else { + memset(data->hash192, 0, sizeof(data->hash192)); + memset(data->rand192, 0, sizeof(data->rand192)); } - memcpy(data->hash192, hash192, sizeof(data->hash192)); - memcpy(data->rand192, rand192, sizeof(data->rand192)); - - memcpy(data->hash256, hash256, sizeof(data->hash256)); - memcpy(data->rand256, rand256, sizeof(data->rand256)); + if (hash256 && rand256) { + memcpy(data->hash256, hash256, sizeof(data->hash256)); + memcpy(data->rand256, rand256, sizeof(data->rand256)); + } else { + memset(data->hash256, 0, sizeof(data->hash256)); + memset(data->rand256, 0, sizeof(data->rand256)); + } BT_DBG("%s for %pMR", hdev->name, bdaddr); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 6e81653..0d92ba9 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3599,7 +3599,8 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, } err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, - cp->hash, cp->rand); + cp->hash, cp->rand, + NULL, NULL); if (err < 0) status = MGMT_STATUS_FAILED; else @@ -3619,9 +3620,9 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, goto unlock; } - err = hci_add_remote_oob_ext_data(hdev, &cp->addr.bdaddr, - cp->hash192, cp->rand192, - cp->hash256, cp->rand256); + err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, + cp->hash192, cp->rand192, + cp->hash256, cp->rand256); if (err < 0) status = MGMT_STATUS_FAILED; else -- cgit v0.10.2 From 6928a9245f2998478047dcc3efad30734766a226 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 26 Oct 2014 20:46:09 +0100 Subject: Bluetooth: Store address type with OOB data To be able to support OOB data for LE pairing we need to store the address type of the remote device. This patch extends the relevant functions and data types with a bdaddr_type variable. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3e89ece..1dae700 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -140,6 +140,7 @@ struct link_key { struct oob_data { struct list_head list; bdaddr_t bdaddr; + u8 bdaddr_type; u8 hash192[16]; u8 rand192[16]; u8 hash256[16]; @@ -942,11 +943,12 @@ void hci_smp_irks_clear(struct hci_dev *hdev); void hci_remote_oob_data_clear(struct hci_dev *hdev); struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev, - bdaddr_t *bdaddr); + bdaddr_t *bdaddr, u8 bdaddr_type); int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 *hash192, u8 *rand192, + u8 bdaddr_type, u8 *hash192, u8 *rand192, u8 *hash256, u8 *rand256); -int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr); +int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 bdaddr_type); void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 967fbfe..c8123f0 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2160,7 +2160,7 @@ u32 hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, BT_DBG("cache %p, %pMR", cache, &data->bdaddr); - hci_remove_remote_oob_data(hdev, &data->bdaddr); + hci_remove_remote_oob_data(hdev, &data->bdaddr, BDADDR_BREDR); if (!data->ssp_mode) flags |= MGMT_DEV_FOUND_LEGACY_PAIRING; @@ -3479,26 +3479,31 @@ static void hci_cmd_timeout(struct work_struct *work) } struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev, - bdaddr_t *bdaddr) + bdaddr_t *bdaddr, u8 bdaddr_type) { struct oob_data *data; - list_for_each_entry(data, &hdev->remote_oob_data, list) - if (bacmp(bdaddr, &data->bdaddr) == 0) - return data; + list_for_each_entry(data, &hdev->remote_oob_data, list) { + if (bacmp(bdaddr, &data->bdaddr) != 0) + continue; + if (data->bdaddr_type != bdaddr_type) + continue; + return data; + } return NULL; } -int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr) +int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 bdaddr_type) { struct oob_data *data; - data = hci_find_remote_oob_data(hdev, bdaddr); + data = hci_find_remote_oob_data(hdev, bdaddr, bdaddr_type); if (!data) return -ENOENT; - BT_DBG("%s removing %pMR", hdev->name, bdaddr); + BT_DBG("%s removing %pMR (%u)", hdev->name, bdaddr, bdaddr_type); list_del(&data->list); kfree(data); @@ -3517,18 +3522,19 @@ void hci_remote_oob_data_clear(struct hci_dev *hdev) } int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 *hash192, u8 *rand192, + u8 bdaddr_type, u8 *hash192, u8 *rand192, u8 *hash256, u8 *rand256) { struct oob_data *data; - data = hci_find_remote_oob_data(hdev, bdaddr); + data = hci_find_remote_oob_data(hdev, bdaddr, bdaddr_type); if (!data) { data = kmalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; bacpy(&data->bdaddr, bdaddr); + data->bdaddr_type = bdaddr_type; list_add(&data->list, &hdev->remote_oob_data); } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index c3d6390..f4e2a61 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3773,7 +3773,7 @@ static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb) cp.authentication = conn->auth_type; - if (hci_find_remote_oob_data(hdev, &conn->dst) && + if (hci_find_remote_oob_data(hdev, &conn->dst, BDADDR_BREDR) && (conn->out || test_bit(HCI_CONN_REMOTE_OOB, &conn->flags))) cp.oob_data = 0x01; else @@ -4028,7 +4028,7 @@ static void hci_remote_oob_data_request_evt(struct hci_dev *hdev, if (!test_bit(HCI_MGMT, &hdev->dev_flags)) goto unlock; - data = hci_find_remote_oob_data(hdev, &ev->bdaddr); + data = hci_find_remote_oob_data(hdev, &ev->bdaddr, BDADDR_BREDR); if (data) { if (bredr_sc_enabled(hdev)) { struct hci_cp_remote_oob_ext_data_reply cp; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 0d92ba9..57de9f7 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3599,8 +3599,8 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, } err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, - cp->hash, cp->rand, - NULL, NULL); + cp->addr.type, cp->hash, + cp->rand, NULL, NULL); if (err < 0) status = MGMT_STATUS_FAILED; else @@ -3621,8 +3621,9 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, } err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, - cp->hash192, cp->rand192, - cp->hash256, cp->rand256); + cp->addr.type, cp->hash192, + cp->rand192, cp->hash256, + cp->rand256); if (err < 0) status = MGMT_STATUS_FAILED; else @@ -3663,7 +3664,7 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev, goto done; } - err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr); + err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr, cp->addr.type); if (err < 0) status = MGMT_STATUS_INVALID_PARAMS; else -- cgit v0.10.2 From 86df9200c77f46a246ca4a78949887eb6dbde091 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 26 Oct 2014 20:52:27 +0100 Subject: Bluetooth: Add support for adding remote OOB data for LE This patch adds proper support for passing LE OOB data to the hci_add_remote_oob_data() function. For LE the 192-bit values are not valid and should therefore be passed as NULL values. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 57de9f7..1e73bea 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3610,6 +3610,7 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, status, &cp->addr, sizeof(cp->addr)); } else if (len == MGMT_ADD_REMOTE_OOB_EXT_DATA_SIZE) { struct mgmt_cp_add_remote_oob_ext_data *cp = data; + u8 *rand192, *hash192; u8 status; if (cp->addr.type != BDADDR_BREDR) { @@ -3620,10 +3621,17 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, goto unlock; } + if (bdaddr_type_is_le(cp->addr.type)) { + rand192 = NULL; + hash192 = NULL; + } else { + rand192 = cp->rand192; + hash192 = cp->hash192; + } + err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, - cp->addr.type, cp->hash192, - cp->rand192, cp->hash256, - cp->rand256); + cp->addr.type, hash192, rand192, + cp->hash256, cp->rand256); if (err < 0) status = MGMT_STATUS_FAILED; else -- cgit v0.10.2 From 02b05bd8b0a632e9a26795bced8f19ca6a5f7079 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 26 Oct 2014 21:19:10 +0100 Subject: Bluetooth: Set SMP OOB flag if OOB data is available If we have OOB data available for the remote device in question we should set the OOB flag appropriately in the SMP pairing request or response. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 589e015..0c2214a 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -57,6 +57,7 @@ enum { SMP_FLAG_DEBUG_KEY, SMP_FLAG_WAIT_USER, SMP_FLAG_DHKEY_PENDING, + SMP_FLAG_OOB, }; struct smp_chan { @@ -562,7 +563,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn, struct smp_chan *smp = chan->data; struct hci_conn *hcon = conn->hcon; struct hci_dev *hdev = hcon->hdev; - u8 local_dist = 0, remote_dist = 0; + u8 local_dist = 0, remote_dist = 0, oob_flag = SMP_OOB_NOT_PRESENT; if (test_bit(HCI_BONDABLE, &conn->hcon->hdev->dev_flags)) { local_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN; @@ -578,19 +579,37 @@ static void build_pairing_cmd(struct l2cap_conn *conn, if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) local_dist |= SMP_DIST_ID_KEY; - if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) { - if ((authreq & SMP_AUTH_SC) && - test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { + if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags) && + (authreq & SMP_AUTH_SC)) { + struct oob_data *oob_data; + u8 bdaddr_type; + + if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { local_dist |= SMP_DIST_LINK_KEY; remote_dist |= SMP_DIST_LINK_KEY; } + + if (hcon->dst_type == ADDR_LE_DEV_PUBLIC) + bdaddr_type = BDADDR_LE_PUBLIC; + else + bdaddr_type = BDADDR_LE_RANDOM; + + oob_data = hci_find_remote_oob_data(hdev, &hcon->dst, + bdaddr_type); + if (oob_data) { + set_bit(SMP_FLAG_OOB, &smp->flags); + oob_flag = SMP_OOB_PRESENT; + memcpy(smp->rrnd, oob_data->rand256, 16); + memcpy(smp->pcnf, oob_data->hash256, 16); + } + } else { authreq &= ~SMP_AUTH_SC; } if (rsp == NULL) { req->io_capability = conn->hcon->io_capability; - req->oob_flag = SMP_OOB_NOT_PRESENT; + req->oob_flag = oob_flag; req->max_key_size = SMP_MAX_ENC_KEY_SIZE; req->init_key_dist = local_dist; req->resp_key_dist = remote_dist; @@ -601,7 +620,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn, } rsp->io_capability = conn->hcon->io_capability; - rsp->oob_flag = SMP_OOB_NOT_PRESENT; + rsp->oob_flag = oob_flag; rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE; rsp->init_key_dist = req->init_key_dist & remote_dist; rsp->resp_key_dist = req->resp_key_dist & local_dist; -- cgit v0.10.2 From a29b073351ffdda0ce9745f3bd98f5a513c523d7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 28 Oct 2014 15:17:05 +0100 Subject: Bluetooth: Add basic LE SC OOB support for remote OOB data This patch adds basic OOB pairing support when we've received the remote OOB data. This includes tracking the remote r value (in smp->rr) as well as doing the appropriate f4() call when needed. Previously the OOB rand would have been stored in smp->rrnd however these are actually two independent values so we need separate variables for them. Na/Nb in the spec maps to smp->prnd/rrnd and ra/rb maps to smp->rr with smp->pr to come once local OOB data is supported. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 0c2214a..059a3da 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -71,6 +71,7 @@ struct smp_chan { u8 rrnd[16]; /* SMP Pairing Random (remote) */ u8 pcnf[16]; /* SMP Pairing Confirm */ u8 tk[16]; /* SMP Temporary Key */ + u8 rr[16]; u8 enc_key_size; u8 remote_key_dist; bdaddr_t id_addr; @@ -599,7 +600,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn, if (oob_data) { set_bit(SMP_FLAG_OOB, &smp->flags); oob_flag = SMP_OOB_PRESENT; - memcpy(smp->rrnd, oob_data->rand256, 16); + memcpy(smp->rr, oob_data->rand256, 16); memcpy(smp->pcnf, oob_data->hash256, 16); } @@ -1334,6 +1335,9 @@ static void sc_dhkey_check(struct smp_chan *smp) if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY) put_unaligned_le32(hcon->passkey_notify, r); + if (smp->method == REQ_OOB) + memcpy(r, smp->rr, 16); + smp_f6(smp->tfm_cmac, smp->mackey, smp->prnd, smp->rrnd, r, io_cap, local_addr, remote_addr, check.e); @@ -1910,6 +1914,14 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) if (!test_bit(SMP_FLAG_SC, &smp->flags)) return smp_random(smp); + if (smp->method == REQ_OOB) { + if (!hcon->out) + smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, + sizeof(smp->prnd), smp->prnd); + SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK); + goto mackey_and_ltk; + } + /* Passkey entry has special treatment */ if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY) return sc_passkey_round(smp, SMP_CMD_PAIRING_RANDOM); @@ -1940,12 +1952,13 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) nb = smp->prnd; } +mackey_and_ltk: /* Generate MacKey and LTK */ err = sc_mackey_and_ltk(smp, smp->mackey, smp->tk); if (err) return SMP_UNSPECIFIED; - if (smp->method == JUST_WORKS) { + if (smp->method == JUST_WORKS || smp->method == REQ_OOB) { if (hcon->out) { sc_dhkey_check(smp); SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK); @@ -2314,6 +2327,9 @@ static u8 sc_select_method(struct smp_chan *smp) struct smp_cmd_pairing *local, *remote; u8 local_mitm, remote_mitm, local_io, remote_io, method; + if (test_bit(SMP_FLAG_OOB, &smp->flags)) + return REQ_OOB; + /* The preq/prsp contain the raw Pairing Request/Response PDUs * which are needed as inputs to some crypto functions. To get * the "struct smp_cmd_pairing" from them we need to skip the @@ -2412,6 +2428,24 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb) return sc_passkey_round(smp, SMP_CMD_PUBLIC_KEY); } + if (smp->method == REQ_OOB) { + err = smp_f4(smp->tfm_cmac, smp->remote_pk, smp->remote_pk, + smp->rr, 0, cfm.confirm_val); + if (err) + return SMP_UNSPECIFIED; + + if (memcmp(cfm.confirm_val, smp->pcnf, 16)) + return SMP_CONFIRM_FAILED; + + if (hcon->out) + smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, + sizeof(smp->prnd), smp->prnd); + + SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM); + + return 0; + } + if (hcon->out) SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM); -- cgit v0.10.2 From c7a3d57db67ce7524c72ecb33f82c099f270a109 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 1 Dec 2014 22:03:16 +0200 Subject: Bluetooth: Introduce SMP_DBG macro for low-level debuging The various inputs & outputs of the crypto functions as well as the values of the ECDH keys can be considered security sensitive. They should therefore not end up in dmesg by mistake. This patch introduces a new SMP_DBG macro which requires explicit compilation with -DDEBUG to be enabled. All crypto related data logs now use this macro instead of BT_DBG. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 059a3da..13b40e8 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -32,6 +32,18 @@ #include "ecc.h" #include "smp.h" +/* Low-level debug macros to be used for stuff that we don't want + * accidentially in dmesg, i.e. the values of the various crypto keys + * and the inputs & outputs of crypto functions. + */ +#ifdef DEBUG +#define SMP_DBG(fmt, ...) printk(KERN_DEBUG "%s: " fmt, __func__, \ + ##__VA_ARGS__) +#else +#define SMP_DBG(fmt, ...) no_printk(KERN_DEBUG "%s: " fmt, __func__, \ + ##__VA_ARGS__) +#endif + #define SMP_ALLOW_CMD(smp, code) set_bit(code, &smp->allow_cmd) /* Keys which are not distributed with Secure Connections */ @@ -154,8 +166,8 @@ static int aes_cmac(struct crypto_hash *tfm, const u8 k[16], const u8 *m, swap_buf(k, tmp, 16); swap_buf(m, msg_msb, len); - BT_DBG("msg (len %zu) %*phN", len, (int) len, m); - BT_DBG("key %16phN", k); + SMP_DBG("msg (len %zu) %*phN", len, (int) len, m); + SMP_DBG("key %16phN", k); err = crypto_hash_setkey(tfm, tmp, 16); if (err) { @@ -179,7 +191,7 @@ static int aes_cmac(struct crypto_hash *tfm, const u8 k[16], const u8 *m, swap_buf(mac_msb, mac, 16); - BT_DBG("mac %16phN", mac); + SMP_DBG("mac %16phN", mac); return 0; } @@ -190,9 +202,9 @@ static int smp_f4(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32], u8 m[65]; int err; - BT_DBG("u %32phN", u); - BT_DBG("v %32phN", v); - BT_DBG("x %16phN z %02x", x, z); + SMP_DBG("u %32phN", u); + SMP_DBG("v %32phN", v); + SMP_DBG("x %16phN z %02x", x, z); m[0] = z; memcpy(m + 1, v, 32); @@ -202,7 +214,7 @@ static int smp_f4(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32], if (err) return err; - BT_DBG("res %16phN", res); + SMP_DBG("res %16phN", res); return err; } @@ -223,15 +235,15 @@ static int smp_f5(struct crypto_hash *tfm_cmac, u8 w[32], u8 n1[16], u8 n2[16], u8 m[53], t[16]; int err; - BT_DBG("w %32phN", w); - BT_DBG("n1 %16phN n2 %16phN", n1, n2); - BT_DBG("a1 %7phN a2 %7phN", a1, a2); + SMP_DBG("w %32phN", w); + SMP_DBG("n1 %16phN n2 %16phN", n1, n2); + SMP_DBG("a1 %7phN a2 %7phN", a1, a2); err = aes_cmac(tfm_cmac, salt, w, 32, t); if (err) return err; - BT_DBG("t %16phN", t); + SMP_DBG("t %16phN", t); memcpy(m, length, 2); memcpy(m + 2, a2, 7); @@ -246,7 +258,7 @@ static int smp_f5(struct crypto_hash *tfm_cmac, u8 w[32], u8 n1[16], u8 n2[16], if (err) return err; - BT_DBG("mackey %16phN", mackey); + SMP_DBG("mackey %16phN", mackey); m[52] = 1; /* Counter */ @@ -254,7 +266,7 @@ static int smp_f5(struct crypto_hash *tfm_cmac, u8 w[32], u8 n1[16], u8 n2[16], if (err) return err; - BT_DBG("ltk %16phN", ltk); + SMP_DBG("ltk %16phN", ltk); return 0; } @@ -267,9 +279,9 @@ static int smp_f6(struct crypto_hash *tfm_cmac, const u8 w[16], u8 m[65]; int err; - BT_DBG("w %16phN", w); - BT_DBG("n1 %16phN n2 %16phN", n1, n2); - BT_DBG("r %16phN io_cap %3phN a1 %7phN a2 %7phN", r, io_cap, a1, a2); + SMP_DBG("w %16phN", w); + SMP_DBG("n1 %16phN n2 %16phN", n1, n2); + SMP_DBG("r %16phN io_cap %3phN a1 %7phN a2 %7phN", r, io_cap, a1, a2); memcpy(m, a2, 7); memcpy(m + 7, a1, 7); @@ -293,9 +305,9 @@ static int smp_g2(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32], u8 m[80], tmp[16]; int err; - BT_DBG("u %32phN", u); - BT_DBG("v %32phN", v); - BT_DBG("x %16phN y %16phN", x, y); + SMP_DBG("u %32phN", u); + SMP_DBG("v %32phN", v); + SMP_DBG("x %16phN y %16phN", x, y); memcpy(m, y, 16); memcpy(m + 16, v, 32); @@ -308,7 +320,7 @@ static int smp_g2(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32], *val = get_unaligned_le32(tmp); *val %= 1000000; - BT_DBG("val %06u", *val); + SMP_DBG("val %06u", *val); return 0; } @@ -357,13 +369,13 @@ static int smp_h6(struct crypto_hash *tfm_cmac, const u8 w[16], { int err; - BT_DBG("w %16phN key_id %4phN", w, key_id); + SMP_DBG("w %16phN key_id %4phN", w, key_id); err = aes_cmac(tfm_cmac, w, key_id, 4, res); if (err) return err; - BT_DBG("res %16phN", res); + SMP_DBG("res %16phN", res); return err; } @@ -1742,9 +1754,9 @@ static u8 sc_send_public_key(struct smp_chan *smp) } } - BT_DBG("Local Public Key X: %32phN", smp->local_pk); - BT_DBG("Local Public Key Y: %32phN", &smp->local_pk[32]); - BT_DBG("Local Private Key: %32phN", smp->local_sk); + SMP_DBG("Local Public Key X: %32phN", smp->local_pk); + SMP_DBG("Local Public Key Y: %32phN", &smp->local_pk[32]); + SMP_DBG("Local Private Key: %32phN", smp->local_sk); smp_send_cmd(smp->conn, SMP_CMD_PUBLIC_KEY, 64, smp->local_pk); @@ -2390,13 +2402,13 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb) return err; } - BT_DBG("Remote Public Key X: %32phN", smp->remote_pk); - BT_DBG("Remote Public Key Y: %32phN", &smp->remote_pk[32]); + SMP_DBG("Remote Public Key X: %32phN", smp->remote_pk); + SMP_DBG("Remote Public Key Y: %32phN", &smp->remote_pk[32]); if (!ecdh_shared_secret(smp->remote_pk, smp->local_sk, smp->dhkey)) return SMP_UNSPECIFIED; - BT_DBG("DHKey %32phN", smp->dhkey); + SMP_DBG("DHKey %32phN", smp->dhkey); set_bit(SMP_FLAG_REMOTE_PK, &smp->flags); -- cgit v0.10.2 From cd0827976205f842cc722f48a1427c9b77c3ca28 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 2 Dec 2014 13:37:41 +0200 Subject: Bluetooth: Fix missing const declarations in SMP functions Several SMP functions take read-only data. This patch fixes the declaration of these parameters to use the const specifier as appropriate. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 13b40e8..f845dbf 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -380,7 +380,8 @@ static int smp_h6(struct crypto_hash *tfm_cmac, const u8 w[16], return err; } -static int smp_ah(struct crypto_blkcipher *tfm, u8 irk[16], u8 r[3], u8 res[3]) +static int smp_ah(struct crypto_blkcipher *tfm, const u8 irk[16], + const u8 r[3], u8 res[3]) { u8 _res[16]; int err; @@ -406,7 +407,8 @@ static int smp_ah(struct crypto_blkcipher *tfm, u8 irk[16], u8 r[3], u8 res[3]) return 0; } -bool smp_irk_matches(struct hci_dev *hdev, u8 irk[16], bdaddr_t *bdaddr) +bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16], + const bdaddr_t *bdaddr) { struct l2cap_chan *chan = hdev->smp_data; struct crypto_blkcipher *tfm; @@ -427,7 +429,7 @@ bool smp_irk_matches(struct hci_dev *hdev, u8 irk[16], bdaddr_t *bdaddr) return !memcmp(bdaddr->b, hash, 3); } -int smp_generate_rpa(struct hci_dev *hdev, u8 irk[16], bdaddr_t *rpa) +int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa) { struct l2cap_chan *chan = hdev->smp_data; struct crypto_blkcipher *tfm; @@ -452,9 +454,9 @@ int smp_generate_rpa(struct hci_dev *hdev, u8 irk[16], bdaddr_t *rpa) return 0; } -static int smp_c1(struct crypto_blkcipher *tfm_aes, u8 k[16], u8 r[16], - u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia, u8 _rat, - bdaddr_t *ra, u8 res[16]) +static int smp_c1(struct crypto_blkcipher *tfm_aes, const u8 k[16], + const u8 r[16], const u8 preq[7], const u8 pres[7], u8 _iat, + const bdaddr_t *ia, u8 _rat, const bdaddr_t *ra, u8 res[16]) { u8 p1[16], p2[16]; int err; @@ -493,8 +495,8 @@ static int smp_c1(struct crypto_blkcipher *tfm_aes, u8 k[16], u8 r[16], return err; } -static int smp_s1(struct crypto_blkcipher *tfm_aes, u8 k[16], u8 r1[16], - u8 r2[16], u8 _r[16]) +static int smp_s1(struct crypto_blkcipher *tfm_aes, const u8 k[16], + const u8 r1[16], const u8 r2[16], u8 _r[16]) { int err; diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h index f955d6b..3296bf4 100644 --- a/net/bluetooth/smp.h +++ b/net/bluetooth/smp.h @@ -185,8 +185,9 @@ bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level, int smp_conn_security(struct hci_conn *hcon, __u8 sec_level); int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey); -bool smp_irk_matches(struct hci_dev *hdev, u8 irk[16], bdaddr_t *bdaddr); -int smp_generate_rpa(struct hci_dev *hdev, u8 irk[16], bdaddr_t *rpa); +bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16], + const bdaddr_t *bdaddr); +int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa); int smp_register(struct hci_dev *hdev); void smp_unregister(struct hci_dev *hdev); -- cgit v0.10.2 From 06edf8deb55dbdcda2177da31d75ac79ccdc5841 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 2 Dec 2014 13:39:23 +0200 Subject: Bluetooth: Organize SMP crypto functions to logical sections This patch organizes the various SMP crypto functions so that the LE SC functions appear in one section and the legacy SMP functions in a separate one. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index f845dbf..1d1c33d 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -141,6 +141,10 @@ static inline void swap_buf(const u8 *src, u8 *dst, size_t len) dst[len - 1 - i] = src[i]; } +/* The following functions map to the LE SC SMP crypto functions + * AES-CMAC, f4, f5, f6, g2 and h6. + */ + static int aes_cmac(struct crypto_hash *tfm, const u8 k[16], const u8 *m, size_t len, u8 mac[16]) { @@ -325,6 +329,26 @@ static int smp_g2(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32], return 0; } +static int smp_h6(struct crypto_hash *tfm_cmac, const u8 w[16], + const u8 key_id[4], u8 res[16]) +{ + int err; + + SMP_DBG("w %16phN key_id %4phN", w, key_id); + + err = aes_cmac(tfm_cmac, w, key_id, 4, res); + if (err) + return err; + + SMP_DBG("res %16phN", res); + + return err; +} + +/* The following functions map to the legacy SMP crypto functions e, c1, + * s1 and ah. + */ + static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r) { struct blkcipher_desc desc; @@ -364,18 +388,59 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r) return err; } -static int smp_h6(struct crypto_hash *tfm_cmac, const u8 w[16], - const u8 key_id[4], u8 res[16]) +static int smp_c1(struct crypto_blkcipher *tfm_aes, const u8 k[16], + const u8 r[16], const u8 preq[7], const u8 pres[7], u8 _iat, + const bdaddr_t *ia, u8 _rat, const bdaddr_t *ra, u8 res[16]) { + u8 p1[16], p2[16]; int err; - SMP_DBG("w %16phN key_id %4phN", w, key_id); + memset(p1, 0, 16); - err = aes_cmac(tfm_cmac, w, key_id, 4, res); - if (err) + /* p1 = pres || preq || _rat || _iat */ + p1[0] = _iat; + p1[1] = _rat; + memcpy(p1 + 2, preq, 7); + memcpy(p1 + 9, pres, 7); + + /* p2 = padding || ia || ra */ + memcpy(p2, ra, 6); + memcpy(p2 + 6, ia, 6); + memset(p2 + 12, 0, 4); + + /* res = r XOR p1 */ + u128_xor((u128 *) res, (u128 *) r, (u128 *) p1); + + /* res = e(k, res) */ + err = smp_e(tfm_aes, k, res); + if (err) { + BT_ERR("Encrypt data error"); return err; + } - SMP_DBG("res %16phN", res); + /* res = res XOR p2 */ + u128_xor((u128 *) res, (u128 *) res, (u128 *) p2); + + /* res = e(k, res) */ + err = smp_e(tfm_aes, k, res); + if (err) + BT_ERR("Encrypt data error"); + + return err; +} + +static int smp_s1(struct crypto_blkcipher *tfm_aes, const u8 k[16], + const u8 r1[16], const u8 r2[16], u8 _r[16]) +{ + int err; + + /* Just least significant octets from r1 and r2 are considered */ + memcpy(_r, r2, 8); + memcpy(_r + 8, r1, 8); + + err = smp_e(tfm_aes, k, _r); + if (err) + BT_ERR("Encrypt data error"); return err; } @@ -454,63 +519,6 @@ int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa) return 0; } -static int smp_c1(struct crypto_blkcipher *tfm_aes, const u8 k[16], - const u8 r[16], const u8 preq[7], const u8 pres[7], u8 _iat, - const bdaddr_t *ia, u8 _rat, const bdaddr_t *ra, u8 res[16]) -{ - u8 p1[16], p2[16]; - int err; - - memset(p1, 0, 16); - - /* p1 = pres || preq || _rat || _iat */ - p1[0] = _iat; - p1[1] = _rat; - memcpy(p1 + 2, preq, 7); - memcpy(p1 + 9, pres, 7); - - /* p2 = padding || ia || ra */ - memcpy(p2, ra, 6); - memcpy(p2 + 6, ia, 6); - memset(p2 + 12, 0, 4); - - /* res = r XOR p1 */ - u128_xor((u128 *) res, (u128 *) r, (u128 *) p1); - - /* res = e(k, res) */ - err = smp_e(tfm_aes, k, res); - if (err) { - BT_ERR("Encrypt data error"); - return err; - } - - /* res = res XOR p2 */ - u128_xor((u128 *) res, (u128 *) res, (u128 *) p2); - - /* res = e(k, res) */ - err = smp_e(tfm_aes, k, res); - if (err) - BT_ERR("Encrypt data error"); - - return err; -} - -static int smp_s1(struct crypto_blkcipher *tfm_aes, const u8 k[16], - const u8 r1[16], const u8 r2[16], u8 _r[16]) -{ - int err; - - /* Just least significant octets from r1 and r2 are considered */ - memcpy(_r, r2, 8); - memcpy(_r + 8, r1, 8); - - err = smp_e(tfm_aes, k, _r); - if (err) - BT_ERR("Encrypt data error"); - - return err; -} - static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data) { struct l2cap_chan *chan = conn->smp; -- cgit v0.10.2 From 923e24143efe654553cabdb08f369d3789413147 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 3 Dec 2014 12:43:39 +0200 Subject: Bluetooth: Fix SMP debug key handling We need to keep debug keys around at least until the point that they are used - otherwise e.g. slave role behavior wouldn't work as there'd be no key to be looked up. The correct behavior should therefore be to return any stored keys but when we clean up the SMP context to remove the key from the hdev list if keeping debug keys around hasn't been requestsed. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index c8123f0..f001856 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3246,15 +3246,7 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, if (addr_type != k->bdaddr_type || bacmp(bdaddr, &k->bdaddr)) continue; - if (smp_ltk_is_sc(k)) { - if (k->type == SMP_LTK_P256_DEBUG && - !test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags)) - continue; - rcu_read_unlock(); - return k; - } - - if (ltk_role(k->type) == role) { + if (smp_ltk_is_sc(k) || ltk_role(k->type) == role) { rcu_read_unlock(); return k; } diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 1d1c33d..a7b973b 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -670,6 +670,7 @@ static void smp_chan_destroy(struct l2cap_conn *conn) { struct l2cap_chan *chan = conn->smp; struct smp_chan *smp = chan->data; + struct hci_conn *hcon = conn->hcon; bool complete; BUG_ON(!smp); @@ -677,7 +678,7 @@ static void smp_chan_destroy(struct l2cap_conn *conn) cancel_delayed_work_sync(&smp->security_timer); complete = test_bit(SMP_FLAG_COMPLETE, &smp->flags); - mgmt_smp_complete(conn->hcon, complete); + mgmt_smp_complete(hcon, complete); kfree(smp->csrk); kfree(smp->slave_csrk); @@ -686,6 +687,16 @@ static void smp_chan_destroy(struct l2cap_conn *conn) crypto_free_blkcipher(smp->tfm_aes); crypto_free_hash(smp->tfm_cmac); + /* Ensure that we don't leave any debug key around if debug key + * support hasn't been explicitly enabled. + */ + if (smp->ltk && smp->ltk->type == SMP_LTK_P256_DEBUG && + !test_bit(HCI_KEEP_DEBUG_KEYS, &hcon->hdev->dev_flags)) { + list_del_rcu(&smp->ltk->list); + kfree_rcu(smp->ltk, rcu); + smp->ltk = NULL; + } + /* If pairing failed clean up any keys we might have */ if (!complete) { if (smp->ltk) { @@ -706,7 +717,7 @@ static void smp_chan_destroy(struct l2cap_conn *conn) chan->data = NULL; kfree(smp); - hci_conn_drop(conn->hcon); + hci_conn_drop(hcon); } static void smp_failure(struct l2cap_conn *conn, u8 reason) -- cgit v0.10.2 From 7f376cd6dc1c9bfd14514c70765e6900a961c4b8 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 3 Dec 2014 16:07:13 +0200 Subject: Bluetooth: Fix minor coding style issue in smp.c The convention for checking for NULL pointers is !ptr and not ptr == NULL. This patch fixes such an occurrence in smp.c. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index a7b973b..7435940 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -356,7 +356,7 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r) uint8_t tmp[16], data[16]; int err; - if (tfm == NULL) { + if (!tfm) { BT_ERR("tfm %p", tfm); return -EINVAL; } -- cgit v0.10.2 From 580039e838a7efaee0048b0857d1bb5582327aa5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 3 Dec 2014 16:26:37 +0200 Subject: Bluetooth: Fix false-positive "uninitialized" compiler warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some gcc versions don't seem to be able to properly track the flow of the smp_cmd_pairing_random() function and end up causing the following types of (false-positive) warnings: smp.c:1995:6: warning: ‘nb’ may be used uninitialized in this function [-Wmaybe-uninitialized] err = smp_g2(smp->tfm_cmac, pkax, pkbx, na, nb, &passkey); smp.c:1995:6: warning: ‘na’ may be used uninitialized in this function [-Wmaybe-uninitialized] err = smp_g2(smp->tfm_cmac, pkax, pkbx, na, nb, &passkey); ^ smp.c:1995:6: warning: ‘pkbx’ may be used uninitialized in this function [-Wmaybe-uninitialized] err = smp_g2(smp->tfm_cmac, pkax, pkbx, na, nb, &passkey); ^ smp.c:1995:6: warning: ‘pkax’ may be used uninitialized in this function [-Wmaybe-uninitialized] err = smp_g2(smp->tfm_cmac, pkax, pkbx, na, nb, &passkey); This patch fixes the issue by moving the pkax/pkbx and na/nb initialization earlier in the function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 7435940..96bf16d 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1947,6 +1947,18 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) if (!test_bit(SMP_FLAG_SC, &smp->flags)) return smp_random(smp); + if (hcon->out) { + pkax = smp->local_pk; + pkbx = smp->remote_pk; + na = smp->prnd; + nb = smp->rrnd; + } else { + pkax = smp->remote_pk; + pkbx = smp->local_pk; + na = smp->rrnd; + nb = smp->prnd; + } + if (smp->method == REQ_OOB) { if (!hcon->out) smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, @@ -1969,20 +1981,10 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) if (memcmp(smp->pcnf, cfm, 16)) return SMP_CONFIRM_FAILED; - - pkax = smp->local_pk; - pkbx = smp->remote_pk; - na = smp->prnd; - nb = smp->rrnd; } else { smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd), smp->prnd); SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK); - - pkax = smp->remote_pk; - pkbx = smp->local_pk; - na = smp->rrnd; - nb = smp->prnd; } mackey_and_ltk: -- cgit v0.10.2 From 7365d475bf6a0e2497ac4ef29474fffb91d024f1 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 24 Nov 2014 02:40:52 -0800 Subject: Bluetooth: btmrvl: remove extra newline character BT_INFO/BT_DBG etc. already takes care of adding a newline An extra newline character inside message is removed in this patch. Signed-off-by: Amitkumar Karwar Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index bb0d2c2..10973ac 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -42,7 +42,7 @@ void btmrvl_interrupt(struct btmrvl_private *priv) priv->adapter->int_count++; if (priv->adapter->hs_state == HS_ACTIVATED) { - BT_DBG("BT: HS DEACTIVATED in ISR!\n"); + BT_DBG("BT: HS DEACTIVATED in ISR!"); priv->adapter->hs_state = HS_DEACTIVATED; } @@ -214,7 +214,7 @@ int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd) ret = btmrvl_send_sync_cmd(priv, BT_CMD_MODULE_CFG_REQ, &subcmd, 1); if (ret) - BT_ERR("module_cfg_cmd(%x) failed\n", subcmd); + BT_ERR("module_cfg_cmd(%x) failed", subcmd); return ret; } @@ -250,7 +250,7 @@ int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv) ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_CONFIG, param, 2); if (ret) - BT_ERR("HSCFG command failed\n"); + BT_ERR("HSCFG command failed"); return ret; } @@ -268,7 +268,7 @@ int btmrvl_enable_ps(struct btmrvl_private *priv) ret = btmrvl_send_sync_cmd(priv, BT_CMD_AUTO_SLEEP_MODE, ¶m, 1); if (ret) - BT_ERR("PSMODE command failed\n"); + BT_ERR("PSMODE command failed"); return 0; } @@ -281,7 +281,7 @@ int btmrvl_enable_hs(struct btmrvl_private *priv) ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_ENABLE, NULL, 0); if (ret) { - BT_ERR("Host sleep enable command failed\n"); + BT_ERR("Host sleep enable command failed"); return ret; } @@ -328,7 +328,7 @@ int btmrvl_prepare_command(struct btmrvl_private *priv) } else { ret = priv->hw_wakeup_firmware(priv); priv->adapter->hs_state = HS_DEACTIVATED; - BT_DBG("BT: HS DEACTIVATED due to host activity!\n"); + BT_DBG("BT: HS DEACTIVATED due to host activity!"); } } @@ -493,7 +493,7 @@ static int btmrvl_download_cal_data(struct btmrvl_private *priv, ret = btmrvl_send_sync_cmd(priv, BT_CMD_LOAD_CONFIG_DATA, data, BT_CAL_HDR_LEN + len); if (ret) - BT_ERR("Failed to download caibration data\n"); + BT_ERR("Failed to download caibration data"); return 0; } diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 550bce0..416d792 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -764,8 +764,8 @@ static void btmrvl_sdio_interrupt(struct sdio_func *func) card = sdio_get_drvdata(func); if (!card || !card->priv) { - BT_ERR("sbi_interrupt(%p) card or priv is " - "NULL, card=%p\n", func, card); + BT_ERR("sbi_interrupt(%p) card or priv is NULL, card=%p", + func, card); return; } -- cgit v0.10.2 From dc759613b0247eb1658d3992f50ba3fad5b61d31 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Mon, 24 Nov 2014 02:40:53 -0800 Subject: Bluetooth: btmrvl add firmware dump support This patch adds firmware dump support for marvell bluetooth chipset. Currently only SD8897 is supported. This is implemented based on dev_coredump, a new mechnism introduced in kernel 3.18rc3 Firmware dump can be trigger by echo 1 > /sys/kernel/debug/bluetooth/hci*/config/fw_dump and when the dump operation is completed, data can be read by cat /sys/class/devcoredump/devcd*/data We have prepared following script to divide fw memory dump data into multiple files based on memory type. [root]# cat btmrvl_split_dump_data.sh #!/bin/bash # usage: ./btmrvl_split_dump_data.sh dump_data fw_dump_data=$1 mem_type="ITCM DTCM SQRAM APU CIU ICU MAC EXT7 EXT8 EXT9 EXT10 EXT11 EXT12 EXT13 EXTLAST" for name in ${mem_type[@]} do sed -n "/Start dump $name/,/End dump/p" $fw_dump_data > tmp.$name.log if [ ! -s tmp.$name.log ] then rm -rf tmp.$name.log else # Remove the describle info "Start dump" and "End dump" sed '1d' tmp.$name.log | sed '$d' > /data/$name.log if [ -s /data/$name.log ] then echo "generate /data/$name.log" else sed '1d' tmp.$name.log | sed '$d' > /var/$name.log echo "generate /var/$name.log" fi rm -rf tmp.$name.log fi done Signed-off-by: Xinming Hu Signed-off-by: Cathy Luo Signed-off-by: Avinash Patil Reviewed-by: Johannes Berg Reviewed-by: Marcel Holtmann Signed-off-by: Amitkumar Karwar Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 4547dc2..364f080 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -210,6 +210,7 @@ config BT_MRVL_SDIO tristate "Marvell BT-over-SDIO driver" depends on BT_MRVL && MMC select FW_LOADER + select WANT_DEV_COREDUMP help The driver for Marvell Bluetooth chipsets with SDIO interface. diff --git a/drivers/bluetooth/btmrvl_debugfs.c b/drivers/bluetooth/btmrvl_debugfs.c index 023d35e..1828ed8 100644 --- a/drivers/bluetooth/btmrvl_debugfs.c +++ b/drivers/bluetooth/btmrvl_debugfs.c @@ -167,6 +167,35 @@ static const struct file_operations btmrvl_hscmd_fops = { .llseek = default_llseek, }; +static ssize_t btmrvl_fwdump_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct btmrvl_private *priv = file->private_data; + char buf[16]; + bool result; + + memset(buf, 0, sizeof(buf)); + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (strtobool(buf, &result)) + return -EINVAL; + + if (!result) + return -EINVAL; + + btmrvl_firmware_dump(priv); + + return count; +} + +static const struct file_operations btmrvl_fwdump_fops = { + .write = btmrvl_fwdump_write, + .open = simple_open, + .llseek = default_llseek, +}; + void btmrvl_debugfs_init(struct hci_dev *hdev) { struct btmrvl_private *priv = hci_get_drvdata(hdev); @@ -197,6 +226,8 @@ void btmrvl_debugfs_init(struct hci_dev *hdev) priv, &btmrvl_hscmd_fops); debugfs_create_file("hscfgcmd", 0644, dbg->config_dir, priv, &btmrvl_hscfgcmd_fops); + debugfs_create_file("fw_dump", 0200, dbg->config_dir, + priv, &btmrvl_fwdump_fops); dbg->status_dir = debugfs_create_dir("status", hdev->debugfs); debugfs_create_u8("curpsmode", 0444, dbg->status_dir, diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h index 38ad662..330f8f8 100644 --- a/drivers/bluetooth/btmrvl_drv.h +++ b/drivers/bluetooth/btmrvl_drv.h @@ -32,6 +32,24 @@ /* Time to wait for command response in millisecond */ #define WAIT_UNTIL_CMD_RESP 5000 +enum rdwr_status { + RDWR_STATUS_SUCCESS = 0, + RDWR_STATUS_FAILURE = 1, + RDWR_STATUS_DONE = 2 +}; + +#define FW_DUMP_MAX_NAME_LEN 8 +#define FW_DUMP_HOST_READY 0xEE +#define FW_DUMP_DONE 0xFF +#define FW_DUMP_READ_DONE 0xFE + +struct memory_type_mapping { + u8 mem_name[FW_DUMP_MAX_NAME_LEN]; + u8 *mem_ptr; + u32 mem_size; + u8 done_flag; +}; + struct btmrvl_thread { struct task_struct *task; wait_queue_head_t wait_q; @@ -81,6 +99,7 @@ struct btmrvl_private { u8 *payload, u16 nb); int (*hw_wakeup_firmware) (struct btmrvl_private *priv); int (*hw_process_int_status) (struct btmrvl_private *priv); + void (*firmware_dump)(struct btmrvl_private *priv); spinlock_t driver_lock; /* spinlock used by driver */ #ifdef CONFIG_DEBUG_FS void *debugfs_data; @@ -151,6 +170,7 @@ int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv); int btmrvl_enable_ps(struct btmrvl_private *priv); int btmrvl_prepare_command(struct btmrvl_private *priv); int btmrvl_enable_hs(struct btmrvl_private *priv); +void btmrvl_firmware_dump(struct btmrvl_private *priv); #ifdef CONFIG_DEBUG_FS void btmrvl_debugfs_init(struct hci_dev *hdev); diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index 10973ac..30939c9 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "btmrvl_drv.h" #include "btmrvl_sdio.h" @@ -335,6 +336,12 @@ int btmrvl_prepare_command(struct btmrvl_private *priv) return ret; } +void btmrvl_firmware_dump(struct btmrvl_private *priv) +{ + if (priv->firmware_dump) + priv->firmware_dump(priv); +} + static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb) { int ret = 0; diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 416d792..0057c0b 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -33,6 +34,24 @@ #define VERSION "1.0" +static struct memory_type_mapping mem_type_mapping_tbl[] = { + {"ITCM", NULL, 0, 0xF0}, + {"DTCM", NULL, 0, 0xF1}, + {"SQRAM", NULL, 0, 0xF2}, + {"APU", NULL, 0, 0xF3}, + {"CIU", NULL, 0, 0xF4}, + {"ICU", NULL, 0, 0xF5}, + {"MAC", NULL, 0, 0xF6}, + {"EXT7", NULL, 0, 0xF7}, + {"EXT8", NULL, 0, 0xF8}, + {"EXT9", NULL, 0, 0xF9}, + {"EXT10", NULL, 0, 0xFA}, + {"EXT11", NULL, 0, 0xFB}, + {"EXT12", NULL, 0, 0xFC}, + {"EXT13", NULL, 0, 0xFD}, + {"EXTLAST", NULL, 0, 0xFE}, +}; + /* The btmrvl_sdio_remove() callback function is called * when user removes this module from kernel space or ejects * the card from the slot. The driver handles these 2 cases @@ -122,6 +141,9 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8897 = { .int_read_to_clear = true, .host_int_rsr = 0x01, .card_misc_cfg = 0xcc, + .fw_dump_ctrl = 0xe2, + .fw_dump_start = 0xe3, + .fw_dump_end = 0xea, }; static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = { @@ -130,6 +152,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = { .reg = &btmrvl_reg_8688, .support_pscan_win_report = false, .sd_blksz_fw_dl = 64, + .supports_fw_dump = false, }; static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = { @@ -138,6 +161,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = { .reg = &btmrvl_reg_87xx, .support_pscan_win_report = false, .sd_blksz_fw_dl = 256, + .supports_fw_dump = false, }; static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = { @@ -146,6 +170,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = { .reg = &btmrvl_reg_87xx, .support_pscan_win_report = false, .sd_blksz_fw_dl = 256, + .supports_fw_dump = false, }; static const struct btmrvl_sdio_device btmrvl_sdio_sd8887 = { @@ -154,6 +179,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8887 = { .reg = &btmrvl_reg_8887, .support_pscan_win_report = true, .sd_blksz_fw_dl = 256, + .supports_fw_dump = false, }; static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = { @@ -162,6 +188,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = { .reg = &btmrvl_reg_8897, .support_pscan_win_report = true, .sd_blksz_fw_dl = 256, + .supports_fw_dump = true, }; static const struct sdio_device_id btmrvl_sdio_ids[] = { @@ -1080,6 +1107,277 @@ static int btmrvl_sdio_wakeup_fw(struct btmrvl_private *priv) return ret; } +static void btmrvl_sdio_dump_regs(struct btmrvl_private *priv) +{ + struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; + int ret = 0; + unsigned int reg, reg_start, reg_end; + char buf[256], *ptr; + u8 loop, func, data; + int MAX_LOOP = 2; + + btmrvl_sdio_wakeup_fw(priv); + sdio_claim_host(card->func); + + for (loop = 0; loop < MAX_LOOP; loop++) { + memset(buf, 0, sizeof(buf)); + ptr = buf; + + if (loop == 0) { + /* Read the registers of SDIO function0 */ + func = loop; + reg_start = 0; + reg_end = 9; + } else { + func = 2; + reg_start = 0; + reg_end = 0x09; + } + + ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", + func, reg_start, reg_end); + for (reg = reg_start; reg <= reg_end; reg++) { + if (func == 0) + data = sdio_f0_readb(card->func, reg, &ret); + else + data = sdio_readb(card->func, reg, &ret); + + if (!ret) { + ptr += sprintf(ptr, "%02x ", data); + } else { + ptr += sprintf(ptr, "ERR"); + break; + } + } + + BT_INFO("%s", buf); + } + + sdio_release_host(card->func); +} + +/* This function read/write firmware */ +static enum +rdwr_status btmrvl_sdio_rdwr_firmware(struct btmrvl_private *priv, + u8 doneflag) +{ + struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; + int ret, tries; + u8 ctrl_data = 0; + + sdio_writeb(card->func, FW_DUMP_HOST_READY, card->reg->fw_dump_ctrl, + &ret); + + if (ret) { + BT_ERR("SDIO write err"); + return RDWR_STATUS_FAILURE; + } + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl, + &ret); + + if (ret) { + BT_ERR("SDIO read err"); + return RDWR_STATUS_FAILURE; + } + + if (ctrl_data == FW_DUMP_DONE) + break; + if (doneflag && ctrl_data == doneflag) + return RDWR_STATUS_DONE; + if (ctrl_data != FW_DUMP_HOST_READY) { + BT_INFO("The ctrl reg was changed, re-try again!"); + sdio_writeb(card->func, FW_DUMP_HOST_READY, + card->reg->fw_dump_ctrl, &ret); + if (ret) { + BT_ERR("SDIO write err"); + return RDWR_STATUS_FAILURE; + } + } + usleep_range(100, 200); + } + + if (ctrl_data == FW_DUMP_HOST_READY) { + BT_ERR("Fail to pull ctrl_data"); + return RDWR_STATUS_FAILURE; + } + + return RDWR_STATUS_SUCCESS; +} + +/* This function dump sdio register and memory data */ +static void btmrvl_sdio_dump_firmware(struct btmrvl_private *priv) +{ + struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; + int ret = 0; + unsigned int reg, reg_start, reg_end; + enum rdwr_status stat; + u8 *dbg_ptr, *end_ptr, *fw_dump_data, *fw_dump_ptr; + u8 dump_num, idx, i, read_reg, doneflag = 0; + u32 memory_size, fw_dump_len = 0; + + /* dump sdio register first */ + btmrvl_sdio_dump_regs(priv); + + if (!card->supports_fw_dump) { + BT_ERR("Firmware dump not supported for this card!"); + return; + } + + for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) { + struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr = NULL; + } + entry->mem_size = 0; + } + + btmrvl_sdio_wakeup_fw(priv); + sdio_claim_host(card->func); + + BT_INFO("== btmrvl firmware dump start =="); + + stat = btmrvl_sdio_rdwr_firmware(priv, doneflag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + + reg = card->reg->fw_dump_start; + /* Read the number of the memories which will dump */ + dump_num = sdio_readb(card->func, reg, &ret); + + if (ret) { + BT_ERR("SDIO read memory length err"); + goto done; + } + + /* Read the length of every memory which will dump */ + for (idx = 0; idx < dump_num; idx++) { + struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; + + stat = btmrvl_sdio_rdwr_firmware(priv, doneflag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + + memory_size = 0; + reg = card->reg->fw_dump_start; + for (i = 0; i < 4; i++) { + read_reg = sdio_readb(card->func, reg, &ret); + if (ret) { + BT_ERR("SDIO read err"); + goto done; + } + memory_size |= (read_reg << i*8); + reg++; + } + + if (memory_size == 0) { + BT_INFO("Firmware dump finished!"); + break; + } + + BT_INFO("%s_SIZE=0x%x", entry->mem_name, memory_size); + entry->mem_ptr = vzalloc(memory_size + 1); + entry->mem_size = memory_size; + if (!entry->mem_ptr) { + BT_ERR("Vzalloc %s failed", entry->mem_name); + goto done; + } + + fw_dump_len += (strlen("========Start dump ") + + strlen(entry->mem_name) + + strlen("========\n") + + (memory_size + 1) + + strlen("\n========End dump========\n")); + + dbg_ptr = entry->mem_ptr; + end_ptr = dbg_ptr + memory_size; + + doneflag = entry->done_flag; + BT_INFO("Start %s output, please wait...", + entry->mem_name); + + do { + stat = btmrvl_sdio_rdwr_firmware(priv, doneflag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + + reg_start = card->reg->fw_dump_start; + reg_end = card->reg->fw_dump_end; + for (reg = reg_start; reg <= reg_end; reg++) { + *dbg_ptr = sdio_readb(card->func, reg, &ret); + if (ret) { + BT_ERR("SDIO read err"); + goto done; + } + if (dbg_ptr < end_ptr) + dbg_ptr++; + else + BT_ERR("Allocated buffer not enough"); + } + + if (stat != RDWR_STATUS_DONE) { + continue; + } else { + BT_INFO("%s done: size=0x%tx", + entry->mem_name, + dbg_ptr - entry->mem_ptr); + break; + } + } while (1); + } + + BT_INFO("== btmrvl firmware dump end =="); + +done: + sdio_release_host(card->func); + + if (fw_dump_len == 0) + return; + + fw_dump_data = vzalloc(fw_dump_len+1); + if (!fw_dump_data) { + BT_ERR("Vzalloc fw_dump_data fail!"); + return; + } + fw_dump_ptr = fw_dump_data; + + /* Dump all the memory data into single file, a userspace script will + be used to split all the memory data to multiple files*/ + BT_INFO("== btmrvl firmware dump to /sys/class/devcoredump start"); + for (idx = 0; idx < dump_num; idx++) { + struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + strcpy(fw_dump_ptr, "========Start dump "); + fw_dump_ptr += strlen("========Start dump "); + + strcpy(fw_dump_ptr, entry->mem_name); + fw_dump_ptr += strlen(entry->mem_name); + + strcpy(fw_dump_ptr, "========\n"); + fw_dump_ptr += strlen("========\n"); + + memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size); + fw_dump_ptr += entry->mem_size; + + strcpy(fw_dump_ptr, "\n========End dump========\n"); + fw_dump_ptr += strlen("\n========End dump========\n"); + + vfree(mem_type_mapping_tbl[idx].mem_ptr); + mem_type_mapping_tbl[idx].mem_ptr = NULL; + } + } + + /* fw_dump_data will be free in device coredump release function + after 5 min*/ + dev_coredumpv(&priv->btmrvl_dev.hcidev->dev, fw_dump_data, + fw_dump_len, GFP_KERNEL); + BT_INFO("== btmrvl firmware dump to /sys/class/devcoredump end"); +} + static int btmrvl_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { @@ -1103,6 +1401,7 @@ static int btmrvl_sdio_probe(struct sdio_func *func, card->reg = data->reg; card->sd_blksz_fw_dl = data->sd_blksz_fw_dl; card->support_pscan_win_report = data->support_pscan_win_report; + card->supports_fw_dump = data->supports_fw_dump; } if (btmrvl_sdio_register_dev(card) < 0) { @@ -1134,6 +1433,7 @@ static int btmrvl_sdio_probe(struct sdio_func *func, priv->hw_host_to_card = btmrvl_sdio_host_to_card; priv->hw_wakeup_firmware = btmrvl_sdio_wakeup_fw; priv->hw_process_int_status = btmrvl_sdio_process_int_status; + priv->firmware_dump = btmrvl_sdio_dump_firmware; if (btmrvl_register_hdev(priv)) { BT_ERR("Register hdev failed!"); diff --git a/drivers/bluetooth/btmrvl_sdio.h b/drivers/bluetooth/btmrvl_sdio.h index 453559f..1a3bd06 100644 --- a/drivers/bluetooth/btmrvl_sdio.h +++ b/drivers/bluetooth/btmrvl_sdio.h @@ -81,6 +81,9 @@ struct btmrvl_sdio_card_reg { bool int_read_to_clear; u8 host_int_rsr; u8 card_misc_cfg; + u8 fw_dump_ctrl; + u8 fw_dump_start; + u8 fw_dump_end; }; struct btmrvl_sdio_card { @@ -90,6 +93,7 @@ struct btmrvl_sdio_card { const char *firmware; const struct btmrvl_sdio_card_reg *reg; bool support_pscan_win_report; + bool supports_fw_dump; u16 sd_blksz_fw_dl; u8 rx_unit; struct btmrvl_private *priv; @@ -101,6 +105,7 @@ struct btmrvl_sdio_device { const struct btmrvl_sdio_card_reg *reg; const bool support_pscan_win_report; u16 sd_blksz_fw_dl; + bool supports_fw_dump; }; -- cgit v0.10.2 From 6029ddc2333ae37f637d44beef3a1480cbbb33b9 Mon Sep 17 00:00:00 2001 From: Heinrich Siebmanns Date: Wed, 3 Dec 2014 19:32:22 +0100 Subject: Bluetooth: Add support for Broadcom BCM20702A0 variants firmware download This requires the flag BTUSB_BCM_PATCHRAM to work. Relevant details from /sys/kernel/debug/usb/devices for my device: T: Bus=03 Lev=02 Prnt=02 Port=03 Cnt=02 Dev#= 4 Spd=12 MxCh= 0 D: Ver= 2.00 Cls=ff(vend.) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0489 ProdID=e031 Rev= 1.12 S: Manufacturer=Broadcom Corp S: Product=BCM20702A0 S: SerialNumber=3859F9CD2AEE C:* #Ifs= 4 Cfg#= 1 Atr=e0 MxPwr= 0mA I:* If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=1ms E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) E: Ad=84(I) Atr=02(Bulk) MxPS= 32 Ivl=0ms E: Ad=04(O) Atr=02(Bulk) MxPS= 32 Ivl=0ms I:* If#= 3 Alt= 0 #EPs= 0 Cls=fe(app. ) Sub=01 Prot=01 Driver=(none) The firmware was extracted from a Windows 7 32-bit installation and converted from 'hex' to 'hcd' for use in Linux. The firmware is named "BCM20702A0_001.001.024.0156.0204.hex" and is located in "%SYSTEMROOT%\system32\drivers\" (md5 d126e6c4e0e669d76c38cf9377f76b7f) (sha1 145d1850b2785a953233b409e7ff77786927c7d2) The firmware file is also available as a download at http://support.ts.fujitsu.com/Download/ contained in "FTS_WIDCOMMBluetoothSoftware_6309000_1072149.zip" Search for the file Win32/bcbtums-win7x86-brcm.inf in the archive, look for the vendor and product ID of your adapter, see the section 'devices' in that file to find out what device name it uses. See the device entry in the inf file (in my case it was 'RAMUSBE031') to find out which hex file you need to convert to hcd for upload 'hcd' file should be placed at "brcm/BCM20702A0-0489-e031.hcd" inside the firmware directory (e.g. "/lib/firmware") Signed-off-by: Heinrich Siebmanns Signed-off-by: Marcel Holtmann diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 9efdace..31dd24a 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -110,7 +110,8 @@ static const struct usb_device_id btusb_table[] = { .driver_info = BTUSB_BCM_PATCHRAM }, /* Foxconn - Hon Hai */ - { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) }, + { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01), + .driver_info = BTUSB_BCM_PATCHRAM }, /* Broadcom devices with vendor specific id */ { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01), -- cgit v0.10.2 From 8ab9731d8c903dc9a2cc53b977979bd757bb76cb Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 3 Dec 2014 19:46:10 +0100 Subject: Bluetooth: Increase minor version of core module With the addition of support for Bluetooth Low Energy Secure Connections feature, it makes sense to increase the minor version of the Bluetooth core module. The module version is not used anywhere, but it gives a nice extra hint for debugging purposes. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 339c74a..4ba1591 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -31,7 +31,7 @@ #include #include -#define VERSION "2.19" +#define VERSION "2.20" /* Bluetooth sockets */ #define BT_MAX_PROTO 8 -- cgit v0.10.2 From 854bda1982bf126d0a951adfcac76ba6d000cd2b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 3 Dec 2014 19:52:43 +0100 Subject: Bluetooth: Increment management interface revision This patch increments the management interface revision due to the addition of support for LE Secure Connection feature. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1e73bea..77cf0ef 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -35,7 +35,7 @@ #include "smp.h" #define MGMT_VERSION 1 -#define MGMT_REVISION 7 +#define MGMT_REVISION 8 static const u16 mgmt_commands[] = { MGMT_OP_READ_INDEX_LIST, -- cgit v0.10.2 From f5a969f23bc32945b0ec4bdb8084f514a5311142 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 4 Dec 2014 11:36:34 +0100 Subject: Bluetooth: Simplify the error handling of Start Discovery command The Start Discovery command has some complicated code when it comes to error handling. With the future introduction of Start Service Discovery simplifying this makes it easier to read. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 77cf0ef..f329637 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3771,14 +3771,8 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, goto failed; } - if (test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY, - MGMT_STATUS_BUSY, &cp->type, - sizeof(cp->type)); - goto failed; - } - - if (hdev->discovery.state != DISCOVERY_STOPPED) { + if (hdev->discovery.state != DISCOVERY_STOPPED || + test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) { err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY, MGMT_STATUS_BUSY, &cp->type, sizeof(cp->type)); @@ -3909,10 +3903,12 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, } err = hci_req_run(&req, start_discovery_complete); - if (err < 0) + if (err < 0) { mgmt_pending_remove(cmd); - else - hci_discovery_set_state(hdev, DISCOVERY_STARTING); + goto failed; + } + + hci_discovery_set_state(hdev, DISCOVERY_STARTING); failed: hci_dev_unlock(hdev); -- cgit v0.10.2 From 11e6e25d052478235a0c3f94d2f2faddeb58eb96 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 4 Dec 2014 11:36:35 +0100 Subject: Bluetooth: Use {start,stop}_discovery_complete handler for cmd_complete Sending the required cmd_complete for the management commands should be done in one place and not in multiple places. Especially for Start and Stop Discovery commands this is split into to sending it in case of failure from the complete handler, but in case of success from the event state update function triggering mgmt_discovering. This is way too convoluted and since hci_request serializes the HCI command processing, send the cmd_complete response from the complete handler for all cases. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f329637..311984f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3686,64 +3686,53 @@ done: return err; } -static int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status) +static void start_discovery_complete(struct hci_dev *hdev, u8 status) { struct pending_cmd *cmd; - u8 type; - int err; - - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); - - cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev); - if (!cmd) - return -ENOENT; - - type = hdev->discovery.type; + unsigned long timeout; - err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status), - &type, sizeof(type)); - mgmt_pending_remove(cmd); + BT_DBG("status %d", status); - return err; -} + hci_dev_lock(hdev); -static void start_discovery_complete(struct hci_dev *hdev, u8 status) -{ - unsigned long timeout = 0; + cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev); + if (cmd) { + u8 type = hdev->discovery.type; - BT_DBG("status %d", status); + cmd_complete(cmd->sk, hdev->id, cmd->opcode, + mgmt_status(status), &type, sizeof(type)); + mgmt_pending_remove(cmd); + } if (status) { - hci_dev_lock(hdev); - mgmt_start_discovery_failed(hdev, status); - hci_dev_unlock(hdev); - return; + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + goto unlock; } - hci_dev_lock(hdev); hci_discovery_set_state(hdev, DISCOVERY_FINDING); - hci_dev_unlock(hdev); switch (hdev->discovery.type) { case DISCOV_TYPE_LE: timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT); break; - case DISCOV_TYPE_INTERLEAVED: timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout); break; - case DISCOV_TYPE_BREDR: + timeout = 0; break; - default: BT_ERR("Invalid discovery type %d", hdev->discovery.type); + timeout = 0; + break; } - if (!timeout) - return; + if (timeout) + queue_delayed_work(hdev->workqueue, + &hdev->le_scan_disable, timeout); - queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable, timeout); +unlock: + hci_dev_unlock(hdev); } static int start_discovery(struct sock *sk, struct hci_dev *hdev, @@ -3915,36 +3904,26 @@ failed: return err; } -static int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status) +static void stop_discovery_complete(struct hci_dev *hdev, u8 status) { struct pending_cmd *cmd; - int err; - cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev); - if (!cmd) - return -ENOENT; - - err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status), - &hdev->discovery.type, sizeof(hdev->discovery.type)); - mgmt_pending_remove(cmd); - - return err; -} - -static void stop_discovery_complete(struct hci_dev *hdev, u8 status) -{ BT_DBG("status %d", status); hci_dev_lock(hdev); - if (status) { - mgmt_stop_discovery_failed(hdev, status); - goto unlock; + cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev); + if (cmd) { + u8 type = hdev->discovery.type; + + cmd_complete(cmd->sk, hdev->id, cmd->opcode, + mgmt_status(status), &type, sizeof(type)); + mgmt_pending_remove(cmd); } - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + if (!status) + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); -unlock: hci_dev_unlock(hdev); } @@ -6909,23 +6888,9 @@ void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, void mgmt_discovering(struct hci_dev *hdev, u8 discovering) { struct mgmt_ev_discovering ev; - struct pending_cmd *cmd; BT_DBG("%s discovering %u", hdev->name, discovering); - if (discovering) - cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev); - else - cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev); - - if (cmd != NULL) { - u8 type = hdev->discovery.type; - - cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0, &type, - sizeof(type)); - mgmt_pending_remove(cmd); - } - memset(&ev, 0, sizeof(ev)); ev.type = hdev->discovery.type; ev.discovering = discovering; -- cgit v0.10.2 From 8019044dcb5944c0f578a1e59cecbd09d7b7c7f3 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 4 Dec 2014 11:36:36 +0100 Subject: Bluetooth: Split triggering of discovery commands into separate function The actual process of compiling the correct HCI commands for triggering discovery is something that should be generic. So instead of mixing it into the Start Discover operation handling, split it out into its own function utilizing HCI request handling and just providing status in case of errors or invalid parameters. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 311984f..415ba41 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3686,6 +3686,103 @@ done: return err; } +static bool trigger_discovery(struct hci_request *req, u8 *status) +{ + struct hci_dev *hdev = req->hdev; + struct hci_cp_le_set_scan_param param_cp; + struct hci_cp_le_set_scan_enable enable_cp; + struct hci_cp_inquiry inq_cp; + /* General inquiry access code (GIAC) */ + u8 lap[3] = { 0x33, 0x8b, 0x9e }; + u8 own_addr_type; + int err; + + switch (hdev->discovery.type) { + case DISCOV_TYPE_BREDR: + *status = mgmt_bredr_support(hdev); + if (*status) + return false; + + if (test_bit(HCI_INQUIRY, &hdev->flags)) { + *status = MGMT_STATUS_BUSY; + return false; + } + + hci_inquiry_cache_flush(hdev); + + memset(&inq_cp, 0, sizeof(inq_cp)); + memcpy(&inq_cp.lap, lap, sizeof(inq_cp.lap)); + inq_cp.length = DISCOV_BREDR_INQUIRY_LEN; + hci_req_add(req, HCI_OP_INQUIRY, sizeof(inq_cp), &inq_cp); + break; + + case DISCOV_TYPE_LE: + case DISCOV_TYPE_INTERLEAVED: + *status = mgmt_le_support(hdev); + if (*status) + return false; + + if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED && + !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { + *status = MGMT_STATUS_NOT_SUPPORTED; + return false; + } + + if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) { + /* Don't let discovery abort an outgoing + * connection attempt that's using directed + * advertising. + */ + if (hci_conn_hash_lookup_state(hdev, LE_LINK, + BT_CONNECT)) { + *status = MGMT_STATUS_REJECTED; + return false; + } + + disable_advertising(req); + } + + /* If controller is scanning, it means the background scanning + * is running. Thus, we should temporarily stop it in order to + * set the discovery scanning parameters. + */ + if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) + hci_req_add_le_scan_disable(req); + + memset(¶m_cp, 0, sizeof(param_cp)); + + /* All active scans will be done with either a resolvable + * private address (when privacy feature has been enabled) + * or unresolvable private address. + */ + err = hci_update_random_address(req, true, &own_addr_type); + if (err < 0) { + *status = MGMT_STATUS_FAILED; + return false; + } + + param_cp.type = LE_SCAN_ACTIVE; + param_cp.interval = cpu_to_le16(DISCOV_LE_SCAN_INT); + param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN); + param_cp.own_address_type = own_addr_type; + hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp), + ¶m_cp); + + memset(&enable_cp, 0, sizeof(enable_cp)); + enable_cp.enable = LE_SCAN_ENABLE; + enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE; + hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp), + &enable_cp); + break; + + default: + *status = MGMT_STATUS_INVALID_PARAMS; + return false; + } + + return true; +} + static void start_discovery_complete(struct hci_dev *hdev, u8 status) { struct pending_cmd *cmd; @@ -3740,13 +3837,8 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, { struct mgmt_cp_start_discovery *cp = data; struct pending_cmd *cmd; - struct hci_cp_le_set_scan_param param_cp; - struct hci_cp_le_set_scan_enable enable_cp; - struct hci_cp_inquiry inq_cp; struct hci_request req; - /* General inquiry access code (GIAC) */ - u8 lap[3] = { 0x33, 0x8b, 0x9e }; - u8 status, own_addr_type; + u8 status; int err; BT_DBG("%s", hdev->name); @@ -3778,115 +3870,9 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, hci_req_init(&req, hdev); - switch (hdev->discovery.type) { - case DISCOV_TYPE_BREDR: - status = mgmt_bredr_support(hdev); - if (status) { - err = cmd_complete(sk, hdev->id, - MGMT_OP_START_DISCOVERY, status, - &cp->type, sizeof(cp->type)); - mgmt_pending_remove(cmd); - goto failed; - } - - if (test_bit(HCI_INQUIRY, &hdev->flags)) { - err = cmd_complete(sk, hdev->id, - MGMT_OP_START_DISCOVERY, - MGMT_STATUS_BUSY, &cp->type, - sizeof(cp->type)); - mgmt_pending_remove(cmd); - goto failed; - } - - hci_inquiry_cache_flush(hdev); - - memset(&inq_cp, 0, sizeof(inq_cp)); - memcpy(&inq_cp.lap, lap, sizeof(inq_cp.lap)); - inq_cp.length = DISCOV_BREDR_INQUIRY_LEN; - hci_req_add(&req, HCI_OP_INQUIRY, sizeof(inq_cp), &inq_cp); - break; - - case DISCOV_TYPE_LE: - case DISCOV_TYPE_INTERLEAVED: - status = mgmt_le_support(hdev); - if (status) { - err = cmd_complete(sk, hdev->id, - MGMT_OP_START_DISCOVERY, status, - &cp->type, sizeof(cp->type)); - mgmt_pending_remove(cmd); - goto failed; - } - - if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED && - !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { - err = cmd_complete(sk, hdev->id, - MGMT_OP_START_DISCOVERY, - MGMT_STATUS_NOT_SUPPORTED, - &cp->type, sizeof(cp->type)); - mgmt_pending_remove(cmd); - goto failed; - } - - if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) { - /* Don't let discovery abort an outgoing - * connection attempt that's using directed - * advertising. - */ - if (hci_conn_hash_lookup_state(hdev, LE_LINK, - BT_CONNECT)) { - err = cmd_complete(sk, hdev->id, - MGMT_OP_START_DISCOVERY, - MGMT_STATUS_REJECTED, - &cp->type, - sizeof(cp->type)); - mgmt_pending_remove(cmd); - goto failed; - } - - disable_advertising(&req); - } - - /* If controller is scanning, it means the background scanning - * is running. Thus, we should temporarily stop it in order to - * set the discovery scanning parameters. - */ - if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) - hci_req_add_le_scan_disable(&req); - - memset(¶m_cp, 0, sizeof(param_cp)); - - /* All active scans will be done with either a resolvable - * private address (when privacy feature has been enabled) - * or unresolvable private address. - */ - err = hci_update_random_address(&req, true, &own_addr_type); - if (err < 0) { - err = cmd_complete(sk, hdev->id, - MGMT_OP_START_DISCOVERY, - MGMT_STATUS_FAILED, - &cp->type, sizeof(cp->type)); - mgmt_pending_remove(cmd); - goto failed; - } - - param_cp.type = LE_SCAN_ACTIVE; - param_cp.interval = cpu_to_le16(DISCOV_LE_SCAN_INT); - param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN); - param_cp.own_address_type = own_addr_type; - hci_req_add(&req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp), - ¶m_cp); - - memset(&enable_cp, 0, sizeof(enable_cp)); - enable_cp.enable = LE_SCAN_ENABLE; - enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE; - hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp), - &enable_cp); - break; - - default: + if (!trigger_discovery(&req, &status)) { err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY, - MGMT_STATUS_INVALID_PARAMS, - &cp->type, sizeof(cp->type)); + status, &cp->type, sizeof(cp->type)); mgmt_pending_remove(cmd); goto failed; } -- cgit v0.10.2 From a89612aac3cc6b13e6738bd70e05faf6f3fc8d9e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 5 Dec 2014 10:55:53 +0100 Subject: Bluetooth: Add HCI_RSSI_INVALID for unknown RSSI value The Bluetooth core specification defines the value 127 as invalid for RSSI values. So instead of hard coding it, lets add a constant for it. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 569c077..b6f7be1 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -412,6 +412,7 @@ enum { /* The core spec defines 127 as the "not available" value */ #define HCI_TX_POWER_INVALID 127 +#define HCI_RSSI_INVALID 127 #define HCI_ROLE_MASTER 0x00 #define HCI_ROLE_SLAVE 0x01 -- cgit v0.10.2 From 7e61df5423fe88a3e92bce59fb47686e52fb1d84 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 5 Dec 2014 10:55:54 +0100 Subject: Bluetooth: Add definitions for MGMT_OP_START_SERVICE_DISCOVERY This patch adds the opcode and structure for Start Service Discovery operation. Signed-off-by: Jakub Pawlowski Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 9b382ea..95c34d5 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -498,6 +498,15 @@ struct mgmt_cp_set_public_address { } __packed; #define MGMT_SET_PUBLIC_ADDRESS_SIZE 6 +#define MGMT_OP_START_SERVICE_DISCOVERY 0x003A +struct mgmt_cp_start_service_discovery { + __u8 type; + __s8 rssi; + __le16 uuid_count; + __u8 uuids[0][16]; +} __packed; +#define MGMT_START_SERVICE_DISCOVERY_SIZE 4 + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; -- cgit v0.10.2 From 37eab042be2196751ff108e6892951338b9a0969 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 5 Dec 2014 10:55:55 +0100 Subject: Bluetooth: Add extra discovery fields for storing filter information With the upcoming addition of support for Start Service Discovery, the discovery handling needs to filter on RSSI and UUID values. For that they need to be stored in the discovery handling. This patch adds the appropiate fields and also make sure they are reset when discovery has been stopped. Signed-off-by: Jakub Pawlowski Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 1dae700..83ca58b 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -75,6 +75,9 @@ struct discovery_state { u32 last_adv_flags; u8 last_adv_data[HCI_MAX_AD_LENGTH]; u8 last_adv_data_len; + s8 rssi; + u16 uuid_count; + u8 (*uuids)[16]; }; struct hci_conn_hash { @@ -503,6 +506,7 @@ static inline void discovery_init(struct hci_dev *hdev) INIT_LIST_HEAD(&hdev->discovery.all); INIT_LIST_HEAD(&hdev->discovery.unknown); INIT_LIST_HEAD(&hdev->discovery.resolve); + hdev->discovery.rssi = HCI_RSSI_INVALID; } bool hci_discovery_active(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index f001856..42f86dc 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2052,6 +2052,20 @@ void hci_discovery_set_state(struct hci_dev *hdev, int state) case DISCOVERY_STOPPED: hci_update_background_scan(hdev); + /* Reset RSSI and UUID filters to ensure Start Discovery + * and Start Service Discovery operate properly no matter + * which one started the previous discovery. + * + * While the Start Discovery and Start Service Discovery + * operations will set proper values for RSSI and UUID + * count, it is important to actually free the allocated + * list of UUIDs here. + */ + hdev->discovery.rssi = HCI_RSSI_INVALID; + hdev->discovery.uuid_count = 0; + kfree(hdev->discovery.uuids); + hdev->discovery.uuids = NULL; + if (old_state != DISCOVERY_STARTING) mgmt_discovering(hdev, 0); break; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 415ba41..b6a0f3e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3867,6 +3867,8 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, } hdev->discovery.type = cp->type; + hdev->discovery.rssi = HCI_RSSI_INVALID; + hdev->discovery.uuid_count = 0; hci_req_init(&req, hdev); -- cgit v0.10.2 From bda157a40077447b25a1172a17b8ef81a2905cb7 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 5 Dec 2014 10:55:56 +0100 Subject: Bluetooth: Filter device found events based on RSSI threshold Using Start Service Discovery allows to provide a RSSI threshold. This patch implements support for filtering out device found events based on the provided value. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index b6a0f3e..1715c91 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6821,6 +6821,15 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, return; } + /* When using service discovery with a RSSI threshold, then check + * if such a RSSI threshold is specified. If a RSSI threshold has + * been specified, then all results with a RSSI smaller than the + * RSSI threshold will be dropped. + */ + if (hdev->discovery.rssi != HCI_RSSI_INVALID && + rssi < hdev->discovery.rssi) + return; + /* Make sure that the buffer is big enough. The 5 extra bytes * are for the potential CoD field. */ -- cgit v0.10.2 From b487b9ce9340a6e98d7f8277399304b23b7be456 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 5 Dec 2014 10:55:57 +0100 Subject: Bluetooth: Add framework for device found filtering based on UUID Using Start Service Discovery provides the option to specifiy a list of UUID that are used to filter out device found events. This patch provides the framework for hooking up the UUID filter. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1715c91..e39190a 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6802,6 +6802,11 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, mgmt_pending_remove(cmd); } +static bool eir_has_uuids(u8 *eir, u16 eir_len, u16 uuid_count, u8 (*uuids)[16]) +{ + return false; +} + void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 *dev_class, s8 rssi, u32 flags, u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len) @@ -6809,6 +6814,7 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, char buf[512]; struct mgmt_ev_device_found *ev = (void *) buf; size_t ev_size; + bool match; /* Don't send events for a non-kernel initiated discovery. With * LE one exception is if we have pend_le_reports > 0 in which @@ -6843,15 +6849,59 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, ev->rssi = rssi; ev->flags = cpu_to_le32(flags); - if (eir_len > 0) + if (eir_len > 0) { + /* When using service discovery and a list of UUID is + * provided, results with no matching UUID should be + * dropped. In case there is a match the result is + * kept and checking possible scan response data + * will be skipped. + */ + if (hdev->discovery.uuid_count > 0) { + match = eir_has_uuids(eir, eir_len, + hdev->discovery.uuid_count, + hdev->discovery.uuids); + if (!match) + return; + } + + /* Copy EIR or advertising data into event */ memcpy(ev->eir, eir, eir_len); + } else { + /* When using service discovery and a list of UUID is + * provided, results with empty EIR or advertising data + * should be dropped since they do not match any UUID. + */ + if (hdev->discovery.uuid_count > 0) + return; + } if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV)) eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV, dev_class, 3); - if (scan_rsp_len > 0) + if (scan_rsp_len > 0) { + /* When using service discovery and a list of UUID is + * provided, results with no matching UUID should be + * dropped if there is no previous match from the + * advertising data. + */ + if (hdev->discovery.uuid_count > 0) { + if (!match && !eir_has_uuids(scan_rsp, scan_rsp_len, + hdev->discovery.uuid_count, + hdev->discovery.uuids)) + return; + } + + /* Append scan response data to event */ memcpy(ev->eir + eir_len, scan_rsp, scan_rsp_len); + } else { + /* When using service discovery and a list of UUID is + * provided, results with empty scan response and no + * previous matched advertising data should be dropped. + */ + if (hdev->discovery.uuid_count > 0 && !match) + return; + } ev->eir_len = cpu_to_le16(eir_len + scan_rsp_len); ev_size = sizeof(*ev) + eir_len + scan_rsp_len; -- cgit v0.10.2 From 799ce93df07bfc85296fc8357fd3a12fdfd4f175 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 5 Dec 2014 10:55:58 +0100 Subject: Bluetooth: Add logic for UUID filter handling The previous patch provided the framework for integrating the UUID filtering into the service discovery. This patch now provides the actual filter logic. Signed-off-by: Jakub Pawlowski Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e39190a..be4219d 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6802,8 +6802,78 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, mgmt_pending_remove(cmd); } +/* this is reversed hex representation of bluetooth base uuid. We need it for + * service uuid parsing in eir. + */ +static const u8 reverse_base_uuid[] = { + 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static inline bool has_uuid(u8 *uuid, u16 uuid_count, u8 (*uuids)[16]) +{ + int i; + + for (i = 0; i < uuid_count; i++) { + if (!memcmp(uuid, uuids[i], 16)) + return true; + } + + return false; +} + static bool eir_has_uuids(u8 *eir, u16 eir_len, u16 uuid_count, u8 (*uuids)[16]) { + u16 parsed = 0; + + while (parsed < eir_len) { + u8 field_len = eir[0]; + u8 uuid[16]; + int i; + + if (field_len == 0) + break; + + if (eir_len - parsed < field_len + 1) + break; + + switch (eir[1]) { + case EIR_UUID16_ALL: + case EIR_UUID16_SOME: + for (i = 0; i + 3 <= field_len; i += 2) { + memcpy(uuid, reverse_base_uuid, 16); + uuid[13] = eir[i + 3]; + uuid[12] = eir[i + 2]; + if (has_uuid(uuid, uuid_count, uuids)) + return true; + } + break; + case EIR_UUID32_ALL: + case EIR_UUID32_SOME: + for (i = 0; i + 5 <= field_len; i += 4) { + memcpy(uuid, reverse_base_uuid, 16); + uuid[15] = eir[i + 5]; + uuid[14] = eir[i + 4]; + uuid[13] = eir[i + 3]; + uuid[12] = eir[i + 2]; + if (has_uuid(uuid, uuid_count, uuids)) + return true; + } + break; + case EIR_UUID128_ALL: + case EIR_UUID128_SOME: + for (i = 0; i + 17 <= field_len; i += 16) { + memcpy(uuid, eir + i + 2, 16); + if (has_uuid(uuid, uuid_count, uuids)) + return true; + } + break; + } + + parsed += field_len + 1; + eir += field_len + 1; + } + return false; } -- cgit v0.10.2 From 66ea9427e08cb0a856006012b15e6b93ce379115 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 5 Dec 2014 10:55:59 +0100 Subject: Bluetooth: Add support for Start Service Discovery command This patch adds support for the Start Service Discovery command. It does all the checks for command parameters and configured the discovery filter settings correctly. However the actual support for filtering will be added with another patch. Signed-off-by: Jakub Pawlowski Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index be4219d..104c4cc 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -93,6 +93,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_READ_CONFIG_INFO, MGMT_OP_SET_EXTERNAL_CONFIG, MGMT_OP_SET_PUBLIC_ADDRESS, + MGMT_OP_START_SERVICE_DISCOVERY, }; static const u16 mgmt_events[] = { @@ -3793,6 +3794,9 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status) hci_dev_lock(hdev); cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev); + if (!cmd) + cmd = mgmt_pending_find(MGMT_OP_START_SERVICE_DISCOVERY, hdev); + if (cmd) { u8 type = hdev->discovery.type; @@ -3892,6 +3896,107 @@ failed: return err; } +static int start_service_discovery(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_start_service_discovery *cp = data; + struct pending_cmd *cmd; + struct hci_request req; + const u16 max_uuid_count = ((U16_MAX - sizeof(*cp)) / 16); + u16 uuid_count, expected_len; + u8 status; + int err; + + BT_DBG("%s", hdev->name); + + hci_dev_lock(hdev); + + if (!hdev_is_powered(hdev)) { + err = cmd_complete(sk, hdev->id, + MGMT_OP_START_SERVICE_DISCOVERY, + MGMT_STATUS_NOT_POWERED, + &cp->type, sizeof(cp->type)); + goto failed; + } + + if (hdev->discovery.state != DISCOVERY_STOPPED || + test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) { + err = cmd_complete(sk, hdev->id, + MGMT_OP_START_SERVICE_DISCOVERY, + MGMT_STATUS_BUSY, &cp->type, + sizeof(cp->type)); + goto failed; + } + + uuid_count = __le16_to_cpu(cp->uuid_count); + if (uuid_count > max_uuid_count) { + BT_ERR("service_discovery: too big uuid_count value %u", + uuid_count); + err = cmd_complete(sk, hdev->id, + MGMT_OP_START_SERVICE_DISCOVERY, + MGMT_STATUS_INVALID_PARAMS, &cp->type, + sizeof(cp->type)); + goto failed; + } + + expected_len = sizeof(*cp) + uuid_count * 16; + if (expected_len != len) { + BT_ERR("service_discovery: expected %u bytes, got %u bytes", + expected_len, len); + err = cmd_complete(sk, hdev->id, + MGMT_OP_START_SERVICE_DISCOVERY, + MGMT_STATUS_INVALID_PARAMS, &cp->type, + sizeof(cp->type)); + goto failed; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_START_SERVICE_DISCOVERY, + hdev, NULL, 0); + if (!cmd) { + err = -ENOMEM; + goto failed; + } + + hdev->discovery.type = cp->type; + hdev->discovery.rssi = cp->rssi; + hdev->discovery.uuid_count = uuid_count; + + if (uuid_count > 0) { + hdev->discovery.uuids = kmemdup(cp->uuids, uuid_count * 16, + GFP_KERNEL); + if (!hdev->discovery.uuids) { + err = cmd_complete(sk, hdev->id, + MGMT_OP_START_SERVICE_DISCOVERY, + MGMT_STATUS_FAILED, + &cp->type, sizeof(cp->type)); + mgmt_pending_remove(cmd); + goto failed; + } + } + + hci_req_init(&req, hdev); + + if (!trigger_discovery(&req, &status)) { + err = cmd_complete(sk, hdev->id, + MGMT_OP_START_SERVICE_DISCOVERY, + status, &cp->type, sizeof(cp->type)); + mgmt_pending_remove(cmd); + goto failed; + } + + err = hci_req_run(&req, start_discovery_complete); + if (err < 0) { + mgmt_pending_remove(cmd); + goto failed; + } + + hci_discovery_set_state(hdev, DISCOVERY_STARTING); + +failed: + hci_dev_unlock(hdev); + return err; +} + static void stop_discovery_complete(struct hci_dev *hdev, u8 status) { struct pending_cmd *cmd; @@ -5726,6 +5831,7 @@ static const struct mgmt_handler { { read_config_info, false, MGMT_READ_CONFIG_INFO_SIZE }, { set_external_config, false, MGMT_SET_EXTERNAL_CONFIG_SIZE }, { set_public_address, false, MGMT_SET_PUBLIC_ADDRESS_SIZE }, + { start_service_discovery,true, MGMT_START_SERVICE_DISCOVERY_SIZE }, }; int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) -- cgit v0.10.2 From 0256325ed666af8346c89deb9d437c2209f463cb Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 5 Dec 2014 11:45:21 +0100 Subject: Bluetooth: Add helper function for clearing the discovery filter The discovery filter allocates memory for its UUID list. So use a helper function to free it and reset it to default states. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 83ca58b..f07b145 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -509,6 +509,14 @@ static inline void discovery_init(struct hci_dev *hdev) hdev->discovery.rssi = HCI_RSSI_INVALID; } +static inline void hci_discovery_filter_clear(struct hci_dev *hdev) +{ + hdev->discovery.rssi = HCI_RSSI_INVALID; + hdev->discovery.uuid_count = 0; + kfree(hdev->discovery.uuids); + hdev->discovery.uuids = NULL; +} + bool hci_discovery_active(struct hci_dev *hdev); void hci_discovery_set_state(struct hci_dev *hdev, int state); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 42f86dc..3c81b5c 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2061,10 +2061,7 @@ void hci_discovery_set_state(struct hci_dev *hdev, int state) * count, it is important to actually free the allocated * list of UUIDs here. */ - hdev->discovery.rssi = HCI_RSSI_INVALID; - hdev->discovery.uuid_count = 0; - kfree(hdev->discovery.uuids); - hdev->discovery.uuids = NULL; + hci_discovery_filter_clear(hdev); if (old_state != DISCOVERY_STARTING) mgmt_discovering(hdev, 0); -- cgit v0.10.2 From 22078800c337cf374d58b63a5b1f670d470e6dbf Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 5 Dec 2014 11:45:22 +0100 Subject: Bluetooth: Fix memory leaks from discovery filter UUID list In case of failure or when unplugging a controller, the allocated memory for the UUID list of the discovery filter is not freed. Use the newly introduced helper for reset the discovery filter and with that also freeing existing memory. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 3c81b5c..8b3f839 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -4255,6 +4255,7 @@ void hci_unregister_dev(struct hci_dev *hdev) hci_remote_oob_data_clear(hdev); hci_bdaddr_list_clear(&hdev->le_white_list); hci_conn_params_clear_all(hdev); + hci_discovery_filter_clear(hdev); hci_dev_unlock(hdev); hci_dev_put(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 104c4cc..74571a4 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3870,9 +3870,12 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, goto failed; } + /* Clear the discovery filter first to free any previously + * allocated memory for the UUID list. + */ + hci_discovery_filter_clear(hdev); + hdev->discovery.type = cp->type; - hdev->discovery.rssi = HCI_RSSI_INVALID; - hdev->discovery.uuid_count = 0; hci_req_init(&req, hdev); @@ -3957,6 +3960,11 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev, goto failed; } + /* Clear the discovery filter first to free any previously + * allocated memory for the UUID list. + */ + hci_discovery_filter_clear(hdev); + hdev->discovery.type = cp->type; hdev->discovery.rssi = cp->rssi; hdev->discovery.uuid_count = uuid_count; -- cgit v0.10.2 From ee3c3ca5ba2fdff10f3f71b4f9923807a0983564 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 5 Dec 2014 11:45:23 +0100 Subject: Bluetooth: Clear discovery filter before starting background scan Currently the discovery filter information are only cleared when the actual discovery procedure has been stopped. To make sure that none of the filters interfere with the background scanning and its device found event reporting, clear the filter before starting background scanning. This means that the discovery filter is now cleared before either Start Discovery, Start Service Discovery or background scanning. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 8b3f839..523700e 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2052,17 +2052,6 @@ void hci_discovery_set_state(struct hci_dev *hdev, int state) case DISCOVERY_STOPPED: hci_update_background_scan(hdev); - /* Reset RSSI and UUID filters to ensure Start Discovery - * and Start Service Discovery operate properly no matter - * which one started the previous discovery. - * - * While the Start Discovery and Start Service Discovery - * operations will set proper values for RSSI and UUID - * count, it is important to actually free the allocated - * list of UUIDs here. - */ - hci_discovery_filter_clear(hdev); - if (old_state != DISCOVERY_STARTING) mgmt_discovering(hdev, 0); break; @@ -5679,6 +5668,15 @@ void hci_update_background_scan(struct hci_dev *hdev) if (hdev->discovery.state != DISCOVERY_STOPPED) return; + /* Reset RSSI and UUID filters when starting background scanning + * since these filters are meant for service discovery only. + * + * The Start Discovery and Start Service Discovery operations + * ensure to set proper values for RSSI threshold and UUID + * filter list. So it is safe to just reset them here. + */ + hci_discovery_filter_clear(hdev); + hci_req_init(&req, hdev); if (list_empty(&hdev->pend_le_conns) && -- cgit v0.10.2 From a413e3fd4bbff78f25d1d581a43fa3662989f6f9 Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Thu, 4 Dec 2014 11:51:57 +0100 Subject: net/6lowpan: Remove FSF address from GPL statement. This might change and we already deliver a copy of the license with the kernel. This was already removed form the ieee802154 code but missed here. Signed-off-by: Stefan Schmidt Acked-by: Alexander Aring Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c index aced97d..32ffec6 100644 --- a/net/6lowpan/iphc.c +++ b/net/6lowpan/iphc.c @@ -15,9 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* Jon's code is based on 6lowpan implementation for Contiki which is: -- cgit v0.10.2 From cad865dc4bae8e6a257196fb0bab2f1154c00a14 Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Thu, 4 Dec 2014 11:51:58 +0100 Subject: net/ieee802154: Make sure alignment matches parenthesis.. Follow coding style of the kernel. Signed-off-by: Stefan Schmidt Acked-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index 6ad0da3..3cdfa78 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -73,7 +73,7 @@ out: } struct sk_buff *ieee802154_nl_new_reply(struct genl_info *info, - int flags, u8 req) + int flags, u8 req) { void *hdr; struct sk_buff *msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); -- cgit v0.10.2 From 6c10dc05b852c4cefa1b2b2fecc850b2110934e7 Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Thu, 4 Dec 2014 11:51:59 +0100 Subject: net/ieee802154: Remove and add extra blank lines as needed. Some have been missing and some have been needed. Just cosmetics. Signed-off-by: Stefan Schmidt Acked-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c index 16f5e91..27eaa65e 100644 --- a/net/ieee802154/6lowpan_rtnl.c +++ b/net/ieee802154/6lowpan_rtnl.c @@ -439,7 +439,6 @@ static void lowpan_set_lockdep_class_one(struct net_device *dev, &lowpan_netdev_xmit_lock_key); } - static int lowpan_dev_init(struct net_device *dev) { netdev_for_each_tx_queue(dev, lowpan_set_lockdep_class_one, NULL); diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c index 26da1e1..d0a1282 100644 --- a/net/ieee802154/af_ieee802154.c +++ b/net/ieee802154/af_ieee802154.c @@ -99,6 +99,7 @@ static int ieee802154_sock_release(struct socket *sock) } return 0; } + static int ieee802154_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) { @@ -231,7 +232,6 @@ static const struct proto_ops ieee802154_dgram_ops = { #endif }; - /* Create a socket. Initialise the socket, blank the addresses * set the state. */ @@ -320,7 +320,6 @@ drop: return NET_RX_DROP; } - static struct packet_type ieee802154_packet_type = { .type = htons(ETH_P_IEEE802154), .func = ieee802154_rcv, @@ -354,6 +353,7 @@ err_dgram: out: return rc; } + static void __exit af_ieee802154_remove(void) { dev_remove_pack(&ieee802154_packet_type); diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c index 3d58bef..8db240b 100644 --- a/net/ieee802154/dgram.c +++ b/net/ieee802154/dgram.c @@ -154,7 +154,6 @@ static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg) spin_unlock_bh(&sk->sk_receive_queue.lock); return put_user(amount, (int __user *)arg); } - } return -ENOIOCTLCMD; diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index 3cdfa78..fa14647 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -147,7 +147,6 @@ static const struct genl_multicast_group ieee802154_mcgrps[] = { [IEEE802154_BEACON_MCGRP] = { .name = IEEE802154_MCAST_BEACON_NAME, }, }; - int __init ieee802154_nl_init(void) { return genl_register_family_with_ops_groups(&nl802154_family, diff --git a/net/ieee802154/nl-mac.c b/net/ieee802154/nl-mac.c index 1f085ed..cd91949 100644 --- a/net/ieee802154/nl-mac.c +++ b/net/ieee802154/nl-mac.c @@ -346,7 +346,6 @@ int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info) else page = 0; - if (addr.short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST)) { ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS); dev_put(dev); @@ -397,7 +396,6 @@ int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info) else page = 0; - ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, page, duration); @@ -548,8 +546,6 @@ out: return rc; } - - static int ieee802154_llsec_parse_key_id(struct genl_info *info, struct ieee802154_llsec_key_id *desc) @@ -765,8 +761,6 @@ out: return rc; } - - struct llsec_dump_data { struct sk_buff *skb; int s_idx, s_idx2; @@ -843,8 +837,6 @@ ieee802154_nl_llsec_change(struct sk_buff *skb, struct genl_info *info, return rc; } - - static int ieee802154_llsec_parse_key(struct genl_info *info, struct ieee802154_llsec_key *key) @@ -989,8 +981,6 @@ int ieee802154_llsec_dump_keys(struct sk_buff *skb, struct netlink_callback *cb) return ieee802154_llsec_dump_table(skb, cb, llsec_iter_keys); } - - static int llsec_parse_dev(struct genl_info *info, struct ieee802154_llsec_device *dev) @@ -1121,8 +1111,6 @@ int ieee802154_llsec_dump_devs(struct sk_buff *skb, struct netlink_callback *cb) return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devs); } - - static int llsec_add_devkey(struct net_device *dev, struct genl_info *info) { struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); @@ -1237,8 +1225,6 @@ int ieee802154_llsec_dump_devkeys(struct sk_buff *skb, return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devkeys); } - - static int llsec_parse_seclevel(struct genl_info *info, struct ieee802154_llsec_seclevel *sl) diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c index 6246354..7baf98b 100644 --- a/net/ieee802154/nl-phy.c +++ b/net/ieee802154/nl-phy.c @@ -94,7 +94,6 @@ int ieee802154_list_phy(struct sk_buff *skb, struct genl_info *info) if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0') return -EINVAL; /* phy name should be null-terminated */ - phy = wpan_phy_find(name); if (!phy) return -ENODEV; diff --git a/net/ieee802154/raw.c b/net/ieee802154/raw.c index 3ffcf4a..f0c61e5 100644 --- a/net/ieee802154/raw.c +++ b/net/ieee802154/raw.c @@ -221,7 +221,6 @@ static int raw_rcv_skb(struct sock *sk, struct sk_buff *skb) return NET_RX_SUCCESS; } - void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb) { struct sock *sk; -- cgit v0.10.2 From a4dc132196ba776d3c34ef91b484e7fa19c0f994 Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Thu, 4 Dec 2014 11:52:00 +0100 Subject: net/mac802154: Remove extra blank lines. Signed-off-by: Stefan Schmidt Acked-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c index fa0d523..4a661f7 100644 --- a/net/mac802154/llsec.c +++ b/net/mac802154/llsec.c @@ -75,8 +75,6 @@ void mac802154_llsec_destroy(struct mac802154_llsec *sec) } } - - int mac802154_llsec_get_params(struct mac802154_llsec *sec, struct ieee802154_llsec_params *params) { @@ -117,8 +115,6 @@ int mac802154_llsec_set_params(struct mac802154_llsec *sec, return 0; } - - static struct mac802154_llsec_key* llsec_key_alloc(const struct ieee802154_llsec_key *template) { @@ -294,8 +290,6 @@ int mac802154_llsec_key_del(struct mac802154_llsec *sec, return -ENOENT; } - - static bool llsec_dev_use_shortaddr(__le16 short_addr) { return short_addr != cpu_to_le16(IEEE802154_ADDR_UNDEF) && @@ -411,8 +405,6 @@ int mac802154_llsec_dev_del(struct mac802154_llsec *sec, __le64 device_addr) return 0; } - - static struct mac802154_llsec_device_key* llsec_devkey_find(struct mac802154_llsec_device *dev, const struct ieee802154_llsec_key_id *key) @@ -475,8 +467,6 @@ int mac802154_llsec_devkey_del(struct mac802154_llsec *sec, return 0; } - - static struct mac802154_llsec_seclevel* llsec_find_seclevel(const struct mac802154_llsec *sec, const struct ieee802154_llsec_seclevel *sl) @@ -532,8 +522,6 @@ int mac802154_llsec_seclevel_del(struct mac802154_llsec *sec, return 0; } - - static int llsec_recover_addr(struct mac802154_llsec *sec, struct ieee802154_addr *addr) { @@ -609,7 +597,6 @@ found: return llsec_key_get(key); } - static void llsec_geniv(u8 iv[16], __le64 addr, const struct ieee802154_sechdr *sec) { @@ -786,8 +773,6 @@ fail: return rc; } - - static struct mac802154_llsec_device* llsec_lookup_dev(struct mac802154_llsec *sec, const struct ieee802154_addr *addr) diff --git a/net/mac802154/mib.c b/net/mac802154/mib.c index 3596b29..5cf019a 100644 --- a/net/mac802154/mib.c +++ b/net/mac802154/mib.c @@ -104,7 +104,6 @@ void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan) } } - int mac802154_get_params(struct net_device *dev, struct ieee802154_llsec_params *params) { @@ -136,7 +135,6 @@ int mac802154_set_params(struct net_device *dev, return res; } - int mac802154_add_key(struct net_device *dev, const struct ieee802154_llsec_key_id *id, const struct ieee802154_llsec_key *key) @@ -168,7 +166,6 @@ int mac802154_del_key(struct net_device *dev, return res; } - int mac802154_add_dev(struct net_device *dev, const struct ieee802154_llsec_device *llsec_dev) { @@ -198,7 +195,6 @@ int mac802154_del_dev(struct net_device *dev, __le64 dev_addr) return res; } - int mac802154_add_devkey(struct net_device *dev, __le64 device_addr, const struct ieee802154_llsec_device_key *key) @@ -231,7 +227,6 @@ int mac802154_del_devkey(struct net_device *dev, return res; } - int mac802154_add_seclevel(struct net_device *dev, const struct ieee802154_llsec_seclevel *sl) { @@ -262,7 +257,6 @@ int mac802154_del_seclevel(struct net_device *dev, return res; } - void mac802154_lock_table(struct net_device *dev) { struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); -- cgit v0.10.2 From 1db996045213e02a5c5e31ef6b07b3154b4f9bd9 Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Thu, 4 Dec 2014 11:52:01 +0100 Subject: net/mac802154: No need for an extra space when casting Coding style cleanup. Signed-off-by: Stefan Schmidt Acked-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c index 4a661f7..dcf7395 100644 --- a/net/mac802154/llsec.c +++ b/net/mac802154/llsec.c @@ -298,12 +298,12 @@ static bool llsec_dev_use_shortaddr(__le16 short_addr) static u32 llsec_dev_hash_short(__le16 short_addr, __le16 pan_id) { - return ((__force u16) short_addr) << 16 | (__force u16) pan_id; + return ((__force u16)short_addr) << 16 | (__force u16)pan_id; } static u64 llsec_dev_hash_long(__le64 hwaddr) { - return (__force u64) hwaddr; + return (__force u64)hwaddr; } static struct mac802154_llsec_device* -- cgit v0.10.2 From 1b9b5ee53023b7299495c01fbee17f1985ec0d47 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 5 Dec 2014 13:36:00 +0200 Subject: Bluetooth: Add callback to create proper cmd_complete events We've got a couple of generic scenarios where all pending mgmt commands are processed and responses are sent to them. These scenarios are powering off the adapter and removing the adapter. So far the code has been generating cmd_status responses with NOT_POWERED and INVALID_INDEX resposes respectively, but this violates the mgmt specification for commands that should always generate a cmd_complete. This patch adds support for specifying a callback for the pending_cmd context that each command handler can use for command-specific cmd_complete event generation. The actual per-command event generators will come in subsequent patches. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 74571a4..98537b0 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -137,6 +137,7 @@ struct pending_cmd { void *param; struct sock *sk; void *user_data; + void (*cmd_complete)(struct pending_cmd *cmd, u8 status); }; /* HCI to MGMT error code conversion table */ @@ -1471,6 +1472,20 @@ static void cmd_status_rsp(struct pending_cmd *cmd, void *data) mgmt_pending_remove(cmd); } +static void cmd_complete_rsp(struct pending_cmd *cmd, void *data) +{ + if (cmd->cmd_complete) { + u8 *status = data; + + cmd->cmd_complete(cmd, *status); + mgmt_pending_remove(cmd); + + return; + } + + cmd_status_rsp(cmd, data); +} + static u8 mgmt_bredr_support(struct hci_dev *hdev) { if (!lmp_bredr_capable(hdev)) @@ -5976,7 +5991,7 @@ void mgmt_index_removed(struct hci_dev *hdev) if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) return; - mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); + mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status); if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) mgmt_event(MGMT_EV_UNCONF_INDEX_REMOVED, hdev, NULL, 0, NULL); @@ -6111,7 +6126,7 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) } mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); - mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status_not_powered); + mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status_not_powered); if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0) mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, -- cgit v0.10.2 From 323b0b885b5586a39a288d8f10c3a6c7ba901282 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 5 Dec 2014 13:36:01 +0200 Subject: Bluetooth: Store parameter length with pending mgmt commands As preparation for making generic cmd_complete responses possible we'll need to track the parameter length in addition to just a pointer to them. This patch adds the necessary variable to the pending_cmd struct. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 98537b0..56c7838 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -135,6 +135,7 @@ struct pending_cmd { u16 opcode; int index; void *param; + size_t param_len; struct sock *sk; void *user_data; void (*cmd_complete)(struct pending_cmd *cmd, u8 status); @@ -1205,14 +1206,13 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode, cmd->opcode = opcode; cmd->index = hdev->id; - cmd->param = kmalloc(len, GFP_KERNEL); + cmd->param = kmemdup(data, len, GFP_KERNEL); if (!cmd->param) { kfree(cmd); return NULL; } - if (data) - memcpy(cmd->param, data, len); + cmd->param_len = len; cmd->sk = sk; sock_hold(sk); -- cgit v0.10.2 From f5818c2241247c0a5f967e41e952682b10db6fd6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 5 Dec 2014 13:36:02 +0200 Subject: Bluetooth: Convert Disconnect mgmt command to use cmd_complete callback This patch converts the Disconnect mgmt command to take advantage of the new cmd_complete callback that's part of the pending_cmd struct. There are many commands whose response parameters map 1:1 to the command parameters and Disconnect is one of them. This patch adds a generic_cmd_complete() function for such commands that can be reused in subsequent patches. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 56c7838..81b2886 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1486,6 +1486,12 @@ static void cmd_complete_rsp(struct pending_cmd *cmd, void *data) cmd_status_rsp(cmd, data); } +static void generic_cmd_complete(struct pending_cmd *cmd, u8 status) +{ + cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param, + cmd->param_len); +} + static u8 mgmt_bredr_support(struct hci_dev *hdev) { if (!lmp_bredr_capable(hdev)) @@ -2872,6 +2878,8 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data, goto failed; } + cmd->cmd_complete = generic_cmd_complete; + err = hci_disconnect(conn, HCI_ERROR_REMOTE_USER_TERM); if (err < 0) mgmt_pending_remove(cmd); @@ -6396,15 +6404,9 @@ void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn, static void disconnect_rsp(struct pending_cmd *cmd, void *data) { - struct mgmt_cp_disconnect *cp = cmd->param; struct sock **sk = data; - struct mgmt_rp_disconnect rp; - bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); - rp.addr.type = cp->addr.type; - - cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, 0, &rp, - sizeof(rp)); + cmd->cmd_complete(cmd, 0); *sk = cmd->sk; sock_hold(*sk); @@ -6486,7 +6488,6 @@ void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, { u8 bdaddr_type = link_to_bdaddr(link_type, addr_type); struct mgmt_cp_disconnect *cp; - struct mgmt_rp_disconnect rp; struct pending_cmd *cmd; mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp, @@ -6504,12 +6505,7 @@ void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, if (cp->addr.type != bdaddr_type) return; - bacpy(&rp.addr.bdaddr, bdaddr); - rp.addr.type = bdaddr_type; - - cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, - mgmt_status(status), &rp, sizeof(rp)); - + cmd->cmd_complete(cmd, mgmt_status(status)); mgmt_pending_remove(cmd); } -- cgit v0.10.2 From 7776d1d8051469e61c8784076921ee16c1093ba1 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 5 Dec 2014 13:36:03 +0200 Subject: Bluetooth: Use cmd_complete callback for authentication mgmt commands This patch converts the user confirmation & PIN code mgmt commands to take advantage of the new cmd_complete callback for pending mgmt commands. The patch also adds a new generic addr_cmd_complete() helper function to be used with commands that send a mgmt_addr_info response based on a mgmt_addr_info in the beginning of the command parameters. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 81b2886..0fc3d69 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1492,6 +1492,12 @@ static void generic_cmd_complete(struct pending_cmd *cmd, u8 status) cmd->param_len); } +static void addr_cmd_complete(struct pending_cmd *cmd, u8 status) +{ + cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param, + sizeof(struct mgmt_addr_info)); +} + static u8 mgmt_bredr_support(struct hci_dev *hdev) { if (!lmp_bredr_capable(hdev)) @@ -3032,6 +3038,8 @@ static int pin_code_reply(struct sock *sk, struct hci_dev *hdev, void *data, goto failed; } + cmd->cmd_complete = addr_cmd_complete; + bacpy(&reply.bdaddr, &cp->addr.bdaddr); reply.pin_len = cp->pin_len; memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code)); @@ -3363,6 +3371,8 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev, goto done; } + cmd->cmd_complete = addr_cmd_complete; + /* Continue with pairing via HCI */ if (hci_op == HCI_OP_USER_PASSKEY_REPLY) { struct hci_cp_user_passkey_reply cp; @@ -6544,18 +6554,12 @@ void mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status) { struct pending_cmd *cmd; - struct mgmt_rp_pin_code_reply rp; cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev); if (!cmd) return; - bacpy(&rp.addr.bdaddr, bdaddr); - rp.addr.type = BDADDR_BREDR; - - cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, - mgmt_status(status), &rp, sizeof(rp)); - + cmd->cmd_complete(cmd, mgmt_status(status)); mgmt_pending_remove(cmd); } @@ -6563,18 +6567,12 @@ void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status) { struct pending_cmd *cmd; - struct mgmt_rp_pin_code_reply rp; cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev); if (!cmd) return; - bacpy(&rp.addr.bdaddr, bdaddr); - rp.addr.type = BDADDR_BREDR; - - cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, - mgmt_status(status), &rp, sizeof(rp)); - + cmd->cmd_complete(cmd, mgmt_status(status)); mgmt_pending_remove(cmd); } @@ -6614,21 +6612,15 @@ static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 opcode) { struct pending_cmd *cmd; - struct mgmt_rp_user_confirm_reply rp; - int err; cmd = mgmt_pending_find(opcode, hdev); if (!cmd) return -ENOENT; - bacpy(&rp.addr.bdaddr, bdaddr); - rp.addr.type = link_to_bdaddr(link_type, addr_type); - err = cmd_complete(cmd->sk, hdev->id, opcode, mgmt_status(status), - &rp, sizeof(rp)); - + cmd->cmd_complete(cmd, mgmt_status(status)); mgmt_pending_remove(cmd); - return err; + return 0; } int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, -- cgit v0.10.2 From 04ab2749ea96a79f5eba3ca26d46f4283b5509a6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 5 Dec 2014 13:36:04 +0200 Subject: Bluetooth: Convert Pair Device to use cmd_complete callback This patch converts the Pair Device mgmt command to use the new cmd_complete callback for pending mgmt commands. The already existing pairing_complete() function is exactly what's needed and doesn't need changing. In addition to getting the return parameters always right this patch actually fixes a reference counting bug and memory leak with the hci_conn that's attached to the pending mgmt command - something that would occur when powering off or unplugging the adapter while pairing is in progress. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 0fc3d69..d3ee728 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3129,7 +3129,7 @@ void mgmt_smp_complete(struct hci_conn *conn, bool complete) cmd = find_pairing(conn); if (cmd) - pairing_complete(cmd, status); + cmd->cmd_complete(cmd, status); } static void pairing_complete_cb(struct hci_conn *conn, u8 status) @@ -3142,7 +3142,7 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status) if (!cmd) BT_DBG("Unable to find a pending command"); else - pairing_complete(cmd, mgmt_status(status)); + cmd->cmd_complete(cmd, mgmt_status(status)); } static void le_pairing_complete_cb(struct hci_conn *conn, u8 status) @@ -3158,7 +3158,7 @@ static void le_pairing_complete_cb(struct hci_conn *conn, u8 status) if (!cmd) BT_DBG("Unable to find a pending command"); else - pairing_complete(cmd, mgmt_status(status)); + cmd->cmd_complete(cmd, mgmt_status(status)); } static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, @@ -3255,6 +3255,8 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, goto unlock; } + cmd->cmd_complete = pairing_complete; + /* For LE, just connecting isn't a proof that the pairing finished */ if (cp->addr.type == BDADDR_BREDR) { conn->connect_cfm_cb = pairing_complete_cb; -- cgit v0.10.2 From d8b7b1e49abe98d67f589d6326658a48d810f875 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 5 Dec 2014 13:36:05 +0200 Subject: Bluetooth: Convert Unpair Device to use cmd_complete callback This patch updates the Unpair Device code to take advantage of the cmd_complete callback of struct pending_cmd. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d3ee728..1accbb9 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2821,6 +2821,8 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, goto unlock; } + cmd->cmd_complete = addr_cmd_complete; + dc.handle = cpu_to_le16(conn->handle); dc.reason = 0x13; /* Remote User Terminated Connection */ err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc); @@ -6430,16 +6432,10 @@ static void unpair_device_rsp(struct pending_cmd *cmd, void *data) { struct hci_dev *hdev = data; struct mgmt_cp_unpair_device *cp = cmd->param; - struct mgmt_rp_unpair_device rp; - - memset(&rp, 0, sizeof(rp)); - bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); - rp.addr.type = cp->addr.type; device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, cmd->sk); - cmd_complete(cmd->sk, cmd->index, cmd->opcode, 0, &rp, sizeof(rp)); - + cmd->cmd_complete(cmd, 0); mgmt_pending_remove(cmd); } -- cgit v0.10.2 From 2922a94fcc04f26aee1b279d38e93d4a5c295f25 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 5 Dec 2014 13:36:06 +0200 Subject: Bluetooth: Convert discovery commands to use cmd_complete callback This patch converts the Start/Stop Discovery mgmt commands to use the cmd_complete callback of struct pending_cmd. Since both of these commands return the same parameters as they take as input we can use the existing generic_cmd_complete() helper for this. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1accbb9..ddaeebb 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3835,10 +3835,7 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status) cmd = mgmt_pending_find(MGMT_OP_START_SERVICE_DISCOVERY, hdev); if (cmd) { - u8 type = hdev->discovery.type; - - cmd_complete(cmd->sk, hdev->id, cmd->opcode, - mgmt_status(status), &type, sizeof(type)); + cmd->cmd_complete(cmd, mgmt_status(status)); mgmt_pending_remove(cmd); } @@ -3901,12 +3898,14 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, goto failed; } - cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, NULL, 0); + cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; } + cmd->cmd_complete = generic_cmd_complete; + /* Clear the discovery filter first to free any previously * allocated memory for the UUID list. */ @@ -3936,6 +3935,11 @@ failed: return err; } +static void service_discovery_cmd_complete(struct pending_cmd *cmd, u8 status) +{ + cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param, 1); +} + static int start_service_discovery(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { @@ -3991,12 +3995,14 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev, } cmd = mgmt_pending_add(sk, MGMT_OP_START_SERVICE_DISCOVERY, - hdev, NULL, 0); + hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; } + cmd->cmd_complete = service_discovery_cmd_complete; + /* Clear the discovery filter first to free any previously * allocated memory for the UUID list. */ @@ -4052,10 +4058,7 @@ static void stop_discovery_complete(struct hci_dev *hdev, u8 status) cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev); if (cmd) { - u8 type = hdev->discovery.type; - - cmd_complete(cmd->sk, hdev->id, cmd->opcode, - mgmt_status(status), &type, sizeof(type)); + cmd->cmd_complete(cmd, mgmt_status(status)); mgmt_pending_remove(cmd); } @@ -4091,12 +4094,14 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data, goto unlock; } - cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0); + cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, data, len); if (!cmd) { err = -ENOMEM; goto unlock; } + cmd->cmd_complete = generic_cmd_complete; + hci_req_init(&req, hdev); hci_stop_discovery(&req); -- cgit v0.10.2 From 69487371d14b667c437d1a08b2c8a92738d12992 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 5 Dec 2014 13:36:07 +0200 Subject: Bluetooth: Convert Get Clock Info to use cmd_complete callback This patch converts the Get Clock Information mgmt command to take advantage of the new cmd_complete callback for pending commands. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ddaeebb..8467d35 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5287,10 +5287,40 @@ unlock: return err; } -static void get_clock_info_complete(struct hci_dev *hdev, u8 status) +static void clock_info_cmd_complete(struct pending_cmd *cmd, u8 status) { - struct mgmt_cp_get_clock_info *cp; + struct hci_conn *conn = cmd->user_data; struct mgmt_rp_get_clock_info rp; + struct hci_dev *hdev; + + memset(&rp, 0, sizeof(rp)); + memcpy(&rp.addr, &cmd->param, sizeof(rp.addr)); + + if (status) + goto complete; + + hdev = hci_dev_get(cmd->index); + if (hdev) { + rp.local_clock = cpu_to_le32(hdev->clock); + hci_dev_put(hdev); + } + + if (conn) { + rp.piconet_clock = cpu_to_le32(conn->clock); + rp.accuracy = cpu_to_le16(conn->clock_accuracy); + } + +complete: + cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, &rp, sizeof(rp)); + + if (conn) { + hci_conn_drop(conn); + hci_conn_put(conn); + } +} + +static void get_clock_info_complete(struct hci_dev *hdev, u8 status) +{ struct hci_cp_read_clock *hci_cp; struct pending_cmd *cmd; struct hci_conn *conn; @@ -5314,29 +5344,8 @@ static void get_clock_info_complete(struct hci_dev *hdev, u8 status) if (!cmd) goto unlock; - cp = cmd->param; - - memset(&rp, 0, sizeof(rp)); - memcpy(&rp.addr, &cp->addr, sizeof(rp.addr)); - - if (status) - goto send_rsp; - - rp.local_clock = cpu_to_le32(hdev->clock); - - if (conn) { - rp.piconet_clock = cpu_to_le32(conn->clock); - rp.accuracy = cpu_to_le16(conn->clock_accuracy); - } - -send_rsp: - cmd_complete(cmd->sk, cmd->index, cmd->opcode, mgmt_status(status), - &rp, sizeof(rp)); + cmd->cmd_complete(cmd, mgmt_status(status)); mgmt_pending_remove(cmd); - if (conn) { - hci_conn_drop(conn); - hci_conn_put(conn); - } unlock: hci_dev_unlock(hdev); @@ -5392,6 +5401,8 @@ static int get_clock_info(struct sock *sk, struct hci_dev *hdev, void *data, goto unlock; } + cmd->cmd_complete = clock_info_cmd_complete; + hci_req_init(&req, hdev); memset(&hci_cp, 0, sizeof(hci_cp)); -- cgit v0.10.2 From ebf86aa3aeaa4e9f434dacdd0a1d851a6c0332f8 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 5 Dec 2014 13:36:08 +0200 Subject: Bluetooth: Fix initializing hci_conn RSSI to invalid value When we create the hci_conn object we should properly initialize the RSSI to HCI_RSSI_INVALID. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 96887ae..79d84b8 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -449,6 +449,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, conn->io_capability = hdev->io_capability; conn->remote_auth = 0xff; conn->key_type = 0xff; + conn->rssi = HCI_RSSI_INVALID; conn->tx_power = HCI_TX_POWER_INVALID; conn->max_tx_power = HCI_TX_POWER_INVALID; -- cgit v0.10.2 From 9981bdb05a220d8c58ec47357d0423d0f07f6089 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 5 Dec 2014 13:42:57 +0200 Subject: Bluetooth: Fix Get Conn Info to use cmd_complete callback This patch fixes the Get Connection Information mgmt command to take advantage of the new cmd_complete callback. This allows for great simplifications in the logic for constructing the cmd_complete event. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 8467d35..61a04a3 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5074,67 +5074,42 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, return err; } -struct cmd_conn_lookup { - struct hci_conn *conn; - bool valid_tx_power; - u8 mgmt_status; -}; - -static void get_conn_info_complete(struct pending_cmd *cmd, void *data) +static void conn_info_cmd_complete(struct pending_cmd *cmd, u8 status) { - struct cmd_conn_lookup *match = data; - struct mgmt_cp_get_conn_info *cp; - struct mgmt_rp_get_conn_info rp; struct hci_conn *conn = cmd->user_data; + struct mgmt_rp_get_conn_info rp; - if (conn != match->conn) - return; - - cp = (struct mgmt_cp_get_conn_info *) cmd->param; + memcpy(&rp.addr, cmd->param, sizeof(rp.addr)); - memset(&rp, 0, sizeof(rp)); - bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); - rp.addr.type = cp->addr.type; - - if (!match->mgmt_status) { + if (status == MGMT_STATUS_SUCCESS) { rp.rssi = conn->rssi; - - if (match->valid_tx_power) { - rp.tx_power = conn->tx_power; - rp.max_tx_power = conn->max_tx_power; - } else { - rp.tx_power = HCI_TX_POWER_INVALID; - rp.max_tx_power = HCI_TX_POWER_INVALID; - } + rp.tx_power = conn->tx_power; + rp.max_tx_power = conn->max_tx_power; + } else { + rp.rssi = HCI_RSSI_INVALID; + rp.tx_power = HCI_TX_POWER_INVALID; + rp.max_tx_power = HCI_TX_POWER_INVALID; } - cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, - match->mgmt_status, &rp, sizeof(rp)); + cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, status, + &rp, sizeof(rp)); hci_conn_drop(conn); hci_conn_put(conn); - - mgmt_pending_remove(cmd); } -static void conn_info_refresh_complete(struct hci_dev *hdev, u8 status) +static void conn_info_refresh_complete(struct hci_dev *hdev, u8 hci_status) { struct hci_cp_read_rssi *cp; + struct pending_cmd *cmd; struct hci_conn *conn; - struct cmd_conn_lookup match; u16 handle; + u8 status; - BT_DBG("status 0x%02x", status); + BT_DBG("status 0x%02x", hci_status); hci_dev_lock(hdev); - /* TX power data is valid in case request completed successfully, - * otherwise we assume it's not valid. At the moment we assume that - * either both or none of current and max values are valid to keep code - * simple. - */ - match.valid_tx_power = !status; - /* Commands sent in request are either Read RSSI or Read Transmit Power * Level so we check which one was last sent to retrieve connection * handle. Both commands have handle as first parameter so it's safe to @@ -5147,29 +5122,29 @@ static void conn_info_refresh_complete(struct hci_dev *hdev, u8 status) cp = hci_sent_cmd_data(hdev, HCI_OP_READ_RSSI); if (!cp) { cp = hci_sent_cmd_data(hdev, HCI_OP_READ_TX_POWER); - status = 0; + status = MGMT_STATUS_SUCCESS; + } else { + status = mgmt_status(hci_status); } if (!cp) { - BT_ERR("invalid sent_cmd in response"); + BT_ERR("invalid sent_cmd in conn_info response"); goto unlock; } handle = __le16_to_cpu(cp->handle); conn = hci_conn_hash_lookup_handle(hdev, handle); if (!conn) { - BT_ERR("unknown handle (%d) in response", handle); + BT_ERR("unknown handle (%d) in conn_info response", handle); goto unlock; } - match.conn = conn; - match.mgmt_status = mgmt_status(status); + cmd = mgmt_pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn); + if (!cmd) + goto unlock; - /* Cache refresh is complete, now reply for mgmt request for given - * connection only. - */ - mgmt_pending_foreach(MGMT_OP_GET_CONN_INFO, hdev, - get_conn_info_complete, &match); + cmd->cmd_complete(cmd, status); + mgmt_pending_remove(cmd); unlock: hci_dev_unlock(hdev); @@ -5215,6 +5190,12 @@ static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data, goto unlock; } + if (mgmt_pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn)) { + err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO, + MGMT_STATUS_BUSY, &rp, sizeof(rp)); + goto unlock; + } + /* To avoid client trying to guess when to poll again for information we * calculate conn info age as random value between min/max set in hdev. */ @@ -5270,6 +5251,7 @@ static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data, hci_conn_hold(conn); cmd->user_data = hci_conn_get(conn); + cmd->cmd_complete = conn_info_cmd_complete; conn->conn_info_timestamp = jiffies; } else { -- cgit v0.10.2 From 189f6ad21faf73da26a4944a1893be871c079733 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 5 Dec 2014 13:40:01 +0200 Subject: Bluetooth: Remove redundant reverse_base_uuid variable The mgmt.c file already has a bluetooth_base_uuid variable which has the exact same value as the reverse_base_uuid one. This patch removes the redundant variable. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 61a04a3..326609f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6915,14 +6915,6 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, mgmt_pending_remove(cmd); } -/* this is reversed hex representation of bluetooth base uuid. We need it for - * service uuid parsing in eir. - */ -static const u8 reverse_base_uuid[] = { - 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - static inline bool has_uuid(u8 *uuid, u16 uuid_count, u8 (*uuids)[16]) { int i; @@ -6954,7 +6946,7 @@ static bool eir_has_uuids(u8 *eir, u16 eir_len, u16 uuid_count, u8 (*uuids)[16]) case EIR_UUID16_ALL: case EIR_UUID16_SOME: for (i = 0; i + 3 <= field_len; i += 2) { - memcpy(uuid, reverse_base_uuid, 16); + memcpy(uuid, bluetooth_base_uuid, 16); uuid[13] = eir[i + 3]; uuid[12] = eir[i + 2]; if (has_uuid(uuid, uuid_count, uuids)) @@ -6964,7 +6956,7 @@ static bool eir_has_uuids(u8 *eir, u16 eir_len, u16 uuid_count, u8 (*uuids)[16]) case EIR_UUID32_ALL: case EIR_UUID32_SOME: for (i = 0; i + 5 <= field_len; i += 4) { - memcpy(uuid, reverse_base_uuid, 16); + memcpy(uuid, bluetooth_base_uuid, 16); uuid[15] = eir[i + 5]; uuid[14] = eir[i + 4]; uuid[13] = eir[i + 3]; -- cgit v0.10.2 From efb2513fd6278d97d2114ab87f019f4fed01c037 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 5 Dec 2014 13:03:34 +0100 Subject: Bluetooth: Fix discovery filter when no RSSI is available When no RSSI value is available then make sure that the result is filtered out when the RSSI threshold filter is active. This means that all Bluetooth 1.1 or earlier devices will not report any results when using a RSSI threshold filter. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index f4e2a61..527dfdc 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2043,13 +2043,14 @@ static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) data.pscan_mode = info->pscan_mode; memcpy(data.dev_class, info->dev_class, 3); data.clock_offset = info->clock_offset; - data.rssi = 0x00; + data.rssi = HCI_RSSI_INVALID; data.ssp_mode = 0x00; flags = hci_inquiry_cache_update(hdev, &data, false); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, 0, flags, NULL, 0, NULL, 0); + info->dev_class, HCI_RSSI_INVALID, + flags, NULL, 0, NULL, 0); } hci_dev_unlock(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 326609f..3ca2818 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -7006,9 +7006,12 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, * if such a RSSI threshold is specified. If a RSSI threshold has * been specified, then all results with a RSSI smaller than the * RSSI threshold will be dropped. + * + * For BR/EDR devices (pre 1.2) providing no RSSI during inquiry, + * the results are also dropped. */ if (hdev->discovery.rssi != HCI_RSSI_INVALID && - rssi < hdev->discovery.rssi) + (rssi < hdev->discovery.rssi || rssi == HCI_RSSI_INVALID)) return; /* Make sure that the buffer is big enough. The 5 extra bytes @@ -7019,6 +7022,10 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, memset(buf, 0, sizeof(buf)); + /* Reset invalid RSSI to 0 to keep backwards API compliance */ + if (rssi == HCI_RSSI_INVALID) + rssi = 0; + bacpy(&ev->addr.bdaddr, bdaddr); ev->addr.type = link_to_bdaddr(link_type, addr_type); ev->rssi = rssi; -- cgit v0.10.2 From da25cf6a9869cff52b4fd189fdcd322ad2daf023 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 5 Dec 2014 13:03:35 +0100 Subject: Bluetooth: Report invalid RSSI for service discovery and background scan When using Start Service Discovery and when background scanning is used to report devices, the RSSI is reported or the value 127 is provided in case RSSI in unavailable. For Start Discovery the value 0 is reported to keep backwards compatibility with the existing users. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index f07b145..3c78270 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -75,6 +75,7 @@ struct discovery_state { u32 last_adv_flags; u8 last_adv_data[HCI_MAX_AD_LENGTH]; u8 last_adv_data_len; + bool report_invalid_rssi; s8 rssi; u16 uuid_count; u8 (*uuids)[16]; @@ -506,11 +507,13 @@ static inline void discovery_init(struct hci_dev *hdev) INIT_LIST_HEAD(&hdev->discovery.all); INIT_LIST_HEAD(&hdev->discovery.unknown); INIT_LIST_HEAD(&hdev->discovery.resolve); + hdev->discovery.report_invalid_rssi = true; hdev->discovery.rssi = HCI_RSSI_INVALID; } static inline void hci_discovery_filter_clear(struct hci_dev *hdev) { + hdev->discovery.report_invalid_rssi = true; hdev->discovery.rssi = HCI_RSSI_INVALID; hdev->discovery.uuid_count = 0; kfree(hdev->discovery.uuids); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 3ca2818..a91e484 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3912,6 +3912,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, hci_discovery_filter_clear(hdev); hdev->discovery.type = cp->type; + hdev->discovery.report_invalid_rssi = false; hci_req_init(&req, hdev); @@ -7022,8 +7023,15 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, memset(buf, 0, sizeof(buf)); - /* Reset invalid RSSI to 0 to keep backwards API compliance */ - if (rssi == HCI_RSSI_INVALID) + /* In case of device discovery with BR/EDR devices (pre 1.2), the + * RSSI value was reported as 0 when not available. This behavior + * is kept when using device discovery. This is required for full + * backwards compatibility with the API. + * + * However when using service discovery, the value 127 will be + * returned when the RSSI is not available. + */ + if (rssi == HCI_RSSI_INVALID && !hdev->discovery.report_invalid_rssi) rssi = 0; bacpy(&ev->addr.bdaddr, bdaddr); -- cgit v0.10.2 From bcb47aabf4121e5cac3d34e3c83418c938374554 Mon Sep 17 00:00:00 2001 From: Varka Bhadram Date: Fri, 5 Dec 2014 17:19:09 +0530 Subject: mac802154: use goto label on failure Signed-off-by: Varka Bhadram Reviewed-by: Stefan Schmidt Acked-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c index 041dbd5..c0d67b2 100644 --- a/net/mac802154/rx.c +++ b/net/mac802154/rx.c @@ -85,8 +85,7 @@ ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata, default: spin_unlock_bh(&sdata->mib_lock); pr_debug("invalid dest mode\n"); - kfree_skb(skb); - return NET_RX_DROP; + goto fail; } spin_unlock_bh(&sdata->mib_lock); -- cgit v0.10.2 From fff71b14113030f490a8e9eace2e1a22dddfa284 Mon Sep 17 00:00:00 2001 From: Varka Bhadram Date: Fri, 5 Dec 2014 17:19:10 +0530 Subject: cc2520: adds terminating newline Signed-off-by: Varka Bhadram Acked-by: Alexander Aring Signed-off-by: Marcel Holtmann diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c index ccbb082..f9df9fa 100644 --- a/drivers/net/ieee802154/cc2520.c +++ b/drivers/net/ieee802154/cc2520.c @@ -858,7 +858,7 @@ static int cc2520_probe(struct spi_device *spi) pinctrl = devm_pinctrl_get_select_default(&spi->dev); if (IS_ERR(pinctrl)) dev_warn(&spi->dev, - "pinctrl pins are not configured"); + "pinctrl pins are not configured\n"); pdata = cc2520_get_platform_data(spi); if (!pdata) { -- cgit v0.10.2 From 2331fd46f5452944f32a2bc78fa05026617887c6 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 5 Dec 2014 16:20:10 +0100 Subject: Bluetooth: Move LE advertising report defines to the right location All Bluetooth commands and events are ordered by its opcode or event id, but for some reason this one now stands out. So move it to its correct spot in the list. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index b6f7be1..edb55e5 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1751,6 +1751,25 @@ struct hci_ev_le_conn_complete { __u8 clk_accurancy; } __packed; +/* Advertising report event types */ +#define LE_ADV_IND 0x00 +#define LE_ADV_DIRECT_IND 0x01 +#define LE_ADV_SCAN_IND 0x02 +#define LE_ADV_NONCONN_IND 0x03 +#define LE_ADV_SCAN_RSP 0x04 + +#define ADDR_LE_DEV_PUBLIC 0x00 +#define ADDR_LE_DEV_RANDOM 0x01 + +#define HCI_EV_LE_ADVERTISING_REPORT 0x02 +struct hci_ev_le_advertising_info { + __u8 evt_type; + __u8 bdaddr_type; + bdaddr_t bdaddr; + __u8 length; + __u8 data[0]; +} __packed; + #define HCI_EV_LE_CONN_UPDATE_COMPLETE 0x03 struct hci_ev_le_conn_update_complete { __u8 status; @@ -1776,25 +1795,6 @@ struct hci_ev_le_remote_conn_param_req { __le16 timeout; } __packed; -/* Advertising report event types */ -#define LE_ADV_IND 0x00 -#define LE_ADV_DIRECT_IND 0x01 -#define LE_ADV_SCAN_IND 0x02 -#define LE_ADV_NONCONN_IND 0x03 -#define LE_ADV_SCAN_RSP 0x04 - -#define ADDR_LE_DEV_PUBLIC 0x00 -#define ADDR_LE_DEV_RANDOM 0x01 - -#define HCI_EV_LE_ADVERTISING_REPORT 0x02 -struct hci_ev_le_advertising_info { - __u8 evt_type; - __u8 bdaddr_type; - bdaddr_t bdaddr; - __u8 length; - __u8 data[0]; -} __packed; - /* Internal events generated by Bluetooth stack */ #define HCI_EV_STACK_INTERNAL 0xfd struct hci_ev_stack_internal { -- cgit v0.10.2 From 32c9d43fa5deac40eee5a689428367982f147044 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 5 Dec 2014 16:20:11 +0100 Subject: Bluetooth: Add definitions for LE Direct Advertising Report event This patch adds the event id and data structures for the LE Direct Advertising Report event. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index edb55e5..ffebd39 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1795,6 +1795,16 @@ struct hci_ev_le_remote_conn_param_req { __le16 timeout; } __packed; +#define HCI_EV_LE_DIRECT_ADV_REPORT 0x0B +struct hci_ev_le_direct_adv_info { + __u8 evt_type; + __u8 bdaddr_type; + bdaddr_t bdaddr; + __u8 direct_addr_type; + bdaddr_t direct_addr; + __s8 rssi; +} __packed; + /* Internal events generated by Bluetooth stack */ #define HCI_EV_STACK_INTERNAL 0xfd struct hci_ev_stack_internal { -- cgit v0.10.2 From 4b71bba45c420065044bf040b2284893ab900d03 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 5 Dec 2014 16:20:12 +0100 Subject: Bluetooth: Enabled LE Direct Advertising Report event if supported When the controller supports the Extended Scanner Filter Policies, it supports the LE Direct Advertising Report event. However by default that event is blocked by the LE event mask. It is required to enable it during controller setup. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index ffebd39..40129b3 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -343,6 +343,7 @@ enum { #define HCI_LE_ENCRYPTION 0x01 #define HCI_LE_CONN_PARAM_REQ_PROC 0x02 #define HCI_LE_PING 0x10 +#define HCI_LE_EXT_SCAN_POLICY 0x80 /* Connection modes */ #define HCI_CM_ACTIVE 0x0000 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 523700e..da8969e 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1735,6 +1735,14 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt) * Parameter Request */ + /* If the controller supports Extended Scanner Filter + * Policies, enable the correspondig event. + */ + if (hdev->le_features[0] & HCI_LE_EXT_SCAN_POLICY) + events[1] |= 0x04; /* LE Direct Advertising + * Report + */ + hci_req_add(req, HCI_OP_LE_SET_EVENT_MASK, sizeof(events), events); -- cgit v0.10.2 From 2f010b55884efad9c2be572070cb7377a5388fcd Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 5 Dec 2014 16:20:13 +0100 Subject: Bluetooth: Add support for handling LE Direct Advertising Report events When the controller sends a LE Direct Advertising Report event, the host must confirm that the resolvable random address provided matches with its own identity resolving key. If it does, then that advertising report needs to be processed. If it does not match, the report needs to be ignored. This patch adds full support for handling these new reports and using them for device discovery and connection handling. This means when a Bluetooth controller supports the Extended Scanner Filter Policies, it is possible to use directed advertising with LE privacy. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 527dfdc..322abbb 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4426,7 +4426,8 @@ static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev, } static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, - u8 bdaddr_type, s8 rssi, u8 *data, u8 len) + u8 bdaddr_type, bdaddr_t *direct_addr, + u8 direct_addr_type, s8 rssi, u8 *data, u8 len) { struct discovery_state *d = &hdev->discovery; struct smp_irk *irk; @@ -4434,6 +4435,32 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, bool match; u32 flags; + /* If the direct address is present, then this report is from + * a LE Direct Advertising Report event. In that case it is + * important to see if the address is matching the local + * controller address. + */ + if (direct_addr) { + /* Only resolvable random addresses are valid for these + * kind of reports and others can be ignored. + */ + if (!hci_bdaddr_is_rpa(direct_addr, direct_addr_type)) + return; + + /* If the controller is not using resolvable random + * addresses, then this report can be ignored. + */ + if (!test_bit(HCI_PRIVACY, &hdev->dev_flags)) + return; + + /* If the local IRK of the controller does not match + * with the resolvable random address provided, then + * this report can be ignored. + */ + if (!smp_irk_matches(hdev, hdev->irk, direct_addr)) + return; + } + /* Check if we need to convert to identity address */ irk = hci_get_irk(hdev, bdaddr, bdaddr_type); if (irk) { @@ -4570,7 +4597,8 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb) rssi = ev->data[ev->length]; process_adv_report(hdev, ev->evt_type, &ev->bdaddr, - ev->bdaddr_type, rssi, ev->data, ev->length); + ev->bdaddr_type, NULL, 0, rssi, + ev->data, ev->length); ptr += sizeof(*ev) + ev->length + 1; } @@ -4711,6 +4739,27 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, hci_send_cmd(hdev, HCI_OP_LE_CONN_PARAM_REQ_REPLY, sizeof(cp), &cp); } +static void hci_le_direct_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_direct_adv_info *ev = ptr; + + process_adv_report(hdev, ev->evt_type, &ev->bdaddr, + ev->bdaddr_type, &ev->direct_addr, + ev->direct_addr_type, ev->rssi, NULL, 0); + + ptr += sizeof(*ev); + } + + hci_dev_unlock(hdev); +} + static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_le_meta *le_ev = (void *) skb->data; @@ -4738,6 +4787,10 @@ static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_le_remote_conn_param_req_evt(hdev, skb); break; + case HCI_EV_LE_DIRECT_ADV_REPORT: + hci_le_direct_adv_report_evt(hdev, skb); + break; + default: break; } -- cgit v0.10.2 From 4efbb2ce8b6f693ce9607c28082f542a70eb5934 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 5 Dec 2014 16:20:14 +0100 Subject: Bluetooth: Add support for enabling Extended Scanner Filter Policies The new Extended Scanner Filter Policies feature has to be enabled by selecting the correct filter policy for the scan parameters. This patch does that when the controller has been enabled to use LE Privacy. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index da8969e..c1d5726 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -5625,6 +5625,19 @@ void hci_req_add_le_passive_scan(struct hci_request *req) */ filter_policy = update_white_list(req); + /* When the controller is using random resolvable addresses and + * with that having LE privacy enabled, then controllers with + * Extended Scanner Filter Policies support can now enable support + * for handling directed advertising. + * + * So instead of using filter polices 0x00 (no whitelist) + * and 0x01 (whitelist enabled) use the new filter policies + * 0x02 (no whitelist) and 0x03 (whitelist enabled). + */ + if (test_bit(HCI_PRIVACY, &hdev->dev_flags) && + (hdev->le_features[0] & HCI_LE_EXT_SCAN_POLICY)) + filter_policy |= 0x02; + memset(¶m_cp, 0, sizeof(param_cp)); param_cp.type = LE_SCAN_PASSIVE; param_cp.interval = cpu_to_le16(hdev->le_scan_interval); -- cgit v0.10.2 From 5a34bd5f5d8119def4feb1d2b4e3906b71059416 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 5 Dec 2014 16:20:15 +0100 Subject: Bluetooth: Enable events for P-256 Public Key and DHKey commands When the LE Read Local P-256 Public Key command is supported, then enable its corresponding complete event. And when the LE Generate DHKey command is supported, enable its corresponding complete event as well. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index c1d5726..93f92a0 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1743,6 +1743,20 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt) * Report */ + /* If the controller supports the LE Read Local P-256 + * Public Key command, enable the corresponding event. + */ + if (hdev->commands[34] & 0x02) + events[0] |= 0x80; /* LE Read Local P-256 + * Public Key Complete + */ + + /* If the controller supports the LE Generate DHKey + * command, enable the corresponding event. + */ + if (hdev->commands[34] & 0x04) + events[1] |= 0x01; /* LE Generate DHKey Complete */ + hci_req_add(req, HCI_OP_LE_SET_EVENT_MASK, sizeof(events), events); -- cgit v0.10.2