Skip to content

Commit

Permalink
Add WebBluetooth Blacklist support
Browse files Browse the repository at this point in the history
  • Loading branch information
fokinv authored and dati91 committed May 31, 2016
1 parent b116489 commit a920e6d
Show file tree
Hide file tree
Showing 8 changed files with 237 additions and 10 deletions.
124 changes: 124 additions & 0 deletions components/script/bluetooth_blacklist.rs
@@ -0,0 +1,124 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use regex::Regex;
use std::cell::RefCell;
use std::collections::HashMap;
use std::io::BufRead;
use std::string::String;
use util::resource_files::read_resource_file;

const BLACKLIST_FILE: &'static str = "gatt_blacklist.txt";
const BLACKLIST_FILE_NOT_FOUND: &'static str = "Could not find gatt_blacklist.txt file";
const EXCLUDE_READS: &'static str = "exclude-reads";
const EXCLUDE_WRITES: &'static str = "exclude-writes";
const VALID_UUID_REGEX: &'static str = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";

thread_local!(pub static BLUETOOTH_BLACKLIST: RefCell<BluetoothBlacklist> =
RefCell::new(BluetoothBlacklist(parse_blacklist())));

pub fn uuid_is_blacklisted(uuid: &str, exclude_type: Blacklist) -> bool {
BLUETOOTH_BLACKLIST.with(|blist| {
match exclude_type {
Blacklist::All => {
blist.borrow().is_blacklisted(uuid)
},
Blacklist::Reads => {
blist.borrow().is_blacklisted_for_reads(uuid)
}
Blacklist::Writes => {
blist.borrow().is_blacklisted_for_writes(uuid)
}
}
})
}

pub struct BluetoothBlacklist(Option<HashMap<String, Blacklist>>);

#[derive(Eq, PartialEq)]
pub enum Blacklist {
All, // Read and Write
Reads,
Writes,
}

impl BluetoothBlacklist {
// https://webbluetoothcg.github.io/web-bluetooth/#blacklisted
pub fn is_blacklisted(&self, uuid: &str) -> bool {
match self.0 {
Some(ref map) => map.get(uuid).map_or(false, |et| et.eq(&Blacklist::All)),
None => false,
}
}

// https://webbluetoothcg.github.io/web-bluetooth/#blacklisted-for-reads
pub fn is_blacklisted_for_reads(&self, uuid: &str) -> bool {
match self.0 {
Some(ref map) => map.get(uuid).map_or(false, |et| et.eq(&Blacklist::All) ||
et.eq(&Blacklist::Reads)),
None => false,
}
}

// https://webbluetoothcg.github.io/web-bluetooth/#blacklisted-for-writes
pub fn is_blacklisted_for_writes(&self, uuid: &str) -> bool {
match self.0 {
Some(ref map) => map.get(uuid).map_or(false, |et| et.eq(&Blacklist::All) ||
et.eq(&Blacklist::Writes)),
None => false,
}
}
}

// https://webbluetoothcg.github.io/web-bluetooth/#parsing-the-blacklist
fn parse_blacklist() -> Option<HashMap<String, Blacklist>> {
// Step 1 missing, currently we parse ./resources/gatt_blacklist.txt.
let valid_uuid_regex = Regex::new(VALID_UUID_REGEX).unwrap();
let content = read_resource_file(BLACKLIST_FILE).expect(BLACKLIST_FILE_NOT_FOUND);
// Step 3
let mut result = HashMap::new();
// Step 2 and 4
for line in content.lines() {
let line = match line {
Ok(l) => l,
Err(_) => return None,
};
// Step 4.1
if line.is_empty() || line.starts_with('#') {
continue;
}
let mut exclude_type = Blacklist::All;
let mut words = line.split_whitespace();
let uuid = match words.next() {
Some(uuid) => uuid,
None => continue,
};
if !valid_uuid_regex.is_match(uuid) {
return None;
}
match words.next() {
// Step 4.2 We already have an initialized exclude_type variable with Blacklist::All.
None => {},
// Step 4.3
Some(EXCLUDE_READS) => {
exclude_type = Blacklist::Reads;
},
Some(EXCLUDE_WRITES) => {
exclude_type = Blacklist::Writes;
},
// Step 4.4
_ => {
return None;
},
}
// Step 4.5
if result.contains_key(uuid) {
return None;
}
// Step 4.6
result.insert(uuid.to_string(), exclude_type);
}
// Step 5
return Some(result);
}
15 changes: 12 additions & 3 deletions components/script/dom/bluetooth.rs
Expand Up @@ -2,11 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use bluetooth_blacklist::{Blacklist, uuid_is_blacklisted};
use core::clone::Clone;
use dom::bindings::codegen::Bindings::BluetoothBinding;
use dom::bindings::codegen::Bindings::BluetoothBinding::RequestDeviceOptions;
use dom::bindings::codegen::Bindings::BluetoothBinding::{BluetoothScanFilter, BluetoothMethods};
use dom::bindings::error::Error::Type;
use dom::bindings::error::Error::{Security, Type};
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::Root;
Expand Down Expand Up @@ -71,7 +72,11 @@ fn canonicalize_filter(filter: &BluetoothScanFilter, global: GlobalRef) -> Falli
return Err(Type(SERVICE_ERROR.to_owned()));
}
for service in services {
services_vec.push(try!(BluetoothUUID::GetService(global, service.clone())).to_string());
let uuid = try!(BluetoothUUID::GetService(global, service.clone())).to_string();
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
return Err(Security)
}
services_vec.push(uuid);
}
}

Expand Down Expand Up @@ -119,7 +124,11 @@ fn convert_request_device_options(options: &RequestDeviceOptions,
let mut optional_services = vec!();
if let Some(ref opt_services) = options.optionalServices {
for opt_service in opt_services {
optional_services.push(try!(BluetoothUUID::GetService(global, opt_service.clone())).to_string());
let uuid = try!(BluetoothUUID::GetService(global, opt_service.clone())).to_string();
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
return Err(Security)
}
optional_services.push(uuid);
}
}

Expand Down
19 changes: 17 additions & 2 deletions components/script/dom/bluetoothremotegattcharacteristic.rs
Expand Up @@ -2,14 +2,15 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use bluetooth_blacklist::{Blacklist, uuid_is_blacklisted};
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding::
BluetoothRemoteGATTCharacteristicMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods;
use dom::bindings::error::Error::{Network, Type};
use dom::bindings::error::Error::{Network, Security, Type};
use dom::bindings::error::{Fallible, ErrorResult};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, MutHeap, Root};
Expand Down Expand Up @@ -93,6 +94,9 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-getdescriptor
fn GetDescriptor(&self, descriptor: BluetoothDescriptorUUID) -> Fallible<Root<BluetoothRemoteGATTDescriptor>> {
let uuid = try!(BluetoothUUID::GetDescriptor(self.global().r(), descriptor)).to_string();
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
return Err(Security)
}
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothMethodMsg::GetDescriptor(self.get_instance_id(), uuid, sender)).unwrap();
Expand All @@ -116,7 +120,12 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris
-> Fallible<Vec<Root<BluetoothRemoteGATTDescriptor>>> {
let mut uuid: Option<String> = None;
if let Some(d) = descriptor {
uuid = Some(try!(BluetoothUUID::GetDescriptor(self.global().r(), d)).to_string())
uuid = Some(try!(BluetoothUUID::GetDescriptor(self.global().r(), d)).to_string());
if let Some(ref uuid) = uuid {
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
return Err(Security)
}
}
};
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
Expand Down Expand Up @@ -144,6 +153,9 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris

// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-readvalue
fn ReadValue(&self) -> Fallible<ByteString> {
if uuid_is_blacklisted(self.uuid.as_ref(), Blacklist::Reads) {
return Err(Security)
}
let (sender, receiver) = ipc::channel().unwrap();
if !self.Service().Device().Gatt().Connected() {
return Err(Network)
Expand All @@ -165,6 +177,9 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris

// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-writevalue
fn WriteValue(&self, value: Vec<u8>) -> ErrorResult {
if uuid_is_blacklisted(self.uuid.as_ref(), Blacklist::Writes) {
return Err(Security)
}
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothMethodMsg::WriteValue(self.get_instance_id(), value, sender)).unwrap();
Expand Down
9 changes: 8 additions & 1 deletion components/script/dom/bluetoothremotegattdescriptor.rs
Expand Up @@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use bluetooth_blacklist::{Blacklist, uuid_is_blacklisted};
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding::
Expand All @@ -10,7 +11,7 @@ use dom::bindings::codegen::Bindings::BluetoothRemoteGATTDescriptorBinding;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTDescriptorBinding::BluetoothRemoteGATTDescriptorMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods;
use dom::bindings::error::Error::{Type, Network};
use dom::bindings::error::Error::{Network, Security, Type};
use dom::bindings::error::{Fallible, ErrorResult};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, MutHeap, Root};
Expand Down Expand Up @@ -85,6 +86,9 @@ impl BluetoothRemoteGATTDescriptorMethods for BluetoothRemoteGATTDescriptor {

// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-readvalue
fn ReadValue(&self) -> Fallible<ByteString> {
if uuid_is_blacklisted(self.uuid.as_ref(), Blacklist::Reads) {
return Err(Security)
}
let (sender, receiver) = ipc::channel().unwrap();
if !self.Characteristic().Service().Device().Gatt().Connected() {
return Err(Network)
Expand All @@ -106,6 +110,9 @@ impl BluetoothRemoteGATTDescriptorMethods for BluetoothRemoteGATTDescriptor {

// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-writevalue
fn WriteValue(&self, value: Vec<u8>) -> ErrorResult {
if uuid_is_blacklisted(self.uuid.as_ref(), Blacklist::Writes) {
return Err(Security)
}
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothMethodMsg::WriteValue(self.get_instance_id(), value, sender)).unwrap();
Expand Down
13 changes: 11 additions & 2 deletions components/script/dom/bluetoothremotegattserver.rs
Expand Up @@ -2,10 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use bluetooth_blacklist::{Blacklist, uuid_is_blacklisted};
use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
use dom::bindings::error::Error::Type;
use dom::bindings::error::Error::{Security, Type};
use dom::bindings::error::{Fallible, ErrorResult};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, MutHeap, Root};
Expand Down Expand Up @@ -96,6 +97,9 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer {
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservice
fn GetPrimaryService(&self, service: BluetoothServiceUUID) -> Fallible<Root<BluetoothRemoteGATTService>> {
let uuid = try!(BluetoothUUID::GetService(self.global().r(), service)).to_string();
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
return Err(Security)
}
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothMethodMsg::GetPrimaryService(String::from(self.Device().Id()), uuid, sender)).unwrap();
Expand All @@ -120,7 +124,12 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer {
-> Fallible<Vec<Root<BluetoothRemoteGATTService>>> {
let mut uuid: Option<String> = None;
if let Some(s) = service {
uuid = Some(try!(BluetoothUUID::GetService(self.global().r(), s)).to_string())
uuid = Some(try!(BluetoothUUID::GetService(self.global().r(), s)).to_string());
if let Some(ref uuid) = uuid {
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
return Err(Security)
}
}
};
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
Expand Down
13 changes: 11 additions & 2 deletions components/script/dom/bluetoothremotegattservice.rs
Expand Up @@ -2,9 +2,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use bluetooth_blacklist::{Blacklist, uuid_is_blacklisted};
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods;
use dom::bindings::error::Error::Type;
use dom::bindings::error::Error::{Security, Type};
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, MutHeap, Root};
Expand Down Expand Up @@ -88,6 +89,9 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService {
characteristic: BluetoothCharacteristicUUID)
-> Fallible<Root<BluetoothRemoteGATTCharacteristic>> {
let uuid = try!(BluetoothUUID::GetCharacteristic(self.global().r(), characteristic)).to_string();
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
return Err(Security)
}
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothMethodMsg::GetCharacteristic(self.get_instance_id(), uuid, sender)).unwrap();
Expand Down Expand Up @@ -122,7 +126,12 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService {
-> Fallible<Vec<Root<BluetoothRemoteGATTCharacteristic>>> {
let mut uuid: Option<String> = None;
if let Some(c) = characteristic {
uuid = Some(try!(BluetoothUUID::GetCharacteristic(self.global().r(), c)).to_string())
uuid = Some(try!(BluetoothUUID::GetCharacteristic(self.global().r(), c)).to_string());
if let Some(ref uuid) = uuid {
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
return Err(Security)
}
}
};
let mut characteristics = vec!();
let (sender, receiver) = ipc::channel().unwrap();
Expand Down
1 change: 1 addition & 0 deletions components/script/lib.rs
Expand Up @@ -87,6 +87,7 @@ extern crate webrender_traits;
extern crate websocket;
extern crate xml5ever;

pub mod bluetooth_blacklist;
pub mod clipboard_provider;
pub mod cors;
mod devtools;
Expand Down
53 changes: 53 additions & 0 deletions resources/gatt_blacklist.txt
@@ -0,0 +1,53 @@
# Source:
# https://github.com/WebBluetoothCG/registries/blob/master/gatt_blacklist.txt
# License:
# https://github.com/WebBluetoothCG/registries/blob/master/LICENSE

# This file holds a list of GATT UUIDs that websites using the Web
# Bluetooth API are forbidden from accessing.

## Services

# org.bluetooth.service.human_interface_device
# Direct access to HID devices like keyboards would let web pages
# become keyloggers.
00001812-0000-1000-8000-00805f9b34fb

# Firmware update services that don't check the update's signature
# present a risk of devices' software being modified by malicious web
# pages. Users may connect to a device believing they are enabling
# only simple interaction or that they're interacting with the
# device's manufacturer, but the site might instead persistently
# compromise the device.
#
# Nordic's Device Firmware Update service, http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.sdk5.v11.0.0/examples_ble_dfu.html:
00001530-1212-efde-1523-785feabcd123
# TI's Over-the-Air Download service, http://www.ti.com/lit/ug/swru271g/swru271g.pdf:
f000ffc0-0451-4000-b000-000000000000


## Characteristics

# org.bluetooth.characteristic.gap.peripheral_privacy_flag
# Don't let web pages turn off privacy mode.
00002a02-0000-1000-8000-00805f9b34fb exclude-writes

# org.bluetooth.characteristic.gap.reconnection_address
# Disallow messing with connection parameters
00002a03-0000-1000-8000-00805f9b34fb

# org.bluetooth.characteristic.serial_number_string
# Block access to standardized unique identifiers, for privacy reasons.
00002a25-0000-1000-8000-00805f9b34fb


## Descriptors

# org.bluetooth.descriptor.gatt.client_characteristic_configuration
# Writing to this would let a web page interfere with other pages'
# notifications and indications.
00002902-0000-1000-8000-00805f9b34fb exclude-writes

# org.bluetooth.descriptor.gatt.server_characteristic_configuration
# Writing to this would let a web page interfere with the broadcasted services.
00002903-0000-1000-8000-00805f9b34fb exclude-writes

0 comments on commit a920e6d

Please sign in to comment.