1 /* 2 * Copyright (c) 2020 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 */ 6 7 #include <sys/types.h> 8 9 #include <dev/usb/usb_ioctl.h> 10 #include <dev/usb/usbhid.h> 11 12 #include <errno.h> 13 #include <unistd.h> 14 15 #include "fido.h" 16 17 #define MAX_UHID 64 18 19 struct hid_freebsd { 20 int fd; 21 size_t report_in_len; 22 size_t report_out_len; 23 sigset_t sigmask; 24 const sigset_t *sigmaskp; 25 }; 26 27 static bool 28 is_fido(int fd) 29 { 30 char buf[64]; 31 struct usb_gen_descriptor ugd; 32 uint32_t usage_page = 0; 33 34 memset(&buf, 0, sizeof(buf)); 35 memset(&ugd, 0, sizeof(ugd)); 36 37 ugd.ugd_report_type = UHID_FEATURE_REPORT; 38 ugd.ugd_data = buf; 39 ugd.ugd_maxlen = sizeof(buf); 40 41 if (ioctl(fd, IOCTL_REQ(USB_GET_REPORT_DESC), &ugd) == -1) { 42 fido_log_error(errno, "%s: ioctl", __func__); 43 return (false); 44 } 45 if (ugd.ugd_actlen > sizeof(buf) || fido_hid_get_usage(ugd.ugd_data, 46 ugd.ugd_actlen, &usage_page) < 0) { 47 fido_log_debug("%s: fido_hid_get_usage", __func__); 48 return (false); 49 } 50 51 return (usage_page == 0xf1d0); 52 } 53 54 static int 55 copy_info(fido_dev_info_t *di, const char *path) 56 { 57 int fd = -1; 58 int ok = -1; 59 struct usb_device_info udi; 60 61 memset(di, 0, sizeof(*di)); 62 memset(&udi, 0, sizeof(udi)); 63 64 if ((fd = fido_hid_unix_open(path)) == -1 || is_fido(fd) == 0) 65 goto fail; 66 67 if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) { 68 fido_log_error(errno, "%s: ioctl", __func__); 69 strlcpy(udi.udi_vendor, "FreeBSD", sizeof(udi.udi_vendor)); 70 strlcpy(udi.udi_product, "uhid(4)", sizeof(udi.udi_product)); 71 udi.udi_vendorNo = 0x0b5d; /* stolen from PCI_VENDOR_OPENBSD */ 72 } 73 74 if ((di->path = strdup(path)) == NULL || 75 (di->manufacturer = strdup(udi.udi_vendor)) == NULL || 76 (di->product = strdup(udi.udi_product)) == NULL) 77 goto fail; 78 79 di->vendor_id = (int16_t)udi.udi_vendorNo; 80 di->product_id = (int16_t)udi.udi_productNo; 81 82 ok = 0; 83 fail: 84 if (fd != -1) 85 close(fd); 86 87 if (ok < 0) { 88 free(di->path); 89 free(di->manufacturer); 90 free(di->product); 91 explicit_bzero(di, sizeof(*di)); 92 } 93 94 return (ok); 95 } 96 97 int 98 fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) 99 { 100 char path[64]; 101 size_t i; 102 103 *olen = 0; 104 105 if (ilen == 0) 106 return (FIDO_OK); /* nothing to do */ 107 108 if (devlist == NULL || olen == NULL) 109 return (FIDO_ERR_INVALID_ARGUMENT); 110 111 for (i = *olen = 0; i < MAX_UHID && *olen < ilen; i++) { 112 snprintf(path, sizeof(path), "/dev/uhid%zu", i); 113 if (copy_info(&devlist[*olen], path) == 0) { 114 devlist[*olen].io = (fido_dev_io_t) { 115 fido_hid_open, 116 fido_hid_close, 117 fido_hid_read, 118 fido_hid_write, 119 }; 120 ++(*olen); 121 } 122 } 123 124 return (FIDO_OK); 125 } 126 127 void * 128 fido_hid_open(const char *path) 129 { 130 char buf[64]; 131 struct hid_freebsd *ctx; 132 struct usb_gen_descriptor ugd; 133 int r; 134 135 memset(&buf, 0, sizeof(buf)); 136 memset(&ugd, 0, sizeof(ugd)); 137 138 if ((ctx = calloc(1, sizeof(*ctx))) == NULL) 139 return (NULL); 140 141 if ((ctx->fd = fido_hid_unix_open(path)) == -1) { 142 free(ctx); 143 return (NULL); 144 } 145 146 ugd.ugd_report_type = UHID_FEATURE_REPORT; 147 ugd.ugd_data = buf; 148 ugd.ugd_maxlen = sizeof(buf); 149 150 if ((r = ioctl(ctx->fd, IOCTL_REQ(USB_GET_REPORT_DESC), &ugd) == -1) || 151 ugd.ugd_actlen > sizeof(buf) || 152 fido_hid_get_report_len(ugd.ugd_data, ugd.ugd_actlen, 153 &ctx->report_in_len, &ctx->report_out_len) < 0) { 154 if (r == -1) 155 fido_log_error(errno, "%s: ioctl", __func__); 156 fido_log_debug("%s: using default report sizes", __func__); 157 ctx->report_in_len = CTAP_MAX_REPORT_LEN; 158 ctx->report_out_len = CTAP_MAX_REPORT_LEN; 159 } 160 161 return (ctx); 162 } 163 164 void 165 fido_hid_close(void *handle) 166 { 167 struct hid_freebsd *ctx = handle; 168 169 if (close(ctx->fd) == -1) 170 fido_log_error(errno, "%s: close", __func__); 171 172 free(ctx); 173 } 174 175 int 176 fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask) 177 { 178 struct hid_freebsd *ctx = handle; 179 180 ctx->sigmask = *sigmask; 181 ctx->sigmaskp = &ctx->sigmask; 182 183 return (FIDO_OK); 184 } 185 186 int 187 fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) 188 { 189 struct hid_freebsd *ctx = handle; 190 ssize_t r; 191 192 if (len != ctx->report_in_len) { 193 fido_log_debug("%s: len %zu", __func__, len); 194 return (-1); 195 } 196 197 if (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) { 198 fido_log_debug("%s: fd not ready", __func__); 199 return (-1); 200 } 201 202 if ((r = read(ctx->fd, buf, len)) == -1) { 203 fido_log_error(errno, "%s: read", __func__); 204 return (-1); 205 } 206 207 if (r < 0 || (size_t)r != len) { 208 fido_log_debug("%s: %zd != %zu", __func__, r, len); 209 return (-1); 210 } 211 212 return ((int)r); 213 } 214 215 int 216 fido_hid_write(void *handle, const unsigned char *buf, size_t len) 217 { 218 struct hid_freebsd *ctx = handle; 219 ssize_t r; 220 221 if (len != ctx->report_out_len + 1) { 222 fido_log_debug("%s: len %zu", __func__, len); 223 return (-1); 224 } 225 226 if ((r = write(ctx->fd, buf + 1, len - 1)) == -1) { 227 fido_log_error(errno, "%s: write", __func__); 228 return (-1); 229 } 230 231 if (r < 0 || (size_t)r != len - 1) { 232 fido_log_debug("%s: %zd != %zu", __func__, r, len - 1); 233 return (-1); 234 } 235 236 return ((int)len); 237 } 238 239 size_t 240 fido_hid_report_in_len(void *handle) 241 { 242 struct hid_freebsd *ctx = handle; 243 244 return (ctx->report_in_len); 245 } 246 247 size_t 248 fido_hid_report_out_len(void *handle) 249 { 250 struct hid_freebsd *ctx = handle; 251 252 return (ctx->report_out_len); 253 } 254