Module Name:    src
Committed By:   martin
Date:           Sat Sep 28 07:51:58 UTC 2019

Modified Files:
        src/sys/netbt [netbsd-7-0]: hci.h hci_event.c

Log Message:
Pull up following revision(s) (requested by plunky in ticket #1709):

        sys/netbt/hci_event.c: revision 1.26
        sys/netbt/hci.h: revision 1.46

When encrypted connections are configured, verify that the encryption
key length has a minimum size when the adaptor supports that.

This addresses the 'Key Negotiation of Bluetooth' attack, CVE-2019-9506
https://www.bluetooth.com/security/statement-key-negotiation-of-bluetooth/


To generate a diff of this commit:
cvs rdiff -u -r1.39 -r1.39.6.1 src/sys/netbt/hci.h
cvs rdiff -u -r1.23 -r1.23.32.1 src/sys/netbt/hci_event.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/netbt/hci.h
diff -u src/sys/netbt/hci.h:1.39 src/sys/netbt/hci.h:1.39.6.1
--- src/sys/netbt/hci.h:1.39	Tue Jul  1 05:49:18 2014
+++ src/sys/netbt/hci.h	Sat Sep 28 07:51:57 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: hci.h,v 1.39 2014/07/01 05:49:18 rtr Exp $	*/
+/*	$NetBSD: hci.h,v 1.39.6.1 2019/09/28 07:51:57 martin Exp $	*/
 
 /*-
  * Copyright (c) 2005 Iain Hibbert.
@@ -54,7 +54,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $Id: hci.h,v 1.39 2014/07/01 05:49:18 rtr Exp $
+ * $Id: hci.h,v 1.39.6.1 2019/09/28 07:51:57 martin Exp $
  * $FreeBSD: src/sys/netgraph/bluetooth/include/ng_hci.h,v 1.6 2005/01/07 01:45:43 imp Exp $
  */
 
@@ -1786,6 +1786,17 @@ typedef struct {
 	uint16_t	accuracy;	/* clock accuracy */
 } __packed hci_read_clock_rp;
 
+#define HCI_OCF_READ_ENCRYPTION_KEY_SIZE		0x0008
+#define HCI_CMD_READ_ENCRYPTION_KEY_SIZE		0x1408
+typedef struct {
+	uint16_t	con_handle;	/* connection handle */
+} __packed hci_read_encryption_key_size_cp;
+
+typedef struct {
+	uint8_t		status;		/* 0x00 - success */
+	uint16_t	con_handle;	/* connection handle */
+	uint8_t		size;		/* key size */
+} __packed hci_read_encryption_key_size_rp;
 
 /**************************************************************************
  **************************************************************************

Index: src/sys/netbt/hci_event.c
diff -u src/sys/netbt/hci_event.c:1.23 src/sys/netbt/hci_event.c:1.23.32.1
--- src/sys/netbt/hci_event.c:1.23	Wed Jul 27 10:25:09 2011
+++ src/sys/netbt/hci_event.c	Sat Sep 28 07:51:57 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: hci_event.c,v 1.23 2011/07/27 10:25:09 plunky Exp $	*/
+/*	$NetBSD: hci_event.c,v 1.23.32.1 2019/09/28 07:51:57 martin Exp $	*/
 
 /*-
  * Copyright (c) 2005 Iain Hibbert.
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: hci_event.c,v 1.23 2011/07/27 10:25:09 plunky Exp $");
+__KERNEL_RCSID(0, "$NetBSD: hci_event.c,v 1.23.32.1 2019/09/28 07:51:57 martin Exp $");
 
 #include <sys/param.h>
 #include <sys/kernel.h>
@@ -63,6 +63,7 @@ static void hci_cmd_read_local_features(
 static void hci_cmd_read_local_extended_features(struct hci_unit *, struct mbuf *);
 static void hci_cmd_read_local_ver(struct hci_unit *, struct mbuf *);
 static void hci_cmd_read_local_commands(struct hci_unit *, struct mbuf *);
+static void hci_cmd_read_encryption_key_size(struct hci_unit *, struct mbuf *);
 static void hci_cmd_reset(struct hci_unit *, struct mbuf *);
 static void hci_cmd_create_con(struct hci_unit *unit, uint8_t status);
 
@@ -351,6 +352,10 @@ hci_event_command_compl(struct hci_unit 
 		hci_cmd_read_local_commands(unit, m);
 		break;
 
+	case HCI_CMD_READ_ENCRYPTION_KEY_SIZE:
+		hci_cmd_read_encryption_key_size(unit, m);
+		break;
+
 	case HCI_CMD_RESET:
 		hci_cmd_reset(unit, m);
 		break;
@@ -618,10 +623,11 @@ hci_event_con_compl(struct hci_unit *uni
 		return;
 	}
 
-	/* XXX could check auth_enable here */
-
-	if (ep.encryption_mode)
-		link->hl_flags |= (HCI_LINK_AUTH | HCI_LINK_ENCRYPT);
+	/*
+	 * We purposefully ignore ep.encryption_mode here - if that is set then
+	 * the link will be authenticated and encrypted, but we still want to
+	 * verify the key size and setmode sets the right flags
+	 */
 
 	link->hl_state = HCI_LINK_OPEN;
 	link->hl_handle = HCI_CON_HANDLE(le16toh(ep.con_handle));
@@ -772,17 +778,16 @@ hci_event_auth_compl(struct hci_unit *un
 /*
  * Encryption Change
  *
- * The encryption status has changed. Basically, we note the change
- * then notify the upper layer protocol unless further mode changes
- * are pending.
- * Note that if encryption gets disabled when it has been requested,
- * we will attempt to enable it again.. (its a feature not a bug :)
+ * The encryption status has changed. Make a note if disabled, or
+ * check the key size if possible before allowing it is enabled.
+ * (checking of key size was enabled in 3.0 spec)
  */
 static void
 hci_event_encryption_change(struct hci_unit *unit, struct mbuf *m)
 {
 	hci_encryption_change_ep ep;
 	struct hci_link *link;
+	uint16_t con_handle;
 	int err;
 
 	if (m->m_pkthdr.len < sizeof(ep))
@@ -791,27 +796,34 @@ hci_event_encryption_change(struct hci_u
 	m_copydata(m, 0, sizeof(ep), &ep);
 	m_adj(m, sizeof(ep));
 
-	ep.con_handle = HCI_CON_HANDLE(le16toh(ep.con_handle));
+	con_handle = HCI_CON_HANDLE(le16toh(ep.con_handle));
 
 	DPRINTFN(1, "handle #%d, status=0x%x, encryption_enable=0x%x\n",
-		 ep.con_handle, ep.status, ep.encryption_enable);
+		 con_handle, ep.status, ep.encryption_enable);
 
-	link = hci_link_lookup_handle(unit, ep.con_handle);
+	link = hci_link_lookup_handle(unit, con_handle);
 	if (link == NULL || link->hl_type != HCI_LINK_ACL)
 		return;
 
 	if (ep.status == 0) {
-		if (ep.encryption_enable == 0)
+		if (ep.encryption_enable == 0) {
 			link->hl_flags &= ~HCI_LINK_ENCRYPT;
-		else
+		} else if (unit->hci_cmds[20] & (1<<4)) {
+			err = hci_send_cmd(unit, HCI_CMD_READ_ENCRYPTION_KEY_SIZE,
+			    &ep.con_handle, sizeof(ep.con_handle));
+
+			if (err == 0)
+				return;
+		} else {
 			link->hl_flags |= (HCI_LINK_AUTH | HCI_LINK_ENCRYPT);
 
-		if (link->hl_state == HCI_LINK_WAIT_ENCRYPT)
-			link->hl_state = HCI_LINK_OPEN;
+			if (link->hl_state == HCI_LINK_WAIT_ENCRYPT)
+				link->hl_state = HCI_LINK_OPEN;
 
-		err = hci_acl_setmode(link);
-		if (err == EINPROGRESS)
-			return;
+			err = hci_acl_setmode(link);
+			if (err == EINPROGRESS)
+				return;
+		}
 	}
 
 	hci_acl_linkmode(link);
@@ -1166,6 +1178,57 @@ hci_cmd_read_local_commands(struct hci_u
 }
 
 /*
+ * process results of read_encryption_key_size command_complete event
+ */
+static void
+hci_cmd_read_encryption_key_size(struct hci_unit *unit, struct mbuf *m)
+{
+	hci_read_encryption_key_size_rp rp;
+	struct hci_link *link;
+	int err;
+
+	if (m->m_pkthdr.len < sizeof(rp))
+		return;
+
+	m_copydata(m, 0, sizeof(rp), &rp);
+	m_adj(m, sizeof(rp));
+
+	if (rp.status != 0)
+		return;
+
+	rp.con_handle = HCI_CON_HANDLE(le16toh(rp.con_handle));
+
+	DPRINTFN(1, "handle #%d, status=0x%x, key_size=0x%x\n",
+		 rp.con_handle, rp.status, rp.size);
+
+	link = hci_link_lookup_handle(unit, rp.con_handle);
+	if (link == NULL || link->hl_type != HCI_LINK_ACL)
+		return;
+
+	/*
+	 * if the key size is less than minimum standard, go straight to
+	 * linkmode as this is non-recoverable. Otherwise, we are encrypted
+	 * so can proceed with setmode.
+	 */
+	if (rp.status == 0) {
+		if (rp.size < 7) {
+			link->hl_flags &= ~HCI_LINK_ENCRYPT;
+		} else {
+			link->hl_flags |= (HCI_LINK_AUTH | HCI_LINK_ENCRYPT);
+
+			if (link->hl_state == HCI_LINK_WAIT_ENCRYPT)
+				link->hl_state = HCI_LINK_OPEN;
+
+			err = hci_acl_setmode(link);
+			if (err == EINPROGRESS)
+				return;
+		}
+	}
+
+	hci_acl_linkmode(link);
+}
+
+/*
  * process results of reset command_complete event
  *
  * This has killed all the connections, so close down anything we have left,

Reply via email to