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