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