From patchwork Mon Jan 6 10:14:37 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Howard Chung X-Patchwork-Id: 197536 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-17.4 required=3.0 tests=DKIMWL_WL_MED, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT, USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7E95EC2D0D1 for ; Mon, 6 Jan 2020 10:14:59 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 4A2C92072C for ; Mon, 6 Jan 2020 10:14:59 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="bDdqcPXV" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726360AbgAFKO6 (ORCPT ); Mon, 6 Jan 2020 05:14:58 -0500 Received: from mail-vk1-f201.google.com ([209.85.221.201]:42082 "EHLO mail-vk1-f201.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726155AbgAFKOx (ORCPT ); Mon, 6 Jan 2020 05:14:53 -0500 Received: by mail-vk1-f201.google.com with SMTP id 198so7457876vkz.9 for ; Mon, 06 Jan 2020 02:14:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:message-id:mime-version:subject:from:to:cc; bh=at/6LW2CyTT3AHJ4DEYijPCx/QdUy/o9K531OsH9S78=; b=bDdqcPXVe98eZZsa1W/fgJGsjHgmbnuAHsdVZRxB/3KvothFJUQUfQ0onHWQZ8cfpX nT4tXkR1hx5QKJ0kqcm+i6hbjFiePhLwRTnnLJECG/i4KiwAfG9k7E6aYHg+osoPighR XXXc5btco0/w1GOLEiK4WqSzvlCx/j9nz6X+8fZqp1uvhV7PRUaXbDN25u71AmYpWLyG fxGkkeRTD0kfFO+jyrVOand0ROEnj5QBZn7670exqa2lPa0stOycRFTxWkSvuO/LOtdZ pYS7sDIxeCDkp8cvSom/ZlQ7HLcbWDA13yy8M1tvGIWYO5o2xdI4JQFmv8Z7mjlYurwT 4nrw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:message-id:mime-version:subject:from:to:cc; bh=at/6LW2CyTT3AHJ4DEYijPCx/QdUy/o9K531OsH9S78=; b=J2MzTzQOjUXrE76NpGFIzMD6/fAgxswGhZTcBi0fzNfkpvVjRaN4P8MhC0hhTzKv/G NGG9Z8XrR0T6mZIP9tlVz/B/msSlnA6uJ8pcamJlK6coB8s+VPXK0ERec1XRpfK0MB9x FzPbA2yIbDtLFXC50uta9eBVXpinjDL8141GghxL4iKxi3TCQmcbUs0WKDTOfGKGqeZ0 Sp/PpqnmdrKfAovZ56krZy8++M+pVKGVAKrjoLNrCMkr3meSpiDatUbeM6DPMacK98rb YOdnlsMIhaN7BIapmC9Bx9H0zq7syNR47NTiAEHniMhU6MwVJDAppk1WgqaJYWXMq4wR y1sw== X-Gm-Message-State: APjAAAXL70F7e/zMdkSd4G4vmD4HRaOK2VFa4bVraPfs8cYksGCGD5Qa lkT4ZMdOdGVk1FvevCDzLeyux3Jn40VSs5zm0+hpYj3qmhsGHCKcDQJdC9wtz+aAOankWQ6mjf0 SgNx47mGsbNRlCGUZY+xZqfYR2ut3z2SNiVEwWw9JKY9s3w739fNIFT8uCETYOVestCVCBu0qZt BkQc3Lwr1BL9k= X-Google-Smtp-Source: APXvYqxeiBsH+6mjRgu/QkCHpQHygOljrWjBgnQ6a4QtftT9FqtqwKY/jlH3egqEL8AIPfxpN+ZzS6YmKEMlPYvLDw== X-Received: by 2002:ab0:714c:: with SMTP id k12mr58239995uao.124.1578305691993; Mon, 06 Jan 2020 02:14:51 -0800 (PST) Date: Mon, 6 Jan 2020 18:14:37 +0800 Message-Id: <20200106181425.Bluez.v1.1.I5ee1ea8e19d41c5bdffb4211aeb9cd9efa5e0a4a@changeid> Mime-Version: 1.0 X-Mailer: git-send-email 2.24.1.735.g03f4e72817-goog Subject: [Bluez PATCH v1] bluetooth: secure bluetooth stack from bluedump attack From: "howardchung@google.com" To: linux-bluetooth@vger.kernel.org, marcel@holtmann.org Cc: chromeos-bluetooth-upstreaming@chromium.org, howardchung , "David S. Miller" , Johan Hedberg , netdev@vger.kernel.org, linux-kernel@vger.kernel.org Sender: linux-bluetooth-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: howardchung Attack scenario: 1. A Chromebook (let's call this device A) is paired to a legitimate Bluetooth classic device (e.g. a speaker) (let's call this device B). 2. A malicious device (let's call this device C) pretends to be the Bluetooth speaker by using the same BT address. 3. If device A is not currently connected to device B, device A will be ready to accept connection from device B in the background (technically, doing Page Scan). 4. Therefore, device C can initiate connection to device A (because device A is doing Page Scan) and device A will accept the connection because device A trusts device C's address which is the same as device B's address. 5. Device C won't be able to communicate at any high level Bluetooth profile with device A because device A enforces that device C is encrypted with their common Link Key, which device C doesn't have. But device C can initiate pairing with device A with just-works model without requiring user interaction (there is only pairing notification). After pairing, device A now trusts device C with a new different link key, common between device A and C. 6. From now on, device A trusts device C, so device C can at anytime connect to device A to do any kind of high-level hijacking, e.g. speaker hijack or mouse/keyboard hijack. To fix this, reject the pairing if all the conditions below are met. - the pairing is initialized by peer - the authorization method is just-work - host already had the link key to the peer Also create a debugfs option to permit the pairing even the conditions above are met. Signed-off-by: howardchung --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_core.c | 47 +++++++++++++++++++++++++++++++++++++ net/bluetooth/hci_event.c | 12 ++++++++++ 3 files changed, 60 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 07b6ecedc6ce..4918b79baa41 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -283,6 +283,7 @@ enum { HCI_FORCE_STATIC_ADDR, HCI_LL_RPA_RESOLUTION, HCI_CMD_PENDING, + HCI_PERMIT_JUST_WORK_REPAIR, __HCI_NUM_FLAGS, }; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9e19d5a3aac8..9014aa567e7b 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -172,10 +172,57 @@ static const struct file_operations vendor_diag_fops = { .llseek = default_llseek, }; +static ssize_t permit_just_work_repair_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] = hci_dev_test_flag(hdev, HCI_PERMIT_JUST_WORK_REPAIR) ? 'Y' + : 'N'; + buf[1] = '\n'; + buf[2] = '\0'; + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static ssize_t permit_just_work_repair_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) + hci_dev_set_flag(hdev, HCI_PERMIT_JUST_WORK_REPAIR); + else + hci_dev_clear_flag(hdev, HCI_PERMIT_JUST_WORK_REPAIR); + + return count; +} + +static const struct file_operations permit_just_work_repair_fops = { + .open = simple_open, + .read = permit_just_work_repair_read, + .write = permit_just_work_repair_write, + .llseek = default_llseek, +}; + static void hci_debugfs_create_basic(struct hci_dev *hdev) { debugfs_create_file("dut_mode", 0644, hdev->debugfs, hdev, &dut_mode_fops); + debugfs_create_file("permit_just_work_repair", 0644, hdev->debugfs, + hdev, &permit_just_work_repair_fops); if (hdev->set_diag) debugfs_create_file("vendor_diag", 0644, hdev->debugfs, hdev, diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 6ddc4a74a5e4..898e347e19e0 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4539,6 +4539,18 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev, goto unlock; } + /* If there already exists link key in local host, terminate the + * connection by default since the remote device could be malicious. + * Permit the connection if permit_just_work_repair is enabled. + */ + if (!hci_dev_test_flag(hdev, HCI_PERMIT_JUST_WORK_REPAIR) && + hci_find_link_key(hdev, &ev->bdaddr)) { + BT_DBG("Rejecting request: local host already have link key"); + hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_NEG_REPLY, + sizeof(ev->bdaddr), &ev->bdaddr); + goto unlock; + } + /* If no side requires MITM protection; auto-accept */ if ((!loc_mitm || conn->remote_cap == HCI_IO_NO_INPUT_OUTPUT) && (!rem_mitm || conn->io_capability == HCI_IO_NO_INPUT_OUTPUT)) {