1 /* 2 * Copyright (c) 2020-2022 Yubico AB. 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/param.h> 9 10 #include <dev/usb/usb_ioctl.h> 11 #include <dev/usb/usbhid.h> 12 #if __FreeBSD_version >= 1300500 13 #include <dev/hid/hidraw.h> 14 #define USE_HIDRAW /* see usbhid(4) and hidraw(4) on FreeBSD 13+ */ 15 #endif 16 17 #include <errno.h> 18 #include <unistd.h> 19 20 #include "fido.h" 21 22 #if defined(__MidnightBSD__) 23 #define UHID_VENDOR "MidnightBSD" 24 #else 25 #define UHID_VENDOR "FreeBSD" 26 #endif 27 28 #define MAX_UHID 64 29 30 struct hid_freebsd { 31 int fd; 32 size_t report_in_len; 33 size_t report_out_len; 34 sigset_t sigmask; 35 const sigset_t *sigmaskp; 36 }; 37 38 static bool 39 is_fido(int fd) 40 { 41 char buf[64]; 42 struct usb_gen_descriptor ugd; 43 uint32_t usage_page = 0; 44 45 memset(&buf, 0, sizeof(buf)); 46 memset(&ugd, 0, sizeof(ugd)); 47 48 ugd.ugd_report_type = UHID_FEATURE_REPORT; 49 ugd.ugd_data = buf; 50 ugd.ugd_maxlen = sizeof(buf); 51 52 if (ioctl(fd, IOCTL_REQ(USB_GET_REPORT_DESC), &ugd) == -1) { 53 fido_log_error(errno, "%s: ioctl", __func__); 54 return (false); 55 } 56 if (ugd.ugd_actlen > sizeof(buf) || fido_hid_get_usage(ugd.ugd_data, 57 ugd.ugd_actlen, &usage_page) < 0) { 58 fido_log_debug("%s: fido_hid_get_usage", __func__); 59 return (false); 60 } 61 62 return (usage_page == 0xf1d0); 63 } 64 65 #ifdef USE_HIDRAW 66 static int 67 copy_info_hidraw(fido_dev_info_t *di, const char *path) 68 { 69 int fd = -1; 70 int ok = -1; 71 struct usb_device_info udi; 72 struct hidraw_devinfo devinfo; 73 char rawname[129]; 74 75 memset(di, 0, sizeof(*di)); 76 memset(&udi, 0, sizeof(udi)); 77 memset(&devinfo, 0, sizeof(devinfo)); 78 memset(rawname, 0, sizeof(rawname)); 79 80 if ((fd = fido_hid_unix_open(path)) == -1 || is_fido(fd) == 0) 81 goto fail; 82 83 if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) { 84 if (ioctl(fd, IOCTL_REQ(HIDIOCGRAWINFO), &devinfo) == -1 || 85 ioctl(fd, IOCTL_REQ(HIDIOCGRAWNAME(128)), rawname) == -1 || 86 (di->path = strdup(path)) == NULL || 87 (di->manufacturer = strdup(UHID_VENDOR)) == NULL || 88 (di->product = strdup(rawname)) == NULL) 89 goto fail; 90 di->vendor_id = devinfo.vendor; 91 di->product_id = devinfo.product; 92 } else { 93 if ((di->path = strdup(path)) == NULL || 94 (di->manufacturer = strdup(udi.udi_vendor)) == NULL || 95 (di->product = strdup(udi.udi_product)) == NULL) 96 goto fail; 97 di->vendor_id = (int16_t)udi.udi_vendorNo; 98 di->product_id = (int16_t)udi.udi_productNo; 99 } 100 101 ok = 0; 102 fail: 103 if (fd != -1 && close(fd) == -1) 104 fido_log_error(errno, "%s: close %s", __func__, path); 105 106 if (ok < 0) { 107 free(di->path); 108 free(di->manufacturer); 109 free(di->product); 110 explicit_bzero(di, sizeof(*di)); 111 } 112 113 return (ok); 114 } 115 #endif /* USE_HIDRAW */ 116 117 static int 118 copy_info_uhid(fido_dev_info_t *di, const char *path) 119 { 120 int fd = -1; 121 int ok = -1; 122 struct usb_device_info udi; 123 124 memset(di, 0, sizeof(*di)); 125 memset(&udi, 0, sizeof(udi)); 126 127 if ((fd = fido_hid_unix_open(path)) == -1 || is_fido(fd) == 0) 128 goto fail; 129 130 if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) { 131 fido_log_error(errno, "%s: ioctl", __func__); 132 strlcpy(udi.udi_vendor, UHID_VENDOR, sizeof(udi.udi_vendor)); 133 strlcpy(udi.udi_product, "uhid(4)", sizeof(udi.udi_product)); 134 udi.udi_vendorNo = 0x0b5d; /* stolen from PCI_VENDOR_OPENBSD */ 135 } 136 137 if ((di->path = strdup(path)) == NULL || 138 (di->manufacturer = strdup(udi.udi_vendor)) == NULL || 139 (di->product = strdup(udi.udi_product)) == NULL) 140 goto fail; 141 di->vendor_id = (int16_t)udi.udi_vendorNo; 142 di->product_id = (int16_t)udi.udi_productNo; 143 144 ok = 0; 145 fail: 146 if (fd != -1 && close(fd) == -1) 147 fido_log_error(errno, "%s: close %s", __func__, path); 148 149 if (ok < 0) { 150 free(di->path); 151 free(di->manufacturer); 152 free(di->product); 153 explicit_bzero(di, sizeof(*di)); 154 } 155 156 return (ok); 157 } 158 159 int 160 fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) 161 { 162 char path[64]; 163 size_t i; 164 165 if (ilen == 0) 166 return (FIDO_OK); /* nothing to do */ 167 168 if (devlist == NULL || olen == NULL) 169 return (FIDO_ERR_INVALID_ARGUMENT); 170 171 *olen = 0; 172 173 #ifdef USE_HIDRAW 174 for (i = 0; i < MAX_UHID && *olen < ilen; i++) { 175 snprintf(path, sizeof(path), "/dev/hidraw%zu", i); 176 if (copy_info_hidraw(&devlist[*olen], path) == 0) { 177 devlist[*olen].io = (fido_dev_io_t) { 178 fido_hid_open, 179 fido_hid_close, 180 fido_hid_read, 181 fido_hid_write, 182 }; 183 ++(*olen); 184 } 185 } 186 /* hidraw(4) is preferred over uhid(4) */ 187 if (*olen != 0) 188 return (FIDO_OK); 189 #endif /* USE_HIDRAW */ 190 191 for (i = 0; i < MAX_UHID && *olen < ilen; i++) { 192 snprintf(path, sizeof(path), "/dev/uhid%zu", i); 193 if (copy_info_uhid(&devlist[*olen], path) == 0) { 194 devlist[*olen].io = (fido_dev_io_t) { 195 fido_hid_open, 196 fido_hid_close, 197 fido_hid_read, 198 fido_hid_write, 199 }; 200 ++(*olen); 201 } 202 } 203 204 return (FIDO_OK); 205 } 206 207 void * 208 fido_hid_open(const char *path) 209 { 210 char buf[64]; 211 struct hid_freebsd *ctx; 212 struct usb_gen_descriptor ugd; 213 int r; 214 215 memset(&buf, 0, sizeof(buf)); 216 memset(&ugd, 0, sizeof(ugd)); 217 218 if ((ctx = calloc(1, sizeof(*ctx))) == NULL) 219 return (NULL); 220 221 if ((ctx->fd = fido_hid_unix_open(path)) == -1) { 222 free(ctx); 223 return (NULL); 224 } 225 226 ugd.ugd_report_type = UHID_FEATURE_REPORT; 227 ugd.ugd_data = buf; 228 ugd.ugd_maxlen = sizeof(buf); 229 230 /* 231 * N.B. if ctx->fd is an hidraw(4) device, the ioctl() below puts it in 232 * uhid(4) compat mode, which we need to keep fido_hid_write() as-is. 233 */ 234 if ((r = ioctl(ctx->fd, IOCTL_REQ(USB_GET_REPORT_DESC), &ugd) == -1) || 235 ugd.ugd_actlen > sizeof(buf) || 236 fido_hid_get_report_len(ugd.ugd_data, ugd.ugd_actlen, 237 &ctx->report_in_len, &ctx->report_out_len) < 0) { 238 if (r == -1) 239 fido_log_error(errno, "%s: ioctl", __func__); 240 fido_log_debug("%s: using default report sizes", __func__); 241 ctx->report_in_len = CTAP_MAX_REPORT_LEN; 242 ctx->report_out_len = CTAP_MAX_REPORT_LEN; 243 } 244 245 return (ctx); 246 } 247 248 void 249 fido_hid_close(void *handle) 250 { 251 struct hid_freebsd *ctx = handle; 252 253 if (close(ctx->fd) == -1) 254 fido_log_error(errno, "%s: close", __func__); 255 256 free(ctx); 257 } 258 259 int 260 fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask) 261 { 262 struct hid_freebsd *ctx = handle; 263 264 ctx->sigmask = *sigmask; 265 ctx->sigmaskp = &ctx->sigmask; 266 267 return (FIDO_OK); 268 } 269 270 int 271 fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) 272 { 273 struct hid_freebsd *ctx = handle; 274 ssize_t r; 275 276 if (len != ctx->report_in_len) { 277 fido_log_debug("%s: len %zu", __func__, len); 278 return (-1); 279 } 280 281 if (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) { 282 fido_log_debug("%s: fd not ready", __func__); 283 return (-1); 284 } 285 286 if ((r = read(ctx->fd, buf, len)) == -1) { 287 fido_log_error(errno, "%s: read", __func__); 288 return (-1); 289 } 290 291 if (r < 0 || (size_t)r != len) { 292 fido_log_debug("%s: %zd != %zu", __func__, r, len); 293 return (-1); 294 } 295 296 return ((int)r); 297 } 298 299 int 300 fido_hid_write(void *handle, const unsigned char *buf, size_t len) 301 { 302 struct hid_freebsd *ctx = handle; 303 ssize_t r; 304 305 if (len != ctx->report_out_len + 1) { 306 fido_log_debug("%s: len %zu", __func__, len); 307 return (-1); 308 } 309 310 if ((r = write(ctx->fd, buf + 1, len - 1)) == -1) { 311 fido_log_error(errno, "%s: write", __func__); 312 return (-1); 313 } 314 315 if (r < 0 || (size_t)r != len - 1) { 316 fido_log_debug("%s: %zd != %zu", __func__, r, len - 1); 317 return (-1); 318 } 319 320 return ((int)len); 321 } 322 323 size_t 324 fido_hid_report_in_len(void *handle) 325 { 326 struct hid_freebsd *ctx = handle; 327 328 return (ctx->report_in_len); 329 } 330 331 size_t 332 fido_hid_report_out_len(void *handle) 333 { 334 struct hid_freebsd *ctx = handle; 335 336 return (ctx->report_out_len); 337 } 338