// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/usb/usb_chooser_context.h" #include #include #include "base/metrics/histogram_macros.h" #include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/profiles/profile.h" #include "device/core/device_client.h" #include "device/usb/public/interfaces/device.mojom.h" #include "device/usb/usb_device.h" using device::UsbDevice; namespace { const char kDeviceNameKey[] = "name"; const char kGuidKey[] = "ephemeral-guid"; const char kProductIdKey[] = "product-id"; const char kSerialNumberKey[] = "serial-number"; const char kVendorIdKey[] = "vendor-id"; // Reasons a permission may be closed. These are used in histograms so do not // remove/reorder entries. Only add at the end just before // WEBUSB_PERMISSION_REVOKED_MAX. Also remember to update the enum listing in // tools/metrics/histograms/histograms.xml. enum WebUsbPermissionRevoked { // Permission to access a USB device was revoked by the user. WEBUSB_PERMISSION_REVOKED = 0, // Permission to access an ephemeral USB device was revoked by the user. WEBUSB_PERMISSION_REVOKED_EPHEMERAL, // Maximum value for the enum. WEBUSB_PERMISSION_REVOKED_MAX }; void RecordPermissionRevocation(WebUsbPermissionRevoked kind) { UMA_HISTOGRAM_ENUMERATION("WebUsb.PermissionRevoked", kind, WEBUSB_PERMISSION_REVOKED_MAX); } bool CanStorePersistentEntry(const scoped_refptr& device) { return !device->serial_number().empty(); } } // namespace UsbChooserContext::UsbChooserContext(Profile* profile) : ChooserContextBase(profile, CONTENT_SETTINGS_TYPE_USB_CHOOSER_DATA), is_off_the_record_(profile->IsOffTheRecord()), observer_(this) { usb_service_ = device::DeviceClient::Get()->GetUsbService(); if (usb_service_) observer_.Add(usb_service_); } UsbChooserContext::~UsbChooserContext() {} std::vector> UsbChooserContext::GetGrantedObjects(const GURL& requesting_origin, const GURL& embedding_origin) { std::vector> objects = ChooserContextBase::GetGrantedObjects(requesting_origin, embedding_origin); auto it = ephemeral_devices_.find( std::make_pair(requesting_origin, embedding_origin)); if (it != ephemeral_devices_.end()) { for (const std::string& guid : it->second) { scoped_refptr device = usb_service_->GetDevice(guid); DCHECK(device); scoped_ptr object(new base::DictionaryValue()); object->SetString(kDeviceNameKey, device->product_string()); object->SetString(kGuidKey, device->guid()); objects.push_back(std::move(object)); } } return objects; } std::vector> UsbChooserContext::GetAllGrantedObjects() { std::vector> objects = ChooserContextBase::GetAllGrantedObjects(); for (const auto& map_entry : ephemeral_devices_) { const GURL& requesting_origin = map_entry.first.first; const GURL& embedding_origin = map_entry.first.second; for (const std::string& guid : map_entry.second) { scoped_refptr device = usb_service_->GetDevice(guid); DCHECK(device); base::DictionaryValue object; object.SetString(kDeviceNameKey, device->product_string()); object.SetString(kGuidKey, device->guid()); objects.push_back(make_scoped_ptr(new ChooserContextBase::Object( requesting_origin, embedding_origin, &object, "preference", is_off_the_record_))); } } return objects; } void UsbChooserContext::RevokeObjectPermission( const GURL& requesting_origin, const GURL& embedding_origin, const base::DictionaryValue& object) { std::string guid; if (object.GetString(kGuidKey, &guid)) { auto it = ephemeral_devices_.find( std::make_pair(requesting_origin, embedding_origin)); if (it != ephemeral_devices_.end()) { it->second.erase(guid); if (it->second.empty()) ephemeral_devices_.erase(it); } RecordPermissionRevocation(WEBUSB_PERMISSION_REVOKED_EPHEMERAL); } else { ChooserContextBase::RevokeObjectPermission(requesting_origin, embedding_origin, object); RecordPermissionRevocation(WEBUSB_PERMISSION_REVOKED); } } void UsbChooserContext::GrantDevicePermission(const GURL& requesting_origin, const GURL& embedding_origin, const std::string& guid) { scoped_refptr device = usb_service_->GetDevice(guid); if (!device) return; if (CanStorePersistentEntry(device)) { scoped_ptr device_dict(new base::DictionaryValue()); device_dict->SetString(kDeviceNameKey, device->product_string()); device_dict->SetInteger(kVendorIdKey, device->vendor_id()); device_dict->SetInteger(kProductIdKey, device->product_id()); device_dict->SetString(kSerialNumberKey, device->serial_number()); GrantObjectPermission(requesting_origin, embedding_origin, std::move(device_dict)); } else { ephemeral_devices_[std::make_pair(requesting_origin, embedding_origin)] .insert(guid); } } bool UsbChooserContext::HasDevicePermission( const GURL& requesting_origin, const GURL& embedding_origin, const device::usb::DeviceInfo& device_info) { auto it = ephemeral_devices_.find( std::make_pair(requesting_origin, embedding_origin)); if (it != ephemeral_devices_.end() && ContainsValue(it->second, device_info.guid)) { return true; } std::vector> device_list = GetGrantedObjects(requesting_origin, embedding_origin); for (const scoped_ptr& device_dict : device_list) { int vendor_id; int product_id; std::string serial_number; if (device_dict->GetInteger(kVendorIdKey, &vendor_id) && device_info.vendor_id == vendor_id && device_dict->GetInteger(kProductIdKey, &product_id) && device_info.product_id == product_id && device_dict->GetString(kSerialNumberKey, &serial_number) && device_info.serial_number == serial_number) { return true; } } return false; } bool UsbChooserContext::IsValidObject(const base::DictionaryValue& object) { return object.size() == 4 && object.HasKey(kDeviceNameKey) && object.HasKey(kVendorIdKey) && object.HasKey(kProductIdKey) && object.HasKey(kSerialNumberKey); } void UsbChooserContext::OnDeviceRemoved(scoped_refptr device) { for (auto& map_entry : ephemeral_devices_) map_entry.second.erase(device->guid()); }