Bluetooth: Don't use incomplete LE LTKs to secure link

If LE link gets terminated during the pairing process, it
may leave SMP with half of a Long Term Key. This fix ensures
that this partial information is not used for future links.

Change-Id: Ib834fc81d7b4b558cc3d242522acef80dd1d8827
Signed-off-by: Brian Gix <bgix@codeaurora.org>
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 94d0f06..742d520 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -644,6 +644,28 @@
 	return 0;
 }
 
+static int smp_encrypt_link(struct hci_conn *hcon, struct link_key *key)
+{
+	struct key_master_id *master;
+	u8 zerobuf[8];
+
+	if (!hcon || !key || !key->data)
+		return -EINVAL;
+
+	memset(zerobuf, 0, sizeof(zerobuf));
+
+	master = (void *) key->data;
+
+	if (!master->ediv && !memcmp(master->rand, zerobuf, sizeof(zerobuf)))
+		return -EINVAL;
+
+	hcon->enc_key_size = key->pin_len;
+	hcon->sec_req = TRUE;
+	hci_le_start_enc(hcon, master->ediv, master->rand, key->val);
+
+	return 0;
+}
+
 static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
 {
 	struct hci_conn *hcon = conn->hcon;
@@ -659,18 +681,19 @@
 	key = hci_find_link_key_type(hcon->hdev, conn->dst, KEY_TYPE_LTK);
 	if (key && ((key->auth & SMP_AUTH_MITM) ||
 					!(rp->auth_req & SMP_AUTH_MITM))) {
-		struct key_master_id *master = (void *) key->data;
 
-		hci_le_start_enc(hcon, master->ediv, master->rand,
-				key->val);
-		hcon->enc_key_size = key->pin_len;
+		if (smp_encrypt_link(hcon, key) < 0)
+			goto invalid_key;
 
-		hcon->sec_req = TRUE;
-		hcon->sec_level = authreq_to_seclevel(rp->auth_req);
+		hcon->sec_level = authreq_to_seclevel(key->auth);
+
+		if (!(hcon->link_mode & HCI_LM_ENCRYPT))
+			hci_conn_hold(hcon);
 
 		return 0;
 	}
 
+invalid_key:
 	hcon->sec_req = FALSE;
 
 	skb_pull(skb, sizeof(*rp));
@@ -730,17 +753,9 @@
 
 		key = hci_find_link_key_type(hcon->hdev, conn->dst,
 							KEY_TYPE_LTK);
-		if (key) {
-			struct key_master_id *master = (void *) key->data;
 
-			hci_le_start_enc(hcon, master->ediv, master->rand,
-								key->val);
-			hcon->enc_key_size = key->pin_len;
-
-			hcon->sec_req = TRUE;
-
+		if (smp_encrypt_link(hcon, key) == 0)
 			goto done;
-		}
 	}
 
 	hcon->sec_req = FALSE;