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 <fcntl.h> 10 #include <string.h> 11 #ifdef HAVE_UNISTD_H 12 #include <unistd.h> 13 #endif 14 #include <windows.h> 15 #include <setupapi.h> 16 #include <initguid.h> 17 #include <hidclass.h> 18 #include <hidsdi.h> 19 20 #include "fido.h" 21 22 #define REPORT_LEN 65 23 24 static bool 25 is_fido(HANDLE dev) 26 { 27 PHIDP_PREPARSED_DATA data = NULL; 28 HIDP_CAPS caps; 29 uint16_t usage_page = 0; 30 31 if (HidD_GetPreparsedData(dev, &data) == false) { 32 fido_log_debug("%s: HidD_GetPreparsedData", __func__); 33 goto fail; 34 } 35 36 if (HidP_GetCaps(data, &caps) != HIDP_STATUS_SUCCESS) { 37 fido_log_debug("%s: HidP_GetCaps", __func__); 38 goto fail; 39 } 40 41 if (caps.OutputReportByteLength != REPORT_LEN || 42 caps.InputReportByteLength != REPORT_LEN) { 43 fido_log_debug("%s: unsupported report len", __func__); 44 goto fail; 45 } 46 47 usage_page = caps.UsagePage; 48 fail: 49 if (data != NULL) 50 HidD_FreePreparsedData(data); 51 52 return (usage_page == 0xf1d0); 53 } 54 55 static int 56 get_int(HANDLE dev, int16_t *vendor_id, int16_t *product_id) 57 { 58 HIDD_ATTRIBUTES attr; 59 60 attr.Size = sizeof(attr); 61 62 if (HidD_GetAttributes(dev, &attr) == false) { 63 fido_log_debug("%s: HidD_GetAttributes", __func__); 64 return (-1); 65 } 66 67 *vendor_id = attr.VendorID; 68 *product_id = attr.ProductID; 69 70 return (0); 71 } 72 73 static int 74 get_str(HANDLE dev, char **manufacturer, char **product) 75 { 76 wchar_t buf[512]; 77 int utf8_len; 78 int ok = -1; 79 80 *manufacturer = NULL; 81 *product = NULL; 82 83 if (HidD_GetManufacturerString(dev, &buf, sizeof(buf)) == false) { 84 fido_log_debug("%s: HidD_GetManufacturerString", __func__); 85 goto fail; 86 } 87 88 if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, 89 -1, NULL, 0, NULL, NULL)) <= 0 || utf8_len > 128) { 90 fido_log_debug("%s: WideCharToMultiByte", __func__); 91 goto fail; 92 } 93 94 if ((*manufacturer = malloc(utf8_len)) == NULL) { 95 fido_log_debug("%s: malloc", __func__); 96 goto fail; 97 } 98 99 if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, -1, 100 *manufacturer, utf8_len, NULL, NULL) != utf8_len) { 101 fido_log_debug("%s: WideCharToMultiByte", __func__); 102 goto fail; 103 } 104 105 if (HidD_GetProductString(dev, &buf, sizeof(buf)) == false) { 106 fido_log_debug("%s: HidD_GetProductString", __func__); 107 goto fail; 108 } 109 110 if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, 111 -1, NULL, 0, NULL, NULL)) <= 0 || utf8_len > 128) { 112 fido_log_debug("%s: WideCharToMultiByte", __func__); 113 goto fail; 114 } 115 116 if ((*product = malloc(utf8_len)) == NULL) { 117 fido_log_debug("%s: malloc", __func__); 118 goto fail; 119 } 120 121 if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, -1, 122 *product, utf8_len, NULL, NULL) != utf8_len) { 123 fido_log_debug("%s: WideCharToMultiByte", __func__); 124 goto fail; 125 } 126 127 ok = 0; 128 fail: 129 if (ok < 0) { 130 free(*manufacturer); 131 free(*product); 132 *manufacturer = NULL; 133 *product = NULL; 134 } 135 136 return (ok); 137 } 138 139 static int 140 copy_info(fido_dev_info_t *di, const char *path) 141 { 142 HANDLE dev = INVALID_HANDLE_VALUE; 143 int ok = -1; 144 145 memset(di, 0, sizeof(*di)); 146 147 dev = CreateFileA(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, 148 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 149 if (dev == INVALID_HANDLE_VALUE || is_fido(dev) == 0) 150 goto fail; 151 152 if (get_int(dev, &di->vendor_id, &di->product_id) < 0 || 153 get_str(dev, &di->manufacturer, &di->product) < 0) 154 goto fail; 155 156 if ((di->path = strdup(path)) == NULL) 157 goto fail; 158 159 ok = 0; 160 fail: 161 if (dev != INVALID_HANDLE_VALUE) 162 CloseHandle(dev); 163 164 if (ok < 0) { 165 free(di->path); 166 free(di->manufacturer); 167 free(di->product); 168 explicit_bzero(di, sizeof(*di)); 169 } 170 171 return (ok); 172 } 173 174 int 175 fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) 176 { 177 GUID hid_guid = GUID_DEVINTERFACE_HID; 178 HDEVINFO devinfo = INVALID_HANDLE_VALUE; 179 SP_DEVICE_INTERFACE_DATA ifdata; 180 SP_DEVICE_INTERFACE_DETAIL_DATA_A *ifdetail = NULL; 181 DWORD len = 0; 182 DWORD idx = 0; 183 int r = FIDO_ERR_INTERNAL; 184 185 *olen = 0; 186 187 if (ilen == 0) 188 return (FIDO_OK); /* nothing to do */ 189 190 if (devlist == NULL) 191 return (FIDO_ERR_INVALID_ARGUMENT); 192 193 devinfo = SetupDiGetClassDevsA(&hid_guid, NULL, NULL, 194 DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); 195 if (devinfo == INVALID_HANDLE_VALUE) { 196 fido_log_debug("%s: SetupDiGetClassDevsA", __func__); 197 goto fail; 198 } 199 200 ifdata.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); 201 202 while (SetupDiEnumDeviceInterfaces(devinfo, NULL, &hid_guid, idx++, 203 &ifdata) == true) { 204 /* 205 * "Get the required buffer size. Call 206 * SetupDiGetDeviceInterfaceDetail with a NULL 207 * DeviceInterfaceDetailData pointer, a 208 * DeviceInterfaceDetailDataSize of zero, and a valid 209 * RequiredSize variable. In response to such a call, this 210 * function returns the required buffer size at RequiredSize 211 * and fails with GetLastError returning 212 * ERROR_INSUFFICIENT_BUFFER." 213 */ 214 if (SetupDiGetDeviceInterfaceDetailA(devinfo, &ifdata, NULL, 0, 215 &len, NULL) != false || 216 GetLastError() != ERROR_INSUFFICIENT_BUFFER) { 217 fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 1", 218 __func__); 219 goto fail; 220 } 221 222 if ((ifdetail = malloc(len)) == NULL) { 223 fido_log_debug("%s: malloc", __func__); 224 goto fail; 225 } 226 227 ifdetail->cbSize = sizeof(*ifdetail); 228 229 if (SetupDiGetDeviceInterfaceDetailA(devinfo, &ifdata, ifdetail, 230 len, NULL, NULL) == false) { 231 fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 2", 232 __func__); 233 goto fail; 234 } 235 236 if (copy_info(&devlist[*olen], ifdetail->DevicePath) == 0) { 237 devlist[*olen].io = (fido_dev_io_t) { 238 fido_hid_open, 239 fido_hid_close, 240 fido_hid_read, 241 fido_hid_write, 242 }; 243 if (++(*olen) == ilen) 244 break; 245 } 246 247 free(ifdetail); 248 ifdetail = NULL; 249 } 250 251 r = FIDO_OK; 252 fail: 253 if (devinfo != INVALID_HANDLE_VALUE) 254 SetupDiDestroyDeviceInfoList(devinfo); 255 256 free(ifdetail); 257 258 return (r); 259 } 260 261 void * 262 fido_hid_open(const char *path) 263 { 264 HANDLE dev; 265 266 dev = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, 267 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 268 FILE_ATTRIBUTE_NORMAL, NULL); 269 270 if (dev == INVALID_HANDLE_VALUE) 271 return (NULL); 272 273 return (dev); 274 } 275 276 void 277 fido_hid_close(void *handle) 278 { 279 CloseHandle(handle); 280 } 281 282 int 283 fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) 284 { 285 DWORD n; 286 int r = -1; 287 uint8_t report[REPORT_LEN]; 288 289 (void)ms; /* XXX */ 290 291 memset(report, 0, sizeof(report)); 292 293 if (len != sizeof(report) - 1) { 294 fido_log_debug("%s: invalid len", __func__); 295 return (-1); 296 } 297 298 if (ReadFile(handle, report, sizeof(report), &n, NULL) == false || 299 n != sizeof(report)) { 300 fido_log_debug("%s: ReadFile", __func__); 301 goto fail; 302 } 303 304 r = sizeof(report) - 1; 305 memcpy(buf, report + 1, len); 306 307 fail: 308 explicit_bzero(report, sizeof(report)); 309 310 return (r); 311 } 312 313 int 314 fido_hid_write(void *handle, const unsigned char *buf, size_t len) 315 { 316 DWORD n; 317 318 if (len != REPORT_LEN) { 319 fido_log_debug("%s: invalid len", __func__); 320 return (-1); 321 } 322 323 if (WriteFile(handle, buf, (DWORD)len, &n, NULL) == false || 324 n != REPORT_LEN) { 325 fido_log_debug("%s: WriteFile", __func__); 326 return (-1); 327 } 328 329 return (REPORT_LEN); 330 } 331