Path: blob/main/contrib/libfido2/src/hid_openbsd.c
39483 views
/*1* Copyright (c) 2019 Google LLC. All rights reserved.2* Use of this source code is governed by a BSD-style3* license that can be found in the LICENSE file.4* SPDX-License-Identifier: BSD-2-Clause5*/67#include <sys/types.h>89#include <sys/ioctl.h>10#include <dev/usb/usb.h>1112#include <errno.h>13#include <fcntl.h>14#include <poll.h>15#include <signal.h>16#include <unistd.h>1718#include "fido.h"1920#define MAX_UHID 642122struct hid_openbsd {23int fd;24size_t report_in_len;25size_t report_out_len;26sigset_t sigmask;27const sigset_t *sigmaskp;28};2930static int31copy_info(fido_dev_info_t *di, const char *path)32{33int fd = -1, ok = -1;34struct usb_device_info udi;3536memset(di, 0, sizeof(*di));37memset(&udi, 0, sizeof(udi));3839if ((fd = fido_hid_unix_open(path)) == -1)40goto fail;41if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) {42fido_log_error(errno, "%s: ioctl %s", __func__, path);43goto fail;44}4546fido_log_debug("%s: %s: bus = 0x%02x, addr = 0x%02x", __func__, path,47udi.udi_bus, udi.udi_addr);48fido_log_debug("%s: %s: vendor = \"%s\", product = \"%s\"", __func__,49path, udi.udi_vendor, udi.udi_product);50fido_log_debug("%s: %s: productNo = 0x%04x, vendorNo = 0x%04x, "51"releaseNo = 0x%04x", __func__, path, udi.udi_productNo,52udi.udi_vendorNo, udi.udi_releaseNo);5354if ((di->path = strdup(path)) == NULL ||55(di->manufacturer = strdup(udi.udi_vendor)) == NULL ||56(di->product = strdup(udi.udi_product)) == NULL)57goto fail;5859di->vendor_id = (int16_t)udi.udi_vendorNo;60di->product_id = (int16_t)udi.udi_productNo;6162ok = 0;63fail:64if (fd != -1 && close(fd) == -1)65fido_log_error(errno, "%s: close %s", __func__, path);6667if (ok < 0) {68free(di->path);69free(di->manufacturer);70free(di->product);71explicit_bzero(di, sizeof(*di));72}7374return (ok);75}7677int78fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)79{80size_t i;81char path[64];8283if (ilen == 0)84return (FIDO_OK); /* nothing to do */8586if (devlist == NULL || olen == NULL)87return (FIDO_ERR_INVALID_ARGUMENT);8889for (i = *olen = 0; i < MAX_UHID && *olen < ilen; i++) {90snprintf(path, sizeof(path), "/dev/fido/%zu", i);91if (copy_info(&devlist[*olen], path) == 0) {92devlist[*olen].io = (fido_dev_io_t) {93fido_hid_open,94fido_hid_close,95fido_hid_read,96fido_hid_write,97};98++(*olen);99}100}101102return (FIDO_OK);103}104105/*106* Workaround for OpenBSD <=6.6-current (as of 201910) bug that loses107* sync of DATA0/DATA1 sequence bit across uhid open/close.108* Send pings until we get a response - early pings with incorrect109* sequence bits will be ignored as duplicate packets by the device.110*/111static int112terrible_ping_kludge(struct hid_openbsd *ctx)113{114u_char data[256];115int i, n;116struct pollfd pfd;117118if (sizeof(data) < ctx->report_out_len + 1)119return -1;120for (i = 0; i < 4; i++) {121memset(data, 0, sizeof(data));122/* broadcast channel ID */123data[1] = 0xff;124data[2] = 0xff;125data[3] = 0xff;126data[4] = 0xff;127/* Ping command */128data[5] = 0x81;129/* One byte ping only, Vasili */130data[6] = 0;131data[7] = 1;132fido_log_debug("%s: send ping %d", __func__, i);133if (fido_hid_write(ctx, data, ctx->report_out_len + 1) == -1)134return -1;135fido_log_debug("%s: wait reply", __func__);136memset(&pfd, 0, sizeof(pfd));137pfd.fd = ctx->fd;138pfd.events = POLLIN;139if ((n = poll(&pfd, 1, 100)) == -1) {140fido_log_error(errno, "%s: poll", __func__);141return -1;142} else if (n == 0) {143fido_log_debug("%s: timed out", __func__);144continue;145}146if (fido_hid_read(ctx, data, ctx->report_out_len, 250) == -1)147return -1;148/*149* Ping isn't always supported on the broadcast channel,150* so we might get an error, but we don't care - we're151* synched now.152*/153fido_log_xxd(data, ctx->report_out_len, "%s: got reply",154__func__);155return 0;156}157fido_log_debug("%s: no response", __func__);158return -1;159}160161void *162fido_hid_open(const char *path)163{164struct hid_openbsd *ret = NULL;165166if ((ret = calloc(1, sizeof(*ret))) == NULL ||167(ret->fd = fido_hid_unix_open(path)) == -1) {168free(ret);169return (NULL);170}171ret->report_in_len = ret->report_out_len = CTAP_MAX_REPORT_LEN;172fido_log_debug("%s: inlen = %zu outlen = %zu", __func__,173ret->report_in_len, ret->report_out_len);174175/*176* OpenBSD (as of 201910) has a bug that causes it to lose177* track of the DATA0/DATA1 sequence toggle across uhid device178* open and close. This is a terrible hack to work around it.179*/180if (terrible_ping_kludge(ret) != 0) {181fido_hid_close(ret);182return NULL;183}184185return (ret);186}187188void189fido_hid_close(void *handle)190{191struct hid_openbsd *ctx = (struct hid_openbsd *)handle;192193if (close(ctx->fd) == -1)194fido_log_error(errno, "%s: close", __func__);195196free(ctx);197}198199int200fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)201{202struct hid_openbsd *ctx = handle;203204ctx->sigmask = *sigmask;205ctx->sigmaskp = &ctx->sigmask;206207return (FIDO_OK);208}209210int211fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)212{213struct hid_openbsd *ctx = (struct hid_openbsd *)handle;214ssize_t r;215216if (len != ctx->report_in_len) {217fido_log_debug("%s: invalid len: got %zu, want %zu", __func__,218len, ctx->report_in_len);219return (-1);220}221222if (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) {223fido_log_debug("%s: fd not ready", __func__);224return (-1);225}226227if ((r = read(ctx->fd, buf, len)) == -1) {228fido_log_error(errno, "%s: read", __func__);229return (-1);230}231232if (r < 0 || (size_t)r != len) {233fido_log_debug("%s: %zd != %zu", __func__, r, len);234return (-1);235}236237return ((int)len);238}239240int241fido_hid_write(void *handle, const unsigned char *buf, size_t len)242{243struct hid_openbsd *ctx = (struct hid_openbsd *)handle;244ssize_t r;245246if (len != ctx->report_out_len + 1) {247fido_log_debug("%s: invalid len: got %zu, want %zu", __func__,248len, ctx->report_out_len);249return (-1);250}251252if ((r = write(ctx->fd, buf + 1, len - 1)) == -1) {253fido_log_error(errno, "%s: write", __func__);254return (-1);255}256257if (r < 0 || (size_t)r != len - 1) {258fido_log_debug("%s: %zd != %zu", __func__, r, len - 1);259return (-1);260}261262return ((int)len);263}264265size_t266fido_hid_report_in_len(void *handle)267{268struct hid_openbsd *ctx = handle;269270return (ctx->report_in_len);271}272273size_t274fido_hid_report_out_len(void *handle)275{276struct hid_openbsd *ctx = handle;277278return (ctx->report_out_len);279}280281282