10afa8e06SEd Maste /* 20afa8e06SEd Maste * Copyright (c) 2019 Google LLC. 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 70afa8e06SEd Maste #include <sys/types.h> 80afa8e06SEd Maste 90afa8e06SEd Maste #include <sys/ioctl.h> 100afa8e06SEd Maste #include <dev/usb/usb.h> 110afa8e06SEd Maste 120afa8e06SEd Maste #include <errno.h> 130afa8e06SEd Maste #include <fcntl.h> 140afa8e06SEd Maste #include <poll.h> 150afa8e06SEd Maste #include <signal.h> 160afa8e06SEd Maste #include <unistd.h> 170afa8e06SEd Maste 180afa8e06SEd Maste #include "fido.h" 190afa8e06SEd Maste 200afa8e06SEd Maste #define MAX_UHID 64 210afa8e06SEd Maste 220afa8e06SEd Maste struct hid_openbsd { 230afa8e06SEd Maste int fd; 240afa8e06SEd Maste size_t report_in_len; 250afa8e06SEd Maste size_t report_out_len; 26*f540a430SEd Maste sigset_t sigmask; 27*f540a430SEd Maste const sigset_t *sigmaskp; 280afa8e06SEd Maste }; 290afa8e06SEd Maste 300afa8e06SEd Maste int 310afa8e06SEd Maste fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) 320afa8e06SEd Maste { 330afa8e06SEd Maste size_t i; 340afa8e06SEd Maste char path[64]; 350afa8e06SEd Maste int fd; 360afa8e06SEd Maste struct usb_device_info udi; 370afa8e06SEd Maste fido_dev_info_t *di; 380afa8e06SEd Maste 390afa8e06SEd Maste if (ilen == 0) 400afa8e06SEd Maste return (FIDO_OK); /* nothing to do */ 410afa8e06SEd Maste 420afa8e06SEd Maste if (devlist == NULL || olen == NULL) 430afa8e06SEd Maste return (FIDO_ERR_INVALID_ARGUMENT); 440afa8e06SEd Maste 450afa8e06SEd Maste for (i = *olen = 0; i < MAX_UHID && *olen < ilen; i++) { 460afa8e06SEd Maste snprintf(path, sizeof(path), "/dev/fido/%zu", i); 470afa8e06SEd Maste if ((fd = fido_hid_unix_open(path)) == -1) 480afa8e06SEd Maste continue; 490afa8e06SEd Maste memset(&udi, 0, sizeof(udi)); 500afa8e06SEd Maste if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) { 510afa8e06SEd Maste fido_log_error(errno, "%s: get device info %s", 520afa8e06SEd Maste __func__, path); 530afa8e06SEd Maste if (close(fd) == -1) 540afa8e06SEd Maste fido_log_error(errno, "%s: close", __func__); 550afa8e06SEd Maste continue; 560afa8e06SEd Maste } 570afa8e06SEd Maste if (close(fd) == -1) 580afa8e06SEd Maste fido_log_error(errno, "%s: close", __func__); 590afa8e06SEd Maste 600afa8e06SEd Maste fido_log_debug("%s: %s: bus = 0x%02x, addr = 0x%02x", 610afa8e06SEd Maste __func__, path, udi.udi_bus, udi.udi_addr); 620afa8e06SEd Maste fido_log_debug("%s: %s: vendor = \"%s\", product = \"%s\"", 630afa8e06SEd Maste __func__, path, udi.udi_vendor, udi.udi_product); 640afa8e06SEd Maste fido_log_debug("%s: %s: productNo = 0x%04x, vendorNo = 0x%04x, " 650afa8e06SEd Maste "releaseNo = 0x%04x", __func__, path, udi.udi_productNo, 660afa8e06SEd Maste udi.udi_vendorNo, udi.udi_releaseNo); 670afa8e06SEd Maste 680afa8e06SEd Maste di = &devlist[*olen]; 690afa8e06SEd Maste memset(di, 0, sizeof(*di)); 700afa8e06SEd Maste di->io = (fido_dev_io_t) { 710afa8e06SEd Maste fido_hid_open, 720afa8e06SEd Maste fido_hid_close, 730afa8e06SEd Maste fido_hid_read, 740afa8e06SEd Maste fido_hid_write, 750afa8e06SEd Maste }; 760afa8e06SEd Maste if ((di->path = strdup(path)) == NULL || 770afa8e06SEd Maste (di->manufacturer = strdup(udi.udi_vendor)) == NULL || 780afa8e06SEd Maste (di->product = strdup(udi.udi_product)) == NULL) { 790afa8e06SEd Maste free(di->path); 800afa8e06SEd Maste free(di->manufacturer); 810afa8e06SEd Maste free(di->product); 820afa8e06SEd Maste explicit_bzero(di, sizeof(*di)); 830afa8e06SEd Maste return FIDO_ERR_INTERNAL; 840afa8e06SEd Maste } 850afa8e06SEd Maste di->vendor_id = (int16_t)udi.udi_vendorNo; 860afa8e06SEd Maste di->product_id = (int16_t)udi.udi_productNo; 870afa8e06SEd Maste (*olen)++; 880afa8e06SEd Maste } 890afa8e06SEd Maste 900afa8e06SEd Maste return FIDO_OK; 910afa8e06SEd Maste } 920afa8e06SEd Maste 930afa8e06SEd Maste /* 940afa8e06SEd Maste * Workaround for OpenBSD <=6.6-current (as of 201910) bug that loses 950afa8e06SEd Maste * sync of DATA0/DATA1 sequence bit across uhid open/close. 960afa8e06SEd Maste * Send pings until we get a response - early pings with incorrect 970afa8e06SEd Maste * sequence bits will be ignored as duplicate packets by the device. 980afa8e06SEd Maste */ 990afa8e06SEd Maste static int 1000afa8e06SEd Maste terrible_ping_kludge(struct hid_openbsd *ctx) 1010afa8e06SEd Maste { 1020afa8e06SEd Maste u_char data[256]; 1030afa8e06SEd Maste int i, n; 1040afa8e06SEd Maste struct pollfd pfd; 1050afa8e06SEd Maste 1060afa8e06SEd Maste if (sizeof(data) < ctx->report_out_len + 1) 1070afa8e06SEd Maste return -1; 1080afa8e06SEd Maste for (i = 0; i < 4; i++) { 1090afa8e06SEd Maste memset(data, 0, sizeof(data)); 1100afa8e06SEd Maste /* broadcast channel ID */ 1110afa8e06SEd Maste data[1] = 0xff; 1120afa8e06SEd Maste data[2] = 0xff; 1130afa8e06SEd Maste data[3] = 0xff; 1140afa8e06SEd Maste data[4] = 0xff; 1150afa8e06SEd Maste /* Ping command */ 1160afa8e06SEd Maste data[5] = 0x81; 1170afa8e06SEd Maste /* One byte ping only, Vasili */ 1180afa8e06SEd Maste data[6] = 0; 1190afa8e06SEd Maste data[7] = 1; 1200afa8e06SEd Maste fido_log_debug("%s: send ping %d", __func__, i); 1210afa8e06SEd Maste if (fido_hid_write(ctx, data, ctx->report_out_len + 1) == -1) 1220afa8e06SEd Maste return -1; 1230afa8e06SEd Maste fido_log_debug("%s: wait reply", __func__); 1240afa8e06SEd Maste memset(&pfd, 0, sizeof(pfd)); 1250afa8e06SEd Maste pfd.fd = ctx->fd; 1260afa8e06SEd Maste pfd.events = POLLIN; 1270afa8e06SEd Maste if ((n = poll(&pfd, 1, 100)) == -1) { 1280afa8e06SEd Maste fido_log_error(errno, "%s: poll", __func__); 1290afa8e06SEd Maste return -1; 1300afa8e06SEd Maste } else if (n == 0) { 1310afa8e06SEd Maste fido_log_debug("%s: timed out", __func__); 1320afa8e06SEd Maste continue; 1330afa8e06SEd Maste } 1340afa8e06SEd Maste if (fido_hid_read(ctx, data, ctx->report_out_len, 250) == -1) 1350afa8e06SEd Maste return -1; 1360afa8e06SEd Maste /* 1370afa8e06SEd Maste * Ping isn't always supported on the broadcast channel, 1380afa8e06SEd Maste * so we might get an error, but we don't care - we're 1390afa8e06SEd Maste * synched now. 1400afa8e06SEd Maste */ 1410afa8e06SEd Maste fido_log_xxd(data, ctx->report_out_len, "%s: got reply", 1420afa8e06SEd Maste __func__); 1430afa8e06SEd Maste return 0; 1440afa8e06SEd Maste } 1450afa8e06SEd Maste fido_log_debug("%s: no response", __func__); 1460afa8e06SEd Maste return -1; 1470afa8e06SEd Maste } 1480afa8e06SEd Maste 1490afa8e06SEd Maste void * 1500afa8e06SEd Maste fido_hid_open(const char *path) 1510afa8e06SEd Maste { 1520afa8e06SEd Maste struct hid_openbsd *ret = NULL; 1530afa8e06SEd Maste 1540afa8e06SEd Maste if ((ret = calloc(1, sizeof(*ret))) == NULL || 1550afa8e06SEd Maste (ret->fd = fido_hid_unix_open(path)) == -1) { 1560afa8e06SEd Maste free(ret); 1570afa8e06SEd Maste return (NULL); 1580afa8e06SEd Maste } 1590afa8e06SEd Maste ret->report_in_len = ret->report_out_len = CTAP_MAX_REPORT_LEN; 1600afa8e06SEd Maste fido_log_debug("%s: inlen = %zu outlen = %zu", __func__, 1610afa8e06SEd Maste ret->report_in_len, ret->report_out_len); 1620afa8e06SEd Maste 1630afa8e06SEd Maste /* 1640afa8e06SEd Maste * OpenBSD (as of 201910) has a bug that causes it to lose 1650afa8e06SEd Maste * track of the DATA0/DATA1 sequence toggle across uhid device 1660afa8e06SEd Maste * open and close. This is a terrible hack to work around it. 1670afa8e06SEd Maste */ 1680afa8e06SEd Maste if (terrible_ping_kludge(ret) != 0) { 1690afa8e06SEd Maste fido_hid_close(ret); 1700afa8e06SEd Maste return NULL; 1710afa8e06SEd Maste } 1720afa8e06SEd Maste 1730afa8e06SEd Maste return (ret); 1740afa8e06SEd Maste } 1750afa8e06SEd Maste 1760afa8e06SEd Maste void 1770afa8e06SEd Maste fido_hid_close(void *handle) 1780afa8e06SEd Maste { 1790afa8e06SEd Maste struct hid_openbsd *ctx = (struct hid_openbsd *)handle; 1800afa8e06SEd Maste 1810afa8e06SEd Maste if (close(ctx->fd) == -1) 1820afa8e06SEd Maste fido_log_error(errno, "%s: close", __func__); 1830afa8e06SEd Maste 1840afa8e06SEd Maste free(ctx); 1850afa8e06SEd Maste } 1860afa8e06SEd Maste 1870afa8e06SEd Maste int 1880afa8e06SEd Maste fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask) 1890afa8e06SEd Maste { 190*f540a430SEd Maste struct hid_openbsd *ctx = handle; 1910afa8e06SEd Maste 192*f540a430SEd Maste ctx->sigmask = *sigmask; 193*f540a430SEd Maste ctx->sigmaskp = &ctx->sigmask; 194*f540a430SEd Maste 195*f540a430SEd Maste return (FIDO_OK); 1960afa8e06SEd Maste } 1970afa8e06SEd Maste 1980afa8e06SEd Maste int 1990afa8e06SEd Maste fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) 2000afa8e06SEd Maste { 2010afa8e06SEd Maste struct hid_openbsd *ctx = (struct hid_openbsd *)handle; 2020afa8e06SEd Maste ssize_t r; 2030afa8e06SEd Maste 2040afa8e06SEd Maste if (len != ctx->report_in_len) { 2050afa8e06SEd Maste fido_log_debug("%s: invalid len: got %zu, want %zu", __func__, 2060afa8e06SEd Maste len, ctx->report_in_len); 2070afa8e06SEd Maste return (-1); 2080afa8e06SEd Maste } 2090afa8e06SEd Maste 210*f540a430SEd Maste if (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) { 211*f540a430SEd Maste fido_log_debug("%s: fd not ready", __func__); 212*f540a430SEd Maste return (-1); 213*f540a430SEd Maste } 214*f540a430SEd Maste 2150afa8e06SEd Maste if ((r = read(ctx->fd, buf, len)) == -1) { 2160afa8e06SEd Maste fido_log_error(errno, "%s: read", __func__); 2170afa8e06SEd Maste return (-1); 2180afa8e06SEd Maste } 2190afa8e06SEd Maste 2200afa8e06SEd Maste if (r < 0 || (size_t)r != len) { 2210afa8e06SEd Maste fido_log_debug("%s: %zd != %zu", __func__, r, len); 2220afa8e06SEd Maste return (-1); 2230afa8e06SEd Maste } 2240afa8e06SEd Maste 2250afa8e06SEd Maste return ((int)len); 2260afa8e06SEd Maste } 2270afa8e06SEd Maste 2280afa8e06SEd Maste int 2290afa8e06SEd Maste fido_hid_write(void *handle, const unsigned char *buf, size_t len) 2300afa8e06SEd Maste { 2310afa8e06SEd Maste struct hid_openbsd *ctx = (struct hid_openbsd *)handle; 2320afa8e06SEd Maste ssize_t r; 2330afa8e06SEd Maste 2340afa8e06SEd Maste if (len != ctx->report_out_len + 1) { 2350afa8e06SEd Maste fido_log_debug("%s: invalid len: got %zu, want %zu", __func__, 2360afa8e06SEd Maste len, ctx->report_out_len); 2370afa8e06SEd Maste return (-1); 2380afa8e06SEd Maste } 2390afa8e06SEd Maste 2400afa8e06SEd Maste if ((r = write(ctx->fd, buf + 1, len - 1)) == -1) { 2410afa8e06SEd Maste fido_log_error(errno, "%s: write", __func__); 2420afa8e06SEd Maste return (-1); 2430afa8e06SEd Maste } 2440afa8e06SEd Maste 2450afa8e06SEd Maste if (r < 0 || (size_t)r != len - 1) { 2460afa8e06SEd Maste fido_log_debug("%s: %zd != %zu", __func__, r, len - 1); 2470afa8e06SEd Maste return (-1); 2480afa8e06SEd Maste } 2490afa8e06SEd Maste 2500afa8e06SEd Maste return ((int)len); 2510afa8e06SEd Maste } 2520afa8e06SEd Maste 2530afa8e06SEd Maste size_t 2540afa8e06SEd Maste fido_hid_report_in_len(void *handle) 2550afa8e06SEd Maste { 2560afa8e06SEd Maste struct hid_openbsd *ctx = handle; 2570afa8e06SEd Maste 2580afa8e06SEd Maste return (ctx->report_in_len); 2590afa8e06SEd Maste } 2600afa8e06SEd Maste 2610afa8e06SEd Maste size_t 2620afa8e06SEd Maste fido_hid_report_out_len(void *handle) 2630afa8e06SEd Maste { 2640afa8e06SEd Maste struct hid_openbsd *ctx = handle; 2650afa8e06SEd Maste 2660afa8e06SEd Maste return (ctx->report_out_len); 2670afa8e06SEd Maste } 268