Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b5ae344d authored by Johan Hedberg's avatar Johan Hedberg Committed by Marcel Holtmann
Browse files

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: default avatarJohan Hedberg <johan.hedberg@intel.com>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent ef8efe4b
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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);

+208 −22
Original line number Diff line number Diff line
@@ -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).
		 */
		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)
		}
	}

	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);
		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 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
	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;