From be2162ec9ebc00a8e56aca490d237fb4540462f7 Mon Sep 17 00:00:00 2001 From: Greg Steuck Date: Tue, 24 Dec 2019 13:23:37 -0800 Subject: [PATCH] Avoid chromium crashes around U2F/FIDO. https://bugs.chromium.org/p/chromium/issues/detail?id=451248 The code was copied from FreeBSD port and then badly hacked. It still has some utility because people can use alternative means of authentication after cancelling the FIDO request. The next step is to build real support based on /dev/fido. --- www/chromium/Makefile | 2 + .../patch-services_device_hid_BUILD_gn | 8 +- .../patch-services_device_hid_hid_service_cc | 23 ++ ...services_device_hid_hid_service_freebsd_cc | 351 ++++++++++++++++++ ...-services_device_hid_hid_service_freebsd_h | 54 +++ 5 files changed, 437 insertions(+), 1 deletion(-) create mode 100644 www/chromium/patches/patch-services_device_hid_hid_service_cc create mode 100644 www/chromium/patches/patch-services_device_hid_hid_service_freebsd_cc create mode 100644 www/chromium/patches/patch-services_device_hid_hid_service_freebsd_h diff --git a/www/chromium/Makefile b/www/chromium/Makefile index 9617138c375..2369d6e3a23 100644 --- a/www/chromium/Makefile +++ b/www/chromium/Makefile @@ -35,6 +35,8 @@ FLAVORS= debug component electron FLAVOR?= .if ${FLAVOR:Melectron} REVISION= 3 +.else +REVISION= 0 .endif # BSD-like diff --git a/www/chromium/patches/patch-services_device_hid_BUILD_gn b/www/chromium/patches/patch-services_device_hid_BUILD_gn index 9ee086068f1..34609716daa 100644 --- a/www/chromium/patches/patch-services_device_hid_BUILD_gn +++ b/www/chromium/patches/patch-services_device_hid_BUILD_gn @@ -3,7 +3,7 @@ $OpenBSD: patch-services_device_hid_BUILD_gn,v 1.3 2018/04/23 15:00:16 robert Ex Index: services/device/hid/BUILD.gn --- services/device/hid/BUILD.gn.orig +++ services/device/hid/BUILD.gn -@@ -41,6 +41,13 @@ source_set("hid") { +@@ -41,6 +41,19 @@ source_set("hid") { "//services/device/public/mojom", ] @@ -12,6 +12,12 @@ Index: services/device/hid/BUILD.gn + "hid_connection_linux.cc", + "hid_connection_linux.h", + ] ++ sources += [ ++ "hid_connection_freebsd.cc", ++ "hid_connection_freebsd.h", ++ "hid_service_freebsd.cc", ++ "hid_service_freebsd.h", ++ ] + } + if (is_linux && use_udev) { diff --git a/www/chromium/patches/patch-services_device_hid_hid_service_cc b/www/chromium/patches/patch-services_device_hid_hid_service_cc new file mode 100644 index 00000000000..ea0516bf18e --- /dev/null +++ b/www/chromium/patches/patch-services_device_hid_hid_service_cc @@ -0,0 +1,23 @@ +$OpenBSD$ + +Index: services/device/hid/hid_service.cc +--- services/device/hid/hid_service.cc.orig ++++ services/device/hid/hid_service.cc +@@ -16,6 +16,8 @@ + + #if defined(OS_LINUX) && defined(USE_UDEV) + #include "services/device/hid/hid_service_linux.h" ++#elif defined(OS_BSD) ++#include "services/device/hid/hid_service_freebsd.h" + #elif defined(OS_MACOSX) + #include "services/device/hid/hid_service_mac.h" + #elif defined(OS_WIN) +@@ -36,6 +38,8 @@ constexpr base::TaskTraits HidService::kBlockingTaskTr + std::unique_ptr HidService::Create() { + #if defined(OS_LINUX) && defined(USE_UDEV) + return base::WrapUnique(new HidServiceLinux()); ++#elif defined(OS_BSD) ++ return base::WrapUnique(new HidServiceFreeBSD()); + #elif defined(OS_MACOSX) + return base::WrapUnique(new HidServiceMac()); + #elif defined(OS_WIN) diff --git a/www/chromium/patches/patch-services_device_hid_hid_service_freebsd_cc b/www/chromium/patches/patch-services_device_hid_hid_service_freebsd_cc new file mode 100644 index 00000000000..527513e2499 --- /dev/null +++ b/www/chromium/patches/patch-services_device_hid_hid_service_freebsd_cc @@ -0,0 +1,351 @@ +$OpenBSD$ + +Index: services/device/hid/hid_service_freebsd.cc +--- services/device/hid/hid_service_freebsd.cc.orig ++++ services/device/hid/hid_service_freebsd.cc +@@ -0,0 +1,345 @@ ++// Copyright 2014 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 "services/device/hid/hid_service_freebsd.h" ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "base/bind.h" ++#include "base/files/file_descriptor_watcher_posix.h" ++#include "base/files/file_enumerator.h" ++#include "base/files/file.h" ++#include "base/location.h" ++#include "base/logging.h" ++#include "base/posix/eintr_wrapper.h" ++#include "base/single_thread_task_runner.h" ++#include "base/stl_util.h" ++#include "base/strings/pattern.h" ++#include "base/strings/stringprintf.h" ++#include "base/strings/sys_string_conversions.h" ++#include "base/strings/string_util.h" ++#include "base/strings/string_split.h" ++#include "base/task/post_task.h" ++#include "base/threading/scoped_blocking_call.h" ++#include "base/threading/thread_task_runner_handle.h" ++#include "components/device_event_log/device_event_log.h" ++#include "services/device/hid/hid_connection_freebsd.h" ++ ++const int kMaxPermissionChecks = 5; ++ ++namespace device { ++ ++struct HidServiceFreeBSD::ConnectParams { ++ ConnectParams(scoped_refptr device_info, ++ const ConnectCallback& callback) ++ : device_info(std::move(device_info)), ++ callback(callback), ++ task_runner(base::ThreadTaskRunnerHandle::Get()), ++ blocking_task_runner( ++ base::CreateSequencedTaskRunner(kBlockingTaskTraits)) {} ++ ~ConnectParams() {} ++ ++ scoped_refptr device_info; ++ ConnectCallback callback; ++ scoped_refptr task_runner; ++ scoped_refptr blocking_task_runner; ++ base::ScopedFD fd; ++}; ++ ++class HidServiceFreeBSD::BlockingTaskHelper { ++ public: ++ BlockingTaskHelper(base::WeakPtr service) ++ : service_(std::move(service)), ++ task_runner_(base::ThreadTaskRunnerHandle::Get()) { ++ DETACH_FROM_SEQUENCE(sequence_checker_); ++ ++ timer_.reset(new base::RepeatingTimer()); ++ devd_buffer_ = new net::IOBufferWithSize(1024); ++ } ++ ++ ~BlockingTaskHelper() { ++ } ++ ++ void Start() { ++ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); ++ ++ const base::FilePath kDevRoot("/dev"); ++ const std::string kUHIDPattern("/dev/uhid*"); ++ ++ base::FileEnumerator enumerator(kDevRoot, false, base::FileEnumerator::FILES); ++ do { ++ const base::FilePath next_device_path(enumerator.Next()); ++ const std::string next_device = next_device_path.value(); ++ if (next_device.empty()) ++ break; ++ ++ if (base::MatchPattern(next_device, kUHIDPattern)) ++ OnDeviceAdded(next_device.substr(5)); ++ } while (true); ++ ++ SetupDevdMonitor(); ++ ++ task_runner_->PostTask( ++ FROM_HERE, ++ base::Bind(&HidServiceFreeBSD::FirstEnumerationComplete, service_)); ++ } ++ ++ bool HaveReadWritePermissions(std::string device_id) { ++ std::string device_node = "/dev/" + device_id; ++ base::internal::AssertBlockingAllowed(); ++ ++ base::FilePath device_path(device_node); ++ base::File device_file; ++ int flags = ++ base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE; ++ device_file.Initialize(device_path, flags); ++ if (!device_file.IsValid()) ++ return false; ++ ++ return true; ++ } ++ ++ void OnDeviceAdded(std::string device_id) { ++ base::ScopedBlockingCall scoped_blocking_call( ++ FROM_HERE, base::BlockingType::MAY_BLOCK); ++ std::string device_node = "/dev/" + device_id; ++ uint16_t vendor_id = 0xffff; ++ uint16_t product_id = 0xffff; ++ std::string product_name = ""; ++ std::string serial_number = ""; ++ ++ std::vector report_descriptor; ++ ++ base::internal::AssertBlockingAllowed(); ++ ++ base::FilePath device_path(device_node); ++ base::File device_file; ++ int flags = ++ base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE; ++ device_file.Initialize(device_path, flags); ++ if (!device_file.IsValid()) { ++ HID_LOG(ERROR) << "Failed to open '" << device_node ++ << "': " ++ << base::File::ErrorToString(device_file.error_details()); ++ return; ++ } ++ ++ base::ScopedFD fd; ++ fd.reset(device_file.TakePlatformFile()); ++ ++ HID_LOG(ERROR) << "Failed to get report descriptor size"; ++ } ++ ++ void OnDeviceRemoved(std::string device_id) { ++ base::ScopedBlockingCall scoped_blocking_call( ++ FROM_HERE, base::BlockingType::MAY_BLOCK); ++ task_runner_->PostTask( ++ FROM_HERE, base::Bind(&HidServiceFreeBSD::RemoveDevice, service_, ++ device_id)); ++ } ++ ++ private: ++ ++ void CheckPendingPermissionChange() { ++ base::internal::AssertBlockingAllowed(); ++ std::map::iterator it; ++ for (it = permissions_checks_attempts_.begin(); it != permissions_checks_attempts_.end();) { ++ std::string device_name = it->first; ++ bool keep = true; ++ if (HaveReadWritePermissions(device_name)) { ++ OnDeviceAdded(device_name); ++ keep = false; ++ } ++ else if (it->second-- <= 0) { ++ HID_LOG(ERROR) << "Still don't have write permissions to '" << device_name ++ << "' after " << kMaxPermissionChecks << " attempts"; ++ keep = false; ++ } ++ ++ if (keep) ++ ++it; ++ else ++ permissions_checks_attempts_.erase(it++); ++ } ++ ++ if (permissions_checks_attempts_.empty()) ++ timer_->Stop(); ++ } ++ ++ void SetupDevdMonitor() { ++ base::internal::AssertBlockingAllowed(); ++ ++ int devd_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); ++ if (devd_fd < 0) ++ return; ++ ++ struct sockaddr_un sa; ++ ++ sa.sun_family = AF_UNIX; ++ strlcpy(sa.sun_path, "/var/run/devd.seqpacket.pipe", sizeof(sa.sun_path)); ++ if (connect(devd_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { ++ close(devd_fd); ++ return; ++ } ++ ++ devd_fd_.reset(devd_fd); ++ file_watcher_ = base::FileDescriptorWatcher::WatchReadable( ++ devd_fd_.get(), base::Bind(&BlockingTaskHelper::OnDevdMessageCanBeRead, ++ base::Unretained(this))); ++ } ++ ++ void OnDevdMessageCanBeRead() { ++ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); ++ ssize_t bytes_read = HANDLE_EINTR(recv(devd_fd_.get(), devd_buffer_->data(), ++ devd_buffer_->size() - 1, MSG_WAITALL)); ++ if (bytes_read < 0) { ++ if (errno != EAGAIN) { ++ HID_LOG(ERROR) << "Read failed"; ++ file_watcher_.reset(); ++ } ++ return; ++ } ++ ++ devd_buffer_->data()[bytes_read] = 0; ++ char *data = devd_buffer_->data(); ++ // It may take some time for devd to change permissions ++ // on /dev/uhidX node. So do not fail immediately if ++ // open fail. Retry each second for kMaxPermissionChecks ++ // times before giving up entirely ++ if (base::StartsWith(data, "+uhid", base::CompareCase::SENSITIVE)) { ++ std::vector parts = base::SplitString( ++ data, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); ++ if (!parts.empty()) { ++ std::string device_name = parts[0].substr(1); // skip '+' ++ if (HaveReadWritePermissions(device_name)) ++ OnDeviceAdded(parts[0].substr(1)); ++ else { ++ // Do not re-add to checks ++ if (permissions_checks_attempts_.find(device_name) == permissions_checks_attempts_.end()) { ++ permissions_checks_attempts_.insert(std::pair(device_name, kMaxPermissionChecks)); ++ timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(1), ++ this, &BlockingTaskHelper::CheckPendingPermissionChange); ++ } ++ } ++ } ++ } ++ ++ if (base::StartsWith(data, "-uhid", base::CompareCase::SENSITIVE)) { ++ std::vector parts = base::SplitString( ++ data, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); ++ if (!parts.empty()) { ++ std::string device_name = parts[0].substr(1); // skip '-' ++ auto it = permissions_checks_attempts_.find(device_name); ++ if (it != permissions_checks_attempts_.end()) { ++ permissions_checks_attempts_.erase(it); ++ if (permissions_checks_attempts_.empty()) ++ timer_->Stop(); ++ } ++ OnDeviceRemoved(parts[0].substr(1)); ++ } ++ } ++ } ++ ++ SEQUENCE_CHECKER(sequence_checker_); ++ ++ // This weak pointer is only valid when checked on this task runner. ++ base::WeakPtr service_; ++ scoped_refptr task_runner_; ++ std::unique_ptr file_watcher_; ++ std::unique_ptr timer_; ++ base::ScopedFD devd_fd_; ++ scoped_refptr devd_buffer_; ++ std::map permissions_checks_attempts_; ++ ++ DISALLOW_COPY_AND_ASSIGN(BlockingTaskHelper); ++}; ++ ++HidServiceFreeBSD::HidServiceFreeBSD() ++ : task_runner_(base::ThreadTaskRunnerHandle::Get()), ++ blocking_task_runner_( ++ base::CreateSequencedTaskRunner(kBlockingTaskTraits)), ++ weak_factory_(this) { ++ helper_ = std::make_unique(weak_factory_.GetWeakPtr()); ++ blocking_task_runner_->PostTask( ++ FROM_HERE, ++ base::Bind(&BlockingTaskHelper::Start, base::Unretained(helper_.get()))); ++} ++ ++HidServiceFreeBSD::~HidServiceFreeBSD() { ++ blocking_task_runner_->DeleteSoon(FROM_HERE, helper_.release()); ++} ++ ++base::WeakPtr HidServiceFreeBSD::GetWeakPtr() { ++ return weak_factory_.GetWeakPtr(); ++} ++ ++// static ++void HidServiceFreeBSD::OpenOnBlockingThread( ++ std::unique_ptr params) { ++ base::ScopedBlockingCall scoped_blocking_call( ++ FROM_HERE, base::BlockingType::MAY_BLOCK); ++ scoped_refptr task_runner = params->task_runner; ++ ++ base::FilePath device_path(params->device_info->device_node()); ++ base::File device_file; ++ int flags = ++ base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE; ++ device_file.Initialize(device_path, flags); ++ if (!device_file.IsValid()) { ++ HID_LOG(EVENT) << "Failed to open '" << params->device_info->device_node() ++ << "': " ++ << base::File::ErrorToString(device_file.error_details()); ++ task_runner->PostTask(FROM_HERE, base::Bind(params->callback, nullptr)); ++ return; ++ } ++ params->fd.reset(device_file.TakePlatformFile()); ++ FinishOpen(std::move(params)); ++} ++ ++void HidServiceFreeBSD::Connect(const std::string& device_guid, ++ const ConnectCallback& callback) { ++ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); ++ ++ const auto& map_entry = devices().find(device_guid); ++ if (map_entry == devices().end()) { ++ base::ThreadTaskRunnerHandle::Get()->PostTask( ++ FROM_HERE, base::Bind(callback, nullptr)); ++ return; ++ } ++ ++ scoped_refptr device_info = map_entry->second; ++ ++ auto params = std::make_unique(device_info, callback); ++ ++ scoped_refptr blocking_task_runner = ++ params->blocking_task_runner; ++ blocking_task_runner->PostTask( ++ FROM_HERE, base::Bind(&HidServiceFreeBSD::OpenOnBlockingThread, ++ base::Passed(¶ms))); ++} ++ ++// static ++void HidServiceFreeBSD::FinishOpen(std::unique_ptr params) { ++ scoped_refptr task_runner = params->task_runner; ++ ++ task_runner->PostTask( ++ FROM_HERE, ++ base::Bind(&HidServiceFreeBSD::CreateConnection, base::Passed(¶ms))); ++} ++ ++// static ++void HidServiceFreeBSD::CreateConnection(std::unique_ptr params) { ++ DCHECK(params->fd.is_valid()); ++ params->callback.Run(base::MakeRefCounted( ++ std::move(params->device_info), std::move(params->fd), ++ std::move(params->blocking_task_runner))); ++} ++ ++} // namespace device diff --git a/www/chromium/patches/patch-services_device_hid_hid_service_freebsd_h b/www/chromium/patches/patch-services_device_hid_hid_service_freebsd_h new file mode 100644 index 00000000000..fb1ace92155 --- /dev/null +++ b/www/chromium/patches/patch-services_device_hid_hid_service_freebsd_h @@ -0,0 +1,54 @@ +$OpenBSD$ + +Index: services/device/hid/hid_service_freebsd.h +--- services/device/hid/hid_service_freebsd.h.orig ++++ services/device/hid/hid_service_freebsd.h +@@ -0,0 +1,48 @@ ++// Copyright 2014 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. ++ ++#ifndef DEVICE_HID_HID_SERVICE_FREEBSD_H_ ++#define DEVICE_HID_HID_SERVICE_FREEBSD_H_ ++ ++#include ++ ++#include "base/macros.h" ++#include "base/memory/ref_counted.h" ++#include "base/memory/weak_ptr.h" ++#include "base/timer/timer.h" ++#include "services/device/hid/hid_service.h" ++#include "net/base/io_buffer.h" ++ ++namespace device { ++ ++class HidServiceFreeBSD : public HidService { ++ public: ++ HidServiceFreeBSD(); ++ ~HidServiceFreeBSD() override; ++ ++ void Connect(const std::string& device_guid, ++ const ConnectCallback& connect) override; ++ base::WeakPtr GetWeakPtr() override; ++ ++ private: ++ struct ConnectParams; ++ class BlockingTaskHelper; ++ ++ static void OpenOnBlockingThread(std::unique_ptr params); ++ static void FinishOpen(std::unique_ptr params); ++ static void CreateConnection(std::unique_ptr params); ++ ++ const scoped_refptr task_runner_; ++ const scoped_refptr blocking_task_runner_; ++ // |helper_| lives on the sequence |blocking_task_runner_| posts to and holds ++ // a weak reference back to the service that owns it. ++ std::unique_ptr helper_; ++ base::WeakPtrFactory weak_factory_; ++ ++ DISALLOW_COPY_AND_ASSIGN(HidServiceFreeBSD); ++}; ++ ++} // namespace device ++ ++#endif // DEVICE_HID_HID_SERVICE_FREEBSD_H_ -- 2.24.1