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