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 #ifdef __linux__ 9 #include <sys/ioctl.h> 10 #include <linux/hidraw.h> 11 #include <linux/input.h> 12 #include <fcntl.h> 13 #endif 14 15 #include <errno.h> 16 #include <hidapi.h> 17 #include <wchar.h> 18 19 #include "fido.h" 20 21 struct hid_hidapi { 22 void *handle; 23 size_t report_in_len; 24 size_t report_out_len; 25 }; 26 27 static size_t 28 fido_wcslen(const wchar_t *wcs) 29 { 30 size_t l = 0; 31 while (*wcs++ != L'\0') 32 l++; 33 return l; 34 } 35 36 static char * 37 wcs_to_cs(const wchar_t *wcs) 38 { 39 char *cs; 40 size_t i; 41 42 if (wcs == NULL || (cs = calloc(fido_wcslen(wcs) + 1, 1)) == NULL) 43 return NULL; 44 45 for (i = 0; i < fido_wcslen(wcs); i++) { 46 if (wcs[i] >= 128) { 47 /* give up on parsing non-ASCII text */ 48 free(cs); 49 return strdup("hidapi device"); 50 } 51 cs[i] = (char)wcs[i]; 52 } 53 54 return cs; 55 } 56 57 static int 58 copy_info(fido_dev_info_t *di, const struct hid_device_info *d) 59 { 60 memset(di, 0, sizeof(*di)); 61 62 if (d->path != NULL) 63 di->path = strdup(d->path); 64 else 65 di->path = strdup(""); 66 67 if (d->manufacturer_string != NULL) 68 di->manufacturer = wcs_to_cs(d->manufacturer_string); 69 else 70 di->manufacturer = strdup(""); 71 72 if (d->product_string != NULL) 73 di->product = wcs_to_cs(d->product_string); 74 else 75 di->product = strdup(""); 76 77 if (di->path == NULL || 78 di->manufacturer == NULL || 79 di->product == NULL) { 80 free(di->path); 81 free(di->manufacturer); 82 free(di->product); 83 explicit_bzero(di, sizeof(*di)); 84 return -1; 85 } 86 87 di->product_id = (int16_t)d->product_id; 88 di->vendor_id = (int16_t)d->vendor_id; 89 di->io = (fido_dev_io_t) { 90 &fido_hid_open, 91 &fido_hid_close, 92 &fido_hid_read, 93 &fido_hid_write, 94 }; 95 96 return 0; 97 } 98 99 #ifdef __linux__ 100 static int 101 get_report_descriptor(const char *path, struct hidraw_report_descriptor *hrd) 102 { 103 int fd; 104 int s = -1; 105 int ok = -1; 106 107 if ((fd = fido_hid_unix_open(path)) == -1) { 108 fido_log_debug("%s: fido_hid_unix_open", __func__); 109 return -1; 110 } 111 112 if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESCSIZE), &s) < 0 || s < 0 || 113 (unsigned)s > HID_MAX_DESCRIPTOR_SIZE) { 114 fido_log_error(errno, "%s: ioctl HIDIOCGRDESCSIZE", __func__); 115 goto fail; 116 } 117 118 hrd->size = (unsigned)s; 119 120 if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESC), hrd) < 0) { 121 fido_log_error(errno, "%s: ioctl HIDIOCGRDESC", __func__); 122 goto fail; 123 } 124 125 ok = 0; 126 fail: 127 if (fd != -1) 128 close(fd); 129 130 return ok; 131 } 132 133 static bool 134 is_fido(const struct hid_device_info *hdi) 135 { 136 uint32_t usage_page = 0; 137 struct hidraw_report_descriptor *hrd; 138 139 if ((hrd = calloc(1, sizeof(*hrd))) == NULL || 140 get_report_descriptor(hdi->path, hrd) < 0 || 141 fido_hid_get_usage(hrd->value, hrd->size, &usage_page) < 0) 142 usage_page = 0; 143 144 free(hrd); 145 146 return usage_page == 0xf1d0; 147 } 148 #elif defined(_WIN32) || defined(__APPLE__) 149 static bool 150 is_fido(const struct hid_device_info *hdi) 151 { 152 return hdi->usage_page == 0xf1d0; 153 } 154 #else 155 static bool 156 is_fido(const struct hid_device_info *hdi) 157 { 158 (void)hdi; 159 fido_log_debug("%s: assuming FIDO HID", __func__); 160 return true; 161 } 162 #endif 163 164 void * 165 fido_hid_open(const char *path) 166 { 167 struct hid_hidapi *ctx; 168 169 if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { 170 return (NULL); 171 } 172 173 if ((ctx->handle = hid_open_path(path)) == NULL) { 174 free(ctx); 175 return (NULL); 176 } 177 178 ctx->report_in_len = ctx->report_out_len = CTAP_MAX_REPORT_LEN; 179 180 return ctx; 181 } 182 183 void 184 fido_hid_close(void *handle) 185 { 186 struct hid_hidapi *ctx = handle; 187 188 hid_close(ctx->handle); 189 free(ctx); 190 } 191 192 int 193 fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask) 194 { 195 (void)handle; 196 (void)sigmask; 197 198 return (FIDO_ERR_INTERNAL); 199 } 200 201 int 202 fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) 203 { 204 struct hid_hidapi *ctx = handle; 205 206 if (len != ctx->report_in_len) { 207 fido_log_debug("%s: len %zu", __func__, len); 208 return -1; 209 } 210 211 return hid_read_timeout(ctx->handle, buf, len, ms); 212 } 213 214 int 215 fido_hid_write(void *handle, const unsigned char *buf, size_t len) 216 { 217 struct hid_hidapi *ctx = handle; 218 219 if (len != ctx->report_out_len + 1) { 220 fido_log_debug("%s: len %zu", __func__, len); 221 return -1; 222 } 223 224 return hid_write(ctx->handle, buf, len); 225 } 226 227 int 228 fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) 229 { 230 struct hid_device_info *hdi; 231 232 *olen = 0; 233 234 if (ilen == 0) 235 return FIDO_OK; /* nothing to do */ 236 if (devlist == NULL) 237 return FIDO_ERR_INVALID_ARGUMENT; 238 if ((hdi = hid_enumerate(0, 0)) == NULL) 239 return FIDO_OK; /* nothing to do */ 240 241 for (struct hid_device_info *d = hdi; d != NULL; d = d->next) { 242 if (is_fido(d) == false) 243 continue; 244 if (copy_info(&devlist[*olen], d) == 0) { 245 if (++(*olen) == ilen) 246 break; 247 } 248 } 249 250 hid_free_enumeration(hdi); 251 252 return FIDO_OK; 253 } 254 255 size_t 256 fido_hid_report_in_len(void *handle) 257 { 258 struct hid_hidapi *ctx = handle; 259 260 return (ctx->report_in_len); 261 } 262 263 size_t 264 fido_hid_report_out_len(void *handle) 265 { 266 struct hid_hidapi *ctx = handle; 267 268 return (ctx->report_out_len); 269 } 270