1 /* 2 * Copyright (c) 2019-2024 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/types.h> 9 #include <sys/file.h> 10 #include <sys/ioctl.h> 11 12 #include <linux/hidraw.h> 13 #include <linux/input.h> 14 15 #include <errno.h> 16 #include <libudev.h> 17 #include <time.h> 18 #include <unistd.h> 19 20 #include "fido.h" 21 22 struct hid_linux { 23 int fd; 24 size_t report_in_len; 25 size_t report_out_len; 26 sigset_t sigmask; 27 const sigset_t *sigmaskp; 28 }; 29 30 static int 31 get_report_descriptor(int fd, struct hidraw_report_descriptor *hrd) 32 { 33 int s = -1; 34 35 if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESCSIZE), &s) == -1) { 36 fido_log_error(errno, "%s: ioctl HIDIOCGRDESCSIZE", __func__); 37 return (-1); 38 } 39 40 if (s < 0 || (unsigned)s > HID_MAX_DESCRIPTOR_SIZE) { 41 fido_log_debug("%s: HIDIOCGRDESCSIZE %d", __func__, s); 42 return (-1); 43 } 44 45 hrd->size = (unsigned)s; 46 47 if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESC), hrd) == -1) { 48 fido_log_error(errno, "%s: ioctl HIDIOCGRDESC", __func__); 49 return (-1); 50 } 51 52 return (0); 53 } 54 55 static bool 56 is_fido(const char *path) 57 { 58 int fd = -1; 59 uint32_t usage_page = 0; 60 struct hidraw_report_descriptor *hrd = NULL; 61 62 if ((hrd = calloc(1, sizeof(*hrd))) == NULL || 63 (fd = fido_hid_unix_open(path)) == -1) 64 goto out; 65 if (get_report_descriptor(fd, hrd) < 0 || 66 fido_hid_get_usage(hrd->value, hrd->size, &usage_page) < 0) 67 usage_page = 0; 68 69 out: 70 free(hrd); 71 72 if (fd != -1 && close(fd) == -1) 73 fido_log_error(errno, "%s: close", __func__); 74 75 return (usage_page == 0xf1d0); 76 } 77 78 static int 79 parse_uevent(const char *uevent, int *bus, int16_t *vendor_id, 80 int16_t *product_id, char **hid_name) 81 { 82 char *cp; 83 char *p; 84 char *s; 85 bool found_id = false; 86 bool found_name = false; 87 short unsigned int x; 88 short unsigned int y; 89 short unsigned int z; 90 91 if ((s = cp = strdup(uevent)) == NULL) 92 return (-1); 93 94 while ((p = strsep(&cp, "\n")) != NULL && *p != '\0') { 95 if (!found_id && strncmp(p, "HID_ID=", 7) == 0) { 96 if (sscanf(p + 7, "%hx:%hx:%hx", &x, &y, &z) == 3) { 97 *bus = (int)x; 98 *vendor_id = (int16_t)y; 99 *product_id = (int16_t)z; 100 found_id = true; 101 } 102 } else if (!found_name && strncmp(p, "HID_NAME=", 9) == 0) { 103 if ((*hid_name = strdup(p + 9)) != NULL) 104 found_name = true; 105 } 106 } 107 108 free(s); 109 110 if (!found_name || !found_id) 111 return (-1); 112 113 return (0); 114 } 115 116 static char * 117 get_parent_attr(struct udev_device *dev, const char *subsystem, 118 const char *devtype, const char *attr) 119 { 120 struct udev_device *parent; 121 const char *value; 122 123 if ((parent = udev_device_get_parent_with_subsystem_devtype(dev, 124 subsystem, devtype)) == NULL || (value = 125 udev_device_get_sysattr_value(parent, attr)) == NULL) 126 return (NULL); 127 128 return (strdup(value)); 129 } 130 131 static char * 132 get_usb_attr(struct udev_device *dev, const char *attr) 133 { 134 return (get_parent_attr(dev, "usb", "usb_device", attr)); 135 } 136 137 static int 138 copy_info(fido_dev_info_t *di, struct udev *udev, 139 struct udev_list_entry *udev_entry) 140 { 141 const char *name; 142 const char *path; 143 char *uevent = NULL; 144 struct udev_device *dev = NULL; 145 int bus = 0; 146 char *hid_name = NULL; 147 int ok = -1; 148 149 memset(di, 0, sizeof(*di)); 150 151 if ((name = udev_list_entry_get_name(udev_entry)) == NULL || 152 (dev = udev_device_new_from_syspath(udev, name)) == NULL || 153 (path = udev_device_get_devnode(dev)) == NULL || 154 is_fido(path) == 0) 155 goto fail; 156 157 if ((uevent = get_parent_attr(dev, "hid", NULL, "uevent")) == NULL || 158 parse_uevent(uevent, &bus, &di->vendor_id, &di->product_id, 159 &hid_name) < 0) { 160 fido_log_debug("%s: uevent", __func__); 161 goto fail; 162 } 163 164 #ifndef FIDO_HID_ANY 165 if (bus != BUS_USB) { 166 fido_log_debug("%s: bus", __func__); 167 goto fail; 168 } 169 #endif 170 171 di->path = strdup(path); 172 di->manufacturer = get_usb_attr(dev, "manufacturer"); 173 di->product = get_usb_attr(dev, "product"); 174 175 if (di->manufacturer == NULL && di->product == NULL) { 176 di->product = hid_name; /* fallback */ 177 hid_name = NULL; 178 } 179 if (di->manufacturer == NULL) 180 di->manufacturer = strdup(""); 181 if (di->product == NULL) 182 di->product = strdup(""); 183 if (di->path == NULL || di->manufacturer == NULL || di->product == NULL) 184 goto fail; 185 186 ok = 0; 187 fail: 188 if (dev != NULL) 189 udev_device_unref(dev); 190 191 free(uevent); 192 free(hid_name); 193 194 if (ok < 0) { 195 free(di->path); 196 free(di->manufacturer); 197 free(di->product); 198 explicit_bzero(di, sizeof(*di)); 199 } 200 201 return (ok); 202 } 203 204 int 205 fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) 206 { 207 struct udev *udev = NULL; 208 struct udev_enumerate *udev_enum = NULL; 209 struct udev_list_entry *udev_list; 210 struct udev_list_entry *udev_entry; 211 int r = FIDO_ERR_INTERNAL; 212 213 *olen = 0; 214 215 if (ilen == 0) 216 return (FIDO_OK); /* nothing to do */ 217 218 if (devlist == NULL) 219 return (FIDO_ERR_INVALID_ARGUMENT); 220 221 if ((udev = udev_new()) == NULL || 222 (udev_enum = udev_enumerate_new(udev)) == NULL) 223 goto fail; 224 225 if (udev_enumerate_add_match_subsystem(udev_enum, "hidraw") < 0 || 226 udev_enumerate_scan_devices(udev_enum) < 0) 227 goto fail; 228 229 if ((udev_list = udev_enumerate_get_list_entry(udev_enum)) == NULL) { 230 r = FIDO_OK; /* zero hidraw devices */ 231 goto fail; 232 } 233 234 udev_list_entry_foreach(udev_entry, udev_list) { 235 if (copy_info(&devlist[*olen], udev, udev_entry) == 0) { 236 devlist[*olen].io = (fido_dev_io_t) { 237 fido_hid_open, 238 fido_hid_close, 239 fido_hid_read, 240 fido_hid_write, 241 }; 242 if (++(*olen) == ilen) 243 break; 244 } 245 } 246 247 r = FIDO_OK; 248 fail: 249 if (udev_enum != NULL) 250 udev_enumerate_unref(udev_enum); 251 if (udev != NULL) 252 udev_unref(udev); 253 254 return (r); 255 } 256 257 void * 258 fido_hid_open(const char *path) 259 { 260 struct hid_linux *ctx; 261 struct hidraw_report_descriptor *hrd; 262 struct timespec tv_pause; 263 long interval_ms, retries = 0; 264 bool looped; 265 266 retry: 267 looped = false; 268 269 if ((ctx = calloc(1, sizeof(*ctx))) == NULL || 270 (ctx->fd = fido_hid_unix_open(path)) == -1) { 271 free(ctx); 272 return (NULL); 273 } 274 275 while (flock(ctx->fd, LOCK_EX|LOCK_NB) == -1) { 276 if (errno != EWOULDBLOCK) { 277 fido_log_error(errno, "%s: flock", __func__); 278 fido_hid_close(ctx); 279 return (NULL); 280 } 281 looped = true; 282 if (retries++ >= 20) { 283 fido_log_debug("%s: flock timeout", __func__); 284 fido_hid_close(ctx); 285 return (NULL); 286 } 287 interval_ms = retries * 100000000L; 288 tv_pause.tv_sec = interval_ms / 1000000000L; 289 tv_pause.tv_nsec = interval_ms % 1000000000L; 290 if (nanosleep(&tv_pause, NULL) == -1) { 291 fido_log_error(errno, "%s: nanosleep", __func__); 292 fido_hid_close(ctx); 293 return (NULL); 294 } 295 } 296 297 if (looped) { 298 fido_log_debug("%s: retrying", __func__); 299 fido_hid_close(ctx); 300 goto retry; 301 } 302 303 if ((hrd = calloc(1, sizeof(*hrd))) == NULL || 304 get_report_descriptor(ctx->fd, hrd) < 0 || 305 fido_hid_get_report_len(hrd->value, hrd->size, &ctx->report_in_len, 306 &ctx->report_out_len) < 0 || ctx->report_in_len == 0 || 307 ctx->report_out_len == 0) { 308 fido_log_debug("%s: using default report sizes", __func__); 309 ctx->report_in_len = CTAP_MAX_REPORT_LEN; 310 ctx->report_out_len = CTAP_MAX_REPORT_LEN; 311 } 312 313 free(hrd); 314 315 return (ctx); 316 } 317 318 void 319 fido_hid_close(void *handle) 320 { 321 struct hid_linux *ctx = handle; 322 323 if (close(ctx->fd) == -1) 324 fido_log_error(errno, "%s: close", __func__); 325 326 free(ctx); 327 } 328 329 int 330 fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask) 331 { 332 struct hid_linux *ctx = handle; 333 334 ctx->sigmask = *sigmask; 335 ctx->sigmaskp = &ctx->sigmask; 336 337 return (FIDO_OK); 338 } 339 340 int 341 fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) 342 { 343 struct hid_linux *ctx = handle; 344 ssize_t r; 345 346 if (len != ctx->report_in_len) { 347 fido_log_debug("%s: len %zu", __func__, len); 348 return (-1); 349 } 350 351 if (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) { 352 fido_log_debug("%s: fd not ready", __func__); 353 return (-1); 354 } 355 356 if ((r = read(ctx->fd, buf, len)) == -1) { 357 fido_log_error(errno, "%s: read", __func__); 358 return (-1); 359 } 360 361 if (r < 0 || (size_t)r != len) { 362 fido_log_debug("%s: %zd != %zu", __func__, r, len); 363 return (-1); 364 } 365 366 return ((int)r); 367 } 368 369 int 370 fido_hid_write(void *handle, const unsigned char *buf, size_t len) 371 { 372 struct hid_linux *ctx = handle; 373 ssize_t r; 374 375 if (len != ctx->report_out_len + 1) { 376 fido_log_debug("%s: len %zu", __func__, len); 377 return (-1); 378 } 379 380 if ((r = write(ctx->fd, buf, len)) == -1) { 381 fido_log_error(errno, "%s: write", __func__); 382 return (-1); 383 } 384 385 if (r < 0 || (size_t)r != len) { 386 fido_log_debug("%s: %zd != %zu", __func__, r, len); 387 return (-1); 388 } 389 390 return ((int)r); 391 } 392 393 size_t 394 fido_hid_report_in_len(void *handle) 395 { 396 struct hid_linux *ctx = handle; 397 398 return (ctx->report_in_len); 399 } 400 401 size_t 402 fido_hid_report_out_len(void *handle) 403 { 404 struct hid_linux *ctx = handle; 405 406 return (ctx->report_out_len); 407 } 408