git: 3e85eb3c5ef0 - stable/13 - libfido2: backport hidraw(4) support

From: Ed Maste <emaste_at_FreeBSD.org>
Date: Fri, 22 Sep 2023 11:07:34 UTC
The branch stable/13 has been updated by emaste:

URL: https://cgit.FreeBSD.org/src/commit/?id=3e85eb3c5ef0c08ae3a7db543d72713c365ca3a4

commit 3e85eb3c5ef0c08ae3a7db543d72713c365ca3a4
Author:     Ed Maste <emaste@FreeBSD.org>
AuthorDate: 2023-09-19 16:50:13 +0000
Commit:     Ed Maste <emaste@FreeBSD.org>
CommitDate: 2023-09-22 11:07:25 +0000

    libfido2: backport hidraw(4) support
    
    PR:             273596
    Submitted by:   David Horn
    Obtained from:  libfido2 2c9dfaae2a56, 930160388700
    
    (cherry picked from commit 1843dfb05ed80149f5a412180af882e3cb8f451b)
    (cherry picked from commit 3df74eecfdecbafe55838b45141c275754d526bb)
---
 contrib/libfido2/src/hid_freebsd.c | 97 ++++++++++++++++++++++++++++++++++----
 1 file changed, 87 insertions(+), 10 deletions(-)

diff --git a/contrib/libfido2/src/hid_freebsd.c b/contrib/libfido2/src/hid_freebsd.c
index 5aefe69c1bec..5151690afc0a 100644
--- a/contrib/libfido2/src/hid_freebsd.c
+++ b/contrib/libfido2/src/hid_freebsd.c
@@ -1,13 +1,17 @@
 /*
- * Copyright (c) 2020 Yubico AB. All rights reserved.
+ * Copyright (c) 2020-2022 Yubico AB. All rights reserved.
  * Use of this source code is governed by a BSD-style
  * license that can be found in the LICENSE file.
  */
 
-#include <sys/types.h>
+#include <sys/param.h>
 
 #include <dev/usb/usb_ioctl.h>
 #include <dev/usb/usbhid.h>
+#if __FreeBSD_version >= 1300500
+#include <dev/hid/hidraw.h>
+#define USE_HIDRAW /* see usbhid(4) and hidraw(4) on FreeBSD 13+ */
+#endif
 
 #include <errno.h>
 #include <unistd.h>
@@ -57,8 +61,60 @@ is_fido(int fd)
 	return (usage_page == 0xf1d0);
 }
 
+#ifdef USE_HIDRAW
+static int
+copy_info_hidraw(fido_dev_info_t *di, const char *path)
+{
+	int			fd = -1;
+	int			ok = -1;
+	struct usb_device_info	udi;
+	struct hidraw_devinfo	devinfo;
+	char			rawname[129];
+
+	memset(di, 0, sizeof(*di));
+	memset(&udi, 0, sizeof(udi));
+	memset(&devinfo, 0, sizeof(devinfo));
+	memset(rawname, 0, sizeof(rawname));
+
+	if ((fd = fido_hid_unix_open(path)) == -1 || is_fido(fd) == 0)
+		goto fail;
+
+	if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) {
+		if (ioctl(fd, IOCTL_REQ(HIDIOCGRAWINFO), &devinfo) == -1 ||
+		    ioctl(fd, IOCTL_REQ(HIDIOCGRAWNAME(128)), rawname) == -1 ||
+		    (di->path = strdup(path)) == NULL ||
+		    (di->manufacturer = strdup(UHID_VENDOR)) == NULL ||
+		    (di->product = strdup(rawname)) == NULL)
+			goto fail;
+		di->vendor_id = devinfo.vendor;
+		di->product_id = devinfo.product;
+	} else {
+		if ((di->path = strdup(path)) == NULL ||
+		    (di->manufacturer = strdup(udi.udi_vendor)) == NULL ||
+		    (di->product = strdup(udi.udi_product)) == NULL)
+			goto fail;
+		di->vendor_id = (int16_t)udi.udi_vendorNo;
+		di->product_id = (int16_t)udi.udi_productNo;
+	}
+
+	ok = 0;
+fail:
+	if (fd != -1 && close(fd) == -1)
+		fido_log_error(errno, "%s: close %s", __func__, path);
+
+	if (ok < 0) {
+		free(di->path);
+		free(di->manufacturer);
+		free(di->product);
+		explicit_bzero(di, sizeof(*di));
+	}
+
+	return (ok);
+}
+#endif /* USE_HIDRAW */
+
 static int
-copy_info(fido_dev_info_t *di, const char *path)
+copy_info_uhid(fido_dev_info_t *di, const char *path)
 {
 	int			fd = -1;
 	int			ok = -1;
@@ -81,14 +137,13 @@ copy_info(fido_dev_info_t *di, const char *path)
 	    (di->manufacturer = strdup(udi.udi_vendor)) == NULL ||
 	    (di->product = strdup(udi.udi_product)) == NULL)
 		goto fail;
-
 	di->vendor_id = (int16_t)udi.udi_vendorNo;
 	di->product_id = (int16_t)udi.udi_productNo;
 
 	ok = 0;
 fail:
-	if (fd != -1)
-		close(fd);
+	if (fd != -1 && close(fd) == -1)
+		fido_log_error(errno, "%s: close %s", __func__, path);
 
 	if (ok < 0) {
 		free(di->path);
@@ -106,17 +161,35 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
 	char	path[64];
 	size_t	i;
 
-	*olen = 0;
-
 	if (ilen == 0)
 		return (FIDO_OK); /* nothing to do */
 
 	if (devlist == NULL || olen == NULL)
 		return (FIDO_ERR_INVALID_ARGUMENT);
 
-	for (i = *olen = 0; i < MAX_UHID && *olen < ilen; i++) {
+	*olen = 0;
+
+#ifdef USE_HIDRAW
+	for (i = 0; i < MAX_UHID && *olen < ilen; i++) {
+		snprintf(path, sizeof(path), "/dev/hidraw%zu", i);
+		if (copy_info_hidraw(&devlist[*olen], path) == 0) {
+			devlist[*olen].io = (fido_dev_io_t) {
+				fido_hid_open,
+				fido_hid_close,
+				fido_hid_read,
+				fido_hid_write,
+			};
+			++(*olen);
+		}
+	}
+	/* hidraw(4) is preferred over uhid(4) */
+	if (*olen != 0)
+		return (FIDO_OK);
+#endif /* USE_HIDRAW */
+
+	for (i = 0; i < MAX_UHID && *olen < ilen; i++) {
 		snprintf(path, sizeof(path), "/dev/uhid%zu", i);
-		if (copy_info(&devlist[*olen], path) == 0) {
+		if (copy_info_uhid(&devlist[*olen], path) == 0) {
 			devlist[*olen].io = (fido_dev_io_t) {
 				fido_hid_open,
 				fido_hid_close,
@@ -153,6 +226,10 @@ fido_hid_open(const char *path)
 	ugd.ugd_data = buf;
 	ugd.ugd_maxlen = sizeof(buf);
 
+	/*
+	 * N.B. if ctx->fd is an hidraw(4) device, the ioctl() below puts it in
+	 * uhid(4) compat mode, which we need to keep fido_hid_write() as-is.
+	 */
 	if ((r = ioctl(ctx->fd, IOCTL_REQ(USB_GET_REPORT_DESC), &ugd) == -1) ||
 	    ugd.ugd_actlen > sizeof(buf) ||
 	    fido_hid_get_report_len(ugd.ugd_data, ugd.ugd_actlen,