xref: /freebsd/contrib/libfido2/src/hid_freebsd.c (revision 1843dfb05ed80149f5a412180af882e3cb8f451b)
10afa8e06SEd Maste /*
2*1843dfb0SEd Maste  * Copyright (c) 2020-2022 Yubico AB. All rights reserved.
30afa8e06SEd Maste  * Use of this source code is governed by a BSD-style
40afa8e06SEd Maste  * license that can be found in the LICENSE file.
50afa8e06SEd Maste  */
60afa8e06SEd Maste 
7*1843dfb0SEd Maste #include <sys/param.h>
80afa8e06SEd Maste 
90afa8e06SEd Maste #include <dev/usb/usb_ioctl.h>
100afa8e06SEd Maste #include <dev/usb/usbhid.h>
11*1843dfb0SEd Maste #if __FreeBSD_version >= 1300500
12*1843dfb0SEd Maste #include <dev/hid/hidraw.h>
13*1843dfb0SEd Maste #define USE_HIDRAW /* see usbhid(4) and hidraw(4) on FreeBSD 13+ */
14*1843dfb0SEd Maste #endif
150afa8e06SEd Maste 
160afa8e06SEd Maste #include <errno.h>
170afa8e06SEd Maste #include <unistd.h>
180afa8e06SEd Maste 
190afa8e06SEd Maste #include "fido.h"
200afa8e06SEd Maste 
21f540a430SEd Maste #if defined(__MidnightBSD__)
22f540a430SEd Maste #define UHID_VENDOR    "MidnightBSD"
23f540a430SEd Maste #else
24f540a430SEd Maste #define UHID_VENDOR    "FreeBSD"
25f540a430SEd Maste #endif
26f540a430SEd Maste 
270afa8e06SEd Maste #define MAX_UHID	64
280afa8e06SEd Maste 
290afa8e06SEd Maste struct hid_freebsd {
300afa8e06SEd Maste 	int             fd;
310afa8e06SEd Maste 	size_t          report_in_len;
320afa8e06SEd Maste 	size_t          report_out_len;
330afa8e06SEd Maste 	sigset_t        sigmask;
340afa8e06SEd Maste 	const sigset_t *sigmaskp;
350afa8e06SEd Maste };
360afa8e06SEd Maste 
370afa8e06SEd Maste static bool
380afa8e06SEd Maste is_fido(int fd)
390afa8e06SEd Maste {
400afa8e06SEd Maste 	char				buf[64];
410afa8e06SEd Maste 	struct usb_gen_descriptor	ugd;
420afa8e06SEd Maste 	uint32_t			usage_page = 0;
430afa8e06SEd Maste 
440afa8e06SEd Maste 	memset(&buf, 0, sizeof(buf));
450afa8e06SEd Maste 	memset(&ugd, 0, sizeof(ugd));
460afa8e06SEd Maste 
470afa8e06SEd Maste 	ugd.ugd_report_type = UHID_FEATURE_REPORT;
480afa8e06SEd Maste 	ugd.ugd_data = buf;
490afa8e06SEd Maste 	ugd.ugd_maxlen = sizeof(buf);
500afa8e06SEd Maste 
510afa8e06SEd Maste 	if (ioctl(fd, IOCTL_REQ(USB_GET_REPORT_DESC), &ugd) == -1) {
520afa8e06SEd Maste 		fido_log_error(errno, "%s: ioctl", __func__);
530afa8e06SEd Maste 		return (false);
540afa8e06SEd Maste 	}
550afa8e06SEd Maste 	if (ugd.ugd_actlen > sizeof(buf) || fido_hid_get_usage(ugd.ugd_data,
560afa8e06SEd Maste 	    ugd.ugd_actlen, &usage_page) < 0) {
570afa8e06SEd Maste 		fido_log_debug("%s: fido_hid_get_usage", __func__);
580afa8e06SEd Maste 		return (false);
590afa8e06SEd Maste 	}
600afa8e06SEd Maste 
610afa8e06SEd Maste 	return (usage_page == 0xf1d0);
620afa8e06SEd Maste }
630afa8e06SEd Maste 
64*1843dfb0SEd Maste #ifdef USE_HIDRAW
650afa8e06SEd Maste static int
66*1843dfb0SEd Maste copy_info_hidraw(fido_dev_info_t *di, const char *path)
67*1843dfb0SEd Maste {
68*1843dfb0SEd Maste 	int			fd = -1;
69*1843dfb0SEd Maste 	int			ok = -1;
70*1843dfb0SEd Maste 	struct usb_device_info	udi;
71*1843dfb0SEd Maste 	struct hidraw_devinfo	devinfo;
72*1843dfb0SEd Maste 	char			rawname[129];
73*1843dfb0SEd Maste 
74*1843dfb0SEd Maste 	memset(di, 0, sizeof(*di));
75*1843dfb0SEd Maste 	memset(&udi, 0, sizeof(udi));
76*1843dfb0SEd Maste 	memset(&devinfo, 0, sizeof(devinfo));
77*1843dfb0SEd Maste 	memset(rawname, 0, sizeof(rawname));
78*1843dfb0SEd Maste 
79*1843dfb0SEd Maste 	if ((fd = fido_hid_unix_open(path)) == -1 || is_fido(fd) == 0)
80*1843dfb0SEd Maste 		goto fail;
81*1843dfb0SEd Maste 
82*1843dfb0SEd Maste 	if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) {
83*1843dfb0SEd Maste 		if (ioctl(fd, IOCTL_REQ(HIDIOCGRAWINFO), &devinfo) == -1 ||
84*1843dfb0SEd Maste 		    ioctl(fd, IOCTL_REQ(HIDIOCGRAWNAME(128)), rawname) == -1 ||
85*1843dfb0SEd Maste 		    (di->path = strdup(path)) == NULL ||
86*1843dfb0SEd Maste 		    (di->manufacturer = strdup(UHID_VENDOR)) == NULL ||
87*1843dfb0SEd Maste 		    (di->product = strdup(rawname)) == NULL)
88*1843dfb0SEd Maste 			goto fail;
89*1843dfb0SEd Maste 		di->vendor_id = devinfo.vendor;
90*1843dfb0SEd Maste 		di->product_id = devinfo.product;
91*1843dfb0SEd Maste 	} else {
92*1843dfb0SEd Maste 		if ((di->path = strdup(path)) == NULL ||
93*1843dfb0SEd Maste 		    (di->manufacturer = strdup(udi.udi_vendor)) == NULL ||
94*1843dfb0SEd Maste 		    (di->product = strdup(udi.udi_product)) == NULL)
95*1843dfb0SEd Maste 			goto fail;
96*1843dfb0SEd Maste 		di->vendor_id = (int16_t)udi.udi_vendorNo;
97*1843dfb0SEd Maste 		di->product_id = (int16_t)udi.udi_productNo;
98*1843dfb0SEd Maste 	}
99*1843dfb0SEd Maste 
100*1843dfb0SEd Maste 	ok = 0;
101*1843dfb0SEd Maste fail:
102*1843dfb0SEd Maste 	if (fd != -1 && close(fd) == -1)
103*1843dfb0SEd Maste 		fido_log_error(errno, "%s: close %s", __func__, path);
104*1843dfb0SEd Maste 
105*1843dfb0SEd Maste 	if (ok < 0) {
106*1843dfb0SEd Maste 		free(di->path);
107*1843dfb0SEd Maste 		free(di->manufacturer);
108*1843dfb0SEd Maste 		free(di->product);
109*1843dfb0SEd Maste 		explicit_bzero(di, sizeof(*di));
110*1843dfb0SEd Maste 	}
111*1843dfb0SEd Maste 
112*1843dfb0SEd Maste 	return (ok);
113*1843dfb0SEd Maste }
114*1843dfb0SEd Maste #endif /* USE_HIDRAW */
115*1843dfb0SEd Maste 
116*1843dfb0SEd Maste static int
117*1843dfb0SEd Maste copy_info_uhid(fido_dev_info_t *di, const char *path)
1180afa8e06SEd Maste {
1190afa8e06SEd Maste 	int			fd = -1;
1200afa8e06SEd Maste 	int			ok = -1;
1210afa8e06SEd Maste 	struct usb_device_info	udi;
1220afa8e06SEd Maste 
1230afa8e06SEd Maste 	memset(di, 0, sizeof(*di));
1240afa8e06SEd Maste 	memset(&udi, 0, sizeof(udi));
1250afa8e06SEd Maste 
1260afa8e06SEd Maste 	if ((fd = fido_hid_unix_open(path)) == -1 || is_fido(fd) == 0)
1270afa8e06SEd Maste 		goto fail;
1280afa8e06SEd Maste 
1290afa8e06SEd Maste 	if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) {
1300afa8e06SEd Maste 		fido_log_error(errno, "%s: ioctl", __func__);
131f540a430SEd Maste 		strlcpy(udi.udi_vendor, UHID_VENDOR, sizeof(udi.udi_vendor));
1320afa8e06SEd Maste 		strlcpy(udi.udi_product, "uhid(4)", sizeof(udi.udi_product));
1330afa8e06SEd Maste 		udi.udi_vendorNo = 0x0b5d; /* stolen from PCI_VENDOR_OPENBSD */
1340afa8e06SEd Maste 	}
1350afa8e06SEd Maste 
1360afa8e06SEd Maste 	if ((di->path = strdup(path)) == NULL ||
1370afa8e06SEd Maste 	    (di->manufacturer = strdup(udi.udi_vendor)) == NULL ||
1380afa8e06SEd Maste 	    (di->product = strdup(udi.udi_product)) == NULL)
1390afa8e06SEd Maste 		goto fail;
1400afa8e06SEd Maste 	di->vendor_id = (int16_t)udi.udi_vendorNo;
1410afa8e06SEd Maste 	di->product_id = (int16_t)udi.udi_productNo;
1420afa8e06SEd Maste 
1430afa8e06SEd Maste 	ok = 0;
1440afa8e06SEd Maste fail:
145*1843dfb0SEd Maste 	if (fd != -1 && close(fd) == -1)
146*1843dfb0SEd Maste 		fido_log_error(errno, "%s: close %s", __func__, path);
1470afa8e06SEd Maste 
1480afa8e06SEd Maste 	if (ok < 0) {
1490afa8e06SEd Maste 		free(di->path);
1500afa8e06SEd Maste 		free(di->manufacturer);
1510afa8e06SEd Maste 		free(di->product);
1520afa8e06SEd Maste 		explicit_bzero(di, sizeof(*di));
1530afa8e06SEd Maste 	}
1540afa8e06SEd Maste 
1550afa8e06SEd Maste 	return (ok);
1560afa8e06SEd Maste }
1570afa8e06SEd Maste 
1580afa8e06SEd Maste int
1590afa8e06SEd Maste fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
1600afa8e06SEd Maste {
1610afa8e06SEd Maste 	char	path[64];
1620afa8e06SEd Maste 	size_t	i;
1630afa8e06SEd Maste 
1640afa8e06SEd Maste 	if (ilen == 0)
1650afa8e06SEd Maste 		return (FIDO_OK); /* nothing to do */
1660afa8e06SEd Maste 
1670afa8e06SEd Maste 	if (devlist == NULL || olen == NULL)
1680afa8e06SEd Maste 		return (FIDO_ERR_INVALID_ARGUMENT);
1690afa8e06SEd Maste 
170*1843dfb0SEd Maste 	*olen = 0;
171*1843dfb0SEd Maste 
172*1843dfb0SEd Maste #ifdef USE_HIDRAW
173*1843dfb0SEd Maste 	for (i = 0; i < MAX_UHID && *olen < ilen; i++) {
174*1843dfb0SEd Maste 		snprintf(path, sizeof(path), "/dev/hidraw%zu", i);
175*1843dfb0SEd Maste 		if (copy_info_hidraw(&devlist[*olen], path) == 0) {
176*1843dfb0SEd Maste 			devlist[*olen].io = (fido_dev_io_t) {
177*1843dfb0SEd Maste 				fido_hid_open,
178*1843dfb0SEd Maste 				fido_hid_close,
179*1843dfb0SEd Maste 				fido_hid_read,
180*1843dfb0SEd Maste 				fido_hid_write,
181*1843dfb0SEd Maste 			};
182*1843dfb0SEd Maste 			++(*olen);
183*1843dfb0SEd Maste 		}
184*1843dfb0SEd Maste 	}
185*1843dfb0SEd Maste 	/* hidraw(4) is preferred over uhid(4) */
186*1843dfb0SEd Maste 	if (*olen != 0)
187*1843dfb0SEd Maste 		return (FIDO_OK);
188*1843dfb0SEd Maste #endif /* USE_HIDRAW */
189*1843dfb0SEd Maste 
190*1843dfb0SEd Maste 	for (i = 0; i < MAX_UHID && *olen < ilen; i++) {
1910afa8e06SEd Maste 		snprintf(path, sizeof(path), "/dev/uhid%zu", i);
192*1843dfb0SEd Maste 		if (copy_info_uhid(&devlist[*olen], path) == 0) {
1930afa8e06SEd Maste 			devlist[*olen].io = (fido_dev_io_t) {
1940afa8e06SEd Maste 				fido_hid_open,
1950afa8e06SEd Maste 				fido_hid_close,
1960afa8e06SEd Maste 				fido_hid_read,
1970afa8e06SEd Maste 				fido_hid_write,
1980afa8e06SEd Maste 			};
1990afa8e06SEd Maste 			++(*olen);
2000afa8e06SEd Maste 		}
2010afa8e06SEd Maste 	}
2020afa8e06SEd Maste 
2030afa8e06SEd Maste 	return (FIDO_OK);
2040afa8e06SEd Maste }
2050afa8e06SEd Maste 
2060afa8e06SEd Maste void *
2070afa8e06SEd Maste fido_hid_open(const char *path)
2080afa8e06SEd Maste {
2090afa8e06SEd Maste 	char				 buf[64];
2100afa8e06SEd Maste 	struct hid_freebsd		*ctx;
2110afa8e06SEd Maste 	struct usb_gen_descriptor	 ugd;
2120afa8e06SEd Maste 	int				 r;
2130afa8e06SEd Maste 
2140afa8e06SEd Maste 	memset(&buf, 0, sizeof(buf));
2150afa8e06SEd Maste 	memset(&ugd, 0, sizeof(ugd));
2160afa8e06SEd Maste 
2170afa8e06SEd Maste 	if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
2180afa8e06SEd Maste 		return (NULL);
2190afa8e06SEd Maste 
2200afa8e06SEd Maste 	if ((ctx->fd = fido_hid_unix_open(path)) == -1) {
2210afa8e06SEd Maste 		free(ctx);
2220afa8e06SEd Maste 		return (NULL);
2230afa8e06SEd Maste 	}
2240afa8e06SEd Maste 
2250afa8e06SEd Maste 	ugd.ugd_report_type = UHID_FEATURE_REPORT;
2260afa8e06SEd Maste 	ugd.ugd_data = buf;
2270afa8e06SEd Maste 	ugd.ugd_maxlen = sizeof(buf);
2280afa8e06SEd Maste 
229*1843dfb0SEd Maste 	/*
230*1843dfb0SEd Maste 	 * N.B. if ctx->fd is an hidraw(4) device, the ioctl() below puts it in
231*1843dfb0SEd Maste 	 * uhid(4) compat mode, which we need to keep fido_hid_write() as-is.
232*1843dfb0SEd Maste 	 */
2330afa8e06SEd Maste 	if ((r = ioctl(ctx->fd, IOCTL_REQ(USB_GET_REPORT_DESC), &ugd) == -1) ||
2340afa8e06SEd Maste 	    ugd.ugd_actlen > sizeof(buf) ||
2350afa8e06SEd Maste 	    fido_hid_get_report_len(ugd.ugd_data, ugd.ugd_actlen,
2360afa8e06SEd Maste 	    &ctx->report_in_len, &ctx->report_out_len) < 0) {
2370afa8e06SEd Maste 		if (r == -1)
2380afa8e06SEd Maste 			fido_log_error(errno, "%s: ioctl", __func__);
2390afa8e06SEd Maste 		fido_log_debug("%s: using default report sizes", __func__);
2400afa8e06SEd Maste 		ctx->report_in_len = CTAP_MAX_REPORT_LEN;
2410afa8e06SEd Maste 		ctx->report_out_len = CTAP_MAX_REPORT_LEN;
2420afa8e06SEd Maste 	}
2430afa8e06SEd Maste 
2440afa8e06SEd Maste 	return (ctx);
2450afa8e06SEd Maste }
2460afa8e06SEd Maste 
2470afa8e06SEd Maste void
2480afa8e06SEd Maste fido_hid_close(void *handle)
2490afa8e06SEd Maste {
2500afa8e06SEd Maste 	struct hid_freebsd *ctx = handle;
2510afa8e06SEd Maste 
2520afa8e06SEd Maste 	if (close(ctx->fd) == -1)
2530afa8e06SEd Maste 		fido_log_error(errno, "%s: close", __func__);
2540afa8e06SEd Maste 
2550afa8e06SEd Maste 	free(ctx);
2560afa8e06SEd Maste }
2570afa8e06SEd Maste 
2580afa8e06SEd Maste int
2590afa8e06SEd Maste fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
2600afa8e06SEd Maste {
2610afa8e06SEd Maste 	struct hid_freebsd *ctx = handle;
2620afa8e06SEd Maste 
2630afa8e06SEd Maste 	ctx->sigmask = *sigmask;
2640afa8e06SEd Maste 	ctx->sigmaskp = &ctx->sigmask;
2650afa8e06SEd Maste 
2660afa8e06SEd Maste 	return (FIDO_OK);
2670afa8e06SEd Maste }
2680afa8e06SEd Maste 
2690afa8e06SEd Maste int
2700afa8e06SEd Maste fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
2710afa8e06SEd Maste {
2720afa8e06SEd Maste 	struct hid_freebsd	*ctx = handle;
2730afa8e06SEd Maste 	ssize_t			 r;
2740afa8e06SEd Maste 
2750afa8e06SEd Maste 	if (len != ctx->report_in_len) {
2760afa8e06SEd Maste 		fido_log_debug("%s: len %zu", __func__, len);
2770afa8e06SEd Maste 		return (-1);
2780afa8e06SEd Maste 	}
2790afa8e06SEd Maste 
2800afa8e06SEd Maste 	if (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) {
2810afa8e06SEd Maste 		fido_log_debug("%s: fd not ready", __func__);
2820afa8e06SEd Maste 		return (-1);
2830afa8e06SEd Maste 	}
2840afa8e06SEd Maste 
2850afa8e06SEd Maste 	if ((r = read(ctx->fd, buf, len)) == -1) {
2860afa8e06SEd Maste 		fido_log_error(errno, "%s: read", __func__);
2870afa8e06SEd Maste 		return (-1);
2880afa8e06SEd Maste 	}
2890afa8e06SEd Maste 
2900afa8e06SEd Maste 	if (r < 0 || (size_t)r != len) {
2910afa8e06SEd Maste 		fido_log_debug("%s: %zd != %zu", __func__, r, len);
2920afa8e06SEd Maste 		return (-1);
2930afa8e06SEd Maste 	}
2940afa8e06SEd Maste 
2950afa8e06SEd Maste 	return ((int)r);
2960afa8e06SEd Maste }
2970afa8e06SEd Maste 
2980afa8e06SEd Maste int
2990afa8e06SEd Maste fido_hid_write(void *handle, const unsigned char *buf, size_t len)
3000afa8e06SEd Maste {
3010afa8e06SEd Maste 	struct hid_freebsd	*ctx = handle;
3020afa8e06SEd Maste 	ssize_t			 r;
3030afa8e06SEd Maste 
3040afa8e06SEd Maste 	if (len != ctx->report_out_len + 1) {
3050afa8e06SEd Maste 		fido_log_debug("%s: len %zu", __func__, len);
3060afa8e06SEd Maste 		return (-1);
3070afa8e06SEd Maste 	}
3080afa8e06SEd Maste 
3090afa8e06SEd Maste 	if ((r = write(ctx->fd, buf + 1, len - 1)) == -1) {
3100afa8e06SEd Maste 		fido_log_error(errno, "%s: write", __func__);
3110afa8e06SEd Maste 		return (-1);
3120afa8e06SEd Maste 	}
3130afa8e06SEd Maste 
3140afa8e06SEd Maste 	if (r < 0 || (size_t)r != len - 1) {
3150afa8e06SEd Maste 		fido_log_debug("%s: %zd != %zu", __func__, r, len - 1);
3160afa8e06SEd Maste 		return (-1);
3170afa8e06SEd Maste 	}
3180afa8e06SEd Maste 
3190afa8e06SEd Maste 	return ((int)len);
3200afa8e06SEd Maste }
3210afa8e06SEd Maste 
3220afa8e06SEd Maste size_t
3230afa8e06SEd Maste fido_hid_report_in_len(void *handle)
3240afa8e06SEd Maste {
3250afa8e06SEd Maste 	struct hid_freebsd *ctx = handle;
3260afa8e06SEd Maste 
3270afa8e06SEd Maste 	return (ctx->report_in_len);
3280afa8e06SEd Maste }
3290afa8e06SEd Maste 
3300afa8e06SEd Maste size_t
3310afa8e06SEd Maste fido_hid_report_out_len(void *handle)
3320afa8e06SEd Maste {
3330afa8e06SEd Maste 	struct hid_freebsd *ctx = handle;
3340afa8e06SEd Maste 
3350afa8e06SEd Maste 	return (ctx->report_out_len);
3360afa8e06SEd Maste }
337