1*0afa8e06SEd Maste /* 2*0afa8e06SEd Maste * Copyright (c) 2019 Google LLC. All rights reserved. 3*0afa8e06SEd Maste * Use of this source code is governed by a BSD-style 4*0afa8e06SEd Maste * license that can be found in the LICENSE file. 5*0afa8e06SEd Maste */ 6*0afa8e06SEd Maste 7*0afa8e06SEd Maste #include <sys/types.h> 8*0afa8e06SEd Maste 9*0afa8e06SEd Maste #include <sys/ioctl.h> 10*0afa8e06SEd Maste #include <dev/usb/usb.h> 11*0afa8e06SEd Maste 12*0afa8e06SEd Maste #include <errno.h> 13*0afa8e06SEd Maste #include <fcntl.h> 14*0afa8e06SEd Maste #include <poll.h> 15*0afa8e06SEd Maste #include <signal.h> 16*0afa8e06SEd Maste #include <unistd.h> 17*0afa8e06SEd Maste 18*0afa8e06SEd Maste #include "fido.h" 19*0afa8e06SEd Maste 20*0afa8e06SEd Maste #define MAX_UHID 64 21*0afa8e06SEd Maste 22*0afa8e06SEd Maste struct hid_openbsd { 23*0afa8e06SEd Maste int fd; 24*0afa8e06SEd Maste size_t report_in_len; 25*0afa8e06SEd Maste size_t report_out_len; 26*0afa8e06SEd Maste }; 27*0afa8e06SEd Maste 28*0afa8e06SEd Maste int 29*0afa8e06SEd Maste fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) 30*0afa8e06SEd Maste { 31*0afa8e06SEd Maste size_t i; 32*0afa8e06SEd Maste char path[64]; 33*0afa8e06SEd Maste int fd; 34*0afa8e06SEd Maste struct usb_device_info udi; 35*0afa8e06SEd Maste fido_dev_info_t *di; 36*0afa8e06SEd Maste 37*0afa8e06SEd Maste if (ilen == 0) 38*0afa8e06SEd Maste return (FIDO_OK); /* nothing to do */ 39*0afa8e06SEd Maste 40*0afa8e06SEd Maste if (devlist == NULL || olen == NULL) 41*0afa8e06SEd Maste return (FIDO_ERR_INVALID_ARGUMENT); 42*0afa8e06SEd Maste 43*0afa8e06SEd Maste for (i = *olen = 0; i < MAX_UHID && *olen < ilen; i++) { 44*0afa8e06SEd Maste snprintf(path, sizeof(path), "/dev/fido/%zu", i); 45*0afa8e06SEd Maste if ((fd = fido_hid_unix_open(path)) == -1) 46*0afa8e06SEd Maste continue; 47*0afa8e06SEd Maste memset(&udi, 0, sizeof(udi)); 48*0afa8e06SEd Maste if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) { 49*0afa8e06SEd Maste fido_log_error(errno, "%s: get device info %s", 50*0afa8e06SEd Maste __func__, path); 51*0afa8e06SEd Maste if (close(fd) == -1) 52*0afa8e06SEd Maste fido_log_error(errno, "%s: close", __func__); 53*0afa8e06SEd Maste continue; 54*0afa8e06SEd Maste } 55*0afa8e06SEd Maste if (close(fd) == -1) 56*0afa8e06SEd Maste fido_log_error(errno, "%s: close", __func__); 57*0afa8e06SEd Maste 58*0afa8e06SEd Maste fido_log_debug("%s: %s: bus = 0x%02x, addr = 0x%02x", 59*0afa8e06SEd Maste __func__, path, udi.udi_bus, udi.udi_addr); 60*0afa8e06SEd Maste fido_log_debug("%s: %s: vendor = \"%s\", product = \"%s\"", 61*0afa8e06SEd Maste __func__, path, udi.udi_vendor, udi.udi_product); 62*0afa8e06SEd Maste fido_log_debug("%s: %s: productNo = 0x%04x, vendorNo = 0x%04x, " 63*0afa8e06SEd Maste "releaseNo = 0x%04x", __func__, path, udi.udi_productNo, 64*0afa8e06SEd Maste udi.udi_vendorNo, udi.udi_releaseNo); 65*0afa8e06SEd Maste 66*0afa8e06SEd Maste di = &devlist[*olen]; 67*0afa8e06SEd Maste memset(di, 0, sizeof(*di)); 68*0afa8e06SEd Maste di->io = (fido_dev_io_t) { 69*0afa8e06SEd Maste fido_hid_open, 70*0afa8e06SEd Maste fido_hid_close, 71*0afa8e06SEd Maste fido_hid_read, 72*0afa8e06SEd Maste fido_hid_write, 73*0afa8e06SEd Maste }; 74*0afa8e06SEd Maste if ((di->path = strdup(path)) == NULL || 75*0afa8e06SEd Maste (di->manufacturer = strdup(udi.udi_vendor)) == NULL || 76*0afa8e06SEd Maste (di->product = strdup(udi.udi_product)) == NULL) { 77*0afa8e06SEd Maste free(di->path); 78*0afa8e06SEd Maste free(di->manufacturer); 79*0afa8e06SEd Maste free(di->product); 80*0afa8e06SEd Maste explicit_bzero(di, sizeof(*di)); 81*0afa8e06SEd Maste return FIDO_ERR_INTERNAL; 82*0afa8e06SEd Maste } 83*0afa8e06SEd Maste di->vendor_id = (int16_t)udi.udi_vendorNo; 84*0afa8e06SEd Maste di->product_id = (int16_t)udi.udi_productNo; 85*0afa8e06SEd Maste (*olen)++; 86*0afa8e06SEd Maste } 87*0afa8e06SEd Maste 88*0afa8e06SEd Maste return FIDO_OK; 89*0afa8e06SEd Maste } 90*0afa8e06SEd Maste 91*0afa8e06SEd Maste /* 92*0afa8e06SEd Maste * Workaround for OpenBSD <=6.6-current (as of 201910) bug that loses 93*0afa8e06SEd Maste * sync of DATA0/DATA1 sequence bit across uhid open/close. 94*0afa8e06SEd Maste * Send pings until we get a response - early pings with incorrect 95*0afa8e06SEd Maste * sequence bits will be ignored as duplicate packets by the device. 96*0afa8e06SEd Maste */ 97*0afa8e06SEd Maste static int 98*0afa8e06SEd Maste terrible_ping_kludge(struct hid_openbsd *ctx) 99*0afa8e06SEd Maste { 100*0afa8e06SEd Maste u_char data[256]; 101*0afa8e06SEd Maste int i, n; 102*0afa8e06SEd Maste struct pollfd pfd; 103*0afa8e06SEd Maste 104*0afa8e06SEd Maste if (sizeof(data) < ctx->report_out_len + 1) 105*0afa8e06SEd Maste return -1; 106*0afa8e06SEd Maste for (i = 0; i < 4; i++) { 107*0afa8e06SEd Maste memset(data, 0, sizeof(data)); 108*0afa8e06SEd Maste /* broadcast channel ID */ 109*0afa8e06SEd Maste data[1] = 0xff; 110*0afa8e06SEd Maste data[2] = 0xff; 111*0afa8e06SEd Maste data[3] = 0xff; 112*0afa8e06SEd Maste data[4] = 0xff; 113*0afa8e06SEd Maste /* Ping command */ 114*0afa8e06SEd Maste data[5] = 0x81; 115*0afa8e06SEd Maste /* One byte ping only, Vasili */ 116*0afa8e06SEd Maste data[6] = 0; 117*0afa8e06SEd Maste data[7] = 1; 118*0afa8e06SEd Maste fido_log_debug("%s: send ping %d", __func__, i); 119*0afa8e06SEd Maste if (fido_hid_write(ctx, data, ctx->report_out_len + 1) == -1) 120*0afa8e06SEd Maste return -1; 121*0afa8e06SEd Maste fido_log_debug("%s: wait reply", __func__); 122*0afa8e06SEd Maste memset(&pfd, 0, sizeof(pfd)); 123*0afa8e06SEd Maste pfd.fd = ctx->fd; 124*0afa8e06SEd Maste pfd.events = POLLIN; 125*0afa8e06SEd Maste if ((n = poll(&pfd, 1, 100)) == -1) { 126*0afa8e06SEd Maste fido_log_error(errno, "%s: poll", __func__); 127*0afa8e06SEd Maste return -1; 128*0afa8e06SEd Maste } else if (n == 0) { 129*0afa8e06SEd Maste fido_log_debug("%s: timed out", __func__); 130*0afa8e06SEd Maste continue; 131*0afa8e06SEd Maste } 132*0afa8e06SEd Maste if (fido_hid_read(ctx, data, ctx->report_out_len, 250) == -1) 133*0afa8e06SEd Maste return -1; 134*0afa8e06SEd Maste /* 135*0afa8e06SEd Maste * Ping isn't always supported on the broadcast channel, 136*0afa8e06SEd Maste * so we might get an error, but we don't care - we're 137*0afa8e06SEd Maste * synched now. 138*0afa8e06SEd Maste */ 139*0afa8e06SEd Maste fido_log_xxd(data, ctx->report_out_len, "%s: got reply", 140*0afa8e06SEd Maste __func__); 141*0afa8e06SEd Maste return 0; 142*0afa8e06SEd Maste } 143*0afa8e06SEd Maste fido_log_debug("%s: no response", __func__); 144*0afa8e06SEd Maste return -1; 145*0afa8e06SEd Maste } 146*0afa8e06SEd Maste 147*0afa8e06SEd Maste void * 148*0afa8e06SEd Maste fido_hid_open(const char *path) 149*0afa8e06SEd Maste { 150*0afa8e06SEd Maste struct hid_openbsd *ret = NULL; 151*0afa8e06SEd Maste 152*0afa8e06SEd Maste if ((ret = calloc(1, sizeof(*ret))) == NULL || 153*0afa8e06SEd Maste (ret->fd = fido_hid_unix_open(path)) == -1) { 154*0afa8e06SEd Maste free(ret); 155*0afa8e06SEd Maste return (NULL); 156*0afa8e06SEd Maste } 157*0afa8e06SEd Maste ret->report_in_len = ret->report_out_len = CTAP_MAX_REPORT_LEN; 158*0afa8e06SEd Maste fido_log_debug("%s: inlen = %zu outlen = %zu", __func__, 159*0afa8e06SEd Maste ret->report_in_len, ret->report_out_len); 160*0afa8e06SEd Maste 161*0afa8e06SEd Maste /* 162*0afa8e06SEd Maste * OpenBSD (as of 201910) has a bug that causes it to lose 163*0afa8e06SEd Maste * track of the DATA0/DATA1 sequence toggle across uhid device 164*0afa8e06SEd Maste * open and close. This is a terrible hack to work around it. 165*0afa8e06SEd Maste */ 166*0afa8e06SEd Maste if (terrible_ping_kludge(ret) != 0) { 167*0afa8e06SEd Maste fido_hid_close(ret); 168*0afa8e06SEd Maste return NULL; 169*0afa8e06SEd Maste } 170*0afa8e06SEd Maste 171*0afa8e06SEd Maste return (ret); 172*0afa8e06SEd Maste } 173*0afa8e06SEd Maste 174*0afa8e06SEd Maste void 175*0afa8e06SEd Maste fido_hid_close(void *handle) 176*0afa8e06SEd Maste { 177*0afa8e06SEd Maste struct hid_openbsd *ctx = (struct hid_openbsd *)handle; 178*0afa8e06SEd Maste 179*0afa8e06SEd Maste if (close(ctx->fd) == -1) 180*0afa8e06SEd Maste fido_log_error(errno, "%s: close", __func__); 181*0afa8e06SEd Maste 182*0afa8e06SEd Maste free(ctx); 183*0afa8e06SEd Maste } 184*0afa8e06SEd Maste 185*0afa8e06SEd Maste int 186*0afa8e06SEd Maste fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask) 187*0afa8e06SEd Maste { 188*0afa8e06SEd Maste (void)handle; 189*0afa8e06SEd Maste (void)sigmask; 190*0afa8e06SEd Maste 191*0afa8e06SEd Maste return (FIDO_ERR_INTERNAL); 192*0afa8e06SEd Maste } 193*0afa8e06SEd Maste 194*0afa8e06SEd Maste int 195*0afa8e06SEd Maste fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) 196*0afa8e06SEd Maste { 197*0afa8e06SEd Maste struct hid_openbsd *ctx = (struct hid_openbsd *)handle; 198*0afa8e06SEd Maste ssize_t r; 199*0afa8e06SEd Maste 200*0afa8e06SEd Maste (void)ms; /* XXX */ 201*0afa8e06SEd Maste 202*0afa8e06SEd Maste if (len != ctx->report_in_len) { 203*0afa8e06SEd Maste fido_log_debug("%s: invalid len: got %zu, want %zu", __func__, 204*0afa8e06SEd Maste len, ctx->report_in_len); 205*0afa8e06SEd Maste return (-1); 206*0afa8e06SEd Maste } 207*0afa8e06SEd Maste 208*0afa8e06SEd Maste if ((r = read(ctx->fd, buf, len)) == -1) { 209*0afa8e06SEd Maste fido_log_error(errno, "%s: read", __func__); 210*0afa8e06SEd Maste return (-1); 211*0afa8e06SEd Maste } 212*0afa8e06SEd Maste 213*0afa8e06SEd Maste if (r < 0 || (size_t)r != len) { 214*0afa8e06SEd Maste fido_log_debug("%s: %zd != %zu", __func__, r, len); 215*0afa8e06SEd Maste return (-1); 216*0afa8e06SEd Maste } 217*0afa8e06SEd Maste 218*0afa8e06SEd Maste return ((int)len); 219*0afa8e06SEd Maste } 220*0afa8e06SEd Maste 221*0afa8e06SEd Maste int 222*0afa8e06SEd Maste fido_hid_write(void *handle, const unsigned char *buf, size_t len) 223*0afa8e06SEd Maste { 224*0afa8e06SEd Maste struct hid_openbsd *ctx = (struct hid_openbsd *)handle; 225*0afa8e06SEd Maste ssize_t r; 226*0afa8e06SEd Maste 227*0afa8e06SEd Maste if (len != ctx->report_out_len + 1) { 228*0afa8e06SEd Maste fido_log_debug("%s: invalid len: got %zu, want %zu", __func__, 229*0afa8e06SEd Maste len, ctx->report_out_len); 230*0afa8e06SEd Maste return (-1); 231*0afa8e06SEd Maste } 232*0afa8e06SEd Maste 233*0afa8e06SEd Maste if ((r = write(ctx->fd, buf + 1, len - 1)) == -1) { 234*0afa8e06SEd Maste fido_log_error(errno, "%s: write", __func__); 235*0afa8e06SEd Maste return (-1); 236*0afa8e06SEd Maste } 237*0afa8e06SEd Maste 238*0afa8e06SEd Maste if (r < 0 || (size_t)r != len - 1) { 239*0afa8e06SEd Maste fido_log_debug("%s: %zd != %zu", __func__, r, len - 1); 240*0afa8e06SEd Maste return (-1); 241*0afa8e06SEd Maste } 242*0afa8e06SEd Maste 243*0afa8e06SEd Maste return ((int)len); 244*0afa8e06SEd Maste } 245*0afa8e06SEd Maste 246*0afa8e06SEd Maste size_t 247*0afa8e06SEd Maste fido_hid_report_in_len(void *handle) 248*0afa8e06SEd Maste { 249*0afa8e06SEd Maste struct hid_openbsd *ctx = handle; 250*0afa8e06SEd Maste 251*0afa8e06SEd Maste return (ctx->report_in_len); 252*0afa8e06SEd Maste } 253*0afa8e06SEd Maste 254*0afa8e06SEd Maste size_t 255*0afa8e06SEd Maste fido_hid_report_out_len(void *handle) 256*0afa8e06SEd Maste { 257*0afa8e06SEd Maste struct hid_openbsd *ctx = handle; 258*0afa8e06SEd Maste 259*0afa8e06SEd Maste return (ctx->report_out_len); 260*0afa8e06SEd Maste } 261