In order to facilitate impersonation attacks against devices using resolvable private addresses (RPAs), this debugfs entry allows the user to set a fixed RPA that is used during undirected and directed advertising. Writing 00:00:00:00:00:00 disables the forced address and will resume generating valid RPAs. Signed-off-by: Mike Ryan <mike@xxxxxxx> --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_debugfs.c | 54 ++++++++++++++++++++++++++++++++++++++++ net/bluetooth/hci_request.c | 15 ++++++++--- 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b689aceb6..a822914d7 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -428,6 +428,7 @@ struct hci_dev { __u32 rpa_timeout; struct delayed_work rpa_expired; bdaddr_t rpa; + bdaddr_t force_rpa; #if IS_ENABLED(CONFIG_BT_LEDS) struct led_trigger *power_led; diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c index 402e2cc54..1efefe82c 100644 --- a/net/bluetooth/hci_debugfs.c +++ b/net/bluetooth/hci_debugfs.c @@ -710,6 +710,58 @@ static const struct file_operations force_static_address_fops = { .llseek = default_llseek, }; +static ssize_t force_rpa_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + char buf[19]; + bdaddr_t *ba = &hdev->force_rpa; + + sprintf(buf, "%pMR\n", ba); + + return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); +} + +static ssize_t force_rpa_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + char buf[18]; + size_t buf_size = min(count, (sizeof(buf)-1)); + bdaddr_t rpa; + + if (test_bit(HCI_UP, &hdev->flags)) + return -EBUSY; + + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + buf[buf_size] = '\0'; + if (str2ba(buf, &rpa)) + return -EINVAL; + + /* The two most significant bits shall be 01 unless the address is + * 00:00:00:00:00:00. + */ + if ((rpa.b[5] & 0xc0) != 0x40 && bacmp(&rpa, BDADDR_ANY)) + return -EINVAL; + + if (!bacmp(&hdev->force_rpa, &rpa)) + return -EALREADY; + + bacpy(&hdev->force_rpa, &rpa); + + return count; +} + +static const struct file_operations force_rpa_fops = { + .open = simple_open, + .read = force_rpa_read, + .write = force_rpa_write, + .llseek = default_llseek, +}; + static int white_list_show(struct seq_file *f, void *ptr) { struct hci_dev *hdev = f->private; @@ -1026,6 +1078,8 @@ void hci_debugfs_create_le(struct hci_dev *hdev) hdev->debugfs, hdev, &force_static_address_fops); + debugfs_create_file("force_rpa", 0644, hdev->debugfs, hdev, + &force_rpa_fops); debugfs_create_u8("white_list_size", 0444, hdev->debugfs, &hdev->le_white_list_size); debugfs_create_file("white_list", 0444, hdev->debugfs, hdev, diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index 2a1b64dbf..63f4d5c16 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -1914,10 +1914,17 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy, !bacmp(&hdev->random_addr, &hdev->rpa)) return 0; - err = smp_generate_rpa(hdev, hdev->irk, &hdev->rpa); - if (err < 0) { - bt_dev_err(hdev, "failed to generate new RPA"); - return err; + /* If force_rpa is set to 00:00:00:00:00:00, generate a valid + * RPA using IRK. Otherwise use the forced value. + */ + if (!bacmp(&hdev->force_rpa, BDADDR_ANY)) { + err = smp_generate_rpa(hdev, hdev->irk, &hdev->rpa); + if (err < 0) { + bt_dev_err(hdev, "failed to generate new RPA"); + return err; + } + } else { + bacpy(&hdev->rpa, &hdev->force_rpa); } set_random_addr(req, &hdev->rpa); -- 2.11.0