Bluetooth: Add workaround for broken OS X legacy SMP pairing
OS X version 10.10.2 (and possibly older versions) doesn't support LE
Secure Connections but incorrectly copies all authentication request
bits from a Security Request to its Pairing Request. The result is that
an SC capable initiator (such as BlueZ) will think OS X intends to do SC
when in fact it's incapable of it:
< ACL Data TX: Handle 3585 flags 0x00 dlen 6
SMP: Security Request (0x0b) len 1
Authentication requirement: Bonding, No MITM, SC, No Keypresses (0x09)
> ACL Data RX: Handle 3585 flags 0x02 dlen 11
SMP: Pairing Request (0x01) len 6
IO capability: KeyboardDisplay (0x04)
OOB data: Authentication data not present (0x00)
Authentication requirement: Bonding, No MITM, SC, No Keypresses (0x09)
Max encryption key size: 16
Initiator key distribution: EncKey (0x01)
Responder key distribution: EncKey IdKey Sign (0x07)
< ACL Data TX: Handle 3585 flags 0x00 dlen 11
SMP: Pairing Response (0x02) len 6
IO capability: NoInputNoOutput (0x03)
OOB data: Authentication data not present (0x00)
Authentication requirement: Bonding, No MITM, SC, No Keypresses (0x09)
Max encryption key size: 16
Initiator key distribution: EncKey (0x01)
Responder key distribution: EncKey Sign (0x05)
The pairing eventually fails when we get an unexpected Pairing Confirm
PDU instead of a Public Key PDU:
> ACL Data RX: Handle 3585 flags 0x02 dlen 21
SMP: Pairing Confirm (0x03) len 16
Confim value: bcc3bed31b8f313a78ec3cce32685faf
It is only at this point that we can speculate that the remote doesn't
really support SC. This patch creates a workaround for the just-works
model, however the MITM case is unsolvable because the OS X user has
already been requested to enter a PIN which we're now expected to
randomly generate and show the user (i.e. a chicken-and-egg problem).
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
committed by
Marcel Holtmann
parent
fa4335d71a
commit
19c5ce9c5f
@@ -880,6 +880,12 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If this function is used for SC -> legacy fallback we
|
||||
* can only recover the just-works case.
|
||||
*/
|
||||
if (test_bit(SMP_FLAG_SC, &smp->flags))
|
||||
return -EINVAL;
|
||||
|
||||
/* Not Just Works/Confirm results in MITM Authentication */
|
||||
if (smp->method != JUST_CFM) {
|
||||
set_bit(SMP_FLAG_MITM_AUTH, &smp->flags);
|
||||
@@ -1806,6 +1812,13 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
|
||||
clear_bit(SMP_FLAG_INITIATOR, &smp->flags);
|
||||
|
||||
/* Strictly speaking we shouldn't allow Pairing Confirm for the
|
||||
* SC case, however some implementations incorrectly copy RFU auth
|
||||
* req bits from our security request, which may create a false
|
||||
* positive SC enablement.
|
||||
*/
|
||||
SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
|
||||
|
||||
if (test_bit(SMP_FLAG_SC, &smp->flags)) {
|
||||
SMP_ALLOW_CMD(smp, SMP_CMD_PUBLIC_KEY);
|
||||
/* Clear bits which are generated but not distributed */
|
||||
@@ -1814,8 +1827,6 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
|
||||
|
||||
/* Request setup of TK */
|
||||
ret = tk_request(conn, 0, auth, rsp.io_capability, req->io_capability);
|
||||
if (ret)
|
||||
@@ -1981,10 +1992,6 @@ static u8 sc_check_confirm(struct smp_chan *smp)
|
||||
|
||||
BT_DBG("");
|
||||
|
||||
/* Public Key exchange must happen before any other steps */
|
||||
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);
|
||||
|
||||
@@ -1997,6 +2004,47 @@ static u8 sc_check_confirm(struct smp_chan *smp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Work-around for some implementations that incorrectly copy RFU bits
|
||||
* from our security request and thereby create the impression that
|
||||
* we're doing SC when in fact the remote doesn't support it.
|
||||
*/
|
||||
static int fixup_sc_false_positive(struct smp_chan *smp)
|
||||
{
|
||||
struct l2cap_conn *conn = smp->conn;
|
||||
struct hci_conn *hcon = conn->hcon;
|
||||
struct hci_dev *hdev = hcon->hdev;
|
||||
struct smp_cmd_pairing *req, *rsp;
|
||||
u8 auth;
|
||||
|
||||
/* The issue is only observed when we're in slave role */
|
||||
if (hcon->out)
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_SC_ONLY)) {
|
||||
BT_ERR("Refusing SMP SC -> legacy fallback in SC-only mode");
|
||||
return SMP_UNSPECIFIED;
|
||||
}
|
||||
|
||||
BT_ERR("Trying to fall back to legacy SMP");
|
||||
|
||||
req = (void *) &smp->preq[1];
|
||||
rsp = (void *) &smp->prsp[1];
|
||||
|
||||
/* Rebuild key dist flags which may have been cleared for SC */
|
||||
smp->remote_key_dist = (req->init_key_dist & rsp->resp_key_dist);
|
||||
|
||||
auth = req->auth_req & AUTH_REQ_MASK(hdev);
|
||||
|
||||
if (tk_request(conn, 0, auth, rsp->io_capability, req->io_capability)) {
|
||||
BT_ERR("Failed to fall back to legacy SMP");
|
||||
return SMP_UNSPECIFIED;
|
||||
}
|
||||
|
||||
clear_bit(SMP_FLAG_SC, &smp->flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
{
|
||||
struct l2cap_chan *chan = conn->smp;
|
||||
@@ -2010,8 +2058,19 @@ 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 (test_bit(SMP_FLAG_SC, &smp->flags)) {
|
||||
int ret;
|
||||
|
||||
/* Public Key exchange must happen before any other steps */
|
||||
if (test_bit(SMP_FLAG_REMOTE_PK, &smp->flags))
|
||||
return sc_check_confirm(smp);
|
||||
|
||||
BT_ERR("Unexpected SMP Pairing Confirm");
|
||||
|
||||
ret = fixup_sc_false_positive(smp);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (conn->hcon->out) {
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
|
||||
|
||||
Reference in New Issue
Block a user