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